diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml new file mode 100644 index 0000000..86b07d7 --- /dev/null +++ b/.gitea/workflows/build.yml @@ -0,0 +1,76 @@ +name: build + +# Builds a versioned Linux release tarball (see `make package`) on every +# push/PR, plus on-demand via the Gitea "Run workflow" button. Runs +# inside the build-image (see ../../build-image/Dockerfile, built/pushed +# via ../../build-image.sh and ../../upload-image.sh), which bundles +# every dependency engine/ needs to compile - no network access needed +# at job runtime. Since the job's container already *is* the build-image +# (QUESTSHOCK_BUILD_IMAGE=1), `make package`'s `engine` prerequisite +# compiles directly instead of trying to docker-run it again, which +# wouldn't work here (no nested docker). +on: + push: + pull_request: + workflow_dispatch: + +jobs: + build: + # Runner defaults `run:` steps to `sh`, which doesn't understand + # `set -o pipefail` used below - force bash explicitly. + defaults: + run: + shell: bash + # Must match a label your act_runner is registered with. + runs-on: ubuntu-latest + + container: + image: cr.ladkau.de/questshock/builder:latest + credentials: + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + steps: + - uses: actions/checkout@v4 + with: + # `make package`'s version comes from `git describe --tags` - + # needs full history/tags, not actions/checkout's default + # shallow single-commit clone. + fetch-depth: 0 + + - name: Preflight + run: | + set -euo pipefail + command -v cc >/dev/null 2>&1 || { echo "PREFLIGHT FAIL: no C compiler (cc) in PATH" >&2; exit 1; } + command -v cmake >/dev/null 2>&1 || { echo "PREFLIGHT FAIL: cmake not in PATH" >&2; exit 1; } + [ -n "${QUESTSHOCK_BUILD_IMAGE:-}" ] || { echo "PREFLIGHT FAIL: QUESTSHOCK_BUILD_IMAGE not set - not running inside the questshock build-image?" >&2; exit 1; } + [ -d /opt/prebuilt/built_sdl ] || { echo "PREFLIGHT FAIL: /opt/prebuilt/built_sdl missing" >&2; exit 1; } + + - name: Build package + run: make package + + - name: Upload build artifact + # v4 uses the newer @actions/artifact backend, which this Gitea + # instance's artifact storage doesn't support (GHESNotSupportedError) - v3 works. + uses: actions/upload-artifact@v3 + with: + name: questshock + path: dist/questshock-*-linux-*.tar.gz + + - name: Publish to dl.ladkau.de + # Uploads the tarball over SFTP instead of using + # actions/upload-artifact (whose zip wrapping can't be disabled). + # Only runs on push so PR builds don't publish. + if: gitea.event_name == 'push' + run: | + set -euo pipefail + TARBALL="$(ls dist/questshock-*-linux-*.tar.gz)" + mkdir -p ~/.ssh + echo "${{ secrets.DL_SFTP_KEY }}" > ~/.ssh/dl_sftp_key + chmod 600 ~/.ssh/dl_sftp_key + sftp -i ~/.ssh/dl_sftp_key -P 2223 \ + -o StrictHostKeyChecking=accept-new \ + uploader@dl.ladkau.de <&2; \ + echo "(see res/assets/extract_assets.sh for how to get the installer from gog.com)" >&2; \ + exit 1; \ + fi + +dist: engine assets + @echo "== Assembling $(DIST_DIR) ==" + rm -rf "$(DIST_DIR)/systemshock" "$(DIST_DIR)/lib" "$(DIST_DIR)/res" \ + "$(DIST_DIR)/shaders" "$(DIST_DIR)/run.sh" + mkdir -p "$(DIST_DIR)/lib" "$(DIST_DIR)/res/data" "$(DIST_DIR)/res/sound" + cp "$(ENGINE_OUT)/systemshock" "$(DIST_DIR)/" + cp -a "$(ENGINE_OUT)/lib/." "$(DIST_DIR)/lib/" + cp "$(ENGINE_OUT)/soundfont.sf2" "$(DIST_DIR)/res/" + cp -a engine/shaders "$(DIST_DIR)/shaders" + cp -a "$(ASSETS_DIR)/data/." "$(DIST_DIR)/res/data/" + cp -a "$(ASSETS_DIR)/sound/." "$(DIST_DIR)/res/sound/" + cp res/run.sh "$(DIST_DIR)/run.sh" + chmod +x "$(DIST_DIR)/run.sh" + @echo "== Done - run $(DIST_DIR)/run.sh to play ==" + +# Builds a versioned, redistributable Linux release tarball at +# dist/questshock--linux-.tar.gz - everything needed to +# run except the proprietary game assets (res/GET_ASSETS.txt explains how +# to get those instead of shipping res/data/, res/sound/). Version +# defaults to the current git tag (vX.Y.Z, tag prefix stripped) - push a +# tag to drive a release. Override with `make package VERSION=1.2.3`, or +# just run it untagged for a local dev build (gets a 0.0.0-dev+ +# placeholder version, with a warning). +package: engine + @git config --global --add safe.directory "$$(pwd)" 2>/dev/null || true + @V="$(VERSION)"; \ + if [ -z "$$V" ]; then \ + if TAG=$$(git describe --tags --exact-match --match 'v[0-9]*.[0-9]*.[0-9]*' 2>/dev/null); then \ + V=$${TAG#v}; \ + else \ + V="0.0.0-dev+$$(git rev-parse --short HEAD)"; \ + echo "WARNING: HEAD is not on a vX.Y.Z tag - building placeholder version $$V (push a tag to drive a real release version)" >&2; \ + fi; \ + fi; \ + case "$$V" in \ + [0-9]*.[0-9]*.[0-9]*) ;; \ + *) echo "PREFLIGHT FAIL: VERSION '$$V' is not a semantic version (expected X.Y.Z, optionally with a -pre+meta suffix)" >&2; exit 1;; \ + esac; \ + PKG_NAME="questshock-$$V-linux-$(ARCH)"; \ + PKG_STAGE="$(BUILD_DIR)/package/$$PKG_NAME"; \ + echo "Packaging $$PKG_NAME"; \ + rm -rf "$$PKG_STAGE"; \ + mkdir -p "$$PKG_STAGE/lib" "$$PKG_STAGE/res"; \ + cp "$(ENGINE_OUT)/systemshock" "$$PKG_STAGE/"; \ + cp -a "$(ENGINE_OUT)/lib/." "$$PKG_STAGE/lib/"; \ + cp -a engine/shaders "$$PKG_STAGE/shaders"; \ + cp "$(ENGINE_OUT)/soundfont.sf2" "$$PKG_STAGE/res/"; \ + cp res/assets/GET_ASSETS.txt "$$PKG_STAGE/res/GET_ASSETS.txt"; \ + cp res/run.sh "$$PKG_STAGE/run.sh"; \ + chmod +x "$$PKG_STAGE/run.sh"; \ + cp LICENSE "$$PKG_STAGE/LICENSE"; \ + cp engine/LICENSE "$$PKG_STAGE/LICENSE.Shockolate"; \ + cp NOTICE.txt "$$PKG_STAGE/NOTICE.txt"; \ + mkdir -p "$(DIST_DIR)"; \ + tar -czf "$(DIST_DIR)/$$PKG_NAME.tar.gz" -C "$(BUILD_DIR)/package" "$$PKG_NAME"; \ + rm -rf "$(BUILD_DIR)/package"; \ + echo "Wrote $(DIST_DIR)/$$PKG_NAME.tar.gz" + +clean: + rm -rf "$(DIST_DIR)" "$(BUILD_DIR)" "$(ENGINE_OUT)" \ + engine/build_ext engine/CMakeCache.txt engine/CMakeFiles \ + engine/cmake_install.cmake engine/Makefile engine/systemshock \ + engine/src/Libraries/CMakeFiles diff --git a/NOTICE.txt b/NOTICE.txt new file mode 100644 index 0000000..630911e --- /dev/null +++ b/NOTICE.txt @@ -0,0 +1,9 @@ +This package's original tooling (build scripts, Makefile, launcher) is +licensed under the MIT License - see LICENSE. + +The systemshock binary and its shared libraries in lib/ are built from +Shockolate (https://github.com/Interrupt/systemshock), which is licensed +under the GNU GPLv3 - see LICENSE.Shockolate. It is included unchanged +and is not relicensed by this project's MIT license. + +Game assets are not included - see res/GET_ASSETS.txt. diff --git a/README.md b/README.md new file mode 100644 index 0000000..919d6fe --- /dev/null +++ b/README.md @@ -0,0 +1,88 @@ +# questshock + +An open source project to play the classic 1994 System Shock on a VR +headset, built on top of [Shockolate](https://github.com/Interrupt/systemshock), +a cross-platform port of the original game. + +## Layout + +- `engine/` - a vendored snapshot of the Shockolate engine source. Built + via Docker; see below. +- `build-image/` - the Dockerfile (and supporting scripts) for the engine + build environment. Every third-party dependency the engine needs to + compile (SDL2, SDL2_mixer, the fluidsynth-lite MIDI synth, a MIDI + soundfont) is fetched and built once into this image - compiling + `engine/` itself needs no network access. +- `build-image.sh` / `run-image.sh` / `upload-image.sh` - build the image, + run it to compile the engine, and push it to a registry, respectively. +- `res/assets/` - where you place your own purchased copy of the game (see + below); `res/assets/extract_assets.sh` extracts it into `ss_ee/`. +- `res/run.sh` - the launcher script, copied into `dist/` on build. +- `Makefile` - assembles `dist/`, a self-contained runnable copy of the + game, out of the compiled engine and the extracted assets. Also builds + `dist/questshock--linux-.tar.gz`, a redistributable + package that omits the proprietary game assets (`make package`). + +## Building + +```sh +# 1. Build the engine build-image (once, or after build-image/ changes) +./build-image.sh + +# 2. Get your own copy of the game data (see "Game assets" below), then: +res/assets/extract_assets.sh + +# 3. Compile the engine and assemble dist/ +make dist + +# 4. Play +dist/run.sh +``` + +`make dist` always recompiles the engine from the current `engine/` +source (via `run-image.sh`), so a fresh build-image plus a re-run of +`make dist` is all that's needed after pulling engine changes. + +## Game assets + +System Shock's game data is not included in this repository and cannot +be redistributed - you need to own a copy. Buy **System Shock: Enhanced +Edition** on [gog.com](https://www.gog.com/), download the offline +installer (a `.exe`), and drop it into `res/assets/`. Then run +`res/assets/extract_assets.sh`, which pulls the classic game's data and +sound files out of the installer (it's an Inno Setup package; the actual +game data lives inside it in a zip-format `sshock.kpf`) into +`res/assets/ss_ee/`. That script needs `innoextract` and `unzip`; if +they aren't installed locally it falls back to running the extraction in +a throwaway Docker container instead. + +## Packaging + +`make package` builds `dist/questshock--linux-.tar.gz`: the +compiled binary, its runtime libraries, shaders, a default MIDI +soundfont, license information, and `res/GET_ASSETS.txt` in place of the +actual game data (which the tarball never includes). Version comes from +the current git tag (push a `vX.Y.Z` tag to drive a release); without one +it builds an untagged `0.0.0-dev+` placeholder. + +A Gitea Actions workflow (`.gitea/workflows/build.yml`) builds this +package on every push, using the build-image as its container (so no +extra setup is needed in CI beyond the image itself), and publishes the +resulting tarball to dl.ladkau.de. + +## License + +The original tooling in this repository (the Docker build image, build +scripts, Makefile, and asset extraction script) is licensed under the +[MIT License](LICENSE). + +The vendored engine snapshot in `engine/` is +[Shockolate](https://github.com/Interrupt/systemshock), which is licensed +under the **GNU GPLv3** (see `engine/LICENSE`) - it is included unchanged +and is *not* relicensed by this project's MIT license. Any build or +distribution of the compiled engine must comply with the GPLv3. + +The game assets extracted into `res/assets/ss_ee/` are proprietary, +copyrighted game data owned by their respective rightsholders - they are +never committed to this repository (see `.gitignore`) and must be +supplied by each user from their own legitimate purchase. diff --git a/build-image.sh b/build-image.sh new file mode 100755 index 0000000..88a71c0 --- /dev/null +++ b/build-image.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# Builds the engine build-image (build-image/Dockerfile) locally, tagged +# with build-image/VERSION and :latest. Does not push - test it with +# ./run-image.sh first, then publish with ./upload-image.sh. +set -euo pipefail + +cd "$(dirname "${BASH_SOURCE[0]}")" +ROOT="$(pwd)" + +fail() { echo "PREFLIGHT FAIL: $*" >&2; exit 1; } + +command -v docker >/dev/null 2>&1 \ + || fail "docker not found in PATH" + +IMAGE="${REGISTRY_IMAGE:-questshock/builder}" +if [ -f "$ROOT/registry.env" ]; then + # shellcheck disable=SC1091 + source "$ROOT/registry.env" + IMAGE="${REGISTRY_IMAGE:-$IMAGE}" +fi + +VERSION="$(<"$ROOT/build-image/VERSION")" +[ -n "$VERSION" ] || fail "build-image/VERSION is empty" + +echo "== Building $IMAGE:$VERSION ==" +docker build \ + -f "$ROOT/build-image/Dockerfile" \ + -t "$IMAGE:$VERSION" \ + -t "$IMAGE:latest" \ + "$ROOT" + +echo "== Done ==" +echo "Image: $IMAGE:$VERSION (and :latest)" +echo "Test it with ./run-image.sh, then publish with ./upload-image.sh" diff --git a/build-image/Dockerfile b/build-image/Dockerfile new file mode 100644 index 0000000..0228ccd --- /dev/null +++ b/build-image/Dockerfile @@ -0,0 +1,99 @@ +# Build environment for the Shockolate (System Shock) engine snapshot in +# engine/. Everything the engine build needs from the network - SDL2, +# SDL2_mixer, the fluidsynth-lite MIDI synth, and a General MIDI soundfont - +# is fetched and built once, here, at image-build time. Rebuild with +# ../build-image.sh whenever a version below changes. +# +# The resulting image needs no network access to compile engine/: the +# in-container build script (build-image/build-engine.sh) copies the +# prebuilt pieces below straight into engine/build_ext/, matching the paths +# engine/CMakeLists.txt's BUNDLED dependency mode expects. +FROM ubuntu:22.04 + +ARG SDL2_VERSION=2.0.9 +ARG SDL2_MIXER_VERSION=2.0.4 +# EtherTyper/fluidsynth-lite has no releases/tags; pin by commit so the image +# is reproducible instead of silently picking up upstream changes. +ARG FLUIDSYNTH_LITE_REF=c539a8d9270ba5a3f7d6e460606483fc2ab1eb61 +# Soundfont used for MIDI music, matching what engine/build_deps.sh itself +# fetches (a free substitute for the Windows default GM soundfont). +ARG SOUNDFONT_URL=http://rancid.kapsi.fi/windows.sf2 + +ENV DEBIAN_FRONTEND=noninteractive + +# build-essential/cmake/make: engine/ itself (CMake) and SDL2/SDL2_mixer +# (autotools). +# git: fetches fluidsynth-lite; also used by engine/CMakeLists.txt's own +# `git describe` version string (harmless no-op if engine/ isn't a repo). +# curl/ca-certificates: fetches SDL2/SDL2_mixer tarballs and the soundfont. +# pkg-config: SDL2_mixer's configure and engine/CMakeLists.txt's +# pkg_check_modules() calls. +# gosu: docker-entrypoint.sh drops from root to a HOST_UID/GID user so +# build output isn't left root-owned on the bind-mounted repo. +# libgl1-mesa-dev/libglx-dev/libxext-dev/libx11-dev/libxrandr-dev/libxi-dev/ +# libxfixes-dev/libxss-dev/libxinerama-dev/libxcursor-dev: SDL2's video +# backend (X11 + GL). +# libogg-dev/libvorbis-dev: SDL2_mixer's OGG Vorbis music decoder. +# libasound2-dev: engine/CMakeLists.txt's optional native ALSA MIDI output. +# openssh-client: the CI workflow's `sftp` publish step. +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential cmake make git curl ca-certificates pkg-config gosu \ + libgl1-mesa-dev libglx-dev libxext-dev libx11-dev libxrandr-dev \ + libxi-dev libxfixes-dev libxss-dev libxinerama-dev libxcursor-dev \ + libogg-dev libvorbis-dev libasound2-dev openssh-client \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /opt/prebuilt + +# SDL2, built the same way engine/build_deps.sh builds it (BUNDLED mode), +# just at image-build time instead of every game build. +RUN curl -sSLO "https://www.libsdl.org/release/SDL2-${SDL2_VERSION}.tar.gz" \ + && tar xf "SDL2-${SDL2_VERSION}.tar.gz" \ + && cd "SDL2-${SDL2_VERSION}" \ + && ./configure --prefix=/opt/prebuilt/built_sdl \ + && make -j"$(nproc)" \ + && make install \ + && cd .. && rm -rf "SDL2-${SDL2_VERSION}" "SDL2-${SDL2_VERSION}.tar.gz" + +# SDL2_mixer, linked against the SDL2 build above via SDL2_CONFIG - same as +# engine/build_deps.sh's build_sdl_mixer(). +RUN curl -sSLO "https://www.libsdl.org/projects/SDL_mixer/release/SDL2_mixer-${SDL2_MIXER_VERSION}.tar.gz" \ + && tar xf "SDL2_mixer-${SDL2_MIXER_VERSION}.tar.gz" \ + && cd "SDL2_mixer-${SDL2_MIXER_VERSION}" \ + && SDL2_CONFIG=/opt/prebuilt/built_sdl/bin/sdl2-config \ + ./configure --prefix=/opt/prebuilt/built_sdl_mixer \ + && make -j"$(nproc)" \ + && make install \ + && cd .. && rm -rf "SDL2_mixer-${SDL2_MIXER_VERSION}" "SDL2_mixer-${SDL2_MIXER_VERSION}.tar.gz" + +# fluidsynth-lite: the stripped-down FluidSynth fork engine/CMakeLists.txt's +# BUNDLED FluidSynth mode expects at build_ext/fluidsynth-lite. Built +# in-source (its own CMake setup expects that), forcing a shared library +# the same way engine/build_deps.sh's sed does. +RUN git clone https://github.com/EtherTyper/fluidsynth-lite.git \ + && cd fluidsynth-lite \ + && git checkout "${FLUIDSYNTH_LITE_REF}" \ + && sed -i 's/DLL"\ off/DLL"\ on/' CMakeLists.txt \ + && cmake . \ + && cmake --build . -j"$(nproc)" \ + && rm -rf .git + +# General MIDI soundfont for fluidsynth playback - engine/build_deps.sh +# fetches the same file and drops it into engine/res/. +RUN mkdir -p soundfont \ + && curl -sSL -o soundfont/default.sf2 "${SOUNDFONT_URL}" + +COPY build-image/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh +COPY build-image/build-engine.sh /usr/local/bin/build-engine.sh +RUN chmod +x /usr/local/bin/docker-entrypoint.sh /usr/local/bin/build-engine.sh + +# Marks a shell as already running inside this image (with every engine +# build dependency prebuilt above) - lets the Makefile's `engine` target +# compile directly instead of shelling out to ./run-image.sh's `docker +# run`, which matters for CI jobs that already run inside this image +# (nested docker-in-docker isn't available there). +ENV QUESTSHOCK_BUILD_IMAGE=1 + +WORKDIR /workspace +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] +CMD ["/usr/local/bin/build-engine.sh"] diff --git a/build-image/VERSION b/build-image/VERSION new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/build-image/VERSION @@ -0,0 +1 @@ +1 diff --git a/build-image/build-engine.sh b/build-image/build-engine.sh new file mode 100755 index 0000000..0d3825e --- /dev/null +++ b/build-image/build-engine.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# Compiles engine/ (the vendored Shockolate snapshot) against the +# dependencies prebuilt into this image at /opt/prebuilt - no network +# access needed. Must be run with the repo root as the working directory +# - either via ../run-image.sh (which docker-runs this image with the +# repo bind-mounted at /workspace and WORKDIR set there), or directly +# when already inside this image (e.g. a CI job using this image as its +# container - see the Makefile's `engine` target, which picks whichever +# of these applies). +# +# Output lands in engine/.build-output/: the systemshock binary, the +# shared libraries it needs at runtime (lib/), and a default MIDI +# soundfont - everything the root Makefile needs to assemble dist/. +set -euo pipefail + +REPO_ROOT="$(pwd)" +ENGINE_DIR="$REPO_ROOT/engine" +OUT_DIR="$ENGINE_DIR/.build-output" + +cd "$ENGINE_DIR" + +echo "== Wiring up prebuilt SDL2/SDL2_mixer/fluidsynth-lite from the image ==" +rm -rf build_ext +mkdir -p build_ext +cp -a /opt/prebuilt/built_sdl build_ext/ +cp -a /opt/prebuilt/built_sdl_mixer build_ext/ +cp -a /opt/prebuilt/fluidsynth-lite build_ext/ + +echo "== Configuring (CMake, BUNDLED SDL2/SDL2_mixer/FluidSynth) ==" +rm -f CMakeCache.txt +cmake -DENABLE_SDL2=BUNDLED -DENABLE_SOUND=BUNDLED -DENABLE_FLUIDSYNTH=BUNDLED . + +echo "== Compiling ==" +make -j"$(nproc)" systemshock + +echo "== Assembling engine/.build-output ==" +rm -rf "$OUT_DIR" +mkdir -p "$OUT_DIR/lib" +cp systemshock "$OUT_DIR/" +find build_ext/built_sdl/lib build_ext/built_sdl_mixer/lib build_ext/fluidsynth-lite/src \ + -name '*.so*' -not -name '*.la' -exec cp -a {} "$OUT_DIR/lib/" \; +cp /opt/prebuilt/soundfont/default.sf2 "$OUT_DIR/soundfont.sf2" + +echo "== Done ==" +echo "Binary: $OUT_DIR/systemshock" +echo "Libraries: $OUT_DIR/lib/" +echo "Soundfont: $OUT_DIR/soundfont.sf2" diff --git a/build-image/docker-entrypoint.sh b/build-image/docker-entrypoint.sh new file mode 100755 index 0000000..810de83 --- /dev/null +++ b/build-image/docker-entrypoint.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# Drops from root (needed to read the prebuilt toolchain under /opt) to a +# user matching the host's UID/GID, so anything written into the +# bind-mounted repo (engine/build_ext, engine/.build-output, ...) is owned +# by the host user instead of root. +set -euo pipefail + +USER_UID="${HOST_UID:-1000}" +USER_GID="${HOST_GID:-1000}" + +groupadd -g "$USER_GID" builder 2>/dev/null || true +useradd -u "$USER_UID" -g "$USER_GID" -m -s /bin/bash builder 2>/dev/null || true + +exec gosu builder "$@" diff --git a/engine/.clang-format b/engine/.clang-format new file mode 100644 index 0000000..158756b --- /dev/null +++ b/engine/.clang-format @@ -0,0 +1,108 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 120 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 8 +UseTab: Never +... + diff --git a/engine/.gitignore b/engine/.gitignore new file mode 100644 index 0000000..75db289 --- /dev/null +++ b/engine/.gitignore @@ -0,0 +1,61 @@ +/RES/ +build +CMakeLists.txt.user +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# CMake +CMakeFiles +src/Libraries/CMakeFiles + +**/CMakeCache.txt +**/cmake_install.cmake +**/Makefile + +TestSimpleMain +**/core + +# 3rd Party builds +build_ext + +# Save files +CurrentGame.dat +savgam*.dat + +# Temporary audio files +.temp.audio + +# Eclipse CDT +.project +.cproject +.DS_Store diff --git a/engine/.travis.yml b/engine/.travis.yml new file mode 100644 index 0000000..8f5cd7a --- /dev/null +++ b/engine/.travis.yml @@ -0,0 +1,83 @@ +language: c + +notifications: + email: false + +matrix: + include: + - os: linux + dist: bionic + sudo: required + env: + - SDL2_LIB=BUNDLED + - SDL2_MIXER_LIB=BUNDLED + - FLUIDSYNTH_LIB=BUNDLED + - BITS=64 + addons: + apt: + packages: + - cmake-data cmake libglu1-mesa-dev libgl1-mesa-dev # libfluidsynth-dev libsdl2-dev libsdl2-mixer-dev + compiler: gcc + - os: linux + dist: trusty + sudo: required + env: + - SDL2_LIB=BUNDLED + - SDL2_MIXER_LIB=BUNDLED + - FLUIDSYNTH_LIB=BUNDLED + - CMAKE_LIBRARY_PATH=/usr/lib/i386-linux-gnu + - BITS=32 + before_script: + - cp ./CMakeLists.32bit.txt ./CMakeLists.txt + addons: + apt: + packages: + - cmake-data cmake libx32gcc-4.8-dev libc6-dev-i386 gcc-multilib g++-multilib libglu1-mesa-dev:i386 libgl1-mesa-dev:i386 + compiler: gcc + - os: osx + compiler: clang + env: + - SDL2_LIB=BUNDLED + - SDL2_MIXER_LIB=BUNDLED + - FLUIDSYNTH_LIB=OFF # Bundled lib failed to compile + - BITS=64 + - os: osx + compiler: gcc + env: + - SDL2_LIB=BUNDLED + - SDL2_MIXER_LIB=BUNDLED + - FLUIDSYNTH_LIB=OFF # Bundled lib failed to compile + - BITS=64 + +script: + - chmod a+rx ./osx-linux/*.sh + - sudo TRAVIS=$TRAVIS ./osx-linux/install_${BITS}bit_sdl.sh + - cmake -DENABLE_SDL2=${SDL2_LIB} -DENABLE_SOUND=${SDL2_MIXER_LIB} -DENABLE_FLUIDSYNTH=${FLUIDSYNTH_LIB} . + - make -j2 systemshock + +before_deploy: + - mkdir -p shockolate + - cp systemshock shockolate + - cp osx-linux/install_${BITS}bit_sdl.sh shockolate/install_sdl.sh + - cp osx-linux/readme_osx_linux.md shockolate + - cp osx-linux/run_$TRAVIS_OS_NAME.sh shockolate/run.sh + - cp -r shaders shockolate/ + - cp -r res shockolate/ + - export PACKAGE_NAME="shockolate-$TRAVIS_OS_NAME-${BITS}bit.tgz" + - tar zcfv $PACKAGE_NAME shockolate + - rm -r shockolate + - mkdir -p shockolate-source + - cp -r systemshock/* shockolate-source/ + - tar zcvf shockolate-source-$TRAVIS_TAG.tar.gz shockolate-source + - rm -r shockolate-source + +deploy: + provider: releases + skip_cleanup: true + overwrite: true + api_key: + secure: "M8fgLU06LQHZS7cf98dHi8bl0BPsyvQHHDWqBaiWqnOoC4XET4fYibFf9B9Ba64RYw5DqPCGbf2onYcDrrUq0cZBwbJsJoVmajKOsWiPzddAtJrk2/nle0MWtjt6OdwbHtg0dNs36QmQ7oRxrEmQaodMmnQW0PKCZOhMmT2zdU73r9ZJ0g4kkkmAAHgfLWYPkfSb9gMj0bn5BLwwGPXv9+NeDFxVG4DY4qjEqQES9tjabVSbVNHretkFCLr0rCpGDQnEZHCP3Wt5c6MoSRunZbRg0X+IwiI1xCEchw2VQFBQiKZ3D4nJIyrZ96iijUQRnnKz5aoMZXQJZQEsnTaZLM+ZbYnK6iA5KWorILdh1odFhNfUJsvWEmEGlrrIQ9qzcAJaIFFch0HRY1S8+gGOy9tEoIpr0VWNZLg8lJvkiQgQmARrt9O+4wIzXZmQnNQcU/N3nWakI68CND4UWk4xAfA6k/Mq2IWyVu477lYxEN+FcqT7EbpowovOOn7e1rutwKDUtb3jWBHZBESF5TCL/hdwdOGNITaV+ENTHbHbWvK6J+3+sCK62xG0/pqzJk3+j7R9zoDvz+htwse/hhk/F3Sa+MpaJQVqtKVD4nBlY/E7+qd3yCyXQm916V+04evjoKOqXXnOOe5d+a87OquLJ6UaynjRsq6lJY2kZ2/Irmc=" + file: "$PACKAGE_NAME" + on: + tags: true + repo: Interrupt/systemshock diff --git a/engine/CMakeLists.32bit.txt b/engine/CMakeLists.32bit.txt new file mode 100644 index 0000000..8c76b7f --- /dev/null +++ b/engine/CMakeLists.32bit.txt @@ -0,0 +1,423 @@ +cmake_minimum_required(VERSION 3.1) + +project(shockolate VERSION 0.7.8) + +include(FeatureSummary) + +set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS OFF) +set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB32_PATHS ON) + +# Required for stdbool.h +set(CMAKE_C_STANDARD 99) +# For nullptr in C++ +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -m32 ") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -m32 -D__STDC_LIMIT_MACROS") + +option(ENABLE_EXAMPLES "Enable example applications" OFF) +add_feature_info(ENABLE_EXAMPLES ENABLE_EXAMPLES "Enable example application (can be broken!)") +option(ENABLE_DEBUG_BLIT "Enable debugging blitter" OFF) +add_feature_info(ENABLE_DEBUG_BLIT ENABLE_DEBUG_BLIT "Enable debugging blitter") +option(ENABLE_OPENGL "Enable OpenGL support" ON) +add_feature_info(ENABLE_OPENGL ENABLE_OPENGL "Enable OpenGL support") + +set(ENABLE_SDL2 "BUNDLED" CACHE STRING "Enable SDL2 (ON/BUNDLED, default BUNDLED)") +set_property(CACHE ENABLE_SDL2 PROPERTY STRINGS "ON" "BUNDLED") +add_feature_info(ENABLE_SDL2 ENABLE_SOUND "Enable SDL2 support") + +set(ENABLE_SOUND "BUNDLED" CACHE STRING "Enable sound support (requires SDL2_mixer) (ON/BUNDLED/OFF, default BUNDLED)") +set_property(CACHE ENABLE_SOUND PROPERTY STRINGS "ON" "BUNDLED" "OFF") +add_feature_info(ENABLE_SOUND ENABLE_SOUND "Enable sound support (requires SDL2_mixer)") + +set(ENABLE_FLUIDSYNTH "BUNDLED" CACHE STRING "Enable FluidSynth MIDI support (ON/BUNDLED/OFF, default BUNDLED)") +set_property(CACHE ENABLE_FLUIDSYNTH PROPERTY STRINGS "ON" "BUNDLED" "OFF") +add_feature_info(ENABLE_FLUIDSYNTH ENABLE_FLUIDSYNTH "Enable FluidSynth MIDI support") + +# HAAAAX!! +add_definitions(-DSVGA_SUPPORT) + +if(ENABLE_DEBUG_BLIT) + add_definitions(-DDEBUGGING_BLIT) +endif() + +add_compile_options(-fsigned-char -fno-strict-aliasing) + +# Find OpenGL +if(ENABLE_OPENGL) + find_package(OpenGL REQUIRED) + add_definitions(-DUSE_OPENGL) + if(WIN32) + list(APPEND OPENGL_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/build_ext/built_glew/include) + list(APPEND OPENGL_LIBRARIES ${CMAKE_SOURCE_DIR}/build_ext/built_glew/lib/libglew32.dll.a winmm) + endif(WIN32) +endif(ENABLE_OPENGL) + +if(ENABLE_SDL2 MATCHES "ON") + find_package(SDL2 REQUIRED) + if(SDL2_FOUND) + message(STATUS "SDL2 found: ${SDL2_INCLUDE_DIRS} ${SDL2_LIBRARIES}") + endif(SDL2_FOUND) +endif(ENABLE_SDL2 MATCHES "ON") +if(ENABLE_SDL2 MATCHES "BUNDLED") + set(SDL2_DIR ${CMAKE_SOURCE_DIR}/build_ext/built_sdl) + find_library(SDL2_LIBRARY SDL2 PATHS ${SDL2_DIR}/lib NO_DEFAULT_PATH) + find_library(SDL2MAIN_LIBRARY SDL2main PATHS ${SDL2_DIR}/lib NO_DEFAULT_PATH) + set(SDL2_INCLUDE_DIRS ${SDL2_DIR}/include/SDL2) + set(SDL2_LIBRARIES "${SDL2MAIN_LIBRARY};${SDL2_LIBRARY}") +endif(ENABLE_SDL2 MATCHES "BUNDLED") + +if(ENABLE_SOUND MATCHES "ON") + # FIXME applies only for *nix systems + find_package(PkgConfig) + pkg_check_modules(SDL2_MIXER REQUIRED SDL2_mixer>=2.0.4) + add_definitions(-DUSE_SDL_MIXER=1) +endif(ENABLE_SOUND MATCHES "ON") +if(ENABLE_SOUND MATCHES "BUNDLED") + set(SDL2_MIXER_DIR ${CMAKE_SOURCE_DIR}/build_ext/built_sdl_mixer) + set(SDL2_MIXER_INCLUDE_DIRS ${SDL2_MIXER_DIR}/include/SDL2) + find_library(SDL2_MIXER_LIBRARY SDL2_mixer PATHS ${SDL2_MIXER_DIR}/lib) + set(SDL2_MIXER_LIBRARIES ${SDL2_MIXER_LIBRARY}) + add_definitions(-DUSE_SDL_MIXER=1) +endif(ENABLE_SOUND MATCHES "BUNDLED") + +if(ENABLE_FLUIDSYNTH MATCHES "ON") + find_package(PkgConfig) + pkg_check_modules(FLUIDSYNTH REQUIRED fluidsynth) + add_definitions("-DUSE_FLUIDSYNTH=1") +endif(ENABLE_FLUIDSYNTH MATCHES "ON") +if(ENABLE_FLUIDSYNTH MATCHES "BUNDLED") + find_library(FLUIDSYNTH_LIBRARY fluidsynth PATHS ${CMAKE_SOURCE_DIR}/build_ext/fluidsynth-lite/src) + set(FLUIDSYNTH_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/build_ext/fluidsynth-lite/include) + set(FLUIDSYNTH_LIBRARIES ${FLUIDSYNTH_LIBRARY}) + add_definitions("-DUSE_FLUIDSYNTH=1") +endif(ENABLE_FLUIDSYNTH MATCHES "BUNDLED") + +include_directories( + ${SDL2_INCLUDE_DIRS} + ${SDL2_MIXER_INCLUDE_DIRS} + ${FLUIDSYNTH_INCLUDE_DIRS} + ${OPENGL_INCLUDE_DIRS} +) + +if(NOT WIN32) + # Find ALSA for Linux native MIDI + # NOTE: this seems to require having 64-bit dev pacakges installed when building + # on 64-bit OS, even when building a 32-bit binary + find_package(ALSA) + if(ALSA_FOUND) + message(STATUS "ALSA found") + include_directories(${ALSA_INCLUDE_DIRS}) + add_definitions(-DUSE_ALSA=1) + endif(ALSA_FOUND) +endif(NOT WIN32) + +# Generate version based on project version +set(PROJECT_REVERSION_STRING "") +find_package(Git) +if(GIT_FOUND) + execute_process(COMMAND ${GIT_EXECUTABLE} describe --dirty + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE git_describe_out + ERROR_VARIABLE git_describe_error + RESULT_VARIABLE git_describe_result + ) + string(REGEX MATCH "[a-z|0-9|.]*-[0-9]*-g([a-z|0-9]*)([-|a-z]*)" git_commit "${git_describe_out}") + set(git_commit ${CMAKE_MATCH_1}) + set(git_dirty ${CMAKE_MATCH_2}) + set(PROJECT_REVERSION_STRING "-g${git_commit}${git_dirty}") +endif(GIT_FOUND) + +message(STATUS "Version is ${PROJECT_VERSION}${PROJECT_REVERSION_STRING}") +configure_file("${CMAKE_SOURCE_DIR}/src/GameSrc/Headers/shockolate_version.h.in" + "${CMAKE_BINARY_DIR}/src/GameSrc/Headers/shockolate_version.h" ) + +# Configuration done. Print features +feature_summary(WHAT ENABLED_FEATURES DESCRIPTION "Enabled features:") +feature_summary(WHAT DISABLED_FEATURES DESCRIPTION "Disabled features:") + +# Sources configuration +add_subdirectory(src/Libraries/) + +set(MAC_SRC + src/MacSrc/ShockBitmap.c + src/MacSrc/InitMac.c + src/MacSrc/Shock.c + src/MacSrc/Prefs.c + src/MacSrc/MacTune.c + src/MacSrc/SDLSound.c + src/MacSrc/Modding.c + src/MacSrc/OpenGL.cc + src/MacSrc/Xmi.c + src/MusicSrc/MusicDevice.c +) + +set(GAME_SRC + src/GameSrc/ai.c + src/GameSrc/airupt.c + src/GameSrc/amap.c + src/GameSrc/amaploop.c + src/GameSrc/ammomfd.c + src/GameSrc/anim.c + src/GameSrc/archiveformat.c + src/GameSrc/audiolog.c + src/GameSrc/automap.c + src/GameSrc/bark.c + src/GameSrc/biohelp.c + src/GameSrc/cardmfd.c + src/GameSrc/citres.c + src/GameSrc/combat.c + src/GameSrc/cone.c + src/GameSrc/criterr.c + src/GameSrc/cyber.c + src/GameSrc/cybermfd.c + src/GameSrc/cybmem.c + src/GameSrc/cybrnd.c + src/GameSrc/cutsloop.c + src/GameSrc/damage.c + src/GameSrc/digifx.c + src/GameSrc/drugs.c + src/GameSrc/effect.c + src/GameSrc/email.c + src/GameSrc/faceobj.c + src/GameSrc/fixtrmfd.c + src/GameSrc/frcamera.c + src/GameSrc/frclip.c + src/GameSrc/frcompil.c + src/GameSrc/frmain.c + src/GameSrc/frobj.c + src/GameSrc/froslew.c + src/GameSrc/frpipe.c + src/GameSrc/frpts.c + src/GameSrc/frsetup.c + src/GameSrc/frtables.c + src/GameSrc/frterr.c + src/GameSrc/frutil.c + src/GameSrc/FrUtils.c + src/GameSrc/fullamap.c + src/GameSrc/fullscrn.c + src/GameSrc/gameloop.c + src/GameSrc/gameobj.c + src/GameSrc/gamesort.c + src/GameSrc/gamestrn.c + src/GameSrc/gamesys.c + src/GameSrc/gametime.c + src/GameSrc/gamewrap.c + src/GameSrc/gearmfd.c + src/GameSrc/gr2ss.c + src/GameSrc/grenades.c + src/GameSrc/hand.c + src/GameSrc/hflip.c + src/GameSrc/hkeyfunc.c + src/GameSrc/hud.c + src/GameSrc/hudobj.c + src/GameSrc/init.c + src/GameSrc/input.c + src/GameSrc/invent.c + src/GameSrc/leanmetr.c + src/GameSrc/mainloop.c + src/GameSrc/map.c + src/GameSrc/mfdfunc.c + src/GameSrc/mfdgadg.c + src/GameSrc/mfdgames.c + src/GameSrc/mfdgump.c + src/GameSrc/mfdpanel.c + src/GameSrc/minimax.c + src/GameSrc/mlimbs.c + src/GameSrc/movekeys.c + src/GameSrc/musicai.c + src/GameSrc/newai.c + src/GameSrc/newmfd.c + src/GameSrc/objapp.c + src/GameSrc/objects.c + src/GameSrc/objload.c + src/GameSrc/objprop.c + src/GameSrc/objsim.c + src/GameSrc/objuse.c + src/GameSrc/olh.c + src/GameSrc/olhscan.c + src/GameSrc/palfx.c + src/GameSrc/pathfind.c + src/GameSrc/physics.c + src/GameSrc/player.c + src/GameSrc/plotware.c + src/GameSrc/popups.c + src/GameSrc/render.c + src/GameSrc/rendtool.c + src/GameSrc/saveload.c + src/GameSrc/schedule.c + src/GameSrc/screen.c + src/GameSrc/setup.c + src/GameSrc/shodan.c + src/GameSrc/sideicon.c + src/GameSrc/sndcall.c + src/GameSrc/star.c + src/GameSrc/statics.c + src/GameSrc/status.c + src/GameSrc/target.c + src/GameSrc/textmaps.c + src/GameSrc/tickcount.c + src/GameSrc/tfdirect.c + src/GameSrc/tfutil.c + src/GameSrc/tools.c + src/GameSrc/trigger.c + src/GameSrc/view360.c + src/GameSrc/viewhelp.c + src/GameSrc/vitals.c + src/GameSrc/vmail.c + src/GameSrc/wares.c + src/GameSrc/weapons.c + src/GameSrc/wrapper.c + src/GameSrc/gamerend.c + src/GameSrc/mouselook.c +) + +include_directories( + BEFORE + src/Libraries/2D/Source + src/Libraries/3D/Source + src/Libraries/AFILE/Source + src/Libraries/DSTRUCT/Source + src/Libraries/EDMS/Source + src/Libraries/FIXPP/Source + src/Libraries/INPUT/Source + src/Libraries/PALETTE/Source + src/Libraries/RES/Source + src/Libraries/UI/Source + src/Libraries/RND/Source + src/Libraries/VOX/Source + src/Libraries/FIX/Source + src/Libraries/SND/Source + src/Libraries/H + src/Libraries/LG/Source + src/Libraries/LG/Source/LOG/src + src/Libraries/adlmidi/include + src/GameSrc/Headers + src/MacSrc + src/MusicSrc + ${CMAKE_BINARY_DIR}/src/GameSrc/Headers +) + +if(ENABLE_EXAMPLES) + +add_executable(playmov + src/Libraries/AFILE/Tests/playmov.c +) +add_executable(movinfo + src/Libraries/AFILE/Tests/movinfo.c +) + +target_link_libraries(playmov + AFILE_LIB + FIX_LIB + ${SDL2_LIBRARIES} + ${SDL2_MIXER_LIBRARIES} +) + +target_link_libraries(movinfo + AFILE_LIB + FIX_LIB +) + +add_executable(TestSimpleMain + src/Libraries/2D/TestSource/SimpleMain.c +) + +target_link_libraries(TestSimpleMain + 2D_LIB + GR_LIB + 3D_LIB + RES_LIB + FIX_LIB + LG_LIB + ${SDL2_LIBRARIES} +) + +add_executable(BoxTest + src/Libraries/3D/Tests/BoxTest.c +) + +target_link_libraries(BoxTest + 2D_LIB + GR_LIB + 3D_LIB + RES_LIB + FIX_LIB + LG_LIB + ${SDL2_LIBRARIES} +) + +add_executable(BitmapTest + src/Libraries/3D/Tests/BitmapTest.c +) + +target_link_libraries(BitmapTest + 2D_LIB + GR_LIB + 3D_LIB + RES_LIB + FIX_LIB + LG_LIB + ${SDL2_LIBRARIES} +) + +add_executable(FixTest + src/Libraries/FIX/Tests/FixTest/fixtest.c +) + +target_link_libraries(FixTest + FIX_LIB + LG_LIB +) + +endif() + +# Include magic header file, set struct packing size +add_definitions( + -include precompiled.h +) + +add_executable(systemshock + ${MAC_SRC} +) + +add_library(GAME_LIB ${GAME_SRC}) + +# MINGW additional linker options +if(MINGW) + set(WINDOWS_LIBRARIES "mingw32 -mwindows") +endif(MINGW) + +target_link_libraries(systemshock + ${WINDOWS_LIBRARIES} # Set it before any linker options! Beware WinMain@16 error!! + GAME_LIB + UI_LIB + 2D_LIB + LG_LIB + GR_LIB + 3D_LIB + RND_LIB + AFILE_LIB + DSTRUCT_LIB + FIX_LIB + INPUT_LIB + PALETTE_LIB + RES_LIB +# SND_LIB + VOX_LIB + EDMS_LIB + FIXPP_LIB + ADLMIDI_LIB + ${SDL2_LIBRARIES} + ${SDL2_MIXER_LIBRARIES} + ${FLUIDSYNTH_LIBRARIES} + ${OPENGL_LIBRARIES} + ${ALSA_LIBRARIES} +) + +# Turn on address sanitizing if wanted +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/externals/sanitizers/cmake" ${CMAKE_MODULE_PATH}) +find_package(Sanitizers) +add_sanitizers(systemshock) + diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt new file mode 100644 index 0000000..f240aa8 --- /dev/null +++ b/engine/CMakeLists.txt @@ -0,0 +1,424 @@ +cmake_minimum_required(VERSION 3.1) + +project(shockolate VERSION 0.7.8) + +include(FeatureSummary) + +#set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS OFF) +#set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB32_PATHS ON) +set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB32_PATHS OFF) +set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS ON) + +# Required for stdbool.h +set(CMAKE_C_STANDARD 99) +# For nullptr in C++ +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g ") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -D__STDC_LIMIT_MACROS") + +option(ENABLE_EXAMPLES "Enable example applications" OFF) +add_feature_info(ENABLE_EXAMPLES ENABLE_EXAMPLES "Enable example application (can be broken!)") +option(ENABLE_DEBUG_BLIT "Enable debugging blitter" OFF) +add_feature_info(ENABLE_DEBUG_BLIT ENABLE_DEBUG_BLIT "Enable debugging blitter") +option(ENABLE_OPENGL "Enable OpenGL support" ON) +add_feature_info(ENABLE_OPENGL ENABLE_OPENGL "Enable OpenGL support") + +set(ENABLE_SDL2 "BUNDLED" CACHE STRING "Enable SDL2 (ON/BUNDLED, default BUNDLED)") +set_property(CACHE ENABLE_SDL2 PROPERTY STRINGS "ON" "BUNDLED") +add_feature_info(ENABLE_SDL2 ENABLE_SOUND "Enable SDL2 support") + +set(ENABLE_SOUND "BUNDLED" CACHE STRING "Enable sound support (requires SDL2_mixer) (ON/BUNDLED/OFF, default BUNDLED)") +set_property(CACHE ENABLE_SOUND PROPERTY STRINGS "ON" "BUNDLED" "OFF") +add_feature_info(ENABLE_SOUND ENABLE_SOUND "Enable sound support (requires SDL2_mixer)") + +set(ENABLE_FLUIDSYNTH "BUNDLED" CACHE STRING "Enable FluidSynth MIDI support (ON/BUNDLED/OFF, default BUNDLED)") +set_property(CACHE ENABLE_FLUIDSYNTH PROPERTY STRINGS "ON" "BUNDLED" "OFF") +add_feature_info(ENABLE_FLUIDSYNTH ENABLE_FLUIDSYNTH "Enable FluidSynth MIDI support") + +# HAAAAX!! +add_definitions(-DSVGA_SUPPORT) + +if(ENABLE_DEBUG_BLIT) + add_definitions(-DDEBUGGING_BLIT) +endif() + +add_compile_options(-fsigned-char -fno-strict-aliasing) + +# Find OpenGL +if(ENABLE_OPENGL) + find_package(OpenGL REQUIRED) + add_definitions(-DUSE_OPENGL) + if(WIN32) + list(APPEND OPENGL_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/build_ext/built_glew/include) + list(APPEND OPENGL_LIBRARIES ${CMAKE_SOURCE_DIR}/build_ext/built_glew/lib/libglew32.dll.a winmm) + endif(WIN32) +endif(ENABLE_OPENGL) + +if(ENABLE_SDL2 MATCHES "ON") + find_package(SDL2 REQUIRED) + if(SDL2_FOUND) + message(STATUS "SDL2 found: ${SDL2_INCLUDE_DIRS} ${SDL2_LIBRARIES}") + endif(SDL2_FOUND) +endif(ENABLE_SDL2 MATCHES "ON") +if(ENABLE_SDL2 MATCHES "BUNDLED") + set(SDL2_DIR ${CMAKE_SOURCE_DIR}/build_ext/built_sdl) + find_library(SDL2_LIBRARY SDL2 PATHS ${SDL2_DIR}/lib NO_DEFAULT_PATH) + find_library(SDL2MAIN_LIBRARY SDL2main PATHS ${SDL2_DIR}/lib NO_DEFAULT_PATH) + set(SDL2_INCLUDE_DIRS ${SDL2_DIR}/include/SDL2) + set(SDL2_LIBRARIES "${SDL2MAIN_LIBRARY};${SDL2_LIBRARY}") +endif(ENABLE_SDL2 MATCHES "BUNDLED") + +if(ENABLE_SOUND MATCHES "ON") + # FIXME applies only for *nix systems + find_package(PkgConfig) + pkg_check_modules(SDL2_MIXER REQUIRED SDL2_mixer>=2.0.4) + add_definitions(-DUSE_SDL_MIXER=1) +endif(ENABLE_SOUND MATCHES "ON") +if(ENABLE_SOUND MATCHES "BUNDLED") + set(SDL2_MIXER_DIR ${CMAKE_SOURCE_DIR}/build_ext/built_sdl_mixer) + set(SDL2_MIXER_INCLUDE_DIRS ${SDL2_MIXER_DIR}/include/SDL2) + find_library(SDL2_MIXER_LIBRARY SDL2_mixer PATHS ${SDL2_MIXER_DIR}/lib) + set(SDL2_MIXER_LIBRARIES ${SDL2_MIXER_LIBRARY}) + add_definitions(-DUSE_SDL_MIXER=1) +endif(ENABLE_SOUND MATCHES "BUNDLED") + +if(ENABLE_FLUIDSYNTH MATCHES "ON") + find_package(PkgConfig) + pkg_check_modules(FLUIDSYNTH REQUIRED fluidsynth) + add_definitions("-DUSE_FLUIDSYNTH=1") +endif(ENABLE_FLUIDSYNTH MATCHES "ON") +if(ENABLE_FLUIDSYNTH MATCHES "BUNDLED") + find_library(FLUIDSYNTH_LIBRARY fluidsynth PATHS ${CMAKE_SOURCE_DIR}/build_ext/fluidsynth-lite/src) + set(FLUIDSYNTH_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/build_ext/fluidsynth-lite/include) + set(FLUIDSYNTH_LIBRARIES ${FLUIDSYNTH_LIBRARY}) + add_definitions("-DUSE_FLUIDSYNTH=1") +endif(ENABLE_FLUIDSYNTH MATCHES "BUNDLED") + +include_directories( + ${SDL2_INCLUDE_DIRS} + ${SDL2_MIXER_INCLUDE_DIRS} + ${FLUIDSYNTH_INCLUDE_DIRS} + ${OPENGL_INCLUDE_DIRS} +) + +if(NOT WIN32) + # Find ALSA for Linux native MIDI + # NOTE: this seems to require having 64-bit dev pacakges installed when building + # on 64-bit OS, even when building a 32-bit binary + find_package(ALSA) + if(ALSA_FOUND) + message(STATUS "ALSA found") + include_directories(${ALSA_INCLUDE_DIRS}) + add_definitions(-DUSE_ALSA=1) + endif(ALSA_FOUND) +endif(NOT WIN32) + +# Generate version based on project version +set(PROJECT_REVERSION_STRING "") +find_package(Git) +if(GIT_FOUND) + execute_process(COMMAND ${GIT_EXECUTABLE} describe --dirty + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE git_describe_out + ERROR_VARIABLE git_describe_error + RESULT_VARIABLE git_describe_result + ) + string(REGEX MATCH "[a-z|0-9|.]*-[0-9]*-g([a-z|0-9]*)([-|a-z]*)" git_commit "${git_describe_out}") + set(git_commit ${CMAKE_MATCH_1}) + set(git_dirty ${CMAKE_MATCH_2}) + set(PROJECT_REVERSION_STRING "-g${git_commit}${git_dirty}") +endif(GIT_FOUND) + +message(STATUS "Version is ${PROJECT_VERSION}${PROJECT_REVERSION_STRING}") +configure_file("${CMAKE_SOURCE_DIR}/src/GameSrc/Headers/shockolate_version.h.in" + "${CMAKE_BINARY_DIR}/src/GameSrc/Headers/shockolate_version.h" ) + +# Configuration done. Print features +feature_summary(WHAT ENABLED_FEATURES DESCRIPTION "Enabled features:") +feature_summary(WHAT DISABLED_FEATURES DESCRIPTION "Disabled features:") + +# Sources configuration +add_subdirectory(src/Libraries/) + +set(MAC_SRC + src/MacSrc/ShockBitmap.c + src/MacSrc/InitMac.c + src/MacSrc/Shock.c + src/MacSrc/Prefs.c + src/MacSrc/MacTune.c + src/MacSrc/SDLSound.c + src/MacSrc/Modding.c + src/MacSrc/OpenGL.cc + src/MacSrc/Xmi.c + src/MusicSrc/MusicDevice.c +) + +set(GAME_SRC + src/GameSrc/ai.c + src/GameSrc/airupt.c + src/GameSrc/amap.c + src/GameSrc/amaploop.c + src/GameSrc/ammomfd.c + src/GameSrc/anim.c + src/GameSrc/archiveformat.c + src/GameSrc/audiolog.c + src/GameSrc/automap.c + src/GameSrc/bark.c + src/GameSrc/biohelp.c + src/GameSrc/cardmfd.c + src/GameSrc/citres.c + src/GameSrc/combat.c + src/GameSrc/cone.c + src/GameSrc/criterr.c + src/GameSrc/cyber.c + src/GameSrc/cybermfd.c + src/GameSrc/cybmem.c + src/GameSrc/cybrnd.c + src/GameSrc/cutsloop.c + src/GameSrc/damage.c + src/GameSrc/digifx.c + src/GameSrc/drugs.c + src/GameSrc/effect.c + src/GameSrc/email.c + src/GameSrc/faceobj.c + src/GameSrc/fixtrmfd.c + src/GameSrc/frcamera.c + src/GameSrc/frclip.c + src/GameSrc/frcompil.c + src/GameSrc/frmain.c + src/GameSrc/frobj.c + src/GameSrc/froslew.c + src/GameSrc/frpipe.c + src/GameSrc/frpts.c + src/GameSrc/frsetup.c + src/GameSrc/frtables.c + src/GameSrc/frterr.c + src/GameSrc/frutil.c + src/GameSrc/FrUtils.c + src/GameSrc/fullamap.c + src/GameSrc/fullscrn.c + src/GameSrc/gameloop.c + src/GameSrc/gameobj.c + src/GameSrc/gamesort.c + src/GameSrc/gamestrn.c + src/GameSrc/gamesys.c + src/GameSrc/gametime.c + src/GameSrc/gamewrap.c + src/GameSrc/gearmfd.c + src/GameSrc/gr2ss.c + src/GameSrc/grenades.c + src/GameSrc/hand.c + src/GameSrc/hflip.c + src/GameSrc/hkeyfunc.c + src/GameSrc/hud.c + src/GameSrc/hudobj.c + src/GameSrc/init.c + src/GameSrc/input.c + src/GameSrc/invent.c + src/GameSrc/leanmetr.c + src/GameSrc/mainloop.c + src/GameSrc/map.c + src/GameSrc/mfdfunc.c + src/GameSrc/mfdgadg.c + src/GameSrc/mfdgames.c + src/GameSrc/mfdgump.c + src/GameSrc/mfdpanel.c + src/GameSrc/minimax.c + src/GameSrc/mlimbs.c + src/GameSrc/movekeys.c + src/GameSrc/musicai.c + src/GameSrc/newai.c + src/GameSrc/newmfd.c + src/GameSrc/objapp.c + src/GameSrc/objects.c + src/GameSrc/objload.c + src/GameSrc/objprop.c + src/GameSrc/objsim.c + src/GameSrc/objuse.c + src/GameSrc/olh.c + src/GameSrc/olhscan.c + src/GameSrc/palfx.c + src/GameSrc/pathfind.c + src/GameSrc/physics.c + src/GameSrc/player.c + src/GameSrc/plotware.c + src/GameSrc/popups.c + src/GameSrc/render.c + src/GameSrc/rendtool.c + src/GameSrc/saveload.c + src/GameSrc/schedule.c + src/GameSrc/screen.c + src/GameSrc/setup.c + src/GameSrc/shodan.c + src/GameSrc/sideicon.c + src/GameSrc/sndcall.c + src/GameSrc/star.c + src/GameSrc/statics.c + src/GameSrc/status.c + src/GameSrc/target.c + src/GameSrc/textmaps.c + src/GameSrc/tickcount.c + src/GameSrc/tfdirect.c + src/GameSrc/tfutil.c + src/GameSrc/tools.c + src/GameSrc/trigger.c + src/GameSrc/view360.c + src/GameSrc/viewhelp.c + src/GameSrc/vitals.c + src/GameSrc/vmail.c + src/GameSrc/wares.c + src/GameSrc/weapons.c + src/GameSrc/wrapper.c + src/GameSrc/gamerend.c + src/GameSrc/mouselook.c +) + +include_directories( + BEFORE + src/Libraries/2D/Source + src/Libraries/3D/Source + src/Libraries/AFILE/Source + src/Libraries/DSTRUCT/Source + src/Libraries/EDMS/Source + src/Libraries/FIXPP/Source + src/Libraries/INPUT/Source + src/Libraries/PALETTE/Source + src/Libraries/RES/Source + src/Libraries/UI/Source + src/Libraries/RND/Source + src/Libraries/VOX/Source + src/Libraries/FIX/Source + src/Libraries/SND/Source + src/Libraries/H + src/Libraries/LG/Source + src/Libraries/LG/Source/LOG/src + src/Libraries/adlmidi/include + src/GameSrc/Headers + src/MacSrc + src/MusicSrc + ${CMAKE_BINARY_DIR}/src/GameSrc/Headers +) + +if(ENABLE_EXAMPLES) + +add_executable(playmov + src/Libraries/AFILE/Tests/playmov.c +) +add_executable(movinfo + src/Libraries/AFILE/Tests/movinfo.c +) + +target_link_libraries(playmov + AFILE_LIB + FIX_LIB + ${SDL2_LIBRARIES} + ${SDL2_MIXER_LIBRARIES} +) + +target_link_libraries(movinfo + AFILE_LIB + FIX_LIB +) + +add_executable(TestSimpleMain + src/Libraries/2D/TestSource/SimpleMain.c +) + +target_link_libraries(TestSimpleMain + 2D_LIB + GR_LIB + 3D_LIB + RES_LIB + FIX_LIB + LG_LIB + ${SDL2_LIBRARIES} +) + +add_executable(BoxTest + src/Libraries/3D/Tests/BoxTest.c +) + +target_link_libraries(BoxTest + 2D_LIB + GR_LIB + 3D_LIB + RES_LIB + FIX_LIB + LG_LIB + ${SDL2_LIBRARIES} +) + +add_executable(BitmapTest + src/Libraries/3D/Tests/BitmapTest.c +) + +target_link_libraries(BitmapTest + 2D_LIB + GR_LIB + 3D_LIB + RES_LIB + FIX_LIB + LG_LIB + ${SDL2_LIBRARIES} +) + +add_executable(FixTest + src/Libraries/FIX/Tests/FixTest/fixtest.c +) + +target_link_libraries(FixTest + FIX_LIB + LG_LIB +) + +endif() + +# Include magic header file, set struct packing size +add_definitions( + -include precompiled.h +) + +add_executable(systemshock + ${MAC_SRC} +) + +add_library(GAME_LIB ${GAME_SRC}) + +# MINGW additional linker options +if(MINGW) + set(WINDOWS_LIBRARIES "mingw32 -mwindows") +endif(MINGW) + +target_link_libraries(systemshock + ${WINDOWS_LIBRARIES} # Set it before any linker options! Beware WinMain@16 error!! + GAME_LIB + UI_LIB + 2D_LIB + LG_LIB + GR_LIB + 3D_LIB + RND_LIB + AFILE_LIB + DSTRUCT_LIB + FIX_LIB + INPUT_LIB + PALETTE_LIB + RES_LIB +# SND_LIB + VOX_LIB + EDMS_LIB + FIXPP_LIB + ADLMIDI_LIB + ${SDL2_LIBRARIES} + ${SDL2_MIXER_LIBRARIES} + ${FLUIDSYNTH_LIBRARIES} + ${OPENGL_LIBRARIES} + ${ALSA_LIBRARIES} +) + +# Turn on address sanitizing if wanted +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/externals/sanitizers/cmake" ${CMAKE_MODULE_PATH}) +find_package(Sanitizers) +add_sanitizers(systemshock) diff --git a/engine/COPYING.txt b/engine/COPYING.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/engine/COPYING.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/engine/LICENSE b/engine/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/engine/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/engine/README.md b/engine/README.md new file mode 100644 index 0000000..59b2ba0 --- /dev/null +++ b/engine/README.md @@ -0,0 +1,88 @@ +Shockolate - System Shock, but cross platform! +============================ +Based on the source code for PowerPC released by Night Dive Studios, Incorporated. + +[![Build Status TravisCI](https://travis-ci.org/Interrupt/systemshock.svg?branch=master)](https://travis-ci.org/Interrupt/systemshock) [![Build Status AppVeyor](https://ci.appveyor.com/api/projects/status/5fmcswq8n7ni0o9j/branch/master?svg=true)](https://ci.appveyor.com/project/Interrupt/systemshock) + +GENERAL NOTES +============= + +Shockolate is a cross platform source port of System Shock, using SDL2. This runs well on OSX, Linux, and Windows right now, with some missing features that need reviving due to not being included in the source code that was released. + +The end goal for this project is something like what Chocolate Doom is for Doom: an experience that closely mimics the original, but portable and with some quality of life improvements including an OpenGL renderer and mod support! + +Join our Discord to follow along with development: https://discord.gg/m45xPan + +![work so far](https://i.imgur.com/kbVWQj4.gif) + +Prerequisites +======= + - Original cd-rom or SS:EE assets in a `res/data` folder next to the executable + - Floppy disk assets are an older version that we can't load currently + + +Running +======= + +## From a prebuilt package + +Find a list of [downloadable packages](https://github.com/Interrupt/systemshock/releases/) for Linux, Mac and Windows. 32 and 64 bit versions are available for Linux and Windows. + +## From source code + +Prerequisites: +- [CMake](https://cmake.org/download/) installed + +Step 1. Build the dependencies: +* Windows: `build_win32.sh` or `build_win64.sh` (Git Bash and MinGW recommended) +* Linux/Mac: `build_deps.sh` or the CI build scripts in `osx-linux` +* Other: `build_deps.sh` + +Step 2. Build and run the game itself +``` +cmake . +make systemshock +./systemshock +``` + +The following CMake options are supported in the build process: +* `ENABLE_SDL2` - use system or bundled SDL2 (ON/BUNDLED, default BUNDLED) +* `ENABLE_SOUND` - enable sound support (requires SDL2_mixer, ON/BUNDLED/OFF, default is BUNDLED) +* `ENABLE_FLUIDSYNTH` - enable FluidSynth MIDI support (ON/BUNDLED/OFF, default is BUNDLED) +* `ENABLE_OPENGL` - enable OpenGL support (ON/OFF, default ON) + +If you find yourself needing to modify the build script for Shockolate itself, `CMakeLists.txt` is the place to look into. + + +Command line parameters +============ + +`-nosplash` Disables the splash screens, causes the game to start straight to the main menu + +Modding Support +============ +Shockolate supports loading mods and full on fan missions. Just point the executable at a mod file or folder and the game will load it in. So far mod loading supports additional `.res` and `.dat` files for resources and missions respectively. + +Run a fan mission from a folder: +``` +./systemshock /Path/To/My/Mission +``` + +Run a fan mission from specific files: +``` +./systemshock my-archive.dat my-strings.res +``` + +Control modifications +======= + +## Movement + +Shockolate replaces the original game's movement with WASD controls, and uses `F` as the mouselook toggle hotkey. This differs from the Enhanced Edition's usage of `E` as the mouselook hotkey, but allows us to keep `Q` and `E` available for leaning. + +## Additional hotkeys + +* `Ctrl+G` cycles between graphics rendering modes +* `Ctrl+F` to enable full screen mode +* `Ctrl+D` to disable full screen mode + diff --git a/engine/ShockMac.sit b/engine/ShockMac.sit new file mode 100644 index 0000000..4333189 Binary files /dev/null and b/engine/ShockMac.sit differ diff --git a/engine/appveyor.yml b/engine/appveyor.yml new file mode 100644 index 0000000..cd8d0c9 --- /dev/null +++ b/engine/appveyor.yml @@ -0,0 +1,94 @@ +# Shockolate AppVeyor configuration +# YAML format reference: https://www.appveyor.com/docs/appveyor-yml/ + +# This determines the disk image AppVeyor uses while building +# Despite its name, this image actually contains all sorts of non-VC goodies +# See https://www.appveyor.com/docs/build-environment/ for all the details +image: Visual Studio 2015 + +# Tell build_windows.sh that we're building for AppVeyor +environment: + APPVEYOR: TRUE + +platform: + - x64 + - x86 + +# Avoid rebuilding external dependencies (ie. SDL and SDL_mixer) +# Uncache build_ext if external deps change +cache: + - res/music.sf2 + - build_ext + +# Set up environment variable values for 32 and 64 bit builds + +for: + - + matrix: + only: + - platform: x86 + before_build: + - set BUILD_SCRIPT=build_win32.sh + - set ARTIFACT=systemshock-x86.zip + - set MINGW_PATH=C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin\ + - copy CMakeLists.32bit.txt CMakeLists.txt + - + matrix: + only: + - platform: x64 + before_build: + - set BUILD_SCRIPT=build_win64.sh + - set ARTIFACT=systemshock-x64.zip + - set MINGW_PATH=C:\mingw-w64\x86_64-7.3.0-posix-seh-rt_v5-rev0\mingw64\bin\ + +# Actual build script.. +# Step 1: Git has to reside in a path without spaces because the SDL build script is weird like that. +# So we create a symlink to the real Git, remove it from PATH and add our own. +# Step 2: We need to use our own make.exe to build stuff, so we add that +# Step 3: Do the actual building + +build_script: + - mklink /D c:\git "C:\Program Files\Git" + - set PATH=%PATH:C:\Program Files (x86)\Git\bin;=% + - set PATH=c:\git\usr\bin;%PATH%;%MINGW_PATH% + - copy windows\make.exe \git\usr\bin + - set CMAKE_MAKE_PROGRAM=c:\git\usr\bin\make.exe + - sh %BUILD_SCRIPT% + - build.bat + + +# For now, we don't have any automatic tests to run +test: off + +# Once building is done, we gather all the necessary DLL files and build our ZIP file. +after_build: + - copy %MINGW_PATH%\libgcc*.dll . + - copy %MINGW_PATH%\libstd*.dll . + - copy %MINGW_PATH%\libwinpthread-1.dll . + - copy build_ext\built_sdl\bin\SDL*.dll . + - copy build_ext\built_sdl_mixer\bin\SDL*.dll . + - copy build_ext\built_glew\lib\glew32.dll . + - copy build_ext\fluidsynth-lite\src\libfluidsynth.dll . + - 7z a %ARTIFACT% systemshock.exe *.dll shaders/ res/ + +artifacts: + - path: systemshock-x86.zip + name: Shockolate (32bit) + - path: systemshock-x64.zip + name: Shockolate (64bit) + +version: '0.5.{build}' + +# Finally, we deploy the ZIP file as a GitHub release +# FIXME: How do we want to be building releases? Seems like this would be better as a manual process +# TODO: invent a better versioning scheme for the tag name + +deploy: + - provider: GitHub + release: $(appveyor_repo_tag_name) + description: 'Latest release build of Shockolate' + artifact: systemshock-x86.zip, systemshock-x64.zip + auth_token: + secure: P1kIk8rRxKAYHqDrOWf0Zf5spfR+N86E0lO8VBu99vHtECJgPLn/Z6tBmL9cxPah + on: + APPVEYOR_REPO_TAG: true diff --git a/engine/build.bat b/engine/build.bat new file mode 100644 index 0000000..5877fc9 --- /dev/null +++ b/engine/build.bat @@ -0,0 +1,4 @@ +@REM Initial build.bat for Appveyor + +cmake -G "Unix Makefiles" . +make -j2 systemshock diff --git a/engine/build_deps.sh b/engine/build_deps.sh new file mode 100755 index 0000000..df5d128 --- /dev/null +++ b/engine/build_deps.sh @@ -0,0 +1,72 @@ +#!/bin/bash +set -e + +SDL_version=2.0.9 +SDL2_mixer_version=2.0.4 + +if [ -d ./build_ext/ ]; then + echo A directory named build_ext already exists. + echo Please remove it if you want to recompile. + exit +fi + +if [ ! -d ./res/ ]; then + mkdir ./res/ +fi + +mkdir ./build_ext/ +cd ./build_ext/ + +install_dir=$(pwd) + +function build_sdl { + curl -O https://www.libsdl.org/release/SDL2-${SDL_version}.tar.gz + tar xvf SDL2-${SDL_version}.tar.gz + pushd SDL2-${SDL_version} + + ./configure --prefix=${install_dir}/built_sdl + make + make install + + popd +} + +function build_sdl_mixer { + curl -O https://www.libsdl.org/projects/SDL_mixer/release/SDL2_mixer-${SDL2_mixer_version}.tar.gz + tar xvf SDL2_mixer-${SDL2_mixer_version}.tar.gz + pushd SDL2_mixer-${SDL2_mixer_version} + + export SDL2_CONFIG="${install_dir}/built_sdl/bin/sdl2-config" + ./configure --prefix=${install_dir}/built_sdl_mixer + make + make install + + popd +} + +function build_fluidsynth { + git clone https://github.com/EtherTyper/fluidsynth-lite.git + pushd fluidsynth-lite + sed -i 's/DLL"\ off/DLL"\ on/' CMakeLists.txt + # if building fluidsynth fails, move on without it + set +e + + #export CFLAGS="-m32" + #export CXXFLAGS="-m32" + + cmake . + cmake --build . + + # download a soundfont that's close to the Windows default everyone knows + curl -o music.sf2 http://rancid.kapsi.fi/windows.sf2 + set -e + popd +} + + +build_sdl +build_sdl_mixer +build_fluidsynth + +cd .. +mv build_ext/fluidsynth-lite/*.sf2 ./res diff --git a/engine/build_win32.sh b/engine/build_win32.sh new file mode 100644 index 0000000..568faad --- /dev/null +++ b/engine/build_win32.sh @@ -0,0 +1,124 @@ +#!/bin/bash +set -e + +SDL_version=2.0.10 +SDL2_mixer_version=2.0.4 +GLEW_version=2.1.0 +CMAKE_target=Unix\ Makefiles + +# Removing the mwindows linker option lets us get console output +function remove_mwindows { + sed -i -e "s/ \-mwindows//g" Makefile +} + +function build_sdl { + curl -O https://www.libsdl.org/release/SDL2-${SDL_version}.tar.gz + tar xvf SDL2-${SDL_version}.tar.gz + pushd SDL2-${SDL_version} + + ./configure --host=x86_64-w64-mingw32 --prefix=${install_dir}/built_sdl + remove_mwindows + make + make install + + popd +} + +function build_sdl_mixer { + curl -O https://www.libsdl.org/projects/SDL_mixer/release/SDL2_mixer-${SDL2_mixer_version}.tar.gz + # Do not extract the Xcode subdirectory because it contains symlinks. + # They cannot be extracted on Windows because the files in the archive are in the wrong order so + # the target of the link cannot be found at the time of the extraction. + tar xf SDL2_mixer-${SDL2_mixer_version}.tar.gz --exclude=Xcode + pushd SDL2_mixer-${SDL2_mixer_version} + + ./configure --host=x86_64-w64-mingw32 --disable-sdltest --with-sdl-prefix=${install_dir}/built_sdl --prefix=${install_dir}/built_sdl_mixer + + remove_mwindows + make + make install + + popd +} + +function build_glew { + curl -O https://netcologne.dl.sourceforge.net/project/glew/glew/${GLEW_version}/glew-${GLEW_version}.tgz + tar xvf glew-${GLEW_version}.tgz + mv glew-${GLEW_version}/ built_glew/ + pushd built_glew + mingw32-make glew.lib + popd +} + +function build_fluidsynth { + git clone https://github.com/EtherTyper/fluidsynth-lite.git + pushd fluidsynth-lite + sed -i 's/DLL"\ off/DLL"\ on/' CMakeLists.txt + # if building fluidsynth fails, move on without it + set +e + cmake -G "${CMAKE_target}" . + cmake --build . + + # download a soundfont that's close to the Windows default everyone knows + curl -o music.sf2 http://rancid.kapsi.fi/windows.sf2 + set -e + popd +} + +## Actual building starts here + +if [ -d ./build_ext/ ]; then + echo A directory named build_ext already exists. + echo Please remove it if you want to recompile. + exit +fi + +if ! [ -x "$(command -v cmake)" ]; then + echo CMake is needed to install Shockolate. + echo Please download CMake from https://cmake.org/download/, + echo install it and try again in a new Git Bash window. + exit +fi + +rm -rf CMakeFiles/ +rm -rf CMakeCache.txt + +cp windows/make.exe /usr/bin/ + +if [ ! -d ./res/ ]; then +mkdir ./res/ +fi + +mkdir ./build_ext/ +cd ./build_ext/ +install_dir=`pwd -W` + +build_sdl +build_sdl_mixer +build_glew +build_fluidsynth + + +# Back to the root directory, copy required DLL files for the executable +cd .. +cp build_ext/built_sdl/bin/SDL*.dll . +cp build_ext/built_sdl_mixer/bin/SDL*.dll . +cp build_ext/built_glew/lib/*.dll . +cp build_ext/fluidsynth-lite/src/*.dll . + +# move the soundfont to the correct place if we successfully built fluidsynth +mv build_ext/fluidsynth-lite/*.sf2 ./res + +# Set up build.bat +if [[ -z "${APPVEYOR}" ]]; then + echo "Normal build" + echo "@echo off + cmake -G \"${CMAKE_target}\" . + mingw32-make systemshock" >build.bat +else + echo "Appveyor" + echo "cmake -G \"${CMAKE_target}\" . + make systemshock" >build.bat +fi + +echo "Our work here is done. Run BUILD.BAT in a Windows shell to build the actual source." diff --git a/engine/build_win64.sh b/engine/build_win64.sh new file mode 100644 index 0000000..6c6ae47 --- /dev/null +++ b/engine/build_win64.sh @@ -0,0 +1,107 @@ +#!/bin/bash +set -e + +SDL_version=2.0.10 +SDL2_mixer_version=2.0.4 +GLEW_version=2.1.0 +CMAKE_target=Unix\ Makefiles + +# Removing the mwindows linker option lets us get console output +function remove_mwindows { + sed -i -e "s/ \-mwindows//g" Makefile +} + +function build_sdl { + curl -O https://www.libsdl.org/release/SDL2-devel-${SDL_version}-mingw.tar.gz + tar xvf SDL2-devel-${SDL_version}-mingw.tar.gz + cp -r SDL2-${SDL_version}/x86_64-w64-mingw32/ built_sdl/ +} + +function build_sdl_mixer { + curl -O https://www.libsdl.org/projects/SDL_mixer/release/SDL2_mixer-devel-${SDL2_mixer_version}-mingw.tar.gz + tar xf SDL2_mixer-devel-${SDL2_mixer_version}-mingw.tar.gz --exclude=Xcode + cp -r SDL2_mixer-${SDL2_mixer_version}/x86_64-w64-mingw32/ built_sdl_mixer/ +} + +function build_glew { + curl -O https://netcologne.dl.sourceforge.net/project/glew/glew/${GLEW_version}/glew-${GLEW_version}.tgz + tar xvf glew-${GLEW_version}.tgz + mv glew-${GLEW_version}/ built_glew/ + pushd built_glew + mingw32-make glew.lib + popd +} + +function build_fluidsynth { + git clone https://github.com/EtherTyper/fluidsynth-lite.git + pushd fluidsynth-lite + sed -i 's/DLL"\ off/DLL"\ on/' CMakeLists.txt + # if building fluidsynth fails, move on without it + set +e + cmake -G "${CMAKE_target}" . + cmake --build . + + # download a soundfont that's close to the Windows default everyone knows + curl -o music.sf2 http://rancid.kapsi.fi/windows.sf2 + set -e + popd +} + +## Actual building starts here + +if [ -d ./build_ext/ ]; then + echo A directory named build_ext already exists. + echo Please remove it if you want to recompile. + exit +fi + +if ! [ -x "$(command -v cmake)" ]; then + echo CMake is needed to install Shockolate. + echo Please download CMake from https://cmake.org/download/, + echo install it and try again in a new Git Bash window. + exit +fi + +rm -rf CMakeFiles/ +rm -rf CMakeCache.txt + +cp windows/make.exe /usr/bin/ + +if [ ! -d ./res/ ]; then +mkdir ./res/ +fi + +mkdir ./build_ext/ +cd ./build_ext/ +install_dir=`pwd -W` + + +build_sdl +build_sdl_mixer +build_glew +build_fluidsynth + + +# Back to the root directory, copy required DLL files for the executable +cd .. +cp build_ext/built_sdl/bin/SDL*.dll . +cp build_ext/built_sdl_mixer/bin/SDL*.dll . +cp build_ext/built_glew/lib/*.dll . +cp build_ext/fluidsynth-lite/src/*.dll . + +# move the soundfont to the correct place if we successfully built fluidsynth +mv build_ext/fluidsynth-lite/*.sf2 ./res + +# Set up build.bat +if [[ -z "${APPVEYOR}" ]]; then + echo "Normal build" + echo "@echo off + cmake -G \"${CMAKE_target}\" . + mingw32-make systemshock" >build.bat +else + echo "Appveyor" + echo "cmake -G \"${CMAKE_target}\" . + make systemshock" >build.bat +fi + +echo "Our work here is done. Run BUILD.BAT in a Windows shell to build the actual source." diff --git a/engine/externals/sanitizers/.gitignore b/engine/externals/sanitizers/.gitignore new file mode 100644 index 0000000..c4a70a6 --- /dev/null +++ b/engine/externals/sanitizers/.gitignore @@ -0,0 +1,3 @@ +# out-of-source build top-level folders. +build/ +_build/ diff --git a/engine/externals/sanitizers/CMakeLists.txt b/engine/externals/sanitizers/CMakeLists.txt new file mode 100644 index 0000000..d249a4d --- /dev/null +++ b/engine/externals/sanitizers/CMakeLists.txt @@ -0,0 +1,51 @@ +# This file is part of CMake-sanitizers. +# +# 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. +# +# +# Copyright (c) +# 2013-2015 Matt Arsenault +# 2015 RWTH Aachen University, Federal Republic of Germany +# + + +# +# project information +# + +# minimum required cmake version +cmake_minimum_required(VERSION 2.8) + +# project name +project("CMake-sanitizers") + + + +# +# cmake configuration +# +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) + + + +# +# add tests +# +enable_testing() +add_subdirectory(tests) diff --git a/engine/externals/sanitizers/LICENSE b/engine/externals/sanitizers/LICENSE new file mode 100644 index 0000000..2520efd --- /dev/null +++ b/engine/externals/sanitizers/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) + 2013 Matthew Arsenault + 2015-2016 RWTH Aachen University, Federal Republic of Germany + +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. diff --git a/engine/externals/sanitizers/README.md b/engine/externals/sanitizers/README.md new file mode 100644 index 0000000..8df8d17 --- /dev/null +++ b/engine/externals/sanitizers/README.md @@ -0,0 +1,73 @@ +# sanitizers-cmake + + [![](https://img.shields.io/github/issues-raw/arsenm/sanitizers-cmake.svg?style=flat-square)](https://github.com/arsenm/sanitizers-cmake/issues) +[![MIT](http://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE) + +CMake module to enable sanitizers for binary targets. + + +## Include into your project + +To use [FindSanitizers.cmake](cmake/FindSanitizers.cmake), simply add this repository as git submodule into your own repository +```Shell +mkdir externals +git submodule add git://github.com/arsenm/sanitizers-cmake.git externals/sanitizers-cmake +``` +and adding ```externals/sanitizers-cmake/cmake``` to your ```CMAKE_MODULE_PATH``` +```CMake +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/externals/sanitizers-cmake/cmake" ${CMAKE_MODULE_PATH}) +``` + +If you don't use git or dislike submodules you can copy the files in [cmake directory](cmake) into your repository. *Be careful and keep updates in mind!* + +Now you can simply run ```find_package``` in your CMake files: +```CMake +find_package(Sanitizers) +``` + + +## Usage + +You can enable the sanitizers with ``SANITIZE_ADDRESS``, ``SANITIZE_MEMORY``, ``SANITIZE_THREAD`` or ``SANITIZE_UNDEFINED`` options in your CMake configuration. You can do this by passing e.g. ``-DSANITIZE_ADDRESS=On`` on your command line or with your graphical interface. + +If sanitizers are supported by your compiler, the specified targets will be build with sanitizer support. If your compiler has no sanitizing capabilities (I asume intel compiler doesn't) you'll get a warning but CMake will continue processing and sanitizing will simply just be ignored. + +#### Compiler issues + +Different compilers may be using different implementations for sanitizers. If you'll try to sanitize targets with C and Fortran code but don't use gcc & gfortran but clang & gfortran, this will cause linking problems. To avoid this, such problems will be detected and sanitizing will be disabled for these targets. + +Even C only targets may cause problems in certain situations. Some problems have been seen with AddressSanitizer for preloading or dynamic linking. In such cases you may try the ``SANITIZE_LINK_STATIC`` to link sanitizers for gcc static. + + + +## Build targets with sanitizer support + +To enable sanitizer support you simply have to add ``add_sanitizers()`` after defining your target. To provide a sanitizer blacklist file you can use the ``sanitizer_add_blacklist_file()`` function: +```CMake +find_package(Sanitizers) + +sanitizer_add_blacklist_file("blacklist.txt") + +add_executable(some_exe foo.c bar.c) +add_sanitizers(some_exe) + +add_library(some_lib foo.c bar.c) +add_sanitizers(some_lib) +``` + +## Run your application + +The sanitizers check your program, while it's running. In some situations (e.g. LD_PRELOAD your target) it might be required to preload the used AddressSanitizer library first. In this case you may use the ``asan-wrapper`` script defined in ``ASan_WRAPPER`` variable to execute your application with ``${ASan_WRAPPER} myexe arg1 ...``. + + +## Contribute + +Anyone is welcome to contribute. Simply fork this repository, make your changes **in an own branch** and create a pull-request for your change. Please do only one change per pull-request. + +You found a bug? Please fill out an [issue](https://github.com/arsenm/sanitizers-cmake/issues) and include any data to reproduce the bug. + + +#### Contributors + +* [Matt Arsenault](https://github.com/arsenm) +* [Alexander Haase](https://github.com/alehaa) diff --git a/engine/externals/sanitizers/cmake/FindASan.cmake b/engine/externals/sanitizers/cmake/FindASan.cmake new file mode 100644 index 0000000..98ea7cb --- /dev/null +++ b/engine/externals/sanitizers/cmake/FindASan.cmake @@ -0,0 +1,59 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# 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. + +option(SANITIZE_ADDRESS "Enable AddressSanitizer for sanitized targets." Off) + +set(FLAG_CANDIDATES + # Clang 3.2+ use this version. The no-omit-frame-pointer option is optional. + "-g -fsanitize=address -fno-omit-frame-pointer" + "-g -fsanitize=address" + + # Older deprecated flag for ASan + "-g -faddress-sanitizer" +) + + +if (SANITIZE_ADDRESS AND (SANITIZE_THREAD OR SANITIZE_MEMORY)) + message(FATAL_ERROR "AddressSanitizer is not compatible with " + "ThreadSanitizer or MemorySanitizer.") +endif () + + +include(sanitize-helpers) + +if (SANITIZE_ADDRESS) + sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "AddressSanitizer" + "ASan") + + find_program(ASan_WRAPPER "asan-wrapper" PATHS ${CMAKE_MODULE_PATH}) + mark_as_advanced(ASan_WRAPPER) +endif () + +function (add_sanitize_address TARGET) + if (NOT SANITIZE_ADDRESS) + return() + endif () + + sanitizer_add_flags(${TARGET} "AddressSanitizer" "ASan") +endfunction () diff --git a/engine/externals/sanitizers/cmake/FindMSan.cmake b/engine/externals/sanitizers/cmake/FindMSan.cmake new file mode 100644 index 0000000..22d0050 --- /dev/null +++ b/engine/externals/sanitizers/cmake/FindMSan.cmake @@ -0,0 +1,57 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# 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. + +option(SANITIZE_MEMORY "Enable MemorySanitizer for sanitized targets." Off) + +set(FLAG_CANDIDATES + "-g -fsanitize=memory" +) + + +include(sanitize-helpers) + +if (SANITIZE_MEMORY) + if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + message(WARNING "MemorySanitizer disabled for target ${TARGET} because " + "MemorySanitizer is supported for Linux systems only.") + set(SANITIZE_MEMORY Off CACHE BOOL + "Enable MemorySanitizer for sanitized targets." FORCE) + elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) + message(WARNING "MemorySanitizer disabled for target ${TARGET} because " + "MemorySanitizer is supported for 64bit systems only.") + set(SANITIZE_MEMORY Off CACHE BOOL + "Enable MemorySanitizer for sanitized targets." FORCE) + else () + sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "MemorySanitizer" + "MSan") + endif () +endif () + +function (add_sanitize_memory TARGET) + if (NOT SANITIZE_MEMORY) + return() + endif () + + sanitizer_add_flags(${TARGET} "MemorySanitizer" "MSan") +endfunction () diff --git a/engine/externals/sanitizers/cmake/FindSanitizers.cmake b/engine/externals/sanitizers/cmake/FindSanitizers.cmake new file mode 100644 index 0000000..4f586a3 --- /dev/null +++ b/engine/externals/sanitizers/cmake/FindSanitizers.cmake @@ -0,0 +1,87 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# 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. + +# If any of the used compiler is a GNU compiler, add a second option to static +# link against the sanitizers. +option(SANITIZE_LINK_STATIC "Try to link static against sanitizers." Off) + + + + +set(FIND_QUIETLY_FLAG "") +if (DEFINED Sanitizers_FIND_QUIETLY) + set(FIND_QUIETLY_FLAG "QUIET") +endif () + +find_package(ASan ${FIND_QUIETLY_FLAG}) +find_package(TSan ${FIND_QUIETLY_FLAG}) +find_package(MSan ${FIND_QUIETLY_FLAG}) +find_package(UBSan ${FIND_QUIETLY_FLAG}) + + + + +function(sanitizer_add_blacklist_file FILE) + if(NOT IS_ABSOLUTE ${FILE}) + set(FILE "${CMAKE_CURRENT_SOURCE_DIR}/${FILE}") + endif() + get_filename_component(FILE "${FILE}" REALPATH) + + sanitizer_check_compiler_flags("-fsanitize-blacklist=${FILE}" + "SanitizerBlacklist" "SanBlist") +endfunction() + +function(add_sanitizers ...) + # If no sanitizer is enabled, return immediately. + if (NOT (SANITIZE_ADDRESS OR SANITIZE_MEMORY OR SANITIZE_THREAD OR + SANITIZE_UNDEFINED)) + return() + endif () + + foreach (TARGET ${ARGV}) + # Check if this target will be compiled by exactly one compiler. Other- + # wise sanitizers can't be used and a warning should be printed once. + sanitizer_target_compilers(${TARGET} TARGET_COMPILER) + list(LENGTH TARGET_COMPILER NUM_COMPILERS) + if (NUM_COMPILERS GREATER 1) + message(WARNING "Can't use any sanitizers for target ${TARGET}, " + "because it will be compiled by incompatible compilers. " + "Target will be compiled without sanitizers.") + return() + + # If the target is compiled by no known compiler, ignore it. + elseif (NUM_COMPILERS EQUAL 0) + message(WARNING "Can't use any sanitizers for target ${TARGET}, " + "because it uses an unknown compiler. Target will be " + "compiled without sanitizers.") + return() + endif () + + # Add sanitizers for target. + add_sanitize_address(${TARGET}) + add_sanitize_thread(${TARGET}) + add_sanitize_memory(${TARGET}) + add_sanitize_undefined(${TARGET}) + endforeach () +endfunction(add_sanitizers) diff --git a/engine/externals/sanitizers/cmake/FindTSan.cmake b/engine/externals/sanitizers/cmake/FindTSan.cmake new file mode 100644 index 0000000..3cba3c0 --- /dev/null +++ b/engine/externals/sanitizers/cmake/FindTSan.cmake @@ -0,0 +1,65 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# 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. + +option(SANITIZE_THREAD "Enable ThreadSanitizer for sanitized targets." Off) + +set(FLAG_CANDIDATES + "-g -fsanitize=thread" +) + + +# ThreadSanitizer is not compatible with MemorySanitizer. +if (SANITIZE_THREAD AND SANITIZE_MEMORY) + message(FATAL_ERROR "ThreadSanitizer is not compatible with " + "MemorySanitizer.") +endif () + + +include(sanitize-helpers) + +if (SANITIZE_THREAD) + if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND + NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") + message(WARNING "ThreadSanitizer disabled for target ${TARGET} because " + "ThreadSanitizer is supported for Linux systems and macOS only.") + set(SANITIZE_THREAD Off CACHE BOOL + "Enable ThreadSanitizer for sanitized targets." FORCE) + elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) + message(WARNING "ThreadSanitizer disabled for target ${TARGET} because " + "ThreadSanitizer is supported for 64bit systems only.") + set(SANITIZE_THREAD Off CACHE BOOL + "Enable ThreadSanitizer for sanitized targets." FORCE) + else () + sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "ThreadSanitizer" + "TSan") + endif () +endif () + +function (add_sanitize_thread TARGET) + if (NOT SANITIZE_THREAD) + return() + endif () + + sanitizer_add_flags(${TARGET} "ThreadSanitizer" "TSan") +endfunction () diff --git a/engine/externals/sanitizers/cmake/FindUBSan.cmake b/engine/externals/sanitizers/cmake/FindUBSan.cmake new file mode 100644 index 0000000..ae103f7 --- /dev/null +++ b/engine/externals/sanitizers/cmake/FindUBSan.cmake @@ -0,0 +1,46 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# 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. + +option(SANITIZE_UNDEFINED + "Enable UndefinedBehaviorSanitizer for sanitized targets." Off) + +set(FLAG_CANDIDATES + "-g -fsanitize=undefined" +) + + +include(sanitize-helpers) + +if (SANITIZE_UNDEFINED) + sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" + "UndefinedBehaviorSanitizer" "UBSan") +endif () + +function (add_sanitize_undefined TARGET) + if (NOT SANITIZE_UNDEFINED) + return() + endif () + + sanitizer_add_flags(${TARGET} "UndefinedBehaviorSanitizer" "UBSan") +endfunction () diff --git a/engine/externals/sanitizers/cmake/asan-wrapper b/engine/externals/sanitizers/cmake/asan-wrapper new file mode 100755 index 0000000..5d54103 --- /dev/null +++ b/engine/externals/sanitizers/cmake/asan-wrapper @@ -0,0 +1,55 @@ +#!/bin/sh + +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# 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. + +# This script is a wrapper for AddressSanitizer. In some special cases you need +# to preload AddressSanitizer to avoid error messages - e.g. if you're +# preloading another library to your application. At the moment this script will +# only do something, if we're running on a Linux platform. OSX might not be +# affected. + + +# Exit immediately, if platform is not Linux. +if [ "$(uname)" != "Linux" ] +then + exec $@ +fi + + +# Get the used libasan of the application ($1). If a libasan was found, it will +# be prepended to LD_PRELOAD. +libasan=$(ldd $1 | grep libasan | sed "s/^[[:space:]]//" | cut -d' ' -f1) +if [ -n "$libasan" ] +then + if [ -n "$LD_PRELOAD" ] + then + export LD_PRELOAD="$libasan:$LD_PRELOAD" + else + export LD_PRELOAD="$libasan" + fi +fi + +# Execute the application. +exec $@ diff --git a/engine/externals/sanitizers/cmake/sanitize-helpers.cmake b/engine/externals/sanitizers/cmake/sanitize-helpers.cmake new file mode 100644 index 0000000..c51ee6a --- /dev/null +++ b/engine/externals/sanitizers/cmake/sanitize-helpers.cmake @@ -0,0 +1,170 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# 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. + +# Helper function to get the language of a source file. +function (sanitizer_lang_of_source FILE RETURN_VAR) + get_filename_component(FILE_EXT "${FILE}" EXT) + string(TOLOWER "${FILE_EXT}" FILE_EXT) + string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT) + + get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) + foreach (LANG ${ENABLED_LANGUAGES}) + list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP) + if (NOT ${TEMP} EQUAL -1) + set(${RETURN_VAR} "${LANG}" PARENT_SCOPE) + return() + endif () + endforeach() + + set(${RETURN_VAR} "" PARENT_SCOPE) +endfunction () + + +# Helper function to get compilers used by a target. +function (sanitizer_target_compilers TARGET RETURN_VAR) + # Check if all sources for target use the same compiler. If a target uses + # e.g. C and Fortran mixed and uses different compilers (e.g. clang and + # gfortran) this can trigger huge problems, because different compilers may + # use different implementations for sanitizers. + set(BUFFER "") + get_target_property(TSOURCES ${TARGET} SOURCES) + foreach (FILE ${TSOURCES}) + # If expression was found, FILE is a generator-expression for an object + # library. Object libraries will be ignored. + string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE}) + if ("${_file}" STREQUAL "") + sanitizer_lang_of_source(${FILE} LANG) + if (LANG) + list(APPEND BUFFER ${CMAKE_${LANG}_COMPILER_ID}) + endif () + endif () + endforeach () + + list(REMOVE_DUPLICATES BUFFER) + set(${RETURN_VAR} "${BUFFER}" PARENT_SCOPE) +endfunction () + + +# Helper function to check compiler flags for language compiler. +function (sanitizer_check_compiler_flag FLAG LANG VARIABLE) + if (${LANG} STREQUAL "C") + include(CheckCCompilerFlag) + check_c_compiler_flag("${FLAG}" ${VARIABLE}) + + elseif (${LANG} STREQUAL "CXX") + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag("${FLAG}" ${VARIABLE}) + + elseif (${LANG} STREQUAL "Fortran") + # CheckFortranCompilerFlag was introduced in CMake 3.x. To be compatible + # with older Cmake versions, we will check if this module is present + # before we use it. Otherwise we will define Fortran coverage support as + # not available. + include(CheckFortranCompilerFlag OPTIONAL RESULT_VARIABLE INCLUDED) + if (INCLUDED) + check_fortran_compiler_flag("${FLAG}" ${VARIABLE}) + elseif (NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${VARIABLE}") + message(STATUS "Performing Test ${VARIABLE}" + " - Failed (Check not supported)") + endif () + endif() +endfunction () + + +# Helper function to test compiler flags. +function (sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX) + set(CMAKE_REQUIRED_QUIET ${${PREFIX}_FIND_QUIETLY}) + + get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) + foreach (LANG ${ENABLED_LANGUAGES}) + # Sanitizer flags are not dependend on language, but the used compiler. + # So instead of searching flags foreach language, search flags foreach + # compiler used. + set(COMPILER ${CMAKE_${LANG}_COMPILER_ID}) + if (NOT DEFINED ${PREFIX}_${COMPILER}_FLAGS) + foreach (FLAG ${FLAG_CANDIDATES}) + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Try ${COMPILER} ${NAME} flag = [${FLAG}]") + endif() + + set(CMAKE_REQUIRED_FLAGS "${FLAG}") + unset(${PREFIX}_FLAG_DETECTED CACHE) + sanitizer_check_compiler_flag("${FLAG}" ${LANG} + ${PREFIX}_FLAG_DETECTED) + + if (${PREFIX}_FLAG_DETECTED) + # If compiler is a GNU compiler, search for static flag, if + # SANITIZE_LINK_STATIC is enabled. + if (SANITIZE_LINK_STATIC AND (${COMPILER} STREQUAL "GNU")) + string(TOLOWER ${PREFIX} PREFIX_lower) + sanitizer_check_compiler_flag( + "-static-lib${PREFIX_lower}" ${LANG} + ${PREFIX}_STATIC_FLAG_DETECTED) + + if (${PREFIX}_STATIC_FLAG_DETECTED) + set(FLAG "-static-lib${PREFIX_lower} ${FLAG}") + endif () + endif () + + set(${PREFIX}_${COMPILER}_FLAGS "${FLAG}" CACHE STRING + "${NAME} flags for ${COMPILER} compiler.") + mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) + break() + endif () + endforeach () + + if (NOT ${PREFIX}_FLAG_DETECTED) + set(${PREFIX}_${COMPILER}_FLAGS "" CACHE STRING + "${NAME} flags for ${COMPILER} compiler.") + mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) + + message(WARNING "${NAME} is not available for ${COMPILER} " + "compiler. Targets using this compiler will be " + "compiled without ${NAME}.") + endif () + endif () + endforeach () +endfunction () + + +# Helper to assign sanitizer flags for TARGET. +function (sanitizer_add_flags TARGET NAME PREFIX) + # Get list of compilers used by target and check, if sanitizer is available + # for this target. Other compiler checks like check for conflicting + # compilers will be done in add_sanitizers function. + sanitizer_target_compilers(${TARGET} TARGET_COMPILER) + list(LENGTH TARGET_COMPILER NUM_COMPILERS) + if ("${${PREFIX}_${TARGET_COMPILER}_FLAGS}" STREQUAL "") + return() + endif() + + # Set compile- and link-flags for target. + set_property(TARGET ${TARGET} APPEND_STRING + PROPERTY COMPILE_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") + set_property(TARGET ${TARGET} APPEND_STRING + PROPERTY COMPILE_FLAGS " ${SanBlist_${TARGET_COMPILER}_FLAGS}") + set_property(TARGET ${TARGET} APPEND_STRING + PROPERTY LINK_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") +endfunction () diff --git a/engine/externals/sanitizers/sanitizers/.gitignore b/engine/externals/sanitizers/sanitizers/.gitignore new file mode 100644 index 0000000..c4a70a6 --- /dev/null +++ b/engine/externals/sanitizers/sanitizers/.gitignore @@ -0,0 +1,3 @@ +# out-of-source build top-level folders. +build/ +_build/ diff --git a/engine/externals/sanitizers/sanitizers/CMakeLists.txt b/engine/externals/sanitizers/sanitizers/CMakeLists.txt new file mode 100644 index 0000000..d249a4d --- /dev/null +++ b/engine/externals/sanitizers/sanitizers/CMakeLists.txt @@ -0,0 +1,51 @@ +# This file is part of CMake-sanitizers. +# +# 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. +# +# +# Copyright (c) +# 2013-2015 Matt Arsenault +# 2015 RWTH Aachen University, Federal Republic of Germany +# + + +# +# project information +# + +# minimum required cmake version +cmake_minimum_required(VERSION 2.8) + +# project name +project("CMake-sanitizers") + + + +# +# cmake configuration +# +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) + + + +# +# add tests +# +enable_testing() +add_subdirectory(tests) diff --git a/engine/externals/sanitizers/sanitizers/LICENSE b/engine/externals/sanitizers/sanitizers/LICENSE new file mode 100644 index 0000000..2520efd --- /dev/null +++ b/engine/externals/sanitizers/sanitizers/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) + 2013 Matthew Arsenault + 2015-2016 RWTH Aachen University, Federal Republic of Germany + +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. diff --git a/engine/externals/sanitizers/sanitizers/README.md b/engine/externals/sanitizers/sanitizers/README.md new file mode 100644 index 0000000..8df8d17 --- /dev/null +++ b/engine/externals/sanitizers/sanitizers/README.md @@ -0,0 +1,73 @@ +# sanitizers-cmake + + [![](https://img.shields.io/github/issues-raw/arsenm/sanitizers-cmake.svg?style=flat-square)](https://github.com/arsenm/sanitizers-cmake/issues) +[![MIT](http://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE) + +CMake module to enable sanitizers for binary targets. + + +## Include into your project + +To use [FindSanitizers.cmake](cmake/FindSanitizers.cmake), simply add this repository as git submodule into your own repository +```Shell +mkdir externals +git submodule add git://github.com/arsenm/sanitizers-cmake.git externals/sanitizers-cmake +``` +and adding ```externals/sanitizers-cmake/cmake``` to your ```CMAKE_MODULE_PATH``` +```CMake +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/externals/sanitizers-cmake/cmake" ${CMAKE_MODULE_PATH}) +``` + +If you don't use git or dislike submodules you can copy the files in [cmake directory](cmake) into your repository. *Be careful and keep updates in mind!* + +Now you can simply run ```find_package``` in your CMake files: +```CMake +find_package(Sanitizers) +``` + + +## Usage + +You can enable the sanitizers with ``SANITIZE_ADDRESS``, ``SANITIZE_MEMORY``, ``SANITIZE_THREAD`` or ``SANITIZE_UNDEFINED`` options in your CMake configuration. You can do this by passing e.g. ``-DSANITIZE_ADDRESS=On`` on your command line or with your graphical interface. + +If sanitizers are supported by your compiler, the specified targets will be build with sanitizer support. If your compiler has no sanitizing capabilities (I asume intel compiler doesn't) you'll get a warning but CMake will continue processing and sanitizing will simply just be ignored. + +#### Compiler issues + +Different compilers may be using different implementations for sanitizers. If you'll try to sanitize targets with C and Fortran code but don't use gcc & gfortran but clang & gfortran, this will cause linking problems. To avoid this, such problems will be detected and sanitizing will be disabled for these targets. + +Even C only targets may cause problems in certain situations. Some problems have been seen with AddressSanitizer for preloading or dynamic linking. In such cases you may try the ``SANITIZE_LINK_STATIC`` to link sanitizers for gcc static. + + + +## Build targets with sanitizer support + +To enable sanitizer support you simply have to add ``add_sanitizers()`` after defining your target. To provide a sanitizer blacklist file you can use the ``sanitizer_add_blacklist_file()`` function: +```CMake +find_package(Sanitizers) + +sanitizer_add_blacklist_file("blacklist.txt") + +add_executable(some_exe foo.c bar.c) +add_sanitizers(some_exe) + +add_library(some_lib foo.c bar.c) +add_sanitizers(some_lib) +``` + +## Run your application + +The sanitizers check your program, while it's running. In some situations (e.g. LD_PRELOAD your target) it might be required to preload the used AddressSanitizer library first. In this case you may use the ``asan-wrapper`` script defined in ``ASan_WRAPPER`` variable to execute your application with ``${ASan_WRAPPER} myexe arg1 ...``. + + +## Contribute + +Anyone is welcome to contribute. Simply fork this repository, make your changes **in an own branch** and create a pull-request for your change. Please do only one change per pull-request. + +You found a bug? Please fill out an [issue](https://github.com/arsenm/sanitizers-cmake/issues) and include any data to reproduce the bug. + + +#### Contributors + +* [Matt Arsenault](https://github.com/arsenm) +* [Alexander Haase](https://github.com/alehaa) diff --git a/engine/externals/sanitizers/sanitizers/cmake/FindASan.cmake b/engine/externals/sanitizers/sanitizers/cmake/FindASan.cmake new file mode 100644 index 0000000..98ea7cb --- /dev/null +++ b/engine/externals/sanitizers/sanitizers/cmake/FindASan.cmake @@ -0,0 +1,59 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# 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. + +option(SANITIZE_ADDRESS "Enable AddressSanitizer for sanitized targets." Off) + +set(FLAG_CANDIDATES + # Clang 3.2+ use this version. The no-omit-frame-pointer option is optional. + "-g -fsanitize=address -fno-omit-frame-pointer" + "-g -fsanitize=address" + + # Older deprecated flag for ASan + "-g -faddress-sanitizer" +) + + +if (SANITIZE_ADDRESS AND (SANITIZE_THREAD OR SANITIZE_MEMORY)) + message(FATAL_ERROR "AddressSanitizer is not compatible with " + "ThreadSanitizer or MemorySanitizer.") +endif () + + +include(sanitize-helpers) + +if (SANITIZE_ADDRESS) + sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "AddressSanitizer" + "ASan") + + find_program(ASan_WRAPPER "asan-wrapper" PATHS ${CMAKE_MODULE_PATH}) + mark_as_advanced(ASan_WRAPPER) +endif () + +function (add_sanitize_address TARGET) + if (NOT SANITIZE_ADDRESS) + return() + endif () + + sanitizer_add_flags(${TARGET} "AddressSanitizer" "ASan") +endfunction () diff --git a/engine/externals/sanitizers/sanitizers/cmake/FindMSan.cmake b/engine/externals/sanitizers/sanitizers/cmake/FindMSan.cmake new file mode 100644 index 0000000..22d0050 --- /dev/null +++ b/engine/externals/sanitizers/sanitizers/cmake/FindMSan.cmake @@ -0,0 +1,57 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# 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. + +option(SANITIZE_MEMORY "Enable MemorySanitizer for sanitized targets." Off) + +set(FLAG_CANDIDATES + "-g -fsanitize=memory" +) + + +include(sanitize-helpers) + +if (SANITIZE_MEMORY) + if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + message(WARNING "MemorySanitizer disabled for target ${TARGET} because " + "MemorySanitizer is supported for Linux systems only.") + set(SANITIZE_MEMORY Off CACHE BOOL + "Enable MemorySanitizer for sanitized targets." FORCE) + elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) + message(WARNING "MemorySanitizer disabled for target ${TARGET} because " + "MemorySanitizer is supported for 64bit systems only.") + set(SANITIZE_MEMORY Off CACHE BOOL + "Enable MemorySanitizer for sanitized targets." FORCE) + else () + sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "MemorySanitizer" + "MSan") + endif () +endif () + +function (add_sanitize_memory TARGET) + if (NOT SANITIZE_MEMORY) + return() + endif () + + sanitizer_add_flags(${TARGET} "MemorySanitizer" "MSan") +endfunction () diff --git a/engine/externals/sanitizers/sanitizers/cmake/FindSanitizers.cmake b/engine/externals/sanitizers/sanitizers/cmake/FindSanitizers.cmake new file mode 100644 index 0000000..4f586a3 --- /dev/null +++ b/engine/externals/sanitizers/sanitizers/cmake/FindSanitizers.cmake @@ -0,0 +1,87 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# 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. + +# If any of the used compiler is a GNU compiler, add a second option to static +# link against the sanitizers. +option(SANITIZE_LINK_STATIC "Try to link static against sanitizers." Off) + + + + +set(FIND_QUIETLY_FLAG "") +if (DEFINED Sanitizers_FIND_QUIETLY) + set(FIND_QUIETLY_FLAG "QUIET") +endif () + +find_package(ASan ${FIND_QUIETLY_FLAG}) +find_package(TSan ${FIND_QUIETLY_FLAG}) +find_package(MSan ${FIND_QUIETLY_FLAG}) +find_package(UBSan ${FIND_QUIETLY_FLAG}) + + + + +function(sanitizer_add_blacklist_file FILE) + if(NOT IS_ABSOLUTE ${FILE}) + set(FILE "${CMAKE_CURRENT_SOURCE_DIR}/${FILE}") + endif() + get_filename_component(FILE "${FILE}" REALPATH) + + sanitizer_check_compiler_flags("-fsanitize-blacklist=${FILE}" + "SanitizerBlacklist" "SanBlist") +endfunction() + +function(add_sanitizers ...) + # If no sanitizer is enabled, return immediately. + if (NOT (SANITIZE_ADDRESS OR SANITIZE_MEMORY OR SANITIZE_THREAD OR + SANITIZE_UNDEFINED)) + return() + endif () + + foreach (TARGET ${ARGV}) + # Check if this target will be compiled by exactly one compiler. Other- + # wise sanitizers can't be used and a warning should be printed once. + sanitizer_target_compilers(${TARGET} TARGET_COMPILER) + list(LENGTH TARGET_COMPILER NUM_COMPILERS) + if (NUM_COMPILERS GREATER 1) + message(WARNING "Can't use any sanitizers for target ${TARGET}, " + "because it will be compiled by incompatible compilers. " + "Target will be compiled without sanitizers.") + return() + + # If the target is compiled by no known compiler, ignore it. + elseif (NUM_COMPILERS EQUAL 0) + message(WARNING "Can't use any sanitizers for target ${TARGET}, " + "because it uses an unknown compiler. Target will be " + "compiled without sanitizers.") + return() + endif () + + # Add sanitizers for target. + add_sanitize_address(${TARGET}) + add_sanitize_thread(${TARGET}) + add_sanitize_memory(${TARGET}) + add_sanitize_undefined(${TARGET}) + endforeach () +endfunction(add_sanitizers) diff --git a/engine/externals/sanitizers/sanitizers/cmake/FindTSan.cmake b/engine/externals/sanitizers/sanitizers/cmake/FindTSan.cmake new file mode 100644 index 0000000..3cba3c0 --- /dev/null +++ b/engine/externals/sanitizers/sanitizers/cmake/FindTSan.cmake @@ -0,0 +1,65 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# 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. + +option(SANITIZE_THREAD "Enable ThreadSanitizer for sanitized targets." Off) + +set(FLAG_CANDIDATES + "-g -fsanitize=thread" +) + + +# ThreadSanitizer is not compatible with MemorySanitizer. +if (SANITIZE_THREAD AND SANITIZE_MEMORY) + message(FATAL_ERROR "ThreadSanitizer is not compatible with " + "MemorySanitizer.") +endif () + + +include(sanitize-helpers) + +if (SANITIZE_THREAD) + if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND + NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") + message(WARNING "ThreadSanitizer disabled for target ${TARGET} because " + "ThreadSanitizer is supported for Linux systems and macOS only.") + set(SANITIZE_THREAD Off CACHE BOOL + "Enable ThreadSanitizer for sanitized targets." FORCE) + elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) + message(WARNING "ThreadSanitizer disabled for target ${TARGET} because " + "ThreadSanitizer is supported for 64bit systems only.") + set(SANITIZE_THREAD Off CACHE BOOL + "Enable ThreadSanitizer for sanitized targets." FORCE) + else () + sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "ThreadSanitizer" + "TSan") + endif () +endif () + +function (add_sanitize_thread TARGET) + if (NOT SANITIZE_THREAD) + return() + endif () + + sanitizer_add_flags(${TARGET} "ThreadSanitizer" "TSan") +endfunction () diff --git a/engine/externals/sanitizers/sanitizers/cmake/FindUBSan.cmake b/engine/externals/sanitizers/sanitizers/cmake/FindUBSan.cmake new file mode 100644 index 0000000..ae103f7 --- /dev/null +++ b/engine/externals/sanitizers/sanitizers/cmake/FindUBSan.cmake @@ -0,0 +1,46 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# 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. + +option(SANITIZE_UNDEFINED + "Enable UndefinedBehaviorSanitizer for sanitized targets." Off) + +set(FLAG_CANDIDATES + "-g -fsanitize=undefined" +) + + +include(sanitize-helpers) + +if (SANITIZE_UNDEFINED) + sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" + "UndefinedBehaviorSanitizer" "UBSan") +endif () + +function (add_sanitize_undefined TARGET) + if (NOT SANITIZE_UNDEFINED) + return() + endif () + + sanitizer_add_flags(${TARGET} "UndefinedBehaviorSanitizer" "UBSan") +endfunction () diff --git a/engine/externals/sanitizers/sanitizers/cmake/asan-wrapper b/engine/externals/sanitizers/sanitizers/cmake/asan-wrapper new file mode 100755 index 0000000..5d54103 --- /dev/null +++ b/engine/externals/sanitizers/sanitizers/cmake/asan-wrapper @@ -0,0 +1,55 @@ +#!/bin/sh + +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# 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. + +# This script is a wrapper for AddressSanitizer. In some special cases you need +# to preload AddressSanitizer to avoid error messages - e.g. if you're +# preloading another library to your application. At the moment this script will +# only do something, if we're running on a Linux platform. OSX might not be +# affected. + + +# Exit immediately, if platform is not Linux. +if [ "$(uname)" != "Linux" ] +then + exec $@ +fi + + +# Get the used libasan of the application ($1). If a libasan was found, it will +# be prepended to LD_PRELOAD. +libasan=$(ldd $1 | grep libasan | sed "s/^[[:space:]]//" | cut -d' ' -f1) +if [ -n "$libasan" ] +then + if [ -n "$LD_PRELOAD" ] + then + export LD_PRELOAD="$libasan:$LD_PRELOAD" + else + export LD_PRELOAD="$libasan" + fi +fi + +# Execute the application. +exec $@ diff --git a/engine/externals/sanitizers/sanitizers/cmake/sanitize-helpers.cmake b/engine/externals/sanitizers/sanitizers/cmake/sanitize-helpers.cmake new file mode 100644 index 0000000..c51ee6a --- /dev/null +++ b/engine/externals/sanitizers/sanitizers/cmake/sanitize-helpers.cmake @@ -0,0 +1,170 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# 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. + +# Helper function to get the language of a source file. +function (sanitizer_lang_of_source FILE RETURN_VAR) + get_filename_component(FILE_EXT "${FILE}" EXT) + string(TOLOWER "${FILE_EXT}" FILE_EXT) + string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT) + + get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) + foreach (LANG ${ENABLED_LANGUAGES}) + list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP) + if (NOT ${TEMP} EQUAL -1) + set(${RETURN_VAR} "${LANG}" PARENT_SCOPE) + return() + endif () + endforeach() + + set(${RETURN_VAR} "" PARENT_SCOPE) +endfunction () + + +# Helper function to get compilers used by a target. +function (sanitizer_target_compilers TARGET RETURN_VAR) + # Check if all sources for target use the same compiler. If a target uses + # e.g. C and Fortran mixed and uses different compilers (e.g. clang and + # gfortran) this can trigger huge problems, because different compilers may + # use different implementations for sanitizers. + set(BUFFER "") + get_target_property(TSOURCES ${TARGET} SOURCES) + foreach (FILE ${TSOURCES}) + # If expression was found, FILE is a generator-expression for an object + # library. Object libraries will be ignored. + string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE}) + if ("${_file}" STREQUAL "") + sanitizer_lang_of_source(${FILE} LANG) + if (LANG) + list(APPEND BUFFER ${CMAKE_${LANG}_COMPILER_ID}) + endif () + endif () + endforeach () + + list(REMOVE_DUPLICATES BUFFER) + set(${RETURN_VAR} "${BUFFER}" PARENT_SCOPE) +endfunction () + + +# Helper function to check compiler flags for language compiler. +function (sanitizer_check_compiler_flag FLAG LANG VARIABLE) + if (${LANG} STREQUAL "C") + include(CheckCCompilerFlag) + check_c_compiler_flag("${FLAG}" ${VARIABLE}) + + elseif (${LANG} STREQUAL "CXX") + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag("${FLAG}" ${VARIABLE}) + + elseif (${LANG} STREQUAL "Fortran") + # CheckFortranCompilerFlag was introduced in CMake 3.x. To be compatible + # with older Cmake versions, we will check if this module is present + # before we use it. Otherwise we will define Fortran coverage support as + # not available. + include(CheckFortranCompilerFlag OPTIONAL RESULT_VARIABLE INCLUDED) + if (INCLUDED) + check_fortran_compiler_flag("${FLAG}" ${VARIABLE}) + elseif (NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${VARIABLE}") + message(STATUS "Performing Test ${VARIABLE}" + " - Failed (Check not supported)") + endif () + endif() +endfunction () + + +# Helper function to test compiler flags. +function (sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX) + set(CMAKE_REQUIRED_QUIET ${${PREFIX}_FIND_QUIETLY}) + + get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) + foreach (LANG ${ENABLED_LANGUAGES}) + # Sanitizer flags are not dependend on language, but the used compiler. + # So instead of searching flags foreach language, search flags foreach + # compiler used. + set(COMPILER ${CMAKE_${LANG}_COMPILER_ID}) + if (NOT DEFINED ${PREFIX}_${COMPILER}_FLAGS) + foreach (FLAG ${FLAG_CANDIDATES}) + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Try ${COMPILER} ${NAME} flag = [${FLAG}]") + endif() + + set(CMAKE_REQUIRED_FLAGS "${FLAG}") + unset(${PREFIX}_FLAG_DETECTED CACHE) + sanitizer_check_compiler_flag("${FLAG}" ${LANG} + ${PREFIX}_FLAG_DETECTED) + + if (${PREFIX}_FLAG_DETECTED) + # If compiler is a GNU compiler, search for static flag, if + # SANITIZE_LINK_STATIC is enabled. + if (SANITIZE_LINK_STATIC AND (${COMPILER} STREQUAL "GNU")) + string(TOLOWER ${PREFIX} PREFIX_lower) + sanitizer_check_compiler_flag( + "-static-lib${PREFIX_lower}" ${LANG} + ${PREFIX}_STATIC_FLAG_DETECTED) + + if (${PREFIX}_STATIC_FLAG_DETECTED) + set(FLAG "-static-lib${PREFIX_lower} ${FLAG}") + endif () + endif () + + set(${PREFIX}_${COMPILER}_FLAGS "${FLAG}" CACHE STRING + "${NAME} flags for ${COMPILER} compiler.") + mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) + break() + endif () + endforeach () + + if (NOT ${PREFIX}_FLAG_DETECTED) + set(${PREFIX}_${COMPILER}_FLAGS "" CACHE STRING + "${NAME} flags for ${COMPILER} compiler.") + mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) + + message(WARNING "${NAME} is not available for ${COMPILER} " + "compiler. Targets using this compiler will be " + "compiled without ${NAME}.") + endif () + endif () + endforeach () +endfunction () + + +# Helper to assign sanitizer flags for TARGET. +function (sanitizer_add_flags TARGET NAME PREFIX) + # Get list of compilers used by target and check, if sanitizer is available + # for this target. Other compiler checks like check for conflicting + # compilers will be done in add_sanitizers function. + sanitizer_target_compilers(${TARGET} TARGET_COMPILER) + list(LENGTH TARGET_COMPILER NUM_COMPILERS) + if ("${${PREFIX}_${TARGET_COMPILER}_FLAGS}" STREQUAL "") + return() + endif() + + # Set compile- and link-flags for target. + set_property(TARGET ${TARGET} APPEND_STRING + PROPERTY COMPILE_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") + set_property(TARGET ${TARGET} APPEND_STRING + PROPERTY COMPILE_FLAGS " ${SanBlist_${TARGET_COMPILER}_FLAGS}") + set_property(TARGET ${TARGET} APPEND_STRING + PROPERTY LINK_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") +endfunction () diff --git a/engine/externals/sanitizers/sanitizers/tests/CMakeLists.txt b/engine/externals/sanitizers/sanitizers/tests/CMakeLists.txt new file mode 100644 index 0000000..6ffb38f --- /dev/null +++ b/engine/externals/sanitizers/sanitizers/tests/CMakeLists.txt @@ -0,0 +1,52 @@ +# This file is part of CMake-sanitizers. +# +# 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. +# +# +# Copyright (c) +# 2013-2015 Matt Arsenault +# 2015 RWTH Aachen University, Federal Republic of Germany +# + +# Function to add testcases. +function(add_testcase TESTNAME SOURCEFILES) + # remove ${TESTNAME} from ${ARGV} to use ${ARGV} as ${SOURCEFILES} + list(REMOVE_AT ARGV 0) + + # add a new executable + add_executable(${TESTNAME} ${ARGV}) + add_sanitizers(${TESTNAME}) + + # add a testcase for executable + add_test(${TESTNAME} ${TESTNAME}) +endfunction(add_testcase) + + + +# +# search for sanitizers +# +find_package(Sanitizers) + + + +# +# add testcases +# +add_testcase("asan_test_cpp" asan_test.cpp) diff --git a/engine/externals/sanitizers/sanitizers/tests/asan_test.cpp b/engine/externals/sanitizers/sanitizers/tests/asan_test.cpp new file mode 100644 index 0000000..4b276de --- /dev/null +++ b/engine/externals/sanitizers/sanitizers/tests/asan_test.cpp @@ -0,0 +1,39 @@ +/* This file is part of CMake-sanitizers. + * + * 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. + * + * + * Copyright (c) + * 2013-2015 Matt Arsenault + * 2015 RWTH Aachen University, Federal Republic of Germany + */ + + +int +main(int argc, char **argv) +{ + // Allocate a new array and delete it. + int *array = new int[argc]; + delete[] array; + + /* Access element of the deleted array. This will cause an memory error with + * address sanitizer. + */ + return array[argc]; +} diff --git a/engine/externals/sanitizers/tests/CMakeLists.txt b/engine/externals/sanitizers/tests/CMakeLists.txt new file mode 100644 index 0000000..6ffb38f --- /dev/null +++ b/engine/externals/sanitizers/tests/CMakeLists.txt @@ -0,0 +1,52 @@ +# This file is part of CMake-sanitizers. +# +# 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. +# +# +# Copyright (c) +# 2013-2015 Matt Arsenault +# 2015 RWTH Aachen University, Federal Republic of Germany +# + +# Function to add testcases. +function(add_testcase TESTNAME SOURCEFILES) + # remove ${TESTNAME} from ${ARGV} to use ${ARGV} as ${SOURCEFILES} + list(REMOVE_AT ARGV 0) + + # add a new executable + add_executable(${TESTNAME} ${ARGV}) + add_sanitizers(${TESTNAME}) + + # add a testcase for executable + add_test(${TESTNAME} ${TESTNAME}) +endfunction(add_testcase) + + + +# +# search for sanitizers +# +find_package(Sanitizers) + + + +# +# add testcases +# +add_testcase("asan_test_cpp" asan_test.cpp) diff --git a/engine/externals/sanitizers/tests/asan_test.cpp b/engine/externals/sanitizers/tests/asan_test.cpp new file mode 100644 index 0000000..4b276de --- /dev/null +++ b/engine/externals/sanitizers/tests/asan_test.cpp @@ -0,0 +1,39 @@ +/* This file is part of CMake-sanitizers. + * + * 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. + * + * + * Copyright (c) + * 2013-2015 Matt Arsenault + * 2015 RWTH Aachen University, Federal Republic of Germany + */ + + +int +main(int argc, char **argv) +{ + // Allocate a new array and delete it. + int *array = new int[argc]; + delete[] array; + + /* Access element of the deleted array. This will cause an memory error with + * address sanitizer. + */ + return array[argc]; +} diff --git a/engine/osx-linux/install_32bit_sdl.sh b/engine/osx-linux/install_32bit_sdl.sh new file mode 100755 index 0000000..a036a22 --- /dev/null +++ b/engine/osx-linux/install_32bit_sdl.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash + +# Setting up + +set -euo pipefail + +SDL_version=2.0.9 +SDL2_mixer_version=2.0.4 + +function build_sdl { + curl -O https://www.libsdl.org/release/SDL2-${SDL_version}.tar.gz + tar xf SDL2-${SDL_version}.tar.gz + pushd SDL2-${SDL_version} + + ./configure "CFLAGS=-m32" "CXXFLAGS=-m32" "LDFLAGS=-m32" --prefix=${install_dir}/built_sdl + make -j2 + make install + + popd +} + +function build_sdl_mixer { + curl -O https://www.libsdl.org/projects/SDL_mixer/release/SDL2_mixer-${SDL2_mixer_version}.tar.gz + tar xf SDL2_mixer-${SDL2_mixer_version}.tar.gz + pushd SDL2_mixer-${SDL2_mixer_version} + + export SDL2_CONFIG="${install_dir}/built_sdl/bin/sdl2-config" + ./configure "CFLAGS=-m32" "CXXFLAGS=-m32" "LDFLAGS=-m32" --prefix=${install_dir}/built_sdl_mixer + make -j2 + make install + + popd +} + +function build_fluidsynth { + git clone https://github.com/EtherTyper/fluidsynth-lite.git + pushd fluidsynth-lite + + # force compilation of dynamic library + sed -i'' -e 's/DLL\"\ off/DLL\"\ on/' CMakeLists.txt + + # force 32bit compilation + sed -i'' -e 's/\${GNUCC_WARNING_FLAGS}/-m32 \${GNUCC_WARNING_FLAGS}/' CMakeLists.txt + + # if building fluidsynth fails, move on without it + set +e + cmake . + cmake --build . + + # download a soundfont that's close to the Windows default everyone knows + curl -o music.sf2 http://rancid.kapsi.fi/windows.sf2 + set -e + popd +} + + +# Actual script starts here + +if [ -d ./build_ext/ ]; then + echo A directory named build_ext already exists. + echo Please remove it if you want to recompile. + exit +fi + +mkdir ./build_ext/ +cd ./build_ext/ + +install_dir=$(pwd) + +build_fluidsynth +build_sdl +build_sdl_mixer + +cd .. + +mkdir -p ./res/ + +# move the soundfont to the correct place if we successfully built fluidsynth +for i in build_ext/fluidsynth-lite/*.sf2; do [ -f "$i" ] || break; mv $i res/; done; + diff --git a/engine/osx-linux/install_64bit_sdl.sh b/engine/osx-linux/install_64bit_sdl.sh new file mode 100644 index 0000000..a0afbe7 --- /dev/null +++ b/engine/osx-linux/install_64bit_sdl.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash + +# Setting up + +set -euo pipefail + +SDL_version=2.0.9 +SDL2_mixer_version=2.0.4 + +function build_sdl { + curl -O https://www.libsdl.org/release/SDL2-${SDL_version}.tar.gz + tar xf SDL2-${SDL_version}.tar.gz + pushd SDL2-${SDL_version} + + ./configure --prefix=${install_dir}/built_sdl + make -j2 + make install + + popd +} + +function build_sdl_mixer { + curl -O https://www.libsdl.org/projects/SDL_mixer/release/SDL2_mixer-${SDL2_mixer_version}.tar.gz + tar xf SDL2_mixer-${SDL2_mixer_version}.tar.gz + pushd SDL2_mixer-${SDL2_mixer_version} + + export SDL2_CONFIG="${install_dir}/built_sdl/bin/sdl2-config" + ./configure --prefix=${install_dir}/built_sdl_mixer + make -j2 + make install + + popd +} + +function build_fluidsynth { + git clone https://github.com/EtherTyper/fluidsynth-lite.git + pushd fluidsynth-lite + + # force compilation of dynamic library + sed -i'' -e 's/DLL\"\ off/DLL\"\ on/' CMakeLists.txt + + # force 32bit compilation + # sed -i'' -e 's/\${GNUCC_WARNING_FLAGS}/-m32 \${GNUCC_WARNING_FLAGS}/' CMakeLists.txt + + # if building fluidsynth fails, move on without it + set +e + cmake . + cmake --build . + + # download a soundfont that's close to the Windows default everyone knows + curl -o music.sf2 http://rancid.kapsi.fi/windows.sf2 + set -e + popd +} + + +# Actual script starts here + +if [ -d ./build_ext/ ]; then + echo A directory named build_ext already exists. + echo Please remove it if you want to recompile. + exit +fi + +mkdir ./build_ext/ +cd ./build_ext/ + +install_dir=$(pwd) + +build_fluidsynth +build_sdl +build_sdl_mixer + +cd .. + +mkdir -p ./res/ + +# move the soundfont to the correct place if we successfully built fluidsynth +for i in build_ext/fluidsynth-lite/*.sf2; do [ -f "$i" ] || break; mv $i res/; done; + diff --git a/engine/osx-linux/readme_osx_linux.md b/engine/osx-linux/readme_osx_linux.md new file mode 100644 index 0000000..03ec735 --- /dev/null +++ b/engine/osx-linux/readme_osx_linux.md @@ -0,0 +1,6 @@ +Shockolate - Portable System Shock +============ + +1. Copy `res/` folder of original System Shock to this directory +2. Install SDL2, SDL2_mixer and Fluidsynth by running `./install_bit_sdl.sh` where `` is 32 or 64, depending on your system capabilities. Linux needs `sudo` for this script. +3. Run Shockolate `./run.sh` diff --git a/engine/osx-linux/run_linux.sh b/engine/osx-linux/run_linux.sh new file mode 100644 index 0000000..926fc95 --- /dev/null +++ b/engine/osx-linux/run_linux.sh @@ -0,0 +1,3 @@ +#!/bin/bash +export LD_LIBRARY_PATH="$(pwd)/lib" +./systemshock "$@" diff --git a/engine/osx-linux/run_osx.sh b/engine/osx-linux/run_osx.sh new file mode 100644 index 0000000..310c8cb --- /dev/null +++ b/engine/osx-linux/run_osx.sh @@ -0,0 +1,3 @@ +#!/bin/bash +export DYLD_LIBRARY_PATH="$(pwd)/lib" +./systemshock "$@" diff --git a/engine/shaders/color.frag b/engine/shaders/color.frag new file mode 100644 index 0000000..8e6a43c --- /dev/null +++ b/engine/shaders/color.frag @@ -0,0 +1,8 @@ +#version 110 + +varying vec4 Color; +varying float Light; + +void main() { + gl_FragColor = vec4(Color.r * Light, Color.g * Light, Color.b * Light, Color.a); +} diff --git a/engine/shaders/main.vert b/engine/shaders/main.vert new file mode 100644 index 0000000..7d7977d --- /dev/null +++ b/engine/shaders/main.vert @@ -0,0 +1,19 @@ +#version 110 + +attribute vec2 texcoords; +attribute float light; +attribute vec4 color; + +varying vec2 TexCoords; +varying float Light; +varying vec4 Color; + +uniform mat4 view; +uniform mat4 proj; + +void main() { + TexCoords = texcoords; + Light = light; + Color = color; + gl_Position = proj * view * gl_Vertex; +} diff --git a/engine/shaders/star.frag b/engine/shaders/star.frag new file mode 100644 index 0000000..7407f20 --- /dev/null +++ b/engine/shaders/star.frag @@ -0,0 +1,13 @@ +#version 120 + +varying float Light; + +void main() { + vec2 mid = vec2(0.5, 0.5) - gl_PointCoord; + + float dist = 0.5 - length(mid); + dist *= 3.0; + dist = min(dist, 1.0); + + gl_FragColor = vec4(dist * Light, dist * Light, dist * Light, 1.0); +} diff --git a/engine/shaders/texture.frag b/engine/shaders/texture.frag new file mode 100644 index 0000000..47df1c3 --- /dev/null +++ b/engine/shaders/texture.frag @@ -0,0 +1,35 @@ +#version 110 + +// The texture alpha value has two separate functions in this shader: +// - Pixels with a zero alpha value are transparent. +// - Pixels with non-zero alpha are opaque, and the alpha value +// defines a minimum light level. + +varying vec2 TexCoords; +varying float Light; + +uniform sampler2D tex; +uniform bool nightsight; +uniform bool mutant; + +void main() { + vec4 t = texture2D(tex, TexCoords); + + // Alpha values > 0.5 are emissive + float alpha = t.a > 0.5 ? 1.0 : t.a * 2.0; + + if (nightsight) { + // approximate the blue tint of palette colors 0xb0..0xbf + float gray = 0.40 * t.r + 0.59 * t.g + 0.11 * t.b; + float rg = 0.7 * gray; + float b = 0.75 * gray + 0.1; + gl_FragColor = vec4(rg, rg, b, (mutant ? 0.2 * alpha : alpha)); + } else { + float emissive = t.a > 0.5 ? (t.a - 0.5) * 2.0 : 0.0; + float light = max(Light * Light, emissive); + if (mutant) + gl_FragColor = vec4(0.5 * t.r * light, 0.5 * t.r * light, 0.5 * t.r * light, 0.2 * alpha); + else + gl_FragColor = vec4(t.r * light, t.g * light, t.b * light, alpha); + } +} diff --git a/engine/src/GameSrc/FrUtils.c b/engine/src/GameSrc/FrUtils.c new file mode 100644 index 0000000..18291a1 --- /dev/null +++ b/engine/src/GameSrc/FrUtils.c @@ -0,0 +1,153 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * FrUtils.c + * + * MLA - 4/14/95 + * + * Contiains Mac specific utils for the renderer (fast draw slot view, full screen, etc.) + * + */ + +#include "FrUtils.h" +#include "gr2ss.h" +#include "Shock.h" +#include "2d.h" + +// ------------------ +// GLOBALS +// ------------------ +//Handle gDoubleSizeOffHdl = NULL; +grs_canvas gDoubleSizeOffCanvas; + +//--------------------------------------------------------------------- +// Allocate the intermediate offscreen buffer for low-res mode in Shock. +//--------------------------------------------------------------------- +/* +int AllocDoubleBuffer(int w, int h) { +#if 1 + STUB_ONCE(""); + return 0; +#else + Size dummy; + + FreeDoubleBuffer(); // If one's there, free it first. + + if (h == 259) // Major Hack!!! In slot view, the double + h++; // buffer needs to be 260 (even number). + + MaxMem(&dummy); // Compact heap before big alloc. + + gDoubleSizeOffHdl = NewHandle(w * h); // Allocate new buffer. + if (gDoubleSizeOffHdl) // If successful, make a canvas for it. + { + HLockHi(gDoubleSizeOffHdl); + gr_init_canvas(&gDoubleSizeOffCanvas, (uchar *)*gDoubleSizeOffHdl, BMT_FLAT8, w, h); + return 1; + } else + return 0; +#endif +} +*/ +//--------------------------------------------------------------------- +//--------------------------------------------------------------------- +/* +void FreeDoubleBuffer(void) { + if (gDoubleSizeOffHdl) // If there's a buffer, + { + HUnlock(gDoubleSizeOffHdl); + DisposeHandle(gDoubleSizeOffHdl); // free it. + gDoubleSizeOffHdl = NULL; + } +} +*/ +// hard coded to copy from 56,57 to 56+536,57+259 to the screen +#define kFastSlotWide 536 +#define kFastSlotHigh 259 +#define kFastSlotLeft 28 +#define kFastSlotTop 24 + +#define kFastSlotWide_Half 268 +#define kFastSlotHigh_Half 129 + +#define LoadStoreTwoDoub(a, b) \ + doub1 = src[a]; \ + doub2 = src[b]; \ + dest[a] = doub1; \ + dest[b] = doub2; + +// copy the slot view from offscreen to on +void Fast_Slot_Copy(grs_bitmap *bm) { + gr_bitmap(bm, SCONV_X(kFastSlotLeft), SCONV_Y(kFastSlotTop)); +} + +// copy the full screen view from offscreen to on +// hard coded to copy from 0,0 to 640,480 to the screen +void Fast_FullScreen_Copy(grs_bitmap *bm) { gr_bitmap(bm, 0, 0); } + +//================================================================= +// Doubling routines +extern bool SkipLines; + +// copy the slot view from offscreen to on, doubling it +// extern "C" +//{ +//extern void BlitLargeAlign(uchar *draw_buffer, int dstRowBytes, void *dstPtr, long w, long h, long modulus); +//extern void BlitLargeAlignSkip(uchar *draw_buffer, int dstRowBytes, void *dstPtr, long w, long h, long modulus); +//} + +/* +void Fast_Slot_Double(grs_bitmap *bm, long w, long h) { + if (!SkipLines) + BlitLargeAlign(bm->bits, gScreenRowbytes, gScreenAddress + (kFastSlotTop * gScreenRowbytes) + kFastSlotLeft, w, + h, bm->row); + else + BlitLargeAlignSkip(bm->bits, gScreenRowbytes, gScreenAddress + (kFastSlotTop * gScreenRowbytes) + kFastSlotLeft, + w, h, bm->row); +} +*/ + +void FastSlotDouble2Canvas(grs_bitmap *bm, grs_canvas *destCanvas, long w, long h) { + if (SkipLines) { + gr_clear(0xFF); + // BlitLargeAlignSkip(bm->bits, destCanvas->bm.row, destCanvas->bm.bits, w, h + 1, bm->row); + } else { + // BlitLargeAlign(bm->bits, destCanvas->bm.row, destCanvas->bm.bits, w, h + 1, bm->row); + } +} + +// copy the full screen view from offscreen to on +// hard coded to copy from 0,0 to 640,480 to the screen +/* +void Fast_FullScreen_Double(grs_bitmap *bm, long w, long h) { + if (!SkipLines) + BlitLargeAlign(bm->bits, gScreenRowbytes, gScreenAddress, w, h, bm->row); + else + BlitLargeAlignSkip(bm->bits, gScreenRowbytes, gScreenAddress, w, h, bm->row); +} +*/ + +void FastFullscreenDouble2Canvas(grs_bitmap *bm, grs_canvas *destCanvas, long w, long h) { + if (SkipLines) { + gr_clear(0xFF); + // BlitLargeAlignSkip(bm->bits, destCanvas->bm.row, destCanvas->bm.bits, w, h + 1, bm->row); + } else { + // BlitLargeAlign(bm->bits, destCanvas->bm.row, destCanvas->bm.bits, w, h + 1, bm->row); + } +} diff --git a/engine/src/GameSrc/Headers/FrUtils.h b/engine/src/GameSrc/Headers/FrUtils.h new file mode 100644 index 0000000..15a6e75 --- /dev/null +++ b/engine/src/GameSrc/Headers/FrUtils.h @@ -0,0 +1,33 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// externs for functions in FrUtils.C + +extern void Fast_Slot_Copy(grs_bitmap *bm); +extern void Fast_FullScreen_Copy(grs_bitmap *bm); +// extern void Fast_Slot_Double(grs_bitmap *bm, long w, long h); +// extern void Fast_FullScreen_Double(grs_bitmap *bm, long w, long h); + +void FastSlotDouble2Canvas(grs_bitmap *bm, grs_canvas *destCanvas, long w, long h); +void FastFullscreenDouble2Canvas(grs_bitmap *bm, grs_canvas *destCanvas, long w, long h); + +// Stuff for the low-res temporary offscreen buffer. +extern grs_canvas gDoubleSizeOffCanvas; + +// int AllocDoubleBuffer(int w, int h); +// void FreeDoubleBuffer(void); diff --git a/engine/src/GameSrc/Headers/ai.h b/engine/src/GameSrc/Headers/ai.h new file mode 100644 index 0000000..8a6ada0 --- /dev/null +++ b/engine/src/GameSrc/Headers/ai.h @@ -0,0 +1,103 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __AI_H +#define __AI_H + +/* + * $Source: n:/project/cit/src/inc/RCS/ai.h $ + * $Revision: 1.22 $ + * $Author: xemu $ + * $Date: 1994/06/21 02:14:10 $ + * + * + */ + +// Includes +#include "objects.h" + +// Defines +#define NUM_AI_MOODS 5 +#define AI_MOOD_FRIENDLY 0 +#define AI_MOOD_NEUTRAL 1 +#define AI_MOOD_HOSTILE 2 +#define AI_MOOD_ISOLATION 3 +#define AI_MOOD_ATTACKING 4 + +#define NUM_AI_ORDERS 6 +#define AI_ORDERS_GUARD 0 // hang out until player comes around +#define AI_ORDERS_ROAM 1 // wander about +#define AI_ORDERS_SLEEP 2 // do nothing ever until awakened +#define AI_ORDERS_PATROL 3 // back n forth between 2 points +#define AI_ORDERS_HIGHWAY 4 // follow invisible highway +#define AI_ORDERS_NOMOVE 5 // like guard, but will never move + +#define AI_FLAG_NONE 0x00 +#define AI_FLAG_FLYING 0x01 // we can fly +#define AI_FLAG_NOALERT 0x02 // don't speed up reaction when in combat +#define AI_FLAG_SMALL 0x04 // for musicai + +#define SPREAD_DIST 2 + +#define SLOW_PROJECTILE_DURATION 1000 +#define SLOW_PROJECTILE_GRAVITY fix_make(0,0x0C00) + +// Macros + +// Prototypes + +errtype set_posture_safe(ObjSpecID osid, ubyte new_pos); +errtype set_posture_movesafe(ObjSpecID osid, ubyte new_pos); +errtype clear_critter_controls(ObjSpecID osid); +errtype apply_EDMS_controls(ObjSpecID osid); +errtype roll_on_dnd_treasure_tables(int *pcont, char treasure_type); + +// External Functions: + +// Let all the AI system spend time figuring out what to do. Those AIs that know what they are doing, +// do it, others plan, the time load is hopefully distributed as nicely as possible +errtype ai_run(void); + +// What do do when we are hit +errtype ai_critter_hit(ObjSpecID osid, short damage, uchar tranq, uchar stun); + +// When we pretend that we're dead (pretend we're dead) +errtype ai_critter_die(ObjSpecID osid); +errtype ai_critter_really_dead(ObjSpecID osid); + +// actually do attack +errtype ai_attack_player(ObjSpecID osid, char a); + +errtype ai_fire_special(ObjID src, ObjID target, int proj_triple, ObjLoc src_loc, ObjLoc target_loc, uchar a, + int duration); + +// Change a critter's posture, and do appropriate +// things to other anim variables +errtype set_posture(ObjSpecID osid, ubyte new_pos); + +// Looting. +errtype do_regular_loot(ObjSpecID source_critter, ObjID corpse); +errtype do_random_loot(ObjID corpse); + +void ai_find_player(ObjID id); +void ai_critter_seen(void); +void ai_misses(ObjSpecID osid); + +// Globals + +#endif // __AI_H diff --git a/engine/src/GameSrc/Headers/aiflags.h b/engine/src/GameSrc/Headers/aiflags.h new file mode 100644 index 0000000..b07b3cb --- /dev/null +++ b/engine/src/GameSrc/Headers/aiflags.h @@ -0,0 +1,25 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#define AI_FLAG_CHASING 0x0001 +#define AI_FLAG_NOLOOT 0x0002 +#define AI_FLAG_CONFUSED 0x0004 +#define AI_FLAG_TRANQ 0x0010 + +#define ai_critter_sleeping(osid) \ + ((objCritters[(osid)].orders == AI_ORDERS_SLEEP) || (objCritters[(osid)].flags & AI_FLAG_TRANQ)) diff --git a/engine/src/GameSrc/Headers/airupt.h b/engine/src/GameSrc/Headers/airupt.h new file mode 100644 index 0000000..6350160 --- /dev/null +++ b/engine/src/GameSrc/Headers/airupt.h @@ -0,0 +1,26 @@ +/* + +Copyright (C) 2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef AIRUPT_H +#define AIRUPT_H + +errtype check_asynch_ai(uchar new_score_ok); +void grind_music_ai(void); + +#endif diff --git a/engine/src/GameSrc/Headers/amap.h b/engine/src/GameSrc/Headers/amap.h new file mode 100644 index 0000000..6f54d54 --- /dev/null +++ b/engine/src/GameSrc/Headers/amap.h @@ -0,0 +1,123 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __AMAP_H +#define __AMAP_H + +// header for the real infernal automap + +// defines +#define AMAP_PURE_MODE 0x0000 +#define AMAP_INT_WALLS 0x0001 +#define AMAP_SHOW_CRIT 0x0002 +#define AMAP_SHOW_ROB 0x0004 +#define AMAP_SHOW_HAZ 0x0008 +#define AMAP_SHOW_FLR 0x0010 +#define AMAP_SHOW_MSG 0x0020 +#define AMAP_SHOW_HGT 0x0040 +#define AMAP_TRACK_OBJ 0x0080 +#define AMAP_SHOW_SEC 0x0100 +#define AMAP_FULL_MSG 0x0200 +#define AMAP_SHOW_ALL 0x0400 +#define AMAP_SHOW_SENS 0x0800 + +#define AMAP_AVAIL_ALWAYS (AMAP_SHOW_SENS | AMAP_SHOW_FLR | AMAP_FULL_MSG) + +#define AMAP_SET 1 +#define AMAP_UNSET 0 +#define AMAP_TOGGLE -1 + +#define AMAP_PAN_N 1 +#define AMAP_PAN_E 2 +#define AMAP_PAN_S 3 +#define AMAP_PAN_W 4 +#define AMAP_DEF_DST 0x40000 + +#define AMAP_MAX_ZOOM 6 +#define AMAP_MIN_ZOOM 1 + +#define AMAP_OFF_MAP 0 +#define AMAP_HAVE_NOTE 1 +#define AMAP_NO_NOTE 2 + +// really should live in the player structure.... +typedef struct { + uchar init; + uchar zoom; + int xf, yf; + ushort lw, lh; + ushort obj_to_follow, sensor_obj; + ushort note_obj; + ushort flags; + ushort avail_flags; + uchar version_id; + ushort sensor_rad; // in obj coords +} curAMap; + +// prototypes +uchar amap_kb_callback(curAMap *amptr, int code); +void amap_draw(curAMap *amptr, int expose); +void amap_version_set(int id, int new_ver); +void automap_init(int version, int id); +void amap_invalidate(int id); +uchar amap_flags(curAMap *amptr, int flags, int set); // set -1 to toggle +uchar amap_zoom(curAMap *amptr, uchar set, int zoom_delta); +void amap_pan(curAMap *amptr, int dir, int *dist); +uchar amap_get_note(curAMap *amptr, char *buf); +void amap_pixratio_set(fix ratio); +void amap_settings_copy(curAMap *from, curAMap *to); + +grs_bitmap *screen_automap_bitmap(char which_amap); + +// this is a mess +// it modifies x and y to be map location of click +// returns null if off map, (void*)mapelemptr if within map +// sets amptr->note_obj to the note if found, else OBJ_NULL +void *amap_deal_with_map_click(curAMap *amptr, int *x, int *y); + +// strings +void amap_str_init(void); +char *amap_str_next(void); +void amap_str_grab(char *str); +int amap_str_deref(char *str); +char *amap_str_reref(int offs); +void amap_str_delete(char *toast_str); +void amap_str_startup(int magic_num); + +#define MFD_FULLSCR_MAP 2 +#define NUM_O_AMAP MFD_FULLSCR_MAP + 1 + +// globals +// for now + +#define oAMap(mid) (&(level_gamedata.auto_maps[mid])) +//#define oAMap(mid) (auto_maps[mid]) + +#define amap_reset() \ + do { \ + int i; \ + for (i = 0; i < NUM_O_AMAP; i++) \ + amap_invalidate(i); \ + } while (0) + +#define amap_note_value(objid) (objTraps[objs[objid].specID].p4) +#define amap_note_string(objid) (amap_str_reref(amap_note_value(objid))) + +#define AMAP_STRING_SIZE 2048 + +#endif diff --git a/engine/src/GameSrc/Headers/amaploop.h b/engine/src/GameSrc/Headers/amaploop.h new file mode 100644 index 0000000..d916f11 --- /dev/null +++ b/engine/src/GameSrc/Headers/amaploop.h @@ -0,0 +1,38 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/cit/src/inc/RCS/amaploop.h $ + * $Revision: 1.1 $ + * $Author: dc $ + * $Date: 1994/04/06 07:27:10 $ + */ + +#include "amap.h" + +#define AMAP_FULLEXPOSE (LL_CHG_BASE << 1) +#define AMAP_MAP_EV (LL_CHG_BASE << 2) +#define AMAP_BUTTON_EV (LL_CHG_BASE << 3) +#define AMAP_MESSAGE_EV (LL_CHG_BASE << 4) + +uchar amap_ms_callback(curAMap *amptr, int x, int y, short action, ubyte but); +uchar amap_scroll_handler(uiEvent *ev, LGRegion *r, intptr_t user_data); +void automap_loop(void); +char *fsmap_get_lev_str(char *buf, int siz); +void fsmap_startup(void); +void fsmap_free(void); diff --git a/engine/src/GameSrc/Headers/ammomfd.h b/engine/src/GameSrc/Headers/ammomfd.h new file mode 100644 index 0000000..c230aca --- /dev/null +++ b/engine/src/GameSrc/Headers/ammomfd.h @@ -0,0 +1,29 @@ +/* + +Copyright (C) 2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef AMMOMFD_H +#define AMMOMFD_H + +#include "event.h" +#include "mfdint.h" + +void mfd_ammo_expose(ubyte control); +uchar mfd_ammo_handler(MFD *m, uiEvent *ev); + +#endif diff --git a/engine/src/GameSrc/Headers/anim.h b/engine/src/GameSrc/Headers/anim.h new file mode 100644 index 0000000..f03f7c4 --- /dev/null +++ b/engine/src/GameSrc/Headers/anim.h @@ -0,0 +1,80 @@ +#ifndef ANIM_H +#define ANIM_H + +#include +//#include +#include +#include <2d.h> +#include <2dres.h> +#include + +// Animation event codes + +typedef uchar AnimCode; + +#define ANCODE_NEWFRAME 0x01 // new frame, update screen +#define ANCODE_END 0x04 // end anim w/o kill (no arg) +#define ANCODE_KILL 0x08 // kill anim (no arg) + +typedef struct { + uchar unknown; + uchar frameRunStart; + uchar frameRunEnd; + fix frameDelay; +} AnimCodeData; + +typedef struct { + LGPoint size; // size of anim + Id frameSetId; // resource id of binary frame set + uchar unknown1[6]; + short unknown2; + AnimCodeData data[]; +} AnimHead; + +// ActAnim: describes an active animation record + +typedef struct ActAnim_ { + LGRegion *reg; + AnimHead *pah; // ptr to animation header + LGPoint loc; + grs_canvas cnv; + void (*notifyFunc) (struct ActAnim_ *paa, AnimCode ancode, AnimCodeData *animData); // owner evt handler + void (*composeFunc) (LGRect *area, ubyte flags); // owner compose handler + Ref currFrameRef; // current frame ref + int curSeq; + int frameNum; + fix animRate; // animation rate + ulong timeContinue; // time at which to advance to next step + void *dataBuffer; // data buffer to use for frames, or NULL + long dataBufferLen; // length of data buffer +} ActAnim; + +// General prototypes: anim.c + +void AnimRecur(); // update animations in progress + +// Play and control anims: anim.c + +ActAnim *AnimPlayRegion(Ref animRef, LGRegion *region, LGPoint loc, char unknown, // play anim into canvas + void (*composeFunc)(LGRect *area, ubyte flags)); + +void AnimKill(ActAnim *paa); // kill one or all anims +void AnimSetNotify(ActAnim *paa, void *powner, AnimCode mask, + void (*func) (ActAnim *, AnimCode ancode, AnimCodeData *animData)); + +// Deal with animation resources: anim.c + +bool AnimPreloadFrames(ActAnim *paa, Ref animRef); // preload an anim's frames + +// Macro to read anim header from resource + +#define AnimReadHeader(ref,pAnhead) (*pAnhead = (* ((AnimHead *) RefGet(ref)))); + +// Convenience macros + +#define AnimSetDataBuffer(paa, buffer) { \ + (paa)->dataBuffer = (buffer); (paa)->dataBufferLen = 0x7FFFFFFFL; } +#define AnimSetDataBufferSafe(paa, buffer, len) { \ + (paa)->dataBuffer = (buffer); (paa)->dataBufferLen = (len); } + +#endif diff --git a/engine/src/GameSrc/Headers/archiveformat.h b/engine/src/GameSrc/Headers/archiveformat.h new file mode 100644 index 0000000..521d15d --- /dev/null +++ b/engine/src/GameSrc/Headers/archiveformat.h @@ -0,0 +1,23 @@ +#if !defined(ARCHIVEFORMAT_H) +#define ARCHIVEFORMAT_H + +#include "res.h" + +// A single 32-bit integer. A couple of resources are just a number. +extern const ResourceFormat U32Format; +#define FORMAT_U32 (&U32Format) + +// The schedules have fixed resource IDs and do not form part of the main +// archive tables. +extern const ResourceFormat ScheduleFormat; +#define FORMAT_SCHEDULE (&ScheduleFormat) + +extern const ResourceFormat ScheduleQueueFormat; +#define FORMAT_SCHEDULE_QUEUE (&ScheduleQueueFormat) + +// Master tables for level archives. +#define MAX_LEVEL_INDEX 51 // based from xx02; level resources go up to xx53 +extern const ResourceFormat LevelVersion11Format[MAX_LEVEL_INDEX+1]; +extern const ResourceFormat LevelVersion12Format[MAX_LEVEL_INDEX+1]; + +#endif // !defined(ARCHIVEFORMAT_H) diff --git a/engine/src/GameSrc/Headers/audiolog.h b/engine/src/GameSrc/Headers/audiolog.h new file mode 100644 index 0000000..493140f --- /dev/null +++ b/engine/src/GameSrc/Headers/audiolog.h @@ -0,0 +1,36 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/inc/RCS/audiolog.h $ + * $Revision: 1.4 $ + * $Author: dc $ + * $Date: 1994/11/19 20:44:42 $ + */ + +//#include "error.h" +//#include "lg.h" + +extern errtype audiolog_init(); +extern errtype audiolog_play(int email_id); +extern errtype audiolog_bark_play(int bark_id); +extern void audiolog_stop(); +extern errtype audiolog_loop_callback(); +extern bool audiolog_playing(int email_id); + +extern uchar audiolog_setting; diff --git a/engine/src/GameSrc/Headers/automap.h b/engine/src/GameSrc/Headers/automap.h new file mode 100644 index 0000000..87feae6 --- /dev/null +++ b/engine/src/GameSrc/Headers/automap.h @@ -0,0 +1,106 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __AUTOMAP_H +#define __AUTOMAP_H + +/* + * $Source: q:/inc/RCS/automap.h $ + * $Revision: 1.5 $ + * $Author: xemu $ + * $Date: 1993/09/02 23:06:30 $ + * + * $Log: automap.h $ + * Revision 1.5 1993/09/02 23:06:30 xemu + * angle me baby + * + * Revision 1.4 1993/08/09 20:45:21 spaz + * Changed some #define's. + * + * + * Revision 1.3 1993/08/09 14:33:12 mahk + * Fixed syntax errors + * + * Revision 1.2 1993/08/09 13:27:47 spaz + * Linked automap stub functions to the MFD system. + * + * Revision 1.1 1993/05/03 11:51:58 xemu + * Initial revision + * + * + */ + +// Includes +#include "rect.h" + +// C Library Includes + +// System Library Includes + +// Master Game Includes + +// Game Library Includes + +// Game Object Includes + +// Defines +typedef struct _AutoText { + char *letters; + LGPoint coordinate; + int color; + struct _AutoText *next_entry; +} AutoText; + +#define AUTOMAP_TERRAIN 0x0001 +#define AUTOMAP_ELEVATION 0x0002 +#define AUTOMAP_SECURITY 0x0004 +#define AUTOMAP_CRITTERS 0x0008 +#define AUTOMAP_INFORMATION 0x0010 +#define AUTOMAP_ALIGNMENT 0x0020 + +#define AUTOMAP_ZOOMIN_X 16 +#define AUTOMAP_ZOOMIN_Y 16 +#define AUTOMAP_ZOOMOUT_X 32 +#define AUTOMAP_ZOOMOUT_Y 32 + +// Prototypes + +// Specify which level is the "current" automap level for viewing +errtype automap_current(int level); + +// Set what kinds of information are shown by the automap +errtype automap_infotype(ulong map_info); + +// Draw the automap, scaled to specified size, in the area with upper left corner +// as specified. +errtype automap_draw(int map_size, LGPoint ul_coord); + +// Get a handle to a new text entry field on the automap, at specified coords +errtype automap_new_entry(LGPoint coord, AutoText *new_text); + +// Delete a given text entry +errtype automap_delete_entry(AutoText *victim); + +// Stubs for the MFD system +void mfd_map_expose(MFD *m, ubyte control); +errtype mfd_map_init(MFD_Func *); +uchar mfd_map_handler(MFD *m, uiEvent *e); + +// Globals + +#endif // __AUTOMAP_H diff --git a/engine/src/GameSrc/Headers/bark.h b/engine/src/GameSrc/Headers/bark.h new file mode 100644 index 0000000..f79ac5c --- /dev/null +++ b/engine/src/GameSrc/Headers/bark.h @@ -0,0 +1,55 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/inc/RCS/bark.h $ + * $Revision: 1.3 $ + * $Author: tjs $ + * $Date: 1994/08/02 13:56:04 $ + * + * $Log: bark.h $ + * Revision 1.3 1994/08/02 13:56:04 tjs + * Rescaled bark timeouts. + * + * Revision 1.2 1994/05/26 20:52:23 tjs + * Added null bark timeout + * + * Revision 1.1 1994/05/26 17:59:08 tjs + * Initial revision + * + * + * + */ +#ifndef BARK_H +#define BARK_H + +#include "mfdint.h" // for MFD struct + +#define MFD_BARK_FUNC 17 + +#define NULL_BARK_TIMEOUT 3 + +#define mfd_bark_string (*(int *)(&player_struct.mfd_func_data[MFD_BARK_FUNC][0])) +#define mfd_bark_speaker (*(ObjID *)(&player_struct.mfd_func_data[MFD_BARK_FUNC][sizeof(int)])) +#define mfd_bark_color (player_struct.mfd_func_data[MFD_BARK_FUNC][sizeof(int) + sizeof(ObjID)]) +#define mfd_bark_mug (player_struct.mfd_func_data[MFD_BARK_FUNC][sizeof(int) + sizeof(ObjID) + sizeof(uchar)]) + +void mfd_bark_expose(MFD *mfd, ubyte control); +void long_bark(ObjID speaker_id, uchar mug_id, int string_id, ubyte color); + +#endif \ No newline at end of file diff --git a/engine/src/GameSrc/Headers/biohelp.h b/engine/src/GameSrc/Headers/biohelp.h new file mode 100644 index 0000000..d33953f --- /dev/null +++ b/engine/src/GameSrc/Headers/biohelp.h @@ -0,0 +1,31 @@ +/* + +Copyright (C) 2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef BIOHELP_H +#define BIOHELP_H + +#include "mfdint.h" + +errtype biohelp_load_cursor(); + +errtype mfd_biohelp_init(MFD_Func *f); +void mfd_biohelp_expose(MFD *mfd, ubyte control); +uchar mfd_biohelp_handler(MFD *m, uiEvent *e); + +#endif diff --git a/engine/src/GameSrc/Headers/biotrax.h b/engine/src/GameSrc/Headers/biotrax.h new file mode 100644 index 0000000..f9d98ca --- /dev/null +++ b/engine/src/GameSrc/Headers/biotrax.h @@ -0,0 +1,42 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __BIOTRAX_H +#define __BIOTRAX_H + +/* + * $Source: n:/project/cit/src/inc/RCS/biotrax.h $ + * $Revision: 1.1 $ + * $Author: mahk $ + * $Date: 1994/02/23 13:20:34 $ + * + */ + +// Defines +#define ENERGY_TRACK 0 +#define BIOHAZARD_TRACK (ENERGY_TRACK + 1) +#define RADIATION_TRACK (BIOHAZARD_TRACK + 1) +#define LOOPLINE_TRACK (RADIATION_TRACK + 1) +#define HEART_TRACK (NUM_BIO_TRACKS - 2) +#define SINE_TRACK (NUM_BIO_TRACKS - 1) + +// Prototypes + +// Globals + +#endif // __BIOTRAX_H diff --git a/engine/src/GameSrc/Headers/canvchek.h b/engine/src/GameSrc/Headers/canvchek.h new file mode 100644 index 0000000..0dc4fc4 --- /dev/null +++ b/engine/src/GameSrc/Headers/canvchek.h @@ -0,0 +1,32 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __CANVCHEK_H +#define __CANVCHEK_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/canvchek.h $ + * $Revision: 1.1 $ + * $Author: mahk $ + * $Date: 1994/11/09 20:44:24 $ + * + */ + +#define is_onscreen() (grd_canvas != NULL && grd_canvas->bm.type == BMT_DEVICE) + +#endif // __CANVCHEK_H diff --git a/engine/src/GameSrc/Headers/cardmfd.h b/engine/src/GameSrc/Headers/cardmfd.h new file mode 100644 index 0000000..62e14d9 --- /dev/null +++ b/engine/src/GameSrc/Headers/cardmfd.h @@ -0,0 +1,25 @@ +/* + +Copyright (C) 2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef CARDMFD_H +#define CARDMFD_H + +void mfd_accesscard_expose(MFD *mfd, ubyte control); + +#endif diff --git a/engine/src/GameSrc/Headers/cit2d.h b/engine/src/GameSrc/Headers/cit2d.h new file mode 100644 index 0000000..ffb4e88 --- /dev/null +++ b/engine/src/GameSrc/Headers/cit2d.h @@ -0,0 +1,49 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __CIT2D_H +#define __CIT2D_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/cit2d.h $ + * $Revision: 1.4 $ + * $Author: mahk $ + * $Date: 1994/08/18 17:20:17 $ + * + */ + +// Stuff that we wish the 2d had, but is too cool. + +#define FONT_IS_MONO(fontptr) ((fontptr)->id != 0xCCCC) + +void draw_shadowed_string(char *s, short x, short y, uchar shadow); + +#ifdef BROKEN_SAFE_CLIPRECT +#define safe_set_cliprect(a, b, c, d) \ + do { \ + short _safe_x = a; \ + short _safe_y = b; \ + short _safe_p = c; \ + short _safe_q = d; \ + gr_safe_set_cliprect(_safe_x, _safe_y, _safe_p, _safe_q); \ + } while (0) +#else +#define safe_set_cliprect(a, b, c, d) gr_safe_set_cliprect(a, b, c, d) +#endif + +#endif // __CIT2D_H diff --git a/engine/src/GameSrc/Headers/citalog.h b/engine/src/GameSrc/Headers/citalog.h new file mode 100644 index 0000000..efa63ba --- /dev/null +++ b/engine/src/GameSrc/Headers/citalog.h @@ -0,0 +1,143 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* This file created by RESTOOL */ + +#ifndef __CITALOG_H +#define __CITALOG_H + +#define RES_alog_email0 0xab5 // (2741) +#define RES_alog_email1 0xab6 // (2742) +#define RES_alog_email2 0xab7 // (2743) +#define RES_alog_email3 0xab8 // (2744) +#define RES_alog_email4 0xab9 // (2745) +#define RES_alog_email5 0xaba // (2746) +#define RES_alog_email6 0xabb // (2747) +#define RES_alog_email7 0xabc // (2748) +#define RES_alog_email8 0xabd // (2749) +#define RES_alog_email9 0xabe // (2750) +#define RES_alog_email10 0xabf // (2751) +#define RES_alog_email11 0xac0 // (2752) +#define RES_alog_email12 0xac1 // (2753) +#define RES_alog_email13 0xac2 // (2754) +#define RES_alog_email14 0xac3 // (2755) +#define RES_alog_email15 0xac4 // (2756) +#define RES_alog_email16 0xac5 // (2757) +#define RES_alog_email17 0xac6 // (2758) +#define RES_alog_email18 0xac7 // (2759) +#define RES_alog_email19 0xac8 // (2760) +#define RES_alog_email20 0xac9 // (2761) +#define RES_alog_email21 0xaca // (2762) +#define RES_alog_email22 0xacb // (2763) +#define RES_alog_email23 0xacc // (2764) +#define RES_alog_email24 0xacd // (2765) +#define RES_alog_email25 0xace // (2766) +#define RES_alog_email26 0xacf // (2767) +#define RES_alog_email27 0xad0 // (2768) +#define RES_alog_email28 0xad1 // (2769) +#define RES_alog_email29 0xad2 // (2770) +#define RES_alog_log00 0xae4 // (2788) +#define RES_alog_log01 0xae5 // (2789) +#define RES_alog_log02 0xae6 // (2790) +#define RES_alog_log03 0xae7 // (2791) +#define RES_alog_log04 0xae8 // (2792) +#define RES_alog_log05 0xae9 // (2793) +#define RES_alog_log10 0xaf4 // (2804) +#define RES_alog_log11 0xaf5 // (2805) +#define RES_alog_log12 0xaf6 // (2806) +#define RES_alog_log13 0xaf7 // (2807) +#define RES_alog_log14 0xaf8 // (2808) +#define RES_alog_log15 0xaf9 // (2809) +#define RES_alog_log16 0xafa // (2810) +#define RES_alog_log17 0xafb // (2811) +#define RES_alog_log18 0xafc // (2812) +#define RES_alog_log19 0xafd // (2813) +#define RES_alog_log110 0xafe // (2814) +#define RES_alog_log111 0xaff // (2815) +#define RES_alog_log112 0xb00 // (2816) +#define RES_alog_log113 0xb01 // (2817) +#define RES_alog_log114 0xb02 // (2818) +#define RES_alog_log115 0xb03 // (2819) +#define RES_alog_log20 0xb04 // (2820) +#define RES_alog_log21 0xb05 // (2821) +#define RES_alog_log22 0xb06 // (2822) +#define RES_alog_log23 0xb07 // (2823) +#define RES_alog_log24 0xb08 // (2824) +#define RES_alog_log25 0xb09 // (2825) +#define RES_alog_log26 0xb0a // (2826) +#define RES_alog_log27 0xb0b // (2827) +#define RES_alog_log28 0xb0c // (2828) +#define RES_alog_log29 0xb0d // (2829) +#define RES_alog_log210 0xb0e // (2830) +#define RES_alog_log211 0xb0f // (2831) +#define RES_alog_log212 0xb10 // (2832) +#define RES_alog_log30 0xb14 // (2836) +#define RES_alog_log31 0xb15 // (2837) +#define RES_alog_log32 0xb16 // (2838) +#define RES_alog_log33 0xb17 // (2839) +#define RES_alog_log34 0xb18 // (2840) +#define RES_alog_log35 0xb19 // (2841) +#define RES_alog_log36 0xb1a // (2842) +#define RES_alog_log37 0xb1b // (2843) +#define RES_alog_log38 0xb1c // (2844) +#define RES_alog_log40 0xb24 // (2852) +#define RES_alog_log41 0xb25 // (2853) +#define RES_alog_log42 0xb26 // (2854) +#define RES_alog_log43 0xb27 // (2855) +#define RES_alog_log44 0xb28 // (2856) +#define RES_alog_log45 0xb29 // (2857) +#define RES_alog_log46 0xb2a // (2858) +#define RES_alog_log50 0xb34 // (2868) +#define RES_alog_log51 0xb35 // (2869) +#define RES_alog_log52 0xb36 // (2870) +#define RES_alog_log53 0xb37 // (2871) +#define RES_alog_log54 0xb38 // (2872) +#define RES_alog_log55 0xb39 // (2873) +#define RES_alog_log56 0xb3a // (2874) +#define RES_alog_log57 0xb3b // (2875) +#define RES_alog_log58 0xb3c // (2876) +#define RES_alog_log59 0xb3d // (2877) +#define RES_alog_log510 0xb3e // (2878) +#define RES_alog_log511 0xb3f // (2879) +#define RES_alog_log60 0xb44 // (2884) +#define RES_alog_log61 0xb45 // (2885) +#define RES_alog_log62 0xb46 // (2886) +#define RES_alog_log63 0xb47 // (2887) +#define RES_alog_log64 0xb48 // (2888) +#define RES_alog_log65 0xb49 // (2889) +#define RES_alog_log66 0xb4a // (2890) +#define RES_alog_log67 0xb4b // (2891) +#define RES_alog_log68 0xb4c // (2892) +#define RES_alog_log69 0xb4d // (2893) +#define RES_alog_log70 0xb54 // (2900) +#define RES_alog_log71 0xb55 // (2901) +#define RES_alog_log72 0xb56 // (2902) +#define RES_alog_log73 0xb57 // (2903) +#define RES_alog_log74 0xb58 // (2904) +#define RES_alog_log75 0xb59 // (2905) +#define RES_alog_log76 0xb5a // (2906) +#define RES_alog_log77 0xb5b // (2907) +#define RES_alog_log78 0xb5c // (2908) +#define RES_alog_log80 0xb64 // (2916) +#define RES_alog_log81 0xb65 // (2917) +#define RES_alog_log82 0xb66 // (2918) +#define RES_alog_log83 0xb67 // (2919) +#define RES_alog_log84 0xb68 // (2920) +#define RES_alog_log85 0xb69 // (2921) + +#endif diff --git a/engine/src/GameSrc/Headers/citbark.h b/engine/src/GameSrc/Headers/citbark.h new file mode 100644 index 0000000..7a67446 --- /dev/null +++ b/engine/src/GameSrc/Headers/citbark.h @@ -0,0 +1,132 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* This file created by RESTOOL */ + +#ifndef __CITBARK_H +#define __CITBARK_H + +#define RES_alog_bark0 0xc1c // (3100) +#define RES_alog_bark1 0xc1d // (3101) +#define RES_alog_bark2 0xc1e // (3102) +#define RES_alog_bark3 0xc1f // (3103) +#define RES_alog_bark4 0xc20 // (3104) +#define RES_alog_bark5 0xc21 // (3105) +#define RES_alog_bark6 0xc22 // (3106) +#define RES_alog_bark7 0xc23 // (3107) +#define RES_alog_bark8 0xc24 // (3108) +#define RES_alog_bark9 0xc25 // (3109) +#define RES_alog_bark10 0xc26 // (3110) +#define RES_alog_bark11 0xc27 // (3111) +#define RES_alog_bark12 0xc28 // (3112) +#define RES_alog_bark13 0xc29 // (3113) +#define RES_alog_bark14 0xc2a // (3114) +#define RES_alog_bark15 0xc2b // (3115) +#define RES_alog_bark16 0xc2c // (3116) +#define RES_alog_bark17 0xc2d // (3117) +#define RES_alog_bark18 0xc2e // (3118) +#define RES_alog_bark19 0xc2f // (3119) +#define RES_alog_bark20 0xc30 // (3120) +#define RES_alog_bark21 0xc31 // (3121) +#define RES_alog_bark22 0xc32 // (3122) +#define RES_alog_bark23 0xc33 // (3123) +#define RES_alog_bark24 0xc34 // (3124) +#define RES_alog_bark25 0xc35 // (3125) +#define RES_alog_bark26 0xc36 // (3126) +#define RES_alog_bark27 0xc37 // (3127) +#define RES_alog_bark28 0xc38 // (3128) +#define RES_alog_bark29 0xc39 // (3129) +#define RES_alog_bark30 0xc3a // (3130) +#define RES_alog_bark31 0xc3b // (3131) +#define RES_alog_bark32 0xc3c // (3132) +#define RES_alog_bark33 0xc3d // (3133) +#define RES_alog_bark34 0xc3e // (3134) +#define RES_alog_bark35 0xc3f // (3135) +#define RES_alog_bark36 0xc40 // (3136) +#define RES_alog_bark37 0xc41 // (3137) +#define RES_alog_bark38 0xc42 // (3138) +#define RES_alog_bark39 0xc43 // (3139) +#define RES_alog_bark40 0xc44 // (3140) +#define RES_alog_bark41 0xc45 // (3141) +#define RES_alog_bark42 0xc46 // (3142) +#define RES_alog_bark43 0xc47 // (3143) +#define RES_alog_bark44 0xc48 // (3144) +#define RES_alog_bark45 0xc49 // (3145) +#define RES_alog_bark46 0xc4a // (3146) +#define RES_alog_bark47 0xc4b // (3147) +#define RES_alog_bark48 0xc4c // (3148) +#define RES_alog_bark49 0xc4d // (3149) +#define RES_alog_bark50 0xc4e // (3150) +#define RES_alog_bark51 0xc4f // (3151) +#define RES_alog_bark52 0xc50 // (3152) +#define RES_alog_bark53 0xc51 // (3153) +#define RES_alog_bark54 0xc52 // (3154) +#define RES_alog_bark55 0xc53 // (3155) +#define RES_alog_bark56 0xc54 // (3156) +#define RES_alog_bark57 0xc55 // (3157) +#define RES_alog_bark58 0xc56 // (3158) +#define RES_alog_bark59 0xc57 // (3159) +#define RES_alog_bark60 0xc58 // (3160) +#define RES_alog_bark61 0xc59 // (3161) +#define RES_alog_bark62 0xc5a // (3162) +#define RES_alog_bark63 0xc5b // (3163) +#define RES_alog_bark64 0xc5c // (3164) +#define RES_alog_bark65 0xc5d // (3165) +#define RES_alog_bark66 0xc5e // (3166) +#define RES_alog_bark67 0xc5f // (3167) +#define RES_alog_bark68 0xc60 // (3168) +#define RES_alog_bark69 0xc61 // (3169) +#define RES_alog_bark70 0xc62 // (3170) +#define RES_alog_bark71 0xc63 // (3171) +#define RES_alog_bark72 0xc64 // (3172) +#define RES_alog_bark73 0xc65 // (3173) +#define RES_alog_bark74 0xc66 // (3174) +#define RES_alog_bark75 0xc67 // (3175) +#define RES_alog_bark76 0xc68 // (3176) +#define RES_alog_bark77 0xc69 // (3177) +#define RES_alog_bark78 0xc6a // (3178) +#define RES_alog_bark79 0xc6b // (3179) +#define RES_alog_bark80 0xc6c // (3180) +#define RES_alog_bark81 0xc6d // (3181) +#define RES_alog_bark82 0xc6e // (3182) +#define RES_alog_bark83 0xc6f // (3183) +#define RES_alog_bark84 0xc70 // (3184) +#define RES_alog_bark85 0xc71 // (3185) +#define RES_alog_bark86 0xc72 // (3186) +#define RES_alog_bark87 0xc73 // (3187) +#define RES_alog_bark88 0xc74 // (3188) +#define RES_alog_bark89 0xc75 // (3189) +#define RES_alog_bark90 0xc76 // (3190) +#define RES_alog_bark91 0xc77 // (3191) +#define RES_alog_bark92 0xc78 // (3192) +#define RES_alog_bark93 0xc79 // (3193) +#define RES_alog_bark94 0xc7a // (3194) +#define RES_alog_bark95 0xc7b // (3195) +#define RES_alog_bark96 0xc7c // (3196) +#define RES_alog_bark97 0xc7d // (3197) +#define RES_alog_bark98 0xc7e // (3198) +#define RES_alog_bark99 0xc7f // (3199) +#define RES_alog_bark100 0xc80 // (3200) +#define RES_alog_bark101 0xc81 // (3201) +#define RES_alog_bark102 0xc82 // (3202) +#define RES_alog_bark103 0xc83 // (3203) +#define RES_alog_bark104 0xc84 // (3204) +#define RES_alog_bark105 0xc85 // (3205) +#define RES_alog_bark106 0xc86 // (3206) + +#endif diff --git a/engine/src/GameSrc/Headers/citmat.h b/engine/src/GameSrc/Headers/citmat.h new file mode 100644 index 0000000..3979364 --- /dev/null +++ b/engine/src/GameSrc/Headers/citmat.h @@ -0,0 +1,28 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* This file created by RESTOOL */ + +#ifndef __CITMAT_H +#define __CITMAT_H + +#define RES_smallTextureMaps 0x141 // (321) +#define RES_materialMaps 0x1db // (475) +#define RES_customTextureMaps 0x884 // (2180) + +#endif diff --git a/engine/src/GameSrc/Headers/citres.h b/engine/src/GameSrc/Headers/citres.h new file mode 100644 index 0000000..dd51a7b --- /dev/null +++ b/engine/src/GameSrc/Headers/citres.h @@ -0,0 +1,55 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __CITRES_H +#define __CITRES_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/citres.h $ + * $Revision: 1.8 $ + * $Author: mahk $ + * $Date: 1994/09/01 18:31:03 $ + * + */ + +// Includes + +// Defines + +// Typedefs + +// Prototypes +#define lock_bitmap_from_ref(r) lock_bitmap_from_ref_anchor(r, NULL) +grs_bitmap *lock_bitmap_from_ref_anchor(Ref r, LGRect *anchor); +#define get_bitmap_from_ref(r) get_bitmap_from_ref_anchor(r, NULL) +grs_bitmap *get_bitmap_from_ref_anchor(Ref r, LGRect *anchor); +errtype load_bitmap_from_res(grs_bitmap *bmp, Id id_num, int i, uchar transp, LGRect *anchor); + +// loads in a bitmap or a bitmap cursor, malloc'ing the bits +// field. +errtype simple_load_res_bitmap(grs_bitmap *bmp, Ref rid); +errtype simple_load_res_bitmap_cursor(LGCursor *c, grs_bitmap *bmp, Ref rid); + +// loads a bitmap, specifying whether to malloc the bits or not. +errtype load_res_bitmap(grs_bitmap *bmp, Ref rid, uchar alloc); +errtype load_res_bitmap_cursor(LGCursor *c, grs_bitmap *bmp, Ref rid, uchar alloc); +errtype load_hires_bitmap_cursor(LGCursor *c, grs_bitmap *bmp, Ref rid, uchar alloc); + +// Globals + +#endif // __CITRES_H diff --git a/engine/src/GameSrc/Headers/colors.h b/engine/src/GameSrc/Headers/colors.h new file mode 100644 index 0000000..5259b38 --- /dev/null +++ b/engine/src/GameSrc/Headers/colors.h @@ -0,0 +1,45 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __COLORS_H +#define __COLORS_H +/* + * $Source: n:/project/cit/src/inc/RCS/colors.h $ + * $Revision: 1.3 $ + * $Author: dc $ + * $Date: 1993/10/18 03:28:38 $ + */ + +// Color defines for the Citadel Palette + +#define PURPLE_BASE 0x20 +#define TAN_BASE 0x29 +#define RED_BASE 0x30 +#define ORANGE_YELLOW_BASE 0x3d +#define GREEN_YELLOW_BASE 0x4b +#define GREEN_BASE 0x58 +#define TURQUOISE_BASE 0x65 +#define BLUE_BASE 0x70 +#define RED_BROWN_BASE 0x7f +#define BROWN_BASE 0x83 +#define TAN_GRAY_BASE 0xc0 +#define GRAY_BASE 0xd0 +#define BLACK 0x00 +#define WHITE 0x02 + +#endif // __COLORS_H diff --git a/engine/src/GameSrc/Headers/combat.h b/engine/src/GameSrc/Headers/combat.h new file mode 100644 index 0000000..f610e2f --- /dev/null +++ b/engine/src/GameSrc/Headers/combat.h @@ -0,0 +1,79 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __COMBAT_H +#define __COMBAT_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/combat.h $ + * $Revision: 1.21 $ + * $Author: minman $ + * $Date: 1994/07/21 01:45:51 $ + * + * + */ + +// Includes +#include "objects.h" + +#define RAYCAST_ATTACK_SIZE (fix_make(0, 0x0400)) +#define NO_RAYCAST_KICKBACK_SPEED (fix_make(0, 0x0100)) + +// all temporary stuff +typedef struct { + fix x; + fix y; + fix z; +} Combat_Pt; + +typedef struct { + fix dx; + fix dy; + fix dz; + Combat_Pt origin; + fix mass; + fix size; + fix speed; + fix range; + physics_handle exclusion; +} Combat_Ray; + +// ******* RAYCAST FUNCTIONS ************* +// the following ray_cast_* functions do the same thing, but allow for different types of input +// returns the first object hit, and returns OBJ_NULL, if ray does not hit any object + +// does a ray cast from object src to the ObjLoc dest +ObjID ray_cast_attack(ObjID src, ObjLoc dest, fix bullet_mass, fix bullet_size, fix bullet_speed, fix bullet_range); + +// does a ray cast between point src to point dest +ObjID ray_cast_points(ObjID exclusion, Combat_Pt src, Combat_Pt dest, fix bullet_mass, fix bullet_size, + fix bullet_speed, fix bullet_range); + +// does a ray cast from point src in the direction of vector +// returns hit location at src +ObjID ray_cast_vector(ObjID exclusion, Combat_Pt *src, Combat_Pt vector, fix bullet_mass, fix bullet_size, + fix bullet_speed, fix bullet_range); + +// does a ray cast from object src to object dest +ObjID ray_cast_objects(ObjID src, ObjID dest, fix bullet_mass, fix bullet_size, fix bullet_speed, fix bullet_range); + +// given the point on the 3d view window, returns the vector in that direction to +// be used for ray_tracing +void find_fire_vector(LGPoint *pt, Combat_Pt *vector); + +#endif // __COMBAT_H diff --git a/engine/src/GameSrc/Headers/cone.h b/engine/src/GameSrc/Headers/cone.h new file mode 100644 index 0000000..4683f53 --- /dev/null +++ b/engine/src/GameSrc/Headers/cone.h @@ -0,0 +1,74 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __CONE_H +#define __CONE_H + +/* + * $Source: n:/project/cit/src/inc/RCS/cone.h $ + * $Revision: 1.8 $ + * $Author: dc $ + * $Date: 1994/01/02 17:15:58 $ + * + * $Log: cone.h $ + * Revision 1.8 1994/01/02 17:15:58 dc + * indoor terrain renderer + * + * Revision 1.7 1993/11/16 16:26:05 minman + * got rid of testing prototypes so there's not + * a depnedency on this file from menus.c + * + * Revision 1.6 1993/11/16 16:22:43 minman + * redid find_view_area prototype + * + * Revision 1.5 1993/10/19 19:40:08 minman + * added simple_cone_clip_pass + * + * Revision 1.4 1993/09/02 23:07:14 xemu + * angle me baby + * + * Revision 1.3 1993/07/01 01:03:52 minman + * added cone test + * + * Revision 1.2 1993/06/24 01:58:49 minman + * find_view_area takes a radius argument now + * + * Revision 1.1 1993/06/17 20:13:19 minman + * Initial revision + * + * + */ + +// Includes + +// finds the view area polygon - modifies first argument to become +// an array of fix points that represents the view area +// polygon is in clockwise order +// x,y order - *count is the number of points in the polygon. +// return TRUE if it's a valid polygon (not one or two points) + +uchar find_view_area(fix *cone_list, fix fix_floor, fix fix_roof, int *count, fix radius); + +// run the cone clip and render it. +void simple_cone_clip_pass(void); + +extern fix span_lines[8]; +extern byte span_index[2]; +extern fix span_intersect[4]; + +#endif // __CONE_H diff --git a/engine/src/GameSrc/Headers/criterr.h b/engine/src/GameSrc/Headers/criterr.h new file mode 100644 index 0000000..f0383c0 --- /dev/null +++ b/engine/src/GameSrc/Headers/criterr.h @@ -0,0 +1,52 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __CRITERR_H +#define __CRITERR_H + +/* + * $Source: n:/project/cit/src/inc/RCS/criterr.h $ + * $Revision: 1.3 $ + * $Author: xemu $ + * $Date: 1994/05/26 17:20:11 $ + * + */ + +// Defines +#define CRITERR_CLASSES 16 +#define CRITERR_CODES 0x10000 + +#define CRITERR_CFG 0x1000 // config errors +#define CRITERR_RES 0x2000 // Resource errors +#define CRITERR_MEM 0x3000 // Out of memory +#define CRITERR_FILE 0x4000 // Misc file error +#define CRITERR_EXEC 0x5000 // execution error +#define CRITERR_MISC 0xF000 // miscellaneous/glitches +#define CRITERR_TEST 0x0000 // Test code + +#define NO_CRITICAL_ERROR 0x0000 + +// Prototypes +void criterr_init(void); +// Initializes critical error system. + +void critical_error(unsigned short code); +// Exits the program with error status, printing +// the error string for the specified code. + +#endif // __CRITERR_H diff --git a/engine/src/GameSrc/Headers/cutsloop.h b/engine/src/GameSrc/Headers/cutsloop.h new file mode 100644 index 0000000..3e7e174 --- /dev/null +++ b/engine/src/GameSrc/Headers/cutsloop.h @@ -0,0 +1,34 @@ + +#ifndef __CUTSLOOP_H +#define __CUTSLOOP_H + +// Includes + +// C Library Includes + +// System Library Includes + +// Master Game Includes + +// Game Library Includes + +// Game Object Includes + +// Defines + +// CC: These are all wrong, should find the right resource IDs +#define START_CUTSCENE 0 +#define DEATH_CUTSCENE 1 +#define WIN_CUTSCENE 2 +#define ENDGAME_CUTSCENE 3 + +// Prototypes + +void cutscene_loop(void); +void cutscene_start(void); +void cutscene_exit(void); +short play_cutscene(int id, bool show_credits); + +// Globals + +#endif // __CUTSLOOP_H \ No newline at end of file diff --git a/engine/src/GameSrc/Headers/cyber.h b/engine/src/GameSrc/Headers/cyber.h new file mode 100644 index 0000000..4850c10 --- /dev/null +++ b/engine/src/GameSrc/Headers/cyber.h @@ -0,0 +1,59 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include "player.h" + +errtype exit_cyberspace_stuff(); +errtype enter_cyberspace_stuff(char dest_level); +errtype early_exit_cyberspace_stuff(); +errtype check_cspace_death(); + +#define NUM_CS_EFFECTS 3 + +#define CS_TURBO_EFF 0 +#define CS_DECOY_EFF 1 +#define CS_MATCHBOX_EFF 2 + +#define CYBER_DIFF (player_struct.difficulty[CYBER_DIFF_INDEX]) + +//#define BASE_CSPACE_TIME ((CYBER_DIFF == 0) ? (CIT_CYCLE * 3600) : ((CYBER_DIFF == 1) ? (CIT_CYCLE * 720) : +//(CIT_CYCLE * 360))) +#define BASE_CSPACE_TIME 1800 * CIT_CYCLE + +#define CSPACE_MIN_TIME (CYBER_DIFF ? CIT_CYCLE * 90 : BASE_CSPACE_TIME) +#define CSPACE_MAX_TIME \ + ((CYBER_DIFF == 0) ? BASE_CSPACE_TIME \ + : (CYBER_DIFF == 1) ? (BASE_CSPACE_TIME / 3) \ + : (CYBER_DIFF == 2) ? (BASE_CSPACE_TIME / 6) : (BASE_CSPACE_TIME / 12)) + +// no time penalties at diff 0 and 1, harsh ones at 3 +#define CSPACE_EXIT_PENALTY ((CYBER_DIFF <= 2) ? 0 : (CIT_CYCLE * 5)) +#define CSPACE_DEATH_PENALTY ((CYBER_DIFF < 2) ? 0 : ((CYBER_DIFF == 2) ? (CIT_CYCLE * 3) : (CIT_CYCLE * 12))) +#define CSPACE_MUX_BONUS CIT_CYCLE * 30 + +#define CYBERMINE_DAMAGE 15 +#define CYBERHEAL_QUANTITY 70 + +// Time until SHODAN appears to kick the player out of cyberspace. +extern uint32_t time_until_shodan_avatar; + +extern void (*cspace_effect_turnoff[])(uchar, uchar); +extern ulong cspace_effect_times[NUM_CS_EFFECTS]; +extern ulong cspace_effect_durations[NUM_CS_EFFECTS]; +extern ObjID cspace_decoy_obj; +extern ObjLoc recall_objloc; diff --git a/engine/src/GameSrc/Headers/cybermfd.h b/engine/src/GameSrc/Headers/cybermfd.h new file mode 100644 index 0000000..f3e34c8 --- /dev/null +++ b/engine/src/GameSrc/Headers/cybermfd.h @@ -0,0 +1,28 @@ +/* + +Copyright (C) 2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef CYBERMFD_H +#define CYBERMFD_H + +#include "mfdint.h" + +// The cyberspace MFD +void mfd_cspace_expose(MFD *mfd, ubyte control); + +#endif diff --git a/engine/src/GameSrc/Headers/cybmem.h b/engine/src/GameSrc/Headers/cybmem.h new file mode 100644 index 0000000..b611928 --- /dev/null +++ b/engine/src/GameSrc/Headers/cybmem.h @@ -0,0 +1,64 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __CYBMEM_H +#define __CYBMEM_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/cybmem.h $ + * $Revision: 1.23 $ + * $Author: xemu $ + * $Date: 1994/11/01 09:19:29 $ + * + */ + +// Defines + +// Typedefs + +// Prototypes +errtype load_dynamic_memory(int mask); +errtype free_dynamic_memory(int mask); +int avail_memory(int debug_src); +void Memory_Check(); +int slorkatron_memory_check(); +int flush_resource_cache(void); + +// If LZW stuff ever gets lots more efficient, may need to raise this up some. +#define BIG_BUFFER_SIZE (LZW_BUFF_SIZE + 3) + +#define MINIMUM_GAME_THRESHOLD 540000 // this will be tweaked as appropriate... +#define BIG_CACHE_THRESHOLD MINIMUM_GAME_THRESHOLD + 1900000 + +#define EXTRA_TMAP_THRESHOLD BIG_CACHE_THRESHOLD + +#define BLEND_THRESHOLD EXTRA_TMAP_THRESHOLD + 64000 +#define BIG_HACKCAM_THRESHOLD BLEND_THRESHOLD + 32000 + +#define PRELOAD_ANIMATION_THRESHOLD EXTRA_TMAP_THRESHOLD // for simplicity, can be set different if we are psyched + +// Globals +#ifdef __CYBMEM_SRC +uchar big_buffer[BIG_BUFFER_SIZE + 16384]; +int start_mem; +#else +extern uchar big_buffer[BIG_BUFFER_SIZE + 16384]; +extern int start_mem; +#endif + +#endif // __CYBMEM_H diff --git a/engine/src/GameSrc/Headers/cybrloop.h b/engine/src/GameSrc/Headers/cybrloop.h new file mode 100644 index 0000000..1a548bf --- /dev/null +++ b/engine/src/GameSrc/Headers/cybrloop.h @@ -0,0 +1,59 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __CYBRLOOP_H +#define __CYBRLOOP_H + +/* + * $Source: q:/inc/RCS/cybrloop.h $ + * $Revision: 1.2 $ + * $Author: xemu $ + * $Date: 1993/09/02 23:07:25 $ + * + * $Log: cybrloop.h $ + * Revision 1.2 1993/09/02 23:07:25 xemu + * angle me baby + * + * Revision 1.1 1993/05/14 15:46:51 xemu + * Initial revision + * + * + */ + +// Includes + +// C Library Includes + +// System Library Includes + +// Master Game Includes + +// Game Library Includes + +// Game Object Includes + +// Defines +#define CL_VITALS_UPDATE 0 +#define CL_MFD_UPDATE 1 + +// Prototypes +void cyber_loop(void); + +// Globals + +#endif // __CYBRLOOP_H diff --git a/engine/src/GameSrc/Headers/cybrnd.h b/engine/src/GameSrc/Headers/cybrnd.h new file mode 100644 index 0000000..ab33800 --- /dev/null +++ b/engine/src/GameSrc/Headers/cybrnd.h @@ -0,0 +1,69 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __CYBRND_H +#define __CYBRND_H + +/* + * $Source: n:/project/cit/src/inc/RCS/cybrnd.h $ + * $Revision: 1.5 $ + * $Author: minman $ + * $Date: 1993/12/15 22:56:25 $ + * + * $Log: cybrnd.h $ + * Revision 1.5 1993/12/15 22:56:25 minman + * added effect random var + * + * Revision 1.4 1993/09/02 23:07:26 xemu + * angle me baby + * + * Revision 1.3 1993/08/17 14:17:55 minman + * added make-info random number + * + * Revision 1.2 1993/08/12 19:45:06 minman + * added grenade random no + * + * Revision 1.1 1993/08/12 19:22:58 minman + * Initial revision + * + * + */ + +#include "rnd.h" + +#ifdef __CYBRND_SRC +RNDSTREAM_STD(damage_rnd); +RNDSTREAM_STD(grenade_rnd); +RNDSTREAM_STD(obj_make_rnd); +RNDSTREAM_STD(effect_rnd); +#else +extern RndStream damage_rnd; +extern RndStream grenade_rnd; +extern RndStream obj_make_rnd; +extern RndStream effect_rnd; +#endif + +// so i feel like using prime numbers - got a problem with that???? +#define DAMAGE_SEED 23 +#define GRENADE_SEED 31 +#define OBJMAKE_SEED 29 +#define EFFECT_SEED 37 + +void rnd_init(void); + +#endif // __CYBRND_H diff --git a/engine/src/GameSrc/Headers/cybstrng.h b/engine/src/GameSrc/Headers/cybstrng.h new file mode 100644 index 0000000..b7d24c5 --- /dev/null +++ b/engine/src/GameSrc/Headers/cybstrng.h @@ -0,0 +1,1565 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* This file created by RESTOOL */ + +#ifndef __CYBSTRNG_H +#define __CYBSTRNG_H + +#define RES_traps 0x867 // (2151) +#define REF_STR_TrapZeroMessage 0x8670000 +#define RES_words 0x868 // (2152) +#define REF_STR_WordZero 0x8680000 +#define RES_names 0x869 // (2153) +#define REF_STR_Name0 0x8690000 +#define RES_texnames 0x86a // (2154) +#define RES_texuse 0x86b // (2155) +#define RES_inventory 0x86c // (2156) +#define REF_STR_Null 0x86c0000 +#define REF_STR_WeaponTitle 0x86c0001 +#define REF_STR_AmmoTitle 0x86c0002 +#define REF_STR_GrenadeTitle 0x86c0003 +#define REF_STR_DrugTitle 0x86c0004 +#define REF_STR_PistolCartTitle 0x86c0005 +#define REF_STR_RifleCartTitle 0x86c0006 +#define REF_STR_AutoCartTitle 0x86c0007 +#define REF_STR_NeedleCartTitle 0x86c0008 +#define REF_STR_SpeclCartTitle 0x86c0009 +#define REF_STR_HardwareTitle 0x86c000a +#define REF_STR_SoftTitle 0x86c000b +#define REF_STR_SoftComTitle 0x86c000c +#define REF_STR_SoftDefTitle 0x86c000d +#define REF_STR_SoftMiscTitle 0x86c000e +#define REF_STR_GeneralTitle 0x86c000f +#define REF_STR_EmailTitle 0x86c0010 +#define REF_STR_DataTitle 0x86c0011 +#define REF_STR_LogTitle 0x86c0012 +#define REF_STR_InvReject 0x86c0013 +#define REF_STR_InvNoRoom 0x86c0014 +#define REF_STR_EmailMoreLeft 0x86c0015 +#define REF_STR_EmailMoreRight 0x86c0016 +#define REF_STR_AmmoLoad 0x86c0017 +#define REF_STR_AmmoOver 0x86c0018 +#define REF_STR_GunHot 0x86c0019 +#define REF_STR_GunWarm 0x86c001a +#define REF_STR_GunOK 0x86c001b +#define REF_STR_View360Update 0x86c001c +#define REF_STR_ClickToLoad 0x86c001d +#define REF_STR_DClickToUnload 0x86c001e +#define REF_STR_TimeSetting 0x86c001f +#define REF_STR_EnergySetting 0x86c0020 +#define REF_STR_LowSetting 0x86c0021 +#define REF_STR_HighSetting 0x86c0022 +#define REF_STR_Overload 0x86c0023 +#define REF_STR_MessageTitle 0x86c0024 +#define REF_STR_MessageSubject 0x86c0025 +#define REF_STR_EmptyGump 0x86c0026 +#define REF_STR_BiowareTitle 0x86c0027 +#define REF_STR_BiowareHealth 0x86c0028 +#define REF_STR_BiowareFatigue 0x86c0029 +#define REF_STR_BioHelpBase 0x86c002a +#define REF_STR_HudBase 0x86c0032 +#define REF_STR_ViewHelpBase 0x86c0035 +#define REF_STR_HudColorsTitle 0x86c0038 +#define REF_STR_AmmoMFDWeaps 0x86c0039 +#define REF_STR_AmmoMFDClips 0x86c003a +#define REF_STR_InvCursor 0x86c003b +#define REF_STR_MFDCursor 0x86c0041 +#define REF_STR_IconCursor 0x86c0046 +#define REF_STR_NoAutomap 0x86c0050 +#define REF_STR_AmmoTypeLetters 0x86c0051 +#define RES_objshortnames 0x86d // (2157) +#define REF_STR_MINIPISTOL_SHT 0x86d0000 +#define REF_STR_DARTPISTOL_SHT 0x86d0001 +#define REF_STR_MAGNUM_SHT 0x86d0002 +#define REF_STR_ASSAULTRFL_SHT 0x86d0003 +#define REF_STR_RIOTGUN_SHT 0x86d0004 +#define REF_STR_FLECHETTE_SHT 0x86d0005 +#define REF_STR_SKORPION_SHT 0x86d0006 +#define REF_STR_MAGPULSE_SHT 0x86d0007 +#define REF_STR_RAILGUN_SHT 0x86d0008 +#define REF_STR_BATON_SHT 0x86d0009 +#define REF_STR_LASERAPIER_SHT 0x86d000a +#define REF_STR_PHASER_SHT 0x86d000b +#define REF_STR_BLASTER_SHT 0x86d000c +#define REF_STR_IONBEAM_SHT 0x86d000d +#define REF_STR_STUNGUN_SHT 0x86d000e +#define REF_STR_PLASMABEAM_SHT 0x86d000f +#define REF_STR_SPAMMO_SHT 0x86d0010 +#define REF_STR_TEFAMMO_SHT 0x86d0011 +#define REF_STR_NNAMMO_SHT 0x86d0012 +#define REF_STR_TNAMMO_SHT 0x86d0013 +#define REF_STR_HTAMMO_SHT 0x86d0014 +#define REF_STR_HSAMMO_SHT 0x86d0015 +#define REF_STR_RBAMMO_SHT 0x86d0016 +#define REF_STR_MRAMMO_SHT 0x86d0017 +#define REF_STR_PRAMMO_SHT 0x86d0018 +#define REF_STR_HNAMMO_SHT 0x86d0019 +#define REF_STR_SPLAMMO_SHT 0x86d001a +#define REF_STR_SLGAMMO_SHT 0x86d001b +#define REF_STR_BGAMMO_SHT 0x86d001c +#define REF_STR_MAGAMMO_SHT 0x86d001d +#define REF_STR_RGAMMO_SHT 0x86d001e +#define REF_STR_BULLTRACE_SHT 0x86d001f +#define REF_STR_ENERTRACE_SHT 0x86d0020 +#define REF_STR_AUTOTRACE_SHT 0x86d0021 +#define REF_STR_NEEDTRACE_SHT 0x86d0022 +#define REF_STR_GRENTRACE_SHT 0x86d0023 +#define REF_STR_RUBBTRACE_SHT 0x86d0024 +#define REF_STR_VIRUSSLOW_SHT 0x86d0025 +#define REF_STR_ELITESLOW_SHT 0x86d0026 +#define REF_STR_ASSASINSLOW_SHT 0x86d0027 +#define REF_STR_MUTANTSLOW_SHT 0x86d0028 +#define REF_STR_ZEROSLOW_SHT 0x86d0029 +#define REF_STR_MAGBURST_SHT 0x86d002a +#define REF_STR_RAILSLOW_SHT 0x86d002b +#define REF_STR_STUNSLOW_SHT 0x86d002c +#define REF_STR_PLASMABLST_SHT 0x86d002d +#define REF_STR_CYBERBOLT_SHT 0x86d002e +#define REF_STR_CYBERSLOW_SHT 0x86d002f +#define REF_STR_DRILLSLOW_SHT 0x86d0030 +#define REF_STR_DISCSLOW_SHT 0x86d0031 +#define REF_STR_SPEWSLOW_SHT 0x86d0032 +#define REF_STR_PLANTSLOW_SHT 0x86d0033 +#define REF_STR_INVISOSLOW_SHT 0x86d0034 +#define REF_STR_DRONECAM_SHT 0x86d0035 +#define REF_STR_EXPLCAM_SHT 0x86d0036 +#define REF_STR_FRAG_G_SHT 0x86d0037 +#define REF_STR_EMP_G_SHT 0x86d0038 +#define REF_STR_GAS_G_SHT 0x86d0039 +#define REF_STR_CONC_G_SHT 0x86d003a +#define REF_STR_L_MINE_SHT 0x86d003b +#define REF_STR_NITRO_G_SHT 0x86d003c +#define REF_STR_EARTH_G_SHT 0x86d003d +#define REF_STR_OBJ_G_SHT 0x86d003e +#define REF_STR_STAMINA_DRUG_SHT 0x86d003f +#define REF_STR_SIGHT_DRUG_SHT 0x86d0040 +#define REF_STR_LSD_DRUG_SHT 0x86d0041 +#define REF_STR_MEDI_DRUG_SHT 0x86d0042 +#define REF_STR_NINJA_DRUG_SHT 0x86d0043 +#define REF_STR_GENIUS_DRUG_SHT 0x86d0044 +#define REF_STR_DETOX_DRUG_SHT 0x86d0045 +#define REF_STR_INFRA_GOG_SHT 0x86d0046 +#define REF_STR_TARG_GOG_SHT 0x86d0047 +#define REF_STR_SENS_HARD_SHT 0x86d0048 +#define REF_STR_AIM_GOG_SHT 0x86d0049 +#define REF_STR_HUD_GOG_SHT 0x86d004a +#define REF_STR_BIOSCAN_HARD_SHT 0x86d004b +#define REF_STR_NAV_HARD_SHT 0x86d004c +#define REF_STR_SHIELD_HARD_SHT 0x86d004d +#define REF_STR_VIDTEX_HARD_SHT 0x86d004e +#define REF_STR_LANTERN_HARD_SHT 0x86d004f +#define REF_STR_FULLSCR_HARD_SHT 0x86d0050 +#define REF_STR_ENV_HARD_SHT 0x86d0051 +#define REF_STR_MOTION_HARD_SHT 0x86d0052 +#define REF_STR_JET_HARD_SHT 0x86d0053 +#define REF_STR_STATUS_HARD_SHT 0x86d0054 +#define REF_STR_DRILL_SHT 0x86d0055 +#define REF_STR_SPEW_SHT 0x86d0056 +#define REF_STR_MINE_SHT 0x86d0057 +#define REF_STR_DISC_SHT 0x86d0058 +#define REF_STR_PULSER_SHT 0x86d0059 +#define REF_STR_SCRAMBLER_SHT 0x86d005a +#define REF_STR_VIRUS_SHT 0x86d005b +#define REF_STR_SHIELD_SHT 0x86d005c +#define REF_STR_OLD_FAKEID_SHT 0x86d005d +#define REF_STR_ICE_SHT 0x86d005e +#define REF_STR_TURBO_SHT 0x86d005f +#define REF_STR_FAKEID_SHT 0x86d0060 +#define REF_STR_DECOY_SHT 0x86d0061 +#define REF_STR_RECALL_SHT 0x86d0062 +#define REF_STR_GAMES_SHT 0x86d0063 +#define REF_STR_MONITOR1_SHT 0x86d0064 +#define REF_STR_IDENTIFY_SHT 0x86d0065 +#define REF_STR_TRACE_SHT 0x86d0066 +#define REF_STR_TOGGLE_SHT 0x86d0067 +#define REF_STR_TEXT1_SHT 0x86d0068 +#define REF_STR_EMAIL1_SHT 0x86d0069 +#define REF_STR_MAP1_SHT 0x86d006a +#define REF_STR_PHONE_SHT 0x86d006b +#define REF_STR_VCR_SHT 0x86d006c +#define REF_STR_MICROWAVE_OVN_SHT 0x86d006d +#define REF_STR_STEREO_SHT 0x86d006e +#define REF_STR_KEYBOARD_SHT 0x86d006f +#define REF_STR_SMALL_CPU_SHT 0x86d0070 +#define REF_STR_TV_SHT 0x86d0071 +#define REF_STR_MONITOR2_SHT 0x86d0072 +#define REF_STR_LARGCPU_SHT 0x86d0073 +#define REF_STR_LDESK_SHT 0x86d0074 +#define REF_STR_FDESK_SHT 0x86d0075 +#define REF_STR_CABINET_SHT 0x86d0076 +#define REF_STR_SHELF_SHT 0x86d0077 +#define REF_STR_HIDEAWAY_SHT 0x86d0078 +#define REF_STR_CHAIR_SHT 0x86d0079 +#define REF_STR_ENDTABLE_SHT 0x86d007a +#define REF_STR_COUCH_SHT 0x86d007b +#define REF_STR_EXECCHR_SHT 0x86d007c +#define REF_STR_COATTREE_SHT 0x86d007d +#define REF_STR_SIGN_SHT 0x86d007e +#define REF_STR_ICON_SHT 0x86d007f +#define REF_STR_GRAF_SHT 0x86d0080 +#define REF_STR_WORDS_SHT 0x86d0081 +#define REF_STR_PAINTING_SHT 0x86d0082 +#define REF_STR_POSTER_SHT 0x86d0083 +#define REF_STR_SCREEN_SHT 0x86d0084 +#define REF_STR_TMAP_SHT 0x86d0085 +#define REF_STR_SUPERSCREEN_SHT 0x86d0086 +#define REF_STR_BIGSCREEN_SHT 0x86d0087 +#define REF_STR_REPULSWALL_SHT 0x86d0088 +#define REF_STR_DESKLAMP_SHT 0x86d0089 +#define REF_STR_FLOORLAMP_SHT 0x86d008a +#define REF_STR_GLOWBULB_SHT 0x86d008b +#define REF_STR_CHAND_SHT 0x86d008c +#define REF_STR_GENE_SPLICER_SHT 0x86d008d +#define REF_STR_TUBING_SHT 0x86d008e +#define REF_STR_MED_CART_SHT 0x86d008f +#define REF_STR_SURG_MACH_SHT 0x86d0090 +#define REF_STR_TTUBE_RACK_SHT 0x86d0091 +#define REF_STR_RSRCH_CHAIR_SHT 0x86d0092 +#define REF_STR_HOSP_BED_SHT 0x86d0093 +#define REF_STR_BROKLAB1_SHT 0x86d0094 +#define REF_STR_BROKLAB2_SHT 0x86d0095 +#define REF_STR_MICROSCOPE_SHT 0x86d0096 +#define REF_STR_SCOPE_SHT 0x86d0097 +#define REF_STR_LAB_PROBE_SHT 0x86d0098 +#define REF_STR_XRAY_MACHINE_SHT 0x86d0099 +#define REF_STR_CAMERA_SHT 0x86d009a +#define REF_STR_CONTPAN_SHT 0x86d009b +#define REF_STR_CONTPED_SHT 0x86d009c +#define REF_STR_ENERGY_MINE_SHT 0x86d009d +#define REF_STR_STATUE1_SHT 0x86d009e +#define REF_STR_SHRUB1_SHT 0x86d009f +#define REF_STR_GRASS_SHT 0x86d00a0 +#define REF_STR_PLANT1_SHT 0x86d00a1 +#define REF_STR_FUNG1_SHT 0x86d00a2 +#define REF_STR_FUNG2_SHT 0x86d00a3 +#define REF_STR_PLANT2_SHT 0x86d00a4 +#define REF_STR_VINE1_SHT 0x86d00a5 +#define REF_STR_VINE2_SHT 0x86d00a6 +#define REF_STR_PLANT3_SHT 0x86d00a7 +#define REF_STR_PLANT4_SHT 0x86d00a8 +#define REF_STR_LBOULDER_SHT 0x86d00a9 +#define REF_STR_BBOULDER_SHT 0x86d00aa +#define REF_STR_SHRUB2_SHT 0x86d00ab +#define REF_STR_VSHRUB1_SHT 0x86d00ac +#define REF_STR_VSHRUB2_SHT 0x86d00ad +#define REF_STR_BRIDGE_SHT 0x86d00ae +#define REF_STR_CATWALK_SHT 0x86d00af +#define REF_STR_WALL_SHT 0x86d00b0 +#define REF_STR_FPILLAR_SHT 0x86d00b1 +#define REF_STR_RAILING1_SHT 0x86d00b2 +#define REF_STR_RAILING2_SHT 0x86d00b3 +#define REF_STR_PILLAR_SHT 0x86d00b4 +#define REF_STR_FORCE_BRIJ_SHT 0x86d00b5 +#define REF_STR_NON_BRIDGE_SHT 0x86d00b6 +#define REF_STR_FORCE_BRIJ2_SHT 0x86d00b7 +#define REF_STR_BEV_CONT_SHT 0x86d00b8 +#define REF_STR_WRAPPER_SHT 0x86d00b9 +#define REF_STR_PAPERS_SHT 0x86d00ba +#define REF_STR_WARECASING_SHT 0x86d00bb +#define REF_STR_EXTING_SHT 0x86d00bc +#define REF_STR_HELMET_SHT 0x86d00bd +#define REF_STR_CLOTHES_SHT 0x86d00be +#define REF_STR_BRIEFCASE_SHT 0x86d00bf +#define REF_STR_BROKEN_GUN_SHT 0x86d00c0 +#define REF_STR_MCHUNK1_SHT 0x86d00c1 +#define REF_STR_MCHUNK2_SHT 0x86d00c2 +#define REF_STR_MCHUNK3_SHT 0x86d00c3 +#define REF_STR_CRATE_FRAG_SHT 0x86d00c4 +#define REF_STR_BROKEN_PAN_SHT 0x86d00c5 +#define REF_STR_BROKEN_CLK_SHT 0x86d00c6 +#define REF_STR_MSCRAP_SHT 0x86d00c7 +#define REF_STR_BROKEN_LEV1_SHT 0x86d00c8 +#define REF_STR_BROKEN_LEV2_SHT 0x86d00c9 +#define REF_STR_CORPSE1_SHT 0x86d00ca +#define REF_STR_CORPSE2_SHT 0x86d00cb +#define REF_STR_CORPSE3_SHT 0x86d00cc +#define REF_STR_CORPSE4_SHT 0x86d00cd +#define REF_STR_CORPSE5_SHT 0x86d00ce +#define REF_STR_CORPSE6_SHT 0x86d00cf +#define REF_STR_CORPSE7_SHT 0x86d00d0 +#define REF_STR_CORPSE8_SHT 0x86d00d1 +#define REF_STR_SKEL_RAGS_SHT 0x86d00d2 +#define REF_STR_BONES1_SHT 0x86d00d3 +#define REF_STR_BONES2_SHT 0x86d00d4 +#define REF_STR_SKULL_SHT 0x86d00d5 +#define REF_STR_LIMB_SHT 0x86d00d6 +#define REF_STR_HEAD_SHT 0x86d00d7 +#define REF_STR_HEAD2_SHT 0x86d00d8 +#define REF_STR_EPICK_SHT 0x86d00d9 +#define REF_STR_BATTERY2_SHT 0x86d00da +#define REF_STR_ROD_SHT 0x86d00db +#define REF_STR_AIDKIT_SHT 0x86d00dc +#define REF_STR_TRACBEAM_SHT 0x86d00dd +#define REF_STR_BATTERY_SHT 0x86d00de +#define REF_STR_GENCARDS_SHT 0x86d00df +#define REF_STR_STDCARD_SHT 0x86d00e0 +#define REF_STR_SCICARD_SHT 0x86d00e1 +#define REF_STR_STORECARD_SHT 0x86d00e2 +#define REF_STR_ENGCARD_SHT 0x86d00e3 +#define REF_STR_MEDCARD_SHT 0x86d00e4 +#define REF_STR_MAINTCARD_SHT 0x86d00e5 +#define REF_STR_ADMINCARD_SHT 0x86d00e6 +#define REF_STR_SECCARD_SHT 0x86d00e7 +#define REF_STR_COMCARD_SHT 0x86d00e8 +#define REF_STR_GROUPCARD_SHT 0x86d00e9 +#define REF_STR_PERSCARD_SHT 0x86d00ea +#define REF_STR_MULTIPLEXR_SHT 0x86d00eb +#define REF_STR_CYBERHEAL_SHT 0x86d00ec +#define REF_STR_CYBERMINE_SHT 0x86d00ed +#define REF_STR_CYBERCARD_SHT 0x86d00ee +#define REF_STR_SHODO_SHRINE_SHT 0x86d00ef +#define REF_STR_ICEWALL_SHT 0x86d00f0 +#define REF_STR_INFONODE_SHT 0x86d00f1 +#define REF_STR_CSPACE_EXIT_SHT 0x86d00f2 +#define REF_STR_DATALET_SHT 0x86d00f3 +#define REF_STR_BARRICADE_SHT 0x86d00f4 +#define REF_STR_TARGET_SHT 0x86d00f5 +#define REF_STR_ARROW_SHT 0x86d00f6 +#define REF_STR_BEAMBLST_SHT 0x86d00f7 +#define REF_STR_ACIDCORR_SHT 0x86d00f8 +#define REF_STR_BULLETHOLE_SHT 0x86d00f9 +#define REF_STR_EXBLAST_SHT 0x86d00fa +#define REF_STR_BURNRES_SHT 0x86d00fb +#define REF_STR_BLOODSTN_SHT 0x86d00fc +#define REF_STR_CHEMSPLAT_SHT 0x86d00fd +#define REF_STR_OILPUDDLE_SHT 0x86d00fe +#define REF_STR_WASTESPILL_SHT 0x86d00ff +#define REF_STR_ISOTOPE_X_SHT 0x86d0100 +#define REF_STR_CIRCBOARD1_SHT 0x86d0101 +#define REF_STR_PLASTIQUE_SHT 0x86d0102 +#define REF_STR_FAUX_X_SHT 0x86d0103 +#define REF_STR_CIRCBOARD4_SHT 0x86d0104 +#define REF_STR_CIRCBOARD5_SHT 0x86d0105 +#define REF_STR_CIRCBOARD6_SHT 0x86d0106 +#define REF_STR_CIRCBOARD7_SHT 0x86d0107 +#define REF_STR_SWITCH1_SHT 0x86d0108 +#define REF_STR_SWITCH2_SHT 0x86d0109 +#define REF_STR_BUTTON1_SHT 0x86d010a +#define REF_STR_BUTTON2_SHT 0x86d010b +#define REF_STR_LEVER1_SHT 0x86d010c +#define REF_STR_LEVER2_SHT 0x86d010d +#define REF_STR_BIGRED_SHT 0x86d010e +#define REF_STR_BIGLEVER_SHT 0x86d010f +#define REF_STR_DIAL_SHT 0x86d0110 +#define REF_STR_ACCESS_SLOT_SHT 0x86d0111 +#define REF_STR_CRCT_BD_SLOT_SHT 0x86d0112 +#define REF_STR_CHEM_RECEPT_SHT 0x86d0113 +#define REF_STR_ANTENNA_PAN_SHT 0x86d0114 +#define REF_STR_PLAS_ANTENNA_SHT 0x86d0115 +#define REF_STR_DEST_ANTENNA_SHT 0x86d0116 +#define REF_STR_RETSCANNER_SHT 0x86d0117 +#define REF_STR_CYB_TERM_SHT 0x86d0118 +#define REF_STR_ENRG_CHARGE_SHT 0x86d0119 +#define REF_STR_FIXUP_STATION_SHT 0x86d011a +#define REF_STR_ACCPANEL1_SHT 0x86d011b +#define REF_STR_ACCPANEL2_SHT 0x86d011c +#define REF_STR_ACCPANEL3_SHT 0x86d011d +#define REF_STR_ACCPANEL4_SHT 0x86d011e +#define REF_STR_ELEPANEL1_SHT 0x86d011f +#define REF_STR_ELEPANEL2_SHT 0x86d0120 +#define REF_STR_ELEPANEL3_SHT 0x86d0121 +#define REF_STR_KEYPAD1_SHT 0x86d0122 +#define REF_STR_KEYPAD2_SHT 0x86d0123 +#define REF_STR_ACCPANEL5_SHT 0x86d0124 +#define REF_STR_ACCPANEL6_SHT 0x86d0125 +#define REF_STR_AMMOVEND_SHT 0x86d0126 +#define REF_STR_HEALVEND_SHT 0x86d0127 +#define REF_STR_CYBERTOG1_SHT 0x86d0128 +#define REF_STR_CYBERTOG2_SHT 0x86d0129 +#define REF_STR_CYBERTOG3_SHT 0x86d012a +#define REF_STR_BLAST_DOOR_SHT 0x86d012b +#define REF_STR_ACCESS_DOOR_SHT 0x86d012c +#define REF_STR_RESID_DOOR_SHT 0x86d012d +#define REF_STR_MAINT_DOOR_SHT 0x86d012e +#define REF_STR_HOSP_DOOR_SHT 0x86d012f +#define REF_STR_LAB_DOOR_SHT 0x86d0130 +#define REF_STR_STOR_DOOR_SHT 0x86d0131 +#define REF_STR_REACTR_DOOR_SHT 0x86d0132 +#define REF_STR_EXEC_DOOR_SHT 0x86d0133 +#define REF_STR_NO_DOOR_SHT 0x86d0134 +#define REF_STR_LAB_DOORWAY_SHT 0x86d0135 +#define REF_STR_RES_DOORWAY_SHT 0x86d0136 +#define REF_STR_BRJ_DOORWAY_SHT 0x86d0137 +#define REF_STR_RCT_DOORWAY_SHT 0x86d0138 +#define REF_STR_GRATING1_SHT 0x86d0139 +#define REF_STR_GRATING2_SHT 0x86d013a +#define REF_STR_GRATING3_SHT 0x86d013b +#define REF_STR_GRATING4_SHT 0x86d013c +#define REF_STR_NO_DOOR2_SHT 0x86d013d +#define REF_STR_LABFORCE_SHT 0x86d013e +#define REF_STR_BROKLABFORCE_SHT 0x86d013f +#define REF_STR_RESFORCE_SHT 0x86d0140 +#define REF_STR_BROKRESFORCE_SHT 0x86d0141 +#define REF_STR_GENFORCE_SHT 0x86d0142 +#define REF_STR_CYBGENFORCE_SHT 0x86d0143 +#define REF_STR_NO_DOOR3_SHT 0x86d0144 +#define REF_STR_EXEC_ELEV_SHT 0x86d0145 +#define REF_STR_REG_ELEV1_SHT 0x86d0146 +#define REF_STR_REG_ELEV2_SHT 0x86d0147 +#define REF_STR_FREIGHT_ELEV_SHT 0x86d0148 +#define REF_STR_NO_DOOR4_SHT 0x86d0149 +#define REF_STR_DOUB_LEFTDOOR_SHT 0x86d014a +#define REF_STR_DOUB_RITEDOOR_SHT 0x86d014b +#define REF_STR_IRIS_SHT 0x86d014c +#define REF_STR_VERT_OPEN_SHT 0x86d014d +#define REF_STR_VERT_SPLIT_SHT 0x86d014e +#define REF_STR_NO_DOOR5_SHT 0x86d014f +#define REF_STR_SECRET_DOOR1_SHT 0x86d0150 +#define REF_STR_SECRET_DOOR2_SHT 0x86d0151 +#define REF_STR_SECRET_DOOR3_SHT 0x86d0152 +#define REF_STR_INVISO_DOOR_SHT 0x86d0153 +#define REF_STR_ALERT_PANEL_OFF_SHT 0x86d0154 +#define REF_STR_ALERT_PANEL_ON_SHT 0x86d0155 +#define REF_STR_HORZ_KLAXOFF_SHT 0x86d0156 +#define REF_STR_HORZ_KLAXON_SHT 0x86d0157 +#define REF_STR_SPARK_CABLE_SHT 0x86d0158 +#define REF_STR_TWITCH_MUT2_SHT 0x86d0159 +#define REF_STR_MACHINE_SHT 0x86d015a +#define REF_STR_HOLOG_ANIM_SHT 0x86d015b +#define REF_STR_TWITCH_MUT_SHT 0x86d015c +#define REF_STR_BLOOD1_SHT 0x86d015d +#define REF_STR_CAMEXPL_SHT 0x86d015e +#define REF_STR_TVEXPL_SHT 0x86d015f +#define REF_STR_SIMPLSMOKE_SHT 0x86d0160 +#define REF_STR_PLANTEXPL_SHT 0x86d0161 +#define REF_STR_BULLETWALLHIT_SHT 0x86d0162 +#define REF_STR_BEAMWALLHIT_SHT 0x86d0163 +#define REF_STR_IMPACT_ANIM_SHT 0x86d0164 +#define REF_STR_BULL_ROBOT_SHT 0x86d0165 +#define REF_STR_BEAM_ROBOT1_SHT 0x86d0166 +#define REF_STR_BEAM_ROBOT2_SHT 0x86d0167 +#define REF_STR_EXPLOSION1_SHT 0x86d0168 +#define REF_STR_EXPLOSION2_SHT 0x86d0169 +#define REF_STR_EXPLOSION3_SHT 0x86d016a +#define REF_STR_LG_EXPLOSION_SHT 0x86d016b +#define REF_STR_MAGPULSEHIT_SHT 0x86d016c +#define REF_STR_STUNHIT_SHT 0x86d016d +#define REF_STR_PLASMAHIT_SHT 0x86d016e +#define REF_STR_SMOKEEXPL_SHT 0x86d016f +#define REF_STR_CRATEEXPL_SHT 0x86d0170 +#define REF_STR_MNTR2EXPL_SHT 0x86d0171 +#define REF_STR_GASEXPL_SHT 0x86d0172 +#define REF_STR_EMPEXPL_SHT 0x86d0173 +#define REF_STR_CORP_HUM_EXPL_SHT 0x86d0174 +#define REF_STR_CORP_ROB_EXPL_SHT 0x86d0175 +#define REF_STR_ENTRY_TRIG_SHT 0x86d0176 +#define REF_STR_NULL_TRIG_SHT 0x86d0177 +#define REF_STR_FLOOR_TRIG_SHT 0x86d0178 +#define REF_STR_PLRDETH_TRIG_SHT 0x86d0179 +#define REF_STR_DETHWATCH_TRIG_SHT 0x86d017a +#define REF_STR_AOE_ENT_TRIG_SHT 0x86d017b +#define REF_STR_AOE_CON_TRIG_SHT 0x86d017c +#define REF_STR_AI_HINT_SHT 0x86d017d +#define REF_STR_LEVEL_TRIG_SHT 0x86d017e +#define REF_STR_CONTIN_TRIG_SHT 0x86d017f +#define REF_STR_REPULSOR_SHT 0x86d0180 +#define REF_STR_ECOLOGY_TRIG_SHT 0x86d0181 +#define REF_STR_SHODO_TRIG_SHT 0x86d0182 +#define REF_STR_TRIPBEAM_SHT 0x86d0183 +#define REF_STR_BIOHAZARD_SHT 0x86d0184 +#define REF_STR_RADHAZARD_SHT 0x86d0185 +#define REF_STR_CHEMHAZARD_SHT 0x86d0186 +#define REF_STR_MAPNOTE_SHT 0x86d0187 +#define REF_STR_MUSIC_MARK_SHT 0x86d0188 +#define REF_STR_SML_CRT_SHT 0x86d0189 +#define REF_STR_LG_CRT_SHT 0x86d018a +#define REF_STR_SECURE_CONTR_SHT 0x86d018b +#define REF_STR_RAD_BARREL_SHT 0x86d018c +#define REF_STR_TOXIC_BARREL_SHT 0x86d018d +#define REF_STR_CHEM_TANK_SHT 0x86d018e +#define REF_STR_THERMOS_SHT 0x86d018f +#define REF_STR_VIAL_CONT_SHT 0x86d0190 +#define REF_STR_FLASK_CONT_SHT 0x86d0191 +#define REF_STR_BEAKER_CONT_SHT 0x86d0192 +#define REF_STR_MUT_CORPSE1_SHT 0x86d0193 +#define REF_STR_MUT_CORPSE2_SHT 0x86d0194 +#define REF_STR_MUT_CORPSE3_SHT 0x86d0195 +#define REF_STR_MUT_CORPSE4_SHT 0x86d0196 +#define REF_STR_MUT_CORPSE5_SHT 0x86d0197 +#define REF_STR_MUT_CORPSE6_SHT 0x86d0198 +#define REF_STR_MUT_CORPSE7_SHT 0x86d0199 +#define REF_STR_MUT_CORPSE8_SHT 0x86d019a +#define REF_STR_ROB_CORPSE1_SHT 0x86d019b +#define REF_STR_ROB_CORPSE2_SHT 0x86d019c +#define REF_STR_ROB_CORPSE3_SHT 0x86d019d +#define REF_STR_ROB_CORPSE4_SHT 0x86d019e +#define REF_STR_ROB_CORPSE5_SHT 0x86d019f +#define REF_STR_ROB_CORPSE6_SHT 0x86d01a0 +#define REF_STR_ROB_CORPSE7_SHT 0x86d01a1 +#define REF_STR_ROB_CORPSE8_SHT 0x86d01a2 +#define REF_STR_ROB_CORPSE9_SHT 0x86d01a3 +#define REF_STR_ROB_CORPSE10_SHT 0x86d01a4 +#define REF_STR_ROB_CORPSE11_SHT 0x86d01a5 +#define REF_STR_ROB_CORPSE12_SHT 0x86d01a6 +#define REF_STR_ROB_CORPSE13_SHT 0x86d01a7 +#define REF_STR_CYB_CORPSE1_SHT 0x86d01a8 +#define REF_STR_CYB_CORPSE2_SHT 0x86d01a9 +#define REF_STR_CYB_CORPSE3_SHT 0x86d01aa +#define REF_STR_CYB_CORPSE4_SHT 0x86d01ab +#define REF_STR_CYB_CORPSE5_SHT 0x86d01ac +#define REF_STR_CYB_CORPSE6_SHT 0x86d01ad +#define REF_STR_CYB_CORPSE7_SHT 0x86d01ae +#define REF_STR_OTH_CORPSE1_SHT 0x86d01af +#define REF_STR_OTH_CORPSE2_SHT 0x86d01b0 +#define REF_STR_OTH_CORPSE3_SHT 0x86d01b1 +#define REF_STR_OTH_CORPSE4_SHT 0x86d01b2 +#define REF_STR_OTH_CORPSE5_SHT 0x86d01b3 +#define REF_STR_OTH_CORPSE6_SHT 0x86d01b4 +#define REF_STR_OTH_CORPSE7_SHT 0x86d01b5 +#define REF_STR_OTH_CORPSE8_SHT 0x86d01b6 +#define REF_STR_HUMAN_CRIT_SHT 0x86d01b7 +#define REF_STR_GOR_TIGER_SHT 0x86d01b8 +#define REF_STR_INSECT_CRIT_SHT 0x86d01b9 +#define REF_STR_AVIAN_CRIT_SHT 0x86d01ba +#define REF_STR_PLANT_CRIT_SHT 0x86d01bb +#define REF_STR_ZERO_CRIT_SHT 0x86d01bc +#define REF_STR_PLAYER_CRIT_SHT 0x86d01bd +#define REF_STR_INVISO_CRIT_SHT 0x86d01be +#define REF_STR_VIRUS_CRIT_SHT 0x86d01bf +#define REF_STR_LIFT_BOT_SHT 0x86d01c0 +#define REF_STR_REPAIRBOT_SHT 0x86d01c1 +#define REF_STR_SERVBOT_SHT 0x86d01c2 +#define REF_STR_EXECBOT_SHT 0x86d01c3 +#define REF_STR_LGTURRET_SHT 0x86d01c4 +#define REF_STR_HOPPER_SHT 0x86d01c5 +#define REF_STR_SECURITY_BOT1_SHT 0x86d01c6 +#define REF_STR_SECURITY_BOT2_SHT 0x86d01c7 +#define REF_STR_AUTOBOMB_SHT 0x86d01c8 +#define REF_STR_REPAIRBOT2_SHT 0x86d01c9 +#define REF_STR_FLIER_SHT 0x86d01ca +#define REF_STR_SECURITY_BOT3_SHT 0x86d01cb +#define REF_STR_CYBORG_DRONE_SHT 0x86d01cc +#define REF_STR_WARRIOR_SHT 0x86d01cd +#define REF_STR_ASSASSIN_SHT 0x86d01ce +#define REF_STR_CYBERBABE_SHT 0x86d01cf +#define REF_STR_ELITE_GUARD_SHT 0x86d01d0 +#define REF_STR_CORTEX_REAVER_SHT 0x86d01d1 +#define REF_STR_MUTANT_BORG_SHT 0x86d01d2 +#define REF_STR_CYBERDOG_SHT 0x86d01d3 +#define REF_STR_CYBERGUARD_SHT 0x86d01d4 +#define REF_STR_CYBER_CORTEX_SHT 0x86d01d5 +#define REF_STR_CYBER_DYN_ICE_SHT 0x86d01d6 +#define REF_STR_CYBER_HNT_KIL_SHT 0x86d01d7 +#define REF_STR_CYBER_SHODAN_SHT 0x86d01d8 +#define REF_STR_CYBERGUARD2_SHT 0x86d01d9 +#define REF_STR_ROBOBABE_SHT 0x86d01da +#define REF_STR_DIEGO_SHT 0x86d01db +#define RES_targeting 0x86e // (2158) +#define REF_STR_NoTarget 0x86e0000 +#define REF_STR_NoTargetWare 0x86e0001 +#define REF_STR_TargetID 0x86e0002 +#define REF_STR_Condition 0x86e0003 +#define REF_STR_TargRange 0x86e0004 +#define REF_STR_NumKills 0x86e0005 +#define REF_STR_MutantDmg 0x86e0006 +#define REF_STR_RobotDmg 0x86e000c +#define REF_STR_CyborgDmg 0x86e0012 +#define REF_STR_CyberDmg 0x86e0018 +#define REF_STR_BabeDmg 0x86e001e +#define REF_STR_CritMoods 0x86e0024 +#define REF_STR_CritClasses 0x86e002c +#define RES_HUDstrings 0x86f // (2159) +#define REF_STR_InfraredOn 0x86f0000 +#define REF_STR_ExplosionDetect 0x86f0001 +#define REF_STR_oClock 0x86f0002 +#define REF_STR_RadiationZone 0x86f0003 +#define REF_STR_BiohazardZone 0x86f0004 +#define REF_STR_ShodanHud 0x86f0005 +#define REF_STR_HighFatigue 0x86f0006 +#define REF_STR_ShieldAbsorb 0x86f0007 +#define REF_STR_AbnormalGravity1 0x86f0008 +#define REF_STR_AbnormalGravity2 0x86f0009 +#define REF_STR_CyberDecoy 0x86f000a +#define REF_STR_CyberFakeID 0x86f000b +#define REF_STR_CyberTurbo 0x86f000c +#define REF_STR_CyberTime 0x86f000d +#define REF_STR_ShodanNow 0x86f000e +#define REF_STR_CyberDanger 0x86f000f +#define REF_STR_RadPoison 0x86f0010 +#define REF_STR_BioPoison 0x86f0011 +#define REF_STR_ExposureUnit 0x86f0012 +#define REF_STR_EnergyCritical 0x86f0013 +#define REF_STR_EnergyUsage 0x86f0014 +#define REF_STR_EnergyUnit 0x86f0015 +#define REF_STR_GametimeLeft 0x86f0016 +#define REF_STR_TargetDamageHit 0x86f0017 +#define REF_STR_TargetDamageBase 0x86f0018 +#define REF_STR_EnviroAbsorb 0x86f0020 +#define REF_STR_EnviroDrain 0x86f0021 +#define RES_lognames 0x870 // (2160) +#define REF_STR_LogName0 0x8700000 +#define REF_STR_LogName1 0x8700001 +#define REF_STR_LogName2 0x8700002 +#define REF_STR_LogName3 0x8700003 +#define REF_STR_LogName4 0x8700004 +#define REF_STR_LogName5 0x8700005 +#define REF_STR_LogName6 0x8700006 +#define REF_STR_LogName7 0x8700007 +#define REF_STR_LogName8 0x8700008 +#define REF_STR_LogName9 0x8700009 +#define REF_STR_LogName10 0x870000a +#define REF_STR_LogName11 0x870000b +#define REF_STR_LogName12 0x870000c +#define REF_STR_LogName13 0x870000d +#define REF_STR_LogName14 0x870000e +#define RES_messages 0x871 // (2161) +#define REF_STR_InvCybFailSoft 0x8710000 +#define REF_STR_InvCybFailHard 0x8710001 +#define REF_STR_InvLiveGrenade 0x8710002 +#define REF_STR_ElevatorNoMove 0x8710003 +#define REF_STR_ElevatorMove 0x8710004 +#define REF_STR_ElevatorSameFloor 0x8710005 +#define REF_STR_ElevatorDoorOpen 0x8710006 +#define REF_STR_DoorLocked 0x8710007 +#define REF_STR_DoorLocked2 0x8710008 +#define REF_STR_DoorLocked3 0x8710009 +#define REF_STR_DoorNeedCard 0x871000a +#define REF_STR_DoorNeedCard2 0x871000b +#define REF_STR_DoorNeedcard3 0x871000c +#define REF_STR_DoorWrongAccess 0x871000d +#define REF_STR_DoorCardGoodButLocked 0x871000e +#define REF_STR_DoorCardGoodButLocked2 0x871000f +#define REF_STR_DoorCardGoodButLocked3 0x8710010 +#define REF_STR_DoorCardGood 0x8710011 +#define REF_STR_DoorCardGood2 0x8710012 +#define REF_STR_DoorCardGood3 0x8710013 +#define REF_STR_EmptyContainer 0x8710014 +#define REF_STR_NonemptyContainer 0x8710015 +#define REF_STR_KeypadGood 0x8710016 +#define REF_STR_KeypadBad 0x8710017 +#define REF_STR_PickupTooFar 0x8710018 +#define REF_STR_UseTooFar 0x8710019 +#define REF_STR_ReceiveEmail 0x871001a +#define REF_STR_ReceiveLog 0x871001b +#define REF_STR_ReceiveData 0x871001c +#define REF_STR_AlreadyHaveOne 0x871001d +#define REF_STR_More 0x871001e +#define REF_STR_NoRecharge 0x871001f +#define REF_STR_Recharge 0x8710020 +#define REF_STR_WareNoPower 0x8710021 +#define REF_STR_PowerRanOut 0x8710022 +#define REF_STR_TractorActivate 0x8710023 +#define REF_STR_TractorDeactivate 0x8710024 +#define REF_STR_MedikitUse 0x8710025 +#define REF_STR_Applied 0x8710026 +#define REF_STR_InvalidSave 0x8710027 +#define REF_STR_NoDataReader 0x8710028 +#define REF_STR_AntiGravActivate 0x8710029 +#define REF_STR_AntiGravDeactivate 0x871002a +#define REF_STR_Pause 0x871002b +#define REF_STR_Unpause 0x871002c +#define REF_STR_NoTargetsAround 0x871002d +#define REF_STR_PlastiqueOn 0x871002e +#define REF_STR_OpenPanelFirst 0x871002f +#define REF_STR_EnterSaveString 0x8710030 +#define REF_STR_LoadSlot 0x8710031 +#define REF_STR_SaveSlot 0x8710032 +#define REF_STR_QuitConfirm 0x8710033 +#define REF_STR_CyberspaceUse 0x8710034 +#define REF_STR_NoCyberSave 0x8710035 +#define REF_STR_CursorObjSave 0x8710036 +#define REF_STR_WrongHead 0x8710037 +#define REF_STR_FixtureAccessBad 0x8710038 +#define REF_STR_CspaceAcquire 0x8710039 +#define REF_STR_IceEncrusted 0x871003a +#define REF_STR_CspaceHeal 0x871003b +#define REF_STR_CspaceToggle 0x871003c +#define REF_STR_SHODANFail 0x871003d +#define REF_STR_AccessCardNoGain 0x8710048 +#define REF_STR_AccessCardNewGain 0x8710049 +#define REF_STR_KeypadForDoor 0x871004a +#define REF_STR_EPickFailure 0x871004c +#define REF_STR_SurgeryHeal 0x871004e +#define REF_STR_HoldingGrenade 0x8710051 +#define REF_STR_WordPage 0x8710052 +#define REF_STR_ViewHelpOffMsg 0x8710053 +#define REF_STR_ViewHelpOnMsg 0x8710056 +#define REF_STR_CspaceMaxHealth 0x8710059 +#define REF_STR_NoEnergyWeapon 0x871005a +#define REF_STR_GunTooHot 0x871005b +#define REF_STR_NoEnergyFireWeapon 0x871005c +#define REF_STR_NormalScreen 0x871005e +#define REF_STR_WordLiveGrenade 0x871005f +#define REF_STR_CantUse 0x8710060 +#define REF_STR_PresetSideicon 0x8710061 +#define REF_STR_InkyBlack 0x8710062 +#define REF_STR_InkyUse 0x8710063 +#define REF_STR_DataObj 0x8710064 +#define REF_STR_CspaceData 0x8710065 +#define REF_STR_CybWall 0x8710066 +#define REF_STR_CybWallUse 0x8710067 +#define REF_STR_NotAvailCspace 0x8710068 +#define REF_STR_SaveGameSaving 0x8710069 +#define REF_STR_SaveGameSaved 0x871006a +#define REF_STR_SaveGameFail 0x871006b +#define REF_STR_LoadGameLoading 0x871006c +#define REF_STR_LoadGameLoaded 0x871006d +#define REF_STR_LoadGameFail 0x871006e +#define REF_STR_GameInitFail 0x871006f +#define REF_STR_FSMode 0x8710070 +#define REF_STR_NoMapMessage 0x8710071 +#define REF_STR_DropMessage 0x8710072 +#define RES_intro 0x872 // (2162) +#define RES_death 0x873 // (2163) +#define RES_win 0x874 // (2164) +#define RES_plotware 0x875 // (2165) +#define REF_STR_pwPage0 0x8750000 +#define REF_STR_pwNull 0x8750003 +#define REF_STR_pwShodometer 0x8750004 +#define REF_STR_pwShodoOverall 0x8750005 +#define REF_STR_pwStatus 0x8750006 +#define REF_STR_pwStatus0 0x8750007 +#define REF_STR_pwLifePods 0x875000b +#define REF_STR_pwEnabled 0x875000c +#define REF_STR_pwComm 0x875000e +#define REF_STR_pwComm0 0x875000f +#define REF_STR_pwCooling 0x8750014 +#define REF_STR_pwCooling0 0x8750015 +#define REF_STR_pwNodes 0x8750018 +#define REF_STR_pwMulti 0x8750019 +#define REF_STR_pwCPUcool 0x875001a +#define REF_STR_pwCPUcool0 0x875001b +#define REF_STR_pwMainProgram 0x875001d +#define REF_STR_pwMainProgram0 0x875001e +#define REF_STR_pwPropulsion 0x8750023 +#define REF_STR_pwPropulsion0 0x8750024 +#define REF_STR_pwDest 0x8750027 +#define REF_STR_pwDest0 0x8750028 +#define REF_STR_pwLaser 0x875002a +#define REF_STR_pwLaser0 0x875002b +#define REF_STR_pwShield 0x875002d +#define REF_STR_pwShield0 0x875002e +#define REF_STR_pwTime2Dest 0x8750030 +#define REF_STR_pwDownLoadTime 0x8750031 +#define REF_STR_pwDestructTime 0x8750032 +#define REF_STR_pwBridgeTime 0x8750033 +#define REF_STR_pwVirusTime 0x8750034 +#define REF_STR_pwGroveStatus 0x8750035 +#define REF_STR_pwAlpha 0x8750036 +#define REF_STR_pwAlpha0 0x8750037 +#define REF_STR_pwBeta 0x8750039 +#define REF_STR_pwBeta0 0x875003a +#define REF_STR_pwGamma 0x875003d +#define REF_STR_pwGamma0 0x875003e +#define REF_STR_pwDelta 0x875003f +#define REF_STR_pwDelta0 0x8750040 +#define RES_objIconNames 0x876 // (2166) +#define REF_STR_IconName0 0x8760000 +#define REF_STR_IconName1 0x8760001 +#define REF_STR_IconName2 0x8760002 +#define REF_STR_IconName3 0x8760003 +#define REF_STR_IconName4 0x8760004 +#define REF_STR_IconName5 0x8760005 +#define REF_STR_IconName6 0x8760006 +#define REF_STR_IconName7 0x8760007 +#define REF_STR_IconName8 0x8760008 +#define REF_STR_IconName9 0x8760009 +#define REF_STR_IconName10 0x876000a +#define REF_STR_IconName11 0x876000b +#define REF_STR_IconName12 0x876000c +#define REF_STR_IconName13 0x876000d +#define REF_STR_IconName14 0x876000e +#define REF_STR_IconName15 0x876000f +#define REF_STR_IconName16 0x8760010 +#define REF_STR_IconName17 0x8760011 +#define RES_screenText 0x877 // (2167) +#define REF_STR_ScreenZero 0x8770000 +#define REF_STR_ScreenOne 0x8770001 +#define REF_STR_ScreenTwo 0x8770002 +#define RES_cyberspaceText 0x878 // (2168) +#define REF_STR_CspaceInfoBase 0x8780000 +#define RES_accessCards 0x879 // (2169) +#define RES_dataletText 0x87a // (2170) +#define REF_STR_DataletZero 0x87a0000 +#define REF_STR_DataletBinaryZero 0x87a0012 +#define RES_wrapperPanelText 0x87b // (2171) +#define REF_STR_WrapperText 0x87b0000 +#define REF_STR_MusicText 0x87b0008 +#define REF_STR_OptionsText 0x87b000d +#define REF_STR_VerifyText 0x87b0013 +#define REF_STR_OffonText 0x87b0015 +#define REF_STR_MouseHand 0x87b0017 +#define REF_STR_TerseText 0x87b0019 +#define REF_STR_GammaCorr 0x87b001b +#define REF_STR_DetailLvl 0x87b001e +#define REF_STR_PopupCursFeedback 0x87b0022 +#define REF_STR_HandFeedback 0x87b0024 +#define REF_STR_TerseFeedback 0x87b0026 +#define REF_STR_GammaCorFeedback 0x87b0028 +#define REF_STR_DetailLvlFeedback 0x87b002b +#define REF_STR_VolumeText 0x87b002f +#define REF_STR_MusicFeedbackText 0x87b0032 +#define REF_STR_UnusedSave 0x87b003c +#define REF_STR_ClickForOptions 0x87b003d +#define REF_STR_DoubleClick 0x87b003e +#define REF_STR_InputMenu 0x87b003f +#define REF_STR_CenterJoy 0x87b0040 +#define REF_STR_CenterJoyPrompt 0x87b0041 +#define REF_STR_CenterJoyDone 0x87b0042 +#define REF_STR_OnlineHelp 0x87b0043 +#define REF_STR_Language 0x87b0044 +#define REF_STR_Languages 0x87b0045 +#define REF_STR_KeyEquivs0 0x87b0048 +#define REF_STR_KeyEquivs1 0x87b0049 +#define REF_STR_KeyEquivs2 0x87b004a +#define REF_STR_InsufficientDisk 0x87b004b +#define REF_STR_VideoText 0x87b004c +#define REF_STR_ScreenModeText 0x87b004d +#define REF_STR_ScreenModeFeedback 0x87b0052 +#define REF_STR_KeyEquivs3 0x87b0054 +#define REF_STR_KeyEquivs4 0x87b0055 +#define REF_STR_KeyEquivs5 0x87b0056 +#define REF_STR_AudiologState 0x87b0057 +#define REF_STR_HeadsetText 0x87b005a +#define REF_STR_JoystickSens 0x87b005e +#define REF_STR_AilThreeText 0x87b005f +#define REF_STR_DigiChannelState 0x87b0062 +#define REF_STR_StereoReverseState 0x87b0065 +#define REF_STR_Joystick 0x87b0067 +#define REF_STR_JoystickType 0x87b0068 +#define REF_STR_JoystickTypes 0x87b0069 +#define REF_STR_KeyEquivs6 0x87b006d +#define REF_STR_MoreHeadset 0x87b006e +#define RES_credits 0x87c // (2172) +#define RES_keyhelp 0x87d // (2173) +#define RES_miscellaneous 0x87e // (2174) +#define REF_STR_ResurrectBase 0x87e0000 +#define REF_STR_StartHeartString 0x87e0001 +#define REF_STR_StartBrainString 0x87e0003 +#define REF_STR_AutomapButtons 0x87e0005 +#define REF_STR_AutomapSpew 0x87e000d +#define REF_STR_NoMessage 0x87e000e +#define REF_STR_WirePuzzHelp 0x87e000f +#define REF_STR_GridPuzzHelp 0x87e0011 +#define REF_STR_GridPuzzSideMay 0x87e0012 +#define REF_STR_GridPuzzSide0 0x87e0013 +#define REF_STR_PanelSolved 0x87e0018 +#define REF_STR_DefaultPlayName 0x87e0019 +#define REF_STR_fakewinStrings 0x87e001a +#define REF_STR_ObjSysBad 0x87e001f +#define REF_STR_Sleeping 0x87e0020 +#define REF_STR_Drugged 0x87e0021 +#define REF_STR_Stunned 0x87e0022 +#define REF_STR_PhrasePickUp 0x87e0023 +#define REF_STR_PhraseUse 0x87e0024 +#define REF_STR_GroveWord 0x87e0025 +#define REF_STR_ReactorWord 0x87e0026 +#define REF_STR_VersionPrefix 0x87e0027 +#define REF_STR_DirectionAbbrev 0x87e0028 +#define REF_STR_Level 0x87e0030 +#define REF_STR_AutomapMFDButtons 0x87e0031 +#define RES_olh_strings 0x87f // (2175) +#define REF_STR_helpTake 0x87f0000 +#define REF_STR_helpUse 0x87f0001 +#define REF_STR_helpSearch 0x87f0002 +#define REF_STR_helpDoor 0x87f0003 +#define REF_STR_helpSwitch 0x87f0004 +#define REF_STR_helpPanel 0x87f0005 +#define REF_STR_helpElevator 0x87f0006 +#define REF_STR_helpGump 0x87f0007 +#define REF_STR_helpKeypad 0x87f0008 +#define REF_STR_helpCursor 0x87f0009 +#define REF_STR_helpGrenade 0x87f000a +#define REF_STR_helpCompound 0x87f000b +#define REF_STR_helpOn 0x87f000c +#define REF_STR_helpOff 0x87f000d +#define REF_STR_helpAttackGun 0x87f000e +#define REF_STR_helpAttackAuto 0x87f000f +#define REF_STR_helpAttackHTH 0x87f0010 +#define REF_STR_helpSecurity 0x87f0011 +#define RES_diffscreenText 0x880 // (2176) +#define REF_STR_journeyOpts 0x8800000 +#define REF_STR_diffStart 0x8800004 +#define REF_STR_diffName 0x8800005 +#define REF_STR_diffCategories 0x8800006 +#define REF_STR_diffStrings 0x880000a +#define REF_STR_BadVersion 0x880001a +#define RES_endgameStat 0x881 // (2177) +#define RES_itemspew 0x882 // (2178) +#define REF_STR_drugSpew0 0x8820000 +#define REF_STR_wareSpew0 0x8820007 +#define REF_STR_wareSpew1 0x882000c +#define REF_STR_gearSpew0 0x88200a7 +#define REF_STR_plotSpew0 0x88200ad +#define RES_games 0x883 // (2179) +#define REF_STR_GamesMenu 0x8830000 +#define REF_STR_DontPlay 0x8830001 +#define REF_STR_NotInstalled 0x8830002 +#define REF_STR_YouHave 0x8830004 +#define REF_STR_ComputerHas 0x8830005 +#define REF_STR_Won 0x8830006 +#define REF_STR_Lost 0x8830007 +#define REF_STR_Scored 0x8830008 +#define REF_STR_You 0x8830009 +#define REF_STR_Computer 0x883000a +#define REF_STR_ClickToPlay 0x883000b +#define REF_STR_LevelNum 0x883000c +#define REF_STR_ShodanHiScore 0x883000d +#define REF_STR_DiegoHiScore 0x883000e +#define REF_STR_DepthChargeBonus 0x883000f +#define REF_STR_GuyBonus 0x8830010 +#define REF_STR_Thinking 0x8830011 +#define REF_STR_YourMove 0x8830012 +#define REF_STR_GameName0 0x8830013 +#define REF_STR_GameDescrip0 0x883001c +#define REF_STR_WingBriefing 0x8830025 +#define REF_STR_WingDebriefing 0x8830033 +#define REF_STR_WingSighted 0x8830040 +#define REF_STR_WingDies 0x883004d +#define REF_STR_WingAttack 0x883005a +#define REF_STR_WingForm 0x8830067 +#define REF_STR_NoWing 0x8830074 +#define REF_STR_WingYouDied 0x8830075 +#define RES_objlongnames 0x24 // (36) +#define REF_STR_MINIPISTOL_LNG 0x240000 +#define REF_STR_DARTPISTOL_LNG 0x240001 +#define REF_STR_MAGNUM_LNG 0x240002 +#define REF_STR_ASSAULTRFL_LNG 0x240003 +#define REF_STR_RIOTGUN_LNG 0x240004 +#define REF_STR_FLECHETTE_LNG 0x240005 +#define REF_STR_SKORPION_LNG 0x240006 +#define REF_STR_MAGPULSE_LNG 0x240007 +#define REF_STR_RAILGUN_LNG 0x240008 +#define REF_STR_BATON_LNG 0x240009 +#define REF_STR_LASERAPIER_LNG 0x24000a +#define REF_STR_PHASER_LNG 0x24000b +#define REF_STR_BLASTER_LNG 0x24000c +#define REF_STR_IONBEAM_LNG 0x24000d +#define REF_STR_STUNGUN_LNG 0x24000e +#define REF_STR_PLASMABEAM_LNG 0x24000f +#define REF_STR_SPAMMO_LNG 0x240010 +#define REF_STR_TEFAMMO_LNG 0x240011 +#define REF_STR_NNAMMO_LNG 0x240012 +#define REF_STR_TNAMMO_LNG 0x240013 +#define REF_STR_HTAMMO_LNG 0x240014 +#define REF_STR_HSAMMO_LNG 0x240015 +#define REF_STR_RBAMMO_LNG 0x240016 +#define REF_STR_MRAMMO_LNG 0x240017 +#define REF_STR_PRAMMO_LNG 0x240018 +#define REF_STR_HNAMMO_LNG 0x240019 +#define REF_STR_SPLAMMO_LNG 0x24001a +#define REF_STR_SLGAMMO_LNG 0x24001b +#define REF_STR_BGAMMO_LNG 0x24001c +#define REF_STR_MAGAMMO_LNG 0x24001d +#define REF_STR_RGAMMO_LNG 0x24001e +#define REF_STR_BULLTRACE_LNG 0x24001f +#define REF_STR_ENERTRACE_LNG 0x240020 +#define REF_STR_AUTOTRACE_LNG 0x240021 +#define REF_STR_NEEDTRACE_LNG 0x240022 +#define REF_STR_GRENTRACE_LNG 0x240023 +#define REF_STR_RUBBTRACE_LNG 0x240024 +#define REF_STR_VIRUSSLOW_LNG 0x240025 +#define REF_STR_ELITESLOW_LNG 0x240026 +#define REF_STR_ASSASINSLOW_LNG 0x240027 +#define REF_STR_MUTANTSLOW_LNG 0x240028 +#define REF_STR_ZEROSLOW_LNG 0x240029 +#define REF_STR_MAGBURST_LNG 0x24002a +#define REF_STR_RAILSLOW_LNG 0x24002b +#define REF_STR_STUNSLOW_LNG 0x24002c +#define REF_STR_PLASMABLST_LNG 0x24002d +#define REF_STR_CYBERBOLT_LNG 0x24002e +#define REF_STR_CYBERSLOW_LNG 0x24002f +#define REF_STR_DRILLSLOW_LNG 0x240030 +#define REF_STR_DISCSLOW_LNG 0x240031 +#define REF_STR_SPEWSLOW_LNG 0x240032 +#define REF_STR_PLANTSLOW_LNG 0x240033 +#define REF_STR_INVISOSLOW_LNG 0x240034 +#define REF_STR_DRONECAM_LNG 0x240035 +#define REF_STR_EXPLCAM_LNG 0x240036 +#define REF_STR_FRAG_G_LNG 0x240037 +#define REF_STR_EMP_G_LNG 0x240038 +#define REF_STR_GAS_G_LNG 0x240039 +#define REF_STR_CONC_G_LNG 0x24003a +#define REF_STR_L_MINE_LNG 0x24003b +#define REF_STR_NITRO_G_LNG 0x24003c +#define REF_STR_EARTH_G_LNG 0x24003d +#define REF_STR_OBJ_G_LNG 0x24003e +#define REF_STR_STAMINA_DRUG_LNG 0x24003f +#define REF_STR_SIGHT_DRUG_LNG 0x240040 +#define REF_STR_LSD_DRUG_LNG 0x240041 +#define REF_STR_MEDI_DRUG_LNG 0x240042 +#define REF_STR_NINJA_DRUG_LNG 0x240043 +#define REF_STR_GENIUS_DRUG_LNG 0x240044 +#define REF_STR_DETOX_DRUG_LNG 0x240045 +#define REF_STR_INFRA_GOG_LNG 0x240046 +#define REF_STR_TARG_GOG_LNG 0x240047 +#define REF_STR_SENS_HARD_LNG 0x240048 +#define REF_STR_AIM_GOG_LNG 0x240049 +#define REF_STR_HUD_GOG_LNG 0x24004a +#define REF_STR_BIOSCAN_HARD_LNG 0x24004b +#define REF_STR_NAV_HARD_LNG 0x24004c +#define REF_STR_SHIELD_HARD_LNG 0x24004d +#define REF_STR_VIDTEX_HARD_LNG 0x24004e +#define REF_STR_LANTERN_HARD_LNG 0x24004f +#define REF_STR_FULLSCR_HARD_LNG 0x240050 +#define REF_STR_ENV_HARD_LNG 0x240051 +#define REF_STR_MOTION_HARD_LNG 0x240052 +#define REF_STR_JET_HARD_LNG 0x240053 +#define REF_STR_STATUS_HARD_LNG 0x240054 +#define REF_STR_DRILL_LNG 0x240055 +#define REF_STR_SPEW_LNG 0x240056 +#define REF_STR_MINE_LNG 0x240057 +#define REF_STR_DISC_LNG 0x240058 +#define REF_STR_PULSER_LNG 0x240059 +#define REF_STR_SCRAMBLER_LNG 0x24005a +#define REF_STR_VIRUS_LNG 0x24005b +#define REF_STR_SHIELD_LNG 0x24005c +#define REF_STR_OLD_FAKEID_LNG 0x24005d +#define REF_STR_ICE_LNG 0x24005e +#define REF_STR_TURBO_LNG 0x24005f +#define REF_STR_FAKEID_LNG 0x240060 +#define REF_STR_DECOY_LNG 0x240061 +#define REF_STR_RECALL_LNG 0x240062 +#define REF_STR_GAMES_LNG 0x240063 +#define REF_STR_MONITOR1_LNG 0x240064 +#define REF_STR_IDENTIFY_LNG 0x240065 +#define REF_STR_TRACE_LNG 0x240066 +#define REF_STR_TOGGLE_LNG 0x240067 +#define REF_STR_TEXT1_LNG 0x240068 +#define REF_STR_EMAIL1_LNG 0x240069 +#define REF_STR_MAP1_LNG 0x24006a +#define REF_STR_PHONE_LNG 0x24006b +#define REF_STR_VCR_LNG 0x24006c +#define REF_STR_MICROWAVE_OVN_LNG 0x24006d +#define REF_STR_STEREO_LNG 0x24006e +#define REF_STR_KEYBOARD_LNG 0x24006f +#define REF_STR_SMALL_CPU_LNG 0x240070 +#define REF_STR_TV_LNG 0x240071 +#define REF_STR_MONITOR2_LNG 0x240072 +#define REF_STR_LARGCPU_LNG 0x240073 +#define REF_STR_LDESK_LNG 0x240074 +#define REF_STR_FDESK_LNG 0x240075 +#define REF_STR_CABINET_LNG 0x240076 +#define REF_STR_SHELF_LNG 0x240077 +#define REF_STR_HIDEAWAY_LNG 0x240078 +#define REF_STR_CHAIR_LNG 0x240079 +#define REF_STR_ENDTABLE_LNG 0x24007a +#define REF_STR_COUCH_LNG 0x24007b +#define REF_STR_EXECCHR_LNG 0x24007c +#define REF_STR_COATTREE_LNG 0x24007d +#define REF_STR_SIGN_LNG 0x24007e +#define REF_STR_ICON_LNG 0x24007f +#define REF_STR_GRAF_LNG 0x240080 +#define REF_STR_WORDS_LNG 0x240081 +#define REF_STR_PAINTING_LNG 0x240082 +#define REF_STR_POSTER_LNG 0x240083 +#define REF_STR_SCREEN_LNG 0x240084 +#define REF_STR_TMAP_LNG 0x240085 +#define REF_STR_SUPERSCREEN_LNG 0x240086 +#define REF_STR_BIGSCREEN_LNG 0x240087 +#define REF_STR_REPULSWALL_LNG 0x240088 +#define REF_STR_DESKLAMP_LNG 0x240089 +#define REF_STR_FLOORLAMP_LNG 0x24008a +#define REF_STR_GLOWBULB_LNG 0x24008b +#define REF_STR_CHAND_LNG 0x24008c +#define REF_STR_GENE_SPLICER_LNG 0x24008d +#define REF_STR_TUBING_LNG 0x24008e +#define REF_STR_MED_CART_LNG 0x24008f +#define REF_STR_SURG_MACH_LNG 0x240090 +#define REF_STR_TTUBE_RACK_LNG 0x240091 +#define REF_STR_RSRCH_CHAIR_LNG 0x240092 +#define REF_STR_HOSP_BED_LNG 0x240093 +#define REF_STR_BROKLAB1_LNG 0x240094 +#define REF_STR_BROKLAB2_LNG 0x240095 +#define REF_STR_MICROSCOPE_LNG 0x240096 +#define REF_STR_SCOPE_LNG 0x240097 +#define REF_STR_LAB_PROBE_LNG 0x240098 +#define REF_STR_XRAY_MACHINE_LNG 0x240099 +#define REF_STR_CAMERA_LNG 0x24009a +#define REF_STR_CONTPAN_LNG 0x24009b +#define REF_STR_CONTPED_LNG 0x24009c +#define REF_STR_ENERGY_MINE_LNG 0x24009d +#define REF_STR_STATUE1_LNG 0x24009e +#define REF_STR_SHRUB1_LNG 0x24009f +#define REF_STR_GRASS_LNG 0x2400a0 +#define REF_STR_PLANT1_LNG 0x2400a1 +#define REF_STR_FUNG1_LNG 0x2400a2 +#define REF_STR_FUNG2_LNG 0x2400a3 +#define REF_STR_PLANT2_LNG 0x2400a4 +#define REF_STR_VINE1_LNG 0x2400a5 +#define REF_STR_VINE2_LNG 0x2400a6 +#define REF_STR_PLANT3_LNG 0x2400a7 +#define REF_STR_PLANT4_LNG 0x2400a8 +#define REF_STR_LBOULDER_LNG 0x2400a9 +#define REF_STR_BBOULDER_LNG 0x2400aa +#define REF_STR_SHRUB2_LNG 0x2400ab +#define REF_STR_VSHRUB1_LNG 0x2400ac +#define REF_STR_VSHRUB2_LNG 0x2400ad +#define REF_STR_BRIDGE_LNG 0x2400ae +#define REF_STR_CATWALK_LNG 0x2400af +#define REF_STR_WALL_LNG 0x2400b0 +#define REF_STR_FPILLAR_LNG 0x2400b1 +#define REF_STR_RAILING1_LNG 0x2400b2 +#define REF_STR_RAILING2_LNG 0x2400b3 +#define REF_STR_PILLAR_LNG 0x2400b4 +#define REF_STR_FORCE_BRIJ_LNG 0x2400b5 +#define REF_STR_NON_BRIDGE_LNG 0x2400b6 +#define REF_STR_FORCE_BRIJ2_LNG 0x2400b7 +#define REF_STR_BEV_CONT_LNG 0x2400b8 +#define REF_STR_WRAPPER_LNG 0x2400b9 +#define REF_STR_PAPERS_LNG 0x2400ba +#define REF_STR_WARECASING_LNG 0x2400bb +#define REF_STR_EXTING_LNG 0x2400bc +#define REF_STR_HELMET_LNG 0x2400bd +#define REF_STR_CLOTHES_LNG 0x2400be +#define REF_STR_BRIEFCASE_LNG 0x2400bf +#define REF_STR_BROKEN_GUN_LNG 0x2400c0 +#define REF_STR_MCHUNK1_LNG 0x2400c1 +#define REF_STR_MCHUNK2_LNG 0x2400c2 +#define REF_STR_MCHUNK3_LNG 0x2400c3 +#define REF_STR_CRATE_FRAG_LNG 0x2400c4 +#define REF_STR_BROKEN_PAN_LNG 0x2400c5 +#define REF_STR_BROKEN_CLK_LNG 0x2400c6 +#define REF_STR_MSCRAP_LNG 0x2400c7 +#define REF_STR_BROKEN_LEV1_LNG 0x2400c8 +#define REF_STR_BROKEN_LEV2_LNG 0x2400c9 +#define REF_STR_CORPSE1_LNG 0x2400ca +#define REF_STR_CORPSE2_LNG 0x2400cb +#define REF_STR_CORPSE3_LNG 0x2400cc +#define REF_STR_CORPSE4_LNG 0x2400cd +#define REF_STR_CORPSE5_LNG 0x2400ce +#define REF_STR_CORPSE6_LNG 0x2400cf +#define REF_STR_CORPSE7_LNG 0x2400d0 +#define REF_STR_CORPSE8_LNG 0x2400d1 +#define REF_STR_SKEL_RAGS_LNG 0x2400d2 +#define REF_STR_BONES1_LNG 0x2400d3 +#define REF_STR_BONES2_LNG 0x2400d4 +#define REF_STR_SKULL_LNG 0x2400d5 +#define REF_STR_LIMB_LNG 0x2400d6 +#define REF_STR_HEAD_LNG 0x2400d7 +#define REF_STR_HEAD2_LNG 0x2400d8 +#define REF_STR_EPICK_LNG 0x2400d9 +#define REF_STR_BATTERY2_LNG 0x2400da +#define REF_STR_ROD_LNG 0x2400db +#define REF_STR_AIDKIT_LNG 0x2400dc +#define REF_STR_TRACBEAM_LNG 0x2400dd +#define REF_STR_BATTERY_LNG 0x2400de +#define REF_STR_GENCARDS_LNG 0x2400df +#define REF_STR_STDCARD_LNG 0x2400e0 +#define REF_STR_SCICARD_LNG 0x2400e1 +#define REF_STR_STORECARD_LNG 0x2400e2 +#define REF_STR_ENGCARD_LNG 0x2400e3 +#define REF_STR_MEDCARD_LNG 0x2400e4 +#define REF_STR_MAINTCARD_LNG 0x2400e5 +#define REF_STR_ADMINCARD_LNG 0x2400e6 +#define REF_STR_SECCARD_LNG 0x2400e7 +#define REF_STR_COMCARD_LNG 0x2400e8 +#define REF_STR_GROUPCARD_LNG 0x2400e9 +#define REF_STR_PERSCARD_LNG 0x2400ea +#define REF_STR_MULTIPLEXR_LNG 0x2400eb +#define REF_STR_CYBERHEAL_LNG 0x2400ec +#define REF_STR_CYBERMINE_LNG 0x2400ed +#define REF_STR_CYBERCARD_LNG 0x2400ee +#define REF_STR_SHODO_SHRINE_LNG 0x2400ef +#define REF_STR_ICEWALL_LNG 0x2400f0 +#define REF_STR_INFONODE_LNG 0x2400f1 +#define REF_STR_CSPACE_EXIT_LNG 0x2400f2 +#define REF_STR_DATALET_LNG 0x2400f3 +#define REF_STR_BARRICADE_LNG 0x2400f4 +#define REF_STR_TARGET_LNG 0x2400f5 +#define REF_STR_ARROW_LNG 0x2400f6 +#define REF_STR_BEAMBLST_LNG 0x2400f7 +#define REF_STR_ACIDCORR_LNG 0x2400f8 +#define REF_STR_BULLETHOLE_LNG 0x2400f9 +#define REF_STR_EXBLAST_LNG 0x2400fa +#define REF_STR_BURNRES_LNG 0x2400fb +#define REF_STR_BLOODSTN_LNG 0x2400fc +#define REF_STR_CHEMSPLAT_LNG 0x2400fd +#define REF_STR_OILPUDDLE_LNG 0x2400fe +#define REF_STR_WASTESPILL_LNG 0x2400ff +#define REF_STR_ISOTOPE_X_LNG 0x240100 +#define REF_STR_CIRCBOARD1_LNG 0x240101 +#define REF_STR_PLASTIQUE_LNG 0x240102 +#define REF_STR_FAUX_X_LNG 0x240103 +#define REF_STR_CIRCBOARD4_LNG 0x240104 +#define REF_STR_CIRCBOARD5_LNG 0x240105 +#define REF_STR_CIRCBOARD6_LNG 0x240106 +#define REF_STR_CIRCBOARD7_LNG 0x240107 +#define REF_STR_SWITCH1_LNG 0x240108 +#define REF_STR_SWITCH2_LNG 0x240109 +#define REF_STR_BUTTON1_LNG 0x24010a +#define REF_STR_BUTTON2_LNG 0x24010b +#define REF_STR_LEVER1_LNG 0x24010c +#define REF_STR_LEVER2_LNG 0x24010d +#define REF_STR_BIGRED_LNG 0x24010e +#define REF_STR_BIGLEVER_LNG 0x24010f +#define REF_STR_DIAL_LNG 0x240110 +#define REF_STR_ACCESS_SLOT_LNG 0x240111 +#define REF_STR_CRCT_BD_SLOT_LNG 0x240112 +#define REF_STR_CHEM_RECEPT_LNG 0x240113 +#define REF_STR_ANTENNA_PAN_LNG 0x240114 +#define REF_STR_PLAS_ANTENNA_LNG 0x240115 +#define REF_STR_DEST_ANTENNA_LNG 0x240116 +#define REF_STR_RETSCANNER_LNG 0x240117 +#define REF_STR_CYB_TERM_LNG 0x240118 +#define REF_STR_ENRG_CHARGE_LNG 0x240119 +#define REF_STR_FIXUP_STATION_LNG 0x24011a +#define REF_STR_ACCPANEL1_LNG 0x24011b +#define REF_STR_ACCPANEL2_LNG 0x24011c +#define REF_STR_ACCPANEL3_LNG 0x24011d +#define REF_STR_ACCPANEL4_LNG 0x24011e +#define REF_STR_ELEPANEL1_LNG 0x24011f +#define REF_STR_ELEPANEL2_LNG 0x240120 +#define REF_STR_ELEPANEL3_LNG 0x240121 +#define REF_STR_KEYPAD1_LNG 0x240122 +#define REF_STR_KEYPAD2_LNG 0x240123 +#define REF_STR_ACCPANEL5_LNG 0x240124 +#define REF_STR_ACCPANEL6_LNG 0x240125 +#define REF_STR_AMMOVEND_LNG 0x240126 +#define REF_STR_HEALVEND_LNG 0x240127 +#define REF_STR_CYBERTOG1_LNG 0x240128 +#define REF_STR_CYBERTOG2_LNG 0x240129 +#define REF_STR_CYBERTOG3_LNG 0x24012a +#define REF_STR_BLAST_DOOR_LNG 0x24012b +#define REF_STR_ACCESS_DOOR_LNG 0x24012c +#define REF_STR_RESID_DOOR_LNG 0x24012d +#define REF_STR_MAINT_DOOR_LNG 0x24012e +#define REF_STR_HOSP_DOOR_LNG 0x24012f +#define REF_STR_LAB_DOOR_LNG 0x240130 +#define REF_STR_STOR_DOOR_LNG 0x240131 +#define REF_STR_REACTR_DOOR_LNG 0x240132 +#define REF_STR_EXEC_DOOR_LNG 0x240133 +#define REF_STR_NO_DOOR_LNG 0x240134 +#define REF_STR_LAB_DOORWAY_LNG 0x240135 +#define REF_STR_RES_DOORWAY_LNG 0x240136 +#define REF_STR_BRJ_DOORWAY_LNG 0x240137 +#define REF_STR_RCT_DOORWAY_LNG 0x240138 +#define REF_STR_GRATING1_LNG 0x240139 +#define REF_STR_GRATING2_LNG 0x24013a +#define REF_STR_GRATING3_LNG 0x24013b +#define REF_STR_GRATING4_LNG 0x24013c +#define REF_STR_NO_DOOR2_LNG 0x24013d +#define REF_STR_LABFORCE_LNG 0x24013e +#define REF_STR_BROKLABFORCE_LNG 0x24013f +#define REF_STR_RESFORCE_LNG 0x240140 +#define REF_STR_BROKRESFORCE_LNG 0x240141 +#define REF_STR_GENFORCE_LNG 0x240142 +#define REF_STR_CYBGENFORCE_LNG 0x240143 +#define REF_STR_NO_DOOR3_LNG 0x240144 +#define REF_STR_EXEC_ELEV_LNG 0x240145 +#define REF_STR_REG_ELEV1_LNG 0x240146 +#define REF_STR_REG_ELEV2_LNG 0x240147 +#define REF_STR_FREIGHT_ELEV_LNG 0x240148 +#define REF_STR_NO_DOOR4_LNG 0x240149 +#define REF_STR_DOUB_LEFTDOOR_LNG 0x24014a +#define REF_STR_DOUB_RITEDOOR_LNG 0x24014b +#define REF_STR_IRIS_LNG 0x24014c +#define REF_STR_VERT_OPEN_LNG 0x24014d +#define REF_STR_VERT_SPLIT_LNG 0x24014e +#define REF_STR_NO_DOOR5_LNG 0x24014f +#define REF_STR_SECRET_DOOR1_LNG 0x240150 +#define REF_STR_SECRET_DOOR2_LNG 0x240151 +#define REF_STR_SECRET_DOOR3_LNG 0x240152 +#define REF_STR_INVISO_DOOR_LNG 0x240153 +#define REF_STR_ALERT_PANEL_OFF_LNG 0x240154 +#define REF_STR_ALERT_PANEL_ON_LNG 0x240155 +#define REF_STR_HORZ_KLAXOFF_LNG 0x240156 +#define REF_STR_HORZ_KLAXON_LNG 0x240157 +#define REF_STR_SPARK_CABLE_LNG 0x240158 +#define REF_STR_TWITCH_MUT2_LNG 0x240159 +#define REF_STR_MACHINE_LNG 0x24015a +#define REF_STR_HOLOG_ANIM_LNG 0x24015b +#define REF_STR_TWITCH_MUT_LNG 0x24015c +#define REF_STR_BLOOD1_LNG 0x24015d +#define REF_STR_CAMEXPL_LNG 0x24015e +#define REF_STR_TVEXPL_LNG 0x24015f +#define REF_STR_SIMPLSMOKE_LNG 0x240160 +#define REF_STR_PLANTEXPL_LNG 0x240161 +#define REF_STR_BULLETWALLHIT_LNG 0x240162 +#define REF_STR_BEAMWALLHIT_LNG 0x240163 +#define REF_STR_IMPACT_ANIM_LNG 0x240164 +#define REF_STR_BULL_ROBOT_LNG 0x240165 +#define REF_STR_BEAM_ROBOT1_LNG 0x240166 +#define REF_STR_BEAM_ROBOT2_LNG 0x240167 +#define REF_STR_EXPLOSION1_LNG 0x240168 +#define REF_STR_EXPLOSION2_LNG 0x240169 +#define REF_STR_EXPLOSION3_LNG 0x24016a +#define REF_STR_LG_EXPLOSION_LNG 0x24016b +#define REF_STR_MAGPULSEHIT_LNG 0x24016c +#define REF_STR_STUNHIT_LNG 0x24016d +#define REF_STR_PLASMAHIT_LNG 0x24016e +#define REF_STR_SMOKEEXPL_LNG 0x24016f +#define REF_STR_CRATEEXPL_LNG 0x240170 +#define REF_STR_MNTR2EXPL_LNG 0x240171 +#define REF_STR_GASEXPL_LNG 0x240172 +#define REF_STR_EMPEXPL_LNG 0x240173 +#define REF_STR_CORP_HUM_EXPL_LNG 0x240174 +#define REF_STR_CORP_ROB_EXPL_LNG 0x240175 +#define REF_STR_ENTRY_TRIG_LNG 0x240176 +#define REF_STR_NULL_TRIG_LNG 0x240177 +#define REF_STR_FLOOR_TRIG_LNG 0x240178 +#define REF_STR_PLRDETH_TRIG_LNG 0x240179 +#define REF_STR_DETHWATCH_TRIG_LNG 0x24017a +#define REF_STR_AOE_ENT_TRIG_LNG 0x24017b +#define REF_STR_AOE_CON_TRIG_LNG 0x24017c +#define REF_STR_AI_HINT_LNG 0x24017d +#define REF_STR_LEVEL_TRIG_LNG 0x24017e +#define REF_STR_CONTIN_TRIG_LNG 0x24017f +#define REF_STR_REPULSOR_LNG 0x240180 +#define REF_STR_ECOLOGY_TRIG_LNG 0x240181 +#define REF_STR_SHODO_TRIG_LNG 0x240182 +#define REF_STR_TRIPBEAM_LNG 0x240183 +#define REF_STR_BIOHAZARD_LNG 0x240184 +#define REF_STR_RADHAZARD_LNG 0x240185 +#define REF_STR_CHEMHAZARD_LNG 0x240186 +#define REF_STR_MAPNOTE_LNG 0x240187 +#define REF_STR_MUSIC_MARK_LNG 0x240188 +#define REF_STR_SML_CRT_LNG 0x240189 +#define REF_STR_LG_CRT_LNG 0x24018a +#define REF_STR_SECURE_CONTR_LNG 0x24018b +#define REF_STR_RAD_BARREL_LNG 0x24018c +#define REF_STR_TOXIC_BARREL_LNG 0x24018d +#define REF_STR_CHEM_TANK_LNG 0x24018e +#define REF_STR_THERMOS_LNG 0x24018f +#define REF_STR_VIAL_CONT_LNG 0x240190 +#define REF_STR_FLASK_CONT_LNG 0x240191 +#define REF_STR_BEAKER_CONT_LNG 0x240192 +#define REF_STR_MUT_CORPSE1_LNG 0x240193 +#define REF_STR_MUT_CORPSE2_LNG 0x240194 +#define REF_STR_MUT_CORPSE3_LNG 0x240195 +#define REF_STR_MUT_CORPSE4_LNG 0x240196 +#define REF_STR_MUT_CORPSE5_LNG 0x240197 +#define REF_STR_MUT_CORPSE6_LNG 0x240198 +#define REF_STR_MUT_CORPSE7_LNG 0x240199 +#define REF_STR_MUT_CORPSE8_LNG 0x24019a +#define REF_STR_ROB_CORPSE1_LNG 0x24019b +#define REF_STR_ROB_CORPSE2_LNG 0x24019c +#define REF_STR_ROB_CORPSE3_LNG 0x24019d +#define REF_STR_ROB_CORPSE4_LNG 0x24019e +#define REF_STR_ROB_CORPSE5_LNG 0x24019f +#define REF_STR_ROB_CORPSE6_LNG 0x2401a0 +#define REF_STR_ROB_CORPSE7_LNG 0x2401a1 +#define REF_STR_ROB_CORPSE8_LNG 0x2401a2 +#define REF_STR_ROB_CORPSE9_LNG 0x2401a3 +#define REF_STR_ROB_CORPSE10_LNG 0x2401a4 +#define REF_STR_ROB_CORPSE11_LNG 0x2401a5 +#define REF_STR_ROB_CORPSE12_LNG 0x2401a6 +#define REF_STR_ROB_CORPSE13_LNG 0x2401a7 +#define REF_STR_CYB_CORPSE1_LNG 0x2401a8 +#define REF_STR_CYB_CORPSE2_LNG 0x2401a9 +#define REF_STR_CYB_CORPSE3_LNG 0x2401aa +#define REF_STR_CYB_CORPSE4_LNG 0x2401ab +#define REF_STR_CYB_CORPSE5_LNG 0x2401ac +#define REF_STR_CYB_CORPSE6_LNG 0x2401ad +#define REF_STR_CYB_CORPSE7_LNG 0x2401ae +#define REF_STR_OTH_CORPSE1_LNG 0x2401af +#define REF_STR_OTH_CORPSE2_LNG 0x2401b0 +#define REF_STR_OTH_CORPSE3_LNG 0x2401b1 +#define REF_STR_OTH_CORPSE4_LNG 0x2401b2 +#define REF_STR_OTH_CORPSE5_LNG 0x2401b3 +#define REF_STR_OTH_CORPSE6_LNG 0x2401b4 +#define REF_STR_OTH_CORPSE7_LNG 0x2401b5 +#define REF_STR_OTH_CORPSE8_LNG 0x2401b6 +#define REF_STR_HUMAN_CRIT_LNG 0x2401b7 +#define REF_STR_GOR_TIGER_LNG 0x2401b8 +#define REF_STR_INSECT_CRIT_LNG 0x2401b9 +#define REF_STR_AVIAN_CRIT_LNG 0x2401ba +#define REF_STR_PLANT_CRIT_LNG 0x2401bb +#define REF_STR_ZERO_CRIT_LNG 0x2401bc +#define REF_STR_PLAYER_CRIT_LNG 0x2401bd +#define REF_STR_INVISO_CRIT_LNG 0x2401be +#define REF_STR_VIRUS_CRIT_LNG 0x2401bf +#define REF_STR_LIFT_BOT_LNG 0x2401c0 +#define REF_STR_REPAIRBOT_LNG 0x2401c1 +#define REF_STR_SERVBOT_LNG 0x2401c2 +#define REF_STR_EXECBOT_LNG 0x2401c3 +#define REF_STR_LGTURRET_LNG 0x2401c4 +#define REF_STR_HOPPER_LNG 0x2401c5 +#define REF_STR_SECURITY_BOT1_LNG 0x2401c6 +#define REF_STR_SECURITY_BOT2_LNG 0x2401c7 +#define REF_STR_AUTOBOMB_LNG 0x2401c8 +#define REF_STR_REPAIRBOT2_LNG 0x2401c9 +#define REF_STR_FLIER_LNG 0x2401ca +#define REF_STR_SECURITY_BOT3_LNG 0x2401cb +#define REF_STR_CYBORG_DRONE_LNG 0x2401cc +#define REF_STR_WARRIOR_LNG 0x2401cd +#define REF_STR_ASSASSIN_LNG 0x2401ce +#define REF_STR_CYBERBABE_LNG 0x2401cf +#define REF_STR_ELITE_GUARD_LNG 0x2401d0 +#define REF_STR_CORTEX_REAVER_LNG 0x2401d1 +#define REF_STR_MUTANT_BORG_LNG 0x2401d2 +#define REF_STR_CYBERDOG_LNG 0x2401d3 +#define REF_STR_CYBERGUARD_LNG 0x2401d4 +#define REF_STR_CYBER_CORTEX_LNG 0x2401d5 +#define REF_STR_CYBER_DYN_ICE_LNG 0x2401d6 +#define REF_STR_CYBER_HNT_KIL_LNG 0x2401d7 +#define REF_STR_CYBER_SHODAN_LNG 0x2401d8 +#define REF_STR_CYBERGUARD2_LNG 0x2401d9 +#define REF_STR_ROBOBABE_LNG 0x2401da +#define REF_STR_DIEGO_LNG 0x2401db +#define RES_paper0 0x3c // (60) +#define RES_paper1 0x3d // (61) +#define RES_paper2 0x3e // (62) +#define RES_paper3 0x3f // (63) +#define RES_paper4 0x40 // (64) +#define RES_paper5 0x41 // (65) +#define RES_paper6 0x42 // (66) +#define RES_paper7 0x43 // (67) +#define RES_paper8 0x44 // (68) +#define RES_paper9 0x45 // (69) +#define RES_paper10 0x46 // (70) +#define RES_email0 0x989 // (2441) +#define RES_email1 0x98a // (2442) +#define RES_email2 0x98b // (2443) +#define RES_email3 0x98c // (2444) +#define RES_email4 0x98d // (2445) +#define RES_email5 0x98e // (2446) +#define RES_email6 0x98f // (2447) +#define RES_email7 0x990 // (2448) +#define RES_email8 0x991 // (2449) +#define RES_email9 0x992 // (2450) +#define RES_email10 0x993 // (2451) +#define RES_email11 0x994 // (2452) +#define RES_email12 0x995 // (2453) +#define RES_email13 0x996 // (2454) +#define RES_email14 0x997 // (2455) +#define RES_email15 0x998 // (2456) +#define RES_email16 0x999 // (2457) +#define RES_email17 0x99a // (2458) +#define RES_email18 0x99b // (2459) +#define RES_email19 0x99c // (2460) +#define RES_email20 0x99d // (2461) +#define RES_email21 0x99e // (2462) +#define RES_email22 0x99f // (2463) +#define RES_email23 0x9a0 // (2464) +#define RES_email24 0x9a1 // (2465) +#define RES_email25 0x9a2 // (2466) +#define RES_email26 0x9a3 // (2467) +#define RES_email27 0x9a4 // (2468) +#define RES_email28 0x9a5 // (2469) +#define RES_email29 0x9a6 // (2470) +#define RES_email30 0x9a7 // (2471) +#define RES_email31 0x9a8 // (2472) +#define RES_email32 0x9a9 // (2473) +#define RES_email33 0x9aa // (2474) +#define RES_email34 0x9ab // (2475) +#define RES_email35 0x9ac // (2476) +#define RES_email36 0x9ad // (2477) +#define RES_email37 0x9ae // (2478) +#define RES_email38 0x9af // (2479) +#define RES_email39 0x9b0 // (2480) +#define RES_email40 0x9b1 // (2481) +#define RES_email41 0x9b2 // (2482) +#define RES_email42 0x9b3 // (2483) +#define RES_email43 0x9b4 // (2484) +#define RES_email44 0x9b5 // (2485) +#define RES_email45 0x9b6 // (2486) +#define RES_log00 0x9b8 // (2488) +#define RES_log01 0x9b9 // (2489) +#define RES_log02 0x9ba // (2490) +#define RES_log03 0x9bb // (2491) +#define RES_log04 0x9bc // (2492) +#define RES_log05 0x9bd // (2493) +#define RES_log10 0x9c8 // (2504) +#define RES_log11 0x9c9 // (2505) +#define RES_log12 0x9ca // (2506) +#define RES_log13 0x9cb // (2507) +#define RES_log14 0x9cc // (2508) +#define RES_log15 0x9cd // (2509) +#define RES_log16 0x9ce // (2510) +#define RES_log17 0x9cf // (2511) +#define RES_log18 0x9d0 // (2512) +#define RES_log19 0x9d1 // (2513) +#define RES_log110 0x9d2 // (2514) +#define RES_log111 0x9d3 // (2515) +#define RES_log112 0x9d4 // (2516) +#define RES_log113 0x9d5 // (2517) +#define RES_log114 0x9d6 // (2518) +#define RES_log115 0x9d7 // (2519) +#define RES_log20 0x9d8 // (2520) +#define RES_log21 0x9d9 // (2521) +#define RES_log22 0x9da // (2522) +#define RES_log23 0x9db // (2523) +#define RES_log24 0x9dc // (2524) +#define RES_log25 0x9dd // (2525) +#define RES_log26 0x9de // (2526) +#define RES_log27 0x9df // (2527) +#define RES_log28 0x9e0 // (2528) +#define RES_log29 0x9e1 // (2529) +#define RES_log210 0x9e2 // (2530) +#define RES_log211 0x9e3 // (2531) +#define RES_log212 0x9e4 // (2532) +#define RES_log30 0x9e8 // (2536) +#define RES_log31 0x9e9 // (2537) +#define RES_log32 0x9ea // (2538) +#define RES_log33 0x9eb // (2539) +#define RES_log34 0x9ec // (2540) +#define RES_log35 0x9ed // (2541) +#define RES_log36 0x9ee // (2542) +#define RES_log37 0x9ef // (2543) +#define RES_log38 0x9f0 // (2544) +#define RES_log40 0x9f8 // (2552) +#define RES_log41 0x9f9 // (2553) +#define RES_log42 0x9fa // (2554) +#define RES_log43 0x9fb // (2555) +#define RES_log44 0x9fc // (2556) +#define RES_log45 0x9fd // (2557) +#define RES_log46 0x9fe // (2558) +#define RES_log50 0xa08 // (2568) +#define RES_log51 0xa09 // (2569) +#define RES_log52 0xa0a // (2570) +#define RES_log53 0xa0b // (2571) +#define RES_log54 0xa0c // (2572) +#define RES_log55 0xa0d // (2573) +#define RES_log56 0xa0e // (2574) +#define RES_log57 0xa0f // (2575) +#define RES_log58 0xa10 // (2576) +#define RES_log59 0xa11 // (2577) +#define RES_log510 0xa12 // (2578) +#define RES_log511 0xa13 // (2579) +#define RES_log60 0xa18 // (2584) +#define RES_log61 0xa19 // (2585) +#define RES_log62 0xa1a // (2586) +#define RES_log63 0xa1b // (2587) +#define RES_log64 0xa1c // (2588) +#define RES_log65 0xa1d // (2589) +#define RES_log66 0xa1e // (2590) +#define RES_log67 0xa1f // (2591) +#define RES_log68 0xa20 // (2592) +#define RES_log69 0xa21 // (2593) +#define RES_log70 0xa28 // (2600) +#define RES_log71 0xa29 // (2601) +#define RES_log72 0xa2a // (2602) +#define RES_log73 0xa2b // (2603) +#define RES_log74 0xa2c // (2604) +#define RES_log75 0xa2d // (2605) +#define RES_log76 0xa2e // (2606) +#define RES_log77 0xa2f // (2607) +#define RES_log78 0xa30 // (2608) +#define RES_log80 0xa38 // (2616) +#define RES_log81 0xa39 // (2617) +#define RES_log82 0xa3a // (2618) +#define RES_log83 0xa3b // (2619) +#define RES_log84 0xa3c // (2620) +#define RES_log85 0xa3d // (2621) +#define RES_data0 0xa98 // (2712) +#define RES_data1 0xa99 // (2713) +#define RES_data2 0xa9a // (2714) +#define RES_data3 0xa9b // (2715) +#define RES_data4 0xa9c // (2716) +#define RES_data5 0xa9d // (2717) +#define RES_data6 0xa9e // (2718) +#define RES_data7 0xa9f // (2719) +#define RES_data8 0xaa0 // (2720) +#define RES_data9 0xaa1 // (2721) +#define RES_data10 0xaa2 // (2722) +#define RES_data11 0xaa3 // (2723) +#define RES_data12 0xaa4 // (2724) + +#endif diff --git a/engine/src/GameSrc/Headers/damage.h b/engine/src/GameSrc/Headers/damage.h new file mode 100644 index 0000000..d88fce5 --- /dev/null +++ b/engine/src/GameSrc/Headers/damage.h @@ -0,0 +1,146 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/inc/RCS/damage.h $ + * $Revision: 1.34 $ + * $Author: minman $ + * $Date: 1994/08/09 04:46:01 $ + * + */ + +#ifndef __DAMAGE_H +#define __DAMAGE_H + +// Includes +#include "objects.h" +#include "combat.h" + +#define EXPLOSION_TYPE 1 +#define ENERGY_BEAM_TYPE 2 +#define MAGNETIC_TYPE 3 +#define RADIATION_TYPE 4 + +#define GAS_TYPE 5 +#define TRANQ_TYPE 6 +#define NEEDLE_TYPE 7 +#define BIO_TYPE 8 + +#define DAMAGE_TYPE2MASK(n) (1 << ((n) - 1)) + +#define DAMAGE_TYPE_FIELD 0x00FF +#define PRIMARY_DAMAGE_FIELD 0x0F00 +#define PRIMARY_DAMAGE(X) (((X) >> 8) & 0xF) +#define SUPER_DAMAGE_FIELD 0xF000 +#define SUPER_DAMAGE(X) (((X) >> 12) & 0xF) + +#define CYBER_EXPLOSION_TYPE 1 +#define CYBER_PROJECTILE_TYPE 2 +#define CYBER_DRILL_TYPE 3 + +#define EXPLOSION_FLAG (0x01 << (EXPLOSION_TYPE - 1)) +#define ENERGY_BEAM_FLAG (0x01 << (ENERGY_BEAM_TYPE - 1)) +#define MAGNETIC_FLAG (0x01 << (MAGNETIC_TYPE - 1)) +#define RADIATION_FLAG (0x01 << (RADIATION_TYPE - 1)) +#define GAS_FLAG (0x01 << (GAS_TYPE - 1)) +#define TRANQ_FLAG (0x01 << (TRANQ_TYPE - 1)) +#define NEEDLE_FLAG (0x01 << (NEEDLE_TYPE - 1)) +#define SLEEP_FLAG (0x01 << (SLEEP_TYPE - 1)) + +#define CYBER_EXPLOSION_FLAG (0x01 << (CYBER_EXPLOSION_TYPE - 1)) +#define CYBER_PROJECTILE_FLAG (0x01 << (CYBER_PROJECTILE_TYPE - 1)) +#define CYBER_DRILL_FLAG (0x01 << (CYBER_DRILL_TYPE - 1)) + +#define DAMAGE_MIN 0 +#define DAMAGE_MAX 4 +#define DAMAGE_DEGREES 6 + +#define DAMAGE_NONE 0 +#define DAMAGE_LIGHT 1 +#define DAMAGE_MEDIUM 2 +#define DAMAGE_SEVERE 3 +#define DAMAGE_CRITICAL 4 +#define DAMAGE_INEFFECTIVE 5 +#define DAMAGE_TRANQ 6 +#define DAMAGE_STUN 7 + +// attack_object flags +#define NO_SHIELD_ABSORBTION 0x01 // should the player's shield not absorb damage + // default is to absorb +#define FLASH_BLOOD 0x02 +#define STUN_ATTACK 0x04 + +#define MAX_DESTROYED_OBJS 100 + +extern short destroyed_obj_count; +extern ObjID destroyed_ids[MAX_DESTROYED_OBJS]; + +#define ADD_DESTROYED_OBJECT(X) (destroyed_ids[destroyed_obj_count++] = X) + +// is_obj_destroyed() +// returns TRUE, if object with id, is scheduled for destruction +uchar is_obj_destroyed(ObjID id); + +// destroy_destroyed_objects() +// destroy all objects that have been scheduled to be destroyed +void destroy_destroyed_objects(void); + +// damage_object() +// flags - if the low bit is set - then damage is not absorbed by shields +// damage_modifier - raw damage value of attack - 0 if attacking player +ubyte damage_object(ObjID target_id, int damage, int dtype, ubyte flags); + +int compute_damage(ObjID target, int damage_type, int damage_mod, ubyte offense, ubyte penet, int power_level, + ubyte *effect, ubyte *effect_row, ubyte attack_effect_type); + +ubyte object_affect(ObjID target_id, short dtype); + +// simple_damage object just takes some damage and damage type +// (and flags) and damages the object if it is vulnerable. + +uchar simple_damage_object(ObjID target, int damage, ubyte dtype, ubyte flags); +uchar terrain_damage_object(physics_handle ph, fix raw_damage); +uchar special_terrain_hit(ObjID cobjid); + +// attack_object() +// +// general all purpose attack/damage object +// target - the ObjID of the target being attacked +// weapon_triple - the triple for the bullet, grenade, or beam gun +// flags - see above +// power_level - will be ignored for projectile weapons, but used for grenades and beam guns +// +ubyte attack_object(ObjID target, int damage_type, int damage_mod, ubyte offense, ubyte penet, ubyte flags, + int power_level, ubyte *effect_row, ubyte *effect, ubyte attack_effect_type, int *damage_inflicted); + +// player_attack_object +// +ubyte player_attack_object(ObjID target, int wpn_triple, int power_level, Combat_Pt origin); + +// get an estimate of how damaged we are, with above numerical index +int get_damage_estimate(ObjSpecID osid); + +void spew_object_specs(void); +uchar test_object_specs(short keycode, ulong context, void *data); +uchar damage_player(int damage, ubyte dtype, ubyte flags); +uchar kill_player(void); +void regenerate_player(void); + +void slow_proj_hit(ObjID id, ObjID victim); + +#endif // __DAMAGE_H diff --git a/engine/src/GameSrc/Headers/diffq.h b/engine/src/GameSrc/Headers/diffq.h new file mode 100644 index 0000000..50b9352 --- /dev/null +++ b/engine/src/GameSrc/Headers/diffq.h @@ -0,0 +1,27 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// difficulty defines + +#define MISSION_DIFF_QVAR 0xD +#define CYBER_DIFF_QVAR 0xE +#define COMBAT_DIFF_QVAR 0xF +#define PUZZLE_DIFF_QVAR 0x1E + +// 7 hours on mission difficulty 3 +#define MISSION_3_TICKS CIT_CYCLE * 3600 * 7 diff --git a/engine/src/GameSrc/Headers/digifx.h b/engine/src/GameSrc/Headers/digifx.h new file mode 100644 index 0000000..2bbb725 --- /dev/null +++ b/engine/src/GameSrc/Headers/digifx.h @@ -0,0 +1,28 @@ +/* + +Copyright (C) 2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef DIGIFX_H +#define DIGIFX_H + +int compute_sfx_vol(ushort x1, ushort y1, ushort x2, ushort y2); +int compute_sfx_pan(ushort x1, ushort y1, ushort x2, ushort y2, fixang our_ang); + +uchar set_sample_pan_gain(snd_digi_parms *sdp); + +#endif diff --git a/engine/src/GameSrc/Headers/dirac.h b/engine/src/GameSrc/Headers/dirac.h new file mode 100644 index 0000000..255bd7e --- /dev/null +++ b/engine/src/GameSrc/Headers/dirac.h @@ -0,0 +1,36 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Here is the high and mighty Dirac Frame header file, courtesy of Mr. Robert Fermier... +// ====================================================================================== + +// Struct... +// --------- +typedef struct { + + fix mass, hardness, roughness, gravity; + + fix corners[10][4]; + +} Dirac_frame; + +physics_handle EDMS_make_Dirac_frame(Dirac_frame *d, State *s); +void EDMS_get_Dirac_frame_viewpoint(physics_handle ph, State *s); +void EDMS_set_Dirac_frame_parameters(physics_handle ph, Dirac_frame *d); +void EDMS_get_Dirac_frame_parameters(physics_handle ph, Dirac_frame *d); +void EDMS_control_Dirac_frame(physics_handle ph, fix forward, fix pitch, fix yaw, fix roll); diff --git a/engine/src/GameSrc/Headers/doorparm.h b/engine/src/GameSrc/Headers/doorparm.h new file mode 100644 index 0000000..de49e7d --- /dev/null +++ b/engine/src/GameSrc/Headers/doorparm.h @@ -0,0 +1,29 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// at which frame and above the door is considered "open" to physics.... +#define DOOR_OPEN_FRAME 3 + +#define DOOR_CLOSED(id) (objs[(id)].info.current_frame < DOOR_OPEN_FRAME) +#define DOOR_REALLY_CLOSED(id) (objs[(id)].info.current_frame == 0) + +#define NEVER_AUTOCLOSE_COOKIE ((ubyte)-1) + +extern uchar check_object_dist(ObjID obj1, ObjID obj2, fix crit); +extern uchar door_locked(ObjID); +extern uchar door_moving(ObjID, uchar); diff --git a/engine/src/GameSrc/Headers/drugs.h b/engine/src/GameSrc/Headers/drugs.h new file mode 100644 index 0000000..9f98f14 --- /dev/null +++ b/engine/src/GameSrc/Headers/drugs.h @@ -0,0 +1,104 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __DRUGS_H +#define __DRUGS_H + +/* + * $Source: n:/project/cit/src/inc/RCS/drugs.h $ + * $Revision: 1.11 $ + * $Author: xemu $ + * $Date: 1994/05/13 21:11:25 $ + * + * $Log: drugs.h $ + * Revision 1.11 1994/05/13 21:11:25 xemu + * reflex + * + * Revision 1.10 1994/02/01 04:37:07 mahk + * DRUGS ! + * + * Revision 1.9 1993/09/02 23:07:28 xemu + * angle me baby + * + * Revision 1.8 1993/08/11 20:53:43 spaz + * Changed drug identifying #define's + * + * Revision 1.7 1993/07/28 16:57:02 mahk + * Added drug2triple & triple2drug + * + * Revision 1.6 1993/07/26 21:24:47 spaz + * moved drug id's to #define's here + * + * Revision 1.5 1993/07/19 11:39:48 mahk + * Added inventory stuff + * + * Revision 1.4 1993/07/08 13:29:35 spaz + * Moved some #define's around. + * + * Revision 1.3 1993/07/01 19:16:38 spaz + * Added constants and prototypes for generic drug.c funcs. + * + * Revision 1.2 1993/07/01 17:19:37 spaz + * Added NUM_DRUGZ #define, and drugs_time[] static array. + * + * Revision 1.1 1993/07/01 16:26:23 mahk + * Initial revision + * + * + */ + +// Defines + +#define DRUG_STAMINUP 0 +#define DRUG_SIGHT 1 +#define DRUG_LSD 2 +#define DRUG_MEDIC 3 +#define DRUG_REFLEX 4 +#define DRUG_GENIUS 5 +#define DRUG_DETOX 6 + +// Includes + +// C Library Includes + +// System Library Includes + +// Master Game Includes + +// Game Library Includes + +// Game Object Includes + +// Defines + +// Prototypes + +char *get_drug_name(int type, char *buf); +void drug_use(int type); +void drug_wear_off(int type); +void drug_effect(int type); +void drugs_update(); +void drugs_init(); +void drug_startup(bool visible); +void drug_closedown(bool visible); +int drug2triple(int type); +int triple2drug(int triple); + +// Globals + +#endif // __DRUGS_H diff --git a/engine/src/GameSrc/Headers/dynmem.h b/engine/src/GameSrc/Headers/dynmem.h new file mode 100644 index 0000000..5e5aa67 --- /dev/null +++ b/engine/src/GameSrc/Headers/dynmem.h @@ -0,0 +1,28 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#define DYNMEM_ALL 0xFFFFFFFF +#define DYNMEM_NONE 0x00000000 +#define DYNMEM_TEXTURES 0x00000001 +#define DYNMEM_SIDEICONS 0x00000002 +#define DYNMEM_FHANDLE_1 0x00000004 +#define DYNMEM_FHANDLE_2 0x00000008 +#define DYNMEM_FHANDLE_3 0x00000010 +#define DYNMEM_FHANDLE_4 0x00000020 +#define DYNMEM_FILEHANDLES DYNMEM_FHANDLE_1 | DYNMEM_FHANDLE_2 | DYNMEM_FHANDLE_3 | DYNMEM_FHANDLE_4 +#define DYNMEM_PARTIAL DYNMEM_SIDEICONS | DYNMEM_FHANDLE_1 | DYNMEM_FHANDLE_3 | DYNMEM_FHANDLE_4 diff --git a/engine/src/GameSrc/Headers/effect.h b/engine/src/GameSrc/Headers/effect.h new file mode 100644 index 0000000..d8548ff --- /dev/null +++ b/engine/src/GameSrc/Headers/effect.h @@ -0,0 +1,197 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __EFFECT_H +#define __EFFECT_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/effect.h $ + * $Revision: 1.35 $ + * $Author: xemu $ + * $Date: 1994/11/21 21:05:41 $ + * + */ + +// Includes +#include "objects.h" +#include "objcrit.h" +#include "objgame.h" +#include "objwpn.h" + +#define BLOOD_LIGHT 1 +#define CAMERA_EXPL 2 +#define TV_EXPL 3 +#define SMOKE 4 +#define PLNT_EXPL 5 +#define BULL_HIT_WALL 6 +#define BEAM_HIT_WALL 7 +#define IMPACT 8 +#define BULLET_ROBOT 9 +#define BEAM_ROBOT_LT 10 +#define BEAM_ROBOT_HVY 11 +#define M_EXPL1 12 +#define M_EXPL2 13 +#define M_EXPL3 14 +#define LG_EXPL 15 +#define MAG_HIT 16 +#define STUN_HIT 17 +#define PLASMA_HIT 18 +#define SMOKEY_EXPL 19 +#define CRATE_EXPL 20 +#define MNTR_EXPL2 21 +#define GAS_EXPL 22 +#define EMP_EXPL 23 +#define CORPSE_HUM_EXPL 24 +#define CORPSE_ROB_EXPL 25 +#define EFFECT_NUMS 25 + +// should these be here - or is this just for special effects in the 3D?? +#define SHIELD_HIT_EFFECT 40 +#define SHIELD_GONE_EFFECT 41 +#define BODY_HIT_EFFECT 42 + +#define CRIT_HIT_NUM 5 +#define SEVERITIES 2 +#define AMMO_TYPES 4 +#define PROJ_TYPE 0 +#define BEAM_TYPE 1 +#define HAND_TYPE 2 +#define GREN_TYPE 3 +#define NON_CRITTER_EFFECT (CRIT_HIT_NUM - 1) +#define SPECIAL_TYPE CRIT_HIT_NUM + +extern ubyte effect_matrix[CRIT_HIT_NUM][AMMO_TYPES][SEVERITIES]; + +// info about the destruction of an object +// (a) should we play an effect? +// (b) should we destroy the object DURING the effect +// (c) should we play a sound effect? + +#define EFFECT_VAL(x) ((x)&0x1F) +#define DESTROY_SOUND_EFFECT(x) (((x)&0x60) >> 5) +#define DESTROY_OBJ_EFFECT(x) ((x)&0x80) + +// these are things about the state of the effects +// they are cleverly hidden in the instance data of the effect +// in start_frame + +#define EFFECT_LIGHT_FLAG 0x01 + +#define EFFECT_LIGHT_MAP_SHIFT 3 +#define EFFECT_LIGHT_MAP_MASK 0x78 +#define EFFECT_DESTROY_OBJ_FLAG 0x80 + +#define EFFECT_LIGHT_MAP(x) (((x)&EFFECT_LIGHT_MAP_MASK) >> EFFECT_LIGHT_MAP_SHIFT) +#define SET_EFFECT_LIGHT_MAP(x, y) (x = (((x) & ~(EFFECT_LIGHT_MAP_MASK)) | (y << EFFECT_LIGHT_MAP_SHIFT))) +#define CLEAR_EFFECT_LIGHT_MAP(x) ((x) &= ~(EFFECT_LIGHT_MAP_MASK)) + +#define EFFECT_DESTROY_OBJ(x) ((x) & EFFECT_DESTROY_OBJ_FLAG) +#define SET_EFFECT_DESTROY_OBJ(x) ((x) |= EFFECT_DESTROY_OBJ_FLAG) +#define CLEAR_EFFECT_DESTROY_OBJ(x) ((x) &= ~(EFFECT_DESTROY_OBJ_FLAG)) + +#define START_FRAME(x) ((x) & ~(EFFECT_LIGHT_MAP_MASK | EFFECT_DESTROY_OBJ_FLAG)) + +// the effect location +#define EFFECT_LOC(id) (objs[id].info.make_info & 0x03) +#define SET_EFFECT_LOC(id, x) (objs[id].info.make_info &= (~0x03 | x)) +#define EFFECT_LEFT 01 +#define EFFECT_RIGHT 02 +#define EFFECT_CENTER 03 + +// the effect number +#define EFFECT_NUM(id) ((objs[id].info.make_info & 0x7C) >> 2) +#define SET_EFFECT_NUM(id, x) (objs[id].info.make_info &= (~0x7C | (x << 2))) + +// show two effects???????? +#define EFFECT_DUAL(id) (objs[id].info.make_info & 0x80) +#define SET_EFFECT_DUAL(id, x) (objs[id].info.make_info &= (x << 7)) + +// frame count +#define EFFECT_FRAME(id) ((objCritters[objs[id].specID].mood & 0x70) >> 4) +#define SET_EFFECT_FRAME(id, x) (objCritters[objs[id].specID].mood &= (~0x70 | x << 4)) + +// height's range is from 1-7 (one being the lowest, 3 the center) +#define EFFECT_HEIGHT(id) ((objCritters[objs[id].specID].orders & 0x70) >> 4) +#define SET_EFFECT_HEIGHT(id, x) (objCritters[objs[id].specID].orders &= (~0x70 | x << 4)) + +// scale's range is from 1-3 (1 being the smallest) +#define EFFECT_SCALE(id) \ + (((objCritters[objs[id].specID].orders & 0x80) >> 6) | ((objCritters[objs[id].specID].mood & 0x80) >> 7)) +#define SET_EFFECT_SCALE(id, x) \ + do { \ + objCritters[objs[id].specID].orders &= (0x7F | ((x & 0x02) << 6)); \ + objCritters[objs[id].specID].mood &= (0x7F | ((x & 0x01) << 7)); \ + } while (0); + +#define EFFECT_EIGHTH(id) ((objCritters[objs[id].specID].current_posture & 0xE0) >> 5) +#define SET_EFFECT_EIGHTH(id, x) (objCritters[objs[id].specID].current_posture &= (~0xE0 | (x << 5))) + +#define EFFT2TRIP(num) \ + ((num <= NUM_TRANSITORY_ANIMATING) ? (BLOOD1_TRIPLE + num - 1) \ + : (EXPLOSION1_TRIPLE + num - (NUM_TRANSITORY_ANIMATING + 1))) + +#define OBJ_LOC_TO_LIGHT_LOC(val) ((val + 0x80) >> 8) + +#define CRITTER_LAMP_MASK 0xF0 +#define CRITTER_LAMP_SHIFT 4 +#define CRITLIT(id) (objCritters[objs[id].specID].current_posture) + +#define CRITLOCX(id) (objs[id].info.make_info) +#define SET_CRITLOCX(id, val) (objs[id].info.make_info = val) +#define CRITLOCY(id) (objCritters[objs[id].specID].ai_mode) +#define SET_CRITLOCY(id, val) (objCritters[objs[id].specID].ai_mode = val) + +#define CRITTER_LAMP(id) ((CRITLIT(id) & CRITTER_LAMP_MASK) >> CRITTER_LAMP_SHIFT) +#define SET_CRITTER_LAMP(id, lval) (CRITLIT(id) = ((CRITLIT(id) & (~CRITTER_LAMP_MASK)) | (lval << CRITTER_LAMP_SHIFT))) +#define CLEAR_CRITTER_LAMP(id) (CRITLIT(id) &= (~CRITTER_LAMP_MASK)) + +typedef void (*AnimlistCB)(ObjID id, intptr_t user_data); + +// do_special_effect +ObjID do_special_effect(ObjID owner, ubyte effect, ubyte start, ObjID obj, short location); +ObjID do_special_effect_location(ObjID owner, ubyte effect, ubyte start, ObjLoc *loc, short location); +void advance_animations(void); +errtype add_obj_to_animlist(ObjID id, uchar repeat, uchar reverse, uchar cycle, short speed, int cb_id, intptr_t user_data, + short cbtype); +errtype remove_obj_from_animlist(ObjID id); +errtype animlist_clear(); +uchar anim_data_from_id(ObjID id, bool *reverse, bool *cycle); + +#define MAX_ANIMLIST_SIZE 64 + +#define ANIMCB_REMOVE 1 +#define ANIMCB_REPEAT 2 +#define ANIMCB_CYCLE 3 + +#define ANIMFLAG_REPEAT 1 +#define ANIMFLAG_REVERSE 2 +#define ANIMFLAG_CYCLE 4 + +typedef struct { + ObjID id; + uchar flags; + short cbtype; + int callback; + intptr_t user_data; + short speed; +} AnimListing; + +extern short anim_counter; +extern AnimListing animlist[MAX_ANIMLIST_SIZE]; + +#endif // __EFFECT_H diff --git a/engine/src/GameSrc/Headers/email.h b/engine/src/GameSrc/Headers/email.h new file mode 100644 index 0000000..24baba0 --- /dev/null +++ b/engine/src/GameSrc/Headers/email.h @@ -0,0 +1,41 @@ +/* + +Copyright (C) 2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef EMAIL_H +#define EMAIL_H + +#include "mfdint.h" + +void add_email_handler(LGRegion *r); +uchar email_color_func(void *dp, int num); +char *email_name_func(void *dp, int num, char *buf); +void read_email(Id new_base, int num); +void select_email(int num, uchar scr); +void set_email_flags(int n); +void update_email_ware(); +void email_page_exit(void); +void mfd_emailmug_expose(MFD *mfd, ubyte control); +uchar mfd_emailmug_handler(MFD *m, uiEvent *ev); +errtype mfd_emailware_init(MFD_Func *f); +void mfd_emailware_expose(MFD *mfd, ubyte control); + +void email_turnon(uchar visible, uchar real_start); +void email_turnoff(uchar visible, uchar real_stop); + +#endif diff --git a/engine/src/GameSrc/Headers/emailbit.h b/engine/src/GameSrc/Headers/emailbit.h new file mode 100644 index 0000000..6c113cf --- /dev/null +++ b/engine/src/GameSrc/Headers/emailbit.h @@ -0,0 +1,43 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __EMAILBIT_H +#define __EMAILBIT_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/emailbit.h $ + * $Revision: 1.2 $ + * $Author: tjs $ + * $Date: 1994/08/12 21:04:42 $ + * + */ + +// bit masks for the player_struct email inventory + +#define EMAIL_GOT 0x80u // we've got the email +#define EMAIL_READ 0x40u // we've read it. +#define EMAIL_SEQ 0x3Fu // sequence number of emails from this sender +#define EMAIL_SEQ_SHF 0u + +// flavors of data + +#define EMAIL_VER 0 +#define LOG_VER 1 +#define DATA_VER 2 + +#endif // __EMAILBIT_H diff --git a/engine/src/GameSrc/Headers/faceobj.h b/engine/src/GameSrc/Headers/faceobj.h new file mode 100644 index 0000000..48d1976 --- /dev/null +++ b/engine/src/GameSrc/Headers/faceobj.h @@ -0,0 +1,25 @@ +/* + +Copyright (C) 2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef FACEOBJ_H_ +#define FACEOBJ_H_ + +void facelet_obj(ObjID cobjid); + +#endif diff --git a/engine/src/GameSrc/Headers/faketime.h b/engine/src/GameSrc/Headers/faketime.h new file mode 100644 index 0000000..e9dfff2 --- /dev/null +++ b/engine/src/GameSrc/Headers/faketime.h @@ -0,0 +1,33 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __FAKETIME_H +#define __FAKETIME_H + +/* total bogosity that saves having to alter 50 source files */ +/* the non-time-faking parts of faketime + the new improved timer.h */ + +#define CIT_CYCLE 280 +#define CIT_FREQ (TMD_FREQ / CIT_CYCLE) + +#define APPROX_CIT_CYCLE_HZ 256u +#define APPROX_CIT_CYCLE_SHFT 8u + +extern volatile uint32_t *tmd_ticks; + +#endif /* !__FAKETIME_H */ diff --git a/engine/src/GameSrc/Headers/fatigue.h b/engine/src/GameSrc/Headers/fatigue.h new file mode 100644 index 0000000..dfd8aca --- /dev/null +++ b/engine/src/GameSrc/Headers/fatigue.h @@ -0,0 +1,37 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __FATIGUE_H +#define __FATIGUE_H + +/* + * $Source: n:/project/cit/src/inc/RCS/fatigue.h $ + * $Revision: 1.1 $ + * $Author: mahk $ + * $Date: 1994/05/05 16:57:50 $ + * + */ + +// Defines +#define MAX_FATIGUE 10000 + +// Prototypes + +// Globals + +#endif // __FATIGUE_H diff --git a/engine/src/GameSrc/Headers/fauxrint.h b/engine/src/GameSrc/Headers/fauxrint.h new file mode 100644 index 0000000..40fe6bd --- /dev/null +++ b/engine/src/GameSrc/Headers/fauxrint.h @@ -0,0 +1,110 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __FAUXRINT_H +#define __FAUXRINT_H + +#define CFG_REND_TEST "rend_test" + +#define PLAYER_HEIGHT 174 + +#define FR_CUR_OBJ_BASE 65 + +// axis setup for the zany extra math-o-tron 3d +//#define AXIS_ORDER X_AXIS,Z_AXIS,Y_AXIS +//#define ANGLE_ORDER ORDER_ZXY + +//#define AXIS_ORDER X_AXIS,-Y_AXIS,Z_AXIS +//#define AXIS_ORDER X_AXIS,Y_AXIS,Z_AXIS + +#define AXIS_ORDER AXIS_RIGHT, AXIS_DOWN, AXIS_IN + +#define ANGLE_ORDER ORDER_YXZ +#define pitch tx +#define bank tz +#define head ty +#define xaxis gX +#define yaxis gY +#define zaxis gZ + +// conversions +#define build_fix_angle(ang) ((65536 * (ang)) / 360) + +// masks for quadrant/octant free facing check +#define FMK_NW (1 << 0) +#define FMK_EW (1 << 1) +#define FMK_SW (1 << 2) +#define FMK_WW (1 << 3) +#define FMK_D1 (1 << 4) +#define FMK_D3 (1 << 5) +#define FMK_D5 (1 << 6) +#define FMK_D7 (1 << 7) + +#define MK_O_N (0) +#define MK_O_E (1) +#define MK_O_S (2) +#define MK_O_W (3) + +#define HG_NW (1 << 1) +#define HG_NE (1 << 2) +#define HG_SE (1 << 3) +#define HG_SW (1 << 0) + +#define RNG_MUL 4 +#define RNG_1 (0 * RNG_MUL) +#define RNG_2 (1 * RNG_MUL) +#define RNG_3 (2 * RNG_MUL) + +#define BOT_P 0 +#define TOP_P 2 +#define BOT_S 0 +#define TOP_S 1 + +#define LEFT_E 0 +#define RIGHT_E 1 + +/* note: 9 is unused + * + * 1 B 3 + * 0 2 + * 8 D A not detected yet + * 4 6 + * 5 C 7 + */ + +#define QUAD_N_BASE 0 +#define QUAD_S_BASE 4 +#define QUAD_A_BASE 8 +#define QUAD_X_OFF 2 +#define QUAD_D_OFF 1 +#define QUAD_CENTER 0xB + +#define FACE_FLOOR 0 +#define FACE_CEIL 1 +#define FACE_WALLS 2 + +#define REND_BT_N (1 << 0) +#define REND_BT_E (1 << 1) +#define REND_BT_S (1 << 2) +#define REND_BT_W (1 << 3) + +#define REND_BT_FLR (1 << 4) +#define REND_BT_CIE (1 << 5) +#define REND_BT_INT (1 << 6) + +#endif diff --git a/engine/src/GameSrc/Headers/fixtrmfd.h b/engine/src/GameSrc/Headers/fixtrmfd.h new file mode 100644 index 0000000..29f0515 --- /dev/null +++ b/engine/src/GameSrc/Headers/fixtrmfd.h @@ -0,0 +1,29 @@ +/* + +Copyright (C) 2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef FIXTRMFD_H +#define FIXTRMFD_H + +// ---------- +// PROTOTYPES +// ---------- +void mfd_fixture_expose(MFD *mfd, ubyte control); +uchar mfd_fixture_handler(MFD *m, uiEvent *e); + +#endif diff --git a/engine/src/GameSrc/Headers/fr3d.h b/engine/src/GameSrc/Headers/fr3d.h new file mode 100644 index 0000000..6101469 --- /dev/null +++ b/engine/src/GameSrc/Headers/fr3d.h @@ -0,0 +1,53 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/cit/src/inc/RCS/fr3d.h $ + * $Revision: 1.2 $ + * $Author: dc $ + * $Date: 1994/06/01 22:50:23 $ + * + * Citadel Renderer + * setup and stuff for point counts, initialization, and so on + */ + +//#define FR_PT_CNT 1024 +#define FR_PT_CNT 256 +#define FR_DEF_FOV 80 +#define FR_DEF_AXIS 'X' + +#define AXIS_ORDER AXIS_RIGHT, AXIS_DOWN, AXIS_IN + +#define ANGLE_ORDER ORDER_YXZ +#define pitch tx +#define bank tz +#define head ty +#define xaxis gX +#define yaxis gY +#define zaxis gZ + +// conversions +// does anyone ever call this... oh yea, for matts stuff... +// ick +#define build_fix_angle(ang) ((65536 * (ang)) / 360) + +// coordinate merge - have to inc frcamera.h to use these +#define coor(val) (fr_camera_last[val]) +#define ang(val) (fr_camera_last[val]) +#define last_coor(val) (fr_camera_last[val]) +#define last_ang(val) (fr_camera_last[val]) diff --git a/engine/src/GameSrc/Headers/framer8.h b/engine/src/GameSrc/Headers/framer8.h new file mode 100644 index 0000000..ba666d3 --- /dev/null +++ b/engine/src/GameSrc/Headers/framer8.h @@ -0,0 +1,33 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __FRAMER8_H +#define __FRAMER8_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/framer8.h $ + * $Revision: 1.1 $ + * $Author: mahk $ + * $Date: 1994/09/06 00:11:53 $ + * + */ + +#define MIN_FRAME_RATE 6 +#define MAX_FRAME_RATE 70 + +#endif // __FRAMER8_H diff --git a/engine/src/GameSrc/Headers/frcamera.h b/engine/src/GameSrc/Headers/frcamera.h new file mode 100644 index 0000000..52a039c --- /dev/null +++ b/engine/src/GameSrc/Headers/frcamera.h @@ -0,0 +1,136 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * FrCamera.h + * + * $Source: n:/project/cit/src/inc/RCS/frcamera.h $ + * $Revision: 1.6 $ + * $Author: dc $ + * $Date: 1994/04/10 05:16:16 $ + * + * Citadel Renderer + * camera position/modification/creation system + * + * $Log: frcamera.h $ + * Revision 1.6 1994/04/10 05:16:16 dc + * support for cyberman, vfx1, other 6d control structure, inc. HEAD_H + * + * Revision 1.5 1994/01/31 05:37:27 dc + * yea yea yea + * + * Revision 1.4 1994/01/02 17:16:19 dc + * Initial revision + * + * Revision 1.3 1993/09/17 17:00:31 mahk + * Added 360 support + * + * Revision 1.2 1993/09/16 23:55:06 dc + * support for fr internal camera view changing + * + * Revision 1.1 1993/09/05 20:58:57 dc + * Initial revision + * + */ + +#ifndef __FRCAMERA_H +#define __FRCAMERA_H + +#define CAM_COOR_CNT 6 +#define CAM_ARGS_CNT 3 + +typedef struct { + uchar type; // camera type code + ushort obj_id; // current obj_id, or null if abs used + fix coor[CAM_COOR_CNT]; // current abs pos + fix args[CAM_ARGS_CNT]; // args interpreted based on type +} cams; + +uchar fr_camera_create(cams *cam, int camtype, ushort oid, fix *coor, fix *args); +uchar fr_camera_modtype(cams *cam, uchar type_on, uchar type_off); +int fr_camera_update(cams *cam, uintptr_t arg1, int whicharg, uintptr_t arg2); +void fr_camera_slewone(cams *cam, int which, int how); +void fr_camera_setone(cams *cam, int which, int newCam); +fix *fr_camera_getpos(cams *cam); +void fr_camera_slewcam(cams *cam, int which, int how); +cams *fr_camera_getdef(void); +void fr_camera_setdef(cams *cam); +void fr_camera_getobjloc(int oid, fix *store); + +extern fix cam_slew_scale[CAM_COOR_CNT]; +extern fix fr_camera_last[CAM_COOR_CNT]; + +// cameras are now 8 bit flags +// 1 mods, 1 flt, 2 ang, 2 off, 1 obj +// +// + +#define CAMOBJ_S 0u +#define CAMOBJ_Z 1u +#define CAMOFF_S (CAMOBJ_S + CAMOBJ_Z) +#define CAMOFF_Z 2u +#define CAMANG_S (CAMOFF_S + CAMOFF_Z) +#define CAMANG_Z 2u +#define CAMFLT_S (CAMANG_S + CAMANG_Z) +#define CAMFLT_Z 1u +#define CAMMOD_S (CAMFLT_S + CAMFLT_Z) +#define CAMMOD_Z 1u + +//#define MakeCambit(x) CAMBIT_##x## (((1<. + +*/ + +#ifndef CURSORS_H +#define CURSORS_H + +uchar cursor_get_callback(LGRegion* reg, LGRect* rect, void* vp); + +errtype ui_init_cursor_stack(uiSlab* slab, LGCursor* default_cursor); +errtype ui_init_cursors(void); +errtype ui_shutdown_cursors(void); +uchar ui_set_current_cursor(LGPoint pos); +void ui_update_cursor(LGPoint pos); + +#endif diff --git a/engine/src/GameSrc/Headers/fredge.h b/engine/src/GameSrc/Headers/fredge.h new file mode 100644 index 0000000..a891101 --- /dev/null +++ b/engine/src/GameSrc/Headers/fredge.h @@ -0,0 +1,51 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/cit/src/inc/RCS/fredge.h $ + * $Revision: 1.2 $ + * $Author: dc $ + * $Date: 1994/06/13 18:01:56 $ + */ + +// noway, no how, no nothing, null space +#define MEDGE_NO_TILE 0x0000 + +// a full wall, end of the line, no further can you go +#define MEDGE_NO_EGRESS 0x0001 +#define MEDGE_FULL_WALL 0x0001 + +// no floor height difference +#define MEDGE_FLAT_CASE 0x0002 + +// various partial things +#define MEDGE_SMALL_STEP 0x0004 +#define MEDGE_LARGE_STEP 0x0008 +#define MEDGE_CLIFF_THING 0x0010 + +// secret bug cases for playtest +#define MEDGE_BOWTIE_CASE 0x1000 +#define MEDGE_INTERNAL_CROSSING 0x2000 + +int get_edge_code(void *mp, int edge); +char *map_get_edge(void *mp, int edge, int ceil_p); + +#ifndef __FRTERR_SRC +// left val, right val, "sum" val +extern char edge_vals[3]; +#endif diff --git a/engine/src/GameSrc/Headers/frflags.h b/engine/src/GameSrc/Headers/frflags.h new file mode 100644 index 0000000..42103b7 --- /dev/null +++ b/engine/src/GameSrc/Headers/frflags.h @@ -0,0 +1,136 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// flags +#define FR_HACKCAM_SHFT 31u +#define FR_HACKCAM_MASK (0x1u << FR_HACKCAM_SHFT) +#define FR_PALETTE_SHFT 28u // which palette to output through +#define FR_PALETTE_MASK (0x7u << FR_PALETTE_SHFT) +#define FR_WINDOWD_SHFT 24u // what window dressing to use +#define FR_WINDOWD_MASK (0xfu << FR_WINDOWD_SHFT) +#define FR_CURFREE_SHFT 20u +#define FR_CURFREE_MASK (0xfu << FR_CURFREE_SHFT) +#define FR_NOTRANS_SHFT 19u // +#define FR_NOTRANS_MASK (0x1u << FR_NOTRANS_SHFT) +#define FR_OVERLAY_SHFT 16u // what is being overlayed/ie. hand art or pings +#define FR_OVERLAY_MASK (0x7u << FR_OVERLAY_SHFT) +#define FR_SOLIDFR_SHFT 13u // are we just a solid this frame +#define FR_SOLIDFR_MASK (0x7u << FR_SOLIDFR_SHFT) +#define FR_DOHFLIP_SHFT 12u +#define FR_DOHFLIP_MASK (0x1u << FR_DOHFLIP_SHFT) +#define FR_OWNBITS_SHFT 11u // your canvas and all came from you +#define FR_OWNBITS_MASK (0x1u << FR_OWNBITS_SHFT) +#define FR_TITLEBR_SHFT 9u // should we be showing a title +#define FR_TITLEBR_MASK (0x3u << FR_TITLEBR_SHFT) +#define FR_SHOWALL_SHFT 8u +#define FR_SHOWALL_MASK (0x1u << FR_SHOWALL_SHFT) +#define FR_DOUBLEB_SHFT 7u // are we double buffering +#define FR_DOUBLEB_MASK (0x1u << FR_DOUBLEB_SHFT) +#define FR_CURVIEW_SHFT 5u +#define FR_CURVIEW_MASK (0x3u << FR_CURVIEW_SHFT) +#define FR_PICKUPM_SHFT 4u +#define FR_PICKUPM_MASK (0x1u << FR_PICKUPM_SHFT) +#define FR_NORENDR_SHFT 3u +#define FR_NORENDR_MASK (0x1u << FR_NORENDR_SHFT) +#define FR_SFX_SHFT 0u +#define FR_SFX_MASK (0x7u << FR_SFX_SHFT) + +// yet another way to have multiple views +#define FR_CURVIEW_STRT (0u << FR_CURVIEW_SHFT) +#define FR_CURVIEW_LEFT (1u << FR_CURVIEW_SHFT) +#define FR_CURVIEW_BACK (2u << FR_CURVIEW_SHFT) +#define FR_CURVIEW_RGHT (3u << FR_CURVIEW_SHFT) + +// Hey, some solid stuff +#define FR_SOLIDFR_NORMAL (0u << FR_SOLIDFR_SHFT) +#define FR_SOLIDFR_STATIC (1u << FR_SOLIDFR_SHFT) +#define FR_SOLIDFR_SLDCLR (2u << FR_SOLIDFR_SHFT) +#define FR_SOLIDFR_SLDKEEP (3u << FR_SOLIDFR_SHFT) + +#ifndef __GAMEREND_SRC +extern uchar fr_solidfr_color; +#else +uchar fr_solidfr_color; +#endif + +// Cool warping effects on the screen, and other draw hacks +#define FR_SFX_NONE (0u << FR_SFX_SHFT) +#define FR_SFX_VHOLD (1u << FR_SFX_SHFT) +#define FR_SFX_HHOLD (2u << FR_SFX_SHFT) +#define FR_SFX_STATIC (3u << FR_SFX_SHFT) +#define FR_SFX_SHAKE (4u << FR_SFX_SHFT) +#define FR_SFX_SHIELD (5u << FR_SFX_SHFT) +#define FR_SFX_TELEPORT (6u << FR_SFX_SHFT) + +// Overlays -- currently this is only SHODAN +#define FR_OVERLAY_NONE (0u << FR_OVERLAY_SHFT) +#define FR_OVERLAY_SHODAN (1u << FR_OVERLAY_SHFT) + +// Palette setup +#define FR_PALETTE_BW (1u << FR_PALETTE_SHFT) +#define FR_PALETTE_GREEN (2u << FR_PALETTE_SHFT) +#define FR_PALETTE_ORANGE (3u << FR_PALETTE_SHFT) + +#define FR_PALETTE_IR (FR_PALETTE_BW | FR_MONOCHR_MASK) +#define FR_PALETTE_LOWTECH (FR_PALETTE_GREEN | FR_MONOCHR_MASK) + +// Hack cameras +#define FR_HACKCAM_FLAG (1u << FR_HACKCAM_SHFT) + +#define _fr_get_pal_idx(f) ((f & FR_PALETTE_MASK) >> FR_PALETTE_SHFT) + +#define FR_MONOPAL_MASK (FR_PALETTE_MASK | FR_MONOCHR_MASK) + +// it was unintentional, when i spit in your beer +// im overinfluenced, by movies +#define FR_DBG_DBG_ALL (0xffffffff) + +#define FR_DBG_MONO_MAP (1 << 1) +#define FR_DBG_MONO_LIST (1 << 2) +#define FR_DBG_NO_MATH (1 << 3) +#define FR_DBG_NO_REND (1 << 4) +#define FR_DBG_NO_2D (1 << 5) +#define FR_DBG_PT_SPEW (1 << 6) +#define FR_DBG_SHOW_BASE (1 << 7) +#define FR_DBG_SANITY (1 << 8) +#define FR_DBG_NO_RTF (1 << 9) +#define FR_DBG_CURSOR (1 << 10) +#define FR_DBG_ANAL_CHK (1 << 11) +#define FR_DBG_ALTCAM (1 << 12) +#define FR_DBG_VECSPEW (1 << 13) +#define FR_DBG_VECTRACK (1 << 14) +#define FR_DBG_POLY_MODE (1 << 15) +#define FR_DBG_NO_SUB_CLIP (1 << 16) +#define FR_DBG_STATS (1 << 17) +#define FR_DBG_SPAN_PARSE (1 << 18) +#define FR_DBG_NO_CONE (1 << 19) +#define FR_DBG_NO_TILE (1 << 20) +#define FR_DBG_OBJ_TALK (1 << 21) +#define FR_DBG_LOC_TMAPS (1 << 22) +#define FR_DBG_SHOW_PICKUP (1 << 23) +#define FR_DBG_LIST_TILES (1 << 24) +#define FR_DBG_NEW_PTS (1 << 25) + +//#define FR_DBG_STATIC_FLG (FR_DBG_SANITY|FR_DBG_SHOW_PICKUP|FR_DBG_VECSPEW|FR_DBG_VECTRACK) + +#ifndef FR_DBG_STATIC_FLG +#define FR_DBG_STATIC_FLG (0) +#endif + +// actual behavior controls, cause heck, why not +#define CLEAR_AS_WE_GO diff --git a/engine/src/GameSrc/Headers/frintern.h b/engine/src/GameSrc/Headers/frintern.h new file mode 100644 index 0000000..a78aa1a --- /dev/null +++ b/engine/src/GameSrc/Headers/frintern.h @@ -0,0 +1,139 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/cit/src/inc/RCS/frintern.h $ + * $Revision: 1.3 $ + * $Author: dc $ + * $Date: 1994/01/16 05:22:06 $ + * + * Citadel Renderer + * internal prototypes for the renderer + * + * $Log: frintern.h $ + * Revision 1.3 1994/01/16 05:22:06 dc + * facelet parse obj + * + * Revision 1.2 1994/01/12 22:04:22 dc + * facelet code hacking + * + * Revision 1.1 1994/01/02 17:16:27 dc + * Initial revision + * + */ +#ifndef __FRINTERN_H +#define __FRINTERN_H + +#include "frcamera.h" +#include "frprotox.h" +#include "frshipm.h" +#include "frworld.h" + +//======== From frsetup.c +// setup current view, send it out +int fr_prepare_view(frc *view); +int fr_start_view(void); +int fr_send_view(void); + +#ifdef __FRTYPES_H +extern fauxrend_context *_fr, *_sr; +#endif // only know about context itself if you already include types +extern uint _fr_curflags, _fr_glob_flags; +extern uchar *_fr_clut_list[4]; + +//======== From frpipe.c +// pipe setup and control +int fr_pipe_resize(int x, int y, int z, void *mptr); +int fr_pipe_start(int rad); +int fr_pipe_go(void); +int fr_pipe_end(void); +int fr_pipe_freemem(void); + +extern int fr_map_x, fr_map_y, fr_map_z; +extern int _fr_x_cen, _fr_y_cen; + +//======== from frpts.c +int fr_pts_frame_start(void); +int fr_pts_resize(int x, int y); +int fr_pts_freemem(void); +int fr_pts_update(int y, int lx, int rx); +int fr_pts_setup(int pt_code); // must call before update + +#ifdef __3D_H +extern g3s_phandle *_fr_ptbase, *_fr_ptnext; +#endif // __3D_H + +//======== From frclip.c +// these all set and modify global clipping arrays +// so, we cannot do these in parallel +int fr_clip_resize(int x, int y); +int fr_clip_frame_start(void); +int fr_clip_frame_end(void); +int fr_clip_cone(void); +int fr_clip_tile(void); +int fr_clip_freemem(void); + +//======== From frtables.c +// setup and integrity test various renderer data tables +int fr_tables_build(void); + +//======== From frobj.c +void render_parse_obj(void); +void facelet_parse_obj(void); + +//======== From frutil.c +#define FR_CUR_OBJ_BASE 65 +extern uchar fr_cur_obj_col; +extern ushort fr_col_to_obj[256]; + +//======== From frterr.c +void fr_draw_tile(void); +void fr_terr_frame_start(void); +void fr_terr_frame_end(void); +void _fr_facelet_init(void); +#ifdef __3D_H +int _fr_do_light(g3s_phandle work, int hgt_code); +#endif + +#ifndef __FRTERR_SRC +// map/world data layout +#ifdef __3D_H +extern sfix _fr_sfuv_list[]; +#endif +extern fix _fr_fhgt_step; +extern fix _fr_fhgt_list[]; +extern fix slope_norm[][3]; +extern int wall_adds[], csp_trans_add[]; +// pipeline controller +extern int _fdt_x, _fdt_y, _fdt_mask, _fdt_dist, _fdt_pbase; +#ifdef __MAP_H +extern MapElem *_fdt_mptr; +#endif +#endif + +#endif // __FRINTERN_H + +// she flew low above the highway +// and i saw the wind +// throwing back her barbie doll hair +// robin flies again, robin flies again + +// and in a kitchen in kentucky +// she thinks she's peter pan +// and in the bottom of her concrete basement +// robin flies again, robin flies again diff --git a/engine/src/GameSrc/Headers/froslew.h b/engine/src/GameSrc/Headers/froslew.h new file mode 100644 index 0000000..d17dbb8 --- /dev/null +++ b/engine/src/GameSrc/Headers/froslew.h @@ -0,0 +1,60 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * FrOslew.h + * + * $Source: n:/project/cit/src/inc/RCS/froslew.h $ + * $Revision: 1.2 $ + * $Author: dc $ + * $Date: 1994/01/02 17:16:29 $ + * + * Citadel Renderer + * object slew system controllers/prototypes/vars + * + * $Log: froslew.h $ + * Revision 1.2 1994/01/02 17:16:29 dc + * Initial revision + * + * Revision 1.1 1993/09/05 20:59:07 dc + * Initial revision + * + */ + +#ifndef __FROSLEW_H +#define __FROSLEW_H + +#include "frcamera.h" +#ifndef __RENDTEST__ +#include "objects.h" +#else +//¥¥#include +#endif + +int32_t *fr_objslew_obj_to_fix(int32_t *flist, Obj *cobj, int count); +Obj *fr_objslew_fix_to_obj(int32_t *flist, Obj *cobj, int count); +uchar fr_objslew_tele_to(Obj *cobj, int x, int y); +uchar fr_objslew_allowed(Obj *cobj, int32_t *eye); +uchar fr_objslew_moveone(Obj *objp, ObjID objnum, int which, int how, uchar conform); +uchar fr_objslew_go_real_height(Obj *cobj, int32_t *eye); +uchar fr_objslew_setone(int which, int l_new); + +extern int32_t eye_mods[3]; +extern uchar slew_conform_to_terrain, slew_full_3d; + +#endif diff --git a/engine/src/GameSrc/Headers/frparams.h b/engine/src/GameSrc/Headers/frparams.h new file mode 100644 index 0000000..779b574 --- /dev/null +++ b/engine/src/GameSrc/Headers/frparams.h @@ -0,0 +1,113 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/inc/RCS/frparams.h $ + * $Revision: 1.3 $ + * $Author: dc $ + * $Date: 1994/07/20 23:05:51 $ + * + * Citadel Renderer + * global parameters structures, setting, and defines + * + * $Log: frparams.h $ + * Revision 1.3 1994/07/20 23:05:51 dc + * odrop + * + * Revision 1.2 1994/01/30 01:54:53 dc + * global lighting + * + * Revision 1.1 1994/01/02 17:16:32 dc + * Initial revision + * + */ + + +#define TM_SIZE_CNT 3 /* # of different tmap sizes */ + +typedef struct { + struct { + uchar main : 1; /* any textures? */ + uchar wall : 1; /* if any, any walls */ + uchar floor : 1; /* any floor */ + uchar ceiling : 1; /* any ceiling */ + uchar cyber : 1; /* cyberspace instead */ + uchar cyber_full : 1; /* full cursors */ + } faces; + struct { + uchar highlights : 1; /* tilemap highlights of cones */ + uchar tilecursor : 1; /* show the tilemap cursor in 3d */ + uchar nodraw : 1; /* dont actually render FB */ + } features; + struct { +#ifdef C_WERE_SUPER_COOL + uchar lighting : 1; /* any lighting at all? */ + uchar terrain : 1; /* terrain light values checked and used? */ + uchar camera : 1; /* camera light values used */ + uchar normal_chk : 1; /* use normal when computing wall lighting */ +#else + uchar flags; /* 0,0,0,0,any,terr,cam,normal */ +#endif + int normal_shf; /* what to shift normal by when adding to dist */ + uchar rad[2]; /* inner, outer lighting radius */ + uchar base[2]; /* inner, outer light values */ + fix slope; /* slope and yintercept */ + fix yint; /* of lighting line */ + short global_mod; /* change to all lighting */ + } lighting; + struct { + uchar qscale_obj; /* radius at which to qscale objects */ + uchar qscale_crit; /* radius at which to qscale critters */ + uchar qscale_texture; /* radius at which to qscale textures */ + uchar detail; /* detail setting */ + uchar clear_color; /* color to clear background too */ + uchar drop_rad[TM_SIZE_CNT]; /* radii to switch to lower res tmaps */ + uchar odrop_rad[TM_SIZE_CNT]; /* original (base) drop radii to switch to lower res tmaps */ + uchar radius; /* maximal view radius */ + uchar show_all : 1; /* render whole world - no clip */ + uchar cone_only : 1; /* no 2 1/2D clip, cone only */ + } view; + struct { + long last_chk_time; + long last_frame_cnt; + long tot_frame_cnt; + long last_frame_len; + } time; +} fauxrend_parameters; + +extern fauxrend_parameters _frp; +#define get_frp() (_frp) + +#define LIGHT_BITS_MASK 0xfu +#define LIGHT_BITS_ANY 0x8u +#define LIGHT_BITS_TERR 0x4u +#define LIGHT_BITS_CAM 0x2u +#define LIGHT_BITS_NORM 0x1u +#define LIGHT_BITS_HOW (LIGHT_BITS_NORM | LIGHT_BITS_CAM | LIGHT_BITS_TERR) + +#define _frp_light_bits_any() (_frp.lighting.flags & LIGHT_BITS_ANY) +#define _frp_light_bits_how() (_frp.lighting.flags & LIGHT_BITS_HOW) +#define _frp_light_bits_cam() (_frp.lighting.flags & LIGHT_BITS_CAM) +#define _frp_light_bits_norm() (_frp.lighting.flags & LIGHT_BITS_NORM) +#define _frp_light_bits_terr() (_frp.lighting.flags & LIGHT_BITS_TERR) + +#define _frp_light_bits_set(m) (_frp.lighting.flags |= m) +#define _frp_light_bits_clear(m) (_frp.lighting.flags &= ~m) +#define _frp_light_bits_tog(m) \ + if (_frp.lighting.flags | m) \ + _frp_light_bits_clear(m) else _frp_light_bits_set(m) diff --git a/engine/src/GameSrc/Headers/frprotox.h b/engine/src/GameSrc/Headers/frprotox.h new file mode 100644 index 0000000..a888830 --- /dev/null +++ b/engine/src/GameSrc/Headers/frprotox.h @@ -0,0 +1,131 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __FRPROTOX_H +#define __FRPROTOX_H +/* + * $Source: r:/prj/cit/src/inc/RCS/frprotox.h $ + * $Revision: 1.11 $ + * $Author: xemu $ + * $Date: 1994/08/05 03:06:45 $ + * + * Citadel Renderer + * global external prototypes for the renderer + * + * $Log: frprotox.h $ + * Revision 1.11 1994/08/05 03:06:45 xemu + * look, fr_get_at with transparency + * + * Revision 1.10 1994/04/23 09:56:24 xemu + * new params1 + * + * Revision 1.9 1994/04/14 15:00:33 kevin + * New detail stuff. + * + * Revision 1.8 1994/03/13 17:18:33 dc + * more fields for obj_block + * + * Revision 1.7 1994/03/03 12:18:27 dc + * place view takes a canvas now + * + * Revision 1.6 1994/02/13 05:48:00 dc + * rend_start + * + * Revision 1.5 1994/01/02 17:16:34 dc + * Initial revision + * + */ + +#ifndef __FRTYPESX_H +typedef void frc; +typedef void fmp; +#endif + +//======== Basic truths +#define FR_OK (1) +#define FR_BAD_VIEW (-1) +#define FR_NOMEM (-2) +#define FR_NULL_PTR (-3) +#define FR_NO_NEED (-4) + +//======== Random prettiness +#define FR_NOCAM ((void *)(-1)) +#define FR_DEFCAM (NULL) +#define FR_DEFVIEW (NULL) +#define FR_NEWVIEW (NULL) + +//======== From frsetup.c +// global initialization +void fr_startup(void); +void fr_shutdown(void); +void fr_closedown(void); + +// view control/setup +frc *fr_place_view(frc *view, void *cam, void *canvas, int pflags, char axis, int fov, int xc, int yc, int wid, + int hgt); +void fr_use_global_detail(frc *view); +int fr_view_resize(frc *view, int wid, int hgt); +int fr_view_full(frc *view, int wid, int hgt); +int fr_mod_size(frc *view, int xc, int yc, int wid, int hgt); +int fr_mod_cams(frc *view, void *cam, int mod_fac); +int fr_context_mod_flag(frc *view, int pflags_on, int pflags_off); // remember to set flags_off for things you turn on +int fr_global_mod_flag(int flags_on, int flags_off); +void *fr_get_canvas(frc *view); // really returns a grs_canvas, but no want 2d.h +int fr_set_view(frc *view); +int fr_free_view(frc *view); +void fr_set_cluts(uchar *base, uchar *bwclut, uchar *greenclut, uchar *amberclut); +int fr_set_callbacks(frc *view, int (*draw)(void *dstc, void *dstbm, int x, int y, int flg), + void (*horizon)(void *dstbm, int flg), void (*render)(void *dstbm, int flg)); +int fr_set_global_callbacks(int (*draw)(void *dstc, void *dstbm, int x, int y, int flg), + void (*horizon)(void *dstbm, int flg), void (*render)(void *dstbm, int flg)); + +//======== From frcompil.c +void fr_compile_rect(fmp *fm, int llx, int lly, int ulx, int uly, uchar seen_bits); +void fr_compile_restart(fmp *fm); + +//======== From frmain.c +int fr_rend(frc *view); +ushort fr_get_at(frc *view, int x, int y, uchar transp); + +//======== From frutil.c +char *fr_get_frame_rate(void); +ushort fr_get_again(frc *fr, int x, int y); + +//======== Externals to provide, initialized to dumb things +extern int _fr_default_detail; +extern int _fr_global_detail; +extern void (*fr_mouse_hide)(void), (*fr_mouse_show)(void); +extern int (*fr_get_idx)(void); +extern uchar (*fr_obj_block)(void *mptr, uchar *_sclip, int *loc); +extern void (*fr_clip_start)(uchar headnorth); +extern void (*fr_rend_start)(void); +#ifdef __2D_H +extern grs_bitmap *(*fr_get_tmap)(void); +#endif + +// default versions of above, defined in frsetup and set there +void fr_default_mouse(void); +int fr_default_idx(void), fr_pickup_idx(void); +uchar fr_default_block(void *mptr, uchar *_sclip, int *loc); +void fr_default_clip_start(uchar headnorth); +void fr_default_rend_start(void); +#ifdef __2D_H +grs_bitmap *fr_default_tmap(void); +#endif + +#endif // __FRPROTOX_H diff --git a/engine/src/GameSrc/Headers/frquad.h b/engine/src/GameSrc/Headers/frquad.h new file mode 100644 index 0000000..df44d45 --- /dev/null +++ b/engine/src/GameSrc/Headers/frquad.h @@ -0,0 +1,68 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/inc/RCS/frquad.h $ + * $Revision: 1.2 $ + * $Author: dc $ + * $Date: 1994/09/05 06:43:05 $ + * + * Citadel Renderer + * quadrant defines and layout, flags, etc... + */ + +/* note: 9 is unused + * + * 1 B 3 + * 0 2 + * 8 D A + * 4 6 + * 5 C 7 + */ + +#define QUAD_N_BASE 0 +#define QUAD_S_BASE 4 +#define QUAD_A_BASE 8 +#define QUAD_X_OFF 2 +#define QUAD_D_OFF 1 +#define QUAD_CENTER 0xB + +/* + * 1 0 2 + * B 4 + * 9 C 3 + * A 5 + * 8 6 7 + */ + +#define QUAD2_BASE 0 +#define QUAD2_RIGHT_FORK 1 +#define QUAD2_LEFT_FORK 2 +#define QUAD2_DELTA 3 +#define QUAD2_CENTER 0xC + +// masks for quadrant/octant free facing check +#define FMK_NW (1 << 0) +#define FMK_EW (1 << 1) +#define FMK_SW (1 << 2) +#define FMK_WW (1 << 3) + +#define FMK_INT_NW (1 << (4 + 0)) +#define FMK_INT_EW (1 << (4 + 1)) +#define FMK_INT_SW (1 << (4 + 2)) +#define FMK_INT_WW (1 << (4 + 3)) diff --git a/engine/src/GameSrc/Headers/frscreen.h b/engine/src/GameSrc/Headers/frscreen.h new file mode 100644 index 0000000..66dfa26 --- /dev/null +++ b/engine/src/GameSrc/Headers/frscreen.h @@ -0,0 +1,25 @@ +/* + +Copyright (C) 2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef SCREEN_H +#define SCREEN_H + +errtype load_misc_cursors(void); + +#endif diff --git a/engine/src/GameSrc/Headers/frshipm.h b/engine/src/GameSrc/Headers/frshipm.h new file mode 100644 index 0000000..1ed3374 --- /dev/null +++ b/engine/src/GameSrc/Headers/frshipm.h @@ -0,0 +1,54 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __FRSHIPM_H +#define __FRSHIPM_H +/* + * $Source: n:/project/cit/src/inc/RCS/frshipm.h $ + * $Revision: 1.1 $ + * $Author: dc $ + * $Date: 1994/01/02 17:16:41 $ + * + * Citadel renderer + * ship vs. not debug spew stuff + */ + + +// have to learn about fixing fr_ret and all to be null + +#define _chkNull(ptr) +#define _chkNullcast(pt, cast) +#define _fr_top(vr) \ + if ((vr) == NULL) \ + _fr = _sr; \ + else \ + _fr = (fauxrend_context *)(vr) +#define _fr_top_cast(vr, cast) _fr_top(vr) +#define _fr_ret return FR_OK +#define _fr_ret_val(v) return v +#define _fr_dbg(exp) +#define _fr_dbgchk(flg, exp) +#define _fr_sdbg(flg, exp) +#define _fr_ndbg(flg, exp) exp +#define _fr_defdbg(flg) 0 +#define static static +#define _fr_dbgflg_add(flg) +#define _fr_dbgflg_tog(flg) +#define _fr_dbgflg_chk(flg) (0) + +#endif // __FRSHIPM_H diff --git a/engine/src/GameSrc/Headers/frspans.h b/engine/src/GameSrc/Headers/frspans.h new file mode 100644 index 0000000..e9c3e8e --- /dev/null +++ b/engine/src/GameSrc/Headers/frspans.h @@ -0,0 +1,53 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/cit/src/inc/RCS/frspans.h $ + * $Revision: 1.1 $ + * $Author: dc $ + * $Date: 1994/01/02 17:16:44 $ + * + * Citadel Renderer + * clipped and coned spans + */ + +// +extern uchar *x_span_lists; +extern uchar *cone_span_list; + +void cone_span_set(int y, int lx, int rx); +void store_x_span(int y, int lx, int rx); + +// maximum spans on a database scan line +#define MAX_SPANS 7 +#define SPAN_MEM (2 * MAX_SPANS + 2) +#define SPAN_SHIFT 4 // 8 spans, each 2 entries, 16 values +#define SPAN_LEFT 0 +#define SPAN_RIGHT 1 + +#define span_count(y) (x_span_lists[((y) << SPAN_SHIFT) + (MAX_SPANS << 1)]) +#define span_left(y, s) (x_span_lists[((y) << SPAN_SHIFT) + ((s) << 1) + SPAN_LEFT]) +#define span_right(y, s) (x_span_lists[((y) << SPAN_SHIFT) + ((s) << 1) + SPAN_RIGHT]) + +#define cone_span_left(y) (cone_span_list[(y) << 1]) +#define cone_span_right(y) (cone_span_list[((y) << 1) + 1]) + +#ifdef CLIPPER_CRACK_CHECK +#define SPAN_NOCRACK 0xff +#define span_crack(y) (x_span_lists[((y) << SPAN_SHIFT) + (MAX_SPANS << 1) + 1]) +#endif diff --git a/engine/src/GameSrc/Headers/frsubclp.h b/engine/src/GameSrc/Headers/frsubclp.h new file mode 100644 index 0000000..6942e4d --- /dev/null +++ b/engine/src/GameSrc/Headers/frsubclp.h @@ -0,0 +1,81 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/cit/src/inc/RCS/frsubclp.h $ + * $Revision: 1.1 $ + * $Author: dc $ + * $Date: 1994/01/02 17:16:45 $ + * + * Citadel renderer + * definition and accessors for subclip data + */ + +// each vec is hi 14 bits of intersection (low word of a fix & ~3) and low 2 bit face code +// so to test if exists & the longword with 0x00030003 and if = 0x00030003 then it means nil + +// subclip cache size +#define FR_SC_FULL 0xff +#define NUM_SUBCLIP (FR_SC_FULL + 1) +#define FR_SC_NONE 0 +#define FR_SC_BASE 1 + +// subclip region id's +#define SC_LEFT_BASE 0 +#define SC_LEFT_END 1 +#define SC_RIGHT_BASE 2 +#define SC_RIGHT_END 3 +#define SC_VEC_COUNT 4 + +#ifndef __FRCLIP_SRC +extern ushort sc_reg[NUM_SUBCLIP][SC_VEC_COUNT]; +extern ushort *cur_sc_ptr; +extern uint cur_sc_reg; +#endif + +#define SC_FACE_MASK 0x0003 +#define SC_INTR_MASK (~SC_FACE_MASK) //#define SC_INTR_MASK 0xFFFC +#define SC_NILL_VECT ((SC_FACE_MASK << 16) | SC_FACE_MASK) + +#define FIRST_SC_REG (1) +#define SUBCLIP_OUT_OF_CONE (0xFF) +#define SUBCLIP_FULL_TILE (0x00) + +// if the compiler doesnt shift here (or even better, scalar) i will kill it +#define sc_get_fullv(sc, vn) (sc_reg[sc][vn]) +#define sc_get_facev(sc, vn) (sc_reg[sc][vn] & SC_INTR_MASK) +#define sc_get_intrv(sc, vn) (sc_reg[sc][vn] & SC_FACE_MASK) +#define sc_set_partv(sc, vn, v, f) (sc_reg[sc][vn] = (v) | (f)) +#define sc_set_fullv(sc, vn, v) (sc_reg[sc][vn] = v) +#define sc_set_facev(sc, vn, f) (sc_reg[sc][vn] = (sc_reg[sc][vn] & SC_INTR_MASK) | (f)) +#define sc_set_intrv(sc, vn, i) (sc_reg[sc][vn] = (sc_reg[sc][vn] & SC_FACE_MASK) | (i & SC_INTR_MASK)) +// wacky read it as a long, vn is the base, ie. 0 or 2 +#define sc_set_novec(sc, vn) ((*((long *)&sc_reg[sc][vn])) = SC_NILL_VECT) +#define sc_chk_novec(sc, vn) ((*((long *)&sc_reg[sc][vn])) == SC_NILL_VECT) + +#define sc_reset() \ + { \ + cur_sc_ptr = &sc_reg[FIRST_SC_REG][0]; \ + cur_sc_reg = FIRST_SC_REG; \ + } +#define sc_nxtvec() \ + { \ + cur_sc_ptr += SC_VEC_COUNT; \ + cur_sc_reg++; \ + } +#define sc_region() cur_sc_reg diff --git a/engine/src/GameSrc/Headers/frtables.h b/engine/src/GameSrc/Headers/frtables.h new file mode 100644 index 0000000..c804906 --- /dev/null +++ b/engine/src/GameSrc/Headers/frtables.h @@ -0,0 +1,280 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * FrTables.h + * + * $Source: n:/project/cit/src/inc/RCS/frtables.h $ + * $Revision: 1.2 $ + * $Author: dc $ + * $Date: 1994/01/12 22:04:14 $ + * + * Citadel Renderer + * externs of the tables for the tile definitions/quadrant codes + * + * $Log: frtables.h $ + * Revision 1.2 1994/01/12 22:04:14 dc + * facelet code hacking + * + * Revision 1.1 1994/01/02 17:16:49 dc + * Initial revision + * + */ + +// data structures + +#define FRTILETYPES 51 + +//======== Points + +// layout of points +// +// 4 D E F 8 +// 3 g h 7 +// b d +// +// 2 6 +// +// a c +// 1 e f 5 +// 0 9 A B C +// +// a-h are really 16-23 for octagonal setups +// each point has 2bits of zmod and then the code as above +// a table is used to take the pretransformed base points into the above +// in uv table note _uv1 is full, a is 2000, b is FROCT, c is 8000, d is -FROCT, e is -2000 + +// pack to uchar, uchar, int +typedef struct { + short base, modcnt; + fix arg; +} pt_mods; + +//-- constants +#define FRPTSUNIQUE 24 +#define FRPTSOFFS 8 +#define FRPTSZSHF 6 +#define FRPTSZMOD (2 << FRPTSZSHF) +#define FRPTSZPICK (1 << FRPTSZSHF) +#define FRPTSZMASK (3 << FRPTSZSHF) +#define FRPTSPTOFF ((1 << FRPTSZSHF) - 1) +// zcoor +#define FRPTSZBASE (0 << FRPTSZSHF) +#define FRPTSZCEIL (1 << FRPTSZSHF) +#define FRPTSZBASEU (2 << FRPTSZSHF) +#define FRPTSZCEILD (3 << FRPTSZSHF) + +// once shifted down, use these +#define FRPTSZPICK_DN (1) +#define FRPTSZCEIL_DN (1) +#define FRPTSZFLR_DN (0) + +#define FRMODNONE 0 +#define FRMODYAXIS 1 +#define FRMODXAXIS 2 + +// 0x2000*8/(2+sqrt(2)) +// =side of a regular octagon whose bounding box is a square of size fix1.0 +#define FROCTNUM 19195 +#define FROCTFIX fix_make(0, 19195) + +//-- externs +extern fix pt_offs[FRPTSOFFS]; +extern pt_mods pt_deref[FRPTSUNIQUE]; +extern ushort pt_uv[FRPTSUNIQUE][4][2]; +extern uchar pt_from_faceoff[4][FRPTSOFFS]; + +//======== WallstoPts + +// this corresponds to the list of point codes that make up an internal +// wall of a given wall code, note that the external walls are tacked on +// at the end for the compiler or dynamic parsing + +// the wall codes are, in order +// 0-3 : main diagonals, SW to NE, NW norm then SE norm, then opposite NE norm SW norm +// 4-11 : quarter diagonals, slope 1/2 low hi, then -1/2 low hi +// 12-19 : quarter diagonals, slope 2 left right, then -2 left right +// 20-23 : halve tiles, EW N norm S norm, then NS W norm E norm +// 24-27 : one foots, S wall, N wall, E wall, W wall +// 28-31 : triangle, NS left right, EW north south facing +// 32-37 : octagonal, NS W three then E three, top to bottom +// 38-43 : octagonal, EW N three then S three, top to bottom +// 44-51 : parameterized main diagonals, 2 of each order, bot then top +// bonus extra +// 52-55 : N,E,S,W + +typedef struct { + // union { + uchar ul, ur, lr, ll; + // uchar ptlst[4]; + // }; +} WallsToPts; + +#define FRWALLPTSCNT 56 +#define FROUTERWALLS (FRWALLPTSCNT - 4) + +extern WallsToPts wall_pts[FRWALLPTSCNT]; + +// one has to wonder, doesnt one? +#define ED (32 + 4) +#define JOE (40 - 4) + +//======== TilestoWalls + +// for each tile type in tilename, we need a list of what walls it contains +// this is done as a bitfield of N|E|S|W|Internal, where internal is a 4bit count +// the second byte is the base into the wall_pts array for this tile, where from +// that value up to that value + internal count are the internal walls + +typedef struct { + uchar wallbits; + uchar wallbase; +} TilesToWalls; + +#define FRWALLNORTH (1 << 7) +#define FRWALLEAST (1 << 6) +#define FRWALLSOUTH (1 << 5) +#define FRWALLWEST (1 << 4) +#define FRWALLINT (0xf) + +#define FRTILEWALLCNT FRTILETYPES + +extern TilesToWalls tile_walls[FRTILEWALLCNT]; + +//======== TilesToFloors + +// a tile has one or two floors, depending on whether it is a split slope/diagonal +// or not. it also has a cieling or not. it also has potential for points on it +// to be modified by the parameter. there is also a double bit for vsplits. +// Each floor has 3 or 4 points, depending on diagonal nature or not... + +// this is expressed as an 8 element reference, containing a header flags list, a +// byte containing the number of points per floor element, and a 6 byte data area +// holding either a 4 element floor or 1 or 2 3 element floors + +typedef struct { + uchar flags; + uchar ptsper; + uchar data[6]; +} TilesToFloors; + +#define FRFLRFLG_2ELEM 0x40 +#define FRFLRSHF_2ELEM 6 +#define FRFLRFLG_USEPR 0x20 +#define FRFLRFLG_DBL 0x10 +#define FRFLRFLG_NOTOP 0x08 +#define FRFLRFLG_PMSK 0x07 // not currently used, but could pack ptsper into struct + +// note set up for zany xor hacking for floor decode +// ie we can xor zmod with 1 if flipped, or and with ~ +// if we want a flat floor or cieling +#define FRFLRDATA_ZMOD FRPTSZMOD +#define FRFLRDATA_PMSK FRPTSPTOFF + +#define FRTILEFLOORCNT FRTILETYPES + +extern TilesToFloors tile_floors[FRTILEFLOORCNT]; + +//======== Normals +#define FRWNORM_MAX 8 +#define FRWNORM_MASK 0xf +#define FRWNORM_SHFT 4 + +#define FRFNORM_SLPN 0 +#define FRFNORM_SLPE 1 +#define FRFNORM_SLPS 2 +#define FRFNORM_SLPW 3 +#define FRFNORM_VZERO 4 +#define FRFNORM_VFULL 5 +#define FRFNORM_VZ_MIR 6 +#define FRFNORM_VF_MIR 7 + +extern ushort fr_wnorm_list[FRWALLPTSCNT]; +extern uchar fr_fnorm_list[FRTILEFLOORCNT]; +extern fix fr_norm_elements[FRWNORM_MAX + 1]; + +//======== Obstruct + +// face obstruct data contains the width data for each face of each +// tile. The format is l parm r parm packed into a byte, which can be looked +// up eventually in the big table, as opposed to actively decoded. + +#define FO_L_PARM 0x80 +#define FO_L_PMSK 0x38 +#define FO_L_SHFT 3 +#define FO_R_PARM 0x40 +#define FO_R_PMSK 0x07 +#define FO_R_SHFT 0 +#define FO_LR_MSK 0x3F +#define FO_HT_MSK 0xC0 + +#define FACE_CNT 4 + +#define FRFACEOBSTRUCTCNT FRTILETYPES +#define FOBASECODES 64 + +extern uchar face_obstruct[FRFACEOBSTRUCTCNT][FACE_CNT]; +extern fix fo_unpack[FOBASECODES][2]; +extern fix fo_anti_unpack[FOBASECODES][2]; + +//======== Merger +// what to or pt_codes with prior to derefing for mirror compatibility +// mirror by flr/ciel by xor/and +extern uchar merge_masks[5][2][2]; +extern uchar mmask_facelet[5][2][2]; + +//======== Tilelets + +// a tilelet is a list of facelet entries + +// there is a 16 bit pointer in each map square to a tile +// if it is NULL, there are no external walls +// otherwise, the low 4 bits are length, the top 12 an offset into the +// tilelet list array, where # 16bit facelet id's live + +typedef ushort TileLet; + +#define TL_LEN_MASK (0x000F) +#define TL_PTR_MASK (0xFFF0) +#define TL_PTR_SHFT 4 + +#define TileLet_Len(x) (x & TL_LEN_MASK) +#define TileLet_Ptr(x) (x & TL_PTR_MASK) // at 16 val res?, or shift down? +#define TileLet_Idx(x) (x >> TL_PTR_SHFT) + +//======== Facelets + +// a facelet id contains a 4bit header, 2 bits of type and 2 bits of which face +// followed by a 12 bit value, either data or ptr based on type + +// types are: +// simple - 5 bit top 5 bit bottom +// middle - ptr to 4 5 bit fields + 4 bit width codes (3 bytes) +// full - ptr to 4 8 bit heights + 2 4 bit width codes (5 bytes) + +typedef ushort FaceLet; + +#define FL_LEN_MASK 0x0003 +#define FL_FACE_MASK 0x000C +#define FL_DATA_MASK 0xFFF0 +#define FL_DATA_SHFT 4 + +#define FaceLet_Len(x) (x & FL_LEN_MASK) +#define FaceLet_Face(x) (x & FL_FACE_MASK) +#define FaceLet_Ptr(x) (x & FL_DATA_MASK) +#define FaceLet_Data(x) (x >> FL_DATA_SHFT) diff --git a/engine/src/GameSrc/Headers/frtypes.h b/engine/src/GameSrc/Headers/frtypes.h new file mode 100644 index 0000000..e4b1967 --- /dev/null +++ b/engine/src/GameSrc/Headers/frtypes.h @@ -0,0 +1,53 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __FRTYPES_H +#define __FRTYPES_H +/* + * $Source: n:/project/cit/src/inc/RCS/frtypes.h $ + * $Revision: 1.8 $ + * $Author: kevin $ + * $Date: 1994/04/14 15:01:10 $ + * + * Citadel renderer + * actual type definitions and such + */ + +#include "frcamera.h" +#include "res.h" +#include "2d.h" +#include "lg.h" + +// structures +typedef struct { + grs_canvas draw_canvas, main_canvas, hack_canvas; + uchar double_buffer; + int xtop, ytop; + int xwid, ywid; /* ywid is, of course, height */ + fix viewer_zoom; + int fov; + char axis; + int flags, detail, last_detail; + int (*draw_call)(void *dest_canvas, void *dest_bm, int x, int y, int flags); + void (*horizon_call)(void *dest_bm, int flags); + void (*render_call)(void *dest_bm, int flags); + cams *camptr, *xtracam; + char *realCanvasPtr; +} fauxrend_context; + +#endif // __FRTYPES_H diff --git a/engine/src/GameSrc/Headers/frtypesx.h b/engine/src/GameSrc/Headers/frtypesx.h new file mode 100644 index 0000000..84852ec --- /dev/null +++ b/engine/src/GameSrc/Headers/frtypesx.h @@ -0,0 +1,29 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __FRTYPESX_H +#define __FRTYPESX_H +/* FrTypesx.h + * $Revision: 1.2 $ + * goofy goofy goofy attempt to cut out include dependencies + */ +#ifndef __FRPROTOX_H +typedef void frc; +typedef void fmp; +#endif +#endif diff --git a/engine/src/GameSrc/Headers/frworld.h b/engine/src/GameSrc/Headers/frworld.h new file mode 100644 index 0000000..7b87896 --- /dev/null +++ b/engine/src/GameSrc/Headers/frworld.h @@ -0,0 +1,38 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/cit/src/inc/RCS/frworld.h $ + * $Revision: 1.1 $ + * $Author: dc $ + * $Date: 1994/01/02 17:16:56 $ + * + * Citadel Renderer + * world size/layout defines + */ + +// default map x by y +#define DEF_MAP_SZ 32 + +// number of discrete heights in database +#define HGT_STEPS 32 + +// how many fake tmaps to build +#define FAKE_TMAPS 64 + +// sizeof defines diff --git a/engine/src/GameSrc/Headers/fullamap.h b/engine/src/GameSrc/Headers/fullamap.h new file mode 100644 index 0000000..ccb6a56 --- /dev/null +++ b/engine/src/GameSrc/Headers/fullamap.h @@ -0,0 +1,26 @@ +/* + +Copyright (C) 2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef FULLAMAP_H +#define FULLAMAP_H + +void amap_start(void); +void amap_exit(void); + +#endif diff --git a/engine/src/GameSrc/Headers/fullscrn.h b/engine/src/GameSrc/Headers/fullscrn.h new file mode 100644 index 0000000..6ac2651 --- /dev/null +++ b/engine/src/GameSrc/Headers/fullscrn.h @@ -0,0 +1,79 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __FULLSCRN_H +#define __FULLSCRN_H + +/* + * $Source: n:/project/cit/src/inc/RCS/fullscrn.h $ + * $Revision: 1.4 $ + * $Author: tjs $ + * $Date: 1994/05/17 02:32:03 $ + * + * $Log: fullscrn.h $ + * Revision 1.4 1994/05/17 02:32:03 tjs + * Save\restore mfd in fullscreen fix. + * + * Revision 1.3 1994/03/04 07:05:02 mahk + * Full screen mania. + * + * Revision 1.2 1993/09/22 00:46:57 xemu + * added raising & lowering of invent * mfd regions + * + * Revision 1.1 1993/09/19 19:06:26 xemu + * Initial revision + * + * + */ + +// Includes +#include "frtypesx.h" + +// Defines + +#define FULL_VIEW_X 0 +#define FULL_VIEW_Y 0 +#define FULL_VIEW_HEIGHT 200 +#define FULL_VIEW_WIDTH 320 + +// Note, these have been kludged to parallel the +// view360 context numbers. +#define FULL_R_MFD_MASK 0x01 +#define FULL_L_MFD_MASK 0x02 +#define FULL_INVENT_MASK 0x04 +#define FULL_MFD_MASK(id) (((id) == 0) ? FULL_L_MFD_MASK : FULL_R_MFD_MASK) + +// Typedefs + +// Prototypes +void change_svga_screen_mode(); +errtype fullscreen_init(void); +void fullscreen_start(); +void fullscreen_exit(void); +errtype fullscreen_overlay(); +errtype full_lower_region(LGRegion *r); +errtype full_raise_region(LGRegion *r); + +// Globals +extern LGRegion *fullroot_region, *fullview_region; +extern LGRegion *inventory_region_full; +extern LGRegion *pagebutton_region_full; +extern uchar full_game_3d; +extern uchar full_visible; + +#endif // __FULLSCRN_H diff --git a/engine/src/GameSrc/Headers/game_screen.h b/engine/src/GameSrc/Headers/game_screen.h new file mode 100644 index 0000000..b687a51 --- /dev/null +++ b/engine/src/GameSrc/Headers/game_screen.h @@ -0,0 +1,82 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __GAMESCREEN_H +#define __GAMESCREEN_H + +/* + * $Source: n:/project/cit/src/inc/RCS/screen.h $ + * $Revision: 1.20 $ + * $Author: dc $ + * $Date: 1994/05/11 21:42:04 $ + * + * + */ + +#define GADGET_GAMESCREEN + +// C Library Includes + +// System Library Includes +#include "frtypesx.h" + +// Master Game Includes + +// Game Library Includes + +// Game Object Includes + +// Defines +#define SCREEN_VIEW_X 28 +#define SCREEN_VIEW_Y 24 +#define SCREEN_VIEW_HEIGHT 108 +#define SCREEN_VIEW_WIDTH 268 + +// Prototypes + +// Initialize the main game screen. This should only be called once, at +// the major initialization stage of the program. +errtype screen_init(void); + +// Bring the main game screen to the monitor, doing appropriate cool +// looking tricks (palette fading, etc.) as necessary +void screen_start(void); + +// Do appropriate stuff to indicate that we have left the game screen +void screen_exit(void); + +// Force a draw of the whole durned screen +errtype screen_draw(void); + +// Stop doing graphics things +errtype screen_shutdown(void); + +// Handle keyboard input anywhere on the main screen +uchar main_kb_callback(uiEvent *h, LGRegion *r, intptr_t udata); + +// rct NULL means fullscreen, slb NULL is no slabinit, key and maus are callbacks, NULL is no install +void generic_reg_init(uchar create_reg, LGRegion *reg, LGRect *rct, uiSlab *slb, uiHandlerProc key_h, uiHandlerProc maus_h); + +// Globals +extern uchar *default_font_buf; +extern LGRegion *root_region, *mainview_region, *inventory_region_game, *status_region; +extern LGRegion *pagebutton_region_game; +extern LGCursor globcursor, wait_cursor, fire_cursor; +extern frc *normal_game_fr_context; + +#endif // __GAMESCREEN_H diff --git a/engine/src/GameSrc/Headers/gameloop.h b/engine/src/GameSrc/Headers/gameloop.h new file mode 100644 index 0000000..b60a7ff --- /dev/null +++ b/engine/src/GameSrc/Headers/gameloop.h @@ -0,0 +1,84 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __GAMELOOP_H +#define __GAMELOOP_H + +/* + * $Source: n:/project/cit/src/inc/RCS/gameloop.h $ + * $Revision: 1.9 $ + * $Author: xemu $ + * $Date: 1993/09/18 00:16:39 $ + * + * $Log: gameloop.h $ + * Revision 1.9 1993/09/18 00:16:39 xemu + * FULLSCREEN_UPDATE + * + * Revision 1.8 1993/09/02 23:07:40 xemu + * angle me baby + * + * Revision 1.7 1993/07/06 01:13:37 mahk + * Added inventory stuff + * + * Revision 1.6 1993/06/06 00:44:11 xemu + * real rendering change flags + * + * Revision 1.5 1993/06/03 17:49:54 minman + * added anim update + * + * Revision 1.4 1993/05/23 19:01:35 xemu + * rmoved time flags + * + * Revision 1.3 1993/05/18 15:19:00 xemu + * new time constants + * + * Revision 1.2 1993/05/14 15:48:40 xemu + * change flags + * + * Revision 1.1 1993/05/12 14:20:21 xemu + * Initial revision + * + * + */ + +// Includes + +// C Library Includes + +// System Library Includes + +// Master Game Includes + +// Game Library Includes + +// Game Object Includes + +// Defines +#define VITALS_UPDATE LL_CHG_BASE << 0u +#define MFD_UPDATE LL_CHG_BASE << 1u +#define ANIM_UPDATE LL_CHG_BASE << 2u +#define DEMOVIEW_UPDATE LL_CHG_BASE << 3u +#define INVENTORY_UPDATE LL_CHG_BASE << 4u +#define FULLSCREEN_UPDATE LL_CHG_BASE << 5u + +// Prototypes +void game_loop(void); + +// Globals + +#endif // __GAMELOOP_H diff --git a/engine/src/GameSrc/Headers/gameobj.h b/engine/src/GameSrc/Headers/gameobj.h new file mode 100644 index 0000000..5168545 --- /dev/null +++ b/engine/src/GameSrc/Headers/gameobj.h @@ -0,0 +1,51 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __FAUXOBJD_H +#define __FAUXOBJD_H +/* + * $Source: n:/project/cit/src/inc/RCS/gameobj.h $ + * $Revision: 1.2 $ + * $Author: dc $ + * $Date: 1994/01/22 23:30:59 $ + */ + +#include "objects.h" + +void show_obj(ObjRefID oRef); + +// list of special obj numbers for renderer +#define FAUBJ_UNKNOWN 0 +#define FAUBJ_TEXTPOLY 1 +#define FAUBJ_BITMAP 2 +#define FAUBJ_TPOLY 3 +#define FAUBJ_CRIT 4 +#define FAUBJ_ANIMPOLY 5 +#define FAUBJ_VOX 6 +#define FAUBJ_NOOBJ 7 +#define FAUBJ_TEXBITMAP 8 +#define FAUBJ_FLATPOLY 9 +#define FAUBJ_MULTIVIEW 10 +#define FAUBJ_SPECIAL 11 +#define FAUBJ_TL_POLY 12 + +#define NUM_OBJ_RENDER_TYPES (FAUBJ_TL_POLY + 1) + +short compute_3drep(Obj *cobj, ObjID cobjid, int obj_type); + +#endif // __FAUXOBJD_H diff --git a/engine/src/GameSrc/Headers/gamepal.h b/engine/src/GameSrc/Headers/gamepal.h new file mode 100644 index 0000000..2e7ff55 --- /dev/null +++ b/engine/src/GameSrc/Headers/gamepal.h @@ -0,0 +1,28 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* This file created by RESTOOL */ + +#ifndef __GAMEPAL_H +#define __GAMEPAL_H + +#define RES_gamePalette 0x2bc // (700) +#define RES_gamePalette2 0x2bd // (701) +#define RES_gamePalette3 0x2be // (702) + +#endif diff --git a/engine/src/GameSrc/Headers/gamerend.h b/engine/src/GameSrc/Headers/gamerend.h new file mode 100644 index 0000000..712357f --- /dev/null +++ b/engine/src/GameSrc/Headers/gamerend.h @@ -0,0 +1,81 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __GAMEREND_H +#define __GAMEREND_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/gamerend.h $ + * $Revision: 1.5 $ + * $Author: tjs $ + * $Date: 1994/08/14 02:33:18 $ + * + * $Log: gamerend.h $ + * Revision 1.5 1994/08/14 02:33:18 tjs + * time limit + * + * Revision 1.4 1994/05/20 03:39:31 dc + * dmg types + * + * Revision 1.3 1994/05/09 06:06:10 dc + * protoypes and defines for secret_fx + * + * Revision 1.2 1993/12/21 03:03:47 minman + * added gamerend_init() prototype + * + * Revision 1.1 1993/10/18 23:42:26 xemu + * Initial revision + * + * + */ + +// Includes + +#define SNOW_COLOR_SET 0 +#define BLOOD_COLOR_SET 1 +#define SHIELD_COLOR_SET 2 + +#define TYPE_REND_SFX 0x0F00 +#define VAL_REND_SFX 0x00FF + +#define DYING_REND_SFX 0x0100 +#define REBORN_REND_SFX 0x0200 +#define FAKEWIN_REND_SFX 0x0300 +#define TIMELIMIT_REND_SFX 0x0400 + +#define DMG_SHIELD 0 +#define DMG_BLOOD 1 +#define DMG_RAD 2 + +// Prototypes +void begin_shodan_conquer_fx(uchar begin); +errtype gamerend_init(void); +void set_dmg_percentage(int which, ubyte percent); +void draw_full_static(grs_bitmap *stat_dest, int c_base); + +int gamesys_draw_func(void *fake_dest_canvas, void *fake_dest_bm, int x, int y, int flags); +void gamesys_render_func(void *fake_dest_bitmap, int flags); +void set_shield_raisage(uchar going_up); + +#ifdef __GAMEREND_SRC +int secret_render_fx = 0; +#else +extern int secret_render_fx; +#endif + +#endif // __GAMEREND_H diff --git a/engine/src/GameSrc/Headers/gamescr.h b/engine/src/GameSrc/Headers/gamescr.h new file mode 100644 index 0000000..8b859da --- /dev/null +++ b/engine/src/GameSrc/Headers/gamescr.h @@ -0,0 +1,241 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* This file created by RESTOOL */ + +#ifndef __GAMESCR_H +#define __GAMESCR_H + +#define RES_helpscreen_english 0x543 // (1347) +#define REF_IMG_bmHelpOverlayEnglish 0x5430000 +#define RES_helpscreen_french 0x544 // (1348) +#define REF_IMG_bmHelpOverlayFrench 0x5440000 +#define RES_helpscreen_german 0x545 // (1349) +#define REF_IMG_bmHelpOverlayGerman 0x5450000 +#define RES_gamescrGfx 0x259 // (601) +#define REF_IMG_bmBlankMessageLine 0x2590000 +#define REF_IMG_bmBlankInventoryPanel 0x2590001 +#define REF_IMG_bmBlankMFD 0x2590002 +#define REF_IMG_bmDiffBio 0x2590003 +#define REF_IMG_bmBiorhythm 0x2590004 +#define REF_IMG_bmVitalInnardsTop 0x2590005 +#define REF_IMG_bmVitalInnardsBottom 0x2590006 +#define REF_IMG_bmStatusAngle1 0x2590007 +#define REF_IMG_bmStatusAngle2 0x2590008 +#define REF_IMG_bmStatusAngle3 0x2590009 +#define REF_IMG_bmStatusAngle4 0x259000a +#define REF_IMG_bmHealthIcon1 0x259000b +#define REF_IMG_bmHealthIcon2 0x259000c +#define REF_IMG_bmHealthIcon3 0x259000d +#define REF_IMG_bmEnergyIcon1 0x259000e +#define REF_IMG_bmEnergyIcon2 0x259000f +#define REF_IMG_bmEnergyIcon3 0x2590010 +#define REF_IMG_bmCyberIcon1 0x2590011 +#define REF_IMG_bmCyberIcon2 0x2590012 +#define REF_IMG_bmCyberIcon3 0x2590013 +#define REF_IMG_bm3dBackground1 0x2590014 +#define REF_IMG_bm3dBackground2 0x2590015 +#define REF_IMG_bm3dBackground3 0x2590016 +#define REF_IMG_bm3dBackground4 0x2590017 +#define REF_IMG_bm3dBackground5 0x2590018 +#define REF_IMG_bm3dBackground6 0x2590019 +#define REF_IMG_bmMFDButtonBackground 0x259001a +#define REF_IMG_bmInventoryButtonBackground 0x259001b +#define RES_smallTechFont 0x25a // (602) +#define RES_tinyTechFont 0x25b // (603) +#define RES_mediumTechFont 0x25c // (604) +#define RES_largeTechFont 0x25d // (605) +#define RES_citadelFont 0x25e // (606) +#define RES_mediumLEDFont 0x25f // (607) +#define RES_bigLEDFont 0x260 // (608) +#define RES_graffitiFont 0x261 // (609) +#define RES_mfdFont 0x262 // (610) +#define RES_cutsceneFont 0x263 // (611) +#define RES_readingFont 0x264 // (612) +#define RES_coloraliasedFont 0x265 // (613) +#define RES_editorGfx 0x266 // (614) +#define REF_IMG_bmBitsIcon 0x2660000 +#define REF_IMG_bmHeightIcon 0x2660001 +#define REF_IMG_bmPokeIcon 0x2660002 +#define REF_IMG_bmPaintIcon 0x2660003 +#define REF_IMG_bmParamsIcon 0x2660004 +#define REF_IMG_bmStringsIcon 0x2660005 +#define REF_IMG_bmToggle3DIcon 0x2660006 +#define REF_IMG_bmBlankIcon1 0x2660007 +#define REF_IMG_bmStopGoIcon 0x2660008 +#define REF_IMG_bmDemoIcon 0x2660009 +#define REF_IMG_bmMusicIcon 0x266000a +#define REF_IMG_bmClearHighlightsIcon 0x266000b +#define REF_IMG_bmRubberbandIcon 0x266000c +#define REF_IMG_bmDebugIcon 0x266000d +#define REF_IMG_bmRenderIcon 0x266000e +#define REF_IMG_bmQuitIcon 0x266000f +#define REF_IMG_bmBlankIcon2 0x2660010 +#define REF_IMG_bmEmptyIcon1 0x2660011 +#define REF_IMG_bmFindIcon 0x2660012 +#define REF_IMG_bmTextureIcon 0x2660013 +#define REF_IMG_bmZoomIcon 0x2660014 +#define REF_IMG_bmRaiseIcon 0x2660015 +#define REF_IMG_bmLoadLevelIcon 0x2660016 +#define REF_IMG_bmPhysicsIcon 0x2660017 +#define REF_IMG_bmTilePopupIcon 0x2660018 +#define REF_IMG_bmBlankIcon3 0x2660019 +#define REF_IMG_bmEmptyIcon2 0x266001a +#define REF_IMG_bmEyeballIcon 0x266001b +#define REF_IMG_bmObjectIcon 0x266001c +#define REF_IMG_bmUnzoomIcon 0x266001d +#define REF_IMG_bmLowerIcon 0x266001e +#define REF_IMG_bmSaveLevelIcon 0x266001f +#define REF_IMG_bmTmapSelectIcon 0x2660020 +#define REF_IMG_bmEditPaintIcon 0x2660021 +#define REF_IMG_bmBlankIcon5 0x2660022 +#define REF_IMG_bmEmptyIcon3 0x2660023 +#define REF_IMG_bmLoadIcon 0x2660024 +#define REF_IMG_bmSaveIcon 0x2660025 +#define REF_IMG_bmCutpasteIcon 0x2660026 +#define REF_IMG_bmGunPistolIcon 0x2660027 +#define REF_IMG_bmGunAutoIcon 0x2660028 +#define REF_IMG_bmGunSpecialIcon 0x2660029 +#define REF_IMG_bmGunHandtohandIcon 0x266002a +#define REF_IMG_bmGunBeamIcon 0x266002b +#define REF_IMG_bmGunBeamprojIcon 0x266002c +#define REF_IMG_bmAmmoPistolIcon 0x266002d +#define REF_IMG_bmAmmoNeedleIcon 0x266002e +#define REF_IMG_bmAmmoMagnumIcon 0x266002f +#define REF_IMG_bmAmmoRifleIcon 0x2660030 +#define REF_IMG_bmAmmoFlechetteIcon 0x2660031 +#define REF_IMG_bmAmmoAutoIcon 0x2660032 +#define REF_IMG_bmAmmoProjIcon 0x2660033 +#define REF_IMG_bmPhysicsTracerIcon 0x2660034 +#define REF_IMG_bmPhysicsSlowIcon 0x2660035 +#define REF_IMG_bmPhysicsCameraIcon 0x2660036 +#define REF_IMG_bmGrenadeDirectIcon 0x2660037 +#define REF_IMG_bmGrenadeTimedIcon 0x2660038 +#define REF_IMG_bmDrugStatsIcon 0x2660039 +#define REF_IMG_bmHardwareGoggleIcon 0x266003a +#define REF_IMG_bmHardwareHardwareIcon 0x266003b +#define REF_IMG_bmSoftwareOffensiveIcon 0x266003c +#define REF_IMG_bmSoftwareDefensiveIcon 0x266003d +#define REF_IMG_bmSoftwareMiscIcon 0x266003e +#define REF_IMG_bmSoftwareOneShotIcon 0x266003f +#define REF_IMG_bmSoftwareDataIcon 0x2660040 +#define REF_IMG_bmBigstuffElectronicsIcon 0x2660041 +#define REF_IMG_bmBigstuffFurnishingsIcon 0x2660042 +#define REF_IMG_bmBigstuffOnthewallIcon 0x2660043 +#define REF_IMG_bmBigstuffLightIcon 0x2660044 +#define REF_IMG_bmBigstuffLabgearIcon 0x2660045 +#define REF_IMG_bmBigstuffTechnoIcon 0x2660046 +#define REF_IMG_bmBigstuffDecor 0x2660047 +#define REF_IMG_bmBigstuffTerrain 0x2660048 +#define REF_IMG_bmSmallstuffUselessIcon 0x2660049 +#define REF_IMG_bmSmallstuffBrokenIcon 0x266004a +#define REF_IMG_bmSmallstuffCorpselikeIcon 0x266004b +#define REF_IMG_bmSmallstuffGearIcon 0x266004c +#define REF_IMG_bmSmallstuffCardsIcon 0x266004d +#define REF_IMG_bmSmallstuffCyberIcon 0x266004e +#define REF_IMG_bmSmallstuffOnthewall 0x266004f +#define REF_IMG_bmSmallstuffPlot 0x2660050 +#define REF_IMG_bmFixtureControlIcon 0x2660051 +#define REF_IMG_bmFixtureReceptacleIcon 0x2660052 +#define REF_IMG_bmFixtureTerminalIcon 0x2660053 +#define REF_IMG_bmFixturePanelIcon 0x2660054 +#define REF_IMG_bmFixtureVendingIcon 0x2660055 +#define REF_IMG_bmFixtureCyberIcon 0x2660056 +#define REF_IMG_bmDoorNormalIcon 0x2660057 +#define REF_IMG_bmDoorDoorwaysIcon 0x2660058 +#define REF_IMG_bmDoorForceIcon 0x2660059 +#define REF_IMG_bmDoorElevatorIcon 0x266005a +#define REF_IMG_bmDoorSpecialIcon 0x266005b +#define REF_IMG_bmAnimatingObjectsIcon 0x266005c +#define REF_IMG_bmAnimatingTransitoryIcon 0x266005d +#define REF_IMG_bmAnimatingExplosionIcon 0x266005e +#define REF_IMG_bmTrapTriggerIcon 0x266005f +#define REF_IMG_bmTrapFeedbackIcon 0x2660060 +#define REF_IMG_bmTrapSecretIcon 0x2660061 +#define REF_IMG_bmContainerActualIcon 0x2660062 +#define REF_IMG_bmContainerWasteIcon 0x2660063 +#define REF_IMG_bmContainerLiquidIcon 0x2660064 +#define REF_IMG_bmContainerMutantCorpseIcon 0x2660065 +#define REF_IMG_bmContainerRobotCorpseIcon 0x2660066 +#define REF_IMG_bmContainerCyborgCorpseIcon 0x2660067 +#define REF_IMG_bmContainerOtherCorpseIcon 0x2660068 +#define REF_IMG_bmCritterMutantIcon 0x2660069 +#define REF_IMG_bmCritterRobotIcon 0x266006a +#define REF_IMG_bmCritterCyborgIcon 0x266006b +#define REF_IMG_bmCritterCyberIcon 0x266006c +#define REF_IMG_bmCritterRobobabeIcon 0x266006d +#define RES_leanmeterstub 0x267 // (615) +#define RES_popups 0x268 // (616) +#define RES_gamescrFull 0x269 // (617) +#define REF_IMG_bmGamescreenBackground 0x2690000 +#define RES_gamescrSHODAN 0x26a // (618) +#define REF_IMG_bmSHODANEndgame 0x26a0000 +#define REF_IMG_bmSHODANEndgameFull 0x26a0001 +#define RES_SideIconArt 0x550 // (1360) +#define RES_FullAmapBack 0x551 // (1361) +#define REF_IMG_bmTriLogoBack 0x5510000 +#define RES_cursorNormal 0x552 // (1362) +#define REF_IMG_bmTargetCursor 0x5520000 +#define REF_IMG_bmUpCursor 0x5520001 +#define REF_IMG_bmUpLeftCursor 0x5520002 +#define REF_IMG_bmUpRightCursor 0x5520003 +#define REF_IMG_bmCircLeftCursor 0x5520004 +#define REF_IMG_bmCircRightCursor 0x5520005 +#define REF_IMG_bmLeftCursor 0x5520006 +#define REF_IMG_bmRightCursor 0x5520007 +#define REF_IMG_bmDownCursor 0x5520008 +#define REF_IMG_bmSprintCursor 0x5520009 +#define REF_IMG_bmMfdPhaserCursor 0x552000a +#define REF_IMG_bmVmailCursor 0x552000b +#define RES_cursorMisc 0x553 // (1363) +#define REF_IMG_bmWaitCursor 0x5530000 +#define REF_IMG_bmOptionCursor 0x5530001 +#define REF_IMG_bmFireCursor 0x5530002 +#define REF_IMG_bmXCursor 0x5530003 +#define RES_cursorCyber 0x554 // (1364) +#define REF_IMG_bmCyberUpLeftCursor 0x5540000 +#define REF_IMG_bmCyberUpCursor 0x5540001 +#define REF_IMG_bmCyberUpRightCursor 0x5540002 +#define REF_IMG_bmCyberBankLeftCursor 0x5540003 +#define REF_IMG_bmCyberForwardCursor 0x5540004 +#define REF_IMG_bmCyberBankRightCursor 0x5540005 +#define REF_IMG_bmCyberRollLeftCursor 0x5540006 +#define REF_IMG_bmCyberDownCursor 0x5540007 +#define REF_IMG_bmCyberRollRightCursor 0x5540008 +#define RES_leanMeterBase 0x555 // (1365) +#define RES_leanMeterBio 0x556 // (1366) +#define RES_leanMeterRad 0x557 // (1367) +#define RES_leanShield1 0x558 // (1368) +#define RES_leanShield2 0x559 // (1369) +#define RES_leanShield3 0x55a // (1370) +#define RES_leanShield4 0x55b // (1371) +#define RES_leanMeterBack 0x55c // (1372) +#define REF_IMG_bmLeanBkgnd 0x55c0000 +#define REF_IMG_bmEyeIconL 0x55c0001 +#define REF_IMG_bmEyeIconLdark 0x55c0002 +#define REF_IMG_bmEyeIconR 0x55c0003 +#define REF_IMG_bmEyeIconRdark 0x55c0004 +#define REF_IMG_bmLeanBkgndTransp 0x55c0005 +#define RES_doubleTinyTechFont 0x730 // (1840) +#define RES_megaTinyTechFont 0x731 // (1841) +#define RES_doubleMediumLEDFont 0x732 // (1842) +#define RES_megaMediumLEDFont 0x733 // (1843) +#define RES_tallTinyTechFont 0x734 // (1844) +#define RES_doubleCutsceneFont 0x735 // (1845) + +#endif diff --git a/engine/src/GameSrc/Headers/gamesort.h b/engine/src/GameSrc/Headers/gamesort.h new file mode 100644 index 0000000..4c24649 --- /dev/null +++ b/engine/src/GameSrc/Headers/gamesort.h @@ -0,0 +1,21 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +void render_sort_start(void); +void render_sorted_objs(void); +void sort_show_obj(ObjRefID oRef); diff --git a/engine/src/GameSrc/Headers/gamestrn.h b/engine/src/GameSrc/Headers/gamestrn.h new file mode 100644 index 0000000..34dab64 --- /dev/null +++ b/engine/src/GameSrc/Headers/gamestrn.h @@ -0,0 +1,77 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __GAMESTRN_H +#define __GAMESTRN_H + +/* + * $Source: n:/project/cit/src/inc/RCS/gamestrn.h $ + * $Revision: 1.6 $ + * $Author: dc $ + * $Date: 1993/10/18 03:35:48 $ + * + * $Log: gamestrn.h $ + * Revision 1.6 1993/10/18 03:35:48 dc + * adding dumb-o @ifndef __SPEW + * + * Revision 1.5 1993/10/08 03:11:27 mahk + * Added more getters for greater flexibility. + * + * Revision 1.4 1993/09/02 23:07:40 xemu + * angle me baby + * + * Revision 1.3 1993/08/06 15:20:53 mahk + * Added long/short name accessors. + * + * Revision 1.2 1993/07/20 14:24:57 mahk + * Added init_strings + * + * Revision 1.1 1993/07/06 01:13:43 mahk + * Initial revision + * + * + */ + +// Includes + +// C Library Includes + +// System Library Includes + +// Master Game Includes + +// Game Library Includes + +// Game Object Includes + +// Defines + +// Prototypes +void init_strings(void); +char *get_string(int num, char *buf, int bufsize); +char *get_object_short_name(int triple, char *buf, int bufsize); +char *get_object_long_name(int triple, char *buf, int bufsize); +char *get_alloc_string(int num); +char *get_temp_string(int num); +void shutdown_strings(void); +char *get_texture_name(int abs_texture, char *buf, int bufsiz); +char *get_texture_use_string(int abs_texture, char *buf, int bufsiz); + +// Globals + +#endif // __GAMESTRN_H diff --git a/engine/src/GameSrc/Headers/gamesys.h b/engine/src/GameSrc/Headers/gamesys.h new file mode 100644 index 0000000..836f241 --- /dev/null +++ b/engine/src/GameSrc/Headers/gamesys.h @@ -0,0 +1,148 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __GAMESYS_H +#define __GAMESYS_H + +/* + * $Source: n:/project/cit/src/inc/RCS/gamesys.h $ + * $Revision: 1.18 $ + * $Author: mahk $ + * $Date: 1993/12/29 16:16:52 $ + * + * $Log: gamesys.h $ + * Revision 1.18 1993/12/29 16:16:52 mahk + * Added some more fatigue defines + * + * Revision 1.17 1993/11/24 21:04:57 mahk + * Added a new game time schedule. + * + * Revision 1.16 1993/11/15 19:33:51 xemu + * level control box params + * + * Revision 1.15 1993/11/14 18:51:00 minman + * got rid of MAX_HP -cause we're using PLAYER_MAX_HP now + * + * Revision 1.14 1993/10/21 21:25:32 xemu + * MAX_ENERGY = 2555 + * errr, 255 that is + * + * Revision 1.13 1993/09/02 23:07:41 xemu + * angle me baby + * + * Revision 1.12 1993/08/11 20:53:59 spaz + * #define'd maximum energy,hp,accuracy + * + * Revision 1.11 1993/08/06 16:01:41 minman + * modified software numbers + * + * Revision 1.10 1993/08/05 14:13:09 minman + * forgot objwpn.h + * + * Revision 1.9 1993/08/05 14:04:12 minman + * changed to new object properties order + * + * Revision 1.8 1993/07/29 20:16:42 minman + * made it rely on objprop.h + * + * Revision 1.7 1993/07/23 12:14:45 mahk + * Changed numbers of combat & defense softs + * + * Revision 1.6 1993/07/19 11:39:57 mahk + * Moved #defines for numbers of things back here. + * + * Revision 1.5 1993/07/02 14:16:23 mahk + * Removed NUM_WEAPONZ + * + * Revision 1.4 1993/07/01 17:18:58 spaz + * Removed double definition of NUM_DRUGZ (now in drugs.h) + * + * Revision 1.3 1993/06/29 09:53:46 mahk + * Modes for fatigue + * + * Revision 1.2 1993/06/16 23:01:45 xemu + * more #defines + * + * Revision 1.1 1993/05/12 14:20:51 xemu + * Initial revision + * + * + */ + +// Includes +#include "map.h" +#include "objwpn.h" +#include "objwarez.h" +#include "schedtyp.h" + +// Defines +#define NUM_LEVELZ 22 +#define NUM_HARDWAREZ NUM_HARDWARE // Steal from Objwarez.h +#define NUM_COMBAT_SOFTS NUM_OFFENSE_SOFTWARE +#define NUM_DEFENSE_SOFTS NUM_DEFENSE_SOFTWARE +#define NUM_MISC_SOFTS (NUM_ONESHOT_SOFTWARE + NUM_MISC_SOFTWARE) +#define NUM_GRENADEZ 7 +#define NUM_DRUGZ 7 +#define NUM_WEAPONZ 16 + +#define NUM_AMMO_TYPES NUM_AMMO // Steal from Objwpn.h + +// just so you don't screw up. +#define NUM_GRENADES NUM_GRENADEZ +#define NUM_LEVELS NUM_LEVELZ +#define NUM_DRUGS NUM_DRUGZ +#define NUM_WEAPONS NUM_WEAPONZ + +#define SPRINT_CONTROL_THRESHOLD 70 // threshold between sprint and jog + +#define MAX_ENERGY 255 +#define MAX_ACCURACY 100 + +#define LEVEL_GRAV_NORMAL 0 +#define LEVEL_GRAV_LOW 1 +#define LEVEL_GRAV_ZERO 2 + +#define LEVEL_MIST_NONE 0 +#define LEVEL_MIST_LIGHT 1 +#define LEVEL_MIST_HEAVY 2 + +#define LEVEL_BIOHAZARD_NONE 0 +#define LEVEL_BIOHAZARD_PARTIAL 1 +#define LEVEL_BIOHAZARD_SEVERE 2 + +#define GAME_SCHEDULE_SIZE 64 + +// Prototypes + +// Run the game system for one frame +errtype gamesys_run(void); + +void check_panel_ref(uchar puntme); +void unshodanizing_callback(ObjID id, intptr_t user_data); + +void game_sched_init(void); +void game_sched_free(void); + +void check_hazard_regions(MapElem *newElem); + +void expose_player(byte damage, ubyte type, ushort tsecs); + +// Globals +extern Schedule game_seconds_schedule; + +#endif // __GAMESYS_H diff --git a/engine/src/GameSrc/Headers/gametime.h b/engine/src/GameSrc/Headers/gametime.h new file mode 100644 index 0000000..528afe8 --- /dev/null +++ b/engine/src/GameSrc/Headers/gametime.h @@ -0,0 +1,76 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __GAMETIME_H +#define __GAMETIME_H + +/* + * $Source: n:/project/cit/src/inc/RCS/gametime.h $ + * $Revision: 1.5 $ + * $Author: mahk $ + * $Date: 1994/02/28 12:16:57 $ + * + * $Log: gametime.h $ + * Revision 1.5 1994/02/28 12:16:57 mahk + * Added suspend_ and resume_game_time + * + * Revision 1.4 1993/09/02 23:07:42 xemu + * angle me baby + * + * Revision 1.3 1993/07/12 16:03:42 mahk + * Changed proto to update_state + * + * Revision 1.2 1993/07/01 13:57:02 mahk + * Added run_fatigue_rate + * + * Revision 1.1 1993/05/12 14:20:32 xemu + * Initial revision + * + * + */ + +// Includes + +// C Library Includes + +// System Library Includes + +// Master Game Includes + +// Game Library Includes + +// Game Object Includes + +// Defines + +// Prototypes + +void update_level_gametime(void); + +// Increment game time by one frame +errtype update_state(uchar run_time); + +// suspend and resume game time, so that sim doesn't +// get run while doing non-real-time stuff. +void suspend_game_time(void); +void resume_game_time(void); + +// Globals +extern int run_fatigue_rate; + +#endif // __GAMETIME_H diff --git a/engine/src/GameSrc/Headers/gamewrap.h b/engine/src/GameSrc/Headers/gamewrap.h new file mode 100644 index 0000000..5e83ef2 --- /dev/null +++ b/engine/src/GameSrc/Headers/gamewrap.h @@ -0,0 +1,67 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __GAMEWRAP_H +#define __GAMEWRAP_H + +/* + * $Source: n:/project/cit/src/inc/RCS/gamewrap.h $ + * $Revision: 1.13 $ + * $Author: xemu $ + * $Date: 1994/03/24 01:22:08 $ + * + * + */ + +// Includes + +// Remember, change in wrapper.c also + +extern char *modding_archive_override; + +#define OLD_SAVE_GAME_ID_BASE 550 +#define SAVE_GAME_ID_BASE 4000 +#define NUM_RESIDS_PER_LEVEL 100 +#define CURRENT_GAME_FNAME "CurrentGame.dat" +#define ARCHIVE_FNAME (modding_archive_override != NULL ? modding_archive_override : "res/data/archive.dat") + +#define ResIdFromLevel(level) (SAVE_GAME_ID_BASE + (level * NUM_RESIDS_PER_LEVEL) + 2) + +// Defines + +// Typedefs + +// Prototypes + +// Loads or saves a game named by fname. +errtype copy_file(char *src_fname, char *dest_fname); +// errtype copy_file(FSSpec *srcFile, FSSpec *destFile, Boolean saveGameFile); +errtype save_game(char *fname, char *comment); +// errtype save_game(FSSpec *fSpec); +errtype load_game(char *fname); +// errtype load_game(FSSpec *loadSpec); +errtype write_level_to_disk(int idnum, uchar flush_mem); +uchar create_initial_game_func(short keycode, ulong context, void *data); +uchar create_level_archive_func(short keycode, ulong context, void *data); +errtype load_level_from_file(int level_num); +void startup_game(uchar visible); +void closedown_game(uchar visible); + +// Globals + +#endif // __GAMEWRAP_H diff --git a/engine/src/GameSrc/Headers/gearmfd.h b/engine/src/GameSrc/Headers/gearmfd.h new file mode 100644 index 0000000..bdfabb5 --- /dev/null +++ b/engine/src/GameSrc/Headers/gearmfd.h @@ -0,0 +1,26 @@ +/* + +Copyright (C) 2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef GEARMFD_H +#define GEARMFD_H + +void mfd_gear_expose(MFD *mfd, ubyte control); +uchar mfd_gear_handler(MFD *m, uiEvent *e); + +#endif diff --git a/engine/src/GameSrc/Headers/gettmaps.h b/engine/src/GameSrc/Headers/gettmaps.h new file mode 100644 index 0000000..cca7f50 --- /dev/null +++ b/engine/src/GameSrc/Headers/gettmaps.h @@ -0,0 +1,32 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Wacky macros and special texture map manipulation functions + +// note: you must first include textmaps.h + +// Note: places where this won't quite work right... +// cybmem.c, where we reclaim the memory +// textpal.c, where we reclaim the memory (since we loaded it wacky for the selector) +// of course, textmaps.c where we load them in the old way, and in texture_crunch_go there where we unload them + +// ok, those should be fixed + +#define BUILD_TEXTURE_BITMAPS +extern grs_bitmap *get_texture_map(int idx, int sz); +#define SAFE_TEXTURE (get_texture_map(0, 0)) diff --git a/engine/src/GameSrc/Headers/gr2ss.h b/engine/src/GameSrc/Headers/gr2ss.h new file mode 100644 index 0000000..1bbb484 --- /dev/null +++ b/engine/src/GameSrc/Headers/gr2ss.h @@ -0,0 +1,140 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include "frprotox.h" + +#ifdef SVGA_SUPPORT +extern void ss_string(char *s, short x, short y); +void ss_scale_string(char *s, short x, short y); +extern void ss_bitmap(grs_bitmap *bmp, short x, short y); +extern void ss_ubitmap(grs_bitmap *bmp, short x, short y); +extern void ss_scale_bitmap(grs_bitmap *bmp, short x, short y, short w, short h); +extern void ss_noscale_bitmap(grs_bitmap *bmp, short x, short y); +extern void ss_rect(short x1, short y1, short x2, short y2); +extern void ss_box(short x1, short y1, short x2, short y2); +extern void ss_int_line(short x1, short y1, short x2, short y2); +extern void ss_thick_int_line(short x1, short y1, short x2, short y2); +extern void ss_int_disk(short x1, short y1, short rad); +extern void ss_safe_set_cliprect(short x1, short y1, short x2, short y2); +extern void ss_cset_cliprect(grs_canvas *pcanv, short x1, short y1, short x2, short y2); +extern void ss_vline(short x1, short y1, short y2); +extern void ss_hline(short x1, short y1, short y2); +extern void ss_fix_line(fix x1, fix y1, fix x2, fix y2); +extern void ss_thick_fix_line(fix x1, fix y1, fix x2, fix y2); +extern void ss_get_bitmap(grs_bitmap *bmp, short x, short y); +extern void ss_set_pixel(long color, short x, short y); +extern void ss_set_thick_pixel(long color, short x, short y); +extern void ss_clut_ubitmap(grs_bitmap *bmp, short x, short y, uchar *cl); +extern void ss_recompute_zoom(frc *w, short oldm); +extern void ss_mouse_convert(short *px, short *py, uchar down); +extern void ss_mouse_convert_round(short *px, short *py, uchar down); +extern void ss_point_convert(short *px, short *py, uchar down); + +extern void gr2ss_register_init(char convert_type, short init_x, short init_y); +extern void gr2ss_register_mode(char conv_mode, short nx, short ny); + +extern short ss_curr_mode_width(void); +extern short ss_curr_mode_height(void); +extern void ss_set_hack_mode(short new_m, short *tval); + +#define MAX_CONVERT_TYPES 4 +#define MAX_USE_MODES 8 + +extern fix convert_x[MAX_CONVERT_TYPES][MAX_USE_MODES]; +extern fix convert_y[MAX_CONVERT_TYPES][MAX_USE_MODES]; +extern fix inv_convert_x[MAX_CONVERT_TYPES][MAX_USE_MODES]; +extern fix inv_convert_y[MAX_CONVERT_TYPES][MAX_USE_MODES]; + +extern char convert_type; +extern char convert_use_mode; +void mouse_unconstrain(void); +uchar perform_svga_conversion(uchar mask); + +extern short MODE_SCONV_X(short cval, short m); +extern short MODE_SCONV_Y(short cval, short m); + +#define SCONV_X(x) \ + (convert_use_mode ? fast_fix_mul_int(fix_make((x), 0), convert_x[convert_type][convert_use_mode]) : x) +#define SCONV_Y(y) \ + (convert_use_mode ? fast_fix_mul_int(fix_make((y), 0), convert_y[convert_type][convert_use_mode]) : y) +#define RSCONV_X(x) fix_int(0x8000 + fast_fix_mul(fix_make((x), 0), convert_x[convert_type][convert_use_mode])) +#define RSCONV_Y(y) fix_int(0x8000 + fast_fix_mul(fix_make((y), 0), convert_y[convert_type][convert_use_mode])) + +#define INV_SCONV_X(x) \ + (convert_use_mode ? fast_fix_mul_int(fix_make((x), 0), inv_convert_x[convert_type][convert_use_mode]) : x) +#define INV_SCONV_Y(y) \ + (convert_use_mode ? fast_fix_mul_int(fix_make((y), 0), inv_convert_y[convert_type][convert_use_mode]) : y) + +#define FIXCONV_X(x) fast_fix_mul((x), convert_x[convert_type][convert_use_mode]) +#define FIXCONV_Y(y) fast_fix_mul((y), convert_y[convert_type][convert_use_mode]) +#define INV_FIXCONV_X(x) fast_fix_mul((x), inv_convert_x[convert_type][convert_use_mode]) +#define INV_FIXCONV_Y(y) fast_fix_mul((y), inv_convert_y[convert_type][convert_use_mode]) + +extern uchar gr2ss_override; + +#define OVERRIDE_NONE 0x00 +#define OVERRIDE_SCALE 0x01 +#define OVERRIDE_FONT 0x02 +#define OVERRIDE_CLIP 0x04 +#define OVERRIDE_GET_BM 0x10 +#define OVERRIDE_ALL 0x7F +#define OVERRIDE_FAIL 0x80 + +#else + +#define ss_string(s, x, y) gr_string(s, x, y) +#define ss_bitmap(bmp, x, y) gr_bitmap(bmp, x, y) +#define ss_ubitmap(bmp, x, y) gr_ubitmap(bmp, x, y) +#define ss_noscale_bitmap(bmp, x, y) gr_bitmap(bmp, x, y) +#define ss_scale_bitmap(bmp, x, y, w, h) gr_scale_bitmap(bmp, x, y, w, h) +#define ss_rect(x1, y1, x2, y2) gr_rect(x1, y1, x2, y2) +#define ss_box(x1, y1, x2, y2) gr_box(x1, y1, x2, y2) +#define ss_int_line(x1, y1, x2, y2) gr_int_line(x1, y1, x2, y2) +#define ss_thick_int_line(x1, y1, x2, y2) gr_int_line(x1, y1, x2, y2) +#define ss_int_disk(x1, y1, rad) gr_int_disk(x1, y1, rad) +#define ss_safe_set_cliprect(x1, y1, x2, y2) gr_safe_set_cliprect(x1, y1, x2, y2) +#define ss_cset_cliprect(pcanv, x1, y1, x2, y2) gr_cset_cliprect(pcanv, x1, y1, x2, y2) +#define ss_vline(x1, y1, y2) gr_vline(x1, y1, y2) +#define ss_hline(x1, y1, y2) gr_hline(x1, y1, y2) +#define ss_fix_line(x1, y1, x2, y2) gr_fix_line(x1, y1, x2, y2) +#define ss_thick_fix_line(x1, y1, x2, y2) gr_fix_line(x1, y1, x2, y2) +#define ss_get_bitmap(bmp, x, y) gr_get_bitmap(bmp, x, y) +#define ss_set_pixel(color, x, y) gr_set_pixel(color, x, y) +#define ss_set_thick_pixel(color, x, y) gr_set_pixel(color, x, y) +#define ss_clut_ubitmap(bmp, x, y, cl) gr_clut_ubitmap(bmp, x, y, cl) +#define ss_recompute_zoom(w, oldm) + +#define gr2ss_register_init(convert_type, init_x, init_y) +#define gr2ss_register_mode(conv_mode, nx, ny) + +extern void ss_mouse_convert(short *px, short *py, uchar down); +extern void ss_mouse_convert_round(short *px, short *py, uchar down); + +#define SCONV_X(x) x +#define SCONV_Y(y) y +#define INV_SCONV_X(x) x +#define INV_SCONV_Y(y) y + +#define FIXCONV_X(x) x +#define FIXCONV_Y(y) y +#define INV_FIXCONV_X(x) x +#define INV_FIXCONV_Y(y) y + +#define MODE_SCONV_X(x, m) x +#define MODE_SCONV_Y(y, m) y +#endif diff --git a/engine/src/GameSrc/Headers/grenades.h b/engine/src/GameSrc/Headers/grenades.h new file mode 100644 index 0000000..18c1036 --- /dev/null +++ b/engine/src/GameSrc/Headers/grenades.h @@ -0,0 +1,104 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __GRENADES_H +#define __GRENADES_H + +/* + * $Source: n:/project/cit/src/inc/RCS/grenades.h $ + * $Revision: 1.17 $ + * $Author: minman $ + * $Date: 1994/06/20 22:59:39 $ + * + */ + +// Includes +#include "objects.h" +#include "objclass.h" + +// Defines + +// type flags +#define GREN_CONTACT_TYPE 0x01 +#define GREN_MOTION_TYPE 0x02 +#define GREN_TIMING_TYPE 0x04 +#define GREN_MINE_TYPE 0x08 + +// instance flags +#define GREN_ACTIVE_FLAG 0x01 +#define GREN_DUD_FLAG 0x02 +#define GREN_MINE_STILL 0x04 + +typedef struct { + ushort timestamp; + ushort type; + ObjID gren_id; + ubyte unique_id; + ubyte filler; +} GrenSchedEvent; + +typedef struct { + fix radius; + fix radius_change; + int damage_mod; + int damage_change; + int dtype; + fix knock_mass; + ubyte offense; + ubyte penet; +} ExplosionData; + +#define SMALL_GAME_EXPL 0 +#define MEDIUM_GAME_EXPL 1 +#define LARGE_GAME_EXPL 2 +#define GAME_EXPLS 3 +extern ExplosionData game_explosions[GAME_EXPLS]; + +#define UNIQUE_LIMIT 255 + +// Prototypes + +// Get the name for a particular grenade type. +char *get_grenade_name(int gtype, char *buf); + +// this will activate the grenade - also set the timing stuff if it's a timing grenade +// give it the SpecID of the grenade +void activate_grenade(ObjSpecID osid); + +// do_explosion() +// will explode the grenade with id, and it'll show a special effect if arg is TRUE +void do_explosion(ObjLoc loc, ObjID exclusion, ubyte special_effect, ExplosionData *attack_data); + +void do_object_explosion(ObjID id); + +// void do_explosion(ObjLoc loc, ObjID exclusion, ubyte special_effect, fix radius, fix radius_change, int damage_mod, +// int damage_change, int dtype, fix knock_mass, fix knock_speed, ubyte offense,ubyte penet);/ void do_explosion(ObjID +// id, uchar special_effect); + +uchar activate_grenade_on_cursor(void); + +void reactivate_mine(ObjID id); + +void do_grenade_explosion(ObjID id, uchar special_effect); + +void grenade_stopped(ObjID id); +void grenade_contact(ObjID id, int severity); + +#define GRENADE_COLOR WHITE + +#endif // __GRENADES_H diff --git a/engine/src/GameSrc/Headers/hand.h b/engine/src/GameSrc/Headers/hand.h new file mode 100644 index 0000000..3e8e5df --- /dev/null +++ b/engine/src/GameSrc/Headers/hand.h @@ -0,0 +1,54 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __HAND_H +#define __HAND_H + +/* + * $Source: n:/project/cit/src/inc/RCS/hand.h $ + * $Revision: 1.4 $ + * $Author: minman $ + * $Date: 1994/01/24 07:26:03 $ + * + * $Log: hand.h $ + * Revision 1.4 1994/01/24 07:26:03 minman + * cleaned up code + * + * Revision 1.3 1993/12/21 03:04:01 minman + * get_handart asks for a mouse_y now + * + * Revision 1.2 1993/11/09 02:56:51 xemu + * added mouse_x to handart + * + * Revision 1.1 1993/09/08 18:44:46 minman + * Initial revision + * + * + */ + +// Includes +#include "objwpn.h" + +extern ubyte weapon_to_handart[NUM_GUN]; + +Ref get_handart(int *x_offset, int *y_offset, int *beam_x_offset, short mouse_x, short mouse_y); +void notify_draw_handart(void); +void reset_handart_count(int wpn_num); + + +#endif // __HAND_H diff --git a/engine/src/GameSrc/Headers/handart.h b/engine/src/GameSrc/Headers/handart.h new file mode 100644 index 0000000..acae4f5 --- /dev/null +++ b/engine/src/GameSrc/Headers/handart.h @@ -0,0 +1,41 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* This file created by RESTOOL */ + +#ifndef __HANDART_H +#define __HANDART_H + +#define RES_handArt_0 0x29e // (670) +#define RES_handArt_1 0x29f // (671) +#define RES_handArt_2 0x2a0 // (672) +#define RES_handArt_3 0x2a1 // (673) +#define RES_handArt_4 0x2a2 // (674) +#define RES_handArt_5 0x2a3 // (675) +#define RES_handArt_6 0x2a4 // (676) +#define RES_handArt_7 0x2a5 // (677) +#define RES_handArt_8 0x2a6 // (678) +#define RES_handArt_9 0x2a7 // (679) +#define RES_handArt_10 0x2a8 // (680) +#define RES_handArt_11 0x2a9 // (681) +#define RES_handArt_12 0x2aa // (682) +#define RES_handArt_13 0x2ab // (683) +#define RES_handArt_14 0x2ac // (684) +#define RES_handArt_15 0x2ad // (685) + +#endif diff --git a/engine/src/GameSrc/Headers/hkeyfunc.h b/engine/src/GameSrc/Headers/hkeyfunc.h new file mode 100644 index 0000000..2e9b906 --- /dev/null +++ b/engine/src/GameSrc/Headers/hkeyfunc.h @@ -0,0 +1,194 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __HKEYFUNC_H +#define __HKEYFUNC_H + +/* + * $Source: n:/project/cit/src/inc/RCS/hkeyfunc.h $ + * $Revision: 1.24 $ + * $Author: dc $ + * $Date: 1994/01/16 05:22:46 $ + * + * $Log: hkeyfunc.h $ + * Revision 1.24 1994/01/16 05:22:46 dc + * inp6d + * + * Revision 1.23 1994/01/12 11:43:37 xemu + * cutpaste mode + * + * Revision 1.22 1993/09/09 03:22:31 dc + * bkpt_me + * + * Revision 1.21 1993/09/02 23:07:44 xemu + * angle me baby + * + * Revision 1.20 1993/08/24 12:21:10 xemu + * sfx toggle + * + * Revision 1.19 1993/07/13 00:45:52 spaz + * killed newmfd.h dependency + * + * Revision 1.18 1993/07/12 01:59:27 spaz + * #ifdef's to distinguish between old and new mfd systems, + * which are still coexisting. (ugh) + * + * Revision 1.17 1993/07/08 14:49:27 xemu + * physics_toggle + * + * Revision 1.16 1993/06/24 20:25:28 mahk + * Added bitsmode + * + * Revision 1.15 1993/06/16 18:25:48 xemu + * really_quit and edit_flag + * + * Revision 1.14 1993/06/16 16:21:05 mahk + * Added new level data + * + * Revision 1.13 1993/06/16 15:52:39 xemu + * mono functions + * + * Revision 1.12 1993/06/14 15:36:51 xemu + * music_stop + * + * Revision 1.11 1993/06/04 17:16:53 xemu + * music hotkey + * + * Revision 1.10 1993/06/01 19:48:26 xemu + * esc hotkey func added + * + * Revision 1.9 1993/05/25 00:00:37 xemu + * slewing and view zoom + * + * Revision 1.8 1993/05/24 15:19:56 xemu + * combined loop switchers + * + * Revision 1.7 1993/05/23 16:35:22 xemu + * find_func, pallete_mode + * + * Revision 1.6 1993/05/21 17:45:34 xemu + * eyeball_mode + * + * Revision 1.5 1993/05/20 20:58:43 xemu + * control_panel_func + * + * Revision 1.4 1993/05/20 09:01:17 mahk + * Added tilemap resize & move. Implemented a currently somewhat broken version of + * the 3d toggle button. Added tilemap cursor. + * + * Revision 1.3 1993/05/19 17:12:57 xemu + * added a bazillion stubs + * + * Revision 1.2 1993/05/18 14:58:02 mahk + * Added zooming and debuggin functions. + * + * Revision 1.1 1993/05/14 15:46:27 xemu + * Initial revision + * + * + */ + +// Includes + +// C Library Includes + +// System Library Includes + +// Master Game Includes + +// Game Library Includes + +// Game Object Includes + +// Defines + +#define ZOOM_IN 0 +#define ZOOM_OUT 1 + +#define TERRAIN_MODE 0 +#define TEXTURING_MODE 1 +#define OBJECT_MODE 2 +#define EYEBALL_MODE 3 +#define MUSIC_MODE 4 +#define BITS_MODE 5 +#define CUTPASTE_MODE 6 + +#define POKE_MODE 0 +#define PAINT_MODE 1 +#define RUBBERBAND_MODE 2 + +// Prototypes +uchar quit_key_func(ushort keycode, uint32_t context, intptr_t data); +uchar really_quit_key_func(ushort keycode, uint32_t context, intptr_t data); +uchar change_mode_func(ushort keycode, uint32_t context, intptr_t data); +uchar toggle_time_func(ushort keycode, uint32_t context, intptr_t data); +uchar do_popup_textmenu(ushort keycode, uint32_t context, intptr_t g); +uchar mono_config_func(ushort keycode, uint32_t context, intptr_t data); +uchar zoom_func(ushort keycode, uint32_t context, intptr_t data); +uchar load_level_func(ushort keycode, uint32_t context, intptr_t data); +uchar save_level_func(ushort keycode, uint32_t context, intptr_t data); +uchar run_intro_func(ushort keycode, uint32_t context, intptr_t data); +uchar toggle_3d_func(ushort keycode, uint32_t context, intptr_t data); +uchar texture_selection_func(ushort keycode, uint32_t context, intptr_t data); +uchar tilemap_mode_func(ushort keycode, uint32_t context, intptr_t data); +uchar draw_mode_func(ushort keycode, uint32_t context, intptr_t data); +uchar clear_highlight_func(ushort keycode, uint32_t context, intptr_t data); +uchar lighting_func(ushort keycode, uint32_t context, intptr_t data); +uchar inp6d_panel_func(ushort keycode, uint32_t context, intptr_t data); +uchar render_panel_func(ushort keycode, uint32_t context, intptr_t data); +uchar bkpt_me(ushort keycode, uint32_t context, intptr_t data); +uchar popup_tilemap_func(ushort keycode, uint32_t context, intptr_t data); +uchar editor_options_func(ushort keycode, uint32_t context, intptr_t data); +uchar editor_modes_func(ushort keycode, uint32_t context, intptr_t data); +uchar misc_menu_func(ushort keycode, uint32_t context, intptr_t data); +uchar control_panel_func(ushort keycode, uint32_t context, intptr_t data); +uchar do_find_func(ushort keycode, uint32_t context, intptr_t data); +uchar stupid_slew_func(ushort keycode, uint32_t context, intptr_t data); +uchar zoom_3d_func(ushort keycode, uint32_t context, intptr_t data); +uchar menu_close_func(ushort keycode, uint32_t context, intptr_t data); +void start_music(void); +void stop_music(void); +uchar toggle_music_func(ushort, uint32_t, intptr_t); +uchar mono_clear_func(ushort keycode, uint32_t context, intptr_t data); +uchar edit_flags_func(ushort keycode, uint32_t context, intptr_t data); +uchar mono_toggle_func(ushort keycode, uint32_t context, intptr_t data); +uchar new_level_func(ushort keycode, uint32_t context, intptr_t data); +uchar toggle_physics_func(ushort keycode, uint32_t context, intptr_t data); +uchar toggle_giveall_func(ushort keycode, uint32_t context, intptr_t data); +uchar toggle_up_level_func(ushort keycode, uint32_t context, intptr_t data); +uchar toggle_down_level_func(ushort keycode, uint32_t context, intptr_t data); +uchar toggle_sfx_func(ushort keycode, uint32_t context, intptr_t data); + +uchar save_hotkey_func(ushort, uint32_t, intptr_t); +uchar pause_game_func(ushort, uint32_t, intptr_t); +uchar clear_fullscreen_func(ushort keycode, uint32_t context, intptr_t data); +uchar arm_grenade_hotkey(ushort keycode, uint32_t context, intptr_t data); +uchar select_grenade_hotkey(ushort keycode, uint32_t context, intptr_t data); +uchar select_drug_hotkey(ushort keycode, uint32_t context, intptr_t data); +uchar use_drug_hotkey(ushort keycode, uint32_t context, intptr_t data); + +uchar toggle_mouse_look(ushort keycode, uint32_t context, intptr_t data); + +// uchar (ushort keycode, uint32_t context, intptr_t data); + +// Globals + +// Unused? +extern int current_palette_mode; + +#endif // __HKEYFUNC_H diff --git a/engine/src/GameSrc/Headers/hud.h b/engine/src/GameSrc/Headers/hud.h new file mode 100644 index 0000000..660a44e --- /dev/null +++ b/engine/src/GameSrc/Headers/hud.h @@ -0,0 +1,96 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __HUD_H +#define __HUD_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/hud.h $ + * $Revision: 1.23 $ + * $Author: mahk $ + * $Date: 1994/08/11 18:31:34 $ + * + * + */ + +// Includes +#include "frtypesx.h" + +// C Library Includes + +// System Library Includes + +// Master Game Includes + +// Game Library Includes + +// Game Object Includes + +// Defines +#define HUD_RADIATION 0x00000001u // Are we in radiation +#define HUD_BIOHAZARD 0x00000002u // Are we in a bio area +#define HUD_RANGE 0x00000004u // Range to target +#define HUD_FATIGUE 0x00000008u // High fatigue levels +#define HUD_TARGRECT 0x00000010u // Rectangle around target +#define HUD_GRENADE 0x00000020u // Time to detonate grenade +#define HUD_INFRARED 0x00000040u // Infrared active +#define HUD_SHIELD 0x00000080u // Show shield absorption +#define HUD_SHODOMETER 0x00000100u // Changes in shodometer +#define HUD_DETECT_EXP 0x00000200u // Explosion detection +#define HUD_COMPASS 0x00000400u // Compass +#define HUD_ZEROGRAV 0x00000800u // abnormal gravity conditions +#define HUD_FAKEID 0x00001000u // fakeid software in use +#define HUD_DECOY 0x00002000u // decoy software in use +#define HUD_TURBO 0x00004000u // turbo software in use +#define HUD_CYBERTIME 0x00008000u // time remaining until SHODAN sends his avatar against you +#define HUD_CYBERDANGER 0x00010000u // imminent peril of being ejected from cspace +#define HUD_RADPOISON 0x00100000u // taking radiation damage +#define HUD_BIOPOISON 0x00200000u // taking bio damage +#define HUD_BEAMHOT 0x00400000u // weapon about to overheat +#define HUD_MSGLINE 0x00800000u // message line. +#define HUD_GAMETIME 0x01000000u // time remaining in game +#define HUD_ENERGYUSE 0x02000000u // energy usage notification +#define HUD_ENVIROUSE 0x04000000u // enviro suit drain/absorb +#define HUD_MESSAGE 0x80000000u // general hud bit for hud_message (NIY) + +#define HUD_ALL 0xFFFFFFFFu + +#define HUD_COLOR_BANKS 3 +#define HUD_COLORS_PER_BANK 5 + +// Prototypes + +// Update the HUD. If redraw_whole is TRUE, better draw the +// whole durned thing. +errtype hud_update(uchar redraw_whole, frc *context); +errtype cyber_hud_update(uchar redraw_whole); + +// Set the data being displayed by the HUD. +errtype hud_set(ulong hud_modes); +errtype hud_unset(ulong hud_modes); +errtype hud_set_time(ulong hud_modes, ulong ticks); + +void hud_do_objs(short xtop, short ytop, short xwid, short ywid, uchar reverse); +void hud_shutdown_lines(void); + +// Globals +extern LGRect target_screen_rect; +extern ubyte hud_colors[HUD_COLOR_BANKS][HUD_COLORS_PER_BANK]; +extern ubyte hud_color_bank; + +#endif // __HUD_H diff --git a/engine/src/GameSrc/Headers/hudobj.h b/engine/src/GameSrc/Headers/hudobj.h new file mode 100644 index 0000000..646ca32 --- /dev/null +++ b/engine/src/GameSrc/Headers/hudobj.h @@ -0,0 +1,93 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __HUDOBJ_H +#define __HUDOBJ_H + +/* + * $Source: n:/project/cit/src/inc/RCS/hudobj.h $ + * $Revision: 1.3 $ + * $Author: mahk $ + * $Date: 1994/06/29 00:47:25 $ + * + * $Log: hudobj.h $ + * Revision 1.3 1994/06/29 00:47:25 mahk + * Added hudobj_rect_capable. + * + * Revision 1.2 1993/12/18 00:32:33 xemu + * made flag definition in line with objbit.h + * + * Revision 1.1 1993/10/13 21:47:24 mahk + * Initial revision + * + * + */ + +// Includes + +// --------- +// INTERNALS +// --------- + +extern ushort hudobj_classes[]; + +#define HUDOBJ_INST_FLAG 0x01 +#define NUM_HUDOBJS 16 + +extern struct _hudobj_data { + short id; + short xl, yl, xh, yh; +} hudobj_vec[NUM_HUDOBJS]; + +extern ubyte current_num_hudobjs; + +// ------------ +// RENDERER API +// ------------ + +// given an objid, determin whether this is an object the renderer +// should store in the u +#define IS_HUDOBJ(id) \ + ((objs[id].info.inst_flags & HUDOBJ_INST_FLAG) || ((1 << objs[id].subclass) & hudobj_classes[objs[id].obclass])) + +#define SET_HUDOBJ_RECT(oid, oxl, oyl, oxh, oyh) \ + if (current_num_hudobjs < NUM_HUDOBJS) { \ + struct _hudobj_data *hd = &hudobj_vec[current_num_hudobjs++]; \ + hd->id = (oid); \ + hd->xl = (oxl); \ + hd->xh = (oxh); \ + hd->yl = (oyl); \ + hd->yh = (oyh); \ + } +// --------------- +// GAME SYSTEM API +// --------------- + +#define HUDOBJ_ALL_SUBCLASSES 0xFF +void hudobj_set_subclass(ubyte l_class, ubyte subclass, uchar val); +// Sets the value of IS_HUDOBJ for all objects of a particular class & subclass. +// if subclass is HUDOBJ_ALL_SUBCLASSES then all subclasses will be set accordingly. + +void hudobj_set_id(short id, uchar val); +// Sets the value of IS_HUDOBJ for the specified object. + +#define hudobj_rect_capable(triple) (ObjProps[OPTRIP(triple)].render_type == FAUBJ_BITMAP) + +// Globals + +#endif // __HUDOBJ_H diff --git a/engine/src/GameSrc/Headers/ice.h b/engine/src/GameSrc/Headers/ice.h new file mode 100644 index 0000000..9730929 --- /dev/null +++ b/engine/src/GameSrc/Headers/ice.h @@ -0,0 +1,45 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include "objects.h" + +// Macro to determine whether or not a given object is ice'd. Assumes that +// A. We are in cyberspace and +// B. That there are no otherwise animating objects in cyberspace (well, besides things of CLASS_ANIMATING) +// If either of these assumptions change, this will probably have to change as well. + +#define obj_ICE_ICE_BABY(cobj) \ + ((cobj)->info.current_frame && ((cobj)->obclass != CLASS_ANIMATING) && ((cobj)->obclass != CLASS_CRITTER)) +#define obj_DEICE(cobj) \ + do { \ + (cobj)->info.current_frame = 0; \ + } while (0) +#define obj_ICE_AGIT(cobj) ((cobj)->info.make_info) +#define obj_SET_ICE_AGIT(cobj, nv) ((cobj)->info.make_info = nv) +#define obj_ICE_LEVEL(cobj) ((cobj)->info.inst_flags >> 6) +// note level is overall level, color in 3d +// agit is how annoyed it is +// hp is strength/size, moded by level and agit, i guess + +#define ICE_ICE_BABY(id) obj_ICE_ICE_BABY(&objs[(id)]) +#define DEICE(id) obj_DEICE(&objs[(id)]) +#define ICE_AGIT(id) obj_ICE_AGIT(&objs[(id)]) +#define SET_ICE_AGIT(id, newval) obj_SET_ICE_AGIT(&objs[(id)], newval) +#define ICE_LEVEL(id) obj_ICE_LEVEL(&objs[(id)]) + +#define MAX_AGIT 255 diff --git a/engine/src/GameSrc/Headers/init.h b/engine/src/GameSrc/Headers/init.h new file mode 100644 index 0000000..1098767 --- /dev/null +++ b/engine/src/GameSrc/Headers/init.h @@ -0,0 +1,35 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __INIT_H +#define __INIT_H + +// init.h +extern void init_all(void); +extern void free_all(void); +extern uchar ppall[]; // pointer to main shadow palette + +extern void shock_alloc_ipal(); + +errtype load_da_palette(void); + +void object_data_flush(void); +errtype object_data_load(void); +extern uchar objdata_loaded; + +#endif diff --git a/engine/src/GameSrc/Headers/input.h b/engine/src/GameSrc/Headers/input.h new file mode 100644 index 0000000..64aa7bb --- /dev/null +++ b/engine/src/GameSrc/Headers/input.h @@ -0,0 +1,88 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __INPUT_H +#define __INPUT_H + +#include "frtypesx.h" +#include "objects.h" + +// ------- +// DEFINES +// ------- + +#define NUM_HOTKEYS 50 // an arbitrary constant + +// Hotkey contexts +#define DEMO_CONTEXT 0x01 +#define EDIT_CONTEXT 0x02 +#define CYBER_CONTEXT 0x04 +#define SETUP_CONTEXT 0x08 +#define MWORK_CONTEXT 0x10 +#define SVGA_CONTEXT 0x20 +#define AMAP_CONTEXT 0x40 +#define EVERY_CONTEXT 0xFFFFFFFF + +// input modes +#define INPUT_NORMAL_CURSOR 0 +#define INPUT_OBJECT_CURSOR 1 + +#define INPUT_CHAINING +#define CHAINING_VAR "kb_chain" + +#define MAX_JUMP_CONTROL (CONTROL_MAX_VAL / 2) + +// ------ +// PROTOS +// ------ + +/** + * @deprecated does nothing + */ +void alloc_cursor_bitmaps(void); + +/** + * @deprecated does nothing + */ +void free_cursor_bitmaps(); + +void input_chk(void); +// uchar main_kb_callback(uiEvent *h, LGRegion *r, intptr_t udata); +void shutdown_input(void); +void init_input(void); +void install_motion_mouse_handler(LGRegion *r, frc *fr); +void install_motion_keyboard_handler(LGRegion *r); +void pop_cursor_object(void); +void push_cursor_object(short id); +void reload_motion_cursors(uchar cyber); +void reset_input_system(void); + +char *get_object_lookname(ObjID id, char use_string[], int sz); + +uchar check_object_dist(ObjID obj1, ObjID obj2, fix crit); + +void SetMotionCursorForMouseXY(void); + +uchar citadel_check_input(void); + +// ------- +// GLOBALS +// ------ +extern int input_cursor_mode; +extern short object_on_cursor; +#endif diff --git a/engine/src/GameSrc/Headers/invdims.h b/engine/src/GameSrc/Headers/invdims.h new file mode 100644 index 0000000..9dc9cde --- /dev/null +++ b/engine/src/GameSrc/Headers/invdims.h @@ -0,0 +1,62 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __INVDIMS_H +#define __INVDIMS_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/invdims.h $ + * $Revision: 1.5 $ + * $Author: mahk $ + * $Date: 1994/08/19 05:07:22 $ + * + * $Log: invdims.h $ + * Revision 1.5 1994/08/19 05:07:22 mahk + * added one to inventory panel size. + * + * Revision 1.4 1994/01/23 04:00:08 dc + * w+h for message line + * + * Revision 1.3 1993/11/12 19:44:13 mahk + * Changed dimensions for purposes of 360 view + * + * Revision 1.2 1993/09/18 03:55:12 mahk + * Added message line coords as well.c + * + * Revision 1.1 1993/09/17 16:59:25 mahk + * Initial revision + * + * + */ + +// INVENTORY PANEL DIMENSIONS + +#define GAME_MESSAGE_X 87 +#define GAME_MESSAGE_Y 138 +#define GAME_MESSAGE_W 145 +#define GAME_MESSAGE_H 7 + +#define INVENTORY_PANEL_X 87 +#define INVENTORY_PANEL_Y 145 +#define INVENTORY_PANEL_WIDTH 145 +#define INVENTORY_PANEL_HEIGHT 50 + +#define INV_FULL_WD (INVENTORY_PANEL_WIDTH + INVENTORY_PANEL_X - GAME_MESSAGE_X) +#define INV_FULL_HT (INVENTORY_PANEL_HEIGHT + INVENTORY_PANEL_Y - GAME_MESSAGE_Y) + +#endif // __INVDIMS_H diff --git a/engine/src/GameSrc/Headers/invent.h b/engine/src/GameSrc/Headers/invent.h new file mode 100644 index 0000000..dabbbcf --- /dev/null +++ b/engine/src/GameSrc/Headers/invent.h @@ -0,0 +1,105 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __INVENT_H +#define __INVENT_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/invent.h $ + * $Revision: 1.14 $ + * $Author: xemu $ + * $Date: 1994/07/18 21:31:31 $ + * + */ + +// Includes +#include "invdims.h" + +// C Library Includes + +// System Library Includes +#include "objects.h" + +// Master Game Includes + +// Game Library Includes + +// Game Object Includes + +#define MAX_GENERAL_INVENTORY 12 + +// Prototypes + +// creates and initializes the inventory region +LGRegion *create_invent_region(LGRegion *parent, LGRegion **pbuttons, LGRegion **pinvent); + +// Draw the inventory area. Keeps information on most recent draw so only does +// incremental updates. +errtype inventory_draw(void); + +// Force the inventory panel to draw, no matter what +errtype inventory_full_redraw(void); + +// switch the inventory page to pgnum and redraw +errtype inventory_draw_new_page(int pgnum); + +// clears the inventory region +errtype inventory_clear(void); + +// Add the appropriate kind of object to the player's inventory. Returns whether +// or not the action succeded (typical failure reason being not enough inventory +// slots remaining). +uchar inventory_add_object(ObjID new_object, uchar select); + +// Removes the specified object from the player's inventory. Does not do any +// correlation with the rest of the Universe -- this needs to be handed by +// the dropping/consuming/destroying code. +errtype inventory_remove_object(ObjID new_object); + +void draw_page_buttons(uchar full); + +void inv_change_fullscreen(uchar on); +void inv_update_fullscreen(uchar full); + +errtype inventory_update_screen_mode(); + +void push_inventory_cursors(LGCursor *newcurs); +void pop_inventory_cursors(void); + +void super_drop_func(int dispnum, int row); +void super_use_func(int dispnum, int row); + +void absorb_object_on_cursor(ushort keycode, uint32_t context, intptr_t data); + +uchar cycle_weapons_func(ushort keycode, uint32_t context, intptr_t data); + +void push_live_grenade_cursor(ObjID obj); + +void set_current_active(int activenum); + +void remove_general_item(ObjID obj); + +void add_email_datamunge(short mung, uchar select); + +void invent_language_change(void); + +// Globals +extern short inventory_page; +extern short inv_last_page; + +#endif // __INVENT_H diff --git a/engine/src/GameSrc/Headers/invpages.h b/engine/src/GameSrc/Headers/invpages.h new file mode 100644 index 0000000..c1eb326 --- /dev/null +++ b/engine/src/GameSrc/Headers/invpages.h @@ -0,0 +1,51 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __INVPAGES_H +#define __INVPAGES_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/invpages.h $ + * $Revision: 1.2 $ + * $Author: mahk $ + * $Date: 1994/07/28 23:07:14 $ + * + */ + +// ---------------------- +// INVENTORY PAGE NUMBERS +// ---------------------- + +#define INV_BLANK_PAGE -1 + +#define INV_MAIN_PAGE 0 +#define INV_HARDWARE_PAGE 1 +#define INV_GENERAL_PAGE 2 +#define INV_SOFTWARE_PAGE 5 +#define INV_LOG_MAIN_PAGE 7 +#define INV_DATA_PAGE 8 +#define INV_AMMO_PAGE 9 +#define INV_EMAIL0_PAGE 50 +#define INV_EMAIL1_PAGE 51 +#define INV_EMAIL2_PAGE 52 +#define INV_LOG0_PAGE 20 + +#define INV_3DVIEW_PAGE -5 +#define INV_EMAILTEXT_PAGE -10 + +#endif // __INVPAGES_H diff --git a/engine/src/GameSrc/Headers/leanmetr.h b/engine/src/GameSrc/Headers/leanmetr.h new file mode 100644 index 0000000..e4a365d --- /dev/null +++ b/engine/src/GameSrc/Headers/leanmetr.h @@ -0,0 +1,45 @@ +/* + +Copyright (C) 2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef LEANMETR_H +#define LEANMETR_H + +// --------- +// PROTOTYPES +// --------- +void set_base_lean_bmap(uchar shield); +fix compute_filter_weight(ulong deltat); +fix apply_weighted_filter(fix input, fix state, ulong deltat); +void slam_posture_meter_state(void); +fix velocity_crouch_filter(fix crouch); +void lean_icon(LGPoint *pos, grs_bitmap **icon, int *inum); +void player_reset_eye(void); +byte player_get_eye(void); +void player_set_eye_fixang(int ang); +int player_get_eye_fixang(void); +uchar eye_mouse_handler(uiEvent *ev, LGRegion *r, intptr_t); +uchar lean_mouse_handler(uiEvent *ev, LGRegion *r, intptr_t); +void init_posture_meters(LGRegion *root, uchar fullscreen); +void update_lean_meter(uchar force); +void draw_eye_bitmap(grs_bitmap *eye_bmap, LGPoint pos, int lasty); +void update_eye_meter(uchar force); +void update_meters(uchar force); +void zoom_to_lean_meter(void); + +#endif diff --git a/engine/src/GameSrc/Headers/loops.h b/engine/src/GameSrc/Headers/loops.h new file mode 100644 index 0000000..e43d46e --- /dev/null +++ b/engine/src/GameSrc/Headers/loops.h @@ -0,0 +1,27 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __LOOPS_H +#define __LOOPS_H + +#include "mainloop.h" +#include "gameloop.h" +#include "cybrloop.h" +#include "setploop.h" + +#endif diff --git a/engine/src/GameSrc/Headers/lvldata.h b/engine/src/GameSrc/Headers/lvldata.h new file mode 100644 index 0000000..e90aadd --- /dev/null +++ b/engine/src/GameSrc/Headers/lvldata.h @@ -0,0 +1,74 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __LVLDATA_H +#define __LVLDATA_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/lvldata.h $ + * $Revision: 1.6 $ + * $Author: tjs $ + * $Date: 1994/09/03 23:43:36 $ + * + * $Log: lvldata.h $ + * Revision 1.6 1994/09/03 23:43:36 tjs + * Automaps are in level_gamedata instead of being Malloc'd + * + * Revision 1.5 1994/08/11 18:31:40 mahk + * New enviro line + * + * Revision 1.4 1994/03/31 20:45:15 xemu + * zero grav for bio + * + * Revision 1.3 1994/02/08 02:00:21 mahk + * Added bio_h and rad_h + * + * Revision 1.2 1994/01/26 23:35:24 mahk + * New bio and rad regime. + * + * Revision 1.1 1993/11/19 05:30:36 mahk + * Initial revision + * + * + */ + +// Includes +#include "amap.h" // for our scheme to put automaps in here. + +// Game system data for each level. + +typedef struct _level_data { + short size; // size of this structure. + uchar mist; + uchar gravity; + struct _hazard { + uchar rad; + uchar bio; // post-exposure damage, or gravity level + uchar zerogbio; // if this is true, bio is interpreted as zero gravity + uchar bio_h; + uchar rad_h; + } hazard; + ulong exit_time; // timestamp at which we exited the level + curAMap auto_maps[NUM_O_AMAP]; +} LevelData; + +extern LevelData level_gamedata; + +#define OLD_LEVEL_GAMEDATA_SIZE 5 // for misc_saveload versions less than 5 + +#endif // __LVLDATA_H diff --git a/engine/src/GameSrc/Headers/mainloop.h b/engine/src/GameSrc/Headers/mainloop.h new file mode 100644 index 0000000..9b07bb7 --- /dev/null +++ b/engine/src/GameSrc/Headers/mainloop.h @@ -0,0 +1,95 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __MAINLOOP_H +#define __MAINLOOP_H + +#include "gameloop.h" +#include "frtypesx.h" + +#define QUIT_LOOP -1 +#define GAME_LOOP 0 +#define FULLSCREEN_LOOP 1 +#define EDIT_LOOP 2 +#define CYBER_LOOP 3 +#define SETUP_LOOP 4 +#define MWORK_LOOP 5 +#define CUTSCENE_LOOP 6 +#define SVGA_LOOP 7 +#define AUTOMAP_LOOP 8 + +#define ML 0x1000 +#define GL 0x1100 +#define EL 0x1200 +#define CL 0x1400 +#define SL 0x1800 +#define WL 0x2000 +#define FL 0x2100 +#define AL 0x2200 + +#define ML_CHG_MASK 0xF000u /* mask for main loop bits in change_flag */ +#define ML_CHG_BASE 0x1000u /* mask for single main loop bit of change_flag */ +#define LL_CHG_MASK 0x0FFFu /* mask for local loop bits of change_flag */ +#define LL_CHG_BASE 0x0001u /* mask for single local loop bit out of change_flag */ + +#define chg_set_flg(x) (_change_flag |= x) +#define chg_get_flg(x) (_change_flag & x) +#define chg_unset_flg(x) (_change_flag &= ~(x)) + +#define chg_set_sta(x) (_static_change |= x) +#define chg_get_sta(x) (_static_change & x) +#define chg_unset_sta(x) (_static_change &= ~(x)) + +#define GL_CHG_1 (ML_CHG_BASE << 0u) +#define GL_CHG_2 (ML_CHG_BASE << 1u) +#define GL_CHG_3 (ML_CHG_BASE << 2u) +#define GL_CHG_LOOP (ML_CHG_BASE << 3u) + +void mainloop(int argc, char *argv[]); +void loopmode_switch(short *cmode); +errtype static_change_copy(); +void loopmode_exit(short loopmode); +void loopmode_enter(short loopmode); + +extern short _current_loop; // which loop we currently are +extern short _current_3d_flag; +extern frc *_current_fr_context; +#ifdef GADGET +extern Gadget *_current_root; +#endif +extern uint _change_flag; // change flags for loop +extern uint _static_change; // current static changes +extern short _new_mode; // mode to change to, if any +extern short _last_mode; // last mode we were in, to switch back to +extern uchar player_invulnerable; +extern uchar player_immortal; +extern uchar physics_running; +extern uchar ai_on; +extern uchar anim_on; +extern uchar always_render; +extern uchar saves_allowed; +extern uchar time_passes; +extern uchar pal_fx_on; +extern LGRegion *_current_view; + +#define loopLine(num, code_line) code_line + +#define localChanges (_change_flag & LL_CHG_MASK) +#define globalChanges (_change_flag & ML_CHG_MASK) + +#endif diff --git a/engine/src/GameSrc/Headers/map.h b/engine/src/GameSrc/Headers/map.h new file mode 100644 index 0000000..e2f60dc --- /dev/null +++ b/engine/src/GameSrc/Headers/map.h @@ -0,0 +1,427 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __MAP_H +#define __MAP_H + +/* + * + * + * $Source: r:/prj/cit/src/inc/RCS/map.h $ + * $Revision: 1.41 $ + * $Author: minman $ + * $Date: 1994/08/28 02:38:51 $ + * + * $Log: map.h $ + * Revision 1.41 1994/08/28 02:38:51 minman + * hey / we have a correct macro for changing fixang to objang + * + * Revision 1.40 1994/07/21 16:28:25 dc + * im a moron, forgot to add reshifting to flags and all last night + * now ALL MUST PAY + * AND PAY + * BLOOD + * HAH HAH HAH HAH GURGLE + * + * Revision 1.39 1994/07/21 01:51:20 dc + * wanted to change some #defines + * + * Revision 1.38 1994/01/26 23:34:08 mahk + * Changed hazard bit stuff. + * + * Revision 1.37 1994/01/22 18:56:59 dc + * new map regieme, take 4 + * + * Revision 1.36 1994/01/02 17:17:09 dc + * indoor terrain renderer + * + * Revision 1.35 1993/10/18 03:35:49 dc + * adding dumb-o @ifndef __SPEW + * + * Revision 1.34 1993/09/08 00:59:21 xemu + * increase map number...EIT! + * sorry, had to do it for loved_textures + * + * Revision 1.33 1993/09/06 05:57:19 mahk + * Map has been packed. + * + * Revision 1.31 1993/09/05 21:01:52 dc + * added the great "pile of translations" + * soon tilenames will get taken out, but not yet + * + */ +#include "schedtyp.h" + +#define MAP_TYPES 32 +#define MAP_NUM_TMAP1 128 +#define MAP_NUM_TMAP2 32 +#define MAP_NUM_TMAP3 16 +#define MAP_HEIGHTS 32 +#define MAP_PARAMS 16 + +#define TMAP_FLR 0 +#define TMAP_WALL 1 +#define TMAP_CEIL 2 + +#define HGT_CEIL 0 +#define HGT_FLOOR 1 + +#define LITE_FLOOR 0 +#define LITE_CEIL 1 + +#define MAP_SCHEDULE_GAMETIME 0 +#define NUM_MAP_SCHEDULES 1 + +// CC: simplified savegames version +#define MAP_EASYSAVES_VERSION_NUMBER ((int)12) + +#define MAP_VERSION_NUMBER ((int)11) +// so we auto convert from it +#define OLD_MAP +#define OLD_MAP_VERSION_NUMBER ((int)10) + +// probably should kick it up to 16 bytes, expand one of the bitfields to three uchars instead +typedef struct _map_element { + // struct _bitfields + // { + // ushort tiletype:6; + // ushort flr_height:5; + // ushort ceil_height:5; + // }; + uchar tiletype; // 2 free bits + uchar flr_rotnhgt; // 1 free bit + uchar ceil_rotnhgt; // 1 free bit + uchar param; + short objRef; + // struct _lighting + // { + // uchar floor:4; + // uchar ceil:4; + // } templight; + ushort tmap_ccolor; + // union _space + // { + // struct _tmaps + // { + // ushort floor:5; + // ushort ceil:5; + // ushort wall:6; + // } real; + // struct _cybcolors + // { + // uchar floor; + // uchar ceil; + // } cyber; + // } space; + uchar flag1; // KLC swapped around + uchar flag2; + uchar flag3; + uchar flag4; // rename these, perhaps + // ulong flags; + uchar sub_clip; + uchar clearsolid; + uchar flick_qclip; + uchar templight; + // struct _render_info { + // uchar sub_clip; + // uchar clear; + // uchar rotflr:2; + // uchar rotceil:2; + // uchar flicker:4; + // } rinfo; +} MapElem; + +#ifdef OLD_MAP +typedef struct _omap_element { + //struct _bitfields { + // ushort tiletype : 6; + // ushort flr_height : 5; + // ushort ceil_height : 5; + //}; + uchar param; + struct _lighting { + uchar floor : 4; + uchar ceil : 4; + } templight; + union _space { + struct _tmaps { + ushort floor : 5; + ushort ceil : 5; + ushort wall : 6; + } real; + struct _cybcolors { + uchar floor; + uchar ceil; + } cyber; + } space; + ulong flags; + short objRef; + struct _render_info { + uchar sub_clip; + uchar clear; + uchar rotflr : 2; + uchar rotceil : 2; + uchar flicker : 4; + } rinfo; +} oMapElem; + +typedef struct { + int x_size, y_size; + int x_shft, y_shft, z_shft; + oMapElem *map; + uchar cyber; + int x_scale, y_scale, z_scale; + Schedule sched[NUM_MAP_SCHEDULES]; +} oFullMap; + +#define ome_tiletype(me_ptr) ((me_ptr)->tiletype) +#define ome_tmap_flr(me_ptr) ((me_ptr)->space.real.floor) +#define ome_tmap_wall(me_ptr) ((me_ptr)->space.real.wall) +#define ome_tmap_ceil(me_ptr) ((me_ptr)->space.real.ceil) +#define ome_tmap(me_ptr, idx) \ + (((idx) == TMAP_FLR) ? me_tmap_flr(me_ptr) : (((idx) == TMAP_CEIL) ? me_tmap_ceil(me_ptr) : me_tmap_wall(me_ptr))) +#define ome_objref(me_ptr) ((me_ptr)->objRef) +#define ome_flags(me_ptr) ((me_ptr)->flags) +#define ome_height_flr(me_ptr) ((me_ptr)->flr_height) +#define ome_height_ceil(me_ptr) ((me_ptr)->ceil_height) +#define ome_param(me_ptr) ((me_ptr)->param) +#define ome_height(me_ptr,idx) (((idx) == HGT_FLOOR) ? me_height_flr(me_ptr) : me_height_ceil(me_ptr)) +#define ome_cybcolor_flr(me_ptr) ((me_ptr)->space.cyber.floor) +#define ome_cybcolor_ceil(me_ptr) ((me_ptr)->space.cyber.ceil) +#define ome_templight_flr(me_ptr) ((me_ptr)->templight.floor) +#define ome_templight_ceil(me_ptr) ((me_ptr)->templight.ceil) +#define ome_subclip(me_ptr) ((me_ptr)->rinfo.sub_clip) +#define ome_clearsolid(me_ptr) ((me_ptr)->rinfo.clear) +#define ome_rotflr(me_ptr) ((me_ptr)->rinfo.rotflr) +#define ome_rotceil(me_ptr) ((me_ptr)->rinfo.rotceil) +#define ome_flicker(me_ptr) ((me_ptr)->rinfo.flicker) +#endif + +typedef struct { + int32_t x_size, y_size; + int32_t x_shft, y_shft, z_shft; + MapElem *map; + uchar cyber; + int32_t x_scale, y_scale, z_scale; + Schedule sched[NUM_MAP_SCHEDULES]; +} FullMap; + +#define _me_normal_x(me_ptr, strname, maskname) ((me_ptr)->strname & (maskname##_MASK)) +#define _me_normal_n(me_ptr, strname, maskname) (_me_normal_x(me_ptr, strname, maskname) >> maskname##_SHF) + +#define _me_tiletype(me_ptr) ((me_ptr)->tiletype) + +#define MAP_HGT_MASK 0x1f +#define MAP_HGT_SHF 0 +#define _me_height_flr(me_ptr) _me_normal_x(me_ptr, flr_rotnhgt, MAP_HGT) +#define _me_height_ceil(me_ptr) _me_normal_x(me_ptr, ceil_rotnhgt, MAP_HGT) +#define _me_height(me_ptr, idx) (((idx) == HGT_FLOOR) ? me_height_flr(me_ptr) : me_height_ceil(me_ptr)) + +#define _me_param(me_ptr) ((me_ptr)->param) +#define _me_objref(me_ptr) ((me_ptr)->objRef) + +#define MAP_TM_FLOOR_MASK 0xF800 +#define MAP_TM_FLOOR_SHF 11 +#define MAP_TM_CEIL_MASK 0x07C0 +#define MAP_TM_CEIL_SHF 6 +#define MAP_TM_WALL_MASK 0x003F +#define MAP_TM_WALL_SHF 0 +#define _me_tmap_flr_x(me_ptr) _me_normal_x(me_ptr, tmap_ccolor, MAP_TM_FLOOR) +#define _me_tmap_flr(me_ptr) _me_normal_n(me_ptr, tmap_ccolor, MAP_TM_FLOOR) +#define _me_tmap_ceil_x(me_ptr) _me_normal_x(me_ptr, tmap_ccolor, MAP_TM_CEIL) +#define _me_tmap_ceil(me_ptr) _me_normal_n(me_ptr, tmap_ccolor, MAP_TM_CEIL) +#define _me_tmap_wall_x(me_ptr) _me_normal_x(me_ptr, tmap_ccolor, MAP_TM_WALL) +#define _me_tmap_wall(me_ptr) _me_normal_n(me_ptr, tmap_ccolor, MAP_TM_WALL) +#define _me_tmap(me_ptr, idx) \ + (((idx) == TMAP_FLR) ? me_tmap_flr(me_ptr) : (((idx) == TMAP_CEIL) ? me_tmap_ceil(me_ptr) : me_tmap_wall(me_ptr))) +#define _me_cybcolor_flr(me_ptr) (*((uchar *)(&((me_ptr)->tmap_ccolor)))) +#define _me_cybcolor_ceil(me_ptr) (*((uchar *)(&((me_ptr)->tmap_ccolor)) + 1)) + +// note this returns a long of form f4f3f2f1, so flag4 is most significant, as it were +#define _me_flags(me_ptr) (*((ulong *)(&((me_ptr)->flag1)))) // KLC changed +#define _me_flag1(me_ptr) ((me_ptr)->flag1) +#define _me_flag2(me_ptr) ((me_ptr)->flag2) +#define _me_flag3(me_ptr) ((me_ptr)->flag3) +#define _me_flag4(me_ptr) ((me_ptr)->flag4) + +#define _me_subclip(me_ptr) ((me_ptr)->sub_clip) +#define _me_clearsolid(me_ptr) ((me_ptr)->clearsolid) + +#define MAP_ROT_MASK 0x60 +#define MAP_ROT_SHF 5 +#define _me_rotflr_x(me_ptr) _me_normal_x(me_ptr, flr_rotnhgt, MAP_ROT) +#define _me_rotflr(me_ptr) _me_normal_n(me_ptr, flr_rotnhgt, MAP_ROT) +#define _me_rotceil_x(me_ptr) _me_normal_x(me_ptr, ceil_rotnhgt, MAP_ROT) +#define _me_rotceil(me_ptr) _me_normal_n(me_ptr, ceil_rotnhgt, MAP_ROT) + +#define MAP_HAZARD_MASK 0x80 +#define MAP_HAZARD_SHF 7 +#define _me_hazard_bio_x(me_ptr) _me_normal_x(me_ptr, flr_rotnhgt, MAP_HAZARD) +#define _me_hazard_bio(me_ptr) _me_normal_n(me_ptr, flr_rotnhgt, MAP_HAZARD) +#define _me_hazard_rad_x(me_ptr) _me_normal_x(me_ptr, ceil_rotnhgt, MAP_HAZARD) +#define _me_hazard_rad(me_ptr) _me_normal_n(me_ptr, ceil_rotnhgt, MAP_HAZARD) + +#define MAP_FLICKER_MASK 0xF0 +#define MAP_FLICKER_SHF 4 +#define MAP_QUICKCLIP_MASK 0x0F +#define MAP_QUICKCLIP_SHF 0 +#define _me_flicker_x(me_ptr) _me_normal_x(me_ptr, flick_qclip, MAP_FLICKER) +#define _me_flicker(me_ptr) _me_normal_n(me_ptr, flick_qclip, MAP_FLICKER) +#define _me_quickclip_x(me_ptr) _me_normal_x(me_ptr, flick_qclip, MAP_QUICKCLIP) +#define _me_quickclip(me_ptr) _me_normal_n(me_ptr, flick_qclip, MAP_QUICKCLIP) + +#define MAP_TLGHT_CEIL_MASK 0xF0 +#define MAP_TLGHT_CEIL_SHF 4 +#define MAP_TLGHT_FLOOR_MASK 0x0F +#define MAP_TLGHT_FLOOR_SHF 0 +#define _me_templight_ceil_x(me_ptr) _me_normal_x(me_ptr, templight, MAP_TLGHT_CEIL) +#define _me_templight_ceil(me_ptr) _me_normal_n(me_ptr, templight, MAP_TLGHT_CEIL) +#define _me_templight_flr_x(me_ptr) _me_normal_x(me_ptr, templight, MAP_TLGHT_FLOOR) +#define _me_templight_flr(me_ptr) _me_normal_n(me_ptr, templight, MAP_TLGHT_FLOOR) + +// implicit me_ptr and v +#define _me_merge_set(me_ptr, v, strname, maskname) \ + ((me_ptr)->strname = ((me_ptr)->strname & ~(maskname##_MASK)) | ((v) << (maskname##_SHF))) + +// Now all the set primitives +#define _me_tiletype_set(me_ptr, v) ((me_ptr)->tiletype = (v)) +#define _me_height_flr_set(me_ptr, v) _me_merge_set(me_ptr, v, flr_rotnhgt, MAP_HGT) +#define _me_height_ceil_set(me_ptr, v) _me_merge_set(me_ptr, v, ceil_rotnhgt, MAP_HGT) +#define _me_height_set(me_ptr, idx, v) \ + (((idx) == HGT_FLOOR) ? me_height_flr_set(me_ptr, v) : me_height_ceil_set(me_ptr, v)) +#define _me_param_set(me_ptr, v) ((me_ptr)->param = (v)) +#define _me_objref_set(me_ptr, v) ((me_ptr)->objRef = (v)) +#define _me_tmap_flr_set(me_ptr, v) _me_merge_set(me_ptr, v, tmap_ccolor, MAP_TM_FLOOR) +#define _me_tmap_ceil_set(me_ptr, v) _me_merge_set(me_ptr, v, tmap_ccolor, MAP_TM_CEIL) +#define _me_tmap_wall_set(me_ptr, v) _me_merge_set(me_ptr, v, tmap_ccolor, MAP_TM_WALL) +#define _me_tmap_set(me_ptr, idx, v) \ + (((idx) == TMAP_FLR) ? me_tmap_flr_set(me_ptr, v) \ + : (((idx) == TMAP_CEIL) ? me_tmap_ceil_set(me_ptr, v) : me_tmap_wall_set(me_ptr, v))) +#define _me_cybcolor_flr_set(me_ptr, v) (_me_cybcolor_flr(me_ptr) = (v)) +#define _me_cybcolor_ceil_set(me_ptr, v) (_me_cybcolor_ceil(me_ptr) = (v)) +#define _me_flags_set(me_ptr, v) (_me_flags(me_ptr) = (v)) +#define _me_flag1_set(me_ptr, v) ((me_ptr)->flag1 = (v)) +#define _me_flag2_set(me_ptr, v) ((me_ptr)->flag2 = (v)) +#define _me_flag3_set(me_ptr, v) ((me_ptr)->flag3 = (v)) +#define _me_flag4_set(me_ptr, v) ((me_ptr)->flag4 = (v)) +#define _me_subclip_set(me_ptr, v) ((me_ptr)->sub_clip = (v)) +#define _me_clearsolid_set(me_ptr, v) ((me_ptr)->clearsolid = (v)) +#define _me_rotflr_set(me_ptr, v) _me_merge_set(me_ptr, v, flr_rotnhgt, MAP_ROT) +#define _me_rotceil_set(me_ptr, v) _me_merge_set(me_ptr, v, ceil_rotnhgt, MAP_ROT) +#define _me_hazard_bio_set(me_ptr, v) _me_merge_set(me_ptr, v, flr_rotnhgt, MAP_HAZARD) +#define _me_hazard_rad_set(me_ptr, v) _me_merge_set(me_ptr, v, ceil_rotnhgt, MAP_HAZARD) +#define _me_flicker_set(me_ptr, v) _me_merge_set(me_ptr, v, flick_qclip, MAP_FLICKER) +#define _me_quickclip_set(me_ptr, v) _me_merge_set(me_ptr, v, flick_qclip, MAP_QUICKCLIP) +#define _me_templight_ceil_set(me_ptr, v) _me_merge_set(me_ptr, v, templight, MAP_TLGHT_CEIL) +#define _me_templight_flr_set(me_ptr, v) _me_merge_set(me_ptr, v, templight, MAP_TLGHT_FLOOR) + +// bind the non _ version of the macros correctly +#ifdef MAP_ACCESS_COUNT +#include "mapcount.h" +#else +#include "mapnorm.h" +#endif + +extern MapElem *global_map; +extern FullMap *global_fullmap; + +FullMap *map_create(int xshf, int yshf, int zshf, uchar cyb); +uchar map_set_default(FullMap *fmap); +void map_init(void); +void map_free(void); + +#define DEFAULT_XSHF 6u +#define DEFAULT_YSHF 6u +#define DEFAULT_ZSHF 3u + +#ifndef MAP_RESIZING +#define fm_x_sz(fm_ptr) (1u << DEFAULT_XSHF) +#define fm_y_sz(fm_ptr) (1u << DEFAULT_YSHF) +#define fm_x_shft(fm_ptr) (DEFAULT_XSHF) +#define fm_y_shft(fm_ptr) (DEFAULT_YSHF) +#else +#define fm_x_sz(fm_ptr) ((fm_ptr)->x_size) +#define fm_y_sz(fm_ptr) ((fm_ptr)->y_size) +#define fm_x_shft(fm_ptr) ((fm_ptr)->x_shft) +#define fm_y_shft(fm_ptr) ((fm_ptr)->y_shft) +#endif + +#ifndef MAP_RESHIFTING +#define fm_z_shft(fm_ptr) (DEFAULT_ZSHF) +#else +#define fm_z_shft(fm_ptr) ((fm_ptr)->z_shft) +#endif + +// look, non stupid non hardcoded defines, how wacky! +#define MAP_YSIZE (fm_y_sz(global_fullmap)) +#define MAP_XSIZE (fm_x_sz(global_fullmap)) +#define MAP_YSHF (fm_y_shft(global_fullmap)) +#define MAP_XSHF (fm_x_shft(global_fullmap)) +#define MAP_ZSHF (fm_z_shft(global_fullmap)) + +#define fm_map(fm_ptr) ((fm_ptr)->map) +#define MAP_MAP (fm_map(global_fullmap)) + +#define FULLMAP_GET_XY(fmap, x, y) ((fmap)->map + (x) + ((y) << fm_x_shft(fmap))) +#define MAP_GET_XY(x, y) FULLMAP_GET_XY(global_fullmap, x, y) + +// who uses these? +#define MAP_ROWS 64 +#define MAP_COLS 64 + +#define SLOPE_TOTAL 5u +#define SLOPE_SHIFT MAP_ZSHF +#define SLOPE_SHIFT_U SLOPE_SHIFT +#define SLOPE_SHIFT_D (SLOPE_TOTAL - SLOPE_SHIFT_U) + +#define MAP_SC 256 +#define MAP_SH 8 +#define MAP_MK 0xff +#define MAP_MS 8 + +#ifdef SAFE_FIX +#define obj_coord_from_fix(fixval) ((fix_int((fixval)) & 0xFF) << 8) + (fix_frac((fixval)) >> 8) +#define obj_height_from_fix(fixval) (fix_int((fixval) * (1 << 8 - SLOPE_SHIFT_D))) +#define obj_angle_from_fix(fixval) (fix_int(fix_div((fixval), fix_2pi) * 255)) +#define obj_angle_from_fixang(fixval) (fix_div((fixval), FIXANG_PI) >> 9) +#define fix_from_obj_coord(sval) (fix_make((sval) >> 8, ((sval)&0xFF) << 8)) +#define fix_from_obj_height(oid) (ACK) +#define fix_from_obj_height_val(hval) (ACK) +#define fix_from_obj_angle(byteval) ((255 - fix_make(byteval, 0)) / 64) +#else +#define obj_coord_from_fix(fixval) ((int)(fixval) >> 8) +#define obj_height_from_fix(fixval) ((fixval) >> (8 + SLOPE_SHIFT_D)) +#define obj_angle_from_fix(fixval) (fix_int(fix_div((fixval), fix_2pi) * 255)) +#define obj_angle_from_fixang(fixval) (fix_div((fixval), FIXANG_PI) >> 9) +#define fix_from_obj_coord(sval) ((fix)(sval) << 8) +#define fix_from_obj_height(oid) ((fix)objs[(oid)].loc.z << (8u + SLOPE_SHIFT_D)) +#define fix_from_obj_height_val(hval) ((hval) << (8 + SLOPE_SHIFT_D)) +#define fix_from_obj_angle(byteval) ((255 - fix_make(byteval, 0)) / 64) +#endif + +#define fix_inv2pi (fix_make(0, 10430)) +#define obj_angle_from_phys(fixinrad) (64 - (((ushort)fix_div(fixinrad, fix_2pi)) >> 8)) +#define phys_angle_from_obj(citang) (fix_mul((64 - citang) << 16, fix_2pi) >> 8) +#define phys_angle_from_fixang(fang) (fixang_to_fixrad(FIXANG_PI/2 - (fang)) +#define fixang_from_phys_angle(fixdingus) (FIXANG_PI / 2 - fixrad_to_fixang(fixdingus)) + +// this stuff needs a safe fix version +#define fix_from_map_height(mht) (fix_make((mht), 0) >> SLOPE_SHIFT) +#define map_height_from_fix(fht) (fix_int(fht << SLOPE_SHIFT)) + +#endif // __MAP_H diff --git a/engine/src/GameSrc/Headers/mapflags.h b/engine/src/GameSrc/Headers/mapflags.h new file mode 100644 index 0000000..c1b1973 --- /dev/null +++ b/engine/src/GameSrc/Headers/mapflags.h @@ -0,0 +1,311 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __MAPFLAGS_H +#define __MAPFLAGS_H +/* + * $Source: r:/prj/cit/src/inc/RCS/mapflags.h $ + * $Revision: 1.16 $ + * $Author: minman $ + * $Date: 1994/07/30 00:18:26 $ + * + * $Log: mapflags.h $ + * Revision 1.16 1994/07/30 00:18:26 minman + * look rend3 settting + * + * Revision 1.15 1994/07/27 04:58:40 dc + * backdated, new set and stuff for family, some _set bug fixes + * + * Revision 1.14 1994/01/22 18:57:12 dc + * new map regieme, take 4 + * + * Revision 1.13 1994/01/02 17:17:14 dc + * Initial revision + * + * Revision 1.12 1993/10/21 16:42:10 mahk + * Added peril bit + * + * Revision 1.11 1993/10/18 03:35:50 dc + * adding dumb-o @ifndef __SPEW + * + * Revision 1.10 1993/09/09 03:22:01 dc + * render bits defines + * + * Revision 1.9 1993/09/06 05:56:56 mahk + * Broke up render bits into two chunks. + * + * Revision 1.8 1993/09/06 05:30:28 dc + * Yo Yo Yo flip defines, so on + * + * Revision 1.7 1993/09/06 01:58:14 mahk + * Consolidated 8 super-special rendering bits at the bottom. + * Moved mirror bits over finally. + * + * Revision 1.6 1993/09/02 23:07:52 xemu + * angle me baby + * + * Revision 1.5 1993/09/02 17:11:22 mahk + * Added fambly bit. + * + * Revision 1.4 1993/08/27 11:31:34 mahk + * Added another music bit. + * + * Revision 1.3 1993/08/22 17:50:55 xemu + * added to me_bits_deconst_set accessor + * + * Revision 1.2 1993/08/12 16:21:42 mahk + * Put in accessors. + * + * Revision 1.1 1993/08/12 16:11:39 mahk + * Initial revision + * + * + */ + +#include "map.h" + +#define OLD_MAP + +#ifdef OLD_MAP + +// for mirror bits, which control how the cieling and floor behave +#define OLD_MAP_MATCH 0 +#define OLD_MAP_MIRROR 1 +#define OLD_MAP_CFLAT 2 +#define OLD_MAP_FFLAT 3 +#define OLD_MIRROR_VALS 4 + +// for flip bits, which control texture left right mapping +#define OLD_MAP_FLIP_NOPE 0 +#define OLD_MAP_FLIP_YUP 1 +#define OLD_MAP_FLIP_ODD 2 +#define OLD_MAP_FLIP_EVEN 3 + +#define OLD_MAP_VLOCK_MASK 0x1F +#define OLD_MAP_VLOCK_SHF 0 + +#define OLD_MAP_FLIP_MASK 0x60 +#define OLD_MAP_FLIP_SHF 5 +#define OLD_MAP_FLIP_FNCY_MASK 0x40 // this is the bit which indicates a parity check is necessary +#define OLD_MAP_FLIP_PRTY_MASK 0x20 // this is the bit which actual has which parity it is +#define OLD_MAP_FLIP_PRTY_SHF MAP_FLIP_SHF + +#define OLD_MAP_FAMILY_MASK 0x80 +#define OLD_MAP_FAMILY_SHF 7 + +#define OLD_MAP_FAUXREND_MASK 0xFF + +#define OLD_MAP_MIRROR_MASK 0x300 +#define OLD_MAP_MIRROR_SHF 8 + +#define OLD_MAP_MUSIC_MASK 0x1C00 +#define OLD_MAP_MUSIC_SHF 10 + +#define OLD_MAP_PERIL_MASK 0x2000 +#define OLD_MAP_PERIL_SHF 13 + +#define OLD_MAP_DECONST_MASK 0x4000 +#define OLD_MAP_DECONST_SHF 14 + +#define OLD_MAP_FRIEND_MASK 0x8000 +#define OLD_MAP_FRIEND_SHF 15 + +#define OLD_MAP_F_LIGHT_MASK 0x0F0000 +#define OLD_MAP_F_LIGHT_SHF 16 + +#define OLD_MAP_C_LIGHT_MASK 0xF00000 +#define OLD_MAP_C_LIGHT_SHF 20 + +#define OLD_MAP_REND4_MASK 0x0F000000 +#define OLD_MAP_REND4_SHF 24 + +#define OLD_MAP_REND3_MASK 0x70000000 +#define OLD_MAP_REND3_SHF 28 + +#define OLD_MAP_REND_MASK (MAP_REND3_MASK | MAP_REND4_MASK) +#define OLD_MAP_REND_SHF 24 + +#define OLD_MAP_SEEN_MASK 0x80000000 +#define OLD_MAP_SEEN_SHF 31 +//#define OLD_MAP_SEEN_MASK 0x40000000 +//#define OLD_MAP_SEEN_SHF 30 + +#define OLD_MAP_MATCH_X (0 << OLD_MAP_MIRROR_SHF) +#define OLD_MAP_MIRROR_X (1 << OLD_MAP_MIRROR_SHF) +#define OLD_MAP_CFLAT_X (2 << OLD_MAP_MIRROR_SHF) +#define OLD_MAP_FFLAT_X (3 << OLD_MAP_MIRROR_SHF) + +#define ome_bits_vlock(me_ptr) ((ome_flags(me_ptr) & OLD_MAP_VLOCK_MASK) >> OLD_MAP_VLOCK_SHF) +#define ome_bits_flip(me_ptr) ((ome_flags(me_ptr) & OLD_MAP_FLIP_MASK) >> OLD_MAP_FLIP_SHF) +#define ome_bits_family(me_ptr) ((ome_flags(me_ptr) & OLD_MAP_FAMILY_MASK) >> OLD_MAP_FAMILY_SHF) +#define ome_bits_friend(me_ptr) ((ome_flags(me_ptr) & OLD_MAP_FRIEND_MASK) >> OLD_MAP_FRIEND_SHF) +#define ome_bits_mirror(me_ptr) ((ome_flags(me_ptr) & OLD_MAP_MIRROR_MASK) >> OLD_MAP_MIRROR_SHF) +#define ome_bits_mirror_x(me_ptr) (ome_flags(me_ptr) & OLD_MAP_MIRROR_MASK) +#define ome_bits_music(me_ptr) ((ome_flags(me_ptr) & OLD_MAP_MUSIC_MASK) >> OLD_MAP_MUSIC_SHF) +#define ome_bits_peril(me_ptr) ((ome_flags(me_ptr) & OLD_MAP_PERIL_MASK) >> OLD_MAP_PERIL_SHF) +#define ome_bits_hit(me_ptr) ((ome_flags(me_ptr) & OLD_MAP_HIT_MASK) >> OLD_MAP_HIT_SHF) +#define ome_bits_deconst(me_ptr) ((ome_flags(me_ptr) & OLD_MAP_DECONST_MASK) >> OLD_MAP_DECONST_SHF) +#define ome_bits_rend(me_ptr) ((ome_flags(me_ptr) & OLD_MAP_REND_MASK) >> OLD_MAP_REND_SHF) +#define ome_bits_rend4(me_ptr) ((ome_flags(me_ptr) & OLD_MAP_REND4_MASK) >> OLD_MAP_REND4_SHF) +#define ome_bits_rend3(me_ptr) ((ome_flags(me_ptr) & OLD_MAP_REND3_MASK) >> OLD_MAP_REND3_SHF) +#define ome_bits_fauxrend(me_ptr) (ome_flags(me_ptr) & OLD_MAP_FAUXREND_MASK) +#define ome_bits_seen_p(me_ptr) (ome_flags(me_ptr) & OLD_MAP_SEEN_MASK) + +#define ome_light_flr(me_ptr) ((ome_flags(me_ptr) & OLD_MAP_F_LIGHT_MASK) >> OLD_MAP_F_LIGHT_SHF) +#define ome_light_ceil(me_ptr) ((ome_flags(me_ptr) & OLD_MAP_C_LIGHT_MASK) >> OLD_MAP_C_LIGHT_SHF) +#define ome_light_flr_set(me_ptr, v) \ + ((me_ptr)->flags = (((me_ptr)->flags & ~OLD_MAP_F_LIGHT_MASK) | ((v) << OLD_MAP_F_LIGHT_SHF))) +#define ome_light_ceil_set(me_ptr, v) \ + ((me_ptr)->flags = (((me_ptr)->flags & ~OLD_MAP_C_LIGHT_MASK) | ((v) << OLD_MAP_C_LIGHT_SHF))) + +// hmm something like this perhaps +//#define ome_light_flr(me_ptr) ( (((uchar *)&(ome_flags(me_ptr)))+1) & 0x0f ) +//#define ome_light_ceil(me_ptr) ( (((uchar *)&(ome_flags(me_ptr)))+1) & 0xf0 ) + +#define ome_bits_rend_set(me_ptr, v) \ + (ome_flags_set(me_ptr, (ome_flags(me_ptr) & ~OLD_MAP_REND_MASK) | ((v) << OLD_MAP_REND_SHF))) +#define ome_bits_music_set(me_ptr, v) \ + (ome_flags_set(me_ptr, (ome_flags(me_ptr) & ~OLD_MAP_MUSIC_MASK) | ((v) << OLD_MAP_MUSIC_SHF))) +#define ome_bits_hit_set(me_ptr, v) \ + (ome_flags_set(me_ptr, (ome_flags(me_ptr) & ~OLD_MAP_HIT_MASK) | ((v) << OLD_MAP_HIT_SHF))) +#define ome_bits_deconst_set(me_ptr, v) \ + (ome_flags_set(me_ptr, (ome_flags(me_ptr) & ~OLD_MAP_DECONST_MASK) | ((v) << OLD_MAP_DECONST_SHF))) +#define ome_bits_mirror_set(me_ptr, v) \ + (ome_flags_set(me_ptr, (ome_flags(me_ptr) & ~OLD_MAP_MIRROR_MASK) | (v) << OLD_MAP_MIRROR_SHF)) +#define ome_bits_seen_set(me_ptr) (ome_flags(me_ptr) |= OLD_MAP_SEEN_MASK) +#define ome_bits_seen_clear(me_ptr) (ome_flags(me_ptr) &= ~OLD_MAP_SEEN_MASK) + +#endif + +// FLAG 1 +// for mirror bits, which control how the cieling and floor behave +#define MAP_MATCH 0 +#define MAP_MIRROR 1 +#define MAP_CFLAT 2 +#define MAP_FFLAT 3 +#define MIRROR_VALS 4 + +// for flip bits, which control texture left right mapping +#define MAP_FLIP_NOPE 0 +#define MAP_FLIP_YUP 1 +#define MAP_FLIP_ODD 2 +#define MAP_FLIP_EVEN 3 + +#define MAP_VLOCK_MASK 0x1F +#define MAP_VLOCK_SHF 0 + +#define MAP_FLIP_MASK 0x60 +#define MAP_FLIP_SHF 5 +#define MAP_FLIP_FNCY_MASK 0x40 // this is the bit which indicates a parity check is necessary +#define MAP_FLIP_PRTY_MASK 0x20 // this is the bit which actual has which parity it is +#define MAP_FLIP_PRTY_SHF MAP_FLIP_SHF + +#define MAP_FAMILY_MASK 0x80 +#define MAP_FAMILY_SHF 7 + +#define MAP_FAUXREND_MASK 0xFF + +// FLAG 2 +#define MAP_FRIEND_MASK 0x1 +#define MAP_FRIEND_SHF 0 +#define MAP_FRIEND_FULL_MASK (MAP_FRIEND_MASK << (MAP_FRIEND_SHF + 8)) + +#define MAP_DECONST_MASK 0x2 +#define MAP_DECONST_SHF 1 + +#define MAP_MIRROR_MASK 0xC +#define MAP_MIRROR_SHF 2 + +#define MAP_PERIL_MASK 0x10 +#define MAP_PERIL_SHF 4 + +#define MAP_MUSIC_MASK 0xE0 +#define MAP_MUSIC_SHF 5 + +// FLAG 3 +#define MAP_F_LIGHT_MASK 0x0F +#define MAP_F_LIGHT_SHF 0 + +#define MAP_REND4_MASK 0xF0 +#define MAP_REND4_SHF 4 + +// FLAG 4 +#define MAP_C_LIGHT_MASK 0xF +#define MAP_C_LIGHT_SHF 0 + +#define MAP_REND3_MASK 0x70 +#define MAP_REND3_SHF 4 + +#define MAP_SEEN_MASK 0x80 +#define MAP_SEEN_SHF 7 + +#define me_bits_vlock_x(me_ptr) (me_flag1(me_ptr) & MAP_VLOCK_MASK) +#define me_bits_vlock(me_ptr) (me_bits_vlock_x(me_ptr) >> MAP_VLOCK_SHF) +#define me_bits_flip_x(me_ptr) (me_flag1(me_ptr) & MAP_FLIP_MASK) +#define me_bits_flip(me_ptr) (me_bits_flip_x(me_ptr) >> MAP_FLIP_SHF) +#define me_bits_family_x(me_ptr) (me_flag1(me_ptr) & MAP_FAMILY_MASK) +#define me_bits_family(me_ptr) (me_bits_family_x(me_ptr) >> MAP_FAMILY_SHF) +#define me_bits_fauxrend me_flag1 + +#define me_bits_friend_x(me_ptr) (me_flag2(me_ptr) & MAP_FRIEND_MASK) +#define me_bits_friend(me_ptr) (me_bits_friend_x(me_ptr) >> MAP_FRIEND_SHF) +#define me_bits_mirror_x(me_ptr) (me_flag2(me_ptr) & MAP_MIRROR_MASK) +#define me_bits_mirror(me_ptr) (me_bits_mirror_x(me_ptr) >> MAP_MIRROR_SHF) +#define me_bits_music_x(me_ptr) (me_flag2(me_ptr) & MAP_MUSIC_MASK) +#define me_bits_music(me_ptr) (me_bits_music_x(me_ptr) >> MAP_MUSIC_SHF) +#define me_bits_peril_x(me_ptr) (me_flag2(me_ptr) & MAP_PERIL_MASK) +#define me_bits_peril(me_ptr) (me_bits_peril_x(me_ptr) >> MAP_PERIL_SHF) +#define me_bits_deconst_x(me_ptr) (me_flag2(me_ptr) & MAP_DECONST_MASK) +#define me_bits_deconst(me_ptr) (me_bits_deconst_x(me_ptr) >> MAP_DECONST_SHF) + +#define me_bits_rend4_x(me_ptr) (me_flag3(me_ptr) & MAP_REND4_MASK) +#define me_bits_rend4(me_ptr) (me_bits_rend4_x(me_ptr) >> MAP_REND4_SHF) +#define me_light_flr_x(me_ptr) (me_flag3(me_ptr) & MAP_F_LIGHT_MASK) +#define me_light_flr(me_ptr) (me_light_flr_x(me_ptr) >> MAP_F_LIGHT_SHF) + +#define me_bits_rend3_x(me_ptr) (me_flag4(me_ptr) & MAP_REND3_MASK) +#define me_bits_rend3(me_ptr) (me_bits_rend3_x(me_ptr) >> MAP_REND3_SHF) +#define me_bits_seen_x(me_ptr) (me_flag4(me_ptr) & MAP_SEEN_MASK) +#define me_bits_seen(me_ptr) (me_bits_seen_x(me_ptr) >> MAP_SEEN_SHF) +#define me_light_ceil_x(me_ptr) (me_flag4(me_ptr) & MAP_C_LIGHT_MASK) +#define me_light_ceil(me_ptr) (me_light_ceil_x(me_ptr) >> MAP_C_LIGHT_SHF) + +#define me_friend_set_x(me_ptr, v) (me_flag2_set(me_ptr, (me_flag2(me_ptr) & ~MAP_FRIEND_MASK) | (v))) +#define me_friend_set(me_ptr, v) (me_friend_set_x(me_ptr, (v) << MAP_FRIEND_SHF)) +#define me_family_set_x(me_ptr, v) (me_flag1_set(me_ptr, (me_flag1(me_ptr) & ~MAP_FAMILY_MASK) | (v))) +#define me_family_set(me_ptr, v) (me_family_set_x(me_ptr, (v) << MAP_FAMILY_SHF)) +#define me_flip_set_x(me_ptr, v) (me_flag1_set(me_ptr, (me_flag1(me_ptr) & ~MAP_FLIP_MASK) | (v))) +#define me_flip_set(me_ptr, v) (me_flip_set_x(me_ptr, (v) << MAP_FLIP_SHF)) + +#define me_rend3_set_x(me_ptr, v) (me_flag4_set(me_ptr, (me_flag4(me_ptr) & ~MAP_REND3_MASK) | (v))) +#define me_rend3_set(me_ptr, v) (me_rend3_set_x(me_ptr, (v) << MAP_REND3_SHF)) + +#define me_light_flr_set_x(me_ptr, v) (me_flag3_set(me_ptr, (me_flag3(me_ptr) & ~MAP_F_LIGHT_MASK) | (v))) +#define me_light_flr_set(me_ptr, v) (me_light_flr_set_x(me_ptr, (v) << MAP_F_LIGHT_SHF)) +#define me_light_ceil_set_x(me_ptr, v) (me_flag3_set(me_ptr, (me_flag3(me_ptr) & ~MAP_C_LIGHT_MASK) | (v))) +#define me_light_ceil_set(me_ptr, v) (me_light_ceil_set_x(me_ptr, (v) << MAP_C_LIGHT_SHF)) +#define me_bits_music_set_x(me_ptr, v) (me_flag2_set(me_ptr, (me_flag2(me_ptr) & ~MAP_MUSIC_MASK) | (v))) +#define me_bits_music_set(me_ptr, v) (me_bits_music_set_x(me_ptr, (v) << MAP_MUSIC_SHF)) +#define me_bits_deconst_set_x(me_ptr, v) (me_flag2_set(me_ptr, (me_flag2(me_ptr) & ~MAP_DECONST_MASK) | (v))) +#define me_bits_deconst_set(me_ptr, v) (me_bits_music_set_x(me_ptr, (v) << MAP_DECONST_SHF)) +#define me_bits_mirror_set_x(me_ptr, v) (me_flag1_set(me_ptr, (me_flag1(me_ptr) & ~MAP_MIRROR_MASK) | (v))) +#define me_bits_mirror_set(me_ptr, v) (me_bits_music_set_x(me_ptr, (v) << MAP_MIRROR_SHF)) +#define me_bits_seen_set(me_ptr) (me_flag4(me_ptr) |= MAP_SEEN_MASK) +#define me_bits_seen_clear(me_ptr) (me_flag4(me_ptr) &= ~MAP_SEEN_MASK) + +#endif // __MAPFLAGS_H diff --git a/engine/src/GameSrc/Headers/mapnorm.h b/engine/src/GameSrc/Headers/mapnorm.h new file mode 100644 index 0000000..cc63088 --- /dev/null +++ b/engine/src/GameSrc/Headers/mapnorm.h @@ -0,0 +1,94 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/cit/src/inc/RCS/mapnorm.h $ + * $Revision: 1.2 $ + * $Author: dc $ + * $Date: 1994/01/22 18:57:15 $ + */ + +#define me_tiletype _me_tiletype +#define me_tmap_flr _me_tmap_flr +#define me_tmap_wall _me_tmap_wall +#define me_tmap_ceil _me_tmap_ceil +#define me_tmap _me_tmap +#define me_objref _me_objref +#define me_flags _me_flags +#define me_height_flr _me_height_flr +#define me_height_ceil _me_height_ceil +#define me_param _me_param +#define me_height _me_height +#define me_cybcolor_flr _me_cybcolor_flr +#define me_cybcolor_ceil _me_cybcolor_ceil +#define me_templight_flr _me_templight_flr +#define me_templight_ceil _me_templight_ceil + +#define me_tiletype_set _me_tiletype_set +#define me_tmap_flr_set _me_tmap_flr_set +#define me_tmap_wall_set _me_tmap_wall_set +#define me_tmap_ceil_set _me_tmap_ceil_set +#define me_objref_set _me_objref_set +#define me_flags_set _me_flags_set +#define me_height_flr_set _me_height_flr_set +#define me_height_ceil_set _me_height_ceil_set +#define me_param_set _me_param_set +#define me_height_set _me_height_set +#define me_tmap_set _me_tmap_set +#define me_cybcolor_flr_set _me_cybcolor_flr_set +#define me_cybcolor_ceil_set _me_cybcolor_ceil_set +#define me_templight_flr_set _me_templight_flr_set +#define me_templight_ceil_set _me_templight_ceil_set + +#define me_subclip _me_subclip +#define me_clearsolid _me_clearsolid +#define me_rotflr _me_rotflr +#define me_rotceil _me_rotceil +#define me_flicker _me_flicker + +#define me_subclip_set _me_subclip_set +#define me_clearsolid_set _me_clearsolid_set +#define me_rotflr_set _me_rotflr_set +#define me_rotceil_set _me_rotceil_set +#define me_flicker_set _me_flicker_set + +#define me_flag1 _me_flag1 +#define me_flag2 _me_flag2 +#define me_flag3 _me_flag3 +#define me_flag4 _me_flag4 +#define me_rotflr_x _me_rotflr_x +#define me_rotceil_x _me_rotceil_x +#define me_hazard_bio_x _me_hazard_bio_x +#define me_hazard_bio _me_hazard_bio +#define me_hazard_rad_x _me_hazard_rad_x +#define me_hazard_rad _me_hazard_rad +#define me_tmap_flr_x _me_tmap_flr_x +#define me_tmap_ceil_x _me_tmap_ceil_x +#define me_tmap_wall_x _me_tmap_wall_x +#define me_flicker_x _me_flicker_x +#define me_quickclip_x _me_quickclip_x +#define me_quickclip _me_quickclip +#define me_templight_ceil_x _me_templight_ceil_x +#define me_templight_flr_x _me_templight_flr_x +#define me_flag1_set _me_flag1_set +#define me_flag2_set _me_flag2_set +#define me_flag3_set _me_flag3_set +#define me_flag4_set _me_flag4_set +#define me_hazard_bio_set _me_hazard_bio_set +#define me_hazard_rad_set _me_hazard_rad_set +#define me_quickclip_set _me_quickclip_set diff --git a/engine/src/GameSrc/Headers/mfdart.h b/engine/src/GameSrc/Headers/mfdart.h new file mode 100644 index 0000000..3d45891 --- /dev/null +++ b/engine/src/GameSrc/Headers/mfdart.h @@ -0,0 +1,123 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* This file created by RESTOOL */ + +#ifndef __MFDART_H +#define __MFDART_H + +#define RES_mfdArtOverlays 0x25 // (37) +#define RES_mfdSpecial 0x26 // (38) +#define REF_IMG_LitLamp0 0x260000 +#define REF_IMG_LitLamp1 0x260001 +#define REF_IMG_LitLamp2 0x260002 +#define REF_IMG_UnlitLamp0 0x260003 +#define REF_IMG_UnlitLamp1 0x260004 +#define REF_IMG_UnlitLamp2 0x260005 +#define REF_IMG_EmailButt0 0x260006 +#define REF_IMG_EmailButt1 0x260007 +#define REF_IMG_EmailButt2 0x260008 +#define REF_IMG_LitEmailButt0 0x260009 +#define REF_IMG_LitEmailButt1 0x26000a +#define REF_IMG_LitEmailButt2 0x26000b +#define REF_IMG_PrevPage 0x26000c +#define REF_IMG_Near 0x26000d +#define REF_IMG_NextPage 0x26000e +#define REF_IMG_LitShield0 0x26000f +#define REF_IMG_LitShield1 0x260010 +#define REF_IMG_LitShield2 0x260011 +#define REF_IMG_LitShieldSuper 0x260012 +#define REF_IMG_UnlitShield0 0x260013 +#define REF_IMG_UnlitShield1 0x260014 +#define REF_IMG_UnlitShield2 0x260015 +#define REF_IMG_UnlitShieldSuper 0x260016 +#define REF_IMG_UnlitMotion0 0x260017 +#define REF_IMG_UnlitMotion1 0x260018 +#define REF_IMG_UnlitMotion2 0x260019 +#define REF_IMG_LitMotion0 0x26001a +#define REF_IMG_LitMotion1 0x26001b +#define REF_IMG_LitMotion2 0x26001c +#define REF_IMG_BullFrame 0x26001d +#define REF_IMG_BullFrame2 0x26001e +#define REF_IMG_BullFrame3 0x26001f +#define REF_IMG_BullRightArrow 0x260020 +#define REF_IMG_BullLeftArrow 0x260021 +#define REF_IMG_NoAmmo 0x260022 +#define REF_IMG_Active 0x260023 +#define REF_IMG_Inactive 0x260024 +#define REF_IMG_Apply 0x260025 +#define REF_IMG_Activate 0x260026 +#define REF_IMG_BeamOverload 0x260027 +#define REF_IMG_BeamOverloadOn 0x260028 +#define REF_IMG_BeamOverloadOff 0x260029 +#define REF_IMG_BeamTemperature 0x26002a +#define REF_IMG_BeamSetting 0x26002b +#define REF_IMG_TargetButton 0x26002c +#define REF_IMG_DiscardButton 0x26002d +#define REF_IMG_BioIcon1 0x26002e +#define REF_IMG_BioIconNot 0x260036 +#define REF_IMG_TinyArrowUp 0x260037 +#define REF_IMG_TinyArrowDown 0x260038 +#define REF_IMG_QuestionCursor 0x260039 +#define REF_IMG_CircuitBack 0x26003a +#define REF_IMG_RookSymbol 0x26003b +#define REF_IMG_KingSymbol 0x26003c +#define REF_IMG_BishopSymbol 0x26003d +#define REF_IMG_QueenSymbol 0x26003e +#define REF_IMG_SimpleSymbol 0x26003f +#define REF_IMG_ViewIcon1 0x260040 +#define REF_IMG_Use 0x260043 +#define REF_IMG_GridHelpSwitch 0x260044 +#define REF_IMG_MFDButtonBack 0x260045 +#define REF_IMG_On 0x260046 +#define REF_IMG_Off 0x260047 +#define RES_GamesBitmaps 0x27 // (39) +#define REF_IMG_LittleGuy 0x270000 +#define REF_IMG_DepthCharge 0x270001 +#define REF_IMG_Destroyer 0x270002 +#define REF_IMG_RAservbot 0x270003 +#define REF_IMG_RAsec1bot 0x270004 +#define REF_IMG_RAhopper 0x270005 +#define REF_IMG_DiegoAnim15 0x270012 +#define REF_IMG_TriopLogo15 0x270016 +#define REF_IMG_ttt_Player 0x270017 +#define REF_IMG_ttt_Shodan 0x270018 +#define REF_IMG_GoofyNed 0x270019 +#define REF_IMG_wing_bad1 0x27001c +#define REF_IMG_wing_bad2 0x270022 +#define REF_IMG_wing_bad3 0x270028 +#define REF_IMG_wing_ship 0x27002e +#define RES_EmailMugShots 0x28 // (40) +#define REF_IMG_EmailMugShotBase 0x280000 +#define RES_mfdClass_1 0x29 // (41) +#define RES_mfdClass_2 0x2a // (42) +#define RES_mfdClass_3 0x2b // (43) +#define RES_mfdClass_4 0x2c // (44) +#define RES_mfdClass_5 0x2d // (45) +#define RES_mfdClass_6 0x2e // (46) +#define RES_mfdClass_7 0x2f // (47) +#define RES_mfdClass_8 0x30 // (48) +#define RES_mfdClass_9 0x31 // (49) +#define RES_mfdClass_10 0x32 // (50) +#define RES_mfdClass_11 0x33 // (51) +#define RES_mfdClass_12 0x34 // (52) +#define RES_mfdClass_13 0x35 // (53) +#define RES_mfdClass_14 0x36 // (54) +#define RES_mfdClass_15 0x37 // (55) + +#endif diff --git a/engine/src/GameSrc/Headers/mfddims.h b/engine/src/GameSrc/Headers/mfddims.h new file mode 100644 index 0000000..30e4fb5 --- /dev/null +++ b/engine/src/GameSrc/Headers/mfddims.h @@ -0,0 +1,52 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __MFDDIMS_H +#define __MFDDIMS_H + +/* + * $Source: n:/project/cit/src/inc/RCS/mfddims.h $ + * $Revision: 1.1 $ + * $Author: mahk $ + * $Date: 1993/09/17 16:59:35 $ + * + * $Log: mfddims.h $ + * Revision 1.1 1993/09/17 16:59:35 mahk + * Initial revision + * + * + */ + +// RELEVANT DIMENSIONS FOR MFD REGIONS +// Geography of the MFD view windows +#define MFD_VIEW_LFTX 8u +#define MFD_VIEW_RGTX 237u +#define MFD_VIEW_WID 74u +#define MFD_VIEW_Y 139u +#define MFD_VIEW_HGT 58u + +// Geography of the MFD button panels +#define MFD_BTTN_LFTX 0u +#define MFD_BTTN_RGTX 314u +#define MFD_BTTN_WID 6u +#define MFD_BTTN_Y 138u +#define MFD_BTTN_HGT 51u // 62 +#define MFD_BTTN_SZ 9u +#define MFD_BTTN_BLNK 2u + +#endif // __MFDDIMS_H diff --git a/engine/src/GameSrc/Headers/mfdext.h b/engine/src/GameSrc/Headers/mfdext.h new file mode 100644 index 0000000..0ebb65c --- /dev/null +++ b/engine/src/GameSrc/Headers/mfdext.h @@ -0,0 +1,132 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __MFDEXT_H +#define __MFDEXT_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/mfdext.h $ + * $Revision: 1.21 $ + * $Author: mahk $ + * $Date: 1994/08/28 04:41:22 $ + * + * + */ + +// Includes +#include "player.h" + +// --------- +// Constants +// --------- + +// These are for the inventory system (invent.c) +#define MFD_INV_NULL 0 +#define MFD_INV_DRUG 1 +#define MFD_INV_HARDWARE 2 +#define MFD_INV_GRENADE 3 +#define MFD_INV_AMMO 4 +#define MFD_INV_WEAPON 5 +#define MFD_INV_GENINV 6 +#define MFD_INV_SOFT_COMBAT 7 +#define MFD_INV_SOFT_DEFENSE 8 +#define MFD_INV_SOFT_MISC 9 +#define MFD_INV_CATEGORIES 10 + +#define MFD_INV_NOTYPE 0xFF + +// Which MFD? +#define MFD_LEFT 0 +#define MFD_RIGHT 1 + +#define ENCODE_MFD_SELECTION(side, no) ((side) * 100 + (no)) +#define DECODE_MFD_SELECTION(side, no, code) do { no = (code) % 100; side = (code) / 100; } while(0) + +// Flags for MFD Functions +#define MFD_CHANGEBIT 0x01 // Needs constant update of some sort +#define MFD_INCREMENTAL 0x02 // Uses standard MFD background +#define MFD_CHANGEBIT_FULL 0x04 +#define MFD_NOSAVEREST 0x08 // don't save/restore me. + +// Flags for Expose Control +#define MFD_EXPOSE 0x01u +#define MFD_EXPOSE_FULL 0x02u + +// Type of each MFD slot +#define MFD_WEAPON_SLOT 0 +#define MFD_ITEM_SLOT 1 +#define MFD_MAP_SLOT 2 +#define MFD_INFO_SLOT 4 +#define MFD_TARGET_SLOT 3 +#define MFD_SPECIAL_SLOT 5 + +// MFD Functions +#define NOTIFY_ANY_FUNC 0xFF // for mfd_notify_func +#define MFD_EMPTY_FUNC 0 +#define MFD_ITEM_FUNC 1 +#define MFD_MAP_FUNC 2 +#define MFD_TARGET_FUNC 3 +#define MFD_ANIM_FUNC 4 +#define MFD_WEAPON_FUNC 5 +#define MFD_BIOWARE_FUNC 6 +#define MFD_LANTERN_FUNC 7 +#define MFD_3DVIEW_FUNC 8 +#define MFD_ELEV_FUNC 9 +#define MFD_GRENADE_FUNC 10 +#define MFD_HUD_FUNC 11 +#define MFD_FIXTURE_FUNC 12 +#define MFD_KEYPAD_FUNC 13 +#define MFD_EMAILMUG_FUNC 14 +#define MFD_EMAILWARE_FUNC 15 +#define MFD_PLOTWARE_FUNC 16 +#define MFD_BARK_FUNC 17 +#define MFD_ACCESSPANEL_FUNC 18 +#define MFD_SHIELD_FUNC 19 +#define MFD_MOTION_FUNC 20 +#define MFD_SEVERED_HEAD_FUNC 21 +#define MFD_TARGETWARE_FUNC 22 +#define MFD_GUMP_FUNC 23 +#define MFD_CARD_FUNC 24 +#define MFD_BIOHELP_FUNC 25 +#define MFD_GRIDPANEL_FUNC 26 +#define MFD_GAMES_FUNC 27 +#define MFD_CSPACE_FUNC 28 +#define MFD_VIEWHELP_FUNC 29 +#define MFD_GEAR_FUNC 30 + +// ------- +// Externs +// ------- + +extern void set_inventory_mfd(ubyte l_class, ubyte type, uchar grab); +extern void init_newmfd(); +extern void screen_init_mfd(uchar fullscrn); +extern void screen_init_mfd_draw(); +extern void keyboard_init_mfd(); +extern void mfd_update(); +extern void mfd_notify_func(ubyte func, ubyte slot, uchar grab, MFD_Status stat, uchar FullRedraw); +extern void mfd_force_update_single(int which_mfd); +extern void mfd_force_update(); +extern int mfd_grab(void); +extern int mfd_grab_func(int my_func, int my_slot); +extern uchar mfd_yield_func(int func, int *mfd_id); +extern void mfd_change_slot(ubyte mfd_id, ubyte l_new); +extern void save_mfd_slot(int mfd_id); +extern void restore_mfd_slot(int mfd_id); + +#endif // __MFDEXT_H diff --git a/engine/src/GameSrc/Headers/mfdfunc.h b/engine/src/GameSrc/Headers/mfdfunc.h new file mode 100644 index 0000000..0f22fa7 --- /dev/null +++ b/engine/src/GameSrc/Headers/mfdfunc.h @@ -0,0 +1,43 @@ +/* + +Copyright (C) 2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef MFDFUNC_H +#define MFDFUNC_H + +#include "mfdint.h" + +void draw_mfd_item_spew(Ref id, int n); +char *level_to_floor(int lev_num, char *buf); +int mfd_bmap_id(int triple); +uchar mfd_distance_remove(ubyte slot_func); +void mfd_item_micro_expose(uchar full, int triple); + +void install_keypad_hotkeys(void); +void mfd_setup_keypad(char special); + +void update_item_mfd(void); +uchar mfd_target_qual(void); +uchar mfd_automap_qual(void); +uchar mfd_weapon_qual(void); +void weapon_mfd_for_reload(void); + +void mfd_setup_elevator(ushort levmask, ushort reachmask, ushort curlevel, uchar special); +void mfd_elevator_expose(MFD *mfd, ubyte control); + +#endif diff --git a/engine/src/GameSrc/Headers/mfdgadg.h b/engine/src/GameSrc/Headers/mfdgadg.h new file mode 100644 index 0000000..55eb324 --- /dev/null +++ b/engine/src/GameSrc/Headers/mfdgadg.h @@ -0,0 +1,94 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __MFDGADG_H +#define __MFDGADG_H + +/* + * $Source: n:/project/cit/src/inc/RCS/mfdgadg.h $ + * $Revision: 1.5 $ + * $Author: mahk $ + * $Date: 1994/02/16 09:27:14 $ + * + * $Log: mfdgadg.h $ + * Revision 1.5 1994/02/16 09:27:14 mahk + * Fixed prototype bug. + * + * Revision 1.4 1994/02/16 09:23:57 mahk + * Added more goofy operations. + * + * Revision 1.3 1993/12/08 10:31:59 mahk + * Changed to mfdint.h + * + * Revision 1.2 1993/10/20 05:47:23 mahk + * Added a slider gadget. + * + * Revision 1.1 1993/09/15 10:50:30 mahk + * Initial revision + * + * + */ + +// Includes +#include "mfdint.h" +#include "mfdext.h" +#include "mfddims.h" + +//-------------------------------------------------------------------------------- +// MFD GADGETS +// +// As the name suggests, this file is a set of simple gadgets usable in MFDs. + +// ------------------- +// BUTTON ARRAYS +// ------------------- + +// A button array is a matrix of buttons. MFD button arrays do not +// draw themselves, only handle input. + +// Defines + +typedef uchar (*MFDBttnCallback)(MFD *mfd, LGPoint button, uiEvent *ev, void *data); + +// Prototypes +errtype MFDBttnArrayInit(MFDhandler *h, LGRect *r, LGPoint bdims, LGPoint bsize, MFDBttnCallback cb, void *cbdata); +// Initialize h to handle a buttonarray in rect r of bdims.x buttons across by bdims.y buttons down. bsize +// describes the pixel dimensions of each button. Whenever a button is clicked on, cb will be called with the +// coordinates of the button, the mouse event, and the value of cbdata. + +errtype MFDBttnArrayShutdown(MFDhandler *h); +// shuts down a button array. + +errtype MFDBttnArrayResize(MFDhandler *h, LGRect *r, LGPoint bdims, LGPoint bsize); +// Changes the dimensions of an mfd button array. + +// Globals + +// ------------------ +// SLIDERS +// ------------------ + +// A slider is a linear "analog" control. + +typedef uchar (*MFDSliderCallback)(MFD *mfd, short val, uiEvent *ev, void *data); + +errtype MFDSliderInit(MFDhandler *h, LGRect *r, MFDSliderCallback cb, void *data); +// Create a (horizontal) slider in a particular sub-rect of the MFD. +// Whenever the slider is adjusted, the callbad will be called with the +// mouse event and the horizontal (relative) position of the slider. +#endif // __MFDGADG_H diff --git a/engine/src/GameSrc/Headers/mfdgames.h b/engine/src/GameSrc/Headers/mfdgames.h new file mode 100644 index 0000000..43c8b05 --- /dev/null +++ b/engine/src/GameSrc/Headers/mfdgames.h @@ -0,0 +1,32 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/cit/src/inc/RCS/mfdgames.h $ + * $Revision: 1.1 $ + * $Author: dc $ + * $Date: 1994/04/22 07:02:03 $ + */ + +// yo, word up +errtype mfd_games_init(MFD_Func *f); +uchar mfd_games_handler(MFD *m, uiEvent *e); +void mfd_games_expose(MFD *m, ubyte control); + +void mfd_games_turnon(uchar visible, uchar real_start); +void mfd_games_turnoff(uchar visible, uchar real_stop); diff --git a/engine/src/GameSrc/Headers/mfdgump.h b/engine/src/GameSrc/Headers/mfdgump.h new file mode 100644 index 0000000..8a1b039 --- /dev/null +++ b/engine/src/GameSrc/Headers/mfdgump.h @@ -0,0 +1,32 @@ +/* + +Copyright (C) 2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef MFDGUMP_H +#define MFDGUMP_H + +// ----------- +// PROTOTYPES +// ----------- +void gump_clear(void); +uchar gump_pickup(byte row); +uchar gump_get_useful(bool shifted); +void mfd_gump_expose(MFD *mfd, ubyte control); +uchar mfd_gump_handler(MFD *m, uiEvent *uie); + +#endif diff --git a/engine/src/GameSrc/Headers/mfdint.h b/engine/src/GameSrc/Headers/mfdint.h new file mode 100644 index 0000000..fe4b25e --- /dev/null +++ b/engine/src/GameSrc/Headers/mfdint.h @@ -0,0 +1,206 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __MFDINT_H +#define __MFDINT_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/mfdint.h $ + * $Revision: 1.12 $ + * $Author: tjs $ + * $Date: 1994/09/10 23:48:52 $ + * + * $Log: mfdint.h $ + * Revision 1.12 1994/09/10 23:48:52 tjs + * proto panel_ref_unexpose. + * + * Revision 1.11 1994/09/10 04:18:45 mahk + * panel ref unexpose + * + * Revision 1.10 1994/08/26 00:49:11 mahk + * new mfd_string_shadow regime. + * + * Revision 1.9 1994/04/25 01:43:28 xemu + * color for unavails + * + * Revision 1.8 1994/04/18 04:09:35 tjs + * Added level overlays for map. + * + * Revision 1.7 1994/03/31 02:06:08 mahk + * Changed proto. + * + * Revision 1.6 1994/02/15 11:07:42 mahk + * Fixed a stupid typo. + * + * Revision 1.5 1994/02/07 23:43:58 mahk + * Hey, maybe you'll need this to compile. + * + * Revision 1.4 1994/02/07 16:12:29 mahk + * Transparent mfd stuff. + * + * Revision 1.3 1993/12/15 13:52:39 mahk + * Added mfd_string wrapping bool + * + * Revision 1.2 1993/12/08 10:50:17 mahk + * Added some useful stuff for mfdfunc.c + * + * Revision 1.1 1993/12/08 10:32:09 mahk + * Initial revision + * + * + */ + +// Includes +#include "player.h" + +// ------ +// MACROS +// ------ + +#define visible_mask(m) ((m == MFD_LEFT) ? FULL_L_MFD_MASK : FULL_R_MFD_MASK) + +// --------- +// Constants +// --------- + +// Button colors +#define MFD_BTTN_EMPTY 0xcb +#define MFD_BTTN_ACTIVE 0xa2 +#define MFD_BTTN_FLASH 0x35 +#define MFD_BTTN_SELECT 0x77 +#define MFD_BTTN_UNAVAIL 0xdd + +// Duration of a button flash +#define MFD_BTTN_FLASH_TIME 256 + +// From the MFD art resource +#define MFD_ART_HUMAN 0 +#define MFD_ART_TRIOP 1 +#define MFD_ART_DRUGS 2 +#define MFD_ART_CHIPS 3 +#define MFD_ART_GRNDE 4 +#define MFD_ART_WEAPN 5 +#define MFD_ART_STATN 6 +#define MFD_ART_LVL(x) (MFD_ART_STATN + 1 + (x)) + +#define mfd_fdata player_struct.mfd_func_data +#define MFD_FONT RES_tinyTechFont + +extern ObjID panel_ref_unexpose(int mfd_id, int func); + +// ---------- +// Structures +// ---------- + +// An MFD points to an arbitrary number of slots, which can +// be shared by other MFD's. A slot points to a expose/handler +// function pair, which can be shared by multiple slots. + +#define NUM_MFD_HANDLERS 4 + +struct _mfd_handler; +struct _MFD; + +typedef uchar (*MFD_handlerProc)(struct _MFD *mfd, uiEvent *ev, struct _mfd_handler *mh); +typedef uchar (*MFD_SimpHandler)(struct _MFD *mfd, uiEvent *ev); + +typedef struct _mfd_handler { + LGRect r; // Sub-rect of MFD I'm handling, in relative coordinates. + MFD_handlerProc proc; // Proc we call when we get in this + // rect + void *data; // proc-specific state. +} MFDhandler; + +// A button's state depends on the state of its associated slot for its +// MFD. But: a button also keeps track of which is selected, but this +// is done through the code and not through the structure. + +typedef struct { // Button panel structure + LGRect rect; + LGRegion reg; + LGRegion reg2; +} MFD_bPanel; + +typedef struct _MFD { + ubyte id; + MFD_bPanel bttn; // The button panel + LGRect rect; + LGRegion reg; + LGRegion reg2; +} MFD; + +typedef struct _mfd_func { + // ubyte id; + void (*expose)(MFD *mfd, ubyte control); + MFD_SimpHandler simp; // Retained for compatibility. + errtype (*init)(struct _mfd_func *); + uchar priority; // one is highest, zero is infinitely low + // The following stuff is most likely to want to be zero. so nyah + ubyte flags; // Static func-specific info + long last; // Timestamp for incremental + int handler_count; + MFDhandler handlers[NUM_MFD_HANDLERS]; +} MFD_Func; + +extern void init_mfd_funcs(); +extern uchar mfd_view_callback_full(uiEvent *e, LGRegion *r, intptr_t udata); +extern uchar mfd_view_callback(uiEvent *e, LGRegion *r, intptr_t udata); +extern uchar mfd_button_callback(uiEvent *e, LGRegion *r, intptr_t udata); +extern uchar mfd_button_callback_kb(ushort keycode, uint32_t context, intptr_t data); +extern uchar mfd_update_current_slot(ubyte mfd_id, ubyte status, ubyte num_steps); +extern void mfd_init_funcs(); +extern void mfd_set_cliprect(LGRect *r); +extern void set_mfd_func(int fnum, void *e, void *h, void *initf, ubyte flags); +extern LGPoint mfd_draw_string(char *s, short x, short y, long c, uchar DrawString); +extern LGPoint mfd_draw_font_string(char *s, short x, short y, long c, int font, uchar DrawString); +extern LGPoint mfd_full_draw_string(char *s, short x, short y, long c, int font, uchar DrawString, uchar transp); +extern void set_slot_to_func(ubyte snum, ubyte fnum, MFD_Status stat); +extern void mfd_draw_bitmap(grs_bitmap *bmp, short x, short y); +extern void mfd_partial_clear(LGRect *r); +extern void init_newmfd_button_cursors(); +extern void mfd_update_display(MFD *m, short x0, short y0, short x1, short y1); +extern void mfd_clear_rects(void); +extern errtype mfd_add_rect(short x, short y, short x1, short y1); +extern void mfd_update_rects(MFD *m); +extern ubyte mfd_get_func(ubyte mfd_id, ubyte s); + +#define MFD_EXTRACT_BUF (get_free_frame_buffer_bits(-1)) + +// ------- +// GLOBALS +// ------- +extern MFD_Func mfd_funcs[MFD_NUM_FUNCS]; +extern uchar mfd_string_wrap; +extern ubyte mfd_string_shadow; + +#define MFD_SHADOW_NEVER 0 +#define MFD_SHADOW_ALWAYS 1 +#define MFD_SHADOW_FULLSCREEN 2 + +extern grs_bitmap mfd_background; +extern grs_canvas *pmfd_canvas; +extern uchar Flash; + +// ------ +// Macros +// ------ + +#define macro_region_create(parent, child, rect) \ + region_create(parent, child, rect, 0, 0, REG_USER_CONTROLLED, NULL, NULL, NULL, NULL) + +#endif // __MFDINT_H diff --git a/engine/src/GameSrc/Headers/mfdpanel.h b/engine/src/GameSrc/Headers/mfdpanel.h new file mode 100644 index 0000000..feaec80 --- /dev/null +++ b/engine/src/GameSrc/Headers/mfdpanel.h @@ -0,0 +1,204 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef MFDPANEL_H +#define MFDPANEL_H + +#include "diffq.h" +#include "faketime.h" +#include "mfdint.h" +#include "objects.h" +#include "player.h" + +char mfd_setup_accesspanel(uchar special, ObjID id); +char mfd_type_accesspanel(ObjID id); +uchar mfd_solve_accesspanel(ObjID id); + +#define EPICK_SOLVED 0 +#define EPICK_FAILED 1 +#define EPICK_PRESOL 2 + +// 1.5 seconds +#define EPICK_TIMEOUT (3 * CIT_CYCLE) / 2 + +// ------------------ +// ACCESS PANEL MFD +// ------------------ +// access panel internals +#define MAX_P_WIRES 6 +#define MAX_P_PINS 16 + +#define ACCESSP_BTN_ROW 6 +#define ACCESSP_BTN_COL 2 +#define ACCESSP_BTN_WD 8 +#define ACCESSP_BTN_HGT 7 +#define ACCESSP_BTN_X 9 +#define ACCESSP_BTN_Y 15 +#define ACCESSP_FULL_WD 56 +#define ACCESSP_FULL_HGT 42 + +#define ACCESSP_SCORE_X 4 +#define ACCESSP_SCORE_YT 3 +#define ACCESSP_SCORE_W 64 +#define ACCESSP_SCORE_YB 13 +#define ACCESSP_SCORE_SHF 2 +#define ACCESSP_SCORE_OFF 1 + +#define ACCESSP_SCORE_COL 0x5B +#define ACCESSP_SCORE_BOR 0x58 +#define ACCESSP_PIN_COL 0x43 +#define ACCESSP_CHIP_COL 0x40 +#define ACCESSP_HOT_COL 0x40 +#define ACCESSP_TARG_COL 0x35 + +#define get_delta(wpv) (wpv.lpos - wpv.rpos) + +#define PUZZLE_DIFFICULTY QUESTVAR_GET(PUZZLE_DIFF_QVAR) +#define MAX_DIFFICULTY 3 + +typedef struct { + uchar lpos; + uchar rpos; +} wirePos; + +typedef struct { + wirePos cur; + wirePos targ; +} wirePTrg; + +typedef struct { + uchar wirecnt; + uchar pincnt; + uchar scale; + uchar score; + wirePTrg wires[MAX_P_WIRES]; // internals for actual puzzle, 16 bytes + + uchar left_tap, right_tap; + + uchar last_score; // stuff for interface, mfd layer, so on + uchar tscore; // target score + uchar wires_moved; + uchar wire_in_motion; + uchar wim_tick; + uchar wim_shown; + uchar scorealg; + uchar special; + ObjID our_id; + uchar have_won; + uchar pad[3]; +} wirePosPuzzle; + +// WP_SCALE_SHF is the number of bits of fractional precision to be used in +// the scale field of a wirePosPuzzle (which is a uchar). Thus, if scale +// is to be a uchar, (256<. + +*/ +/* + * $Source: r:/prj/cit/src/inc/RCS/minimax.h $ + * $Revision: 1.1 $ + * $Author: tjs $ + * $Date: 1994/09/22 14:18:06 $ + * + * + */ + +void minimax_setup(void *boardpos, uint pos_siz, char depth, uchar minimize, int (*evaluator)(void *), + uchar (*generate)(void *, int, bool), uchar (*horizon)(void *)); +void minimax_step(void); +uchar minimax_done(void); +void minimax_get_result(int *value, char *which); +void fstack_init(uchar *fs, uint siz); diff --git a/engine/src/GameSrc/Headers/miscqvar.h b/engine/src/GameSrc/Headers/miscqvar.h new file mode 100644 index 0000000..885b927 --- /dev/null +++ b/engine/src/GameSrc/Headers/miscqvar.h @@ -0,0 +1,43 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Miscellaneous code-references quest variables + +#define MUSIC_VOLUME_QVAR 0x29 +#define GAMMACOR_QVAR 0x2a +#define SFX_VOLUME_QVAR 0x2b +#define MOUSEHAND_QVAR 0x2c +#define DCLICK_QVAR 0x2f +#define LANGUAGE_QVAR 0x30 +#define ALOG_VOLUME_QVAR 0x31 +#define SCREENMODE_QVAR 0x32 +#define JOYSENS_QVAR 0x33 +#define FULLSCRN_ICON_QVAR 0x34 +#define ALOG_OPT_QVAR 0x35 +#define FULLSCRN_VITAL_QVAR 0x36 +#define AMAP_NOTES_QVAR 0x37 +#define HUDCOLOR_QVAR 0x39 +#define DIGI_CHANNELS_QVAR 0x3A + +#define QVAR_TO_VOLUME(x) (long_sqrt(100 * (x))) +#define VOLUME_TO_QVAR(x) ((x) * (x) / 100) +#define QVAR_TO_GAMMA(x) (fix_mul((FIX_UNIT - fix_make(0, (x))), (FIX_UNIT - fix_make(0, (x)))) + FIX_UNIT / 2) + +#define QVAR_TO_DCLICK(v, t) ((((t) == 0) ? 30 : 100) * (USHRT_MAX + 3 * ((ulong)(v))) / (2 * USHRT_MAX)) + +#define QVAR_TO_JOYSENS(x) ((FIX_UNIT * 3 * (x) / (256 * 2)) + (FIX_UNIT / 4)) diff --git a/engine/src/GameSrc/Headers/mlimbs.h b/engine/src/GameSrc/Headers/mlimbs.h new file mode 100644 index 0000000..d780fa4 --- /dev/null +++ b/engine/src/GameSrc/Headers/mlimbs.h @@ -0,0 +1,133 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __MLIMBS_H +#define __MLIMBS_H +/* + * $Source: r:/prj/cit/src/inc/RCS/mlimbs.h $ + * $Revision: 1.14 $ + * $Author: dc $ + * $Date: 1994/11/19 20:44:53 $ + */ + + +// KLC what's the diff between a char and a signed char? typedef signed char schar; + +/* defines */ +#define MAX_SEQUENCES 80 +#define CALLBACK_SEQ_NUM 0 +#define HUGE_PRIORITY 10000 +#define MLIMBS_MAX_CHANNELS 8 +#define MLIMBS_MAX_SEQUENCES 8 +#define MLIMBS_TIMER_FREQUENCY 120 + +#define MLIMBS_STOPPED 0 +#define MLIMBS_PLAYING_PIECE 1 + +#define SEQUENCE_CHANNEL_PENDING -1 +#define SEQUENCE_CHANNEL_MUTED -2 +#define SEQUENCE_CHANNEL_UNUSED -3 + +#define DEFAULT_REL_VOL 100 +#define DEFAULT_RAMP_TIME 0 + +// referent to mlimbs generated sequences +#define MLIMBS_REF 0xFF0000 +#define mrefBuild(themeid, seq) (MLIMBS_REF | (themeid << 16) | seq) + +#define CALLBACK_ON + +/* XMIDI_info contains 'permanent' information about a given piece. */ +struct mlimbs_piece_info { + uchar max_voices; // Maximum number of voices this sequence uses at once. + uchar avg_voices; + ushort channel_map; // Bit map of what channels this sequence uses. + uchar num_measures; + int priority; // Base priority of this sequence. + uchar channel_voices[7]; // channel_voice[i] = # of voices channel i+11 uses in this sequence. +}; + +struct mlimbs_request_info { + int pieceID; // Indexes an array of XMIDI_info structs. Specifies which piece to play. + int priority; // Priority of this request. + int loops; // Number of loops. -1 => until deliberately stopped. + uint rel_vol; // Specifies at what relative volume to play it at. (percent) + uint ramp_time; // Specifies the time to ramp in to the specified rel_vol, or ramp out to 0. + int pan; // Note that this pan value affects all channels. + uchar channel_prioritize; + char crossfade; // 0 - don't crossfade, <0 - crossfade out, >0 - crossfade in. + char ramp; // 0 - don't ramp, <0 - ramp out >0 - ramp in + uchar pad; +}; + +struct mlimbs_channel_info { + int usernum; // Index into userID[] for the chunk currently using this channel + int sequence_channel; // For channels used by sequences, this field shows which of the + // sequences channels should be mapped to this channel. + int mchannel; // this channels existance + char status; // status can be one of the following +}; + +struct mlimbs_playing_info { + int pieceID; + ushort current_channel_map; + short sequence_channel_status[7]; // Status of the sequence channel, >= 0 is mlimbs physical channel + char seq_id; + uint rel_vol; + uchar channel_prioritize; + char crossfade_status; // <= 0 - no fade, >= 10 & <= 16, next channel to fade in or fade out. +}; + +extern volatile struct mlimbs_request_info current_request[MLIMBS_MAX_SEQUENCES - 1]; + +extern char mlimbs_status; // could make this one bitfield of status, on/off, enable/not, so on +extern uchar mlimbs_on; +extern volatile long mlimbs_error; +extern volatile uint default_rel_vol; +extern volatile uint default_ramp_time; +extern volatile uchar num_XMIDI_sequences; +extern volatile ulong mlimbs_counter; +extern volatile void (*mlimbs_AI)(); +extern volatile int mlimbs_master_slot; + +/* Function prototypes */ +int mlimbs_init(void); +//¥¥¥void cdecl mlimbs_callback(snd_midi_parms *mprm, int trigger_value); +//¥¥¥void cdecl mlimbs_seq_done_call(snd_midi_parms *mprm); + +void mlimbs_shutdown(void); +int mlimbs_load_theme(char *, char *, int); +void mlimbs_stop_theme(void); +int mlimbs_start_theme(void); +void mlimbs_purge_theme(void); +void mlimbs_mute_sequence_channel(int usernum, int x, bool); +int mlimbs_unmute_sequence_channel(int usernum, int x); +int mlimbs_channel_prioritize(int priority, int pieceID, int voices_needed, uchar crossfade, uchar channel_prioritize); +int mlimbs_assign_channels(int, bool); +int mlimbs_play_piece(int, int, int, int, bool, bool); +int mlimbs_punt_piece(int); +char mlimbs_get_crossfade_status(int); +void mlimbs_reassign_channels(void); +void mlimbs_timer_callback(void); +void mlimbs_change_master_volume(int); +void mlimbs_change_relative_volume(int, int, int); +void mlimbs_change_relative_tempo(int, int, int); +void mlimbs_return_to_synch(void); +void mlimbs_preload_full_timbres_and_go_asynch(void); +void mlimbs_preload_requested_timbres(void); +#endif // __MLIMBS_H diff --git a/engine/src/GameSrc/Headers/models.h b/engine/src/GameSrc/Headers/models.h new file mode 100644 index 0000000..8880162 --- /dev/null +++ b/engine/src/GameSrc/Headers/models.h @@ -0,0 +1,26 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include "obj3d.h" + +#define RES_MODEL_BASE 2300 +#define OBJ3D_BASE RES_object3d_0 + +#define get_model_data(model_num) RefLock(MKREF(RES_MODEL_BASE + (model_num), 0)) +#define release_model_data(model_num) RefUnlock(MKREF(RES_MODEL_BASE + (model_num), 0)) +#define model_valid(model_num) ResInUse(RES_MODEL_BASE + (model_num)) diff --git a/engine/src/GameSrc/Headers/modtext.h b/engine/src/GameSrc/Headers/modtext.h new file mode 100644 index 0000000..3af71f6 --- /dev/null +++ b/engine/src/GameSrc/Headers/modtext.h @@ -0,0 +1,25 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef MODTEXT_H +#define MODTEXT_H + +#define MAX_VTEXT_OBJS 51 + +extern char model_vtext_data[]; +#endif diff --git a/engine/src/GameSrc/Headers/mouselook.h b/engine/src/GameSrc/Headers/mouselook.h new file mode 100644 index 0000000..20ce68d --- /dev/null +++ b/engine/src/GameSrc/Headers/mouselook.h @@ -0,0 +1,14 @@ + + +// Prototypes + +void mouse_look_stop(); +void mouse_look_physics(); +void mouse_look_toggle(); +void mouse_look_off(); +void center_mouse(); +void mouse_look_unpause(); + +// Globals + +extern int mlook_vel_x, mlook_vel_y; diff --git a/engine/src/GameSrc/Headers/movekeys.h b/engine/src/GameSrc/Headers/movekeys.h new file mode 100644 index 0000000..1271692 --- /dev/null +++ b/engine/src/GameSrc/Headers/movekeys.h @@ -0,0 +1,88 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#define MAX_MOVE_KEYBINDS 256 + +#define CODE_Q 0x0C +#define CODE_W 0x0D +#define CODE_E 0x0E +#define CODE_A 0x00 +#define CODE_S 0x01 +#define CODE_D 0x02 +#define CODE_Z 0x06 +#define CODE_X 0x07 +#define CODE_C 0x08 +#define CODE_R 0x0F +#define CODE_V 0x09 +#define CODE_ENTER 0x24 +#define CODE_J 0x26 +#define CODE_SPACE 0x31 +#define CODE_UP 0x7E +#define CODE_LEFT 0x7B +#define CODE_RIGHT 0x7C +#define CODE_DOWN 0x7D +#define CODE_KP_ENTER 0x4C +#define CODE_KP_HOME 0x59 +#define CODE_KP_UP 0x5B +#define CODE_KP_PGUP 0x5C +#define CODE_KP_LEFT 0x56 +#define CODE_KP_5 0x57 +#define CODE_KP_RIGHT 0x58 +#define CODE_KP_END 0x53 +#define CODE_KP_DOWN 0x54 +#define CODE_KP_PGDN 0x55 + +typedef struct MOVE_KEYBIND_STRUCT {int code, move;} MOVE_KEYBIND; + +enum +{ + M_RUNFORWARD, + M_FORWARD, + M_FASTTURNLEFT, + M_TURNLEFT, + M_FASTTURNRIGHT, + M_TURNRIGHT, + M_BACK, + M_SLIDELEFT, + M_SLIDERIGHT, + M_JUMP, + M_LEANUP, + M_LEANLEFT, + M_LEANRIGHT, + M_LOOKUP, + M_LOOKDOWN, + M_RUNLEFT, + M_RUNRIGHT, + M_THRUST, //cyber start + M_CLIMB, + M_BANKLEFT, + M_BANKRIGHT, + M_DIVE, + M_ROLLRIGHT, + M_ROLLLEFT, + M_CLIMBLEFT, + M_CLIMBRIGHT, + M_DIVERIGHT, + M_DIVELEFT, + + NUM_MOVES +}; + +uchar motion_keycheck_handler(uiEvent *ev, LGRegion *r, intptr_t data); +void setup_motion_polling(void); +void process_motion_keys(void); diff --git a/engine/src/GameSrc/Headers/musicai.h b/engine/src/GameSrc/Headers/musicai.h new file mode 100644 index 0000000..a0f33d0 --- /dev/null +++ b/engine/src/GameSrc/Headers/musicai.h @@ -0,0 +1,170 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __MUSICAI_H +#define __MUSICAI_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/musicai.h $ + * $Revision: 1.28 $ + * $Author: dc $ + * $Date: 1994/11/19 20:44:54 $ + * + * + */ + +// Includes +#include "objects.h" + +// C Library Includes + +// System Library Includes + +// Master Game Includes + +// Game Library Includes + +// Game Object Includes + +// Defines +#define DEFAULT_PERIL_MAX 100 +#define DEFAULT_PERIL_MIN 1 +#define DEFAULT_POSIT_MAX 100 +#define DEFAULT_POSIT_MIN 1 +#define DEFAULT_MOTION_MAX 100 +#define DEFAULT_MOTION_MIN 1 + +#define NO_MUSIC_ZONE 0 +#define HOSPITAL_ZONE 1 // nee Residential +#define EXECUTIVE_ZONE 2 +#define INDUSTRIAL_ZONE 3 +#define METAL_ZONE 4 +#define PARK_ZONE 5 // also used in access corridors +#define BRIDGE_ZONE 6 // once Outer bridge +#define ELEVATOR_ZONE 7 + +#define NUM_EVENTS 9 + +#define WALKING_SCORE 0 +#define PERIL_SCORE 4 +#define COMBAT_SCORE 6 + +#define PERIL_THRESHOLD 70 + +#define NO_MONSTER -1 +#define SMALL_ROBOT 0 +#define LARGE_ROBOT 1 +#define MUTANT 2 + +#define SAME_MODE 0 +#define NORMAL_MODE 1 +#define TRANSITION_MODE 2 + +#define NUM_TRANSITIONS 9 + +#define TRANS_INTRO 0 +#define TRANS_WALK_TO_PERIL 1 +#define TRANS_PERIL_TO_COMB 2 +#define TRANS_DEATH 3 +#define TRANS_VICTORY 4 +#define TRANS_PERIL_TO_WALK 5 +#define TRANS_COMB_TO_WALK 6 +#define TRANS_COMB_TO_PERIL 7 +#define TRANS_WALK_TO_COMB 8 + +#define MONSTER_MUSIC_MUTANT 0 +#define MONSTER_MUSIC_ROBOT 1 +#define MONSTER_MUSIC_CYBORG 2 +#define MONSTER_MUSIC_SMALL_ROBOT 3 + +#define NUM_SCORES 8 +#define NUM_LAYERABLE_SUPERCHUNKS 22 +#define FIRST_SUPERCHUNK_LAYER 16 +#define NUM_LAYERS 32 +#define LAYER_BASE 32 + +#define SUPERCHUNKS_PER_SCORE 4 +#define MAX_KEYS 10 +#define KEY_BAR_RESOLUTION 2 + +#define NUM_PARK_SOUNDS 10 +#define PARK_LAYER_BASE 32 + +#define CYBERSPACE_SCORE_BASE 10 +#define NUM_NODE_THEMES 2 + +#define DANGER_LAYER_BASE 10 // actually one less than Danger1 since a minimum of 1 gets added to it... +#define SUCCESS_LAYER_BASE (DANGER_LAYER_BASE + 2) +#define DECONSTRUCT_LAYER 15 +#define TRANSITION_LAYER_BASE 16 + +// Prototypes + +// Initialize the AI portion of the MLIMBS system. +errtype mlimbs_AI_init(void); +void music_ai(void); +errtype musicai_shutdown(); +errtype musicai_reset(uchar runai); +int gen_monster(int monster_num); +void musicai_clear(); +errtype mai_monster_nearby(int monster_type); +errtype mai_attack(); +errtype mai_intro(); +errtype mai_monster_defeated(); +errtype mai_player_death(); +errtype mai_transition(int new_trans); + +errtype make_request(int chunk_num, int piece_ID); + +errtype fade_into_location(int x, int y); +errtype load_score_for_location(int x, int y); +errtype load_score_from_cfg(char *filename); +void load_score_guts(uint8_t score_playing); +errtype music_init(); +errtype digifx_init(); +errtype stop_digi_fx(); +void clear_digi_fx(); +int play_digi_fx_master(int sfx_code, int num_loops, ObjID id, ushort x, ushort y); +#define play_digi_fx(sfx_code, loops) play_digi_fx_master(sfx_code, loops, OBJ_NULL, 0, 0) +#define play_digi_fx_obj(sfx_code, num_loops, id) play_digi_fx_master(sfx_code, num_loops, id, 0, 0) +#define play_digi_fx_loc(sfx_code, num_loops, x, y) play_digi_fx_master(sfx_code, num_loops, OBJ_NULL, x, y) +errtype play_sound_effect(char *filename); +uchar digi_fx_playing(int fx_id, int *handle_ptr); +errtype output_text(char *); +extern void mlimbs_do_ai(void); +extern void digifx_EOS_callback(snd_digi_parms *sdp); + +extern uchar digi_pan_reverse; + +void grind_credits_music_ai(void); + +// Globals +extern int mlimbs_peril, mlimbs_positive, mlimbs_motion, mlimbs_monster; +extern ulong mlimbs_combat; +extern int current_score, current_zone, current_mode, random_flag; +extern int current_transition, last_score; +extern int boring_count; +extern int mlimbs_boredom; +extern int *output_table; +extern uchar wait_flag; +extern int next_mode, ai_cycle; +extern uchar music_card, music_on; +extern uchar /*sfx_card, */ sfx_on; +extern int cur_digi_channels; + +#endif // __MUSICAI_H diff --git a/engine/src/GameSrc/Headers/newmfd.h b/engine/src/GameSrc/Headers/newmfd.h new file mode 100644 index 0000000..c81b211 --- /dev/null +++ b/engine/src/GameSrc/Headers/newmfd.h @@ -0,0 +1,50 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __NEWMFD_H +#define __NEWMFD_H + +/* + * $Source: newmfd.h + + * $Revision: 1.3 + + * $Author: spaz + + * $Date: 7/13/93 + + * + */ + +// This file is now a placeholder. + +#include "mfdext.h" + +void cap_mfds_with_func(uchar func, uchar max); +void fullscreen_refresh_mfd(ubyte mfd_id); +void mfd_change_fullscreen(uchar on); +int mfd_choose_func(int my_func, int my_slot); +errtype mfd_clear_all(); +void mfd_draw_button_panel(ubyte mfd_id); +ubyte mfd_get_func(ubyte mfd_id, ubyte s); +uchar mfd_scan_opacity(int mfd_id, LGPoint epos); +errtype mfd_update_screen_mode(); +void mfd_zoom_rect(LGRect *start, int mfdnum); +void mfd_language_change(void); + +#endif // NEWMFD_H diff --git a/engine/src/GameSrc/Headers/obj3d.h b/engine/src/GameSrc/Headers/obj3d.h new file mode 100644 index 0000000..fba51bb --- /dev/null +++ b/engine/src/GameSrc/Headers/obj3d.h @@ -0,0 +1,185 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* This file created by RESTOOL */ + +#ifndef __OBJ3D_H +#define __OBJ3D_H + +#define RES_object3d_0 0x8fc // (2300) +#define REF_OBJ3D_MODEL000 0x8fc0000 +#define RES_object3d_1 0x8fd // (2301) +#define REF_OBJ3D_MODEL001 0x8fd0000 +#define RES_object3d_2 0x8fe // (2302) +#define REF_OBJ3D_MODEL002 0x8fe0000 +#define RES_object3d_3 0x8ff // (2303) +#define REF_OBJ3D_MODEL003 0x8ff0000 +#define RES_object3d_4 0x900 // (2304) +#define REF_OBJ3D_MODEL004 0x9000000 +#define RES_object3d_5 0x901 // (2305) +#define REF_OBJ3D_MODEL005 0x9010000 +#define RES_object3d_6 0x902 // (2306) +#define REF_OBJ3D_MODEL006 0x9020000 +#define RES_object3d_7 0x903 // (2307) +#define REF_OBJ3D_MODEL007 0x9030000 +#define RES_object3d_8 0x904 // (2308) +#define REF_OBJ3D_MODEL008 0x9040000 +#define RES_object3d_9 0x905 // (2309) +#define REF_OBJ3D_MODEL009 0x9050000 +#define RES_object3d_10 0x906 // (2310) +#define REF_OBJ3D_MODEL010 0x9060000 +#define RES_object3d_11 0x907 // (2311) +#define REF_OBJ3D_MODEL011 0x9070000 +#define RES_object3d_12 0x908 // (2312) +#define REF_OBJ3D_MODEL012 0x9080000 +#define RES_object3d_13 0x909 // (2313) +#define REF_OBJ3D_MODEL013 0x9090000 +#define RES_object3d_14 0x90a // (2314) +#define REF_OBJ3D_MODEL014 0x90a0000 +#define RES_object3d_15 0x90b // (2315) +#define REF_OBJ3D_MODEL015 0x90b0000 +#define RES_object3d_16 0x90c // (2316) +#define REF_OBJ3D_MODEL016 0x90c0000 +#define RES_object3d_17 0x90d // (2317) +#define REF_OBJ3D_MODEL017 0x90d0000 +#define RES_object3d_18 0x90e // (2318) +#define REF_OBJ3D_MODEL018 0x90e0000 +#define RES_object3d_19 0x90f // (2319) +#define REF_OBJ3D_MODEL019 0x90f0000 +#define RES_object3d_20 0x910 // (2320) +#define REF_OBJ3D_MODEL020 0x9100000 +#define RES_object3d_21 0x911 // (2321) +#define REF_OBJ3D_MODEL021 0x9110000 +#define RES_object3d_22 0x912 // (2322) +#define REF_OBJ3D_MODEL022 0x9120000 +#define RES_object3d_23 0x913 // (2323) +#define REF_OBJ3D_MODEL023 0x9130000 +#define RES_object3d_24 0x914 // (2324) +#define REF_OBJ3D_MODEL024 0x9140000 +#define RES_object3d_25 0x915 // (2325) +#define REF_OBJ3D_MODEL025 0x9150000 +#define RES_object3d_26 0x916 // (2326) +#define REF_OBJ3D_MODEL026 0x9160000 +#define RES_object3d_27 0x917 // (2327) +#define REF_OBJ3D_MODEL027 0x9170000 +#define RES_object3d_28 0x918 // (2328) +#define REF_OBJ3D_MODEL028 0x9180000 +#define RES_object3d_29 0x919 // (2329) +#define REF_OBJ3D_MODEL029 0x9190000 +#define RES_object3d_30 0x91a // (2330) +#define REF_OBJ3D_MODEL030 0x91a0000 +#define RES_object3d_31 0x91b // (2331) +#define REF_OBJ3D_MODEL031 0x91b0000 +#define RES_object3d_32 0x91c // (2332) +#define REF_OBJ3D_MODEL032 0x91c0000 +#define RES_object3d_33 0x91d // (2333) +#define REF_OBJ3D_MODEL033 0x91d0000 +#define RES_object3d_34 0x91e // (2334) +#define REF_OBJ3D_MODEL034 0x91e0000 +#define RES_object3d_35 0x91f // (2335) +#define REF_OBJ3D_MODEL035 0x91f0000 +#define RES_object3d_36 0x920 // (2336) +#define REF_OBJ3D_MODEL036 0x9200000 +#define RES_object3d_37 0x921 // (2337) +#define REF_OBJ3D_MODEL037 0x9210000 +#define RES_object3d_38 0x922 // (2338) +#define REF_OBJ3D_MODEL038 0x9220000 +#define RES_object3d_39 0x923 // (2339) +#define REF_OBJ3D_MODEL039 0x9230000 +#define RES_object3d_40 0x924 // (2340) +#define REF_OBJ3D_MODEL040 0x9240000 +#define RES_object3d_41 0x925 // (2341) +#define REF_OBJ3D_MODEL041 0x9250000 +#define RES_object3d_42 0x926 // (2342) +#define REF_OBJ3D_MODEL042 0x9260000 +#define RES_object3d_43 0x927 // (2343) +#define REF_OBJ3D_MODEL043 0x9270000 +#define RES_object3d_44 0x928 // (2344) +#define REF_OBJ3D_MODEL044 0x9280000 +#define RES_object3d_45 0x929 // (2345) +#define REF_OBJ3D_MODEL045 0x9290000 +#define RES_object3d_46 0x92a // (2346) +#define REF_OBJ3D_MODEL046 0x92a0000 +#define RES_object3d_47 0x92b // (2347) +#define REF_OBJ3D_MODEL047 0x92b0000 +#define RES_object3d_48 0x92c // (2348) +#define REF_OBJ3D_MODEL048 0x92c0000 +#define RES_object3d_49 0x92d // (2349) +#define REF_OBJ3D_MODEL049 0x92d0000 +#define RES_object3d_50 0x92e // (2350) +#define REF_OBJ3D_MODEL050 0x92e0000 +#define RES_object3d_51 0x92f // (2351) +#define REF_OBJ3D_MODEL051 0x92f0000 +#define RES_object3d_52 0x930 // (2352) +#define REF_OBJ3D_MODEL052 0x9300000 +#define RES_object3d_53 0x931 // (2353) +#define REF_OBJ3D_MODEL053 0x9310000 +#define RES_object3d_54 0x932 // (2354) +#define REF_OBJ3D_MODEL054 0x9320000 +#define RES_object3d_55 0x933 // (2355) +#define REF_OBJ3D_MODEL055 0x9330000 +#define RES_object3d_56 0x934 // (2356) +#define REF_OBJ3D_MODEL056 0x9340000 +#define RES_object3d_57 0x935 // (2357) +#define REF_OBJ3D_MODEL057 0x9350000 +#define RES_object3d_58 0x936 // (2358) +#define REF_OBJ3D_MODEL058 0x9360000 +#define RES_object3d_59 0x937 // (2359) +#define REF_OBJ3D_MODEL059 0x9370000 +#define RES_object3d_60 0x938 // (2360) +#define REF_OBJ3D_MODEL060 0x9380000 +#define RES_object3d_61 0x939 // (2361) +#define REF_OBJ3D_MODEL061 0x9390000 +#define RES_object3d_62 0x93a // (2362) +#define REF_OBJ3D_MODEL062 0x93a0000 +#define RES_object3d_63 0x93b // (2363) +#define REF_OBJ3D_MODEL063 0x93b0000 +#define RES_object3d_64 0x93c // (2364) +#define REF_OBJ3D_MODEL064 0x93c0000 +#define RES_object3d_65 0x93d // (2365) +#define REF_OBJ3D_MODEL065 0x93d0000 +#define RES_object3d_66 0x93e // (2366) +#define REF_OBJ3D_MODEL066 0x93e0000 +#define RES_object3d_67 0x93f // (2367) +#define REF_OBJ3D_MODEL067 0x93f0000 +#define RES_object3d_68 0x940 // (2368) +#define REF_OBJ3D_MODEL068 0x9400000 +#define RES_object3d_69 0x941 // (2369) +#define REF_OBJ3D_MODEL069 0x9410000 +#define RES_object3d_70 0x942 // (2370) +#define REF_OBJ3D_MODEL070 0x9420000 +#define RES_object3d_71 0x943 // (2371) +#define REF_OBJ3D_MODEL071 0x9430000 +#define RES_object3d_72 0x944 // (2372) +#define REF_OBJ3D_MODEL072 0x9440000 +#define RES_object3d_73 0x945 // (2373) +#define REF_OBJ3D_MODEL073 0x9450000 +#define RES_object3d_74 0x946 // (2374) +#define REF_OBJ3D_MODEL074 0x9460000 +#define RES_object3d_75 0x947 // (2375) +#define REF_OBJ3D_MODEL075 0x9470000 +#define RES_object3d_76 0x948 // (2376) +#define REF_OBJ3D_MODEL076 0x9480000 +#define RES_object3d_77 0x949 // (2377) +#define REF_OBJ3D_MODEL077 0x9490000 +#define RES_object3d_78 0x94a // (2378) +#define REF_OBJ3D_MODEL078 0x94a0000 +#define RES_object3d_79 0x94b // (2379) +#define REF_OBJ3D_MODEL079 0x94b0000 + +#endif diff --git a/engine/src/GameSrc/Headers/objapp.h b/engine/src/GameSrc/Headers/objapp.h new file mode 100644 index 0000000..3b7dc06 --- /dev/null +++ b/engine/src/GameSrc/Headers/objapp.h @@ -0,0 +1,308 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __OBJAPP_H +#define __OBJAPP_H + +#pragma pack(push,2) + +/* +** $Header: r:/prj/cit/src/inc/RCS/objapp.h 1.25 1994/08/30 07:15:21 xemu Exp $ +* +*/ + +////////////////////////////// +// +// An ObjClass is an enum encompassing all the different classes +// in the world. Be sure to set NUM_CLASSES and CLASS_FIRST +// correctly. +// +// An ObjRefState specifies the location of an ObjRef. +// +// It is made up of two parts: +// +// - ObjRefStateBin is used to choose where to put an ObjRef +// in the world-wide data structure. All ObjRefs with the +// same ObjRefStateBin are part of the same chain. +// +// - ObjRefStateInfo is extra information associated with an +// ObjRef's location. For example, in Freefall we specify +// here what rendering triangles an object overlaps. If +// you have no such information in your application, then +// #define NO_OBJ_REF_STATE_INFO. +// +// There must be a specific ObjRefStateBin which is "null". +// For example, it is used to terminate lists of ObjRefStateBins. +// The ObjRefStateBinSetNull and -CheckNull macros respectively +// set a bin to be null and check if it is null. +// +// An ObjLoc specifies the location of an Obj. +// An ObjInfo contains any extra information that you need to have associated +// with each and every object. +// +// #define HASH_OBJECTS if you want ObjRef chains to be hashed +// by location. You must then define the number of entries, and +// how many of those entries are accessible by the hashing function. +// (I recommend keeping these in the ratio 2:1). You must also write +// a macro (or function if you prefer) that computes a number n such that +// OBJ_HASH_HEAD_ENTRIES_START <= n < OBJ_HASH_ENTRIES, given an +// ObjRefStateBin. +// +// If you do not #define HASH_OBJECTS, you must write a macro that provides +// the head of the ObjRef chain for a given bin. (You are responsible for +// keeping a 2-dimensional array of bins, or whatever.) This must be a macro +// so that the object system can take the address of the result. + +////////////////////////////// HERE IS THE STUFF YOU MUST CHANGE +// // +// //////////////////////////////// + +//#include + +// #define HASH_OBJECTS +#define NO_OBJ_REF_STATE_INFO + +// enumeration of classes +// ## INSERT NEW CLASS HERE +// Note these have a fixed size and fixed values in the game files. They are +// as defined constants than an enum, and gcc does not like to make enums +// 8-bit values. +typedef uint8_t ObjClass; +#define CLASS_GUN 0 +#define CLASS_AMMO 1 +#define CLASS_PHYSICS 2 +#define CLASS_GRENADE 3 +#define CLASS_DRUG 4 +#define CLASS_HARDWARE 5 +#define CLASS_SOFTWARE 6 +#define CLASS_BIGSTUFF 7 +#define CLASS_SMALLSTUFF 8 +#define CLASS_FIXTURE 9 +#define CLASS_DOOR 10 +#define CLASS_ANIMATING 11 +#define CLASS_TRAP 12 +#define CLASS_CONTAINER 13 +#define CLASS_CRITTER 14 +#define NUM_CLASSES 15 +#define CLASS_FIRST CLASS_GUN + +// The total number of objects in the game, and of each type +// ## INSERT NEW CLASS HERE +// + +#define NUM_OBJECTS 872 +#define NUM_OBJECTS_GUN 16 +#define NUM_OBJECTS_AMMO 32 +#define NUM_OBJECTS_PHYSICS 32 +#define NUM_OBJECTS_GRENADE 32 +#define NUM_OBJECTS_DRUG 32 +#define NUM_OBJECTS_HARDWARE 8 +#define NUM_OBJECTS_SOFTWARE 16 +#define NUM_OBJECTS_BIGSTUFF 176 +#define NUM_OBJECTS_SMALLSTUFF 128 +#define NUM_OBJECTS_FIXTURE 64 +#define NUM_OBJECTS_DOOR 64 +#define NUM_OBJECTS_ANIMATING 32 +#define NUM_OBJECTS_TRAP 160 +#define NUM_OBJECTS_CONTAINER 64 +#define NUM_OBJECTS_CRITTER 64 + +// THe total number of references of objects +#define NUM_REF_OBJECTS 1600 + +// i hate cpp, no sizeof() in #if, so we have to do this +#define SIZEOF_AN_OBJREFSTATEBIN 4 +typedef struct { + LGPoint sq; +} ObjRefStateBin; + +#define OBJREF_SQ(ori) (objRefs[ori].state.bin.sq) + +// i hate cpp, no sizeof() in #if, so we have to do this +#define SIZEOF_AN_OBJREFSTATEINFO 1 +typedef struct { + uchar flags; +} ObjRefStateInfo; + +#define ObjRefStateBinSetNull(bin) PointSetNull((bin).sq) +#define ObjRefStateBinCheckNull(bin) (PointCheckNull((bin).sq)) + +// i hate cpp, no sizeof() in #if, so we have to do this +#define SIZEOF_AN_OBJLOC 8 +typedef struct { + ushort x, y; // high 8 bits: what square low 8 bits: where within square + ubyte z; + ubyte p, h, b; +} ObjLoc; + +#define OBJ_LOC_BIN_X(oloc) ((oloc).x >> 8u) +#define OBJ_LOC_BIN_Y(oloc) ((oloc).y >> 8u) +#define OBJ_LOC_FINE_X(oloc) ((ushort)((oloc).x & 0xFF00u)) +#define OBJ_LOC_FINE_Y(oloc) ((ushort)((oloc).y & 0xFF00u)) +#ifdef SAFE_FIX +#define OBJ_LOC_VAL_TO_FIX(value) (fix_make((value >> 8), ((value & 0xFF00) << 8))) +#else +#define OBJ_LOC_VAL_TO_FIX(value) (((fix)value) << 8) +#endif + +typedef struct { + char ph; + byte type; + short current_hp; + ubyte make_info; // maker, as in Zortech MK III laser rifle or whatever + ubyte current_frame; // animdata + ubyte time_remainder; // animdata + uchar inst_flags; // flags for instance data. right now 0x01 is used by Mahk's render tricks +} ObjInfo; + +typedef struct { + int ph; + byte type; + short current_hp; + ubyte make_info; // maker, as in Zortech MK III laser rifle or whatever + ubyte current_frame; // animdata + ubyte time_remainder; // animdata + uchar inst_flags; // flags for instance data. right now 0x01 is used by Mahk's render tricks +} old_ObjInfo; + +#ifdef HASH_OBJECTS +#define OBJ_HASH_ENTRIES 512 +#define OBJ_HASH_HEAD_ENTRIES 256 +#define OBJ_HASH_HEAD_ENTRIES_START (OBJ_HASH_ENTRIES - OBJ_HASH_HEAD_ENTRIES) +#define OBJ_HASH_FUNC(bin) \ + ((((((bin).sq.x) << 2) + ((bin).sq.y)) & (OBJ_HASH_HEAD_ENTRIES - 1)) + OBJ_HASH_HEAD_ENTRIES_START) +#define ObjRefHead(bin) (objHashTable[ObjGetHashElem((bin), FALSE)].ref) /* don't change this */ +#else +#define ObjRefHead(bin) (MAP_GET_XY((bin).sq.x, (bin).sq.y))->objRef +#endif + +// //////////////////////////////// +// // +////////////////////////////// WASN'T THAT EASY? + +typedef struct { + ObjRefStateBin bin; +} ObjRefState; + +typedef struct { + ObjRefStateBin bin; + ObjRefStateInfo info; +} oldObjRefState; + +// The following macros perform simple comparing and copying operations. +// If your structures are immensely complicated, you can turn them into +// functions. It will slow things down, though. + +// isnt it neat that you cant do sizeof(ObjRefStateBin) in a #if +// i love cpp with an unholy, inhuman, and altogether pathetic way + +#if (SIZEOF_AN_OBJREFSTATEBIN == 4) +#define ObjRefStateBinEqual(bin1, bin2) (*((int *)(&bin1)) == *((int *)(&bin2))) +#elif (SIZEOF_AN_OBJREFSTATEBIN == 2) +#define ObjRefStateBinEqual(bin1, bin2) (*((short *)(&bin1)) == *((short *)(&bin2))) +#elif (SIZEOF_AN_OBJREFSTATEBIN == 1) +#define ObjRefStateBinEqual(bin1, bin2) (*((char *)(&bin1)) == *((char *)(&bin2))) +#else +#define ObjRefStateBinEqual(bin1, bin2) (!memcmp(&(bin1), &(bin2), sizeof(ObjRefStateBin))) +#endif +#define ObjRefStateBinCopy(srcbin, dstbin) \ + do { \ + dstbin = srcbin; \ + } while (0) + +#ifndef NO_OBJ_REF_STATE_INFO +#if (SIZEOF_AN_OBJREFSTATEINFO == 4) +#define ObjRefStateInfoEqual(info1, info2) (*((int *)(&info1)) == *((int *)(&info2))) +#elif (SIZEOF_AN_OBJREFSTATEINFO == 2) +#define ObjRefStateInfoEqual(info1, info2) (*((short *)(&info1)) == *((short *)(&info2))) +#elif (SIZEOF_AN_OBJREFSTATEINFO == 1) +#define ObjRefStateInfoEqual(info1, info2) (*((char *)(&info1)) == *((char *)(&info2))) +#else +#define ObjRefStateInfoEqual(info1, info2) (!memcmp(&(info1), &(info2), sizeof(ObjRefStateInfo))) +#endif +#define ObjRefStateInfoCopy(srcinfo, dstinfo) \ + do { \ + dstinfo = srcinfo; \ + } while (0) +#endif + +#if (SIZEOF_AN_OBJLOC == 4) +#define ObjLocEqual(bin1, bin2) (*((int *)(&bin1)) == *((int *)(&bin2))) +#elif (SIZEOF_AN_OBJLOC == 2) +#define ObjLocEqual(bin1, bin2) (*((short *)(&bin1)) == *((short *)(&bin2))) +#elif (SIZEOF_AN_OBJLOC == 1) +#define ObjLocEqual(bin1, bin2) (*((char *)(&bin1)) == *((char *)(&bin2))) +#else +#define ObjLocEqual(bin1, bin2) (!memcmp(&(bin1), &(bin2), sizeof(ObjLoc))) +#endif +#define ObjLocCopy(srcbin, dstbin) \ + do { \ + dstbin = srcbin; \ + } while (0) + +////////////////////////////// MORE STUFF YOU MUST CHANGE +// // +// //////////////////////////////// +// +// You can turn some of the following macros into functions, if they get complicated. +// +// These are all for the use of the debugging system. They should print +// a user-friendly representation of the appropriate structure into str, +// without a trailing newline. + +#define ObjRefStateSprint(str, refstate) +#define ObjRefStateBinSprint(str, bin) +#define ObjRefStateInfoSprint(str, info) +#define ObjLocSprint(str, loc) +#define ObjInfoSprint(str, info) + +// //////////////////////////////// +// // +////////////////////////////// END OF STUFF YOU MUST CHANGE + +//////////////////////////////////////////////////////////// +// +// Here are prototypes of a few functions you should define in +// objapp.c. Any macros above that you decided to turn into functions +// should also be defined in objapp.c. +// + +void ObjInfoInit(ObjInfo *info); + +////////////////////////////// +// +// This should initialize the following iterator. Nice name, huh? + +void ObjRefStateBinIteratorInit(void); + +////////////////////////////// +// +// After ObjRefStateBinIteratorInit () has been called, calling this +// should put a new valid ObjRefStateBin in bin every time it is called. +// It returns FALSE if it has already returned all valid bins (in which +// case the value of bin is undefined); otherwise it returns TRUE, of +// course. +// +// It can use static variables; the iterator is guaranteed to be active +// only once at a time. + +uchar ObjRefStateBinIterator(ObjRefStateBin *bin); + +#pragma pack(pop) + +#endif // OBJAPP_H diff --git a/engine/src/GameSrc/Headers/objart.h b/engine/src/GameSrc/Headers/objart.h new file mode 100644 index 0000000..5e527db --- /dev/null +++ b/engine/src/GameSrc/Headers/objart.h @@ -0,0 +1,26 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* This file created by RESTOOL */ + +#ifndef __OBJART_H +#define __OBJART_H + +#define RES_bmObjectIcons 0x546 // (1350) + +#endif diff --git a/engine/src/GameSrc/Headers/objart2.h b/engine/src/GameSrc/Headers/objart2.h new file mode 100644 index 0000000..500008e --- /dev/null +++ b/engine/src/GameSrc/Headers/objart2.h @@ -0,0 +1,443 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* This file created by RESTOOL */ + +#ifndef __OBJART2_H +#define __OBJART2_H + +#define RES_bmCritterAttack_0 0x578 // (1400) +#define RES_bmCritterAttack_1 0x579 // (1401) +#define RES_bmCritterAttack_2 0x57a // (1402) +#define RES_bmCritterAttack_3 0x57b // (1403) +#define RES_bmCritterAttack_4 0x57c // (1404) +#define RES_bmCritterAttack_5 0x57d // (1405) +#define RES_bmCritterAttack_6 0x57e // (1406) +#define RES_bmCritterAttack_7 0x57f // (1407) +#define RES_bmCritterAttack_8 0x580 // (1408) +#define RES_bmCritterAttack_9 0x581 // (1409) +#define RES_bmCritterAttack_10 0x582 // (1410) +#define RES_bmCritterAttack_11 0x583 // (1411) +#define RES_bmCritterAttack_12 0x584 // (1412) +#define RES_bmCritterAttack_13 0x585 // (1413) +#define RES_bmCritterAttack_14 0x586 // (1414) +#define RES_bmCritterAttack_15 0x587 // (1415) +#define RES_bmCritterAttack_16 0x588 // (1416) +#define RES_bmCritterAttack_17 0x589 // (1417) +#define RES_bmCritterAttack_18 0x58a // (1418) +#define RES_bmCritterAttack_19 0x58b // (1419) +#define RES_bmCritterAttack_20 0x58c // (1420) +#define RES_bmCritterAttack_21 0x58d // (1421) +#define RES_bmCritterAttack_22 0x58e // (1422) +#define RES_bmCritterAttack_23 0x58f // (1423) +#define RES_bmCritterAttack_24 0x590 // (1424) +#define RES_bmCritterAttack_25 0x591 // (1425) +#define RES_bmCritterAttack_26 0x592 // (1426) +#define RES_bmCritterAttack_27 0x593 // (1427) +#define RES_bmCritterAttack_28 0x594 // (1428) +#define RES_bmCritterAttack_29 0x595 // (1429) +#define RES_bmCritterAttack_30 0x596 // (1430) +#define RES_bmCritterAttack_31 0x597 // (1431) +#define RES_bmCritterAttack_32 0x598 // (1432) +#define RES_bmCritterAttack_33 0x599 // (1433) +#define RES_bmCritterAttack_34 0x59a // (1434) +#define RES_bmCritterAttack_35 0x59b // (1435) +#define RES_bmCritterAttack_36 0x59c // (1436) +#define RES_bmCritterAttackRest_0 0x59d // (1437) +#define RES_bmCritterAttackRest_1 0x59e // (1438) +#define RES_bmCritterAttackRest_2 0x59f // (1439) +#define RES_bmCritterAttackRest_3 0x5a0 // (1440) +#define RES_bmCritterAttackRest_4 0x5a1 // (1441) +#define RES_bmCritterAttackRest_5 0x5a2 // (1442) +#define RES_bmCritterAttackRest_6 0x5a3 // (1443) +#define RES_bmCritterAttackRest_7 0x5a4 // (1444) +#define RES_bmCritterAttackRest_8 0x5a5 // (1445) +#define RES_bmCritterAttackRest_9 0x5a6 // (1446) +#define RES_bmCritterAttackRest_10 0x5a7 // (1447) +#define RES_bmCritterAttackRest_11 0x5a8 // (1448) +#define RES_bmCritterAttackRest_12 0x5a9 // (1449) +#define RES_bmCritterAttackRest_13 0x5aa // (1450) +#define RES_bmCritterAttackRest_14 0x5ab // (1451) +#define RES_bmCritterAttackRest_15 0x5ac // (1452) +#define RES_bmCritterAttackRest_16 0x5ad // (1453) +#define RES_bmCritterAttackRest_17 0x5ae // (1454) +#define RES_bmCritterAttackRest_18 0x5af // (1455) +#define RES_bmCritterAttackRest_19 0x5b0 // (1456) +#define RES_bmCritterAttackRest_20 0x5b1 // (1457) +#define RES_bmCritterAttackRest_21 0x5b2 // (1458) +#define RES_bmCritterAttackRest_22 0x5b3 // (1459) +#define RES_bmCritterAttackRest_23 0x5b4 // (1460) +#define RES_bmCritterAttackRest_24 0x5b5 // (1461) +#define RES_bmCritterAttackRest_25 0x5b6 // (1462) +#define RES_bmCritterAttackRest_26 0x5b7 // (1463) +#define RES_bmCritterAttackRest_27 0x5b8 // (1464) +#define RES_bmCritterAttackRest_28 0x5b9 // (1465) +#define RES_bmCritterAttackRest_29 0x5ba // (1466) +#define RES_bmCritterAttackRest_30 0x5bb // (1467) +#define RES_bmCritterAttackRest_31 0x5bc // (1468) +#define RES_bmCritterAttackRest_32 0x5bd // (1469) +#define RES_bmCritterAttackRest_33 0x5be // (1470) +#define RES_bmCritterAttackRest_34 0x5bf // (1471) +#define RES_bmCritterAttackRest_35 0x5c0 // (1472) +#define RES_bmCritterAttackRest_36 0x5c1 // (1473) +#define RES_bmCritterDeath_0 0x5c2 // (1474) +#define RES_bmCritterDeath_1 0x5c3 // (1475) +#define RES_bmCritterDeath_2 0x5c4 // (1476) +#define RES_bmCritterDeath_3 0x5c5 // (1477) +#define RES_bmCritterDeath_4 0x5c6 // (1478) +#define RES_bmCritterDeath_5 0x5c7 // (1479) +#define RES_bmCritterDeath_6 0x5c8 // (1480) +#define RES_bmCritterDeath_7 0x5c9 // (1481) +#define RES_bmCritterDeath_8 0x5ca // (1482) +#define RES_bmCritterDeath_9 0x5cb // (1483) +#define RES_bmCritterDeath_10 0x5cc // (1484) +#define RES_bmCritterDeath_11 0x5cd // (1485) +#define RES_bmCritterDeath_12 0x5ce // (1486) +#define RES_bmCritterDeath_13 0x5cf // (1487) +#define RES_bmCritterDeath_14 0x5d0 // (1488) +#define RES_bmCritterDeath_15 0x5d1 // (1489) +#define RES_bmCritterDeath_16 0x5d2 // (1490) +#define RES_bmCritterDeath_17 0x5d3 // (1491) +#define RES_bmCritterDeath_18 0x5d4 // (1492) +#define RES_bmCritterDeath_19 0x5d5 // (1493) +#define RES_bmCritterDeath_20 0x5d6 // (1494) +#define RES_bmCritterDeath_21 0x5d7 // (1495) +#define RES_bmCritterDeath_22 0x5d8 // (1496) +#define RES_bmCritterDeath_23 0x5d9 // (1497) +#define RES_bmCritterDeath_24 0x5da // (1498) +#define RES_bmCritterDeath_25 0x5db // (1499) +#define RES_bmCritterDeath_26 0x5dc // (1500) +#define RES_bmCritterDeath_27 0x5dd // (1501) +#define RES_bmCritterDeath_28 0x5de // (1502) +#define RES_bmCritterDeath_29 0x5df // (1503) +#define RES_bmCritterDeath_30 0x5e0 // (1504) +#define RES_bmCritterDeath_31 0x5e1 // (1505) +#define RES_bmCritterDeath_32 0x5e2 // (1506) +#define RES_bmCritterDeath_33 0x5e3 // (1507) +#define RES_bmCritterDeath_34 0x5e4 // (1508) +#define RES_bmCritterDeath_35 0x5e5 // (1509) +#define RES_bmCritterDeath_36 0x5e6 // (1510) +#define RES_bmCritterKnockback_0 0x5e7 // (1511) +#define RES_bmCritterKnockback_1 0x5e8 // (1512) +#define RES_bmCritterKnockback_2 0x5e9 // (1513) +#define RES_bmCritterKnockback_3 0x5ea // (1514) +#define RES_bmCritterKnockback_4 0x5eb // (1515) +#define RES_bmCritterKnockback_5 0x5ec // (1516) +#define RES_bmCritterKnockback_6 0x5ed // (1517) +#define RES_bmCritterKnockback_7 0x5ee // (1518) +#define RES_bmCritterKnockback_8 0x5ef // (1519) +#define RES_bmCritterKnockback_9 0x5f0 // (1520) +#define RES_bmCritterKnockback_10 0x5f1 // (1521) +#define RES_bmCritterKnockback_11 0x5f2 // (1522) +#define RES_bmCritterKnockback_12 0x5f3 // (1523) +#define RES_bmCritterKnockback_13 0x5f4 // (1524) +#define RES_bmCritterKnockback_14 0x5f5 // (1525) +#define RES_bmCritterKnockback_15 0x5f6 // (1526) +#define RES_bmCritterKnockback_16 0x5f7 // (1527) +#define RES_bmCritterKnockback_17 0x5f8 // (1528) +#define RES_bmCritterKnockback_18 0x5f9 // (1529) +#define RES_bmCritterKnockback_19 0x5fa // (1530) +#define RES_bmCritterKnockback_20 0x5fb // (1531) +#define RES_bmCritterKnockback_21 0x5fc // (1532) +#define RES_bmCritterKnockback_22 0x5fd // (1533) +#define RES_bmCritterKnockback_23 0x5fe // (1534) +#define RES_bmCritterKnockback_24 0x5ff // (1535) +#define RES_bmCritterKnockback_25 0x600 // (1536) +#define RES_bmCritterKnockback_26 0x601 // (1537) +#define RES_bmCritterKnockback_27 0x602 // (1538) +#define RES_bmCritterKnockback_28 0x603 // (1539) +#define RES_bmCritterKnockback_29 0x604 // (1540) +#define RES_bmCritterKnockback_30 0x605 // (1541) +#define RES_bmCritterKnockback_31 0x606 // (1542) +#define RES_bmCritterKnockback_32 0x607 // (1543) +#define RES_bmCritterKnockback_33 0x608 // (1544) +#define RES_bmCritterKnockback_34 0x609 // (1545) +#define RES_bmCritterKnockback_35 0x60a // (1546) +#define RES_bmCritterKnockback_36 0x60b // (1547) +#define RES_bmCritterDisrupt_0 0x60c // (1548) +#define RES_bmCritterDisrupt_1 0x60d // (1549) +#define RES_bmCritterDisrupt_2 0x60e // (1550) +#define RES_bmCritterDisrupt_3 0x60f // (1551) +#define RES_bmCritterDisrupt_4 0x610 // (1552) +#define RES_bmCritterDisrupt_5 0x611 // (1553) +#define RES_bmCritterDisrupt_6 0x612 // (1554) +#define RES_bmCritterDisrupt_7 0x613 // (1555) +#define RES_bmCritterDisrupt_8 0x614 // (1556) +#define RES_bmCritterDisrupt_9 0x615 // (1557) +#define RES_bmCritterDisrupt_10 0x616 // (1558) +#define RES_bmCritterDisrupt_11 0x617 // (1559) +#define RES_bmCritterDisrupt_12 0x618 // (1560) +#define RES_bmCritterDisrupt_13 0x619 // (1561) +#define RES_bmCritterDisrupt_14 0x61a // (1562) +#define RES_bmCritterDisrupt_15 0x61b // (1563) +#define RES_bmCritterDisrupt_16 0x61c // (1564) +#define RES_bmCritterDisrupt_17 0x61d // (1565) +#define RES_bmCritterDisrupt_18 0x61e // (1566) +#define RES_bmCritterDisrupt_19 0x61f // (1567) +#define RES_bmCritterDisrupt_20 0x620 // (1568) +#define RES_bmCritterDisrupt_21 0x621 // (1569) +#define RES_bmCritterDisrupt_22 0x622 // (1570) +#define RES_bmCritterDisrupt_23 0x623 // (1571) +#define RES_bmCritterDisrupt_24 0x624 // (1572) +#define RES_bmCritterDisrupt_25 0x625 // (1573) +#define RES_bmCritterDisrupt_26 0x626 // (1574) +#define RES_bmCritterDisrupt_27 0x627 // (1575) +#define RES_bmCritterDisrupt_28 0x628 // (1576) +#define RES_bmCritterDisrupt_29 0x629 // (1577) +#define RES_bmCritterDisrupt_30 0x62a // (1578) +#define RES_bmCritterDisrupt_31 0x62b // (1579) +#define RES_bmCritterDisrupt_32 0x62c // (1580) +#define RES_bmCritterDisrupt_33 0x62d // (1581) +#define RES_bmCritterDisrupt_34 0x62e // (1582) +#define RES_bmCritterDisrupt_35 0x62f // (1583) +#define RES_bmCritterDisrupt_36 0x630 // (1584) +#define RES_bmCritterStanding_0 0x631 // (1585) +#define RES_bmCritterStanding_1 0x632 // (1586) +#define RES_bmCritterStanding_2 0x633 // (1587) +#define RES_bmCritterStanding_3 0x634 // (1588) +#define RES_bmCritterStanding_4 0x635 // (1589) +#define RES_bmCritterStanding_5 0x636 // (1590) +#define RES_bmCritterStanding_6 0x637 // (1591) +#define RES_bmCritterStanding_7 0x638 // (1592) +#define RES_bmCritterStanding_8 0x639 // (1593) +#define RES_bmCritterStanding_9 0x63a // (1594) +#define RES_bmCritterStanding_10 0x63b // (1595) +#define RES_bmCritterStanding_11 0x63c // (1596) +#define RES_bmCritterStanding_12 0x63d // (1597) +#define RES_bmCritterStanding_13 0x63e // (1598) +#define RES_bmCritterStanding_14 0x63f // (1599) +#define RES_bmCritterStanding_15 0x640 // (1600) +#define RES_bmCritterStanding_16 0x641 // (1601) +#define RES_bmCritterStanding_17 0x642 // (1602) +#define RES_bmCritterStanding_18 0x643 // (1603) +#define RES_bmCritterStanding_19 0x644 // (1604) +#define RES_bmCritterStanding_20 0x645 // (1605) +#define RES_bmCritterStanding_21 0x646 // (1606) +#define RES_bmCritterStanding_22 0x647 // (1607) +#define RES_bmCritterStanding_23 0x648 // (1608) +#define RES_bmCritterStanding_24 0x649 // (1609) +#define RES_bmCritterStanding_25 0x64a // (1610) +#define RES_bmCritterStanding_26 0x64b // (1611) +#define RES_bmCritterStanding_27 0x64c // (1612) +#define RES_bmCritterStanding_28 0x64d // (1613) +#define RES_bmCritterStanding_29 0x64e // (1614) +#define RES_bmCritterStanding_30 0x64f // (1615) +#define RES_bmCritterStanding_31 0x650 // (1616) +#define RES_bmCritterStanding_32 0x651 // (1617) +#define RES_bmCritterStanding_33 0x652 // (1618) +#define RES_bmCritterStanding_34 0x653 // (1619) +#define RES_bmCritterStanding_35 0x654 // (1620) +#define RES_bmCritterStanding_36 0x655 // (1621) +#define RES_bmCritterStanding_37 0x656 // (1622) +#define RES_bmCritterStanding_38 0x657 // (1623) +#define RES_bmCritterStanding_39 0x658 // (1624) +#define RES_bmCritterStanding_40 0x659 // (1625) +#define RES_bmCritterStanding_41 0x65a // (1626) +#define RES_bmCritterStanding_42 0x65b // (1627) +#define RES_bmCritterStanding_43 0x65c // (1628) +#define RES_bmCritterStanding_44 0x65d // (1629) +#define RES_bmCritterStanding_45 0x65e // (1630) +#define RES_bmCritterStanding_46 0x65f // (1631) +#define RES_bmCritterStanding_47 0x660 // (1632) +#define RES_bmCritterStanding_48 0x661 // (1633) +#define RES_bmCritterStanding_49 0x662 // (1634) +#define RES_bmCritterStanding_50 0x663 // (1635) +#define RES_bmCritterStanding_51 0x664 // (1636) +#define RES_bmCritterStanding_52 0x665 // (1637) +#define RES_bmCritterStanding_53 0x666 // (1638) +#define RES_bmCritterStanding_54 0x667 // (1639) +#define RES_bmCritterStanding_55 0x668 // (1640) +#define RES_bmCritterStanding_56 0x669 // (1641) +#define RES_bmCritterStanding_57 0x66a // (1642) +#define RES_bmCritterStanding_58 0x66b // (1643) +#define RES_bmCritterStanding_59 0x66c // (1644) +#define RES_bmCritterStanding_60 0x66d // (1645) +#define RES_bmCritterStanding_61 0x66e // (1646) +#define RES_bmCritterStanding_62 0x66f // (1647) +#define RES_bmCritterStanding_63 0x670 // (1648) +#define RES_bmCritterStanding_64 0x671 // (1649) +#define RES_bmCritterStanding_65 0x672 // (1650) +#define RES_bmCritterStanding_66 0x673 // (1651) +#define RES_bmCritterStanding_67 0x674 // (1652) +#define RES_bmCritterStanding_68 0x675 // (1653) +#define RES_bmCritterStanding_69 0x676 // (1654) +#define RES_bmCritterStanding_70 0x677 // (1655) +#define RES_bmCritterStanding_71 0x678 // (1656) +#define RES_bmCritterStanding_72 0x679 // (1657) +#define RES_bmCritterStanding_73 0x67a // (1658) +#define RES_bmCritterStanding_74 0x67b // (1659) +#define RES_bmCritterStanding_75 0x67c // (1660) +#define RES_bmCritterStanding_76 0x67d // (1661) +#define RES_bmCritterStanding_77 0x67e // (1662) +#define RES_bmCritterStanding_78 0x67f // (1663) +#define RES_bmCritterStanding_79 0x680 // (1664) +#define RES_bmCritterStanding_80 0x681 // (1665) +#define RES_bmCritterStanding_81 0x682 // (1666) +#define RES_bmCritterStanding_82 0x683 // (1667) +#define RES_bmCritterStanding_83 0x684 // (1668) +#define RES_bmCritterStanding_84 0x685 // (1669) +#define RES_bmCritterStanding_85 0x686 // (1670) +#define RES_bmCritterStanding_86 0x687 // (1671) +#define RES_bmCritterStanding_87 0x688 // (1672) +#define RES_bmCritterStanding_88 0x689 // (1673) +#define RES_bmCritterStanding_89 0x68a // (1674) +#define RES_bmCritterStanding_90 0x68b // (1675) +#define RES_bmCritterStanding_91 0x68c // (1676) +#define RES_bmCritterStanding_92 0x68d // (1677) +#define RES_bmCritterStanding_93 0x68e // (1678) +#define RES_bmCritterStanding_94 0x68f // (1679) +#define RES_bmCritterStanding_95 0x690 // (1680) +#define RES_bmCritterStanding_96 0x691 // (1681) +#define RES_bmCritterStanding_97 0x692 // (1682) +#define RES_bmCritterStanding_98 0x693 // (1683) +#define RES_bmCritterStanding_99 0x694 // (1684) +#define RES_bmCritterStanding_100 0x695 // (1685) +#define RES_bmCritterStanding_101 0x696 // (1686) +#define RES_bmCritterStanding_102 0x697 // (1687) +#define RES_bmCritterStanding_103 0x698 // (1688) +#define RES_bmCritterStanding_104 0x699 // (1689) +#define RES_bmCritterStanding_105 0x69a // (1690) +#define RES_bmCritterStanding_106 0x69b // (1691) +#define RES_bmCritterStanding_107 0x69c // (1692) +#define RES_bmCritterStanding_108 0x69d // (1693) +#define RES_bmCritterStanding_109 0x69e // (1694) +#define RES_bmCritterStanding_110 0x69f // (1695) +#define RES_bmCritterStanding_111 0x6a0 // (1696) +#define RES_bmCritterStanding_112 0x6a1 // (1697) +#define RES_bmCritterStanding_113 0x6a2 // (1698) +#define RES_bmCritterStanding_114 0x6a3 // (1699) +#define RES_bmCritterStanding_115 0x6a4 // (1700) +#define RES_bmCritterStanding_116 0x6a5 // (1701) +#define RES_bmCritterStanding_117 0x6a6 // (1702) +#define RES_bmCritterStanding_118 0x6a7 // (1703) +#define RES_bmCritterStanding_119 0x6a8 // (1704) +#define RES_bmCritterStanding_120 0x6a9 // (1705) +#define RES_bmCritterStanding_121 0x6aa // (1706) +#define RES_bmCritterStanding_122 0x6ab // (1707) +#define RES_bmCritterStanding_123 0x6ac // (1708) +#define RES_bmCritterStanding_124 0x6ad // (1709) +#define RES_bmCritterStanding_125 0x6ae // (1710) +#define RES_bmCritterStanding_126 0x6af // (1711) +#define RES_bmCritterStanding_127 0x6b0 // (1712) +#define RES_bmCritterStanding_128 0x6b1 // (1713) +#define RES_bmCritterStanding_129 0x6b2 // (1714) +#define RES_bmCritterStanding_130 0x6b3 // (1715) +#define RES_bmCritterStanding_131 0x6b4 // (1716) +#define RES_bmCritterStanding_132 0x6b5 // (1717) +#define RES_bmCritterStanding_133 0x6b6 // (1718) +#define RES_bmCritterStanding_134 0x6b7 // (1719) +#define RES_bmCritterStanding_135 0x6b8 // (1720) +#define RES_bmCritterStanding_136 0x6b9 // (1721) +#define RES_bmCritterStanding_137 0x6ba // (1722) +#define RES_bmCritterStanding_138 0x6bb // (1723) +#define RES_bmCritterStanding_139 0x6bc // (1724) +#define RES_bmCritterStanding_140 0x6bd // (1725) +#define RES_bmCritterStanding_141 0x6be // (1726) +#define RES_bmCritterStanding_142 0x6bf // (1727) +#define RES_bmCritterStanding_143 0x6c0 // (1728) +#define RES_bmCritterStanding_144 0x6c1 // (1729) +#define RES_bmCritterStanding_145 0x6c2 // (1730) +#define RES_bmCritterStanding_146 0x6c3 // (1731) +#define RES_bmCritterStanding_147 0x6c4 // (1732) +#define RES_bmCritterStanding_148 0x6c5 // (1733) +#define RES_bmCritterStanding_149 0x6c6 // (1734) +#define RES_bmCritterStanding_150 0x6c7 // (1735) +#define RES_bmCritterStanding_151 0x6c8 // (1736) +#define RES_bmCritterStanding_152 0x6c9 // (1737) +#define RES_bmCritterStanding_153 0x6ca // (1738) +#define RES_bmCritterStanding_154 0x6cb // (1739) +#define RES_bmCritterStanding_155 0x6cc // (1740) +#define RES_bmCritterStanding_156 0x6cd // (1741) +#define RES_bmCritterStanding_157 0x6ce // (1742) +#define RES_bmCritterStanding_158 0x6cf // (1743) +#define RES_bmCritterStanding_159 0x6d0 // (1744) +#define RES_bmCritterStanding_160 0x6d1 // (1745) +#define RES_bmCritterStanding_161 0x6d2 // (1746) +#define RES_bmCritterStanding_162 0x6d3 // (1747) +#define RES_bmCritterStanding_163 0x6d4 // (1748) +#define RES_bmCritterStanding_164 0x6d5 // (1749) +#define RES_bmCritterStanding_165 0x6d6 // (1750) +#define RES_bmCritterStanding_166 0x6d7 // (1751) +#define RES_bmCritterStanding_167 0x6d8 // (1752) +#define RES_bmCritterStanding_168 0x6d9 // (1753) +#define RES_bmCritterStanding_169 0x6da // (1754) +#define RES_bmCritterStanding_170 0x6db // (1755) +#define RES_bmCritterStanding_171 0x6dc // (1756) +#define RES_bmCritterStanding_172 0x6dd // (1757) +#define RES_bmCritterStanding_173 0x6de // (1758) +#define RES_bmCritterStanding_174 0x6df // (1759) +#define RES_bmCritterStanding_175 0x6e0 // (1760) +#define RES_bmCritterStanding_176 0x6e1 // (1761) +#define RES_bmCritterStanding_177 0x6e2 // (1762) +#define RES_bmCritterStanding_178 0x6e3 // (1763) +#define RES_bmCritterStanding_179 0x6e4 // (1764) +#define RES_bmCritterStanding_180 0x6e5 // (1765) +#define RES_bmCritterStanding_181 0x6e6 // (1766) +#define RES_bmCritterStanding_182 0x6e7 // (1767) +#define RES_bmCritterStanding_183 0x6e8 // (1768) +#define RES_bmCritterStanding_184 0x6e9 // (1769) +#define RES_bmCritterStanding_185 0x6ea // (1770) +#define RES_bmCritterStanding_186 0x6eb // (1771) +#define RES_bmCritterStanding_187 0x6ec // (1772) +#define RES_bmCritterStanding_188 0x6ed // (1773) +#define RES_bmCritterStanding_189 0x6ee // (1774) +#define RES_bmCritterStanding_190 0x6ef // (1775) +#define RES_bmCritterStanding_191 0x6f0 // (1776) +#define RES_bmCritterStanding_192 0x6f1 // (1777) +#define RES_bmCritterStanding_193 0x6f2 // (1778) +#define RES_bmCritterStanding_194 0x6f3 // (1779) +#define RES_bmCritterStanding_195 0x6f4 // (1780) +#define RES_bmCritterStanding_196 0x6f5 // (1781) +#define RES_bmCritterStanding_197 0x6f6 // (1782) +#define RES_bmCritterStanding_198 0x6f7 // (1783) +#define RES_bmCritterStanding_199 0x6f8 // (1784) +#define RES_bmCritterStanding_200 0x6f9 // (1785) +#define RES_bmCritterStanding_201 0x6fa // (1786) +#define RES_bmCritterStanding_202 0x6fb // (1787) +#define RES_bmCritterStanding_203 0x6fc // (1788) +#define RES_bmCritterStanding_204 0x6fd // (1789) +#define RES_bmCritterStanding_205 0x6fe // (1790) +#define RES_bmCritterStanding_206 0x6ff // (1791) +#define RES_bmCritterStanding_207 0x700 // (1792) +#define RES_bmCritterStanding_208 0x701 // (1793) +#define RES_bmCritterStanding_209 0x702 // (1794) +#define RES_bmCritterStanding_210 0x703 // (1795) +#define RES_bmCritterStanding_211 0x704 // (1796) +#define RES_bmCritterStanding_212 0x705 // (1797) +#define RES_bmCritterStanding_213 0x706 // (1798) +#define RES_bmCritterStanding_214 0x707 // (1799) +#define RES_bmCritterStanding_215 0x708 // (1800) +#define RES_bmCritterStanding_216 0x709 // (1801) +#define RES_bmCritterStanding_217 0x70a // (1802) +#define RES_bmCritterStanding_218 0x70b // (1803) +#define RES_bmCritterStanding_219 0x70c // (1804) +#define RES_bmCritterStanding_220 0x70d // (1805) +#define RES_bmCritterStanding_221 0x70e // (1806) +#define RES_bmCritterStanding_222 0x70f // (1807) +#define RES_bmCritterStanding_223 0x710 // (1808) +#define RES_bmCritterStanding_224 0x711 // (1809) +#define RES_bmCritterStanding_225 0x712 // (1810) +#define RES_bmCritterStanding_226 0x713 // (1811) +#define RES_bmCritterStanding_227 0x714 // (1812) +#define RES_bmCritterStanding_228 0x715 // (1813) +#define RES_bmCritterStanding_229 0x716 // (1814) +#define RES_bmCritterStanding_230 0x717 // (1815) +#define RES_bmCritterStanding_231 0x718 // (1816) +#define RES_bmCritterStanding_232 0x719 // (1817) + +#endif diff --git a/engine/src/GameSrc/Headers/objart3.h b/engine/src/GameSrc/Headers/objart3.h new file mode 100644 index 0000000..f2c40f9 --- /dev/null +++ b/engine/src/GameSrc/Headers/objart3.h @@ -0,0 +1,339 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* This file created by RESTOOL */ + +#ifndef __OBJART3_H +#define __OBJART3_H + +#define RES_bmCritterMovement_0 0x758 // (1880) +#define RES_bmCritterMovement_1 0x759 // (1881) +#define RES_bmCritterMovement_2 0x75a // (1882) +#define RES_bmCritterMovement_3 0x75b // (1883) +#define RES_bmCritterMovement_4 0x75c // (1884) +#define RES_bmCritterMovement_5 0x75d // (1885) +#define RES_bmCritterMovement_6 0x75e // (1886) +#define RES_bmCritterMovement_7 0x75f // (1887) +#define RES_bmCritterMovement_8 0x760 // (1888) +#define RES_bmCritterMovement_9 0x761 // (1889) +#define RES_bmCritterMovement_10 0x762 // (1890) +#define RES_bmCritterMovement_11 0x763 // (1891) +#define RES_bmCritterMovement_12 0x764 // (1892) +#define RES_bmCritterMovement_13 0x765 // (1893) +#define RES_bmCritterMovement_14 0x766 // (1894) +#define RES_bmCritterMovement_15 0x767 // (1895) +#define RES_bmCritterMovement_16 0x768 // (1896) +#define RES_bmCritterMovement_17 0x769 // (1897) +#define RES_bmCritterMovement_18 0x76a // (1898) +#define RES_bmCritterMovement_19 0x76b // (1899) +#define RES_bmCritterMovement_20 0x76c // (1900) +#define RES_bmCritterMovement_21 0x76d // (1901) +#define RES_bmCritterMovement_22 0x76e // (1902) +#define RES_bmCritterMovement_23 0x76f // (1903) +#define RES_bmCritterMovement_24 0x770 // (1904) +#define RES_bmCritterMovement_25 0x771 // (1905) +#define RES_bmCritterMovement_26 0x772 // (1906) +#define RES_bmCritterMovement_27 0x773 // (1907) +#define RES_bmCritterMovement_28 0x774 // (1908) +#define RES_bmCritterMovement_29 0x775 // (1909) +#define RES_bmCritterMovement_30 0x776 // (1910) +#define RES_bmCritterMovement_31 0x777 // (1911) +#define RES_bmCritterMovement_32 0x778 // (1912) +#define RES_bmCritterMovement_33 0x779 // (1913) +#define RES_bmCritterMovement_34 0x77a // (1914) +#define RES_bmCritterMovement_35 0x77b // (1915) +#define RES_bmCritterMovement_36 0x77c // (1916) +#define RES_bmCritterMovement_37 0x77d // (1917) +#define RES_bmCritterMovement_38 0x77e // (1918) +#define RES_bmCritterMovement_39 0x77f // (1919) +#define RES_bmCritterMovement_40 0x780 // (1920) +#define RES_bmCritterMovement_41 0x781 // (1921) +#define RES_bmCritterMovement_42 0x782 // (1922) +#define RES_bmCritterMovement_43 0x783 // (1923) +#define RES_bmCritterMovement_44 0x784 // (1924) +#define RES_bmCritterMovement_45 0x785 // (1925) +#define RES_bmCritterMovement_46 0x786 // (1926) +#define RES_bmCritterMovement_47 0x787 // (1927) +#define RES_bmCritterMovement_48 0x788 // (1928) +#define RES_bmCritterMovement_49 0x789 // (1929) +#define RES_bmCritterMovement_50 0x78a // (1930) +#define RES_bmCritterMovement_51 0x78b // (1931) +#define RES_bmCritterMovement_52 0x78c // (1932) +#define RES_bmCritterMovement_53 0x78d // (1933) +#define RES_bmCritterMovement_54 0x78e // (1934) +#define RES_bmCritterMovement_55 0x78f // (1935) +#define RES_bmCritterMovement_56 0x790 // (1936) +#define RES_bmCritterMovement_57 0x791 // (1937) +#define RES_bmCritterMovement_58 0x792 // (1938) +#define RES_bmCritterMovement_59 0x793 // (1939) +#define RES_bmCritterMovement_60 0x794 // (1940) +#define RES_bmCritterMovement_61 0x795 // (1941) +#define RES_bmCritterMovement_62 0x796 // (1942) +#define RES_bmCritterMovement_63 0x797 // (1943) +#define RES_bmCritterMovement_64 0x798 // (1944) +#define RES_bmCritterMovement_65 0x799 // (1945) +#define RES_bmCritterMovement_66 0x79a // (1946) +#define RES_bmCritterMovement_67 0x79b // (1947) +#define RES_bmCritterMovement_68 0x79c // (1948) +#define RES_bmCritterMovement_69 0x79d // (1949) +#define RES_bmCritterMovement_70 0x79e // (1950) +#define RES_bmCritterMovement_71 0x79f // (1951) +#define RES_bmCritterMovement_72 0x7a0 // (1952) +#define RES_bmCritterMovement_73 0x7a1 // (1953) +#define RES_bmCritterMovement_74 0x7a2 // (1954) +#define RES_bmCritterMovement_75 0x7a3 // (1955) +#define RES_bmCritterMovement_76 0x7a4 // (1956) +#define RES_bmCritterMovement_77 0x7a5 // (1957) +#define RES_bmCritterMovement_78 0x7a6 // (1958) +#define RES_bmCritterMovement_79 0x7a7 // (1959) +#define RES_bmCritterMovement_80 0x7a8 // (1960) +#define RES_bmCritterMovement_81 0x7a9 // (1961) +#define RES_bmCritterMovement_82 0x7aa // (1962) +#define RES_bmCritterMovement_83 0x7ab // (1963) +#define RES_bmCritterMovement_84 0x7ac // (1964) +#define RES_bmCritterMovement_85 0x7ad // (1965) +#define RES_bmCritterMovement_86 0x7ae // (1966) +#define RES_bmCritterMovement_87 0x7af // (1967) +#define RES_bmCritterMovement_88 0x7b0 // (1968) +#define RES_bmCritterMovement_89 0x7b1 // (1969) +#define RES_bmCritterMovement_90 0x7b2 // (1970) +#define RES_bmCritterMovement_91 0x7b3 // (1971) +#define RES_bmCritterMovement_92 0x7b4 // (1972) +#define RES_bmCritterMovement_93 0x7b5 // (1973) +#define RES_bmCritterMovement_94 0x7b6 // (1974) +#define RES_bmCritterMovement_95 0x7b7 // (1975) +#define RES_bmCritterMovement_96 0x7b8 // (1976) +#define RES_bmCritterMovement_97 0x7b9 // (1977) +#define RES_bmCritterMovement_98 0x7ba // (1978) +#define RES_bmCritterMovement_99 0x7bb // (1979) +#define RES_bmCritterMovement_100 0x7bc // (1980) +#define RES_bmCritterMovement_101 0x7bd // (1981) +#define RES_bmCritterMovement_102 0x7be // (1982) +#define RES_bmCritterMovement_103 0x7bf // (1983) +#define RES_bmCritterMovement_104 0x7c0 // (1984) +#define RES_bmCritterMovement_105 0x7c1 // (1985) +#define RES_bmCritterMovement_106 0x7c2 // (1986) +#define RES_bmCritterMovement_107 0x7c3 // (1987) +#define RES_bmCritterMovement_108 0x7c4 // (1988) +#define RES_bmCritterMovement_109 0x7c5 // (1989) +#define RES_bmCritterMovement_110 0x7c6 // (1990) +#define RES_bmCritterMovement_111 0x7c7 // (1991) +#define RES_bmCritterMovement_112 0x7c8 // (1992) +#define RES_bmCritterMovement_113 0x7c9 // (1993) +#define RES_bmCritterMovement_114 0x7ca // (1994) +#define RES_bmCritterMovement_115 0x7cb // (1995) +#define RES_bmCritterMovement_116 0x7cc // (1996) +#define RES_bmCritterMovement_117 0x7cd // (1997) +#define RES_bmCritterMovement_118 0x7ce // (1998) +#define RES_bmCritterMovement_119 0x7cf // (1999) +#define RES_bmCritterMovement_120 0x7d0 // (2000) +#define RES_bmCritterMovement_121 0x7d1 // (2001) +#define RES_bmCritterMovement_122 0x7d2 // (2002) +#define RES_bmCritterMovement_123 0x7d3 // (2003) +#define RES_bmCritterMovement_124 0x7d4 // (2004) +#define RES_bmCritterMovement_125 0x7d5 // (2005) +#define RES_bmCritterMovement_126 0x7d6 // (2006) +#define RES_bmCritterMovement_127 0x7d7 // (2007) +#define RES_bmCritterMovement_128 0x7d8 // (2008) +#define RES_bmCritterMovement_129 0x7d9 // (2009) +#define RES_bmCritterMovement_130 0x7da // (2010) +#define RES_bmCritterMovement_131 0x7db // (2011) +#define RES_bmCritterMovement_132 0x7dc // (2012) +#define RES_bmCritterMovement_133 0x7dd // (2013) +#define RES_bmCritterMovement_134 0x7de // (2014) +#define RES_bmCritterMovement_135 0x7df // (2015) +#define RES_bmCritterMovement_136 0x7e0 // (2016) +#define RES_bmCritterMovement_137 0x7e1 // (2017) +#define RES_bmCritterMovement_138 0x7e2 // (2018) +#define RES_bmCritterMovement_139 0x7e3 // (2019) +#define RES_bmCritterMovement_140 0x7e4 // (2020) +#define RES_bmCritterMovement_141 0x7e5 // (2021) +#define RES_bmCritterMovement_142 0x7e6 // (2022) +#define RES_bmCritterMovement_143 0x7e7 // (2023) +#define RES_bmCritterMovement_144 0x7e8 // (2024) +#define RES_bmCritterMovement_145 0x7e9 // (2025) +#define RES_bmCritterMovement_146 0x7ea // (2026) +#define RES_bmCritterMovement_147 0x7eb // (2027) +#define RES_bmCritterMovement_148 0x7ec // (2028) +#define RES_bmCritterMovement_149 0x7ed // (2029) +#define RES_bmCritterMovement_150 0x7ee // (2030) +#define RES_bmCritterMovement_151 0x7ef // (2031) +#define RES_bmCritterMovement_152 0x7f0 // (2032) +#define RES_bmCritterMovement_153 0x7f1 // (2033) +#define RES_bmCritterMovement_154 0x7f2 // (2034) +#define RES_bmCritterMovement_155 0x7f3 // (2035) +#define RES_bmCritterMovement_156 0x7f4 // (2036) +#define RES_bmCritterMovement_157 0x7f5 // (2037) +#define RES_bmCritterMovement_158 0x7f6 // (2038) +#define RES_bmCritterMovement_159 0x7f7 // (2039) +#define RES_bmCritterMovement_160 0x7f8 // (2040) +#define RES_bmCritterMovement_161 0x7f9 // (2041) +#define RES_bmCritterMovement_162 0x7fa // (2042) +#define RES_bmCritterMovement_163 0x7fb // (2043) +#define RES_bmCritterMovement_164 0x7fc // (2044) +#define RES_bmCritterMovement_165 0x7fd // (2045) +#define RES_bmCritterMovement_166 0x7fe // (2046) +#define RES_bmCritterMovement_167 0x7ff // (2047) +#define RES_bmCritterMovement_168 0x800 // (2048) +#define RES_bmCritterMovement_169 0x801 // (2049) +#define RES_bmCritterMovement_170 0x802 // (2050) +#define RES_bmCritterMovement_171 0x803 // (2051) +#define RES_bmCritterMovement_172 0x804 // (2052) +#define RES_bmCritterMovement_173 0x805 // (2053) +#define RES_bmCritterMovement_174 0x806 // (2054) +#define RES_bmCritterMovement_175 0x807 // (2055) +#define RES_bmCritterMovement_176 0x808 // (2056) +#define RES_bmCritterMovement_177 0x809 // (2057) +#define RES_bmCritterMovement_178 0x80a // (2058) +#define RES_bmCritterMovement_179 0x80b // (2059) +#define RES_bmCritterMovement_180 0x80c // (2060) +#define RES_bmCritterMovement_181 0x80d // (2061) +#define RES_bmCritterMovement_182 0x80e // (2062) +#define RES_bmCritterMovement_183 0x80f // (2063) +#define RES_bmCritterMovement_184 0x810 // (2064) +#define RES_bmCritterMovement_185 0x811 // (2065) +#define RES_bmCritterMovement_186 0x812 // (2066) +#define RES_bmCritterMovement_187 0x813 // (2067) +#define RES_bmCritterMovement_188 0x814 // (2068) +#define RES_bmCritterMovement_189 0x815 // (2069) +#define RES_bmCritterMovement_190 0x816 // (2070) +#define RES_bmCritterMovement_191 0x817 // (2071) +#define RES_bmCritterMovement_192 0x818 // (2072) +#define RES_bmCritterMovement_193 0x819 // (2073) +#define RES_bmCritterMovement_194 0x81a // (2074) +#define RES_bmCritterMovement_195 0x81b // (2075) +#define RES_bmCritterMovement_196 0x81c // (2076) +#define RES_bmCritterMovement_197 0x81d // (2077) +#define RES_bmCritterMovement_198 0x81e // (2078) +#define RES_bmCritterMovement_199 0x81f // (2079) +#define RES_bmCritterMovement_200 0x820 // (2080) +#define RES_bmCritterMovement_201 0x821 // (2081) +#define RES_bmCritterMovement_202 0x822 // (2082) +#define RES_bmCritterMovement_203 0x823 // (2083) +#define RES_bmCritterMovement_204 0x824 // (2084) +#define RES_bmCritterMovement_205 0x825 // (2085) +#define RES_bmCritterMovement_206 0x826 // (2086) +#define RES_bmCritterMovement_207 0x827 // (2087) +#define RES_bmCritterMovement_208 0x828 // (2088) +#define RES_bmCritterMovement_209 0x829 // (2089) +#define RES_bmCritterMovement_210 0x82a // (2090) +#define RES_bmCritterMovement_211 0x82b // (2091) +#define RES_bmCritterMovement_212 0x82c // (2092) +#define RES_bmCritterMovement_213 0x82d // (2093) +#define RES_bmCritterMovement_214 0x82e // (2094) +#define RES_bmCritterMovement_215 0x82f // (2095) +#define RES_bmCritterMovement_216 0x830 // (2096) +#define RES_bmCritterMovement_217 0x831 // (2097) +#define RES_bmCritterMovement_218 0x832 // (2098) +#define RES_bmCritterMovement_219 0x833 // (2099) +#define RES_bmCritterMovement_220 0x834 // (2100) +#define RES_bmCritterMovement_221 0x835 // (2101) +#define RES_bmCritterMovement_222 0x836 // (2102) +#define RES_bmCritterMovement_223 0x837 // (2103) +#define RES_bmCritterMovement_224 0x838 // (2104) +#define RES_bmCritterMovement_225 0x839 // (2105) +#define RES_bmCritterMovement_226 0x83a // (2106) +#define RES_bmCritterMovement_227 0x83b // (2107) +#define RES_bmCritterMovement_228 0x83c // (2108) +#define RES_bmCritterMovement_229 0x83d // (2109) +#define RES_bmCritterMovement_230 0x83e // (2110) +#define RES_bmCritterMovement_231 0x83f // (2111) +#define RES_bmCritterMovement_232 0x840 // (2112) +#define RES_bmCritterAttack2_0 0x841 // (2113) +#define RES_bmCritterAttack2_1 0x842 // (2114) +#define RES_bmCritterAttack2_2 0x843 // (2115) +#define RES_bmCritterAttack2_3 0x844 // (2116) +#define RES_bmCritterAttack2_4 0x845 // (2117) +#define RES_bmCritterAttack2_5 0x846 // (2118) +#define RES_bmCritterAttack2_6 0x847 // (2119) +#define RES_bmCritterAttack2_7 0x848 // (2120) +#define RES_bmCritterAttack2_8 0x849 // (2121) +#define RES_bmCritterAttack2_9 0x84a // (2122) +#define RES_bmCritterAttack2_10 0x84b // (2123) +#define RES_bmCritterAttack2_11 0x84c // (2124) +#define RES_bmCritterAttack2_12 0x84d // (2125) +#define RES_bmCritterAttack2_13 0x84e // (2126) +#define RES_bmCritterAttack2_14 0x84f // (2127) +#define RES_bmCritterAttack2_15 0x850 // (2128) +#define RES_bmCritterAttack2_16 0x851 // (2129) +#define RES_bmCritterAttack2_17 0x852 // (2130) +#define RES_bmCritterAttack2_18 0x853 // (2131) +#define RES_bmCritterAttack2_19 0x854 // (2132) +#define RES_bmCritterAttack2_20 0x855 // (2133) +#define RES_bmCritterAttack2_21 0x856 // (2134) +#define RES_bmCritterAttack2_22 0x857 // (2135) +#define RES_bmCritterAttack2_23 0x858 // (2136) +#define RES_bmCritterAttack2_24 0x859 // (2137) +#define RES_bmCritterAttack2_25 0x85a // (2138) +#define RES_bmCritterAttack2_26 0x85b // (2139) +#define RES_bmCritterAttack2_27 0x85c // (2140) +#define RES_bmCritterAttack2_28 0x85d // (2141) +#define RES_bmCritterAttack2_29 0x85e // (2142) +#define RES_bmCritterAttack2_30 0x85f // (2143) +#define RES_bmCritterAttack2_31 0x860 // (2144) +#define RES_bmCritterAttack2_32 0x861 // (2145) +#define RES_bmCritterAttack2_33 0x862 // (2146) +#define RES_bmCritterAttack2_34 0x863 // (2147) +#define RES_bmCritterAttack2_35 0x864 // (2148) +#define RES_bmCritterAttack2_36 0x865 // (2149) +#define RES_bmDoorArt_0 0x960 // (2400) +#define RES_bmDoorArt_1 0x961 // (2401) +#define RES_bmDoorArt_2 0x962 // (2402) +#define RES_bmDoorArt_3 0x963 // (2403) +#define RES_bmDoorArt_4 0x964 // (2404) +#define RES_bmDoorArt_5 0x965 // (2405) +#define RES_bmDoorArt_6 0x966 // (2406) +#define RES_bmDoorArt_7 0x967 // (2407) +#define RES_bmDoorArt_8 0x968 // (2408) +#define RES_bmDoorArt_9 0x969 // (2409) +#define RES_bmDoorArt_10 0x96a // (2410) +#define RES_bmDoorArt_11 0x96b // (2411) +#define RES_bmDoorArt_12 0x96c // (2412) +#define RES_bmDoorArt_13 0x96d // (2413) +#define RES_bmDoorArt_14 0x96e // (2414) +#define RES_bmDoorArt_15 0x96f // (2415) +#define RES_bmDoorArt_16 0x970 // (2416) +#define RES_bmDoorArt_17 0x971 // (2417) +#define RES_bmDoorArt_18 0x972 // (2418) +#define RES_bmDoorArt_19 0x973 // (2419) +#define RES_bmDoorArt_20 0x974 // (2420) +#define RES_bmDoorArt_21 0x975 // (2421) +#define RES_bmDoorArt_22 0x976 // (2422) +#define RES_bmDoorArt_23 0x977 // (2423) +#define RES_bmDoorArt_24 0x978 // (2424) +#define RES_bmDoorArt_25 0x979 // (2425) +#define RES_bmDoorArt_26 0x97a // (2426) +#define RES_bmDoorArt_27 0x97b // (2427) +#define RES_bmDoorArt_28 0x97c // (2428) +#define RES_bmDoorArt_29 0x97d // (2429) +#define RES_bmDoorArt_30 0x97e // (2430) +#define RES_bmDoorArt_31 0x97f // (2431) +#define RES_bmDoorArt_32 0x980 // (2432) +#define RES_bmDoorArt_33 0x981 // (2433) +#define RES_bmDoorArt_34 0x982 // (2434) +#define RES_bmDoorArt_35 0x983 // (2435) +#define RES_bmDoorArt_36 0x984 // (2436) +#define RES_bmDoorArt_37 0x985 // (2437) +#define RES_bmDoorArt_38 0x986 // (2438) +#define RES_bmDoorArt_39 0x987 // (2439) +#define RES_bmDoorArt_40 0x988 // (2440) +#define RES_bmIconArt_0 0x4e // (78) +#define RES_bmGraffitiArt_0 0x4f // (79) +#define RES_bmRepulsArt_0 0x50 // (80) + +#endif diff --git a/engine/src/GameSrc/Headers/objbit.h b/engine/src/GameSrc/Headers/objbit.h new file mode 100644 index 0000000..219a5dd --- /dev/null +++ b/engine/src/GameSrc/Headers/objbit.h @@ -0,0 +1,115 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __OBJBIT_H +#define __OBJBIT_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/objbit.h $ + * $Revision: 1.21 $ + * $Author: xemu $ + * $Date: 1994/08/22 03:03:51 $ + * + */ + +// Includes + +// OBJECT PROPERTIES FLAGS + +#define INVENTORY_GENERAL 0x0001u +#define INVENTORY_GENERAL_SHF 0u +#define EDMS_PRESERVE 0x0002u +#define EDMS_PRESERVE_SHF 1u +#define INVENT_USEMODE 0x000Cu +#define INVENT_USEMODE_SHF 2u +#define OBJECT_USE_NOCURSOR 0x0010u +#define OBJECT_USE_NOCURSOR_SHIFT 4u + +// RENDER_BLOCK is true if the thing can block the renderer +// currently only used for doors, where instance bit need be checked +// as well. +#define RENDER_BLOCK 0x0020u +#define RENDER_BLOCK_SHF 5u + +// LIGHT_TYPE is for how it should be lit. +// 0 -- normal, simple lighting applies +// 1 -- use complicated lighting +// 2 -- never apply lighting +// 3 -- consult instance bit to determine whether to light +#define LIGHT_TYPE 0x00C0u +#define LIGHT_TYPE_SHF 6u + +// TERRAIN_OBJECT +// 0 - ignore for terrain +// 1 - wall-like terrain +// 2 - complex terrain +// 3 - unused +#define TERRAIN_OBJECT 0x0300u +#define TERRAIN_OBJECT_SHF 8u + +// MY_IM_LARGE +// Doubles the size of the bitmap when it renders, relative to the source art +#define MY_IM_LARGE 0x0400u +#define MY_IM_LARGE_SHF 10u + +// terrain will special case terrain damage if this bit is set +#define SPCL_TERR_DMG 0x0800u +#define SPCL_TERR_DMG_SHT 11u + +// Class-specific flags go here. +#define CLASS_FLAGS 0x7000u +#define CLASS_FLAGS_SHF 12u + +// If this bit is set, that class of object is considered "useless", and unless +// it is specially preserved via USEFUL_FLAG (instance flag) it can be destroyed +// to make room for others. +#define USELESS_FLAG 0x8000u +#define USELESS_FLAG_SHF 15u + +// INSTANCE DATA FLAGS +#define HUDOBJ_INST_FLAG 0x01 // This is defined redundantly in hudobj.h +#define RENDER_BLOCK_FLAG 0x02 // whether we are blocking the renderer +#define UNLIT_FLAG 0x04 // Don't light up my life +#define INDESTRUCT_FLAG 0x08 // We can't be stopped, but we must be stopped +#define USEFUL_FLAG 0x10 // Don't punt us to make room for others +#define OLH_INST_FLAG 0x20 // have we interacted with this yet? +#define CLASS_INST_FLAG2 0x40 // Class-specific stuff +#define CLASS_INST_FLAG 0x80 // Class-specific stuff + +// Notes on class specific instance data (flags) +// Fixtures: used for whether to zoom to mfd on use +// Critters: CLASS_INST_FLAG used to denote "loner" +// CLASS_INST_FLAG2 used to denote that the critter wants to get closer. +// Containers: CLASS_INST_FLAG used to indicate that the container is "freshly" dead +// and needs to have loot placed on it when searched. +// Smallstuffs: corpses use their CLASS_INST_FLAG like Containers. +// Doors: Both CLASS_INST_FLAGs are used as secret identifier codes to make sure that +// we get the right autoclose events. + +// Notes on class specific property flagss +// Bigstuff & Smallstuff: CLASS_FLAG 0x1000 used to denote data1 is 1-2 ObjIDs +// to be "used" ala a splitter trap +#define STUFF_OBJUSE_FLAG 0x1 + +// Critters: +// CLASS_FLAG 0x1000 used to indicate that creature is incapable of movement. +// CLASS_FLAG 0x2000 used to indicate that creature is unable to open doors. +#define CRITTER_NOMOVE_OBJPROP_FLAG 0x1 +#define CRITTER_NODOOR_OBJPROP_FLAG 0x2 + +#endif // __OBJBIT_H diff --git a/engine/src/GameSrc/Headers/objclass.h b/engine/src/GameSrc/Headers/objclass.h new file mode 100644 index 0000000..f1fe987 --- /dev/null +++ b/engine/src/GameSrc/Headers/objclass.h @@ -0,0 +1,108 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __OBJCLASS_H +#define __OBJCLASS_H + +/* + * $Source: n:/project/cit/src/inc/RCS/objclass.h $ + * $Revision: 1.6 $ + * $Author: dc $ + * $Date: 1994/04/03 05:59:14 $ + * + * $Log: objclass.h $ + * Revision 1.6 1994/04/03 05:59:14 dc + * OPNUM and the other ??NUM three, running a bit faster, still should probably change + * to inline assembler, along with the other main macros... + * perhaps just make ObjProps[OPNUM(id)] an inlined asm thing + * + * Revision 1.5 1994/01/02 21:09:50 xemu + * new containers + * + * Revision 1.4 1993/10/01 23:55:13 xemu + * new object regime + * + * Revision 1.3 1993/09/02 23:08:12 xemu + * angle me baby + * + * Revision 1.2 1993/08/17 21:54:15 minman + * added prototype for nth_after_triple and get_nth_from_triple + * + * Revision 1.1 1993/08/05 14:05:20 minman + * Initial revision + * + * + */ + +// Includes +#include "objects.h" + +// Some macros... +#define MAKETRIP(obclass, subclass, type) ((obclass << 16u) + (subclass << 8u) + type) +#define ID2TRIP(id) MAKETRIP(objs[id].obclass, objs[id].subclass, objs[id].info.type) + +#define TRIP2CL(trip) ((ObjClass)(trip >> 16)) +#define TRIP2SC(trip) ((trip & 0xFF00) >> 8) +#define TRIP2TY(trip) (trip & 0xFF) +#define OBJBASE(triple) (((TRIP2CL(triple) & 0xF) << 4) + (TRIP2SC(triple) & 0xF)) + +#define OPTRIP(triple) (ObjBaseArray[OBJBASE(triple)] + TRIP2TY(triple)) +#define CPTRIP(triple) (ClassBaseArray[TRIP2CL(triple)][TRIP2SC(triple)] + TRIP2TY(triple)) +#define SCTRIP(triple) TRIP2TY(triple) + +#define OPNUM(id) (ObjBaseArray[(objs[id].obclass << 4u) + (objs[id].subclass)] + objs[id].info.type) +#define CPNUM(id) (ClassBaseArray[objs[id].obclass][objs[id].subclass] + objs[id].info.type) +#define SCNUM(id) (objs[id].info.type) + +#ifdef SLOW +#define OPNUM(id) OPTRIP(ID2TRIP(id)) +#define CPNUM(id) CPTRIP(ID2TRIP(id)) +#define SCNUM(id) SCTRIP(ID2TRIP(id)) +#endif + +// Prototypes +short num_types(uchar obclass, uchar subclass); + +// Told to find the nth object of class "class", will return appropriate +// triple or -1 if nth object didn't exist +int get_triple_from_class_nth_item(uchar obclass, uchar n); + +// returns the triple which is n past the given base +int nth_after_triple(int base, uchar n); + +// given a triple, returns n which represents the count +// past the first of its class +int get_nth_from_triple(int triple); + +// Defines +/* +#define COMMON_OBJSPEC_FIELDS \ + union { \ + ObjID id; \ + ObjSpecID headused; \ + }; \ + union { \ + ObjSpecID next; \ + ObjSpecID headfree; \ + }; \ + ObjSpecID prev + +#define COMMON_OBJSPEC_SIZE (sizeof(ObjSpecID) * 3) +*/ + +#endif // __OBJCLASS_H diff --git a/engine/src/GameSrc/Headers/objcrit.h b/engine/src/GameSrc/Headers/objcrit.h new file mode 100644 index 0000000..0945e5a --- /dev/null +++ b/engine/src/GameSrc/Headers/objcrit.h @@ -0,0 +1,269 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __OBJCRIT_H +#define __OBJCRIT_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/objcrit.h $ + * $Revision: 1.41 $ + * $Author: minman $ + * $Date: 1994/08/02 22:09:15 $ + * + * + */ + +// Includes +#include "objsim.h" +#include "objclass.h" + +#pragma pack(push,2) + +typedef struct { + // COMMON_OBJSPEC_FIELDS; + union { + ObjID id; + ObjSpecID headused; + }; + union { + ObjSpecID next; + ObjSpecID headfree; + }; + ObjSpecID prev; + fix des_heading, des_speed, urgency; + short wait_frames; + ushort flags; + uint32_t attack_count; // can attack when game time reaches this + ubyte ai_mode; + ubyte mood; + ubyte orders; + ubyte current_posture; + char x1; + char y1; + char dest_x; // Current destination coordinates + char dest_y; + char pf_x; // where we are currently pathfinding to (what our current step is) + char pf_y; + char path_id; // what pathfinding track we are on + char path_tries; // how many frames have we been trying to get to the + // next step on our pathfinding? + ObjID loot1, loot2; // Some loot to get when we destroy critter + // Note: Num frames is gotten from object properties + fix sidestep; +} ObjCritter; + +typedef struct { + // COMMON_OBJSPEC_FIELDS; + union { + ObjID id; + ObjSpecID headused; + }; + union { + ObjSpecID next; + ObjSpecID headfree; + }; + ObjSpecID prev; + fix des_heading, des_speed, urgency; + short wait_frames; + short base_time_interval; + uint32_t attack_count; // can attack when game time reaches this + ubyte ai_mode; + ubyte mood; + ubyte orders; + ubyte current_posture; + char x1; + char y1; + char dest_x; // Current destination coordinate + char dest_y; + char old_x; // the bin we were in prior to being in this bin + char old_y; + char last_x; // the bin we were in last ai cycle + char last_y; + ObjID loot1, loot2; // Some loot to get when we destroy critter + // Note: Num frames is gotten from object properties +} oldObjCritter; + +// -------------------------- +// Class typedefs + +#define NUM_CRITTER_POSTURES 8 + +typedef struct _CritterAttack { + int damage_type; + short damage_modifier; + ubyte offense_value; + ubyte penetration; + ubyte attack_mass; + short attack_velocity; + ubyte accuracy; + ubyte att_range; + int speed; // Wait this long between attacks .. in game_time units + int slow_proj; // what, if any, slow projectile we fire +} CritterAttack; + +#define NUM_ALTERNATE_ATTACKS 2 +#define MAX_CRITTER_VIEWS 8 + +typedef struct CritterProp { + ubyte intelligence; + CritterAttack attacks[NUM_ALTERNATE_ATTACKS]; + ubyte perception; // each ai interval that player is seeable, this is percent of detection + ubyte defense; + ubyte proj_offset; // slow projectile offset (y) + // int speed; + int flags; // flying??, shield??, fixed point?, does it move?? + uchar mirror; // should it's views be mirrored? + ubyte frames[NUM_CRITTER_POSTURES]; // number of animation frames. + ubyte anim_speed; + ubyte attack_sound; // play this when attacking. -1 for no sound. + ubyte near_sound; // play when creature is nearby + ubyte hurt_sound; // play when damaged a large percentage + ubyte death_sound; // play when dying + ubyte notice_sound; // play when it notices the player + int corpse; // object triple of thing to put here when we die. + ubyte views; // number of views for multi-view postures + ubyte alt_perc; // percentage of using alternate attack + ubyte disrupt_perc; // chance of being disrupted if hit while attacking + ubyte treasure_type; // what kind of loot this critter carries. + ubyte hit_effect; // what kind of class of hit effects should we do + ubyte fire_frame; // what frame do we fire on +} CritterProp; + +// ------------------ +// Subclass typedefs + +typedef struct MutantCritterProp { + ubyte dummy; +} MutantCritterProp; + +typedef struct RobotCritterProp { + ubyte backup_weapon; + ubyte metal_thickness; +} RobotCritterProp; + +typedef struct CyborgCritterProp { + short shield_energy; +} CyborgCritterProp; + +#define NUM_VCOLORS 3 +typedef struct CyberCritterProp { + uchar vcolors[NUM_VCOLORS]; + uchar alt_vcolors[NUM_VCOLORS]; +} CyberCritterProp; + +#define EMPTY_STRUCTS + +typedef struct RobobabeCritterProp { +#ifdef EMPTY_STRUCTS + ubyte dummy; +#endif +} RobobabeCritterProp; + +// Quantity defines - subclasses + +#define NUM_MUTANT_CRITTER 9 +#define NUM_ROBOT_CRITTER 12 +#define NUM_CYBORG_CRITTER 7 +#define NUM_CYBER_CRITTER 7 +// Note that for our purposes, ROBOBABE = PLOT which might in itself tell you something +// significant about our game +#define NUM_ROBOBABE_CRITTER 2 + +#define NUM_CRITTER \ + (NUM_MUTANT_CRITTER + NUM_ROBOT_CRITTER + NUM_CYBORG_CRITTER + NUM_CYBER_CRITTER + NUM_ROBOBABE_CRITTER) + +// Enumeration of subclasses +// + +// Critter +#define CRITTER_SUBCLASS_MUTANT 0 +#define CRITTER_SUBCLASS_ROBOT 1 +#define CRITTER_SUBCLASS_CYBORG 2 +#define CRITTER_SUBCLASS_CYBER 3 +#define CRITTER_SUBCLASS_ROBOBABE 4 + +// Lots of posture stuff... + +// view = 0-7 side views at angles +// view = 8 top +// view = 9 bottom + +#define FRONT_VIEW 6 + +#define STANDING_CRITTER_POSTURE 0 +#define MOVING_CRITTER_POSTURE 1 +#define ATTACKING_CRITTER_POSTURE 2 +#define ATTACK_REST_CRITTER_POSTURE 3 +#define KNOCKBACK_CRITTER_POSTURE 4 +#define DEATH_CRITTER_POSTURE 5 +#define DISRUPT_CRITTER_POSTURE 6 +#define ATTACKING2_CRITTER_POSTURE 7 + +#define FIRST_FRONT_POSTURE ATTACKING_CRITTER_POSTURE +#define DEFAULT_CRITTER_POSTURE STANDING_CRITTER_POSTURE + +// single-view postures +#define CRITTER_ATTACK_BASE RES_bmCritterAttack_0 +#define CRITTER_ATTACK2_BASE RES_bmCritterAttack2_0 +#define CRITTER_ATTACK_REST_BASE RES_bmCritterAttackRest_0 +#define CRITTER_DEATH_BASE RES_bmCritterDeath_0 +#define CRITTER_DISRUPT_BASE RES_bmCritterDisrupt_0 +#define CRITTER_KNOCKBACK_BASE RES_bmCritterKnockback_0 + +// multi-view postures +#define CRITTER_MOVE_BASE RES_bmCritterMovement_0 +#define CRITTER_STAND_BASE RES_bmCritterStanding_0 + +// Properties of subclasses +// + +#ifdef __OBJSIM_SRC +CritterProp CritterProps[NUM_CRITTER]; +MutantCritterProp MutantCritterProps[NUM_MUTANT_CRITTER]; +RobotCritterProp RobotCritterProps[NUM_ROBOT_CRITTER]; +CyborgCritterProp CyborgCritterProps[NUM_CYBORG_CRITTER]; +CyberCritterProp CyberCritterProps[NUM_CYBER_CRITTER]; +RobobabeCritterProp RobobabeCritterProps[NUM_ROBOBABE_CRITTER]; +#else +extern CritterProp CritterProps[NUM_CRITTER]; +extern MutantCritterProp MutantCritterProps[NUM_MUTANT_CRITTER]; +extern RobotCritterProp RobotCritterProps[NUM_ROBOT_CRITTER]; +extern CyborgCritterProp CyborgCritterProps[NUM_CYBORG_CRITTER]; +extern CyberCritterProp CyberCritterProps[NUM_CYBER_CRITTER]; +extern RobobabeCritterProp RobobabeCritterProps[NUM_ROBOBABE_CRITTER]; +#endif + +#ifdef __OBJSIM_SRC +ObjCritter objCritters[NUM_OBJECTS_CRITTER]; +ObjCritter default_critter; +#else +extern ObjCritter objCritters[NUM_OBJECTS_CRITTER]; +extern ObjCritter default_critter; +#endif + +#define get_crit_posture(osid) (objCritters[osid].current_posture & 0xFu) +#define set_crit_posture(osid, newpos) \ + objCritters[osid].current_posture = (objCritters[osid].current_posture & 0xF0u) + newpos + +#define get_crit_view(oisd) (objCritters[osid].current_posture >> 8) +#define set_crit_view(osid, newview) \ + objCritters[osid].current_posture = (newview << 8) + (objCritters[osid].current_posture & 0xF) + +#pragma pack(pop) + +#endif // __OBJCRIT_H diff --git a/engine/src/GameSrc/Headers/objects.h b/engine/src/GameSrc/Headers/objects.h new file mode 100644 index 0000000..fa65e8d --- /dev/null +++ b/engine/src/GameSrc/Headers/objects.h @@ -0,0 +1,316 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef __OBJECTS_H +#define __OBJECTS_H + +/* +** $Header: r:/prj/cit/src/inc/RCS/objects.h 1.18 1994/08/30 07:15:13 xemu Exp $ +* +*/ + +// The overriding concept: there is a master array of all objects in the game. +// This is an array of the Obj structure. Every object has a distinct ID +// which is its index in the array. The Obj structure contains only +// information which is common to all objects. Space for extra information is +// allocated separately, as the amount of extra information varies widely +// from object to object. There are several different classes of objects +// (the typedef ObjClass), each of which has a distinct array storing +// information specific to objects of that class. This information is kept +// track of in the following manner: the Obj structure contains a "class" +// field, a "subclass" field, and a "specID" field. The specID field +// specifies what number object of this particular class this object is; +// any given combination of class and specID is unique. specID is used +// as an index into that class's array to retrieve class-specific information. + +// Within a class, there may be many subclasses. For example, rocks and +// buildings are both static objects, but rocks probably only need one +// field for physical state while a building would more likely want at +// least four. They do share the same data structure, but may interpret it +// differently. +typedef uchar ObjSubClass; + +// An object is ID'd by its position in the Obj array. For all of these +// arrays, 0 is a null object, and the zeroth element of the array is +// reserved for storing special information. + +typedef short ObjID; +#define OBJ_NULL 0 // null object + +typedef short ObjRefID; +#define OBJ_REF_NULL 0 + +typedef short ObjSpecID; +#define OBJ_SPEC_NULL 0 + +// Now that we have some basic typedef's, we include the application-specific ones. +#ifndef OBJAPP_H +#include "objapp.h" +#endif + +#pragma pack(push,2) + +// The common data for all objects +typedef struct Obj { + uchar active; // does this object really exist? + ObjClass obclass; // what class this is + ObjSubClass subclass; // subclass within that class + ObjSpecID specID; // ID within that class + union { + ObjRefID ref; // what refers to this + ObjID headused; + }; + union { + ObjID next; // next Obj in free chain or used chain + ObjID headfree; + }; + ObjID prev; // prev Obj in used chain + ObjLoc loc; // location + ObjInfo info; // extra, application-specific information +} Obj; + +typedef struct old_Obj { + uchar active; // does this object really exist? + ObjClass obclass; // what class this is + ObjSubClass subclass; // subclass within that class + ObjSpecID specID; // ID within that class + union { + ObjRefID ref; // what refers to this + ObjID headused; + }; + union { + ObjID next; // next Obj in free chain or used chain + ObjID headfree; + }; + ObjID prev; // prev Obj in used chain + ObjLoc loc; // location + old_ObjInfo info; // extra, application-specific information +} old_Obj; + +//#define FORALLOBJS(pmo) for (pmo = (objs[OBJ_NULL]).headused; pmo != OBJ_NULL; pmo = objs[pmo].next) + +// The "next" field of object 0 is the ID of the first element of the chain +// of "free" objects; objects that are not currently in the world. The "next" +// field of that object points to the next element in the free chain, and so +// on, until a "next" field of 0 means that there are no more free objects. +// +// For an object not in the free chain, the "next" field points to the next +// object in the used chain. The "ref" field (sorry, next was taken) of the +// zeroth element points to the head of the used chain. Every element is in +// either the free chain or the used chain. + +// The header for an array of class-specific data +typedef struct ObjSpecHeader { + uchar size; // size of array + uchar struct_size; // size of each element + char *data; // pointer to array of class-specific data +} ObjSpecHeader; + +// The common part of any class-specific structure. You can cast any class-specific +// structure to an ObjSpec if you want to write super-general code. +// As with Objs and ObjRefs, the 0th element is reserved, and its next +// field is the head of the free chain, while its id element is the head of the +// used chain. The next field is the next element in the free chain or used chain, +// as appropriate. + +typedef struct ObjSpec { + union { + struct { + ObjID id : 15; // ID in master list + ushort tile : 1; // look in tiled array? + } bits; + ObjSpecID headused; + }; + union { + ObjSpecID next; // next struct in free or used chain + ObjSpecID headfree; + }; + ObjSpecID prev; // prev struct in used chain +} ObjSpec; + +// This macro permutes the ObjSpecID pmo through all of the ObjSpecs in the +// used chain of objspec. "tile" is set to whether it's a tiled object. +// Note that the "tile =" line in the third part refers to the pmo that +// has been set earlier that line. +// +#define FORALLOBJSPECS(pmo, tiled, objspec) \ + for (pmo = (objspec[OBJ_SPEC_NULL]).id, tiled = objspec[pmo].tile; pmo != OBJ_SPEC_NULL; \ + pmo = objspec[pmo].next, tiled = objspec[pmo].tile) + +// The master array of objects +extern Obj objs[NUM_OBJECTS]; + +// The array of class-specific headers. Index into this array by an ObjClass. +extern /*const*/ ObjSpecHeader objSpecHeaders[NUM_CLASSES]; + +////////////////////////////// +// +// Now, we get to actual references of objects. Since a given object can reside +// in more than one map element (if it is large), we need different map elements to be +// able to refer to it. A map element, then, contains not the object itself +// (an Obj) but rather a reference to an object (an ObjRef). Each Obj may +// have several different ObjRefs referring to it, each in a different map element. +// All the ObjRefs referring to a given Obj are linked in a circular list by +// the nextref field, and every Obj contains the ID of some ObjRef referring +// to it in its ref field. As an Obj moves from location to location, at some +// times occupying just one map element and at other times overlapping two or more, +// appropriate ObjRefs will be created and deleted in those map elements. Note +// that if we implement an Underworld-like "link" field for objects (used to +// specify that one object somehow "contains" another), it can be put in the +// Obj itself and does not need to be put out in the ObjRef. + +typedef struct ObjRef { + ObjRefState state; // location + ObjID obj; // what Obj this refers to + ObjRefID next; // next ObjRef in this square, or OBJ_REF_NULL if last + ObjRefID nextref; // next ObjRef to refer to the same Obj +} ObjRef; + +extern ObjRef objRefs[NUM_REF_OBJECTS]; + +////////////////////////////// +// +// Routines to make it easy to deal with objects only once + +extern uchar objsDealt[NUM_OBJECTS / 8]; + +#define ObjsClearDealt() \ + do { \ + LG_memset(objsDealt, 0, NUM_OBJECTS / 8); \ + } while (0) +#define ObjSetDealt(x) \ + do { \ + objsDealt[(x) >> 3] |= (1 << ((x)&7)); \ + } while (0) +#define ObjClearDealt(x) \ + do { \ + objsDealt[(x) >> 3] &= ~(1 << ((x)&7)); \ + } while (0) +#define ObjCheckDealt(x) (objsDealt[(x) >> 3] & (1 << ((x)&7))) + +////////////////////////////// +// +// Here is a structure by which an object's location is specified. +// Physics will use it to tell the object manager how to update the world. + +#define MAX_REFS_PER_OBJ 12 // set as appropriate +#define MAX_OBJS_CHANGING 20 // ditto + +typedef struct ObjLocState { + ObjID obj; // which obj is this? + ObjLoc loc; + ObjRefState refs[MAX_REFS_PER_OBJ + 1]; // list of points extended into +} ObjLocState; + +// refs is a list of map elements that the Obj extends into; i.e., the bins +// that should have ObjRefs referring to that object, along with any +// extra state. The list is terminated by an ObjRefState with a null bin. + +// Physics puts information about objects that have moved in objLocStates. +extern ObjLocState objLocStates[MAX_OBJS_CHANGING]; + +// numObjLocStates contains the number of entries of objLocStates that are valid. +extern uchar numObjLocStates; + +////////////////////////////// +// +// Here is a structure that we use to tell physics what objects are interacting +// with a given object. + +#define MAX_REFS_COLLIDING 32 + +////////////////////////////// +// +// Hashing stuff +// + +#ifdef HASH_OBJECTS + +typedef short ObjHashElemID; + +typedef struct ObjHashElem { + ObjRefID ref; + ObjHashElemID next; +} ObjHashElem; + +// The entries which can actually be accessed by the hash function +// range from OBJ_HASH_HEAD_ENTRIES_START to that + OBJ_HASH_HEAD_ENTRIES. +// We don't start at zero because we want to reserve the zeroth element +// to be null. + +extern ObjHashElem objHashTable[OBJ_HASH_ENTRIES]; + +// ObjGetHashElem() is called by the macro ObjRefHead(), which tends +// to be called in inner loops. Thus, making it a function slows things +// down a lot. The solution used here is to make it a macro that handles +// the simple cases (which happen most of the time) and that calls a function +// when it encounters the complicated case (a chain is hanging off of the +// entry). This seems to speed up code which calls ObjRefHead() repeatedly +// by a factor of two. +// +// See the full ObjGetHashElem() function in objects.c for a commented +// version of what this is doing. +// +// The global variable is a pain but I don't see a way to get rid of it. + +#ifdef USE_FUNCTION_FOR_HASH_GET +ObjHashElemID ObjGetHashElem(ObjRefStateBin thebin, uchar create); +#else +extern ObjHashElemID HASHENTRY; // global, found in objects.c +#define ObjGetHashElem(thebin, create) \ + (HASHENTRY = OBJ_HASH_FUNC(thebin), \ + (objHashTable[HASHENTRY].ref == OBJ_REF_NULL \ + ? (create ? HASHENTRY : 0) \ + : (ObjRefStateBinEqual(objRefs[objHashTable[HASHENTRY].ref].state.bin, thebin) \ + ? HASHENTRY \ + : ObjGetHashElemFromChain(thebin, create, HASHENTRY)))) +ObjHashElemID ObjGetHashElemFromChain(ObjRefStateBin bin, uchar create, ObjHashElemID firstentry); +#endif + +#endif // HASH_OBJECTS + +////////////////////////////// +// +// Public functions +// + +void ObjsInit(void); +uchar ObjAndSpecGrab(ObjClass obclass, ObjID *id, ObjSpecID *specid); +uchar ObjPlace(ObjID id, ObjLoc *loc); +ObjID ObjRefDel(ObjRefID ref); +ObjRefID ObjRefMake(ObjID obj, ObjRefState refstate); +uchar ObjDel(ObjID obj); +uchar ObjChangeClass(ObjID obj, ObjClass obclass); +uchar ObjUpdateLocs(ObjLocState *olsp); +uchar ObjsUpdateLocs(void); +void ObjPossibleCollisions(ObjID obj, ObjID *colls); + +void ObjBinPrint(ObjRefStateBin bin); +uchar ObjSysOkay(void); +void ObjPrintRefs(ObjID obj); + +////////////// + +uchar ObjInstInit(ObjID id, ObjSpecID specid, ObjSubClass subclass); + +////////////////////////////// + +#pragma pack(pop) + +#endif // __OBJECTS_H diff --git a/engine/src/GameSrc/Headers/objgame.h b/engine/src/GameSrc/Headers/objgame.h new file mode 100644 index 0000000..16f184e --- /dev/null +++ b/engine/src/GameSrc/Headers/objgame.h @@ -0,0 +1,425 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __OBJGAME_H +#define __OBJGAME_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/objgame.h $ + * $Revision: 1.52 $ + * $Author: minman $ + * $Date: 1994/07/30 00:19:06 $ + * + */ + +// Includes +#include "objclass.h" + +#pragma pack(push,2) + +// Instance Typedefs +typedef struct { + // COMMON_OBJSPEC_FIELDS; + union { + ObjID id; + ObjSpecID headused; + }; + union { + ObjSpecID next; + ObjSpecID headfree; + }; + ObjSpecID prev; + ubyte trap_type; + ubyte destroy_count; + uint comparator; + uint p1, p2, p3, p4; + short access_level; +} ObjFixture; + +typedef struct { + // COMMON_OBJSPEC_FIELDS; + union { + ObjID id; + ObjSpecID headused; + }; + union { + ObjSpecID next; + ObjSpecID headfree; + }; + ObjSpecID prev; + short locked; + ubyte stringnum; + ubyte cosmetic_value; + ubyte access_level; + ubyte autoclose_time; + ObjID other_half; +} ObjDoor; + +typedef struct { + // COMMON_OBJSPEC_FIELDS; + union { + ObjID id; + ObjSpecID headused; + }; + union { + ObjSpecID next; + ObjSpecID headfree; + }; + ObjSpecID prev; + ubyte start_frame; + ubyte end_frame; + ObjID owner; +} ObjAnimating; + +typedef struct { + // COMMON_OBJSPEC_FIELDS; + union { + ObjID id; + ObjSpecID headused; + }; + union { + ObjSpecID next; + ObjSpecID headfree; + }; + ObjSpecID prev; + ubyte trap_type; + ubyte destroy_count; + uint comparator; + uint p1, p2, p3, p4; +} ObjTrap; + +typedef struct { + // COMMON_OBJSPEC_FIELDS; + union { + ObjID id; + ObjSpecID headused; + }; + union { + ObjSpecID next; + ObjSpecID headfree; + }; + ObjSpecID prev; + int contents1; + int contents2; + ubyte dim_x; + ubyte dim_y; + ubyte dim_z; + int data1; +} ObjContainer; + +// Class typedefs +typedef struct FixtureProp { + ubyte characteristics; +} FixtureProp; + +typedef struct DoorProp { + ubyte security_level; // i.e difficulty to unlock +} DoorProp; + +#define ANIM_FLAG_NONE 0 +#define ANIM_FLAG_REPEAT 1 +#define ANIM_FLAG_REVERSE 2 + +typedef struct AnimatingProp { + ubyte speed; + ubyte flags; +} AnimatingProp; + +typedef struct TrapProp { + ubyte dummy; +} TrapProp; + +typedef struct ContainerProp { + ObjID contents; // obviously not the way to do it, but you get the idea + ubyte num_contents; +} ContainerProp; + +// Subclass typedefs +typedef struct ControlFixtureProp { + ubyte dummy; +} ControlFixtureProp; + +typedef struct ReceptacleFixtureProp { + ubyte dummy; +} ReceptacleFixtureProp; + +typedef struct TerminalFixtureProp { + ubyte dummy; +} TerminalFixtureProp; + +typedef struct PanelFixtureProp { + ubyte dummy; +} PanelFixtureProp; + +typedef struct VendingFixtureProp { + ubyte dummy; +} VendingFixtureProp; + +typedef struct CyberFixtureProp { + ubyte dummy; +} CyberFixtureProp; + +typedef struct _NormalDoorProp { + ubyte dummy; +} NormalDoorProp; + +typedef struct _DoorwaysDoorProp { + ubyte dummy; +} DoorwaysDoorProp; + +typedef struct _ForceDoorProp { + ubyte dummy; +} ForceDoorProp; + +typedef struct _ElevatorDoorProp { + ubyte dummy; +} ElevatorDoorProp; + +typedef struct _SpecialDoorProp { + ubyte dummy; +} SpecialDoorProp; + +typedef struct _ObjectsAnimatingProp { + ubyte dummy; +} ObjectsAnimatingProp; + +typedef struct _TransitoryAnimatingProp { + ubyte dummy; +} TransitoryAnimatingProp; + +typedef struct _ExplosionAnimatingProp { + ubyte frame_explode; +} ExplosionAnimatingProp; + +typedef struct _TriggerTrapProp { + ubyte dummy; +} TriggerTrapProp; + +typedef struct _FeedbacksTrapProp { + ubyte dummy; +} FeedbacksTrapProp; + +typedef struct _SecretTrapProp { + ubyte dummy; +} SecretTrapProp; + +typedef struct _ActualContainerProp { + ubyte dummy; +} ActualContainerProp; + +typedef struct _WasteContainerProp { + ubyte dummy; +} WasteContainerProp; + +typedef struct _LiquidContainerProp { + ubyte dummy; +} LiquidContainerProp; + +typedef struct _MutantCorpseContainerProp { + ubyte dummy; +} MutantCorpseContainerProp; + +typedef struct _RobotCorpseContainerProp { + ubyte dummy; +} RobotCorpseContainerProp; + +typedef struct _CyborgCorpseContainerProp { + ubyte dummy; +} CyborgCorpseContainerProp; + +typedef struct _OtherCorpseContainerProp { + ubyte dummy; +} OtherCorpseContainerProp; + +// Quantity defines - subclasses +// Fixture +#define NUM_CONTROL_FIXTURE 9 +#define NUM_RECEPTACLE_FIXTURE 7 +#define NUM_TERMINAL_FIXTURE 3 +#define NUM_PANEL_FIXTURE 11 +#define NUM_VENDING_FIXTURE 2 +#define NUM_CYBER_FIXTURE 3 + +// Door +#define NUM_NORMAL_DOOR 10 +#define NUM_DOORWAYS_DOOR 9 +#define NUM_FORCE_DOOR 7 +#define NUM_ELEVATOR_DOOR 5 +#define NUM_SPECIAL_DOOR 10 + +// Animating +#define NUM_OBJECT_ANIMATING 9 +#define NUM_TRANSITORY_ANIMATING 11 +#define NUM_EXPLOSION_ANIMATING 14 + +// Trap +#define NUM_TRIGGER_TRAP 13 +#define NUM_FEEDBACKS_TRAP 1 +#define NUM_SECRET_TRAP 5 + +// Container +#define NUM_ACTUAL_CONTAINER 3 +#define NUM_WASTE_CONTAINER 3 +#define NUM_LIQUID_CONTAINER 4 +#define NUM_MUTANT_CORPSE_CONTAINER 8 +#define NUM_ROBOT_CORPSE_CONTAINER 13 +#define NUM_CYBORG_CORPSE_CONTAINER 7 +#define NUM_OTHER_CORPSE_CONTAINER 8 + +#define NUM_FIXTURE \ + (NUM_CONTROL_FIXTURE + NUM_RECEPTACLE_FIXTURE + NUM_TERMINAL_FIXTURE + NUM_PANEL_FIXTURE + NUM_CYBER_FIXTURE + \ + NUM_VENDING_FIXTURE) +#define NUM_DOOR (NUM_NORMAL_DOOR + NUM_DOORWAYS_DOOR + NUM_FORCE_DOOR + NUM_ELEVATOR_DOOR + NUM_SPECIAL_DOOR) +#define NUM_ANIMATING (NUM_OBJECT_ANIMATING + NUM_TRANSITORY_ANIMATING + NUM_EXPLOSION_ANIMATING) +#define NUM_TRAP (NUM_TRIGGER_TRAP + NUM_FEEDBACKS_TRAP + NUM_SECRET_TRAP) +#define NUM_CONTAINER \ + (NUM_ACTUAL_CONTAINER + NUM_WASTE_CONTAINER + NUM_LIQUID_CONTAINER + NUM_MUTANT_CORPSE_CONTAINER + \ + NUM_ROBOT_CORPSE_CONTAINER + NUM_CYBORG_CORPSE_CONTAINER + NUM_OTHER_CORPSE_CONTAINER) + +// Enumeration of subclasses +// Fixture +#define FIXTURE_SUBCLASS_CONTROL 0 +#define FIXTURE_SUBCLASS_RECEPTACLE 1 +#define FIXTURE_SUBCLASS_TERMINAL 2 +#define FIXTURE_SUBCLASS_PANEL 3 +#define FIXTURE_SUBCLASS_VENDING 4 +#define FIXTURE_SUBCLASS_CYBER 5 + +// Door +#define DOOR_SUBCLASS_NORMAL 0 +#define DOOR_SUBCLASS_DOORWAYS 1 +#define DOOR_SUBCLASS_FORCE 2 +#define DOOR_SUBCLASS_ELEVATOR 3 +#define DOOR_SUBCLASS_SPECIAL 4 + +// Animating +#define ANIMATING_SUBCLASS_OBJECTS 0 +#define ANIMATING_SUBCLASS_TRANSITORY 1 +#define ANIMATING_SUBCLASS_EXPLOSION 2 + +// Trap +#define TRAP_SUBCLASS_TRIGGER 0 +#define TRAP_SUBCLASS_FEEDBACKS 1 +#define TRAP_SUBCLASS_SECRET 2 + +// Container +#define CONTAINER_SUBCLASS_ACTUAL 0 +#define CONTAINER_SUBCLASS_WASTE 1 +#define CONTAINER_SUBCLASS_LIQUID 2 +#define CONTAINER_SUBCLASS_MUTANT_CORPSE 3 +#define CONTAINER_SUBCLASS_ROBOT_CORPSE 4 +#define CONTAINER_SUBCLASS_CYBORG_CORPSE 5 +#define CONTAINER_SUBCLASS_OTHER_CORPSE 6 + +#ifdef __OBJSIM_SRC +FixtureProp FixtureProps[NUM_FIXTURE]; +ControlFixtureProp ControlFixtureProps[NUM_CONTROL_FIXTURE]; +ReceptacleFixtureProp ReceptacleFixtureProps[NUM_RECEPTACLE_FIXTURE]; +TerminalFixtureProp TerminalFixtureProps[NUM_TERMINAL_FIXTURE]; +PanelFixtureProp PanelFixtureProps[NUM_PANEL_FIXTURE]; +VendingFixtureProp VendingFixtureProps[NUM_VENDING_FIXTURE]; +CyberFixtureProp CyberFixtureProps[NUM_CYBER_FIXTURE]; + +DoorProp DoorProps[NUM_DOOR]; +NormalDoorProp NormalDoorProps[NUM_NORMAL_DOOR]; +DoorwaysDoorProp DoorwaysDoorProps[NUM_DOORWAYS_DOOR]; +ForceDoorProp ForceDoorProps[NUM_FORCE_DOOR]; +ElevatorDoorProp ElevatorDoorProps[NUM_ELEVATOR_DOOR]; +SpecialDoorProp SpecialDoorProps[NUM_SPECIAL_DOOR]; + +AnimatingProp AnimatingProps[NUM_ANIMATING]; +ObjectsAnimatingProp ObjectsAnimatingProps[NUM_OBJECT_ANIMATING]; +TransitoryAnimatingProp TransitoryAnimatingProps[NUM_TRANSITORY_ANIMATING]; +ExplosionAnimatingProp ExplosionAnimatingProps[NUM_EXPLOSION_ANIMATING]; + +TrapProp TrapProps[NUM_TRAP]; +TriggerTrapProp TriggerTrapProps[NUM_TRIGGER_TRAP]; +FeedbacksTrapProp FeedbacksTrapProps[NUM_FEEDBACKS_TRAP]; +SecretTrapProp SecretTrapProps[NUM_SECRET_TRAP]; + +ContainerProp ContainerProps[NUM_CONTAINER]; +ActualContainerProp ActualContainerProps[NUM_ACTUAL_CONTAINER]; +WasteContainerProp WasteContainerProps[NUM_WASTE_CONTAINER]; +LiquidContainerProp LiquidContainerProps[NUM_LIQUID_CONTAINER]; +MutantCorpseContainerProp MutantCorpseContainerProps[NUM_MUTANT_CORPSE_CONTAINER]; +RobotCorpseContainerProp RobotCorpseContainerProps[NUM_ROBOT_CORPSE_CONTAINER]; +CyborgCorpseContainerProp CyborgCorpseContainerProps[NUM_CYBORG_CORPSE_CONTAINER]; +OtherCorpseContainerProp OtherCorpseContainerProps[NUM_OTHER_CORPSE_CONTAINER]; +#else +extern FixtureProp FixtureProps[NUM_FIXTURE]; +extern ControlFixtureProp ControlFixtureProps[NUM_CONTROL_FIXTURE]; +extern ReceptacleFixtureProp ReceptacleFixtureProps[NUM_RECEPTACLE_FIXTURE]; +extern TerminalFixtureProp TerminalFixtureProps[NUM_TERMINAL_FIXTURE]; +extern PanelFixtureProp PanelFixtureProps[NUM_PANEL_FIXTURE]; +extern VendingFixtureProp VendingFixtureProps[NUM_VENDING_FIXTURE]; +extern CyberFixtureProp CyberFixtureProps[NUM_CYBER_FIXTURE]; + +extern DoorProp DoorProps[NUM_DOOR]; +extern NormalDoorProp NormalDoorProps[NUM_NORMAL_DOOR]; +extern DoorwaysDoorProp DoorwaysDoorProps[NUM_DOORWAYS_DOOR]; +extern ForceDoorProp ForceDoorProps[NUM_FORCE_DOOR]; +extern ElevatorDoorProp ElevatorDoorProps[NUM_ELEVATOR_DOOR]; +extern SpecialDoorProp SpecialDoorProps[NUM_SPECIAL_DOOR]; + +extern AnimatingProp AnimatingProps[NUM_ANIMATING]; +extern ObjectsAnimatingProp ObjectsAnimatingProps[NUM_OBJECT_ANIMATING]; +extern TransitoryAnimatingProp TransitoryAnimatingProps[NUM_TRANSITORY_ANIMATING]; +extern ExplosionAnimatingProp ExplosionAnimatingProps[NUM_EXPLOSION_ANIMATING]; + +extern TrapProp TrapProps[NUM_TRAP]; +extern TriggerTrapProp TriggerTrapProps[NUM_TRIGGER_TRAP]; +extern FeedbacksTrapProp FeedbacksTrapProps[NUM_FEEDBACKS_TRAP]; +extern SecretTrapProp SecretTrapProps[NUM_SECRET_TRAP]; + +extern ContainerProp ContainerProps[NUM_CONTAINER]; +extern ActualContainerProp ActualContainerProps[NUM_ACTUAL_CONTAINER]; +extern WasteContainerProp WasteContainerProps[NUM_WASTE_CONTAINER]; +extern LiquidContainerProp LiquidContainerProps[NUM_LIQUID_CONTAINER]; +extern MutantCorpseContainerProp MutantCorpseContainerProps[NUM_MUTANT_CORPSE_CONTAINER]; +extern RobotCorpseContainerProp RobotCorpseContainerProps[NUM_ROBOT_CORPSE_CONTAINER]; +extern CyborgCorpseContainerProp CyborgCorpseContainerProps[NUM_CYBORG_CORPSE_CONTAINER]; +extern OtherCorpseContainerProp OtherCorpseContainerProps[NUM_OTHER_CORPSE_CONTAINER]; +#endif + +#ifdef __OBJSIM_SRC +ObjFixture objFixtures[NUM_OBJECTS_FIXTURE]; +ObjDoor objDoors[NUM_OBJECTS_DOOR]; +ObjAnimating objAnimatings[NUM_OBJECTS_ANIMATING]; +ObjTrap objTraps[NUM_OBJECTS_TRAP]; +ObjContainer objContainers[NUM_OBJECTS_CONTAINER]; +ObjFixture default_fixture; +ObjDoor default_door; +ObjAnimating default_animating; +ObjTrap default_trap; +ObjContainer default_container; +#else +extern ObjFixture objFixtures[NUM_OBJECTS_FIXTURE]; +extern ObjDoor objDoors[NUM_OBJECTS_DOOR]; +extern ObjAnimating objAnimatings[NUM_OBJECTS_ANIMATING]; +extern ObjTrap objTraps[NUM_OBJECTS_TRAP]; +extern ObjContainer objContainers[NUM_OBJECTS_CONTAINER]; +extern ObjFixture default_fixture; +extern ObjDoor default_door; +extern ObjAnimating default_animating; +extern ObjTrap default_trap; +extern ObjContainer default_container; +#endif + +#pragma pack(pop) + +#endif // __OBJGAME_H diff --git a/engine/src/GameSrc/Headers/objload.h b/engine/src/GameSrc/Headers/objload.h new file mode 100644 index 0000000..1ab4e6c --- /dev/null +++ b/engine/src/GameSrc/Headers/objload.h @@ -0,0 +1,72 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __OBJLOAD_H +#define __OBJLOAD_H + +/* + * $Source: u:/inc/RCS/objload.h $ + * $Revision: 1.2 $ + * $Author: minman $ + * $Date: 1994/08/27 01:25:54 $ + * + */ + +// Includes +#include "objprop.h" +#include "objapp.h" + +#pragma pack(push,2) + +#define NUM_OBJECT_BIT_LEN ((NUM_OBJECT + 7) >> 3) + +#define ObjLoadMeSetAll() \ + do { \ + LG_memset(loadme, 0xFF, NUM_OBJECT_BIT_LEN); \ + } while (0) +#define ObjLoadMeClearAll() \ + do { \ + LG_memset(loadme, 0, NUM_OBJECT_BIT_LEN); \ + } while (0) +#define ObjLoadMeSet(opnum) \ + do { \ + loadme[(opnum) >> 3] |= 1 << ((opnum)&0x7); \ + } while (0) +#define ObjLoadMeClear(opnum) \ + do { \ + loadme[(opnum) >> 3] &= ~(1 << ((opnum)&0x7)); \ + } while (0) +#define ObjLoadMeCheck(opnum) (loadme[(opnum) >> 3] & (1 << ((opnum)&0x7))) + +#define EXTRA_FRAMES 500 + +extern errtype obj_load_art(uchar flush_all); + +#ifdef __OBJSIM_SRC +LGPoint anchors_3d[NUM_OBJECT + EXTRA_FRAMES]; +grs_bitmap *bitmaps_2d[NUM_OBJECT]; +grs_bitmap *bitmaps_3d[NUM_OBJECT + EXTRA_FRAMES]; +#else +extern LGPoint anchors_3d[NUM_OBJECT + EXTRA_FRAMES]; +extern grs_bitmap *bitmaps_2d[NUM_OBJECT]; +extern grs_bitmap *bitmaps_3d[NUM_OBJECT]; +#endif + +#pragma pack(pop) + +#endif // __OBJLOAD_H diff --git a/engine/src/GameSrc/Headers/objmode.h b/engine/src/GameSrc/Headers/objmode.h new file mode 100644 index 0000000..efc99fd --- /dev/null +++ b/engine/src/GameSrc/Headers/objmode.h @@ -0,0 +1,120 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __OBJMODE_H +#define __OBJMODE_H + +/* + * $Source: n:/project/cit/src/inc/RCS/objmode.h $ + * $Revision: 1.14 $ + * $Author: xemu $ + * $Date: 1994/01/06 10:34:20 $ + * + * $Log: objmode.h $ + * Revision 1.14 1994/01/06 10:34:20 xemu + * camera fun + * + * Revision 1.13 1993/09/25 16:58:12 xemu + * resized stuff + * + * Revision 1.12 1993/09/19 19:07:05 xemu + * cameras in editor + * + * Revision 1.11 1993/09/02 23:08:16 xemu + * angle me baby + * + * Revision 1.10 1993/07/20 11:08:05 xemu + * removed bitmap triple function + * + * Revision 1.9 1993/07/11 12:56:55 xemu + * hack bitmap func for Mahk + * + * Revision 1.8 1993/07/08 23:49:42 xemu + * object properties + * + * Revision 1.7 1993/06/28 12:57:51 xemu + * soem fixes + * + * Revision 1.6 1993/06/24 00:05:35 xemu + * minor move around + * + * Revision 1.5 1993/06/03 20:05:15 xemu + * browser stuff + * + * Revision 1.4 1993/05/24 15:19:19 xemu + * moved to own slab/loop + * + * Revision 1.3 1993/05/23 16:35:37 xemu + * added highlight func and mode brush + * + * Revision 1.2 1993/05/21 17:46:04 xemu + * edit buttons, classes + * + * Revision 1.1 1993/05/20 20:58:16 xemu + * Initial revision + * + * + */ + +// Includes +#include "objects.h" +#include "map.h" + +// C Library Includes + +// System Library Includes + +// Master Game Includes + +// Game Library Includes + +// Game Object Includes + +// Defines + +// Prototypes + +// Creates all appropriate gadgets for the object palette area, +// then hides them under the magic curtain of the palette region. +errtype object_palette_create(void); + +// Brings the object palette up to the front of the object palette area. +errtype object_palette_popup(void); + +errtype object_load_subclass(ObjClass loadclass); +errtype object_load_type(ObjClass loadclass, ubyte subclass); + +uchar object_find_func(int highlight_num); + +void object_mode_brush(MapElem *paint, FullMap *map, LGRect *r, LGPoint square, void *brushdata); + +// Globals +#ifdef __OBJMODE_SRC +ObjRefID current_ref = OBJ_REF_NULL; +int curr_x = 0, curr_y = 0; +ubyte curr_z = 0; +int curr_int = 0, curr_class = 0, curr_subclass = 0; +Point current_loc = {0, 0}; +#else +extern ObjRefID current_ref; +extern int curr_x, curr_y; +extern ubyte curr_z; +extern int curr_int, curr_class, curr_subclass; +#endif + +#endif // __OBJMODE_H diff --git a/engine/src/GameSrc/Headers/objprop.h b/engine/src/GameSrc/Headers/objprop.h new file mode 100644 index 0000000..386c931 --- /dev/null +++ b/engine/src/GameSrc/Headers/objprop.h @@ -0,0 +1,123 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __OBJPROP_H +#define __OBJPROP_H + +/* + * $Source: n:/project/cit/src/inc/RCS/objprop.h $ + * $Revision: 1.33 $ + * $Author: xemu $ + * $Date: 1994/06/01 01:39:20 $ + * + */ + +// Includes +#include "objwpn.h" +#include "objwarez.h" +#include "objstuff.h" +#include "objgame.h" +#include "objcrit.h" + +#pragma pack(push,2) + +// The overall object properties typedef +typedef struct ObjProp { + int mass; + short hit_points; + ubyte armor; + ubyte render_type; + ubyte physics_model; + ubyte hardness; + ubyte pep; + ubyte physics_xr; + ubyte physics_y; + ubyte physics_z; + int resistances; + ubyte defense_value; + ubyte toughness; + short flags; + short mfd_id; + short bitmap_3d; + ubyte destroy_effect; +} ObjProp; + +// Overall + +#define NUM_OBJECT \ + (NUM_GUN + NUM_AMMO + NUM_PHYSICS + NUM_GRENADE + NUM_DRUG + NUM_HARDWARE + NUM_SOFTWARE + NUM_BIGSTUFF + \ + NUM_SMALLSTUFF + NUM_FIXTURE + NUM_DOOR + NUM_ANIMATING + NUM_TRAP + NUM_CONTAINER + NUM_CRITTER) + +// This is "extra" in the sense of "fewer" +#define EXTRA_OBJ_ANIMS -600 + +// Number of subclasses for each classes +// + +#define NUM_SC_GUN 6 +#define NUM_SC_AMMO 7 +#define NUM_SC_PHYSICS 3 +#define NUM_SC_GRENADE 2 +#define NUM_SC_DRUG 1 +#define NUM_SC_HARDWARE 2 +#define NUM_SC_SOFTWARE 5 +#define NUM_SC_BIGSTUFF 8 +#define NUM_SC_SMALLSTUFF 8 +#define NUM_SC_FIXTURE 6 +#define NUM_SC_DOOR 5 +#define NUM_SC_ANIMATING 3 +#define NUM_SC_TRAP 3 +#define NUM_SC_CONTAINER 7 +#define NUM_SC_CRITTER 5 + +#define NUM_SUBCLASSES \ + NUM_SC_GUN + NUM_SC_AMMO + NUM_SC_PHYSICS + NUM_SC_GRENADE + NUM_SC_DRUG + NUM_SC_HARDWARE + NUM_SC_SOFTWARE + \ + NUM_SC_BIGSTUFF + NUM_SC_SMALLSTUFF + NUM_SC_FIXTURE + NUM_SC_DOOR + NUM_SC_ANIMATING + NUM_SC_TRAP + \ + NUM_SC_CONTAINER + NUM_SC_CRITTER + +#ifdef __OBJSIM_SRC +uchar num_subclasses[NUM_CLASSES] = { + NUM_SC_GUN, + NUM_SC_AMMO, + NUM_SC_PHYSICS, + NUM_SC_GRENADE, + NUM_SC_DRUG, + NUM_SC_HARDWARE, + NUM_SC_SOFTWARE, + NUM_SC_BIGSTUFF, + NUM_SC_SMALLSTUFF, + NUM_SC_FIXTURE, + NUM_SC_DOOR, + NUM_SC_ANIMATING, + NUM_SC_TRAP, + NUM_SC_CONTAINER, + NUM_SC_CRITTER +}; +#else +extern uchar num_subclasses[NUM_CLASSES]; +#endif + +#ifdef __OBJSIM_SRC +ObjProp ObjProps[NUM_OBJECT]; +#else +extern ObjProp ObjProps[NUM_OBJECT]; +#endif + +#pragma pack(pop) + +#endif // __OBJPROP_H diff --git a/engine/src/GameSrc/Headers/objsim.h b/engine/src/GameSrc/Headers/objsim.h new file mode 100644 index 0000000..2d95895 --- /dev/null +++ b/engine/src/GameSrc/Headers/objsim.h @@ -0,0 +1,138 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __OBJSIM_H +#define __OBJSIM_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/objsim.h $ + * $Revision: 1.60 $ + * $Author: xemu $ + * $Date: 1994/09/06 23:47:57 $ + * + * + */ + +// Includes +#include "objects.h" + +#define ID2SPEC(id) (objs[(id)].specID) + +#ifdef __OBJSIM_SRC +int ObjBaseArray[255]; +int ClassBaseArray[16][16]; +#else +extern int ObjBaseArray[255]; +extern int ClassBaseArray[16][16]; +#endif + +#define OBJ_PLAYER_CAMERA 0 +#define OBJ_STATIC_CAMERA 1 +#define OBJ_CURRENT_CAMERA 2 +#define OBJ_DYNAMIC_CAMERA 3 + +#define FIND_TRIPLE 0 +#define FIND_SUBCLASS 1 +#define FIND_CLASS 2 +#define FIND_ALL 3 + +// Functions for getting the bitmap for a given critter on a specific frame, from a specific view +// macros for now -- this is basically a cheap interface to the resource system's LRU cache +#define get_critter_bitmap(id, triple, posture, frame, view) \ + (lock_bitmap_from_ref(ref_from_critter_data((id), (triple), (posture), (frame), (view)))) +#define release_critter_bitmap(triple, posture, frame, view) \ + (RefUnlock(ref_from_critter_data((triple), (posture), (frame), (view)))) + +#define get_critter_bitmap_fast(id, triple, posture, frame, view, pref, panch) \ + (lock_bitmap_from_ref_anchor(*(pref) = ref_from_critter_data((id), (triple), (posture), (frame), (view)), (panch))) +#define release_critter_bitmap_fast(ref) (RefUnlock((ref))) + +// macros for calling those other macros with only an object ID +#define get_critter_bitmap_obj(id, view) \ + (get_critter_bitmap((id), ID2TRIP((id)), get_crit_posture(ID2SPEC(id)), objs[(id)].info.current_frame, (view))) +#define release_critter_bitmap_obj(id, view) \ + (release_critter_bitmap(ID2TRIP((id)), get_crit_posture(ID2SPEC(id)), objs[(id)].info.current_frame, (view))) +#define get_critter_bitmap_obj_fast(id, view, pref, panch) \ + (get_critter_bitmap_fast((id), ID2TRIP((id)), get_crit_posture(ID2SPEC(id)), objs[(id)].info.current_frame, \ + (view), (pref), (panch))) + +#define DOOR_ID_BASE 2400 +#define door_id(id) (DOOR_ID_BASE + CPTRIP(ID2TRIP(id))) + +Ref obj_cache_ref(ObjID id); +#define get_obj_cache_bitmap(id, pref) (lock_bitmap_from_ref(*(pref) = obj_cache_ref(id))) +#define release_obj_cache_bitmap(ref) (RefUnlock((ref))) + +// Animation +#define get_anim_bitmap(triple, frame) (obj_bitmaps[ObjProps[OPTRIP((triple))].bitmap_3d + (frame)]) +#define get_anim_bitmap_obj(id) (get_anim_bitmap(ID2TRIP((id)), objAnimatings[ID2SPEC(id)].current_frame)) +// keep starting index in lower two bytes, keep # of anim frames in next-to-top byte. +// wow, this has become gruesomely complex +// we will consider the possbility of just moving all the other flags around in order to +// steal that top bit, rather than this gruesome nightmare +#define BMAP_NUM_3D(x) (((x)&0x3FF) + (((x) & (0x8000)) >> 5)) +#define FRAME_NUM_3D(x) (((x)&0x7000) >> 12) +#define REPEAT_3D(x) (((x)&0x0800) >> 11) +#define ANIM_3D(x) (((x)&0x0400) >> 10) + +// Textured Polygons +grs_bitmap *bitmap_from_tpoly_data(int tpdata, ubyte *scale, int *index, uchar *type, Ref *ref); + +grs_bitmap *get_text_bitmap_obj(ObjID cobjid, char dest_type, char *pscale); + +// Prototypes +errtype obj_init(); +errtype obj_shutdown(); +ObjID obj_create_base(int triple); +ObjID obj_create_clone(ObjID dna); +errtype obj_move_to_vel(ObjID id, ObjLoc *newloc, uchar phys_tel, fix x_dot, fix y_dot, fix z_dot); +errtype obj_move_to(ObjID id, ObjLoc *newloc, uchar phys_tel); +uchar obj_destroy(ObjID id); +errtype obj_holocaust(); +uchar obj_holocaust_func(short keycode, ulong context, void *data); +errtype obj_load_properties(); +errtype obj_create_player(ObjLoc *plr_loc); +Ref ref_from_critter_data(ObjID oid, int triple, byte posture, short frame, short view); +errtype obj_zero_unused(); +grs_bitmap *obj_get_model_data(ObjID id, fix *x, fix *y, fix *z, grs_bitmap *bm2, Ref *ref1, Ref *ref2); +errtype obj_model_hack(ObjID id, uchar *hack_x, uchar *hack_y, uchar *hack_z, uchar *hack_type); +uchar obj_combat_destroy(ObjID id); +ObjID object_place(int triple, LGPoint square); +ushort obj_floor_compute(ObjID id, uchar flrh); +ushort obj_floor_height(ObjID id); +uchar obj_is_display(int triple); +errtype obj_physics_refresh_area(short x, short y, uchar use_floor); + +errtype obj_physics_refresh(short x, short y, uchar use_floor); + +void destroy_screen_callback_func(ObjID id, intptr_t user_data); +void diego_teleport_callback(ObjID id, intptr_t user_data); + +char extract_object_special_color(ObjID id); + +errtype set_door_data(ObjID id); + +ObjID physics_handle_to_id(physics_handle p); + +#ifdef __OBJSIM_SRC +ObjID current_object = OBJ_NULL; +#else +extern ObjID current_object; +#endif + +#endif // __OBJSIM_H diff --git a/engine/src/GameSrc/Headers/objstuff.h b/engine/src/GameSrc/Headers/objstuff.h new file mode 100644 index 0000000..6fead03 --- /dev/null +++ b/engine/src/GameSrc/Headers/objstuff.h @@ -0,0 +1,247 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __OBJSTUFF_H +#define __OBJSTUFF_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/objstuff.h $ + * $Revision: 1.23 $ + * $Author: xemu $ + * $Date: 1994/07/09 00:08:58 $ + * + * + */ + +// Includes +#include "objclass.h" + +// Instance typedefs +typedef struct { + // COMMON_OBJSPEC_FIELDS; + union { + ObjID id; + ObjSpecID headused; + }; + union { + ObjSpecID next; + ObjSpecID headfree; + }; + ObjSpecID prev; + short cosmetic_value; + int data1; + int data2; +} ObjBigstuff; + +typedef struct { + // COMMON_OBJSPEC_FIELDS; + union { + ObjID id; + ObjSpecID headused; + }; + union { + ObjSpecID next; + ObjSpecID headfree; + }; + ObjSpecID prev; + short cosmetic_value; + int data1; + int data2; +} ObjSmallstuff; + +// Class typedefs +typedef struct _BigstuffProp { + int data; +} BigstuffProp; + +typedef struct _SmallstuffProp { + short uses_flags; +} SmallstuffProp; + +// Subclass typedefs +typedef struct _ElectronicBigstuffProp { + ubyte dummy; +} ElectronicBigstuffProp; + +typedef struct _FurnishingBigstuffProp { + ubyte dummy; +} FurnishingBigstuffProp; + +typedef struct _OnthewallBigstuffProp { + ubyte dummy; +} OnthewallBigstuffProp; + +typedef struct _LightBigstuffProp { + ubyte dummy; +} LightBigstuffProp; + +typedef struct _LabgearBigstuffProp { + ubyte dummy; +} LabgearBigstuffProp; + +typedef struct _TechnoBigstuffProp { + ubyte dummy; +} TechnoBigstuffProp; + +typedef struct _DecorBigstuffProp { + ubyte dummy; +} DecorBigstuffProp; + +typedef struct _TerrainBigstuffProp { + ubyte dummy; +} TerrainBigstuffProp; + +typedef struct _UselessSmallstuffProp { + ubyte dummy; +} UselessSmallstuffProp; + +typedef struct _BrokenSmallstuffProp { + ubyte dummy; +} BrokenSmallstuffProp; + +typedef struct _CorpselikeSmallstuffProp { + ubyte dummy; +} CorpselikeSmallstuffProp; + +typedef struct _GearSmallstuffProp { + ubyte dummy; +} GearSmallstuffProp; + +typedef struct _CardsSmallstuffProp { + ubyte dummy; +} CardsSmallstuffProp; + +#define NUM_SMALLSTUFF_VCOLORS 6 +typedef struct _CyberSmallstuffProp { + uchar vcolors[NUM_SMALLSTUFF_VCOLORS]; +} CyberSmallstuffProp; + +typedef struct _OnthewallSmallstuffProp { + ubyte dummy; +} OnthewallSmallstuffProp; + +typedef struct _PlotSmallstuffProp { + ObjID target; +} PlotSmallstuffProp; + +// Big Stuff +#define NUM_ELECTRONIC_BIGSTUFF 9 +#define NUM_FURNISHING_BIGSTUFF 10 +#define NUM_ONTHEWALL_BIGSTUFF 11 +#define NUM_LIGHT_BIGSTUFF 4 +#define NUM_LABGEAR_BIGSTUFF 9 +#define NUM_TECHNO_BIGSTUFF 8 +#define NUM_DECOR_BIGSTUFF 16 +#define NUM_TERRAIN_BIGSTUFF 10 + +// Small Stuff +#define NUM_USELESS_SMALLSTUFF 8 +#define NUM_BROKEN_SMALLSTUFF 10 +#define NUM_CORPSELIKE_SMALLSTUFF 15 +#define NUM_GEAR_SMALLSTUFF 6 +#define NUM_CARDS_SMALLSTUFF 12 +#define NUM_CYBER_SMALLSTUFF 12 +#define NUM_ONTHEWALL_SMALLSTUFF 9 +#define NUM_PLOT_SMALLSTUFF 8 + +#define NUM_BIGSTUFF \ + (NUM_ELECTRONIC_BIGSTUFF + NUM_FURNISHING_BIGSTUFF + NUM_ONTHEWALL_BIGSTUFF + NUM_LIGHT_BIGSTUFF + \ + NUM_LABGEAR_BIGSTUFF + NUM_TECHNO_BIGSTUFF + NUM_DECOR_BIGSTUFF + NUM_TERRAIN_BIGSTUFF) + +#define NUM_SMALLSTUFF \ + (NUM_USELESS_SMALLSTUFF + NUM_BROKEN_SMALLSTUFF + NUM_CORPSELIKE_SMALLSTUFF + NUM_GEAR_SMALLSTUFF + \ + NUM_CARDS_SMALLSTUFF + NUM_CYBER_SMALLSTUFF + NUM_ONTHEWALL_SMALLSTUFF + NUM_PLOT_SMALLSTUFF) + +// Enumeration of subclasses +// Furniture +#define BIGSTUFF_SUBCLASS_ELECTRONIC 0 +#define BIGSTUFF_SUBCLASS_FURNISHING 1 +#define BIGSTUFF_SUBCLASS_ONTHEWALL 2 +#define BIGSTUFF_SUBCLASS_LIGHT 3 +#define BIGSTUFF_SUBCLASS_LABGEAR 4 +#define BIGSTUFF_SUBCLASS_TECHNO 5 +#define BIGSTUFF_SUBCLASS_DECOR 6 +#define BIGSTUFF_SUBCLASS_TERRAIN 7 + +// Stuff +#define SMALLSTUFF_SUBCLASS_USELESS 0 +#define SMALLSTUFF_SUBCLASS_BROKEN 1 +#define SMALLSTUFF_SUBCLASS_CORPSELIKE 2 +#define SMALLSTUFF_SUBCLASS_GEAR 3 +#define SMALLSTUFF_SUBCLASS_CARDS 4 +#define SMALLSTUFF_SUBCLASS_CYBER 5 +#define SMALLSTUFF_SUBCLASS_ONTHEWALL 6 +#define SMALLSTUFF_SUBCLASS_PLOT 7 + +#ifdef __OBJSIM_SRC + +BigstuffProp BigstuffProps[NUM_BIGSTUFF]; +ElectronicBigstuffProp ElectronicBigstuffProps[NUM_ELECTRONIC_BIGSTUFF]; +FurnishingBigstuffProp FurnishingBigstuffProps[NUM_FURNISHING_BIGSTUFF]; +OnthewallBigstuffProp OnthewallBigstuffProps[NUM_ONTHEWALL_BIGSTUFF]; +LightBigstuffProp LightBigstuffProps[NUM_LIGHT_BIGSTUFF]; +LabgearBigstuffProp LabgearBigstuffProps[NUM_LABGEAR_BIGSTUFF]; +TechnoBigstuffProp TechnoBigstuffProps[NUM_TECHNO_BIGSTUFF]; +DecorBigstuffProp DecorBigstuffProps[NUM_DECOR_BIGSTUFF]; +TerrainBigstuffProp TerrainBigstuffProps[NUM_TERRAIN_BIGSTUFF]; + +SmallstuffProp SmallstuffProps[NUM_SMALLSTUFF]; +UselessSmallstuffProp UselessSmallstuffProps[NUM_USELESS_SMALLSTUFF]; +BrokenSmallstuffProp BrokenSmallstuffProps[NUM_BROKEN_SMALLSTUFF]; +CorpselikeSmallstuffProp CorpselikeSmallstuffProps[NUM_CORPSELIKE_SMALLSTUFF]; +GearSmallstuffProp GearSmallstuffProps[NUM_GEAR_SMALLSTUFF]; +CardsSmallstuffProp CardsSmallstuffProps[NUM_CARDS_SMALLSTUFF]; +CyberSmallstuffProp CyberSmallstuffProps[NUM_CYBER_SMALLSTUFF]; +OnthewallSmallstuffProp OnthewallSmallstuffProps[NUM_ONTHEWALL_SMALLSTUFF]; +PlotSmallstuffProp PlotSmallstuffProps[NUM_PLOT_SMALLSTUFF]; +#else +extern BigstuffProp BigstuffProps[NUM_BIGSTUFF]; +extern ElectronicBigstuffProp ElectronicBigstuffProps[NUM_ELECTRONIC_BIGSTUFF]; +extern FurnishingBigstuffProp FurnishingBigstuffProps[NUM_FURNISHING_BIGSTUFF]; +extern OnthewallBigstuffProp OnthewallBigstuffProps[NUM_ONTHEWALL_BIGSTUFF]; +extern LightBigstuffProp LightBigstuffProps[NUM_LIGHT_BIGSTUFF]; +extern LabgearBigstuffProp LabgearBigstuffProps[NUM_LABGEAR_BIGSTUFF]; +extern TechnoBigstuffProp TechnoBigstuffProps[NUM_TECHNO_BIGSTUFF]; +extern DecorBigstuffProp DecorBigstuffProps[NUM_DECOR_BIGSTUFF]; +extern TerrainBigstuffProp TerrainBigstuffProps[NUM_TERRAIN_BIGSTUFF]; + +extern SmallstuffProp SmallstuffProps[NUM_SMALLSTUFF]; +extern UselessSmallstuffProp UselessSmallstuffProps[NUM_USELESS_SMALLSTUFF]; +extern BrokenSmallstuffProp BrokenSmallstuffProps[NUM_BROKEN_SMALLSTUFF]; +extern CorpselikeSmallstuffProp CorpselikeSmallstuffProps[NUM_CORPSELIKE_SMALLSTUFF]; +extern GearSmallstuffProp GearSmallstuffProps[NUM_GEAR_SMALLSTUFF]; +extern CardsSmallstuffProp CardsSmallstuffProps[NUM_CARDS_SMALLSTUFF]; +extern CyberSmallstuffProp CyberSmallstuffProps[NUM_CYBER_SMALLSTUFF]; +extern OnthewallSmallstuffProp OnthewallSmallstuffProps[NUM_ONTHEWALL_SMALLSTUFF]; +extern PlotSmallstuffProp PlotSmallstuffProps[NUM_PLOT_SMALLSTUFF]; + +#endif + +#ifdef __OBJSIM_SRC +ObjBigstuff objBigstuffs[NUM_OBJECTS_BIGSTUFF]; +ObjSmallstuff objSmallstuffs[NUM_OBJECTS_SMALLSTUFF]; +ObjBigstuff default_bigstuff; +ObjSmallstuff default_smallstuff; +#else +extern ObjBigstuff objBigstuffs[NUM_OBJECTS_BIGSTUFF]; +extern ObjSmallstuff objSmallstuffs[NUM_OBJECTS_SMALLSTUFF]; +extern ObjBigstuff default_bigstuff; +extern ObjSmallstuff default_smallstuff; +#endif + +#endif // __OBJSTUFF_H diff --git a/engine/src/GameSrc/Headers/objuse.h b/engine/src/GameSrc/Headers/objuse.h new file mode 100644 index 0000000..996ff35 --- /dev/null +++ b/engine/src/GameSrc/Headers/objuse.h @@ -0,0 +1,98 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __OBJUSE_H +#define __OBJUSE_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/objuse.h $ + * $Revision: 1.13 $ + * $Author: xemu $ + * $Date: 1994/08/22 03:04:22 $ + * + * + */ + +// Includes +#include "objects.h" + +// Defines + +#define NUM_ACCESS_CODES 32 + +#define PICKUP_USE_MODE 0 +#define USE_USE_MODE 1 +#define NULL_USE_MODE (INVENT_USEMODE >> INVENT_USEMODE_SHF) + +extern ubyte use_distance_mod; +extern ubyte pickup_distance_mod; + +#define BASE_PICKUP_DIST (FIX_UNIT * 20 / 8) +#define BASE_USE_DIST BASE_PICKUP_DIST + +#define MAX_PICKUP_DIST (BASE_PICKUP_DIST + fix_make(pickup_distance_mod, 0)) +#define MAX_USE_DIST (BASE_USE_DIST + fix_make(use_distance_mod, 0)) + +#define USE_MODE(x) ((ObjProps[OPNUM((x))].flags & INVENT_USEMODE) >> INVENT_USEMODE_SHF) + +// Typedefs +typedef struct { + ushort timestamp; + ushort type; + ObjID door_id; + ushort secret_code; +} DoorSchedEvent; + +// Prototypes + +// Hey. You've been USED, man. Prove your manhood. Do something about it! +// Of course for now, do nothing if not of CLASS_FIXTURE. +// Returns whether or not the message line was used. +uchar object_use(ObjID id, uchar in_inv, ObjID cursor_obj); + +// Lock/unlock a door +errtype obj_door_lock(ObjID door_id, uchar new_lock); + +errtype obj_screen_animate(ObjID id); + +errtype obj_tractor_beam_func(ObjID id, uchar on); + +char container_extract(ObjID *pidlist, int d1, int d2); + +void container_stuff(ObjID *pidlist, int numobjs, int *d1, int *d2); + +uchar is_container(ObjID id, int **d1, int **d2); + +errtype keypad_trigger(ObjID id, uchar digits[]); + +// Special case for elevator +uchar elevator_use(short dest_level, ubyte which_panel); + +errtype obj_cspace_collide(ObjID id, ObjID collider); + +uchar obj_too_smart(ObjID id); + +void multi_anim_callback(ObjID id, intptr_t user_data); + +void unmulti_anim_callback(ObjID id, intptr_t user_data); + +errtype gear_power_outage(); + +// Globals + +#endif // __OBJUSE_H diff --git a/engine/src/GameSrc/Headers/objver.h b/engine/src/GameSrc/Headers/objver.h new file mode 100644 index 0000000..a357e1b --- /dev/null +++ b/engine/src/GameSrc/Headers/objver.h @@ -0,0 +1,62 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __OBJVER_H +#define __OBJVER_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/objver.h $ + * $Revision: 1.67 $ + * $Author: xemu $ + * $Date: 1994/11/21 21:05:31 $ + * + */ + +// v19 - v20 : removed objRefs from the save format, added sidestep to critter data +// v20 - v21 : switched over to individual resources rather than a compound resource, to cut memory req in 3 +// v21 - v22 : switch to pad-less objRefs +// v22 - v23 : go from 12 byte to 10 byte objrefs +// v23 - v24 : a massive sociopolitical upheaval where the empowered majority +// of the Bigstuffs makes a daring coup, boldly seizing the resources +// once held by its smaller brethren. Most noticeably, the power of the Traps +// and the Containers are severely cut back by clever political infighting. +// v24 - v25 : after the fallout of the big v23/24 conflict, the Bigstuffs further consolidate their +// power, going to 176 instead of a mere 160. +// v25 - v26 : smaller objinfo structs, to save some memory +// v26 - v27 : the incredible shrinking objinfo continues, this time as physhandles lose 3 +// bytes without dieting or exercise. +#define OBJECT_VERSION_NUMBER ((int)27) + +// v39 - v40 : eliminated bitmap_2d +// v40 - v41 : explosions have a frame_explode - and got rid of useless combat data +// v41 - v42 : oops - damage for attacks should be an int not a ubyte..... +// v42 - v43 : hey - critters throw grenades +// v43 - v44 : slow projectiles have a light flag now +// v44 - v45: critters with slow projectile attacks have y offsets +#define OBJPROP_FILENAME "objprop.dat" +#define OBJPROP_VERSION_NUMBER 45 + +// 7 - 8 : Added automap strings +// 8 - 9 : automap strings, for real this time, honest +// 9 - 10 : added an EDMS_State for the player +// 10 - 11 : added pathfinding data +// 11 - 12 : animlist and h_sems added +// 12 - 13 : rev 2 of the animlist +#define MISC_SAVELOAD_VERSION_NUMBER 13 + +#endif // __OBJVER_H diff --git a/engine/src/GameSrc/Headers/objwarez.h b/engine/src/GameSrc/Headers/objwarez.h new file mode 100644 index 0000000..ec3f133 --- /dev/null +++ b/engine/src/GameSrc/Headers/objwarez.h @@ -0,0 +1,214 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __OBJWAREZ_H +#define __OBJWAREZ_H + +/* + * $Source: n:/project/cit/src/inc/RCS/objwarez.h $ + * $Revision: 1.12 $ + * $Author: xemu $ + * $Date: 1994/04/23 09:18:56 $ + * + */ + +// Includes +#include "objclass.h" + +// Instance Stuff +typedef struct { + // COMMON_OBJSPEC_FIELDS; + union { + ObjID id; + ObjSpecID headused; + }; + union { + ObjSpecID next; + ObjSpecID headfree; + }; + ObjSpecID prev; +} ObjDrug; + +typedef struct { + // COMMON_OBJSPEC_FIELDS; + union { + ObjID id; + ObjSpecID headused; + }; + union { + ObjSpecID next; + ObjSpecID headfree; + }; + ObjSpecID prev; + ubyte version; +} ObjHardware; + +typedef struct { + // COMMON_OBJSPEC_FIELDS; + union { + ObjID id; + ObjSpecID headused; + }; + union { + ObjSpecID next; + ObjSpecID headfree; + }; + ObjSpecID prev; + ubyte version; + short data_munge; +} ObjSoftware; + +#define SOFTWARE_SECURITY(specid) ((objSoftwares[(specid)].data_munge & 0xF000) >> 12) +#define SOFTWARE_CONTENTS(specid) (objSoftwares[(specid)].data_munge & 0x0FFF) +#define SOFTWARE_SET_MUNGE(specid, sec, cont) (objSoftwares[(specid)].data_munge = ((sec) << 12) & (cont)) + +// Class Typedefs + +typedef struct DrugProp { + ubyte intensity; + ubyte delay; + ubyte duration; + int effect; + int side_effect; // should we do a big case statement - therefore this is not needed + int after_effect; + short flags; // cyberspace? +} DrugProp; + +typedef struct HardwareProp { + short flags; // activated, damaged?? +} HardwareProp; + +typedef struct SoftwareProp { + short flags; // none right now. +} SoftwareProp; + +// Subclass Typedefs +typedef struct StatsDrugProp { + short effectiveness; + ubyte sound_effect_num; // or whatever + int duration; +} StatsDrugProp; + +typedef struct GoggleHardwareProp { + ubyte dummy; +} GoggleHardwareProp; + +typedef struct HardwareHardwareProp { + short target_flag; +} HardwareHardwareProp; + +typedef struct OffenseSoftwareProp { + ubyte damage; +} OffenseSoftwareProp; + +typedef struct DefenseSoftwareProp { + ubyte dummy; +} DefenseSoftwareProp; + +typedef struct OneshotSoftwareProp { + ubyte dummy; +} OneshotSoftwareProp; + +typedef struct MiscSoftwareProp { + ubyte dummy; +} MiscSoftwareProp; + +typedef struct DataSoftwareProp { + ubyte dummy; +} DataSoftwareProp; + +// Drug +#define NUM_STATS_DRUG 7 + +// Hardware +#define NUM_GOGGLE_HARDWARE 5 +#define NUM_HARDWARE_HARDWARE 10 + +// Software +#define NUM_OFFENSE_SOFTWARE 7 +#define NUM_DEFENSE_SOFTWARE 3 +#define NUM_ONESHOT_SOFTWARE 4 +#define NUM_MISC_SOFTWARE 5 +#define NUM_DATA_SOFTWARE 3 + +// Class count +#define NUM_DRUG (NUM_STATS_DRUG) +#define NUM_HARDWARE (NUM_GOGGLE_HARDWARE + NUM_HARDWARE_HARDWARE) +#define NUM_SOFTWARE \ + (NUM_OFFENSE_SOFTWARE + NUM_DEFENSE_SOFTWARE + NUM_ONESHOT_SOFTWARE + NUM_MISC_SOFTWARE + NUM_DATA_SOFTWARE) + +// Enumeration of subclasses +// + +// Drug +#define DRUG_SUBCLASS_STATS 0 + +// Hardware +#define HARDWARE_SUBCLASS_GOGGLE 0 +#define HARDWARE_SUBCLASS_HARDWARE 1 + +// Software +#define SOFTWARE_SUBCLASS_OFFENSE 0 +#define SOFTWARE_SUBCLASS_DEFENSE 1 +#define SOFTWARE_SUBCLASS_ONESHOT 2 +#define SOFTWARE_SUBCLASS_MISC 3 +#define SOFTWARE_SUBCLASS_DATA 4 + +#ifdef __OBJSIM_SRC +DrugProp DrugProps[NUM_DRUG]; +StatsDrugProp StatsDrugProps[NUM_STATS_DRUG]; +HardwareProp HardwareProps[NUM_HARDWARE]; +GoggleHardwareProp GoggleHardwareProps[NUM_GOGGLE_HARDWARE]; +HardwareHardwareProp HardwareHardwareProps[NUM_HARDWARE_HARDWARE]; +SoftwareProp SoftwareProps[NUM_SOFTWARE]; +OffenseSoftwareProp OffenseSoftwareProps[NUM_OFFENSE_SOFTWARE]; +DefenseSoftwareProp DefenseSoftwareProps[NUM_DEFENSE_SOFTWARE]; +OneshotSoftwareProp OneshotSoftwareProps[NUM_ONESHOT_SOFTWARE]; +MiscSoftwareProp MiscSoftwareProps[NUM_MISC_SOFTWARE]; +DataSoftwareProp DataSoftwareProps[NUM_DATA_SOFTWARE]; +#else +extern DrugProp DrugProps[NUM_DRUG]; +extern StatsDrugProp StatsDrugProps[NUM_STATS_DRUG]; +extern HardwareProp HardwareProps[NUM_HARDWARE]; +extern GoggleHardwareProp GoggleHardwareProps[NUM_GOGGLE_HARDWARE]; +extern HardwareHardwareProp HardwareHardwareProps[NUM_HARDWARE_HARDWARE]; +extern SoftwareProp SoftwareProps[NUM_SOFTWARE]; +extern OffenseSoftwareProp OffenseSoftwareProps[NUM_OFFENSE_SOFTWARE]; +extern DefenseSoftwareProp DefenseSoftwareProps[NUM_DEFENSE_SOFTWARE]; +extern OneshotSoftwareProp OneshotSoftwareProps[NUM_ONESHOT_SOFTWARE]; +extern MiscSoftwareProp MiscSoftwareProps[NUM_MISC_SOFTWARE]; +extern DataSoftwareProp DataSoftwareProps[NUM_DATA_SOFTWARE]; +#endif + +#ifdef __OBJSIM_SRC +ObjDrug objDrugs[NUM_OBJECTS_DRUG]; +ObjHardware objHardwares[NUM_OBJECTS_HARDWARE]; +ObjSoftware objSoftwares[NUM_OBJECTS_SOFTWARE]; +ObjDrug default_drug; +ObjHardware default_hardware; +ObjSoftware default_software; +#else +extern ObjDrug objDrugs[NUM_OBJECTS_DRUG]; +extern ObjHardware objHardwares[NUM_OBJECTS_HARDWARE]; +extern ObjSoftware objSoftwares[NUM_OBJECTS_SOFTWARE]; +extern ObjDrug default_drug; +extern ObjHardware default_hardware; +extern ObjSoftware default_software; +#endif + +#endif // __OBJWAREZ_H diff --git a/engine/src/GameSrc/Headers/objwpn.h b/engine/src/GameSrc/Headers/objwpn.h new file mode 100644 index 0000000..2890559 --- /dev/null +++ b/engine/src/GameSrc/Headers/objwpn.h @@ -0,0 +1,407 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __OBJWPN_H +#define __OBJWPN_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/objwpn.h $ + * $Revision: 1.33 $ + * $Author: minman $ + * $Date: 1994/07/26 00:50:45 $ + * + * + */ + +// Includes +#include "objclass.h" + +// Instance Data +typedef struct { + // COMMON_OBJSPEC_FIELDS; + union { + ObjID id; + ObjSpecID headused; + }; + union { + ObjSpecID next; + ObjSpecID headfree; + }; + ObjSpecID prev; + ubyte ammo_type; + ubyte ammo_count; +} ObjGun; + +typedef struct { + // COMMON_OBJSPEC_FIELDS; + union { + ObjID id; + ObjSpecID headused; + }; + union { + ObjSpecID next; + ObjSpecID headfree; + }; + ObjSpecID prev; +} ObjAmmo; + +typedef struct { + // COMMON_OBJSPEC_FIELDS; + union { + ObjID id; + ObjSpecID headused; + }; + union { + ObjSpecID next; + ObjSpecID headfree; + }; + ObjSpecID prev; + ObjID owner; + int bullet_triple; + int duration; + // char power; + ObjLoc p1, p2, p3; +} ObjPhysics; + +typedef struct { + // COMMON_OBJSPEC_FIELDS; + union { + ObjID id; + ObjSpecID headused; + }; + union { + ObjSpecID next; + ObjSpecID headfree; + }; + ObjSpecID prev; + ubyte unique_id; + ubyte walls_hit; + short flags; + short timestamp; +} ObjGrenade; + +// Class typedefs + +#define COMBAT_DATA_FIELDS \ + short damage_modifier; \ + ubyte offense_value; \ + int damage_type; \ + ubyte penetration + +typedef struct _GunProp { + ubyte fire_rate; + ubyte useable_ammo_type; +} GunProp; + +typedef struct _AmmoProp { + COMBAT_DATA_FIELDS; + ubyte cartridge_size; + ubyte bullet_mass; + short bullet_speed; + ubyte range; + ubyte recoil_force; +} AmmoProp; + +typedef struct _PhysicsProp { + ubyte flags; +} PhysicsProp; + +typedef struct _GrenadeProp { + COMBAT_DATA_FIELDS; + ubyte touchiness; + ubyte radius; + ubyte radius_change; + ubyte damage_change; + ubyte attack_mass; + short flags; // Does it spew shrapnel? Can timer be set? Can it stick to wall? +} GrenadeProp; + +// Subclass typedefs + +#define EMPTY_STRUCTS + +typedef struct _PistolGunProp { +#ifdef EMPTY_STRUCTS + ubyte dummy; +#endif +} PistolGunProp; + +typedef struct _AutoGunProp { +#ifdef EMPTY_STRUCTS + ubyte dummy; +#endif +} AutoGunProp; + +typedef struct _SpecialGunProp { + COMBAT_DATA_FIELDS; + ubyte speed; + int proj_triple; + ubyte attack_mass; + short attack_speed; +} SpecialGunProp; + +typedef struct _HandtohandGunProp { + COMBAT_DATA_FIELDS; + ubyte energy_use; + ubyte attack_mass; + ubyte attack_range; + short attack_speed; +} HandtohandGunProp; + +typedef struct _BeamGunProp { + COMBAT_DATA_FIELDS; + ubyte max_charge; + ubyte attack_mass; + ubyte attack_range; + short attack_speed; +} BeamGunProp; + +typedef struct _BeamprojGunProp { + COMBAT_DATA_FIELDS; + ubyte max_charge; + ubyte attack_mass; + short attack_speed; + ubyte speed; + int proj_triple; + ubyte flags; +} BeamprojGunProp; + +typedef struct _PistolAmmoProp { +#ifdef EMPTY_STRUCTS + ubyte dummy; +#endif +} PistolAmmoProp; + +typedef struct _NeedleAmmoProp { +#ifdef EMPTY_STRUCTS + ubyte dummy; +#endif +} NeedleAmmoProp; + +typedef struct _MagnumAmmoProp { +#ifdef EMPTY_STRUCTS + ubyte dummy; +#endif +} MagnumAmmoProp; + +typedef struct _RifleAmmoProp { +#ifdef EMPTY_STRUCTS + ubyte dummy; +#endif +} RifleAmmoProp; + +typedef struct _FlechetteAmmoProp { +#ifdef EMPTY_STRUCTS + ubyte dummy; +#endif +} FlechetteAmmoProp; + +typedef struct _AutoAmmoProp { +#ifdef EMPTY_STRUCTS + ubyte dummy; +#endif +} AutoAmmoProp; + +typedef struct _ProjAmmoProp { +#ifdef EMPTY_STRUCTS + ubyte dummy; +#endif +} ProjAmmoProp; + +typedef struct _TracerPhysicsProp { + short xcoords[4]; + short ycoords[4]; + ubyte zcoords[4]; +} TracerPhysicsProp; + +#define NUM_SLOW_VCOLORS 6 +typedef struct _SlowPhysicsProp { + uchar vcolors[NUM_SLOW_VCOLORS]; +} SlowPhysicsProp; + +typedef struct _CameraPhysicsProp { +#ifdef EMPTY_STRUCTS + ubyte dummy; +#endif +} CameraPhysicsProp; + +typedef struct _DirectGrenadeProp { +#ifdef EMPTY_STRUCTS + ubyte dummy; +#endif +} DirectGrenadeProp; + +typedef struct _TimedGrenadeProp { + ubyte min_time_set; + ubyte max_time_set; + ubyte timing_deviation; +} TimedGrenadeProp; + +// Gun +#define NUM_PISTOL_GUN 5 +#define NUM_AUTO_GUN 2 +#define NUM_SPECIAL_GUN 2 +#define NUM_HANDTOHAND_GUN 2 +#define NUM_BEAM_GUN 3 +#define NUM_BEAMPROJ_GUN 2 + +// Ammo +#define NUM_PISTOL_AMMO 2 +#define NUM_NEEDLE_AMMO 2 +#define NUM_MAGNUM_AMMO 3 +#define NUM_RIFLE_AMMO 2 +#define NUM_FLECHETTE_AMMO 2 +#define NUM_AUTO_AMMO 2 +#define NUM_PROJ_AMMO 2 + +// Physics +#define NUM_TRACER_PHYSICS 6 +#define NUM_SLOW_PHYSICS 16 +#define NUM_CAMERA_PHYSICS 2 + +// Grenade +#define NUM_DIRECT_GRENADE 5 +#define NUM_TIMED_GRENADE 3 + +#define NUM_GUN (NUM_PISTOL_GUN + NUM_AUTO_GUN + NUM_SPECIAL_GUN + NUM_HANDTOHAND_GUN + NUM_BEAM_GUN + NUM_BEAMPROJ_GUN) + +#define NUM_AMMO \ + (NUM_PISTOL_AMMO + NUM_NEEDLE_AMMO + NUM_MAGNUM_AMMO + NUM_RIFLE_AMMO + NUM_FLECHETTE_AMMO + NUM_AUTO_AMMO + \ + NUM_PROJ_AMMO) + +#define NUM_PHYSICS (NUM_TRACER_PHYSICS + NUM_SLOW_PHYSICS + NUM_CAMERA_PHYSICS) + +#define NUM_GRENADE (NUM_DIRECT_GRENADE + NUM_TIMED_GRENADE) + +// Gun +#define GUN_SUBCLASS_PISTOL 0 +#define GUN_SUBCLASS_AUTO 1 +#define GUN_SUBCLASS_SPECIAL 2 +#define GUN_SUBCLASS_HANDTOHAND 3 +#define GUN_SUBCLASS_BEAM 4 +#define GUN_SUBCLASS_BEAMPROJ 5 + +// Ammo +#define AMMO_SUBCLASS_PISTOL 0 +#define AMMO_SUBCLASS_NEEDLE 1 +#define AMMO_SUBCLASS_MAGNUM 2 +#define AMMO_SUBCLASS_RIFLE 3 +#define AMMO_SUBCLASS_FLECHETTE 4 +#define AMMO_SUBCLASS_AUTO 5 +#define AMMO_SUBCLASS_PROJ 6 + +// Physics +#define PHYSICS_SUBCLASS_TRACER 0 +#define PHYSICS_SUBCLASS_SLOW 1 +#define PHYSICS_SUBCLASS_CAMERA 2 + +// Grenade +#define GRENADE_SUBCLASS_DIRECT 0 +#define GRENADE_SUBCLASS_TIMED 1 + +#ifdef STRANGE_EFFICIOMATRON_WAY +#define GunBase (&PropsArray) +#define GunProps ((GunProp *)GunBase) +#define PistolBase (GunBase + (sizeof(GunProp) * NUM_GUN)) +#define PistolProps ((PistolGunProp *)PistolBase) +#define AutoBase (PistolBase + (sizeof(PistolGunProp * NUM_PISTOL_GUN))) +#define AutoProps ((AutoGunProp *)AutoBase) +#define SpecialBase (AutoBase + (sizeof(AutoGunProp * NUM_AUTO_GUN))) +#define SpecialProps ((SpecialGunProp *)SpecialBase) +#define HandtohandBase (SpecialBase + (sizeof(SpecialGunProp * NUM_SPECIAL_GUN))) +#define HandtohandProps ((HandtohandGunProp *)HandtohandBase) +#define BeamBase (HandtohandBase + (sizeof(HandtohandGunProp * NUM_HANDTOHAND_GUN))) +#define BeamProps ((BeamGunProp *)BeamBase) +#define BeamprojBase (BeamBase + (sizeof(BeamGunProp * NUM_BEAM_GUN))) +#define BeamprojProps ((BeamprojGunProp *)BeamprojBase) + +#define AmmoBase (BeamprojBase + sizeof(BeamprojGunProp * NUM_BEAMPROJ_GUN)) +#define AmmoProps ((AmmoProp *) +#endif + +#ifdef __OBJSIM_SRC +GunProp GunProps[NUM_GUN]; +PistolGunProp PistolGunProps[NUM_PISTOL_GUN]; +AutoGunProp AutoGunProps[NUM_AUTO_GUN]; +SpecialGunProp SpecialGunProps[NUM_SPECIAL_GUN]; +HandtohandGunProp HandtohandGunProps[NUM_HANDTOHAND_GUN]; +BeamGunProp BeamGunProps[NUM_BEAM_GUN]; +BeamprojGunProp BeamprojGunProps[NUM_BEAMPROJ_GUN]; + +AmmoProp AmmoProps[NUM_AMMO]; +PistolAmmoProp PistolAmmoProps[NUM_PISTOL_AMMO]; +NeedleAmmoProp NeedleAmmoProps[NUM_NEEDLE_AMMO]; +MagnumAmmoProp MagnumAmmoProps[NUM_MAGNUM_AMMO]; +RifleAmmoProp RifleAmmoProps[NUM_RIFLE_AMMO]; +FlechetteAmmoProp FlechetteAmmoProps[NUM_FLECHETTE_AMMO]; +AutoAmmoProp AutoAmmoProps[NUM_AUTO_AMMO]; +ProjAmmoProp ProjAmmoProps[NUM_PROJ_AMMO]; + +PhysicsProp PhysicsProps[NUM_PHYSICS]; +TracerPhysicsProp TracerPhysicsProps[NUM_TRACER_PHYSICS]; +SlowPhysicsProp SlowPhysicsProps[NUM_SLOW_PHYSICS]; +CameraPhysicsProp CameraPhysicsProps[NUM_CAMERA_PHYSICS]; +GrenadeProp GrenadeProps[NUM_GRENADE]; +DirectGrenadeProp DirectGrenadeProps[NUM_DIRECT_GRENADE]; +TimedGrenadeProp TimedGrenadeProps[NUM_TIMED_GRENADE]; +#else +extern GunProp GunProps[NUM_GUN]; +extern PistolGunProp PistolGunProps[NUM_PISTOL_GUN]; +extern AutoGunProp AutoGunProps[NUM_AUTO_GUN]; +extern SpecialGunProp SpecialGunProps[NUM_SPECIAL_GUN]; +extern HandtohandGunProp HandtohandGunProps[NUM_HANDTOHAND_GUN]; +extern BeamGunProp BeamGunProps[NUM_BEAM_GUN]; +extern BeamprojGunProp BeamprojGunProps[NUM_BEAMPROJ_GUN]; +extern AmmoProp AmmoProps[NUM_AMMO]; +extern PistolAmmoProp PistolAmmoProps[NUM_PISTOL_AMMO]; +extern NeedleAmmoProp NeedleAmmoProps[NUM_NEEDLE_AMMO]; +extern MagnumAmmoProp MagnumAmmoProps[NUM_MAGNUM_AMMO]; +extern RifleAmmoProp RifleAmmoProps[NUM_RIFLE_AMMO]; +extern FlechetteAmmoProp FlechetteAmmoProps[NUM_FLECHETTE_AMMO]; +extern AutoAmmoProp AutoAmmoProps[NUM_AUTO_AMMO]; +extern ProjAmmoProp ProjAmmoProps[NUM_PROJ_AMMO]; +extern PhysicsProp PhysicsProps[NUM_PHYSICS]; +extern TracerPhysicsProp TracerPhysicsProps[NUM_TRACER_PHYSICS]; +extern SlowPhysicsProp SlowPhysicsProps[NUM_SLOW_PHYSICS]; +extern CameraPhysicsProp CameraPhysicsProps[NUM_CAMERA_PHYSICS]; +extern GrenadeProp GrenadeProps[NUM_GRENADE]; +extern DirectGrenadeProp DirectGrenadeProps[NUM_DIRECT_GRENADE]; +extern TimedGrenadeProp TimedGrenadeProps[NUM_TIMED_GRENADE]; +#endif + +#ifdef __OBJSIM_SRC +ObjGun objGuns[NUM_OBJECTS_GUN]; +ObjAmmo objAmmos[NUM_OBJECTS_AMMO]; +ObjPhysics objPhysicss[NUM_OBJECTS_PHYSICS]; +ObjGrenade objGrenades[NUM_OBJECTS_GRENADE]; +ObjGun default_gun; +ObjAmmo default_ammo; +ObjPhysics default_physics; +ObjGrenade default_grenade; +#else +extern ObjGun objGuns[NUM_OBJECTS_GUN]; +extern ObjAmmo objAmmos[NUM_OBJECTS_AMMO]; +extern ObjPhysics objPhysicss[NUM_OBJECTS_PHYSICS]; +extern ObjGrenade objGrenades[NUM_OBJECTS_GRENADE]; +extern ObjGun default_gun; +extern ObjAmmo default_ammo; +extern ObjPhysics default_physics; +extern ObjGrenade default_grenade; +#endif + +#endif // __OBJWPN_H diff --git a/engine/src/GameSrc/Headers/olhext.h b/engine/src/GameSrc/Headers/olhext.h new file mode 100644 index 0000000..1235cf4 --- /dev/null +++ b/engine/src/GameSrc/Headers/olhext.h @@ -0,0 +1,43 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __OLHEXT_H +#define __OLHEXT_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/olhext.h $ + * $Revision: 1.2 $ + * $Author: mahk $ + * $Date: 1994/07/15 21:31:57 $ + * + */ + +//#define OLH_QBIT 0x91 +extern uchar olh_active; +extern uchar olh_overlay_on; + +void olh_do_hudobjs(short xl, short yl); +void olh_overlay(void); +void olh_scan_objects(void); +void olh_init(void); +void olh_closedown(void); +void olh_shutdown(void); +uchar toggle_olh_func(ushort, uint32_t, intptr_t); +uchar olh_overlay_func(ushort keycode, uint32_t context, intptr_t); + +#endif // __OLHEXT_H diff --git a/engine/src/GameSrc/Headers/olhint.h b/engine/src/GameSrc/Headers/olhint.h new file mode 100644 index 0000000..65a8b51 --- /dev/null +++ b/engine/src/GameSrc/Headers/olhint.h @@ -0,0 +1,48 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __OLHINT_H +#define __OLHINT_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/olhint.h $ + * $Revision: 1.3 $ + * $Author: mahk $ + * $Date: 1994/08/22 01:14:44 $ + * + */ + +#include "objects.h" + +#define SCAN_RATIO 3 + +#define SCAN_WID (SCREEN_VIEW_WIDTH / SCAN_RATIO) +#define SCAN_HGT (SCREEN_VIEW_HEIGHT / SCAN_RATIO) + +#define OLH_WRAP_WID 100 + +typedef struct _olh_data { + ObjID obj; + LGPoint loc; +} olh_data; + +extern olh_data olh_object; + +extern uchar olh_candidate(ObjID obj); + +#endif // __OLHINT_H diff --git a/engine/src/GameSrc/Headers/olhscan.h b/engine/src/GameSrc/Headers/olhscan.h new file mode 100644 index 0000000..9840406 --- /dev/null +++ b/engine/src/GameSrc/Headers/olhscan.h @@ -0,0 +1,31 @@ +/* + +Copyright (C) 2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef OLHSCAN_H +#define OLHSCAN_H + +#include "frtypes.h" + +void olh_init_single_scan(fauxrend_context **outxt, fauxrend_context *intxt); +void olh_free_scan(void); +void olh_svga_deal(void); +ushort olh_scan_objs(void); + + +#endif diff --git a/engine/src/GameSrc/Headers/otrip.h b/engine/src/GameSrc/Headers/otrip.h new file mode 100644 index 0000000..55daab5 --- /dev/null +++ b/engine/src/GameSrc/Headers/otrip.h @@ -0,0 +1,494 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#define MINIPISTOL_TRIPLE 0x0 // (0) +#define DARTPISTOL_TRIPLE 0x1 // (1) +#define MAGNUM_TRIPLE 0x2 // (2) +#define ASSAULTRFL_TRIPLE 0x3 // (3) +#define RIOTGUN_TRIPLE 0x4 // (4) +#define FLECHETTE_TRIPLE 0x100 // (256) +#define SKORPION_TRIPLE 0x101 // (257) +#define MAGPULSE_TRIPLE 0x200 // (512) +#define RAILGUN_TRIPLE 0x201 // (513) +#define BATON_TRIPLE 0x300 // (768) +#define LASERAPIER_TRIPLE 0x301 // (769) +#define PHASER_TRIPLE 0x400 // (1024) +#define BLASTER_TRIPLE 0x401 // (1025) +#define IONBEAM_TRIPLE 0x402 // (1026) +#define STUNGUN_TRIPLE 0x500 // (1280) +#define PLASMABEAM_TRIPLE 0x501 // (1281) +#define SPAMMO_TRIPLE 0x10000 // (65536) +#define TEFAMMO_TRIPLE 0x10001 // (65537) +#define NNAMMO_TRIPLE 0x10100 // (65792) +#define TNAMMO_TRIPLE 0x10101 // (65793) +#define HTAMMO_TRIPLE 0x10200 // (66048) +#define HSAMMO_TRIPLE 0x10201 // (66049) +#define RBAMMO_TRIPLE 0x10202 // (66050) +#define MRAMMO_TRIPLE 0x10300 // (66304) +#define PRAMMO_TRIPLE 0x10301 // (66305) +#define HNAMMO_TRIPLE 0x10400 // (66560) +#define SPLAMMO_TRIPLE 0x10401 // (66561) +#define SLGAMMO_TRIPLE 0x10500 // (66816) +#define BGAMMO_TRIPLE 0x10501 // (66817) +#define MAGAMMO_TRIPLE 0x10600 // (67072) +#define RGAMMO_TRIPLE 0x10601 // (67073) +#define BULLTRACE_TRIPLE 0x20000 // (131072) +#define ENERTRACE_TRIPLE 0x20001 // (131073) +#define AUTOTRACE_TRIPLE 0x20002 // (131074) +#define NEEDTRACE_TRIPLE 0x20003 // (131075) +#define GRENTRACE_TRIPLE 0x20004 // (131076) +#define RUBBTRACE_TRIPLE 0x20005 // (131077) +#define VIRUSSLOW_TRIPLE 0x20100 // (131328) +#define ELITESLOW_TRIPLE 0x20101 // (131329) +#define ASSASINSLOW_TRIPLE 0x20102 // (131330) +#define MUTANTSLOW_TRIPLE 0x20103 // (131331) +#define ZEROSLOW_TRIPLE 0x20104 // (131332) +#define MAGBURST_TRIPLE 0x20105 // (131333) +#define RAILSLOW_TRIPLE 0x20106 // (131334) +#define STUNSLOW_TRIPLE 0x20107 // (131335) +#define PLASMABLST_TRIPLE 0x20108 // (131336) +#define CYBERBOLT_TRIPLE 0x20109 // (131337) +#define CYBERSLOW_TRIPLE 0x2010a // (131338) +#define DRILLSLOW_TRIPLE 0x2010b // (131339) +#define DISCSLOW_TRIPLE 0x2010c // (131340) +#define SPEWSLOW_TRIPLE 0x2010d // (131341) +#define PLANTSLOW_TRIPLE 0x2010e // (131342) +#define INVISOSLOW_TRIPLE 0x2010f // (131343) +#define DRONECAM_TRIPLE 0x20200 // (131584) +#define EXPLCAM_TRIPLE 0x20201 // (131585) +#define FRAG_G_TRIPLE 0x30000 // (196608) +#define EMP_G_TRIPLE 0x30001 // (196609) +#define GAS_G_TRIPLE 0x30002 // (196610) +#define CONC_G_TRIPLE 0x30003 // (196611) +#define L_MINE_TRIPLE 0x30004 // (196612) +#define NITRO_G_TRIPLE 0x30100 // (196864) +#define EARTH_G_TRIPLE 0x30101 // (196865) +#define OBJ_G_TRIPLE 0x30102 // (196866) +#define STAMINA_DRUG_TRIPLE 0x40000 // (262144) +#define SIGHT_DRUG_TRIPLE 0x40001 // (262145) +#define LSD_DRUG_TRIPLE 0x40002 // (262146) +#define MEDI_DRUG_TRIPLE 0x40003 // (262147) +#define NINJA_DRUG_TRIPLE 0x40004 // (262148) +#define GENIUS_DRUG_TRIPLE 0x40005 // (262149) +#define DETOX_DRUG_TRIPLE 0x40006 // (262150) +#define INFRA_GOG_TRIPLE 0x50000 // (327680) +#define TARG_GOG_TRIPLE 0x50001 // (327681) +#define SENS_HARD_TRIPLE 0x50002 // (327682) +#define AIM_GOG_TRIPLE 0x50003 // (327683) +#define HUD_GOG_TRIPLE 0x50004 // (327684) +#define BIOSCAN_HARD_TRIPLE 0x50100 // (327936) +#define NAV_HARD_TRIPLE 0x50101 // (327937) +#define SHIELD_HARD_TRIPLE 0x50102 // (327938) +#define VIDTEX_HARD_TRIPLE 0x50103 // (327939) +#define LANTERN_HARD_TRIPLE 0x50104 // (327940) +#define FULLSCR_HARD_TRIPLE 0x50105 // (327941) +#define ENV_HARD_TRIPLE 0x50106 // (327942) +#define MOTION_HARD_TRIPLE 0x50107 // (327943) +#define JET_HARD_TRIPLE 0x50108 // (327944) +#define STATUS_HARD_TRIPLE 0x50109 // (327945) +#define DRILL_TRIPLE 0x60000 // (393216) +#define SPEW_TRIPLE 0x60001 // (393217) +#define MINE_TRIPLE 0x60002 // (393218) +#define DISC_TRIPLE 0x60003 // (393219) +#define PULSER_TRIPLE 0x60004 // (393220) +#define SCRAMBLER_TRIPLE 0x60005 // (393221) +#define VIRUS_TRIPLE 0x60006 // (393222) +#define SHIELD_TRIPLE 0x60100 // (393472) +#define OLD_FAKEID_TRIPLE 0x60101 // (393473) +#define ICE_TRIPLE 0x60102 // (393474) +#define TURBO_TRIPLE 0x60200 // (393728) +#define FAKEID_TRIPLE 0x60201 // (393729) +#define DECOY_TRIPLE 0x60202 // (393730) +#define RECALL_TRIPLE 0x60203 // (393731) +#define GAMES_TRIPLE 0x60300 // (393984) +#define MONITOR1_TRIPLE 0x60301 // (393985) +#define IDENTIFY_TRIPLE 0x60302 // (393986) +#define TRACE_TRIPLE 0x60303 // (393987) +#define TOGGLE_TRIPLE 0x60304 // (393988) +#define TEXT1_TRIPLE 0x60400 // (394240) +#define EMAIL1_TRIPLE 0x60401 // (394241) +#define MAP1_TRIPLE 0x60402 // (394242) +#define PHONE_TRIPLE 0x70000 // (458752) +#define VCR_TRIPLE 0x70001 // (458753) +#define MICROWAVE_OVN_TRIPLE 0x70002 // (458754) +#define STEREO_TRIPLE 0x70003 // (458755) +#define KEYBOARD_TRIPLE 0x70004 // (458756) +#define SMALL_CPU_TRIPLE 0x70005 // (458757) +#define TV_TRIPLE 0x70006 // (458758) +#define MONITOR2_TRIPLE 0x70007 // (458759) +#define LARGCPU_TRIPLE 0x70008 // (458760) +#define LDESK_TRIPLE 0x70100 // (459008) +#define FDESK_TRIPLE 0x70101 // (459009) +#define CABINET_TRIPLE 0x70102 // (459010) +#define SHELF_TRIPLE 0x70103 // (459011) +#define HIDEAWAY_TRIPLE 0x70104 // (459012) +#define CHAIR_TRIPLE 0x70105 // (459013) +#define ENDTABLE_TRIPLE 0x70106 // (459014) +#define COUCH_TRIPLE 0x70107 // (459015) +#define EXECCHR_TRIPLE 0x70108 // (459016) +#define COATTREE_TRIPLE 0x70109 // (459017) +#define SIGN_TRIPLE 0x70200 // (459264) +#define ICON_TRIPLE 0x70201 // (459265) +#define GRAF_TRIPLE 0x70202 // (459266) +#define WORDS_TRIPLE 0x70203 // (459267) +#define PAINTING_TRIPLE 0x70204 // (459268) +#define POSTER_TRIPLE 0x70205 // (459269) +#define SCREEN_TRIPLE 0x70206 // (459270) +#define TMAP_TRIPLE 0x70207 // (459271) +#define SUPERSCREEN_TRIPLE 0x70208 // (459272) +#define BIGSCREEN_TRIPLE 0x70209 // (459273) +#define REPULSWALL_TRIPLE 0x7020a // (459274) +#define DESKLAMP_TRIPLE 0x70300 // (459520) +#define FLOORLAMP_TRIPLE 0x70301 // (459521) +#define GLOWBULB_TRIPLE 0x70302 // (459522) +#define CHAND_TRIPLE 0x70303 // (459523) +#define GENE_SPLICER_TRIPLE 0x70400 // (459776) +#define TUBING_TRIPLE 0x70401 // (459777) +#define MED_CART_TRIPLE 0x70402 // (459778) +#define SURG_MACH_TRIPLE 0x70403 // (459779) +#define TTUBE_RACK_TRIPLE 0x70404 // (459780) +#define RSRCH_CHAIR_TRIPLE 0x70405 // (459781) +#define HOSP_BED_TRIPLE 0x70406 // (459782) +#define BROKLAB1_TRIPLE 0x70407 // (459783) +#define BROKLAB2_TRIPLE 0x70408 // (459784) +#define MICROSCOPE_TRIPLE 0x70500 // (460032) +#define SCOPE_TRIPLE 0x70501 // (460033) +#define LAB_PROBE_TRIPLE 0x70502 // (460034) +#define XRAY_MACHINE_TRIPLE 0x70503 // (460035) +#define CAMERA_TRIPLE 0x70504 // (460036) +#define CONTPAN_TRIPLE 0x70505 // (460037) +#define CONTPED_TRIPLE 0x70506 // (460038) +#define ENERGY_MINE_TRIPLE 0x70507 // (460039) +#define STATUE1_TRIPLE 0x70600 // (460288) +#define SHRUB1_TRIPLE 0x70601 // (460289) +#define GRASS_TRIPLE 0x70602 // (460290) +#define PLANT1_TRIPLE 0x70603 // (460291) +#define FUNG1_TRIPLE 0x70604 // (460292) +#define FUNG2_TRIPLE 0x70605 // (460293) +#define PLANT2_TRIPLE 0x70606 // (460294) +#define VINE1_TRIPLE 0x70607 // (460295) +#define VINE2_TRIPLE 0x70608 // (460296) +#define PLANT3_TRIPLE 0x70609 // (460297) +#define PLANT4_TRIPLE 0x7060a // (460298) +#define LBOULDER_TRIPLE 0x7060b // (460299) +#define BBOULDER_TRIPLE 0x7060c // (460300) +#define SHRUB2_TRIPLE 0x7060d // (460301) +#define VSHRUB1_TRIPLE 0x7060e // (460302) +#define VSHRUB2_TRIPLE 0x7060f // (460303) +#define BRIDGE_TRIPLE 0x70700 // (460544) +#define CATWALK_TRIPLE 0x70701 // (460545) +#define WALL_TRIPLE 0x70702 // (460546) +#define FPILLAR_TRIPLE 0x70703 // (460547) +#define RAILING1_TRIPLE 0x70704 // (460548) +#define RAILING2_TRIPLE 0x70705 // (460549) +#define PILLAR_TRIPLE 0x70706 // (460550) +#define FORCE_BRIJ_TRIPLE 0x70707 // (460551) +#define NON_BRIDGE_TRIPLE 0x70708 // (460552) +#define FORCE_BRIJ2_TRIPLE 0x70709 // (460553) +#define BEV_CONT_TRIPLE 0x80000 // (524288) +#define WRAPPER_TRIPLE 0x80001 // (524289) +#define PAPERS_TRIPLE 0x80002 // (524290) +#define WARECASING_TRIPLE 0x80003 // (524291) +#define EXTING_TRIPLE 0x80004 // (524292) +#define HELMET_TRIPLE 0x80005 // (524293) +#define CLOTHES_TRIPLE 0x80006 // (524294) +#define BRIEFCASE_TRIPLE 0x80007 // (524295) +#define BROKEN_GUN_TRIPLE 0x80100 // (524544) +#define MCHUNK1_TRIPLE 0x80101 // (524545) +#define MCHUNK2_TRIPLE 0x80102 // (524546) +#define MCHUNK3_TRIPLE 0x80103 // (524547) +#define CRATE_FRAG_TRIPLE 0x80104 // (524548) +#define BROKEN_PAN_TRIPLE 0x80105 // (524549) +#define BROKEN_CLK_TRIPLE 0x80106 // (524550) +#define MSCRAP_TRIPLE 0x80107 // (524551) +#define BROKEN_LEV1_TRIPLE 0x80108 // (524552) +#define BROKEN_LEV2_TRIPLE 0x80109 // (524553) +#define CORPSE1_TRIPLE 0x80200 // (524800) +#define CORPSE2_TRIPLE 0x80201 // (524801) +#define CORPSE3_TRIPLE 0x80202 // (524802) +#define CORPSE4_TRIPLE 0x80203 // (524803) +#define CORPSE5_TRIPLE 0x80204 // (524804) +#define CORPSE6_TRIPLE 0x80205 // (524805) +#define CORPSE7_TRIPLE 0x80206 // (524806) +#define CORPSE8_TRIPLE 0x80207 // (524807) +#define SKEL_RAGS_TRIPLE 0x80208 // (524808) +#define BONES1_TRIPLE 0x80209 // (524809) +#define BONES2_TRIPLE 0x8020a // (524810) +#define SKULL_TRIPLE 0x8020b // (524811) +#define LIMB_TRIPLE 0x8020c // (524812) +#define HEAD_TRIPLE 0x8020d // (524813) +#define HEAD2_TRIPLE 0x8020e // (524814) +#define EPICK_TRIPLE 0x80300 // (525056) +#define BATTERY2_TRIPLE 0x80301 // (525057) +#define ROD_TRIPLE 0x80302 // (525058) +#define AIDKIT_TRIPLE 0x80303 // (525059) +#define TRACBEAM_TRIPLE 0x80304 // (525060) +#define BATTERY_TRIPLE 0x80305 // (525061) +#define GENCARDS_TRIPLE 0x80400 // (525312) +#define STDCARD_TRIPLE 0x80401 // (525313) +#define SCICARD_TRIPLE 0x80402 // (525314) +#define STORECARD_TRIPLE 0x80403 // (525315) +#define ENGCARD_TRIPLE 0x80404 // (525316) +#define MEDCARD_TRIPLE 0x80405 // (525317) +#define MAINTCARD_TRIPLE 0x80406 // (525318) +#define ADMINCARD_TRIPLE 0x80407 // (525319) +#define SECCARD_TRIPLE 0x80408 // (525320) +#define COMCARD_TRIPLE 0x80409 // (525321) +#define GROUPCARD_TRIPLE 0x8040a // (525322) +#define PERSCARD_TRIPLE 0x8040b // (525323) +#define MULTIPLEXR_TRIPLE 0x80500 // (525568) +#define CYBERHEAL_TRIPLE 0x80501 // (525569) +#define CYBERMINE_TRIPLE 0x80502 // (525570) +#define CYBERCARD_TRIPLE 0x80503 // (525571) +#define SHODO_SHRINE_TRIPLE 0x80504 // (525572) +#define ICEWALL_TRIPLE 0x80505 // (525573) +#define INFONODE_TRIPLE 0x80506 // (525574) +#define CSPACE_EXIT_TRIPLE 0x80507 // (525575) +#define DATALET_TRIPLE 0x80508 // (525576) +#define BARRICADE_TRIPLE 0x80509 // (525577) +#define TARGET_TRIPLE 0x8050a // (525578) +#define ARROW_TRIPLE 0x8050b // (525579) +#define BEAMBLST_TRIPLE 0x80600 // (525824) +#define ACIDCORR_TRIPLE 0x80601 // (525825) +#define BULLETHOLE_TRIPLE 0x80602 // (525826) +#define EXBLAST_TRIPLE 0x80603 // (525827) +#define BURNRES_TRIPLE 0x80604 // (525828) +#define BLOODSTN_TRIPLE 0x80605 // (525829) +#define CHEMSPLAT_TRIPLE 0x80606 // (525830) +#define OILPUDDLE_TRIPLE 0x80607 // (525831) +#define WASTESPILL_TRIPLE 0x80608 // (525832) +#define ISOTOPE_X_TRIPLE 0x80700 // (526080) +#define CIRCBOARD1_TRIPLE 0x80701 // (526081) +#define PLASTIQUE_TRIPLE 0x80702 // (526082) +#define FAUX_X_TRIPLE 0x80703 // (526083) +#define CIRCBOARD4_TRIPLE 0x80704 // (526084) +#define CIRCBOARD5_TRIPLE 0x80705 // (526085) +#define CIRCBOARD6_TRIPLE 0x80706 // (526086) +#define CIRCBOARD7_TRIPLE 0x80707 // (526087) +#define SWITCH1_TRIPLE 0x90000 // (589824) +#define SWITCH2_TRIPLE 0x90001 // (589825) +#define BUTTON1_TRIPLE 0x90002 // (589826) +#define BUTTON2_TRIPLE 0x90003 // (589827) +#define LEVER1_TRIPLE 0x90004 // (589828) +#define LEVER2_TRIPLE 0x90005 // (589829) +#define BIGRED_TRIPLE 0x90006 // (589830) +#define BIGLEVER_TRIPLE 0x90007 // (589831) +#define DIAL_TRIPLE 0x90008 // (589832) +#define ACCESS_SLOT_TRIPLE 0x90100 // (590080) +#define CRCT_BD_SLOT_TRIPLE 0x90101 // (590081) +#define CHEM_RECEPT_TRIPLE 0x90102 // (590082) +#define ANTENNA_PAN_TRIPLE 0x90103 // (590083) +#define PLAS_ANTENNA_TRIPLE 0x90104 // (590084) +#define DEST_ANTENNA_TRIPLE 0x90105 // (590085) +#define RETSCANNER_TRIPLE 0x90106 // (590086) +#define CYB_TERM_TRIPLE 0x90200 // (590336) +#define ENRG_CHARGE_TRIPLE 0x90201 // (590337) +#define FIXUP_STATION_TRIPLE 0x90202 // (590338) +#define ACCPANEL1_TRIPLE 0x90300 // (590592) +#define ACCPANEL2_TRIPLE 0x90301 // (590593) +#define ACCPANEL3_TRIPLE 0x90302 // (590594) +#define ACCPANEL4_TRIPLE 0x90303 // (590595) +#define ELEPANEL1_TRIPLE 0x90304 // (590596) +#define ELEPANEL2_TRIPLE 0x90305 // (590597) +#define ELEPANEL3_TRIPLE 0x90306 // (590598) +#define KEYPAD1_TRIPLE 0x90307 // (590599) +#define KEYPAD2_TRIPLE 0x90308 // (590600) +#define ACCPANEL5_TRIPLE 0x90309 // (590601) +#define ACCPANEL6_TRIPLE 0x9030a // (590602) +#define AMMOVEND_TRIPLE 0x90400 // (590848) +#define HEALVEND_TRIPLE 0x90401 // (590849) +#define CYBERTOG1_TRIPLE 0x90500 // (591104) +#define CYBERTOG2_TRIPLE 0x90501 // (591105) +#define CYBERTOG3_TRIPLE 0x90502 // (591106) +#define BLAST_DOOR_TRIPLE 0xa0000 // (655360) +#define ACCESS_DOOR_TRIPLE 0xa0001 // (655361) +#define RESID_DOOR_TRIPLE 0xa0002 // (655362) +#define MAINT_DOOR_TRIPLE 0xa0003 // (655363) +#define HOSP_DOOR_TRIPLE 0xa0004 // (655364) +#define LAB_DOOR_TRIPLE 0xa0005 // (655365) +#define STOR_DOOR_TRIPLE 0xa0006 // (655366) +#define REACTR_DOOR_TRIPLE 0xa0007 // (655367) +#define EXEC_DOOR_TRIPLE 0xa0008 // (655368) +#define NO_DOOR_TRIPLE 0xa0009 // (655369) +#define LAB_DOORWAY_TRIPLE 0xa0100 // (655616) +#define RES_DOORWAY_TRIPLE 0xa0101 // (655617) +#define BRJ_DOORWAY_TRIPLE 0xa0102 // (655618) +#define RCT_DOORWAY_TRIPLE 0xa0103 // (655619) +#define GRATING1_TRIPLE 0xa0104 // (655620) +#define GRATING2_TRIPLE 0xa0105 // (655621) +#define GRATING3_TRIPLE 0xa0106 // (655622) +#define GRATING4_TRIPLE 0xa0107 // (655623) +#define NO_DOOR2_TRIPLE 0xa0108 // (655624) +#define LABFORCE_TRIPLE 0xa0200 // (655872) +#define BROKLABFORCE_TRIPLE 0xa0201 // (655873) +#define RESFORCE_TRIPLE 0xa0202 // (655874) +#define BROKRESFORCE_TRIPLE 0xa0203 // (655875) +#define GENFORCE_TRIPLE 0xa0204 // (655876) +#define CYBGENFORCE_TRIPLE 0xa0205 // (655877) +#define NO_DOOR3_TRIPLE 0xa0206 // (655878) +#define EXEC_ELEV_TRIPLE 0xa0300 // (656128) +#define REG_ELEV1_TRIPLE 0xa0301 // (656129) +#define REG_ELEV2_TRIPLE 0xa0302 // (656130) +#define FREIGHT_ELEV_TRIPLE 0xa0303 // (656131) +#define NO_DOOR4_TRIPLE 0xa0304 // (656132) +#define DOUB_LEFTDOOR_TRIPLE 0xa0400 // (656384) +#define DOUB_RITEDOOR_TRIPLE 0xa0401 // (656385) +#define IRIS_TRIPLE 0xa0402 // (656386) +#define VERT_OPEN_TRIPLE 0xa0403 // (656387) +#define VERT_SPLIT_TRIPLE 0xa0404 // (656388) +#define NO_DOOR5_TRIPLE 0xa0405 // (656389) +#define SECRET_DOOR1_TRIPLE 0xa0406 // (656390) +#define SECRET_DOOR2_TRIPLE 0xa0407 // (656391) +#define SECRET_DOOR3_TRIPLE 0xa0408 // (656392) +#define INVISO_DOOR_TRIPLE 0xa0409 // (656393) +#define ALERT_PANEL_OFF_TRIPLE 0xb0000 // (720896) +#define ALERT_PANEL_ON_TRIPLE 0xb0001 // (720897) +#define HORZ_KLAXOFF_TRIPLE 0xb0002 // (720898) +#define HORZ_KLAXON_TRIPLE 0xb0003 // (720899) +#define SPARK_CABLE_TRIPLE 0xb0004 // (720900) +#define TWITCH_MUT2_TRIPLE 0xb0005 // (720901) +#define MACHINE_TRIPLE 0xb0006 // (720902) +#define HOLOG_ANIM_TRIPLE 0xb0007 // (720903) +#define TWITCH_MUT_TRIPLE 0xb0008 // (720904) +#define BLOOD1_TRIPLE 0xb0100 // (721152) +#define CAMEXPL_TRIPLE 0xb0101 // (721153) +#define TVEXPL_TRIPLE 0xb0102 // (721154) +#define SIMPLSMOKE_TRIPLE 0xb0103 // (721155) +#define PLANTEXPL_TRIPLE 0xb0104 // (721156) +#define BULLETWALLHIT_TRIPLE 0xb0105 // (721157) +#define BEAMWALLHIT_TRIPLE 0xb0106 // (721158) +#define IMPACT_ANIM_TRIPLE 0xb0107 // (721159) +#define BULL_ROBOT_TRIPLE 0xb0108 // (721160) +#define BEAM_ROBOT1_TRIPLE 0xb0109 // (721161) +#define BEAM_ROBOT2_TRIPLE 0xb010a // (721162) +#define EXPLOSION1_TRIPLE 0xb0200 // (721408) +#define EXPLOSION2_TRIPLE 0xb0201 // (721409) +#define EXPLOSION3_TRIPLE 0xb0202 // (721410) +#define LG_EXPLOSION_TRIPLE 0xb0203 // (721411) +#define MAGPULSEHIT_TRIPLE 0xb0204 // (721412) +#define STUNHIT_TRIPLE 0xb0205 // (721413) +#define PLASMAHIT_TRIPLE 0xb0206 // (721414) +#define SMOKEEXPL_TRIPLE 0xb0207 // (721415) +#define CRATEEXPL_TRIPLE 0xb0208 // (721416) +#define MNTR2EXPL_TRIPLE 0xb0209 // (721417) +#define GASEXPL_TRIPLE 0xb020a // (721418) +#define EMPEXPL_TRIPLE 0xb020b // (721419) +#define CORP_HUM_EXPL_TRIPLE 0xb020c // (721420) +#define CORP_ROB_EXPL_TRIPLE 0xb020d // (721421) +#define ENTRY_TRIG_TRIPLE 0xc0000 // (786432) +#define NULL_TRIG_TRIPLE 0xc0001 // (786433) +#define FLOOR_TRIG_TRIPLE 0xc0002 // (786434) +#define PLRDETH_TRIG_TRIPLE 0xc0003 // (786435) +#define DETHWATCH_TRIG_TRIPLE 0xc0004 // (786436) +#define AOE_ENT_TRIG_TRIPLE 0xc0005 // (786437) +#define AOE_CON_TRIG_TRIPLE 0xc0006 // (786438) +#define AI_HINT_TRIPLE 0xc0007 // (786439) +#define LEVEL_TRIG_TRIPLE 0xc0008 // (786440) +#define CONTIN_TRIG_TRIPLE 0xc0009 // (786441) +#define REPULSOR_TRIPLE 0xc000a // (786442) +#define ECOLOGY_TRIG_TRIPLE 0xc000b // (786443) +#define SHODO_TRIG_TRIPLE 0xc000c // (786444) +#define TRIPBEAM_TRIPLE 0xc0100 // (786688) +#define BIOHAZARD_TRIPLE 0xc0200 // (786944) +#define RADHAZARD_TRIPLE 0xc0201 // (786945) +#define CHEMHAZARD_TRIPLE 0xc0202 // (786946) +#define MAPNOTE_TRIPLE 0xc0203 // (786947) +#define MUSIC_MARK_TRIPLE 0xc0204 // (786948) +#define SML_CRT_TRIPLE 0xd0000 // (851968) +#define LG_CRT_TRIPLE 0xd0001 // (851969) +#define SECURE_CONTR_TRIPLE 0xd0002 // (851970) +#define RAD_BARREL_TRIPLE 0xd0100 // (852224) +#define TOXIC_BARREL_TRIPLE 0xd0101 // (852225) +#define CHEM_TANK_TRIPLE 0xd0102 // (852226) +#define THERMOS_TRIPLE 0xd0200 // (852480) +#define VIAL_CONT_TRIPLE 0xd0201 // (852481) +#define FLASK_CONT_TRIPLE 0xd0202 // (852482) +#define BEAKER_CONT_TRIPLE 0xd0203 // (852483) +#define MUT_CORPSE1_TRIPLE 0xd0300 // (852736) +#define MUT_CORPSE2_TRIPLE 0xd0301 // (852737) +#define MUT_CORPSE3_TRIPLE 0xd0302 // (852738) +#define MUT_CORPSE4_TRIPLE 0xd0303 // (852739) +#define MUT_CORPSE5_TRIPLE 0xd0304 // (852740) +#define MUT_CORPSE6_TRIPLE 0xd0305 // (852741) +#define MUT_CORPSE7_TRIPLE 0xd0306 // (852742) +#define MUT_CORPSE8_TRIPLE 0xd0307 // (852743) +#define ROB_CORPSE1_TRIPLE 0xd0400 // (852992) +#define ROB_CORPSE2_TRIPLE 0xd0401 // (852993) +#define ROB_CORPSE3_TRIPLE 0xd0402 // (852994) +#define ROB_CORPSE4_TRIPLE 0xd0403 // (852995) +#define ROB_CORPSE5_TRIPLE 0xd0404 // (852996) +#define ROB_CORPSE6_TRIPLE 0xd0405 // (852997) +#define ROB_CORPSE7_TRIPLE 0xd0406 // (852998) +#define ROB_CORPSE8_TRIPLE 0xd0407 // (852999) +#define ROB_CORPSE9_TRIPLE 0xd0408 // (853000) +#define ROB_CORPSE10_TRIPLE 0xd0409 // (853001) +#define ROB_CORPSE11_TRIPLE 0xd040a // (853002) +#define ROB_CORPSE12_TRIPLE 0xd040b // (853003) +#define ROB_CORPSE13_TRIPLE 0xd040c // (853004) +#define CYB_CORPSE1_TRIPLE 0xd0500 // (853248) +#define CYB_CORPSE2_TRIPLE 0xd0501 // (853249) +#define CYB_CORPSE3_TRIPLE 0xd0502 // (853250) +#define CYB_CORPSE4_TRIPLE 0xd0503 // (853251) +#define CYB_CORPSE5_TRIPLE 0xd0504 // (853252) +#define CYB_CORPSE6_TRIPLE 0xd0505 // (853253) +#define CYB_CORPSE7_TRIPLE 0xd0506 // (853254) +#define OTH_CORPSE1_TRIPLE 0xd0600 // (853504) +#define OTH_CORPSE2_TRIPLE 0xd0601 // (853505) +#define OTH_CORPSE3_TRIPLE 0xd0602 // (853506) +#define OTH_CORPSE4_TRIPLE 0xd0603 // (853507) +#define OTH_CORPSE5_TRIPLE 0xd0604 // (853508) +#define OTH_CORPSE6_TRIPLE 0xd0605 // (853509) +#define OTH_CORPSE7_TRIPLE 0xd0606 // (853510) +#define OTH_CORPSE8_TRIPLE 0xd0607 // (853511) +#define HUMAN_CRIT_TRIPLE 0xe0000 // (917504) +#define GOR_TIGER_TRIPLE 0xe0001 // (917505) +#define INSECT_CRIT_TRIPLE 0xe0002 // (917506) +#define AVIAN_CRIT_TRIPLE 0xe0003 // (917507) +#define PLANT_CRIT_TRIPLE 0xe0004 // (917508) +#define ZERO_CRIT_TRIPLE 0xe0005 // (917509) +#define PLAYER_CRIT_TRIPLE 0xe0006 // (917510) +#define INVISO_CRIT_TRIPLE 0xe0007 // (917511) +#define VIRUS_CRIT_TRIPLE 0xe0008 // (917512) +#define LIFT_BOT_TRIPLE 0xe0100 // (917760) +#define REPAIRBOT_TRIPLE 0xe0101 // (917761) +#define SERVBOT_TRIPLE 0xe0102 // (917762) +#define EXECBOT_TRIPLE 0xe0103 // (917763) +#define LGTURRET_TRIPLE 0xe0104 // (917764) +#define HOPPER_TRIPLE 0xe0105 // (917765) +#define SECURITY_BOT1_TRIPLE 0xe0106 // (917766) +#define SECURITY_BOT2_TRIPLE 0xe0107 // (917767) +#define AUTOBOMB_TRIPLE 0xe0108 // (917768) +#define REPAIRBOT2_TRIPLE 0xe0109 // (917769) +#define FLIER_TRIPLE 0xe010a // (917770) +#define SECURITY_BOT3_TRIPLE 0xe010b // (917771) +#define CYBORG_DRONE_TRIPLE 0xe0200 // (918016) +#define WARRIOR_TRIPLE 0xe0201 // (918017) +#define ASSASSIN_TRIPLE 0xe0202 // (918018) +#define CYBERBABE_TRIPLE 0xe0203 // (918019) +#define ELITE_GUARD_TRIPLE 0xe0204 // (918020) +#define CORTEX_REAVER_TRIPLE 0xe0205 // (918021) +#define MUTANT_BORG_TRIPLE 0xe0206 // (918022) +#define CYBERDOG_TRIPLE 0xe0300 // (918272) +#define CYBERGUARD_TRIPLE 0xe0301 // (918273) +#define CYBER_CORTEX_TRIPLE 0xe0302 // (918274) +#define CYBER_DYN_ICE_TRIPLE 0xe0303 // (918275) +#define CYBER_HNT_KIL_TRIPLE 0xe0304 // (918276) +#define CYBER_SHODAN_TRIPLE 0xe0305 // (918277) +#define CYBERGUARD2_TRIPLE 0xe0306 // (918278) +#define ROBOBABE_TRIPLE 0xe0400 // (918528) +#define DIEGO_TRIPLE 0xe0401 // (918529) diff --git a/engine/src/GameSrc/Headers/palfx.h b/engine/src/GameSrc/Headers/palfx.h new file mode 100644 index 0000000..97e6751 --- /dev/null +++ b/engine/src/GameSrc/Headers/palfx.h @@ -0,0 +1,36 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __PALFX_H +#define __PALFX_H +/* + * $Source: r:/prj/cit/src/inc/RCS/palfx.h $ + * $Revision: 1.5 $ + * $Author: xemu $ + * $Date: 1994/07/19 23:25:37 $ + */ + +extern void palfx_fade_up(uchar do_now); +extern void palfx_fade_down(); +extern void palfx_init(); + +void finish_pal_effect(byte id); +byte palfx_start_fade_up(uchar *new_pal); + +extern byte cyc_id0, cyc_id1, cyc_id2, cyc_id3, cyc_id4, cyc_id5; +#endif // __PALFX_H diff --git a/engine/src/GameSrc/Headers/pathfind.h b/engine/src/GameSrc/Headers/pathfind.h new file mode 100644 index 0000000..f377a15 --- /dev/null +++ b/engine/src/GameSrc/Headers/pathfind.h @@ -0,0 +1,54 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include "map.h" +#include "objects.h" + +char request_pathfind(LGPoint source, LGPoint dest, uchar dest_z, uchar start_z, uchar priority); +char next_step_on_path(char path_id, LGPoint *next, char *steps_left); +errtype check_requests(uchar priority); +errtype delete_path(char path_id); +uchar check_path_cutting(LGPoint new_sq, char path_id); +errtype reset_pathfinding(); +char compute_next_step(char path_id, LGPoint *pt, char step_num); +uchar pf_check_doors(MapElem *pme, char dir, ObjID *open_door); +uchar pf_obj_doors(MapElem *pme1, MapElem *pme2, char dir, ObjID *open_door); + +#define NUM_PATH_STEPS 64 +#define MAX_PATHS 16 + +typedef struct { + LGPoint source; + LGPoint dest; + // dest_z and start_z are in objLoc height coordinates + uchar dest_z; + uchar start_z; + char num_steps; + char curr_step; + uchar moves[NUM_PATH_STEPS / 4]; // each char holds 4 steps, so we need 16 of 'em +} Path; + +#ifdef __PATHFIND_SRC +Path paths[MAX_PATHS]; +ushort used_paths = 0; +#else +extern Path paths[MAX_PATHS]; +extern ushort used_paths; +#endif + +#define path_length(x) (paths[(x)].num_steps) diff --git a/engine/src/GameSrc/Headers/physics.h b/engine/src/GameSrc/Headers/physics.h new file mode 100644 index 0000000..6ae4a6e --- /dev/null +++ b/engine/src/GameSrc/Headers/physics.h @@ -0,0 +1,180 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __PHYSICS_H +#define __PHYSICS_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/physics.h $ + * $Revision: 1.36 $ + * $Author: mahk $ + * $Date: 1994/09/06 08:44:53 $ + * + * + */ + +// Includes +#include "objects.h" +#include "dirac.h" + +// Defines + +typedef fix Physvec[6]; +#define CONTROL_NO_CHANGE -127 + +#define MAX_PHYS_HANDLE MAX_OBJ + +#define CHECK_OBJ_PH(oid) ((objs[oid].info.ph != -1) && (objs[oid].info.ph <= MAX_PHYS_HANDLE)) + +#define CSLOPE_SET(a, b, c) \ + { \ + terrain_info.cx = (a); \ + terrain_info.cy = (b); \ + terrain_info.cz = (c); \ + } +#define FSLOPE_SET(a, b, c) \ + { \ + terrain_info.fx = (a); \ + terrain_info.fy = (b); \ + terrain_info.fz = (c); \ + } +#define WGRAD_SET(a, b, c) \ + { \ + terrain_info.wx = (a); \ + terrain_info.wy = (b); \ + terrain_info.wz = (c); \ + } +#define WGRAD_ADD(a, b, c) \ + { \ + terrain_info.wx += (a); \ + terrain_info.wy += (b); \ + terrain_info.wz += (c); \ + } + +#define NUM_EDMS_MODELS 5 +#define EDMS_NONE 0 +#define EDMS_ROBOT 1 +#define EDMS_PELVIS 2 +#define EDMS_JELLO 3 +#define EDMS_DIRAC 4 + +// These are the magic edms cyber_space numbers. +#define PELVIS_MODE_NORMAL 0 +#define PELVIS_MODE_SKATES 1 +#define PELVIS_MODE_CYBER 2 + +#define STANDARD_MASS fix_make(1, 0) +#define STANDARD_SIZE (standard_robot.size) +#define STANDARD_GRAVITY fix_make(4, 0) + +// Prototypes + +#define CONTROL_BANKS 4 +#define CONTROL_MAX_VAL 100 +// why is this number 100? what is the point?? + +// Set the player motion controls, on a scale of -100 to +100. +// An arg of CONTROL_NO_CHANGE leaves the value unchanged. +// There are CONTROL_BANKS banks of controls, which roughly average +// together. +errtype physics_set_player_controls(int bank, byte xvel, byte yvel, byte zvel, byte xyrot, byte yzrot, byte xzrot); + +// Set a single control, using the defined control numbers + +#define CONTROL_XVEL 0 // x translation +#define CONTROL_YVEL 1 // y translation +#define CONTROL_ZVEL 2 // z translation +#define CONTROL_XYROT 3 // xy rotation +#define CONTROL_YZROT 4 // yz rotation +#define CONTROL_XZROT 5 // xz rotation + +#define MOUSE_CONTROL_BANK 0 +#define KEYBD_CONTROL_BANK 1 +#define JOYST_CONTROL_BANK 2 +#define INP6D_CONTROL_BANK 3 +errtype physics_set_one_control(int bank, int num, byte val); +errtype physics_get_one_control(int bank, int num, byte *val); + +// Run the physics system for one frame +errtype physics_run(void); + +// Initialize EDMS, player, etc. +errtype physics_init(void); + +// Set the gravity parameter of all objects to new_grav +errtype apply_gravity_to_objects(fix new_grav); + +// Take an object, and moves it to a position and velocity relative to the +// player. returns true if it finds an appropriate place to put the object. +uchar player_throw_object(ObjID id, int x, int y, int lastx, int lasty, fix vel); + +// Cause the player to assume one of three postures +#define POSTURE_STAND 0 +#define POSTURE_STOOP 1 +#define POSTURE_PRONE 2 +#define NUM_POSTURES 3 +errtype player_set_posture(ubyte new_posture); + +// Lean the player. Values are in a -100-+100 scale. +// Hey kids, this don't exist no more. set your self an +// XZROT control if you want to lean sideways. +errtype player_set_lean(byte x, byte y); + +// Plant the player's foot, turning directional controls into +// translational ones. Unplants foot IFF planted is false +errtype player_plant_foot(uchar planted); + +// Set the player's eye position -100 to +100 +void player_set_eye(byte eyecntl); + +// Build the model given a state and object ID, and assign appropriate +// data into the object and do appropriate bookkeeping +errtype assemble_physics_object(ObjID id, State *pnew_state); + +// Instantiators +void instantiate_robot(int triple, Robot *r); +void instantiate_pelvis(int triple, Pelvis *r); +void instantiate_dirac(int triple, Dirac_frame *new_dirac); + +errtype apply_gravity_to_one_object(ObjID oid, fix new_grav); + +uchar get_phys_info(int ph, fix *list, int cnt); + +void get_phys_state(int ph, State *new_state, ObjID id); + +fix ID2radius(ObjID id); + +void physics_set_relax(int axis, uchar relax); + +void physics_zero_all_controls(void); + +void state_to_objloc(State *s, ObjLoc *l); + +void cit_sleeper_callback(physics_handle caller); + +void edms_delete_go(void); + +errtype collide_objects(ObjID collision, ObjID victim, int bad); + +// Globals + +extern TerrainData terrain_info; +extern State standard_state; +extern Robot standard_robot; + +#endif // __PHYSICS_H diff --git a/engine/src/GameSrc/Headers/physunit.h b/engine/src/GameSrc/Headers/physunit.h new file mode 100644 index 0000000..53070a6 --- /dev/null +++ b/engine/src/GameSrc/Headers/physunit.h @@ -0,0 +1,59 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __PHYSUNIT_H +#define __PHYSUNIT_H + +/* + * $Source: n:/project/cit/src/inc/RCS/physunit.h $ + * $Revision: 1.8 $ + * $Author: xemu $ + * $Date: 1994/05/24 23:33:39 $ + * + * + */ + +// Includes + +// Defines +#define PHYSICS_RADIUS_UNIT 96 // radius units per square +#define PHYS_PEP_UNIT 1 // Objprop units per physics unit +#define PHYS_HARDNESS_UNIT 1 // Objprop units per physics unit +#define PHYS_ROUGHNESS_UNIT 1 // Objprop units per physics unit + +// 10 is much more correct +#define BROKEN_NEW_WAY +#ifdef BROKEN_NEW_WAY +#define PHYS_MASS_UNIT 10 +#define PHYS_MASS_C_NUM 80 +#define PHYS_MASS_C_DEN 33 +#endif + +//#define BROKEN_OLD_WAY +#ifdef BROKEN_OLD_WAY +// what in hell is this...? +#define PHYS_MASS_UNIT 1000 // tenths of kilograms to players +#define PHYS_MASS_C_NUM 1 +#define PHYS_MASS_C_DEN 1 +#endif + +// Prototypes + +// Globals + +#endif // __PHYSUNIT_H diff --git a/engine/src/GameSrc/Headers/player.h b/engine/src/GameSrc/Headers/player.h new file mode 100644 index 0000000..78c3e4d --- /dev/null +++ b/engine/src/GameSrc/Headers/player.h @@ -0,0 +1,304 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __PLAYER_H +#define __PLAYER_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/player.h $ + * $Revision: 1.95 $ + * $Author: minman $ + * $Date: 1994/09/06 21:17:30 $ + * + * + */ + +// Includes +#include "gamesys.h" +#include "objects.h" +#include "map.h" + +// Defines +#define DEGREES_OF_FREEDOM 6 // number of physics control axes. + +// Basic quantities, of MFD's, slots, buttons, functions +#define NUM_MFDS 2 +#define MFD_NUM_VIRTUAL_SLOTS 5 +#define MFD_NUM_REAL_SLOTS (MFD_NUM_VIRTUAL_SLOTS + NUM_MFDS) +#define MFD_NUM_FUNCS 32 // this oughta do for now. + +#define NUM_EMAIL_PROPER 47 +#define NUM_LOG_LEVELS 14 +#define LOGS_PER_LEVEL 16 +#define NUM_DATA 23 + +#define NUM_EMAIL (NUM_EMAIL_PROPER + NUM_DATA + NUM_LOG_LEVELS * LOGS_PER_LEVEL) + +#define NUM_DAMAGE_TYPES 8 + +typedef uint8_t MFD_Status; +#define MFD_EMPTY 0 +#define MFD_FLASH 1 +#define MFD_ACTIVE 2 +#define MFD_UNAVAIL 3 + +#define DEFAULT_FATIGUE_REGEN 50 +#define NUM_WEAPON_SLOTS 7 +#define EMPTY_WEAPON_SLOT 0xFF +#define NUM_GENERAL_SLOTS 14 + +typedef enum { + ACTIVE_WEAPON = 0, + ACTIVE_GRENADE = 1, + ACTIVE_DRUG = 2, + ACTIVE_CART = 3, + ACTIVE_HARDWARE = 4, + ACTIVE_COMBAT_SOFT = 5, + ACTIVE_DEFENSE_SOFT = 6, + ACTIVE_MISC_SOFT = 7, + ACTIVE_GENERAL = 8, + ACTIVE_EMAIL = 9, + NUM_ACTIVES = 10 +} Actives; + +#define PLAYER_VERSION_NUMBER ((int)6) + +// Some questbitty stuff +#define NUM_QUESTBITS 512 +#define QUESTBIT_GET(qnum) (player_struct.questbits[((qnum) / 8u)] & (1u << ((qnum) % 8u))) +#define QUESTBIT_ON(qnum) (player_struct.questbits[((qnum) / 8u)] |= (1u << ((qnum) % 8u))) +#define QUESTBIT_OFF(qnum) (player_struct.questbits[((qnum) / 8u)] &= ~(1u << ((qnum) % 8u))) + +#define NUM_QUESTVARS 64 +#define QUESTVAR_GET(qnum) (player_struct.questvars[(qnum)]) +#define QUESTVAR_SET(qnum, x) \ + do { \ + player_struct.questvars[(qnum)] = (x); \ + } while (0) + +#define COMBAT_DIFF_INDEX 0 +#define QUEST_DIFF_INDEX 1 +#define PUZZLE_DIFF_INDEX 2 +#define CYBER_DIFF_INDEX 3 + +#define PLAYER_MAX_HP 255 + +#define get_righto_hp(w) (*((&(player_struct.hit_points)) + w)) + +typedef struct _weapon_slot { + ubyte type; // type of weapon in slot or EMPTY_WEAPON_SLOT + ubyte subtype; // subtype of weapon + union { + ubyte ammo; // current number of rounds + ubyte heat; // how hot am I? + }; + union { + ubyte ammo_type; // current ammo type. + ubyte setting; // current charge setting. + }; + ubyte make_info; // manufacturer +} weapon_slot; + +typedef struct _softs { + ubyte combat[NUM_COMBAT_SOFTS]; + ubyte defense[NUM_DEFENSE_SOFTS]; + ubyte misc[NUM_MISC_SOFTS]; +} softs_data; + +// FIXME pragma +#pragma pack(push,1) + +typedef struct _Player { + // Static Game Data + char name[20]; + char realspace_level; // this is the last realspace level we were in + + // Difficulty related stuff + byte difficulty[4]; + ubyte level_diff_zorched[(NUM_LEVELS / 8) + 1]; // bitfield for levels -- 1 if difficulty dealt with yet, 0 else + + // system stuff + uint32_t game_time; + uint32_t last_second_update; // when was last do_stuff_every_second + uint32_t last_drug_update; + uint32_t last_ware_update; + uint32_t last_anim_check; + int queue_time; + int deltat; + byte detail_level; + + // World stuff + ubyte level; // current level + short initial_shodan_vals[NUM_LEVELS]; // initial shodan security levels + byte controls[DEGREES_OF_FREEDOM]; // physics controls + ObjID rep; // The player's object-system object + ObjLoc realspace_loc; // This is where the player will come back out of cspace into + int version_num; + ObjID inventory[NUM_GENERAL_SLOTS]; // general inventory + + // Random physics state. + ubyte posture; // current posture (standing/stooped/prone) + uchar foot_planted; // Player's foot is planted + byte leanx, leany; // leaning, -100-+100 + + // not used - eye postion - lower in struct!!!! + fixang eye; // eye position + + // Gamesys stuff + ubyte hit_points; // I bet we will want these. + ubyte cspace_hp; // after hit_points so we can array ref this stuff + ushort hit_points_regen; // Rate at which hit points regenerate, per minute + ubyte hit_points_lost[NUM_DAMAGE_TYPES]; // Rate at which damage is taken, per minute + ushort bio_post_expose; // expose damage from bio squares long past. + ushort rad_post_expose; // expose damage from rad squares long past. + ubyte energy; // suit power charge + ubyte energy_spend; // rate of energy burn + ubyte energy_regen; // Rate at which suit recharges + uchar energy_out; // out of energy last check + short cspace_trips; + int cspace_time_base; + ubyte questbits[NUM_QUESTBITS / 8]; // Mask of which "quests" you have completed + short questvars[NUM_QUESTVARS]; + uint32_t hud_modes; // What hud functions are currently active? + uchar experience; // Are you experienced? + int fatigue; // how fatigued are you + ushort fatigue_spend; // Current rate of fatigue expenditure in pts/sec + ushort fatigue_regen; // Current rate of fatigue regeneration + ushort fatigue_regen_base; // base fatigue regen rate + ushort fatigue_regen_max; // max fatigue regen rate + byte accuracy; + ubyte shield_absorb_rate; // % of damage shields absorb + ubyte shield_threshold; // Level where shields turn off + ubyte light_value; // current lamp setting + + // MFD State + ubyte mfd_virtual_slots[NUM_MFDS][MFD_NUM_VIRTUAL_SLOTS]; // ptrs to mfd_slot id's + MFD_Status mfd_slot_status[MFD_NUM_REAL_SLOTS]; + ubyte mfd_all_slots[MFD_NUM_REAL_SLOTS]; // ptrs to mfd_func id's + ubyte mfd_func_status[MFD_NUM_FUNCS]; // ptrs to mfd_func flags + ubyte mfd_func_data[MFD_NUM_FUNCS][8]; + ubyte mfd_current_slots[NUM_MFDS]; // ptrs to mfd's curr slots + ubyte mfd_empty_funcs[NUM_MFDS]; // ptrs to mfd's empty func + uchar mfd_access_puzzles[64]; // this is 4 times as much as that hardcoded 8 up there + // who knows how much we really need, hopefully in soon + // KLC - changed to 64 + char mfd_save_slot[NUM_MFDS]; + + // Inventory stuff, in general, a value of zero will indicate an empty slot + // indices are drug/grenade/ware "types" + ubyte hardwarez[NUM_HARDWAREZ]; // Which warez do we have? (level of each type?) + softs_data softs; + ubyte cartridges[NUM_AMMO_TYPES]; // Cartridges for each ammo type. + ubyte partial_clip[NUM_AMMO_TYPES]; + ubyte drugs[NUM_DRUGZ]; // Quantity of each drug + ubyte grenades[NUM_GRENADEZ]; // Quantity of each grenade. + + uchar email[NUM_EMAIL]; // Which email messages do you have. + ubyte logs[NUM_LOG_LEVELS]; // on which levels do we have logs. + + // Weapons are arranged into "slots" + weapon_slot weapons[NUM_WEAPON_SLOTS]; // Which weapons do you have? + + // Inventory status + ubyte hardwarez_status[NUM_HARDWAREZ]; // Status of active wares (on/off, activation time, recharge time?) + struct _softs softs_status; + ubyte jumpjet_energy_fraction; // fractional units of energy spent on jumpjets. + ubyte email_sender_counts[32]; // who has sent how many emails + byte drug_status[NUM_DRUGZ]; // Time left on active drugs, 0 if inactive + ubyte drug_intensity[NUM_DRUGZ]; // Intensity of active drugs, 0 if inactive + ushort grenades_time_setting[NUM_GRENADEZ]; // Time setting for each grenade + + // PLOT STUFF + ushort time2dest; // Time to destination (seconds) + ushort time2comp; // time to completion of current program (seconds) + + // Combat shtuff + ObjID curr_target; // creature currently "targeted" + uint32_t last_fire; // last gametime the weapon fired. + ushort fire_rate; // game time required between weapon fires. + + // Selectied items + ubyte actives[NUM_ACTIVES]; + + // Other transitory state + ObjID save_obj_cursor; // saving object cursor when you change to cyberspace + ObjID panel_ref; // Last panel utilized. stuffed here for reference + + // Stats... + int num_victories; + int time_in_cspace; + int rounds_fired; + int num_hits; + + // Playtesting data + int num_deaths; + + // from this point on - data is taking the time_to_level space + int32_t eye_pos; // physics eye position + + // let's hope State stays at 12 fixes + fix edms_state[12]; + + // the player's actively selected inventory category. + ubyte current_active; + ubyte active_bio_tracks; + + short current_email; + + char version[6]; + uchar dead; + + ushort lean_filter_state; + ushort FREE_BITS_HERE; + uchar mfd_save_vis; + + uint32_t auto_fire_click; + + uint32_t posture_slam_state; + + uchar terseness; + + uint32_t last_bob; // not last paul, but last bob + + uchar pad[9]; +} Player; + +#pragma pack(pop) + +#define PLAYER_OBJ (player_struct.rep) +#define PLAYER_BIN_X OBJ_LOC_BIN_X(objs[PLAYER_OBJ].loc) +#define PLAYER_BIN_Y OBJ_LOC_BIN_Y(objs[PLAYER_OBJ].loc) +#define PLAYER_FINE_X OBJ_LOC_FINE_X(objs[PLAYER_OBJ].loc) +#define PLAYER_FINE_Y OBJ_LOC_FINE_Y(objs[PLAYER_OBJ].loc) +#define PLAYER_PHYSICS (objs[PLAYER_OBJ].info.ph) +#define player_physics PLAYER_PHYSICS + +// Prototypes +errtype init_player(Player *pplr); +errtype player_tele_to(int x, int y); +errtype player_create_initial(void); +errtype player_startup(void); +errtype player_shutdown(void); +ubyte set_player_energy_spend(ubyte new_val); +bool IsFullscreenWareOn(void); + +// Globals +extern Player player_struct; +extern Obj *player_dos_obj; + +#endif // __PLAYER_H diff --git a/engine/src/GameSrc/Headers/playerlayout.h b/engine/src/GameSrc/Headers/playerlayout.h new file mode 100644 index 0000000..24ca4ff --- /dev/null +++ b/engine/src/GameSrc/Headers/playerlayout.h @@ -0,0 +1,181 @@ +// Note no include guard. Expected to be included more than once with different +// macros controlling the details of the structure. + +// Expects PL_MFD_PUZZLE_SIZE to be set. The original DOS game had 32 bytes +// reserved for MFD puzzle status; this was later expanded to 64. Unfortunately, +// save game compatibility between versions wasn't really a thing and the +// structure version number wasn't changed, so we have to go by length. +// We can load both DOS and enhanced edition save files, but the DOS edition +// won't be able to load ours; the enhanced edition should. + +// C preprocessor hackery to name the struct according to the size of the MFD +// access puzzles field. It will ultimately create structures named +// PlayerLayout_M32 and PlayerLayout_M64 according to the value of +// PL_MFD_PUZZLE_SIZE that is set on entry. +#define JOIN(x,y) x ## y +#define STRUCTNAME(x,y) JOIN(x,y) + +// Describe the layout of the player in a resfile. +const ResLayout STRUCTNAME(PlayerLayout_M, PL_MFD_PUZZLE_SIZE) = +{ + 711 + PL_MFD_PUZZLE_SIZE + 654, // size on disc + sizeof(Player), // size in memory + 0, // flags + { + { RFFT_BIN(20), offsetof(Player, name) }, //char [20] + { RFFT_UINT8, offsetof(Player, realspace_level) }, //char + { RFFT_BIN(4), offsetof(Player, difficulty) }, //byte [4] + { RFFT_BIN(3), offsetof(Player, level_diff_zorched) }, //ubyte [3] + { RFFT_UINT32, offsetof(Player, game_time) }, //uint32_t + { RFFT_UINT32, offsetof(Player, last_second_update) }, //uint32_t + { RFFT_UINT32, offsetof(Player, last_drug_update) }, //uint32_t + { RFFT_UINT32, offsetof(Player, last_ware_update) }, //uint32_t + { RFFT_UINT32, offsetof(Player, last_anim_check) }, //uint32_t + { RFFT_UINT32, offsetof(Player, queue_time) }, //int + { RFFT_UINT32, offsetof(Player, deltat) }, //int + { RFFT_UINT8, offsetof(Player, detail_level) }, //byte + { RFFT_UINT8, offsetof(Player, level) }, //ubyte + + //short initial_shodan_vals[22]; + #define L(x) { RFFT_UINT16, offsetof(Player, initial_shodan_vals[x]) } + L(0),L(1),L(2),L(3),L(4),L(5),L(6),L(7),L(8),L(9),L(10),L(11),L(12),L(13),L(14),L(15),L(16),L(17),L(18),L(19), + L(20),L(21), + #undef L + + { RFFT_BIN(6), offsetof(Player, controls) }, //byte [6] + { RFFT_UINT16, offsetof(Player, rep) }, //short + { RFFT_UINT16, offsetof(Player, realspace_loc.x) }, //ushort + { RFFT_UINT16, offsetof(Player, realspace_loc.y) }, //ushort + { RFFT_UINT8, offsetof(Player, realspace_loc.z) }, //ubyte + { RFFT_UINT8, offsetof(Player, realspace_loc.p) }, //ubyte + { RFFT_UINT8, offsetof(Player, realspace_loc.h) }, //ubyte + { RFFT_UINT8, offsetof(Player, realspace_loc.b) }, //ubyte + { RFFT_UINT32, offsetof(Player, version_num) }, //int + + //short inventory[14]; + #define L(x) { RFFT_UINT16, offsetof(Player, inventory[x]) } + L(0),L(1),L(2),L(3),L(4),L(5),L(6),L(7),L(8),L(9),L(10),L(11),L(12),L(13), + #undef L + + { RFFT_UINT8, offsetof(Player, posture) }, //ubyte + { RFFT_UINT8, offsetof(Player, foot_planted) }, //uchar + { RFFT_UINT8, offsetof(Player, leanx) }, //byte + { RFFT_UINT8, offsetof(Player, leany) }, //byte + { RFFT_UINT16, offsetof(Player, eye) }, //uint16_t + { RFFT_UINT8, offsetof(Player, hit_points) }, //ubyte + { RFFT_UINT8, offsetof(Player, cspace_hp) }, //ubyte + { RFFT_UINT16, offsetof(Player, hit_points_regen) }, //ushort + { RFFT_BIN(8), offsetof(Player, hit_points_lost) }, //ubyte [8] + { RFFT_UINT16, offsetof(Player, bio_post_expose) }, //ushort + { RFFT_UINT16, offsetof(Player, rad_post_expose) }, //ushort + { RFFT_UINT8, offsetof(Player, energy) }, //ubyte + { RFFT_UINT8, offsetof(Player, energy_spend) }, //ubyte + { RFFT_UINT8, offsetof(Player, energy_regen) }, //ubyte + { RFFT_UINT8, offsetof(Player, energy_out) }, //uchar + { RFFT_UINT16, offsetof(Player, cspace_trips) }, //short + { RFFT_UINT32, offsetof(Player, cspace_time_base) }, //int + { RFFT_BIN(64), offsetof(Player, questbits) }, //ubyte [64] + + //short questvars[64]; + #define L(x) { RFFT_UINT16, offsetof(Player, questvars[x]) } + L(0),L(1),L(2),L(3),L(4),L(5),L(6),L(7),L(8),L(9),L(10),L(11),L(12),L(13),L(14),L(15),L(16),L(17),L(18),L(19), + L(20),L(21),L(22),L(23),L(24),L(25),L(26),L(27),L(28),L(29),L(30),L(31),L(32),L(33),L(34),L(35),L(36),L(37), + L(38),L(39),L(40),L(41),L(42),L(43),L(44),L(45),L(46),L(47),L(48),L(49),L(50),L(51),L(52),L(53),L(54),L(55), + L(56),L(57),L(58),L(59),L(60),L(61),L(62),L(63), + #undef L + + { RFFT_UINT32, offsetof(Player, hud_modes) }, //uint32_t + { RFFT_UINT8, offsetof(Player, experience) }, //uchar + { RFFT_UINT32, offsetof(Player, fatigue) }, //int + { RFFT_UINT16, offsetof(Player, fatigue_spend) }, //ushort + { RFFT_UINT16, offsetof(Player, fatigue_regen) }, //ushort + { RFFT_UINT16, offsetof(Player, fatigue_regen_base) }, //ushort + { RFFT_UINT16, offsetof(Player, fatigue_regen_max) }, //ushort + { RFFT_UINT8, offsetof(Player, accuracy) }, //byte + { RFFT_UINT8, offsetof(Player, shield_absorb_rate) }, //ubyte + { RFFT_UINT8, offsetof(Player, shield_threshold) }, //ubyte + { RFFT_UINT8, offsetof(Player, light_value) }, //ubyte + { RFFT_BIN(2*5), offsetof(Player, mfd_virtual_slots) }, //ubyte [2][5] + { RFFT_BIN(7), offsetof(Player, mfd_slot_status) }, //uint8_t [7] + { RFFT_BIN(7), offsetof(Player, mfd_all_slots) }, //ubyte [7] + { RFFT_BIN(32), offsetof(Player, mfd_func_status) }, //ubyte [32] + { RFFT_BIN(32*8), offsetof(Player, mfd_func_data) }, //ubyte [32][8] + { RFFT_BIN(2), offsetof(Player, mfd_current_slots) }, //ubyte [2] + { RFFT_BIN(2), offsetof(Player, mfd_empty_funcs) }, //ubyte [2] + { RFFT_BIN(PL_MFD_PUZZLE_SIZE), + offsetof(Player, mfd_access_puzzles) }, //uchar [64] + { RFFT_BIN(2), offsetof(Player, mfd_save_slot) }, //char [2] + { RFFT_BIN(15), offsetof(Player, hardwarez) }, //ubyte [15] + { RFFT_BIN(7), offsetof(Player, softs.combat) }, //ubyte [7] + { RFFT_BIN(3), offsetof(Player, softs.defense) }, //ubyte [3] + { RFFT_BIN(9), offsetof(Player, softs.misc) }, //ubyte [9] + { RFFT_BIN(15), offsetof(Player, cartridges) }, //ubyte [15] + { RFFT_BIN(15), offsetof(Player, partial_clip) }, //ubyte [15] + { RFFT_BIN(7), offsetof(Player, drugs) }, //ubyte [7] + { RFFT_BIN(7), offsetof(Player, grenades) }, //ubyte [7] + { RFFT_BIN(294), offsetof(Player, email) }, //uchar [294] + { RFFT_BIN(14), offsetof(Player, logs) }, //ubyte [14] + + //weapon_slot weapons[7]; + #define L(x) { RFFT_UINT8, offsetof(Player, weapons[x].type) }, \ + { RFFT_UINT8, offsetof(Player, weapons[x].subtype) }, \ + { RFFT_UINT8, offsetof(Player, weapons[x].ammo) }, \ + { RFFT_UINT8, offsetof(Player, weapons[x].ammo_type) }, \ + { RFFT_UINT8, offsetof(Player, weapons[x].make_info) } + L(0),L(1),L(2),L(3),L(4),L(5),L(6), + #undef L + + { RFFT_BIN(15), offsetof(Player, hardwarez_status) }, //ubyte [15] + { RFFT_BIN(7), offsetof(Player, softs_status.combat) }, //ubyte [7] + { RFFT_BIN(3), offsetof(Player, softs_status.defense) }, //ubyte [3] + { RFFT_BIN(9), offsetof(Player, softs_status.misc) }, //ubyte [9] + { RFFT_UINT8, offsetof(Player, jumpjet_energy_fraction) }, //ubyte + { RFFT_BIN(32), offsetof(Player, email_sender_counts) }, //ubyte [32] + { RFFT_BIN(7), offsetof(Player, drug_status) }, //byte [7] + { RFFT_BIN(7), offsetof(Player, drug_intensity) }, //ubyte [7] + + //ushort grenades_time_setting[7]; + #define L(x) { RFFT_UINT16, offsetof(Player, grenades_time_setting[x]) } + L(0),L(1),L(2),L(3),L(4),L(5),L(6), + #undef L + + { RFFT_UINT16, offsetof(Player, time2dest) }, //ushort + { RFFT_UINT16, offsetof(Player, time2comp) }, //ushort + { RFFT_UINT16, offsetof(Player, curr_target) }, //short + { RFFT_UINT32, offsetof(Player, last_fire) }, //uint32_t + { RFFT_UINT16, offsetof(Player, fire_rate) }, //ushort + { RFFT_BIN(10), offsetof(Player, actives) }, //ubyte [10] + { RFFT_UINT16, offsetof(Player, save_obj_cursor) }, //short + { RFFT_UINT16, offsetof(Player, panel_ref) }, //short + { RFFT_UINT32, offsetof(Player, num_victories) }, //int + { RFFT_UINT32, offsetof(Player, time_in_cspace) }, //int + { RFFT_UINT32, offsetof(Player, rounds_fired) }, //int + { RFFT_UINT32, offsetof(Player, num_hits) }, //int + { RFFT_UINT32, offsetof(Player, num_deaths) }, //int + { RFFT_UINT32, offsetof(Player, eye_pos) }, //int32_t + + //int32_t edms_state[12]; + #define L(x) { RFFT_UINT32, offsetof(Player, edms_state[x]) } + L(0),L(1),L(2),L(3),L(4),L(5),L(6),L(7),L(8),L(9),L(10),L(11), + #undef L + + { RFFT_UINT8, offsetof(Player, current_active) }, //ubyte + { RFFT_UINT8, offsetof(Player, active_bio_tracks) }, //ubyte + { RFFT_UINT16, offsetof(Player, current_email) }, //short + { RFFT_BIN(6), offsetof(Player, version) }, //char [6] + { RFFT_UINT8, offsetof(Player, dead) }, //uchar + { RFFT_UINT16, offsetof(Player, lean_filter_state) }, //ushort + { RFFT_UINT16, offsetof(Player, FREE_BITS_HERE) }, //ushort + { RFFT_UINT8, offsetof(Player, mfd_save_vis) }, //uchar + { RFFT_UINT32, offsetof(Player, auto_fire_click) }, //uint32_t + { RFFT_UINT32, offsetof(Player, posture_slam_state) }, //uint32_t + { RFFT_UINT8, offsetof(Player, terseness) }, //uchar + { RFFT_UINT32, offsetof(Player, last_bob) }, //uint32_t + { RFFT_BIN(9), offsetof(Player, pad) }, //uchar [9] + + { RFFT_END, 0 } + } +}; + +#undef JOIN +#undef STRUCTNAME diff --git a/engine/src/GameSrc/Headers/plotware.h b/engine/src/GameSrc/Headers/plotware.h new file mode 100644 index 0000000..cfa88ea --- /dev/null +++ b/engine/src/GameSrc/Headers/plotware.h @@ -0,0 +1,27 @@ +/* + +Copyright (C) 2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef PLOTWARE_H +#define PLOTWARE_H + +errtype mfd_plotware_init(MFD_Func *f); +void mfd_plotware_expose(MFD *mfd, ubyte control); +void plotware_turnon(uchar visible, uchar real); + +#endif diff --git a/engine/src/GameSrc/Headers/popups.h b/engine/src/GameSrc/Headers/popups.h new file mode 100644 index 0000000..f1f510a --- /dev/null +++ b/engine/src/GameSrc/Headers/popups.h @@ -0,0 +1,57 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __POPUPS_H +#define __POPUPS_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/popups.h $ + * $Revision: 1.3 $ + * $Author: xemu $ + * $Date: 1994/08/16 22:13:23 $ + * + */ + +#define POPUP_MFD_LEFT 0 +#define POPUP_MFD_RIGHT 1 +#define POPUP_DOWN 2 +#define POPUP_ICON_LEFT 3 +#define POPUP_ICON_RIGHT 4 +#define NUM_POPUPS 5 + +void init_popups(void); +// initalizes popup cursors + +void make_popup_cursor(LGCursor *c, grs_bitmap *bm, char *string, uint tmplt, uchar allocate, LGPoint offset); +void make_email_cursor(LGCursor *c, grs_bitmap *bm, uchar page, bool init); + +/* Modifies c and bm to be a cursor built from the specified + string and tmplt. if allocate is true the bits for the cursor bitmap + will be Malloc'ed. Otherwise, bm must already have a bits field set that + points to enough memory for the cursor bitmap. + */ + +void load_string_array(Ref first, char *arry[], char buf[], int sz, int n); +/* + Loads N seuquential strings (starting with ) into buf. + Fills the array arry with pointers to each of the strings. +*/ + +extern uchar popup_cursors; + +#endif // __POPUPS_H diff --git a/engine/src/GameSrc/Headers/precompiled.h b/engine/src/GameSrc/Headers/precompiled.h new file mode 100644 index 0000000..435cf28 --- /dev/null +++ b/engine/src/GameSrc/Headers/precompiled.h @@ -0,0 +1,52 @@ +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "2d.h" + +#include "3d.h" +#include "3dinterp.h" + +#include "array.h" +#include "hash.h" +#include "pqueue.h" +#include "rect.h" +#include "slist.h" + +#include "edms.h" +#include "edms_chk.h" + +#include "fix.h" + +#include "2dres.h" +// use explicit path for error.h include so it doesn't use system's +#include "../../Libraries/H/error.h" +#include "keydefs.h" +#include "lg_types.h" +#include "lg.h" +#include "memall.h" +#include "tmpalloc.h" + +#include "palette.h" + +#include "res.h" +#include "lzw.h" + +#include "rnd.h" + +#include "lgsndx.h" + +#include "event.h" +#include "hotkey.h" +#include "region.h" +#include "slab.h" +#include "vmouse.h" + +#include "vox.h" + +#ifdef __cplusplus +} +#endif diff --git a/engine/src/GameSrc/Headers/rcolors.h b/engine/src/GameSrc/Headers/rcolors.h new file mode 100644 index 0000000..271f15c --- /dev/null +++ b/engine/src/GameSrc/Headers/rcolors.h @@ -0,0 +1,36 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#define PURPLE_8_BASE 0x20 +#define MAIZE_8_BASE 0x28 +#define RED_8_BASE 0x33 +#define ORANGE_8_BASE 0x41 +#define YELLOW_8_BASE 0x4b +#define GREEN_8_BASE 0x59 +#define AQUA_8_BASE 0x66 +#define BLUE_8_BASE 0x76 +#define REDBROWN_8_BASE 0x85 +#define BROWN_8_BASE 0x94 +#define GRAYGREEN_8_BASE 0xA0 +#define BRIGHTBROWN_8_BASE 0xA8 +#define METALBLUE_8_BASE 0xB6 +#define LIGHTBROWN_8_BASE 0xC6 +#define GRAY_8_BASE 0xD6 + +#define PULSE_RED 0x1c +#define PULSE_GREEN 0x0d diff --git a/engine/src/GameSrc/Headers/refstuf.h b/engine/src/GameSrc/Headers/refstuf.h new file mode 100644 index 0000000..b289575 --- /dev/null +++ b/engine/src/GameSrc/Headers/refstuf.h @@ -0,0 +1,55 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include "objects.h" + +#define CitrefsClearDealt() \ + do { \ + LG_memset(refdealt, 0, NUM_REF_OBJECTS / 8); \ + } while (0) +#define CitrefSetDealt(orefid) \ + do { \ + refdealt[(orefid) >> 3] |= 1 << ((orefid)&0x7); \ + } while (0) +#define CitrefClearDealt(orefid) \ + do { \ + refdealt[(orefid) >> 3] &= ~(1 << ((orefid)&0x7)); \ + } while (0) +#define CitrefCheckDealt(orefid) (refdealt[(orefid) >> 3] & (1 << ((orefid)&0x7))) + +#define CitrefsClearHomeSq() \ + do { \ + LG_memset(homesquare, 0, NUM_REF_OBJECTS / 8); \ + } while (0) +#define CitrefSetHomeSq(orefid) \ + do { \ + homesquare[(orefid) >> 3] |= 1 << ((orefid)&0x7); \ + } while (0) +#define CitrefClearHomeSq(orefid) \ + do { \ + homesquare[(orefid) >> 3] &= ~(1 << ((orefid)&0x7)); \ + } while (0) +#define CitrefCheckHomeSq(orefid) (homesquare[(orefid) >> 3] & (1 << ((orefid)&0x7))) + +#ifdef __OBJSIM_SRC +uchar homesquare[NUM_REF_OBJECTS / 8]; +uchar refdealt[NUM_REF_OBJECTS / 8]; +#else +extern uchar homesquare[NUM_REF_OBJECTS / 8]; +extern uchar refdealt[NUM_REF_OBJECTS / 8]; +#endif diff --git a/engine/src/GameSrc/Headers/render.h b/engine/src/GameSrc/Headers/render.h new file mode 100644 index 0000000..adf91cd --- /dev/null +++ b/engine/src/GameSrc/Headers/render.h @@ -0,0 +1,86 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __RENDER_H +#define __RENDER_H + +/* + * $Source: n:/project/cit/src/inc/RCS/render.h $ + * $Revision: 1.16 $ + * $Author: xemu $ + * $Date: 1994/03/20 21:16:13 $ + * + * + */ + +// Includes + +// C Library Includes + +// System Library Includes + +// Master Game Includes + +// Game Library Includes + +// Game Object Includes + +// Defines +#define TM_SIZE_CNT 3 + +// Prototypes +errtype render_run(void); + +// Globals +extern LGRect *rendrect; + +extern uchar fr_texture; +extern uchar fr_txt_walls; +extern uchar fr_txt_floors; +extern uchar fr_txt_ceilings; +extern uchar fr_lighting, fr_play_lighting, fr_lights_out, fr_normal_lights; +extern int fr_detail_value; +extern int fr_drop[TM_SIZE_CNT]; +extern uchar fr_show_tilecursor; +extern uchar fr_cont_tilecursor; +extern uchar fr_show_all; +extern int fr_qscale_crit, fr_qscale_obj; +extern uchar fr_highlights; +extern int fr_lite_rad1, fr_lite_base1, fr_lite_rad2, fr_lite_base2; +extern int fr_normal_shf; +extern fix fr_lite_slope, fr_lite_yint; +extern int fr_detail_master; +extern int fr_pseudo_spheres; + +#define MAX_CAMERAS_VISIBLE 2 +#define NUM_HACK_CAMERAS 8 +// hack cameras are "custom textures" 7c through 7f +// (so, factoring in type, the low byte is fc to ff +//#define FIRST_CAMERA_TMAP (short)0x80 - NUM_HACK_CAMERAS +#define FIRST_CAMERA_TMAP 0x78 + +errtype init_hack_cameras(void); +errtype shutdown_hack_cameras(void); +errtype do_screen_static(void); +errtype render_hack_cameras(void); +errtype hack_camera_takeover(int hack_cam); +errtype hack_camera_relinquish(void); + +void tile_hit(int mx, int my); + +#endif // __RENDER_H diff --git a/engine/src/GameSrc/Headers/rendfx.h b/engine/src/GameSrc/Headers/rendfx.h new file mode 100644 index 0000000..774ae67 --- /dev/null +++ b/engine/src/GameSrc/Headers/rendfx.h @@ -0,0 +1,36 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include "gamescr.h" + +#define MAX_SHODAN_LEVEL 120 +// a little less than a half-second per level +#define SHODAN_TIME_SHIFT 7 +#define SHODAN_CONQUER_REF REF_IMG_bmSHODANEndgame +#define SHODAN_FULLSCRN_CONQUER_REF REF_IMG_bmSHODANEndgameFull +#define SHODAN_BITMASK_SIZE (320 * 200) +#define SHODAN_INTERVAL (CIT_CYCLE >> 4u) + +#define LOWER_SHODAN_X (full_game_3d) ? 15 : 5 +#define UPPER_SHODAN_X (full_game_3d) ? 305 : 263 +#define LOWER_SHODAN_Y (full_game_3d) ? 15 : 5 +#define UPPER_SHODAN_Y (full_game_3d) ? 185 : 108 + +#define SHODAN_CONQUER_GET(arr, i) (arr[i >> 3u] & (1u << (i & 0x7u))) +#define SHODAN_CONQUER_SET(arr, i) (arr[i >> 3u] |= (1u << (i & 0x7u))) +#define SHODAN_CONQUER_UNSET(arr, i) (arr[i >> 3u] &= ~(1u << (i & 0x7u))) diff --git a/engine/src/GameSrc/Headers/rendtool.h b/engine/src/GameSrc/Headers/rendtool.h new file mode 100644 index 0000000..ba98983 --- /dev/null +++ b/engine/src/GameSrc/Headers/rendtool.h @@ -0,0 +1,53 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __RENDTOOL_H +#define __RENDTOOL_H + +/* + * $Source: n:/project/cit/src/inc/RCS/rendtool.h $ + * $Revision: 1.5 $ + * $Author: dc $ + * $Date: 1994/05/09 06:06:01 $ + * + */ + +#include "frprotox.h" +#include "map.h" + +uchar draw_tmap_p(int ptcnt); + +void fr_show_rate(int color); +void game_fr_startup(void); +void game_fr_shutdown(void); +uchar *get_free_frame_buffer_bits(int size); // to get bitmap bits +void *get_scr_canvas_from_frame_buffer(int x, int y, int wid, int hgt); // to get an actual canvas +void game_fr_reparam(int is_128s, int full_scrn, int show_all); + +void game_redrop_rad(int rad_mod); + +void free_model_vtexts(char model_num); +void load_model_vtexts(char model_num); +void set_global_lighting(short l_lev); + +void rendedit_process_tilemap(FullMap *fmap, LGRect *r, uchar newMap); + +ushort fr_get_at_raw(frc *fr, int x, int y, uchar again, uchar transp); +void change_detail_level(byte new_level); + +#endif // __RENDTOOL_H diff --git a/engine/src/GameSrc/Headers/safeedms.h b/engine/src/GameSrc/Headers/safeedms.h new file mode 100644 index 0000000..2e26d1a --- /dev/null +++ b/engine/src/GameSrc/Headers/safeedms.h @@ -0,0 +1,30 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#define safe_EDMS_get_state(ph, pst) \ + if (ph != -1) \ + EDMS_get_state(ph, pst) +#define safe_EDMS_kill_object(ph) \ + if (ph != -1) \ + EDMS_kill_object(ph) +#define safe_EDMS_ai_control_robot(ph, a, b, c, d, e, f) \ + if (ph != -1) \ + EDMS_ai_control_robot(ph, a, b, c, d, e, f) +#define safe_EDMS_get_pelvic_viewpoint(ph, st) \ + if (ph != -1) \ + EDMS_get_pelvic_viewpoint(ph, st) diff --git a/engine/src/GameSrc/Headers/saveload.h b/engine/src/GameSrc/Headers/saveload.h new file mode 100644 index 0000000..fa542ae --- /dev/null +++ b/engine/src/GameSrc/Headers/saveload.h @@ -0,0 +1,55 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __SAVELOAD_H +#define __SAVELOAD_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/saveload.h $ + * $Revision: 1.9 $ + * $Author: tjs $ + * $Date: 1994/09/20 13:19:55 $ + * + * + */ + +// Includes + +// C Library Includes + +// System Library Includes + +// Master Game Includes + +// Game Library Includes + +// Game Object Includes + +// Defines +#define CFG_LEVEL_VAR "LEVEL" +#define OLD_LEVEL_ID_NUM 540 +#define LEVEL_ID_NUM 100 + +// Prototypes +errtype save_current_map(char *fname, Id id_num, uchar flush_mem, uchar pack); +errtype load_current_map(Id id_num); +uchar go_to_different_level(int targlevel); + +// Globals + +#endif // __SAVELOAD_H diff --git a/engine/src/GameSrc/Headers/schedtyp.h b/engine/src/GameSrc/Headers/schedtyp.h new file mode 100644 index 0000000..582665e --- /dev/null +++ b/engine/src/GameSrc/Headers/schedtyp.h @@ -0,0 +1,47 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __SCHEDTYPE_H +#define __SCHEDTYPE_H + +#include "pqueue.h" + +/* + * $Source: q:/inc/RCS/schedtyp.h $ + * $Revision: 1.2 $ + * $Author: xemu $ + * $Date: 1993/09/02 23:08:32 $ + * + * $Log: schedtyp.h $ + * Revision 1.2 1993/09/02 23:08:32 xemu + * angle me baby + * + * Revision 1.1 1993/08/18 11:52:55 mahk + * Initial revision + * + * + */ + +// Includes + +// This is the only thing defined here. To minimize dependency. +typedef struct _schedule { + PQueue queue; +} Schedule; + +#endif // __SCHEDTYPE_H diff --git a/engine/src/GameSrc/Headers/schedule.h b/engine/src/GameSrc/Headers/schedule.h new file mode 100644 index 0000000..d5dac2b --- /dev/null +++ b/engine/src/GameSrc/Headers/schedule.h @@ -0,0 +1,121 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __SCHEDULE_H +#define __SCHEDULE_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/schedule.h $ + * $Revision: 1.10 $ + * $Author: mahk $ + * $Date: 1994/07/11 14:08:14 $ + * + */ + +// Includes +#include "schedtyp.h" + +/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + DER SCHEDUMIFIERINEN + ----------------------------------------- + The game has a number of schedules, probably one + in the map and another in the player struct. + A schedule is a queue of events. Each event has + an event type, a timestamp, and some satellite data. + When a schedule runs, all events whose timestamp is earlier + than the current time are dispatched to the event handler + for that type. The event handlers are stored in a global + function array indexed on event type. + Each schedule can have a different notion of time; + although typically we will schedule on player_struct.game_time. + +*/ + +// ----------------------------- +// DEFINES +// ------- +#define SCHED_DATASIZ 4 + +typedef struct _sched_event { + ushort timestamp; + ushort type; + char data[SCHED_DATASIZ]; +} SchedEvent; + +typedef void (*SchedHandler)(Schedule *s, SchedEvent *ev); + +/// -------------------------------- +/// TIMESTAMP CONSTRUCTORS + +// Convert a tmd_ticks time to a scheduler timestamp +// When building an event for a schedule that uses +// player_struct.game_time or .real_time, use these +// macros to get scheduler stamps. +#define TICKS2TSTAMP(t) ((ushort)(((t) >> 4) & 0xFFFF)) +#define TSTAMP2TICKS(t) ((t) << 4) + +#define NULL_SCHED_EVENT 0 +#define GRENADE_SCHED_EVENT 1 +#define EXPLOSION_SCHED_EVENT 2 +#define DOOR_SCHED_EVENT 3 +#define TRAP_SCHED_EVENT 4 +#define EXPOSE_SCHED_EVENT 5 +#define FLOOR_SCHED_EVENT 6 +#define CEIL_SCHED_EVENT 7 +#define LIGHT_SCHED_EVENT 8 +#define BARK_SCHED_EVENT 9 +#define EMAIL_SCHED_EVENT 10 + +typedef struct _expose_data { + byte damage; + ubyte type; + ubyte tsecs; + ubyte count; +} SchedExposeData; + +// Prototypes +errtype schedule_init(Schedule *s, int size, uchar grow); +// Initialize a schedule. If grow is true, the schedule +// will realloc memory when it needs space for more events. +// size is the number of events the shedule can hold. + +errtype schedule_free(Schedule *s); +// Free a schedule. + +errtype schedule_event(Schedule *s, SchedEvent *ev); +// Add an event to the specified schedule. + +errtype schedule_run(Schedule *s, ushort time); +// Run the schedule to the specified time, dispatching all events +// which are scheduled for earlier than time. + +errtype schedule_reset(Schedule *s); +// Removes all events from the schedule without dispatching them. + +void run_schedules(void); +// Runs all loaded schedules. + +void reset_schedules(void); + +int compare_events(void *e1, void *e2); + +uchar register_h_event(uchar x, uchar y, uchar floor, char *sem, char *key, uchar no_sfx); + +// Globals + +#endif // __SCHEDULE_H diff --git a/engine/src/GameSrc/Headers/setploop.h b/engine/src/GameSrc/Headers/setploop.h new file mode 100644 index 0000000..3106111 --- /dev/null +++ b/engine/src/GameSrc/Headers/setploop.h @@ -0,0 +1,75 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __SETPLOOP_H +#define __SETPLOOP_H + +/* + * $Source: n:/project/cit/src/inc/RCS/setploop.h $ + * $Revision: 1.4 $ + * $Author: dc $ + * $Date: 1994/06/13 23:24:03 $ + * + * $Log: setploop.h $ + * Revision 1.4 1994/06/13 23:24:03 dc + * doug is a bonehead + * + * Revision 1.3 1993/09/02 23:08:34 xemu + * angle me baby + * + * Revision 1.2 1993/07/27 18:34:53 xemu + * SETUP_ANIM_UPDATE + * + * Revision 1.1 1993/05/14 15:46:41 xemu + * Initial revision + * + * + */ + +// Includes + +// C Library Includes + +// System Library Includes + +// Master Game Includes + +// Game Library Includes + +// Game Object Includes + +// Defines +#define SETUP_ANIM_UPDATE LL_CHG_BASE << 0 + +// loop id's +typedef enum { + SETUP_JOURNEY, + SETUP_DIFFICULTY, + SETUP_CREDITS, + SETUP_CONTINUE +} SetupMode; + +// Prototypes +void setup_loop(void); +void journey_credits_done(void); +errtype journey_credits_func(uchar draw_stuff); + +// Globals +extern SetupMode setup_mode; + +#endif // __SETPLOOP_H diff --git a/engine/src/GameSrc/Headers/setup.h b/engine/src/GameSrc/Headers/setup.h new file mode 100644 index 0000000..0d72e0a --- /dev/null +++ b/engine/src/GameSrc/Headers/setup.h @@ -0,0 +1,93 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __SETUP_H +#define __SETUP_H + +/* + * $Source: n:/project/cit/src/inc/RCS/setup.h $ + * $Revision: 1.11 $ + * $Author: xemu $ + * $Date: 1994/05/03 23:45:22 $ + * + * $Log: setup.h $ + * Revision 1.11 1994/05/03 23:45:22 xemu + * moved location controlling #defines + * + * Revision 1.10 1994/03/14 22:28:20 xemu + * setup_continue + * + * Revision 1.9 1994/02/12 14:34:24 xemu + * credits define + * + * Revision 1.8 1994/01/30 15:35:42 minman + * got rid of animation stuff + * + * Revision 1.7 1994/01/15 03:52:15 minman + * incremented number of cutscenes + * + * Revision 1.6 1993/09/02 23:08:35 xemu + * angle me baby + * + * Revision 1.5 1993/08/12 10:47:31 xemu + * allow playing of different cutscenes + * + * Revision 1.4 1993/07/28 20:44:37 xemu + * new art + * + * Revision 1.3 1993/07/27 18:35:13 xemu + * all sorts of #defines about difficulty and journey layout + * + * Revision 1.2 1993/07/13 04:28:18 minman + * added setup_init + * + * Revision 1.1 1993/05/14 15:49:21 xemu + * Initial revision + * + * + */ + +// Includes + +// Prototypes + +errtype setup_init(void); + +// Do appropriate things upon first entering the setup loop +void setup_start(void); + +// Do appropriate things for leaving the setup loop +void setup_exit(void); + +// Displays the intro screen(s) +errtype setup_intro_draw(); + +// Call this when ready to start a new game. +void go_and_start_the_game_already(void); + +// Call this when opening a saved game. +errtype load_that_thar_game(int which_slot); + +// Show splash screens +void splash_draw(bool show_splash); + +void empty_slate(void); + +// Globals + +#endif // __SETUP_H diff --git a/engine/src/GameSrc/Headers/sfxlist.h b/engine/src/GameSrc/Headers/sfxlist.h new file mode 100644 index 0000000..c3dee5d --- /dev/null +++ b/engine/src/GameSrc/Headers/sfxlist.h @@ -0,0 +1,327 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __SFXLIST_H +#define __SFXLIST_H + +#ifdef DEMO +#define SFX_NONE -1 + +// Doors +#define SFX_DOOR_METAL 3 +#define SFX_DOOR_NORMAL 5 +#define SFX_DOOR_IRIS 6 +#define SFX_DOOR_BULKHEAD 67 +#define SFX_DOOR_GRATING 90 + +// Ambient +#define SFX_BRIDGE_1 -1 +#define SFX_BRIDGE_2 -1 +#define SFX_BRIDGE_3 -1 +#define SFX_BRIDGE_4 -1 +#define SFX_MAINT_1 -1 +#define SFX_MAINT_2 -1 +#define SFX_MAINT_3 -1 +#define SFX_MAINT_4 -1 +#define SFX_GROVE_1 -1 + +// Wacky Objects +#define SFX_REPULSOR -1 +#define SFX_FORCE_BRIDGE 72 +#define SFX_TERRAIN_ELEV_LOOP 15 +#define SFX_SPARKING_CABLE -1 // not done? +#define SFX_SURGERY_MACHINE 102 + +// Combat +#define SFX_GUN_MINIPISTOL 39 +#define SFX_GUN_DARTPISTOL 86 +#define SFX_GUN_MAGNUM 40 +#define SFX_GUN_ASSAULT -1 +#define SFX_GUN_RIOT -1 +#define SFX_GUN_FLECHETTE -1 +#define SFX_GUN_SKORPION -1 +#define SFX_GUN_MAGPULSE 45 +#define SFX_GUN_RAILGUN -1 +#define SFX_GUN_PIPE_HIT_MEAT 4 +#define SFX_GUN_PIPE_HIT_METAL 21 +#define SFX_GUN_PIPE_MISS 24 +#define SFX_GUN_LASEREPEE_HIT -1 +#define SFX_GUN_LASEREPEE_MISS -1 +#define SFX_GUN_PHASER 18 +#define SFX_GUN_BLASTER -1 +#define SFX_GUN_IONBEAM -1 +#define SFX_GUN_STUNGUN 19 +#define SFX_GUN_PLASMA -1 + +#define SFX_PLAYER_HURT 64 +#define SFX_SHIELD_1 -1 +#define SFX_SHIELD_2 -1 +#define SFX_SHIELD_UP -1 +#define SFX_SHIELD_DOWN -1 +#define SFX_METAL_SPANG -1 // robot hit? not in +#define SFX_RADIATION SFX_STATIC + +#define SFX_RELOAD_1 22 +#define SFX_RELOAD_2 23 +#define SFX_GRENADE_ARM 7 +#define SFX_BATTERY_USE 28 + +#define SFX_EXPLOSION_1 44 +#define SFX_RUMBLE -1 +#define SFX_TELEPORT -1 + +#define SFX_MONITOR_EXPLODE 57 +#define SFX_CAMERA_EXPLODE 8 +#define SFX_CPU_EXPLODE 10 +#define SFX_DESTROY_CRATE 37 +#define SFX_DESTROY_BARREL 109 + +// Cspace +#define SFX_PULSER -1 +#define SFX_DRILL -1 +#define SFX_DISC -1 +#define SFX_DATASTORM -1 + +#define SFX_RECALL -1 +#define SFX_TURBO -1 +#define SFX_FAKEID -1 +#define SFX_DECOY -1 + +#define SFX_ENTER_CSPACE 27 +#define SFX_OTTO_SHODAN -1 +#define SFX_CYBER_DAMAGE -1 +#define SFX_CYBERHEAL -1 +#define SFX_CYBERTOGGLE -1 +#define SFX_ICE_DEFENSE -1 + +#define SFX_CYBER_ATTACK_1 -1 +#define SFX_CYBER_ATTACK_2 -1 +#define SFX_CYBER_ATTACK_3 -1 + +// MFD and UI Wackiness +#define SFX_VIDEO_DOWN 33 +#define SFX_MFD_BUTTON 35 +#define SFX_INVENT_BUTTON 79 +#define SFX_INVENT_SELECT 80 +#define SFX_INVENT_ADD 81 +#define SFX_INVENT_WARE 82 +#define SFX_PATCH_USE 83 +#define SFX_ZOOM_BOX 84 +#define SFX_MAP_ZOOM 85 +#define SFX_MFD_KEYPAD 76 +#define SFX_MFD_BUZZ 77 +#define SFX_MFD_SUCCESS 78 +#define SFX_GOGGLE 0 +#define SFX_HUDFROB 1 +#define SFX_STATIC 2 +#define SFX_EMAIL 107 + +// SHODAN +#define SFX_SHODAN_BARK -1 +#define SFX_SHODAN_WEAK -1 +#define SFX_SHODAN_STRONG -1 + +// Other +#define SFX_PANEL_SUCCESS 78 +#define SFX_POWER_OUT 26 +#define SFX_ENERGY_DRAIN -1 +#define SFX_ENERGY_RECHARGE 98 +#define SFX_SURGE -1 +#define SFX_VMAIL -1 +#define SFX_DROP_ITEM -1 + +// 3d World +#define SFX_BUTTON 71 +#define SFX_MECH_BUTTON 36 +#define SFX_BIGBUTTON 61 +#define SFX_NORMAL_LEVER 62 +#define SFX_BIGLEVER 62 +#define SFX_KLAXON -1 // not in + +// Plot +#define SFX_GROVE_JETT -1 + +#else + +#define SFX_NONE -1 + +// Doors +#define SFX_DOOR_METAL 3 +#define SFX_DOOR_NORMAL 5 +#define SFX_DOOR_IRIS 6 +#define SFX_DOOR_BULKHEAD 67 +#define SFX_DOOR_GRATING 90 + +// Ambient +#define SFX_BRIDGE_1 -1 +#define SFX_BRIDGE_2 -1 +#define SFX_BRIDGE_3 -1 +#define SFX_BRIDGE_4 -1 +#define SFX_MAINT_1 9 +#define SFX_MAINT_2 -1 +#define SFX_MAINT_3 -1 +#define SFX_MAINT_4 -1 +#define SFX_GROVE_1 43 + +// Critters +#define SFX_DEATH_1 11 // robot +#define SFX_DEATH_2 49 // bird +#define SFX_DEATH_3 50 // gort +#define SFX_DEATH_4 51 // mutant +#define SFX_DEATH_5 53 // big robot +#define SFX_DEATH_6 54 // small robot +#define SFX_DEATH_7 68 // cyb1 +#define SFX_DEATH_8 69 // cyb2 +#define SFX_DEATH_9 88 // 0 grav +#define SFX_DEATH_10 93 // plant +#define SFX_DEATH_11 101 // virus mutant +#define SFX_ATTACK_1 12 +#define SFX_ATTACK_4 46 +#define SFX_ATTACK_5 48 +#define SFX_ATTACK_6 52 +#define SFX_ATTACK_7 55 +#define SFX_ATTACK_8 63 +#define SFX_ATTACK_9 16 // cyborg drone +#define SFX_NOTICE_1 58 +#define SFX_NOTICE_2 59 // replace with new cyborg sound +#define SFX_NOTICE_3 74 +#define SFX_NOTICE_4 75 +#define SFX_NOTICE_5 100 // virus mutant +#define SFX_NEAR_1 73 +#define SFX_NEAR_2 56 +#define SFX_NEAR_3 47 // gort +#define SFX_NEAR_4 25 // bigcyb near + +// Wacky Objects +#define SFX_REPULSOR -1 +#define SFX_FORCE_BRIDGE 72 +#define SFX_TERRAIN_ELEV_LOOP 15 +#define SFX_SPARKING_CABLE 87 // not done? +#define SFX_SURGERY_MACHINE 102 + +// Combat +#define SFX_GUN_MINIPISTOL 39 +#define SFX_GUN_DARTPISTOL 86 +#define SFX_GUN_MAGNUM 40 +#define SFX_GUN_ASSAULT 17 +#define SFX_GUN_RIOT 41 +#define SFX_GUN_FLECHETTE 38 +#define SFX_GUN_SKORPION 65 +#define SFX_GUN_MAGPULSE 45 +#define SFX_GUN_RAILGUN 29 +#define SFX_GUN_PIPE_HIT_MEAT 4 +#define SFX_GUN_PIPE_HIT_METAL 21 +#define SFX_GUN_PIPE_MISS 24 +#define SFX_GUN_LASEREPEE_HIT 31 +#define SFX_GUN_LASEREPEE_MISS 34 +#define SFX_GUN_PHASER 18 +#define SFX_GUN_BLASTER 94 +#define SFX_GUN_IONBEAM 95 +#define SFX_GUN_STUNGUN 19 +#define SFX_GUN_PLASMA 97 + +#define SFX_PLAYER_HURT 64 +#define SFX_SHIELD_1 32 +#define SFX_SHIELD_2 20 +#define SFX_SHIELD_UP 96 +#define SFX_SHIELD_DOWN 42 +#define SFX_METAL_SPANG 89 // robot hit? not in +#define SFX_RADIATION SFX_STATIC + +#define SFX_RELOAD_1 22 +#define SFX_RELOAD_2 23 +#define SFX_GRENADE_ARM 7 +#define SFX_BATTERY_USE 28 + +#define SFX_EXPLOSION_1 44 +#define SFX_RUMBLE 106 +#define SFX_TELEPORT 103 + +#define SFX_MONITOR_EXPLODE 57 +#define SFX_CAMERA_EXPLODE 8 +#define SFX_CPU_EXPLODE 10 +#define SFX_DESTROY_CRATE 37 +#define SFX_DESTROY_BARREL 109 + +// Cspace +#define SFX_PULSER -1 +#define SFX_DRILL -1 +#define SFX_DISC -1 +#define SFX_DATASTORM -1 + +#define SFX_RECALL -1 +#define SFX_TURBO -1 +#define SFX_FAKEID -1 +#define SFX_DECOY -1 + +#define SFX_ENTER_CSPACE 27 +#define SFX_OTTO_SHODAN 30 +#define SFX_CYBER_DAMAGE -1 +#define SFX_CYBERHEAL -1 +#define SFX_CYBERTOGGLE -1 +#define SFX_ICE_DEFENSE -1 + +#define SFX_CYBER_ATTACK_1 -1 +#define SFX_CYBER_ATTACK_2 -1 +#define SFX_CYBER_ATTACK_3 -1 + +// MFD and UI Wackiness +#define SFX_VIDEO_DOWN 33 +#define SFX_MFD_BUTTON 35 +#define SFX_INVENT_BUTTON 79 +#define SFX_INVENT_SELECT 80 +#define SFX_INVENT_ADD 81 +#define SFX_INVENT_WARE 82 +#define SFX_PATCH_USE 83 +#define SFX_ZOOM_BOX 84 +#define SFX_MAP_ZOOM 85 +#define SFX_MFD_KEYPAD 76 +#define SFX_MFD_BUZZ 77 +#define SFX_MFD_SUCCESS 78 +#define SFX_GOGGLE 0 +#define SFX_HUDFROB 1 +#define SFX_STATIC 2 +#define SFX_EMAIL 107 + +// SHODAN +#define SFX_SHODAN_BARK 30 +#define SFX_SHODAN_WEAK 30 +#define SFX_SHODAN_STRONG 30 + +// Other +#define SFX_PANEL_SUCCESS 78 +#define SFX_POWER_OUT 26 +#define SFX_ENERGY_DRAIN 14 +#define SFX_ENERGY_RECHARGE 98 +#define SFX_SURGE 60 +#define SFX_VMAIL 92 +#define SFX_DROP_ITEM 99 + +// 3d World +#define SFX_BUTTON 71 +#define SFX_MECH_BUTTON 36 +#define SFX_BIGBUTTON 61 +#define SFX_NORMAL_LEVER 62 +#define SFX_BIGLEVER 62 +#define SFX_KLAXON 70 // not in + +// Plot +#define SFX_GROVE_JETT 66 + +#endif +#endif diff --git a/engine/src/GameSrc/Headers/shockolate_version.h b/engine/src/GameSrc/Headers/shockolate_version.h new file mode 100644 index 0000000..24b9987 --- /dev/null +++ b/engine/src/GameSrc/Headers/shockolate_version.h @@ -0,0 +1,27 @@ +/* + +Copyright (C) 2019 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +// This is autgenerated CMake file, don't edit it manually! + +#ifndef SHOCKOLATE_SRC_GAMESRC_HEADERS_SHOCOLATE_VERSION_H_ +#define SHOCKOLATE_SRC_GAMESRC_HEADERS_SHOCOLATE_VERSION_H_ + +#define SHOCKOLATE_VERSION "Shockolate 0.7.8-g" + +#endif // SHOCKOLATE_SRC_GAMESRC_HEADERS_SHOCOLATE_VERSION_H_ diff --git a/engine/src/GameSrc/Headers/shockolate_version.h.in b/engine/src/GameSrc/Headers/shockolate_version.h.in new file mode 100644 index 0000000..d6bf331 --- /dev/null +++ b/engine/src/GameSrc/Headers/shockolate_version.h.in @@ -0,0 +1,27 @@ +/* + +Copyright (C) 2019 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +// This is autgenerated CMake file, don't edit it manually! + +#ifndef SHOCKOLATE_SRC_GAMESRC_HEADERS_SHOCOLATE_VERSION_H_ +#define SHOCKOLATE_SRC_GAMESRC_HEADERS_SHOCOLATE_VERSION_H_ + +#define SHOCKOLATE_VERSION "Shockolate @PROJECT_VERSION@@PROJECT_REVERSION_STRING@" + +#endif // SHOCKOLATE_SRC_GAMESRC_HEADERS_SHOCOLATE_VERSION_H_ diff --git a/engine/src/GameSrc/Headers/shodan.h b/engine/src/GameSrc/Headers/shodan.h new file mode 100644 index 0000000..6e49f0a --- /dev/null +++ b/engine/src/GameSrc/Headers/shodan.h @@ -0,0 +1,66 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include "objects.h" +#include "cybstrng.h" + +#define CREATURE_SHODODMETER + +#ifdef CREATURE_SHODOMETER +#define CYBORG_DRONE_TRIPLE_VALUE 2 +#define WARRIOR_TRIPLE_VALUE 4 +#define ASSASSIN_TRIPLE_VALUE 4 +#define CYBERBABE_TRIPLE_VALUE 8 +#define ELITE_GUARD_TRIPLE_VALUE 20 +#define CORTEX_REAVER_TRIPLE_VALUE 16 +#define MUTANT_BORG_TRIPLE_VALUE 10 +#define SECURITY_BOT1_TRIPLE_VALUE 5 +#define SECURITY_BOT2_TRIPLE_VALUE 22 +#define EXECBOT_TRIPLE_VALUE 6 +#endif +#define CAMERA_TRIPLE_VALUE 5 +#define SMALL_CPU_TRIPLE_VALUE 10 +#define LARGCPU_TRIPLE_VALUE 50 + +#define SHODAN_INTERVAL_SHIFT 6 +#define SHODAN_COLOR 0x4a + +#define FIRST_SHODAN_QV 0x10 +#define SHODAN_QV (FIRST_SHODAN_QV + player_struct.level) +#define MAX_SHODOMETER_LEVEL 13 + +#define SHODAN_BARK_CODE -1 +#define SHODAN_BARK_TIMEOUT 3 +#define DIEGO_BARK_CODE -2 +#define FIRST_SHODAN_BARK 0x666 +#define NUM_SHODAN_BARKS 4 +#define SHODAN_MUG 17 +#define DIEGO_MUG 4 +#define SHODAN_MUG_2 23 +#define FIRST_SHODAN_MUG 31 +#define NUM_SHODAN_MUGS NUM_SHODAN_BARKS + +#define FIRST_SHODAN_ANIM 0x3f +#define NUM_SHODAN_FRAMES 6 + +#define SPECIAL_SHODAN_FAIL_CODE 0xFF +#define SHODAN_FAILURE_STRING REF_STR_SHODANFail + +short compute_shodometer_value(uchar game_stuff); +short increment_shodan_value(ObjID oid, uchar game_stuff); +short decrement_shodan_value(ObjID oid, uchar game_stuff); diff --git a/engine/src/GameSrc/Headers/sideart.h b/engine/src/GameSrc/Headers/sideart.h new file mode 100644 index 0000000..46f52a5 --- /dev/null +++ b/engine/src/GameSrc/Headers/sideart.h @@ -0,0 +1,26 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* This file created by RESTOOL */ + +#ifndef __SIDEART_H +#define __SIDEART_H + +#define RES_SideIconArt 0x550 // (1360) + +#endif diff --git a/engine/src/GameSrc/Headers/sideicon.h b/engine/src/GameSrc/Headers/sideicon.h new file mode 100644 index 0000000..e1462d6 --- /dev/null +++ b/engine/src/GameSrc/Headers/sideicon.h @@ -0,0 +1,91 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __SIDEICON_H +#define __SIDEICON_H + +/* + * $Source: n:/project/cit/src/inc/RCS/sideicon.h $ + * $Revision: 1.7 $ + * $Author: mahk $ + * $Date: 1994/03/04 07:05:11 $ + * + * $Log: sideicon.h $ + * Revision 1.7 1994/03/04 07:05:11 mahk + * Full screen mania. + * + * Revision 1.6 1993/09/15 00:28:47 xemu + * use different LGRegion creation macro + * + * Revision 1.5 1993/09/02 23:08:36 xemu + * angle me baby + * + * Revision 1.4 1993/08/22 17:53:28 spaz + * added all SI_ numbers + * + * Revision 1.3 1993/08/18 20:44:22 spaz + * SI_SIXTH, for infrared button + * + * Revision 1.2 1993/07/26 16:58:53 spaz + * Threw in some #define's for side icon identification + * + * Revision 1.1 1993/07/21 18:02:41 spaz + * Initial revision + * + * + */ + +// Includes + +// Defines + +#define SI_NONE 0xff + +#define SI_FIRST 0 +#define SI_SECOND 1 +#define SI_THIRD 2 +#define SI_FOURTH 3 +#define SI_FIFTH 4 +#define SI_SIXTH 5 +#define SI_SEVENTH 6 +#define SI_EIGHTH 7 +#define SI_NINTH 8 +#define SI_TENTH 9 + +// Typedefs + +// Prototypes + +extern void init_side_icon(ubyte side_icon, int type, int num); +extern void side_icon_expose_all(); +extern void side_icon_expose(ubyte side_icon); +extern void init_all_side_icons(); +void init_side_icon_popups(void); +extern void screen_init_side_icons(LGRegion *root); +errtype side_icon_load_bitmaps(); +errtype side_icon_free_bitmaps(); +void side_icon_language_change(void); + +void zoom_to_side_icon(LGPoint from, int icon); + +// Globals + +#define macro_region_create_with_autodestroy(parent, child, LGRect) \ + region_create(parent, child, LGRect, 0, 0, REG_USER_CONTROLLED | AUTODESTROY_FLAG, NULL, NULL, NULL, NULL) + +#endif // __SIDEICON_H diff --git a/engine/src/GameSrc/Headers/sndcall.h b/engine/src/GameSrc/Headers/sndcall.h new file mode 100644 index 0000000..77838b4 --- /dev/null +++ b/engine/src/GameSrc/Headers/sndcall.h @@ -0,0 +1,25 @@ +/* + +Copyright (C) 2020 Shocolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef SNDCALL_H +#define SNDCALL_H + +void sound_frame_update(void); + +#endif diff --git a/engine/src/GameSrc/Headers/softdef.h b/engine/src/GameSrc/Headers/softdef.h new file mode 100644 index 0000000..9b92695 --- /dev/null +++ b/engine/src/GameSrc/Headers/softdef.h @@ -0,0 +1,51 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#define SOFTWARE_DRILL 0 +#define SOFTWARE_SPEW 1 +#define SOFTWARE_MINE 2 +#define SOFTWARE_DISC 3 +#define SOFTWARE_PULSER 4 +#define SOFTWARE_SCRAMBLER 5 +#define SOFTWARE_VIRUS 6 + +#define SOFTWARE_CSHIELD 0 +#define SOFTWARE_OLD_FAKEID 1 +#define SOFTWARE_ICE 2 + +#define SOFTWARE_TURBO 0 +#define SOFTWARE_FAKEID 1 +#define SOFTWARE_DECOY 2 +#define SOFTWARE_RECALL 3 +#define SOFTWARE_GAMES 4 + +#define SOFTWARE_FILTER 4 +#define SOFTWARE_MONITOR 5 +#define SOFTWARE_IDENTIFY 6 +#define SOFTWARE_TRACE 7 +#define SOFTWARE_TOGGLE 8 + +// ORing combination +#define GAME_PING 0b00000001u +#define GAME_EEL_ZAPPER 0b00000010u +#define GAME_ROAD 0b00000100u +#define GAME_BOTBOUNCE 0b00001000u +#define GAME_15 0b00010000u +#define GAME_TRIPTOE 0b00100000u +#define GAME_GAME6 0b01000000u +#define GAME_WING0 0b10000000u \ No newline at end of file diff --git a/engine/src/GameSrc/Headers/splash.h b/engine/src/GameSrc/Headers/splash.h new file mode 100644 index 0000000..be1485f --- /dev/null +++ b/engine/src/GameSrc/Headers/splash.h @@ -0,0 +1,29 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* This file created by RESTOOL */ + +#ifndef __SPLASH_H +#define __SPLASH_H + +#define RES_splash 0x73a // (1850) +#define REF_IMG_bmOriginSplash 0x73a0000 +#define REF_IMG_bmLGSplash 0x73a0001 +#define REF_IMG_bmSystemShockTitle 0x73a0002 + +#endif diff --git a/engine/src/GameSrc/Headers/splshpal.h b/engine/src/GameSrc/Headers/splshpal.h new file mode 100644 index 0000000..1bb085f --- /dev/null +++ b/engine/src/GameSrc/Headers/splshpal.h @@ -0,0 +1,21 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* This file created by RESTOOL */ + +#define RES_splashPalette 0x71c // (1820) diff --git a/engine/src/GameSrc/Headers/star.h b/engine/src/GameSrc/Headers/star.h new file mode 100644 index 0000000..80fc6b3 --- /dev/null +++ b/engine/src/GameSrc/Headers/star.h @@ -0,0 +1,82 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/star/RCS/star.h $ + * $Revision: 1.1 $ + * $Author: jaemz $ + * $Date: 1994/10/24 23:27:39 $ + * + * Star library header + * + * $Log: star.h $ + * Revision 1.1 1994/10/24 23:27:39 jaemz + * Initial revision + * + */ + +#ifndef __STAR_H +#define __STAR_H + +// small vector structure, all elements are 15 bit +// with top bit of sign. Shift left when unpacking +// to a fix, since sts_vecs are normalized they can +// be this size +typedef struct { + short x, y, z; +} sts_vec; + +extern int std_size; + +// sets global pointers in the star library +// to the number of stars, their positions, their colors +void star_set(int n, sts_vec *vlist, uchar *clist); + +// allocates the necessary space for stars using alloc +// returns neg 1 if problem +int star_alloc(int n); + +// frees star space using free +void star_free(void); + +// stuffs random vectors and colors into the set areas +// randomly assigning a color range to them +void star_rand(uchar col, uchar range); + +// render a starry polygon +void star_poly(int n, g3s_phandle *vp); + +// render a starry polygon +void star_empty(int n, g3s_phandle *vp); + +// Render to an empty sky, you'll have +// to blacken it for us to color 0 +void star_sky(void); + +// renders star field in the polygon defined by the vertex list +// uses your 3d context, so make sure that's been set +// call this before doing a frame end. You can put it in +// an object frame if you'd like to rotate them and such +void star_render(void); + +// transform star point frugally, only doing z if possible against +// half plane, then projecting if in viewing pyramid +g3s_phandle star_transform_point(g3s_vector *v); +//#pragma aux star_transform_point "*" parm [esi] value [edi] modify [eax ebx ecx edx esi edi]; + +#endif diff --git a/engine/src/GameSrc/Headers/statics.h b/engine/src/GameSrc/Headers/statics.h new file mode 100644 index 0000000..b9c1022 --- /dev/null +++ b/engine/src/GameSrc/Headers/statics.h @@ -0,0 +1,95 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __STATICS_H +#define __STATICS_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/statics.h $ + * $Revision: 1.6 $ + * $Author: xemu $ + * $Date: 1994/11/01 09:19:38 $ + * + * $Log: statics.h $ + * Revision 1.6 1994/11/01 09:19:38 xemu + * share memory for cutscens and 128x128 + * + * Revision 1.5 1994/09/09 18:40:44 xemu + * object pool stuff. + * + * Revision 1.4 1994/09/06 21:57:29 xemu + * increase frame buffer a teeny bit + * + * Revision 1.3 1994/09/05 09:35:58 xemu + * rearrange + * + * Revision 1.2 1994/09/05 06:25:36 xemu + * expanded buffering capability + * + * Revision 1.1 1994/09/01 13:38:14 minman + * Initial revision + * + * + */ + +// the idea is you can take just obj mem, tmap and obj, or both +// + either big buffer or the frame buffer or both + +// still need to have static.h for people to use this stuff... + +// how do we get this aligned right. +// perhaps do this file in asm for real? +// if we did it in asm, and had these point, then we could use +// labels for top and bottom, which would be good... +// sadly, this alphabetizes, since it is so cool + +// put big buffer here? and have a define after it + +#include "textmaps.h" +extern uchar tmap_static_mem[NUM_STATIC_TMAPS * SIZE_STATIC_TMAP]; +#ifdef SVGA_CUTSCENES +extern uchar tmap_big_buffer[NUM_STATIC_TMAPS * SIZE_BIG_TMAP]; +#endif + +#include "objects.h" +#include "objapp.h" +extern Obj objs[NUM_OBJECTS]; +extern ObjRef objRefs[NUM_REF_OBJECTS]; +extern uchar objsDealt[NUM_OBJECTS / 8]; + +// put rest of obj system here, define after it + +#define FRAME_BUFFER_SIZE (320 * 200) + 28 +extern uchar frameBuffer[FRAME_BUFFER_SIZE]; + +#include "mfddims.h" +extern uchar *mfd_canvas_bits; + +#define ALTERNATE_BUFFER frameBuffer +#define ALTERNATE_BUFFER_SIZE ((MFD_VIEW_HGT * MFD_VIEW_WID) + FRAME_BUFFER_SIZE) + +#include "map.h" +#define STATIC_MAP_SIZE 16 << (DEFAULT_XSHF + DEFAULT_YSHF) + +extern uchar static_map[STATIC_MAP_SIZE]; + +#include "objprop.h" +#define OBJ_BITMAP_POOL_SIZE ((NUM_OBJECT * 2) + 230) +extern grs_bitmap obj_bitmap_pool[OBJ_BITMAP_POOL_SIZE]; + +#endif // __STATICS_H diff --git a/engine/src/GameSrc/Headers/status.h b/engine/src/GameSrc/Headers/status.h new file mode 100644 index 0000000..2d9a658 --- /dev/null +++ b/engine/src/GameSrc/Headers/status.h @@ -0,0 +1,229 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __STATUS_H +#define __STATUS_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/status.h $ + * $Revision: 1.24 $ + * $Author: tjs $ + * $Date: 1994/08/10 02:35:07 $ + * + * $Log: status.h $ + * Revision 1.24 1994/08/10 02:35:07 tjs + * death. + * + * Revision 1.23 1994/05/12 00:38:43 dc + * c:\sspro\util\promode 54 bio for diff screen really + * + * Revision 1.22 1994/05/12 00:30:57 dc + * diff screen bio defines + * + * Revision 1.21 1994/02/18 18:17:40 minman + * fixed art offset + * + * Revision 1.20 1994/02/16 08:43:11 mahk + * Changed left margin of biorhythm. Should have no noticeable effect. + * + * Revision 1.19 1993/11/23 02:09:50 xemu + * moved upper right around some + * + * Revision 1.18 1993/11/22 19:55:16 xemu + * minor readjustments + * + * Revision 1.17 1993/09/18 00:15:45 xemu + * made room for eye/lean + * + * Revision 1.16 1993/09/13 23:48:22 xemu + * enhanced biorhythms + * + * Revision 1.15 1993/09/08 19:21:15 minman + * moved down biorhythms + * + * Revision 1.14 1993/09/02 23:08:42 xemu + * angle me baby + * + * Revision 1.13 1993/08/20 16:10:27 spaz + * changed prototype for status_vitals_update() + * + * Revision 1.12 1993/08/12 22:44:31 spaz + * attempted to change coords to benefit mankind + * + * Revision 1.11 1993/08/05 22:33:56 spaz + * Fixed biorhythm tracking within art (thanks, Art!), + * and threw in #define's neccessary for getting dynamic + * status bar graphs in the upper right hand corner + * + * Revision 1.10 1993/06/10 18:36:12 minman + * made little optimizations and commented some + * + * Revision 1.9 1993/06/09 16:53:23 minman + * moved startup and shutdown code into status.c (from screen.c) + * + * Revision 1.8 1993/06/08 23:00:28 minman + * made little optimizations + * + * Revision 1.7 1993/06/08 22:10:34 minman + * status_bio_add now takes a tail length argument + * + * Revision 1.6 1993/06/08 14:42:41 minman + * made modifications to NO_HEIGHT define + * + * Revision 1.5 1993/06/08 01:05:46 minman + * overlapping lines work, and so do deletions of tails + * + * Revision 1.4 1993/06/07 22:45:58 minman + * allowed track to remember color + * + * Revision 1.3 1993/06/07 04:44:31 minman + * biorhythm works + * + * Revision 1.2 1993/05/14 15:50:07 xemu + * draw & update + * + * Revision 1.1 1993/04/30 14:36:25 xemu + * Initial revision + * + * + */ + +// Includes + +// C Library Includes + +// System Library Includes + +// Master Game Includes + +// Game Library Includes + +// Game Object Includes + +// Defines +#define NUM_BIO_TRACKS 8 + +#define GAMESCR_BIO 0 +#define GAMESCR_BIO_X 5 +#define GAMESCR_BIO_Y 1 +//#define GAMESCR_BIO_WIDTH 149 +#define GAMESCR_BIO_WIDTH 131 +#define GAMESCR_BIO_HEIGHT 17 + +#define DIFF_BIO 1 +#define DIFF_BIO_X 6 +#define DIFF_BIO_Y 181 +#define DIFF_BIO_WIDTH 307 +#define DIFF_BIO_HEIGHT 17 + +#define STATUS_CHI_AMP 8 + +#define STATUS_BIO_X curr_bio_x +#define STATUS_BIO_Y curr_bio_y +#define STATUS_BIO_WIDTH curr_bio_w +#define STATUS_BIO_HEIGHT curr_bio_h + +#define STATUS_START_OFFSET 0 +#define STATUS_BIO_Y_DELTA 1 +#define MAX_BIO_LENGTH 307 +#define STATUS_BIO_LENGTH (STATUS_BIO_WIDTH - STATUS_START_OFFSET) +#define STATUS_BIO_TAIL 30 +#define STATUS_BIO_PEAK (STATUS_BIO_HEIGHT - 3) // 3 because of zany art size +#define STATUS_BIO_X_BASE (STATUS_BIO_X + STATUS_START_OFFSET) +#define STATUS_BIO_Y_BASE (STATUS_BIO_Y + STATUS_BIO_HEIGHT - STATUS_BIO_Y_DELTA - 2) + +#define SPIKE_THRESHOLD 4 +#define COLOR_CHANGES 6 +#define COLOR_LENGTH (STATUS_BIO_TAIL / COLOR_CHANGES) + +#define MAX_TAIL_LENGTH 5 +#define NO_HEIGHT 0x1f +#define INVALID_HEIGHT 0xE0 + +#define COLOR_BIO_MASK 0xE0 // Top Three bits signify depth of color +#define HEIGHT_BIO_MASK 0x1f // Bottom Five bits signify height +#define BIT6 0x20 // First bit of the color field. + +#define COLOR_BIT_SHIFT(x) ((x) << 5) + +//#define FIND_OVERLAP(x,y) (((x) - y + STATUS_BIO_LENGTH) % STATUS_BIO_LENGTH) + +#define STATUS_VITALS_X 184 +#define STATUS_VITALS_Y 0 +#define STATUS_VITALS_WIDTH 130 +#define STATUS_VITALS_HEIGHT 17 + +#define STATUS_VITALS_X_BASE (STATUS_VITALS_X + 4) +#define STATUS_VITALS_Y_TOP (STATUS_VITALS_Y + 1) +#define STATUS_VITALS_Y_BOTTOM (STATUS_VITALS_Y + 11) +#define STATUS_VITALS_H 8 +#define STATUS_VITALS_W (STATUS_VITALS_WIDTH - 9) + +#define STATUS_X 4 +#define STATUS_Y 1 +#define STATUS_HEIGHT 20 +#define STATUS_WIDTH 312 + +#define GAMESCR_BIO_REF REF_IMG_bmBiorhythm +#define DIFF_BIO_REF REF_IMG_bmDiffBio +#define STATUS_RESID curr_bio_ref +#define STATUS_RES_VITALSID REF_IMG_bmVitals +#define STATUS_RES_HEALTH_ID REF_IMG_bmVitalInnardsTop +#define STATUS_RES_ENERGY_ID REF_IMG_bmVitalInnardsBottom + +// Special Status Biorhythm variables + +#define NO_SPIKE 0x01 +#define SPIKE_NOISE 0x02 + +// Prototypes + +void gamescr_bio_func(void); +void diff_bio_func(void); + + +// Draw the background for the biorhythm thing + +void status_bio_set(short bio_mode); +void status_bio_init(void); +void status_bio_start(void); +void status_bio_end(void); +void status_bio_update_screenmode(); + +void status_bio_draw(void); +extern void status_vitals_init(); + +// Add a variable to be tracked by the biorhythm monitor. +// Track the NULL pointer to clear out a track slot. +// special - parameters to set characteristics of track (not in use currently - for future use cause we're powerful) +errtype status_bio_add(int *var, int max_value, int update_time, int track_number, int tail_length, uchar special); + +// Draw the biorhythm quasi-persistent thing. Keep track of +// previous draws to do clever incremental strategies. +void status_bio_update(void); + +// Draw in the health / energy diagram. Keep track of +// previous draws to do clever incremental strategies. +errtype status_vitals_update(uchar Full_Redraw); + +// Globals + +extern uchar flatline_heart; +extern uchar chi_amp; + +#endif //__STATUS_H diff --git a/engine/src/GameSrc/Headers/strwrap.h b/engine/src/GameSrc/Headers/strwrap.h new file mode 100644 index 0000000..eabf5c1 --- /dev/null +++ b/engine/src/GameSrc/Headers/strwrap.h @@ -0,0 +1,34 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __STRWRAP_H +#define __STRWRAP_H + +/* + * $Source: n:/project/cit/src/inc/RCS/strwrap.h $ + * $Revision: 1.1 $ + * $Author: mahk $ + * $Date: 1994/06/16 21:40:14 $ + * + */ + +// Includes +#define CHAR_SOFTCR 0x01 // soft carriage return (wrapped text) +#define CHAR_SOFTSP 0x02 // soft space (wrapped text) + +#endif // __STRWRAP_H diff --git a/engine/src/GameSrc/Headers/svgacurs.h b/engine/src/GameSrc/Headers/svgacurs.h new file mode 100644 index 0000000..e72f9b4 --- /dev/null +++ b/engine/src/GameSrc/Headers/svgacurs.h @@ -0,0 +1,23 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#define SVGA_CURSOR_WIDTH 256 +#define SVGA_CURSOR_HEIGHT 256 + +extern uchar svga_cursor_bits[SVGA_CURSOR_WIDTH * SVGA_CURSOR_HEIGHT]; +extern grs_bitmap svga_cursor_bmp; diff --git a/engine/src/GameSrc/Headers/target.h b/engine/src/GameSrc/Headers/target.h new file mode 100644 index 0000000..5777999 --- /dev/null +++ b/engine/src/GameSrc/Headers/target.h @@ -0,0 +1,70 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __TARGET_H +#define __TARGET_H + +/* + * $Source: q:/inc/RCS/target.h $ + * $Revision: 1.2 $ + * $Author: xemu $ + * $Date: 1993/09/02 23:08:43 $ + * + * $Log: target.h $ + * Revision 1.2 1993/09/02 23:08:43 xemu + * angle me baby + * + * Revision 1.1 1993/08/24 12:23:02 spaz + * Initial revision + * + * + */ + +// Includes +#include "mfdint.h" +#include "objects.h" + +// C Library Includes + +// System Library Includes + +// Master Game Includes + +// Game Library Includes + +// Game Object Includes + +// Defines + +// Prototypes +void select_current_target(ObjID id, uchar force_mfd); +void mfd_target_expose(MFD *m, ubyte control); +uchar mfd_target_handler(MFD *m, uiEvent *e); +void toggle_current_target(); + +void mfd_targetware_expose(MFD *mfd, ubyte control); +uchar mfd_targetware_handler(MFD *m, uiEvent *e); + +void right_justify_num(char *num, int dlen); +uchar iter_eligible_targets(ObjSpecID *sid); +void select_closest_target(void); +void toggle_current_target_backwards(void); + +// Globals + +#endif // __TARGET_H diff --git a/engine/src/GameSrc/Headers/textmaps.h b/engine/src/GameSrc/Headers/textmaps.h new file mode 100644 index 0000000..9d25690 --- /dev/null +++ b/engine/src/GameSrc/Headers/textmaps.h @@ -0,0 +1,160 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __TEXTMAPS_H +#define __TEXTMAPS_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/textmaps.h $ + * $Revision: 1.45 $ + * $Author: xemu $ + * $Date: 1994/11/01 09:19:48 $ + * + * + * + */ + +// Includes + +// Defines + +#define NUM_TEXTURE_SIZES 4 + +//#define NUM_LOADED_TEXTURES 64 +#define NUM_LOADED_TEXTURES 54 +#define GAME_TEXTURES 400 +#define NUM_ANIMATING_TEXTURES 0 +#define NUM_STATIC_TEXTURES (GAME_TEXTURES - NUM_ANIMATING_TEXTURES) +#define MAX_LOADED_BITMAPS NUM_LOADED_TEXTURES *NUM_TEXTURE_SIZES + +/* These are for reference, in case the #defines below need to be recomputed. +They are hardwired to avoid needless dependencies. + +#define TEXTURE_128_ID RES_bmTextureMap128 +#define TEXTURE_64_ID RES_bmTextureMap64 +#define TEXTURE_32_ID RES_bmTextureMap32 +#define TEXTURE_16_ID RES_bmTextureMap16 + +#define TEXTURE_SMALL_ID RES_smallTextureMaps +*/ + +#define TEXTURE_128_ID 1000 +#define TEXTURE_64_ID 707 +#define TEXTURE_32_ID 77 +#define TEXTURE_16_ID 76 + +#define MAX_SMALL_TMAPS 128 +#define TEXTURE_SMALL_ID 321 + +#define TEXTURE_128_INDEX 0 +#define TEXTURE_64_INDEX 1 +#define TEXTURE_32_INDEX 2 +#define TEXTURE_16_INDEX 3 + +#define SMALLEST_SIZE_INDEX TEXTURE_128_INDEX +#define LARGEST_SIZE_INDEX TEXTURE_16_INDEX + +#define TEXTPROP_VERSION_NUMBER 9 +#define TEXTPROP_FILENAME "textprop.dat" + +#define NUM_ANIM_TEXTURE_GROUPS 4 + +// for new regieme +#define NUM_STATIC_TMAPS NUM_LOADED_TEXTURES +#define SIZE_STATIC_TMAP ((64 * 64) + (32 * 32) + (16 * 16)) +#define SIZE_BIG_TMAP (128 * 128) + +typedef struct { + // These are indices into the texture_bitmaps array + // Note that for animating textures, the entry and the + // next 7 are all reserved for that texture. + int size_index[NUM_TEXTURE_SIZES]; + ubyte sizes_loaded; +} TextureMap; + +typedef struct { + char family_texture; + char target_texture; + short resilience; + short distance_mod; + char friction_climb; + char friction_walk; + char force_dir; + char anim_group; + char group_pos; +} TextureProp; + +#define ANIMTEXTURE_CYCLE 0x01 + +#define ANIMTEXTURE_REVERSED 0x80 + +typedef struct { + short anim_speed; + short time_remainder; + char current_frame; + char num_frames; + char flags; +} AnimTextureData; + +#define SUPER_MOD(val, modval) ((modval) ? (val % modval) : val) // make sure modval is non-zero before moding + +// new rob +#define ANIMTEXT_BASE(tid) (tid - textprops[(tid)].group_pos) +#define ANIMTEXT_FRAME(tid) \ + SUPER_MOD((animtextures[textprops[(tid)].anim_group].current_frame + textprops[(tid)].group_pos), \ + animtextures[textprops[(tid)].anim_group].num_frames) +#define GET_TEXTURE_INDEX(tid, size) texture_array[ANIMTEXT_BASE(tid) + ANIMTEXT_FRAME(tid)].size_index[(size)] + +// Prototypes +void free_textures(void); +void load_textures(); +errtype load_alternate_textures(); +errtype load_master_texture_properties(); +errtype load_small_texturemaps(void); +errtype bitmap_array_unload(int *num_bitmaps, grs_bitmap *arr[]); +errtype Init_Lighting(void); +errtype unload_master_texture_properties(); +errtype clear_texture_properties(); + + +// returns whether or not a given bitmap is, well, empty. +uchar empty_bitmap(grs_bitmap *bmp); + +#define SHADING_TABLE_FNAME "shadtabl.dat" +#define SHADING_TABLE_AMBER_FNAME "ambrtabl.dat" +#define SHADING_TABLE_BW_FNAME "bwtabl.dat" + +// Globals + +#ifdef __TEXTMAPS_SRC +short loved_textures[NUM_LOADED_TEXTURES]; +TextureProp textprops[NUM_LOADED_TEXTURES]; +AnimTextureData animtextures[NUM_ANIM_TEXTURE_GROUPS]; +uchar shading_table[256 * 16]; +uchar bw_shading_table[256 * 16]; +TextureProp *texture_properties; +#else +extern short loved_textures[NUM_LOADED_TEXTURES]; +extern TextureProp textprops[NUM_LOADED_TEXTURES]; +extern AnimTextureData animtextures[NUM_ANIM_TEXTURE_GROUPS]; +extern uchar shading_table[256 * 16]; +extern uchar bw_shading_table[256 * 16]; +extern TextureProp *texture_properties; +#endif + +#endif // __TEXTMAPS_H diff --git a/engine/src/GameSrc/Headers/texture.h b/engine/src/GameSrc/Headers/texture.h new file mode 100644 index 0000000..fe264d0 --- /dev/null +++ b/engine/src/GameSrc/Headers/texture.h @@ -0,0 +1,31 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* This file created by RESTOOL */ + +#ifndef __TEXTURE_H +#define __TEXTURE_H + +#define RES_customTextureMaps 0x4b // (75) +#define RES_bmTextureMap16 0x4c // (76) +#define RES_bmTextureMap32 0x4d // (77) +#define RES_smallTextureMaps 0x141 // (321) +#define RES_bmTextureMap64 0x2c3 // (707) +#define RES_bmTextureMap28 0x3e8 // (1000) + +#endif diff --git a/engine/src/GameSrc/Headers/tfdirect.h b/engine/src/GameSrc/Headers/tfdirect.h new file mode 100644 index 0000000..a8a8ae1 --- /dev/null +++ b/engine/src/GameSrc/Headers/tfdirect.h @@ -0,0 +1,70 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/inc/RCS/tfdirect.h $ + * $Revision: 1.4 $ + * $Author: dc $ + * $Date: 1994/08/30 05:10:47 $ + */ + +// with tommorrow, track 13, till i can gain (18) + +#ifndef __TFDIRECT_H +#define __TFDIRECT_H + +// some useful constants, i hope +#define fix_0 fix_make(0, 0) +#define fix_1 fix_make(1, 0) +#define fix_root2 fix_make(1, 27146) // 1.414 +#define fix_inv_root2 fix_make(0, 46341) // 0.707 + +// globals +extern fix (*tf_vert_2d)[2]; // 4 elements +extern char tf_norm_hnts[4]; // hint for each if used +extern fix *tf_pt; // 3 elements: first 2 in plane, 3 is distance from plane +extern fix tf_loc_pt[3]; +extern fix tf_raw_pt[3]; // raw world location of object +extern int tf_ph; // current physics handle +extern fix tf_rad; // current physics handle + +// masks and hints +#define FACELET_MASK_N (1 << 0) +#define FACELET_MASK_E (1 << 1) +#define FACELET_MASK_S (1 << 2) +#define FACELET_MASK_W (1 << 3) +#define FACELET_MASK_F (1 << 4) +#define FACELET_MASK_C (1 << 5) +#define FACELET_MASK_I (1 << 6) + +#define NO_NORM_HINT (127) + +// prototypes.... +uchar tf_solve_aligned_face(fix pt[3], fix walls[4][2], int flags, fix *norm); +uchar tf_solve_remetriced_face(fix pt[3], fix walls[4][2], int flags, fix norm[3], fix metric); +uchar tf_solve_cylinder(fix pt[3], fix rad, fix height); +void tf_global_bcd_add(int flg, int param); + +// for now, really this will go soon.... +// tfutil stupidity till physics really deals +#define FCE_NO_PRIM (113) +void facelet_clear(void); +void facelet_add(int which, fix norm[3], fix atten, fix comp, int prim); +void facelet_send(void); + +#endif diff --git a/engine/src/GameSrc/Headers/tickcount.h b/engine/src/GameSrc/Headers/tickcount.h new file mode 100644 index 0000000..db36edc --- /dev/null +++ b/engine/src/GameSrc/Headers/tickcount.h @@ -0,0 +1,30 @@ +/* + +Copyright (C) 2019 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef TICKCOUNT_H_ +#define TICKCOUNT_H_ + +#include +#include + +// http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/Toolbox/Toolbox-80.html +// number of ticks since system start (1 Tick is about 1/60 second) +uint32_t TickCount(void); + +#endif // TICKCOUNT_H_ diff --git a/engine/src/GameSrc/Headers/tilecam.h b/engine/src/GameSrc/Headers/tilecam.h new file mode 100644 index 0000000..4973f05 --- /dev/null +++ b/engine/src/GameSrc/Headers/tilecam.h @@ -0,0 +1,56 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __TILECAM_H +#define __TILECAM_H + +/* + * $Source: n:/project/cit/src/inc/RCS/tilecam.h $ + * $Revision: 1.1 $ + * $Author: mahk $ + * $Date: 1993/09/03 14:30:23 $ + * + * $Log: tilecam.h $ + * Revision 1.1 1993/09/03 14:30:23 mahk + * Initial revision + * + * + */ + +// Includes + +// Defines + +struct _tilecamera; + +typedef void (*camera_setfunc)(struct _tilecamera *tc); +// Updates the state of the camera + +typedef struct _tilecamera { + long bcolor, fcolor; + fix x, y, theta; + uchar show; + camera_setfunc func; + void *data; +} TileCamera; + +// Prototypes + +// Globals + +#endif // __TILECAM_H diff --git a/engine/src/GameSrc/Headers/tilemap.h b/engine/src/GameSrc/Headers/tilemap.h new file mode 100644 index 0000000..50bb79f --- /dev/null +++ b/engine/src/GameSrc/Headers/tilemap.h @@ -0,0 +1,258 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __TILEMAP_H +#define __TILEMAP_H + +/* + * $Source: n:/project/cit/src/inc/RCS/tilemap.h $ + * $Revision: 1.19 $ + * $Author: mahk $ + * $Date: 1993/09/03 14:29:09 $ + * + * $Log: tilemap.h $ + * Revision 1.19 1993/09/03 14:29:09 mahk + * Moved tile cameras out to there own files. + * + * Revision 1.18 1993/09/02 23:08:48 xemu + * angle me baby + * + * Revision 1.17 1993/08/17 18:32:08 mahk + * Changed texture2color + * + * Revision 1.16 1993/06/21 13:23:50 mahk + * Changed HEIGHT2COLOR + * + * Revision 1.15 1993/06/08 22:03:52 mahk + * Changed TEXTURE2COLOR + * ls + * + * Revision 1.14 1993/06/05 07:44:22 mahk + * Added tilemap cameras. + * + * Revision 1.13 1993/06/02 10:26:47 mahk + * Added event dispatching + * + * Revision 1.12 1993/05/26 19:39:45 mahk + * Added TEXTURE2COLOR + * + * Revision 1.11 1993/05/25 19:01:35 mahk + * Modified height color palette + * + * Revision 1.10 1993/05/25 17:41:11 mahk + * added HEIGHT2COLOR + * ls + * + * Revision 1.9 1993/05/21 22:51:54 xemu + * fixed typo + * + * Revision 1.8 1993/05/21 21:31:55 mahk + * new improved highlights. + * + * Revision 1.7 1993/05/21 20:28:45 mahk + * We are now fully fullmap-compliant + * + * Revision 1.6 1993/05/20 11:56:31 mahk + * Implmented highlights + * + * Revision 1.5 1993/05/20 09:01:34 mahk + * Added tilemap resize & move. Implemented a currently somewhat broken version of + * the 3d toggle button. Added tilemap cursor. + * + * Revision 1.4 1993/05/18 14:59:26 mahk + * Changed the prototype for TileMapGetZoom + * + * Revision 1.3 1993/05/17 15:57:25 mahk + * Added a cursor and collected drawfunc and data into a func. + * + * Revision 1.2 1993/05/14 16:51:55 mahk + * Mods to deal with tile editor. + * + * Revision 1.1 1993/05/12 15:47:43 mahk + * Initial revision + * + * + */ + +// Includes +#include "map.h" +#include "colors.h" +#include "tilecam.h" + +// Defines +extern long height_colors[MAP_HEIGHTS]; + +#define MAX_HIGHLIGHTS 8 +#define NEW_HIGHLIGHT 8 + +#define SOLID_TERR_COLOR (BLUE_BASE + 0x8) +#define HEIGHT2COLOR(h) (height_colors[h]) +#define TEXTURE2COLOR(t) ((t)*2 + 32) +#define CAMERA_COLOR (BROWN_BASE + 5) + +struct _tilemap; + +typedef uchar (*highlight_func)(FullMap *map, MapElem *elem, LGPoint square, void *data); + +typedef void (*tile_drawfunc)(struct _tilemap *t, LGPoint square, MapElem *elem, LGPoint pix); + +typedef struct _tiledraw { + tile_drawfunc func; + void *data; +} TileDraw; + +#define NUM_CAMERAS 2 + +typedef struct _tilemap { + LGRegion reg; + MapElem *map; + FullMap *fmap; + ushort zoom; // pixels per tile. + LGPoint topleft; + TileDraw draw; + uchar showcursor; + LGPoint cursor; // Cursor for keyboard input. + uchar highlights[MAP_ROWS][MAP_COLS]; + uchar hilitebits; + TileCamera cameras[NUM_CAMERAS]; + uchar cameras_used[NUM_CAMERAS]; +} TileMap; + +// user-defined event types +#define CURSOR_CHANGE 0 // signalled when the cursor changes +#define ZOOM_CHANGE 1 // signalled when zoom factor changes. + +// Prototypes + +errtype TileMapInit(TileMap *t, LGRegion *parent, LGRect *boundingrect, int z, FullMap *fmap, ushort zoompix, + LGPoint topleft, TileDraw draw); +// Initialize a tilemap. +// Cursor defaults to on and in the top left. +// camera defaults to off. + +void TileMapSetDefault(TileMap *t); +// Set the default tilemap, which will be used in place of NULL +// in any of the following tilemap operations. + +errtype TileMapSetTopLeft(TileMap *t, LGPoint topleft); +// Changes the top left displayed square of the tilemap, +// redisplaying the tilemap. + +errtype TileMapSetMap(TileMap *t, FullMap *fmap); +// Changes the map which is displayed by the tilemap, redisplaying +// the tilemap. + +errtype TileMapSetDraw(TileMap *t, TileDraw draw); +// Changes the function used to display mapsquares, +// and redisplays the tilemap. + +errtype TileMapGetDraw(TileMap *t, TileDraw *draw); +// Gets the function used to display mapsquares, and its data + +errtype TileMapSetHighlight(TileMap *t, LGPoint square, int hilitenum, uchar on); +// Sets the highlighted-ness of the specified square to the value of "on" for +// highlight number hilitenum. (there are MAX_HIGHLIGHTS possible hilitenums) + +errtype TileMapGetHighlight(TileMap *t, LGPoint square, int hilitenum, bool *on); +// Gets the highlighted-ness of the specified square for +// highlight number hilitenum. (there are MAX_HIGHLIGHTS possible hilitenums) + +errtype TileMapHighlight(TileMap *t, int hilitenum, highlight_func func, void *data); +// Sets the value of hilitenum for each square to the value returned by +// the specified function when applied to that square. If func is NULL, +// clears hilitenum. + +errtype TileMapClearHighlights(TileMap *t); +// Clears ALL highlights for tilemap t. + +errtype TileMapFindHighlightNum(TileMap *tm, int *num); +// finds and allocates a free highlight number. + +errtype TileMapSetZoom(TileMap *t, ushort zoom); +// Changes the zoom factor for t, redisplaying it. + +errtype TileMapGetZoom(TileMap *t, ushort *zoom); +// Gets the zoom facter of t. + +uchar TileMapSquare2Pixel(TileMap *t, LGPoint in, LGPoint *out); +// if the square "in" is visible, sets *out to the upper left +// corner of it in screen coordinates, and returns true. +// otherwise, returns false. + +uchar TileMapPixel2Square(TileMap *t, LGPoint in, LGPoint *out); +// If the LGPoint "in" is contained in a square of tilemap t, +// set *out to that square in map coordinates, and return true. +// Otherwise, return false. + +uchar TileMapRedrawPixels(TileMap *t, LGRect *r); +// If any part of r intersects with t in screen coordinates, +// return true and redraw that intersection, otherwise +// return false. the NULL rectangle represents all pixels. + +uchar TileMapRedrawSquares(TileMap *t, LGRect *r); +// If any of the squares in r are visible in t, redraw those +// squares and return true. otherwise, return false. +// The NULL rectangle represents all squares. + +uchar TileMapRedrawSquare(TileMap *t, LGPoint sq); +// Redraws a single square of t, returns whether that +// square is visible, and thus was actually redrawn. + +errtype TileMapSetCursor(TileMap *t, LGPoint p); +// Puts the tilemap cursor at LGPoint p. + +errtype TileMapGetCursor(TileMap *t, LGPoint *p); +// Gets the tilemap cursor. + +errtype TileMapCursorOnOff(TileMap *t, uchar onoff); +// Turns the cursor display on or off. + +errtype TileMapAddCamera(TileMap *t, TileCamera *tc, uint *id); +// Adds a new camera to the tilemap. id will have the id number of the camera + +errtype TileMapRemoveCamera(TileMap *t, uint id); +// Removes the camera with the specified id; + +errtype TileMapGetCamera(TileMap *t, TileCamera *tc, uint id); +// Fills in tc with the current data on the camera with the specified id. + +errtype TileMapSetCamera(TileMap *t, TileCamera *tc, uint id); +// Sets the data of the camera with the specified id to the contents of tc. + +errtype TileMapUpdateCameras(TileMap *t); +// redraws all active cameras in t. + +errtype TileMapResize(TileMap *tm, LGPoint newdims); +// resizes the tilemap to the dimensions specified by newdims. +// redisplaying the tilemap. + +errtype TileMapMove(TileMap *tm, LGPoint newloc, int z); +// Moves a tilemap to new coordinates relative to parent, +// redisplaying the tilemap. + +errtype TileMapInstallHandler(TileMap *tm, ulong evmask, uiHandlerProc proc, void *data, int *id); +// Installs an event handler on the tilemap's LGRegion. + +errtype TileMapRemoveHandler(TileMap *tm, int id); +// removes a previously-installed event handler. + +// Globals + +extern TileMap *TheTileMap; + +#endif // __TILEMAP_H diff --git a/engine/src/GameSrc/Headers/tilename.h b/engine/src/GameSrc/Headers/tilename.h new file mode 100644 index 0000000..482e711 --- /dev/null +++ b/engine/src/GameSrc/Headers/tilename.h @@ -0,0 +1,88 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * tilename.h + * + * $Source: n:/project/cit/src/inc/RCS/tilename.h $ + * $Revision: 1.1 $ + * $Author: dc $ + * $Date: 1994/01/02 17:17:28 $ + * + * Citadel + * list of all tile defines, ordered and such + * + * $Log: tilename.h $ + * Revision 1.1 1994/01/02 17:17:28 dc + * Initial revision + * + */ + +#define TILE_SOLID 0 +#define TILE_OPEN 1 +#define TILE_SOLID_NW 2 +#define TILE_SOLID_NE 3 +#define TILE_SOLID_SE 4 +#define TILE_SOLID_SW 5 +#define TILE_SLOPEUP_N 6 +#define TILE_SLOPEUP_E 7 +#define TILE_SLOPEUP_S 8 +#define TILE_SLOPEUP_W 9 +#define TILE_SLOPECC_NW 10 +#define TILE_SLOPECC_NE 11 +#define TILE_SLOPECC_SE 12 +#define TILE_SLOPECC_SW 13 +#define TILE_SLOPECV_NW 14 +#define TILE_SLOPECV_NE 15 +#define TILE_SLOPECV_SE 16 +#define TILE_SLOPECV_SW 17 +#define TILE_DSPLIT_NW 18 +#define TILE_DSPLIT_NE 19 +#define TILE_DSPLIT_SW 20 +#define TILE_DSPLIT_SE 21 +#define TILE_OCT_NS 22 +#define TILE_OCT_EW 23 +#define TILE_TRI_NS 24 +#define TILE_TRI_EW 25 +#define TILE_1Q_NW2E 26 +#define TILE_1Q_NW2S 27 +#define TILE_1Q_SW2E 28 +#define TILE_1Q_SW2N 29 +#define TILE_1Q_NE2W 30 +#define TILE_1Q_NE2S 31 +#define TILE_1Q_SE2W 32 +#define TILE_1Q_SE2N 33 +#define TILE_3Q_NW2E 34 +#define TILE_3Q_NW2S 35 +#define TILE_3Q_SW2E 36 +#define TILE_3Q_SW2N 37 +#define TILE_3Q_NE2W 38 +#define TILE_3Q_NE2S 39 +#define TILE_3Q_SE2W 40 +#define TILE_3Q_SE2N 41 +#define TILE_VSPLIT 42 +#define TILE_HALVED_EWN 43 +#define TILE_HALVED_NSE 44 +#define TILE_HALVED_EWS 45 +#define TILE_HALVED_NSW 46 +#define TILE_SLIMWALL_N 47 +#define TILE_SLIMWALL_E 48 +#define TILE_SLIMWALL_S 49 +#define TILE_SLIMWALL_W 50 + +#define TILE_TYPES 64 // (TILE_SLOPEUP_W+1) diff --git a/engine/src/GameSrc/Headers/tools.h b/engine/src/GameSrc/Headers/tools.h new file mode 100644 index 0000000..539b6e0 --- /dev/null +++ b/engine/src/GameSrc/Headers/tools.h @@ -0,0 +1,141 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __TOOLS_H +#define __TOOLS_H + +#include "lg_types.h" +#include "error.h" +#include "res.h" +#include "fix.h" +#include "region.h" +#include "2d.h" + +/* + * $Source: r:/prj/cit/src/inc/RCS/tools.h $ + * $Revision: 1.27 $ + * $Author: xemu $ + * $Date: 1994/10/30 06:07:38 $ + * + */ + +// Includes + +// C Library Includes + +// System Library Includes + +// Defines + +#define STORE_CLIP(a, b, c, d) \ + (a) = gr_get_clip_l(); \ + (b) = gr_get_clip_t(); \ + (c) = gr_get_clip_r(); \ + (d) = gr_get_clip_b() + +#define RESTORE_CLIP(a, b, c, d) gr_set_cliprect((a), (b), (c), (d)) + +// Prototypes + +// Draw a resouce bitmap at the x,y coordinates, without doing any pallet or +// mouse tricks. +errtype draw_raw_resource_bm(Ref id, int x, int y); + +// same thing, down to error handling. FIXME do we need both of these? (Or +// indeed either.) +errtype draw_raw_res_bm_temp(Ref id, int x, int y); + +// Draw a resource bitmap at the x,y coordinates, loading the pallet (if available) +// and doing appropriate mouse tricks. +void draw_hires_resource_bm(Ref id, int x, int y); +void draw_hires_halfsize_bm(Ref id, int x, int y); +errtype draw_res_bm(Ref id, int x, int y); +errtype draw_res_bm_core(Ref id, int x, int y, uchar scale); +errtype draw_full_res_bm(Ref id, int x, int y, uchar fade_in); + +// Return the width or height of a resource bitmap. +int res_bm_width(Ref id); +int res_bm_height(Ref id); + +// Draw a Text string to the screen, given a resource font pointer +#define res_draw_text(font, text, x, y) res_draw_text_shadowed(font, text, x, y, FALSE) +errtype res_draw_text_shadowed(Id id, char *text, int x, int y, uchar shadow); + +// Like res_draw_text, but takes a string number instead. +errtype res_draw_string(Id font, int strid, int x, int y); + +// hmmm, why dont these work, eh +// note the void's so we dont need LGRect.h in here, neat huh? +void Rect_gr_box(LGRect *rv); +void Rect_gr_rect(LGRect *rv); + +// Dump the current screen out to a .GIF in the GEN directory +uchar gifdump_func(short keycode, ulong context, void *data); + +// Spit up a box containing a message. +errtype message_box(char *box_text); + +// Writes a message to the info LGRegion +errtype string_message_info(int strnum); +errtype message_info(const char *info_text); +errtype message_clear_check(); + +// Spit up a box asking for confirmation. Returns true or false, accordingly. +uchar confirm_box(char *box_text); + +// From the short-lived util.c +// ¥¥¥FILE *fopen_gen(char *fname, const char *how); +int open_gen(char *fname, int access1, int access2); +char *next_number_fname(char *fname); +//¥¥¥Êchar *next_number_dpath_fname(Datapath *dpath, char *fname); + +// Execute a tight loop, doing appropriate music/palette things +errtype tight_loop(uchar check_input); + +// set / unset "wait" cursor +errtype begin_wait(); +errtype end_wait(); + +// search/replace characters in string +void string_replace_char(char *s, char from, char to); + +fixang point_in_view_arc(fix target_x, fix target_y, fix looker_x, fix looker_y, fixang look_facing, fixang *real_dir); + +// our very own strtoupper! +void strtoupper(char *text); + +// KLC - moved here from WRAPPER.H. +void gamma_dealfunc(ushort gamma_qvar); + +// KLC - added +void second_format(int sec_remain, char *s); + +int hyphenated_wrap_text(char *ps, char *out, short width); + +int str_to_hex(char val); +void strip_newlines(char *buf); + +void text_button(char *text, int xc, int yc, int col, int shad, int w, int h); + +void zoom_rect(LGRect *start, LGRect *end); + +void ZoomDrawProc(int erase); + +// Globals + +#endif // __TOOLS_H diff --git a/engine/src/GameSrc/Headers/tpolys.h b/engine/src/GameSrc/Headers/tpolys.h new file mode 100644 index 0000000..91136aa --- /dev/null +++ b/engine/src/GameSrc/Headers/tpolys.h @@ -0,0 +1,51 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Since anim is taking top 6 bits of the bitmap_3d, we are +// left with the bottom 10. + +#define TPOLY_TYPE_ALT_TMAP 0u +#define TPOLY_TYPE_CUSTOM_MAT 1u +#define TPOLY_TYPE_TEXT_BITMAP 2u +#define TPOLY_TYPE_SCROLL_TEXT 3u + +// Note: we may need to steal yet another bit for accessing anims +// 1 bit of scale +// 2 bits of type -- 0 = "screen" anim 1= custom texture material 2 = screen text 3 = scrolling screen +// 7 bits of index +#define TPOLY_INDEX_BITS 7u +#define TPOLY_INDEX_MASK 0x007Fu +#define TPOLY_TYPE_BITS 2u +#define TPOLY_TYPE_MASK 0x0180u +#define TPOLY_SCALE_BITS 2u +#define TPOLY_SCALE_SHIFT 1u +#define TPOLY_SCALE_MASK 0x0600u +#define TPOLY_STYLE_MASK 0x0800u +#define TPOLY_STYLE_BITS 1u + +#define RANDOM_TEXT_MAGIC_COOKIE 0x7F + +// "texture" 0x77 is algorithmic static, generated each frame +// "texture" 0x76 is like 0x77, but has a change of turning to a SHODAN sometimes when you are near it + +#define REGULAR_STATIC_MAGIC_COOKIE 0x77u +#define SHODAN_STATIC_MAGIC_COOKIE 0x76u + +// automap is "textures" 0x70 through 0x76 +#define NUM_AUTOMAP_MAGIC_COOKIES 6 +#define FIRST_AUTOMAP_MAGIC_COOKIE 0x70 diff --git a/engine/src/GameSrc/Headers/treasure.h b/engine/src/GameSrc/Headers/treasure.h new file mode 100644 index 0000000..1247412 --- /dev/null +++ b/engine/src/GameSrc/Headers/treasure.h @@ -0,0 +1,26 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +// You got nothing. Zero. +#define NOTHING_TRIPLE 0xFFFFFFFF +#define NUM_TREASURE_TYPES 15 +#define NUM_TREASURE_SLOTS 7 +#define NUM_TREASURE_ENTRIES 2 + +extern int treasure_table[NUM_TREASURE_TYPES][NUM_TREASURE_SLOTS][NUM_TREASURE_ENTRIES]; diff --git a/engine/src/GameSrc/Headers/trigger.h b/engine/src/GameSrc/Headers/trigger.h new file mode 100644 index 0000000..cc552bd --- /dev/null +++ b/engine/src/GameSrc/Headers/trigger.h @@ -0,0 +1,134 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __TRIGGER_H +#define __TRIGGER_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/trigger.h $ + * $Revision: 1.28 $ + * $Author: xemu $ + * $Date: 1994/11/21 06:19:41 $ + * + * + */ + +// Includes +#include "objects.h" + +// Defines +#define ENTRY_TRIGGER_TYPE 0 +#define NULL_TRIGGER_TYPE 1 +#define FLOOR_TRIGGER_TYPE 2 +#define PLAYER_DEATH_TRIGGER_TYPE 3 +#define DEATHWATCH_TRIGGER_TYPE 4 +#define AREA_ENTRY_TRIGGER_TYPE 5 +#define AREA_CONTINUOUS_TRIGGER_TYPE 6 + +// Typedefs +typedef struct { + ushort timestamp; + ushort type; + ObjID target_id; + ObjID source_id; +} TrapSchedEvent; + +#define HEIGHT_STEP_TIME 3 +#define HEIGHT_TIME_UNIT 10 + +#define NUM_HEIGHT_SEMAPHORS 32 +#define MAX_HSEM_KEY 63 + +typedef struct { + ushort timestamp; + ushort type; + char semaphor; + char key; + char steps_remaining; + char sfx_code; +} HeightSchedEvent; + +// sfx_codes -- +// 0x1 for no terrain sound + +typedef struct { + uchar x; + uchar y; + union { + struct { + uchar floor : 1; + uchar key : 7; + }; + uchar floor_key; + }; + char inuse; +} height_semaphor; + +typedef struct _EmailSchedEvent { + ushort timestamp; + ushort type; + short datamunge; + short pad; // must be at least as big as a SchedEvent +} EmailSchedEvent; + +// Prototypes + +// Somewhere in the city, an object has been destroyed. Was it a +// destroy trigger? YOU be the judge. +errtype trigger_check_destroyed(ObjID id); + +// Trap/Trigger identified by id might have been set off -- player +// just entered it's square. Deal appropriately. +errtype location_trigger_activate(ObjID id); + +// Trap/Trigger identified by id should actually go off. +// return value is whether or not the trap beneath the trigger +// actually went off. use_message is a pointer to a boolean +// to set if the trap utilizes the message line. +uchar trap_activate(ObjID id, uchar *use_message); + +#define is_trap(id) (objs[(id)].class == CLASS_TRAP) + +// Use these functions to directly access trap-like functions +errtype trap_teleport_func(int targ_x, int targ_y, int targ_z, int targlevel); +errtype trap_scheduler_func(int p1, int p2, int p3, int p4); +errtype trap_lighting_func(uchar floor, int p1, int p2, int p3, int p4); +errtype trap_damage_func(int p1, int p2, int p3, int p4); +errtype trap_create_obj_func(int p1, int p2, int p3, int p4); +errtype trap_questbit_func(int p1, int p2, int p3, int p4); +errtype trap_cutscene_func(int p1, int p2, int p3, int p4); +errtype trap_terrain_func(int p1, int p2, int p3, int p4); +errtype trap_sfx_func(int p1, int p2, int p3, int p4); + +errtype check_deathwatch_triggers(ObjID id, uchar really_dead); +errtype check_entrance_triggers(uchar old_x, uchar old_y, uchar new_x, uchar new_y); +errtype do_shodan_triggers(); + +errtype do_multi_stuff(ObjID id); + +void animate_callback_func(ObjID id, intptr_t user_data); +uchar comparator_check(int comparator, ObjID obj, uchar *special_code); +errtype do_level_entry_triggers(); + +short qdata_get(short qdata); + +// Globals + +extern char *trapname_strings[]; + +#endif // __TRIGGER_H diff --git a/engine/src/GameSrc/Headers/verify.h b/engine/src/GameSrc/Headers/verify.h new file mode 100644 index 0000000..53c6b63 --- /dev/null +++ b/engine/src/GameSrc/Headers/verify.h @@ -0,0 +1,21 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#define SAVELOAD_VERIFICATION_ID 0xF9F +#define OLD_VERIFY_COOKIE_VALID 0x2222 +#define VERIFY_COOKIE_VALID 0x4444 diff --git a/engine/src/GameSrc/Headers/version.h b/engine/src/GameSrc/Headers/version.h new file mode 100644 index 0000000..f4d0a83 --- /dev/null +++ b/engine/src/GameSrc/Headers/version.h @@ -0,0 +1,31 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#define SS_VERSION_NUM "1.6" +#define SS_GREEK_LETTER "F" // CD is in Final! + +// C is for cookie, it's good enough for me. + +#ifdef SVGA_SUPPORT +#define SS_FLAG_LETTER "C" // for CD ship +#else +#define SS_FLAG_LETTER "S" // for ship +#endif // SVGA_SUPPORT + +#define SYSTEM_SHOCK_VERSION "v" SS_GREEK_LETTER SS_VERSION_NUM SS_FLAG_LETTER diff --git a/engine/src/GameSrc/Headers/view360.h b/engine/src/GameSrc/Headers/view360.h new file mode 100644 index 0000000..0ae375f --- /dev/null +++ b/engine/src/GameSrc/Headers/view360.h @@ -0,0 +1,57 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#include "mfdint.h" + +// render contexts for MFD and invent panels. +#define NUM_360_CONTEXTS 3 + +// ------------------------------------ +// WHOOP WHOOP HACK HACK HACK ALERT +// left & right contexts IDs must be the same as MFD ids. + +#define LEFT_CONTEXT 0 +#define RIGHT_CONTEXT 1 +#define MID_CONTEXT 2 + +// Hey, let's expose the rep of the fullscreen visible bits. +// (see fullscrn.h) +#define VISIBLE_BIT(c) (1u << (c)) + +#define MODE_360 0 // All 3 views +#define MODE_270 1 // Just side views +#define MODE_REAR 2 // Just rear view in mfd + +#define REAR_FOV 110 + +extern uchar view360_active_contexts[NUM_360_CONTEXTS]; // which contexts should actually draw +extern uchar view360_context_views[NUM_360_CONTEXTS]; // which view is being shown by a given context + +void view360_init(void); +void view360_shutdown(void); +void mfd_view360_expose(MFD *mfd, ubyte control); +uchar inv_is_360_view(void); +void view360_update_screen_mode(void); +void view360_render(void); +void view360_setup_mode(uchar mode); +void view360_restore_inventory(void); +int view360_fullscrn_draw_callback(void *, void *vbm, int x, int y, int flg); +void view360_turnon(uchar visible, uchar real_start); +void view360_turnoff(uchar visible, uchar real_stop); +bool view360_check(void); diff --git a/engine/src/GameSrc/Headers/viewhelp.h b/engine/src/GameSrc/Headers/viewhelp.h new file mode 100644 index 0000000..204a05c --- /dev/null +++ b/engine/src/GameSrc/Headers/viewhelp.h @@ -0,0 +1,29 @@ +/* + +Copyright (C) 2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef VIEWHELP_H +#define VIEWHELP_H + +#include "mfdint.h" + +errtype mfd_viewhelp_init(MFD_Func *f); +void mfd_viewhelp_expose(MFD *mfd, ubyte control); + + +#endif diff --git a/engine/src/GameSrc/Headers/visible.h b/engine/src/GameSrc/Headers/visible.h new file mode 100644 index 0000000..1d5d90e --- /dev/null +++ b/engine/src/GameSrc/Headers/visible.h @@ -0,0 +1,29 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifdef VIS_FROM_NEWAI +#define VISIBLE_MASS 0 +#define VISIBLE_SIZE fix_make(0, 0x0a00) +#define VISIBLE_SPEED 0 +#define VISIBLE_RANGE fix_make(100, 0) +#else +#define VISIBLE_MASS 0 +#define VISIBLE_SIZE fix_make(0, 0x1000) +#define VISIBLE_SPEED fix_make(10, 0) +#define VISIBLE_RANGE fix_make(20, 0) +#endif diff --git a/engine/src/GameSrc/Headers/vitals.h b/engine/src/GameSrc/Headers/vitals.h new file mode 100644 index 0000000..3ea6fd3 --- /dev/null +++ b/engine/src/GameSrc/Headers/vitals.h @@ -0,0 +1,26 @@ +/* + +Copyright (C) 2020 Shocolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef VITALS_H +#define VITALS_H + +void status_vitals_start(); +void status_vitals_end(); + +#endif diff --git a/engine/src/GameSrc/Headers/vmail.h b/engine/src/GameSrc/Headers/vmail.h new file mode 100644 index 0000000..62c19b7 --- /dev/null +++ b/engine/src/GameSrc/Headers/vmail.h @@ -0,0 +1,71 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __VMAIL_H +#define __VMAIL_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/vmail.h $ + * $Revision: 1.2 $ + * $Author: minman $ + * $Date: 1994/09/05 06:43:57 $ + * + * $Log: vmail.h $ + * Revision 1.2 1994/09/05 06:43:57 minman + * got rid of clear_vmail! + * + * Revision 1.1 1994/01/20 03:00:03 minman + * Initial revision + * + * + */ + +// Includes + +#define SHIELD_VMAIL 0 +#define GROVE_VMAIL 1 +#define BRIDGE_VMAIL 2 + +#define VINTRO_X (SCREEN_VIEW_X + 32) +#define VINTRO_Y (SCREEN_VIEW_Y + 6) + +#define VINTRO_W 200 +#define VINTRO_H 100 + +#define RES_FRAMES_shield 0xA40 +#define RES_FRAMES_grove 0xA42 +#define RES_FRAMES_bridge 0xA42 //0xA44 +#define RES_FRAMES_laser1 0xA46 +#define RES_FRAMES_status 0xA48 +#define RES_FRAMES_explode1 0xA4A + +#define RES_shield 0xA4C +#define RES_grove 0xA4D +#define RES_bridge 0xA4E +#define RES_laser1 0xA4F +#define RES_status 0xA57 +#define RES_explode1 0xA51 + +#define BEFORE_ANIM_BITMAP 0x04 + +#define REF_ANIM_vintro 0xa560000 +#define RES_FRAMES_vintro 0xA4A + +errtype play_vmail(byte vmail_no); + +#endif // __VMAIL_H diff --git a/engine/src/GameSrc/Headers/wares.h b/engine/src/GameSrc/Headers/wares.h new file mode 100644 index 0000000..5673400 --- /dev/null +++ b/engine/src/GameSrc/Headers/wares.h @@ -0,0 +1,198 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __WARES_H +#define __WARES_H + +#include "gamesys.h" + +/* + * $Source: r:/prj/cit/src/inc/RCS/wares.h $ + * $Revision: 1.27 $ + * $Author: xemu $ + * $Date: 1994/08/07 21:00:48 $ + * + * + */ + +// Includes + +// ------ +// Macros +// ------ + +#define WareActive(status) ((status)&WARE_ON) + +// ------- +// Defines +// ------- + +#define WARE_HARD 0 +#define WARE_SOFT_COMBAT 1 +#define WARE_SOFT_DEFENSE 2 +#define WARE_SOFT_MISC 3 +#define NUM_WARE_TYPES 4 + +#define WARE_UPDATE_FREQ 280 // How often all wares updated + +#define WARE_ON 0x01u // Player_struct status flags +#define WARE_DAMAGED 0x02u +#define WARE_FLASH 0x04u + +#define WARE_FLAGS_NONE 0x00 // Here will go various flag bits + +// ---------- +// Ware Types +// ---------- + +// HARDWARE + +#define FIRST_GOGGLE_WARE 0 +#define LAST_GOGGLE_WARE 4 + +#define HARDWARE_GOGGLE_INFRARED 0 +#define HARDWARE_TARGET 1 +#define HARDWARE_360 2 +#define HARDWARE_AIM 3 +#define HARDWARE_HUD 4 +#define HARDWARE_BIOWARE 5 +#define HARDWARE_AUTOMAP 6 +#define HARDWARE_SHIELD 7 +#define HARDWARE_EMAIL 8 +#define HARDWARE_LANTERN 9 +#define HARDWARE_FULLSCREEN 10 +#define HARDWARE_ENVIROSUIT 11 +#define HARDWARE_MOTION 12 +#define HARDWARE_SKATES 13 +#define HARDWARE_STATUS 14 + +// Wacky ware-specific defines +#define LAMP_MASK 0xC0u +#define LAMP_SHF 6u +#define LAMP_SETTING(status) (((status)&LAMP_MASK) >> LAMP_SHF) +#define LAMP_SETTING_SET(status, val) ((status) = (((status) & ~LAMP_MASK) | ((val) << LAMP_SHF))) +#define LAMP_VERSIONS 3 +#define SHIELD_SETTING LAMP_SETTING +#define SHIELD_SETTING_SET LAMP_SETTING_SET +#define SHIELD_VERSIONS 4 + +// ---------- +// Structures +// ---------- + +typedef struct { + ubyte flags; // Do we have a sideicon, etc. + ubyte sideicon; // Which sideicon corresponds + void (*turnon)(uchar visible, uchar real_start); // Function slots for turn on, etc. + void (*effect)(); + void (*turnoff)(uchar visible, uchar real_stop); + bool (*check)(); +} WARE; + +typedef struct { + ushort timestamp; + ushort type; + byte light_value; + ubyte previous; // was the light on before ?? + ubyte filler; +} LightSchedEvent; + +// ---------- +// Prototypes +// ---------- + +void get_ware_pointers(int type, ubyte **player_wares, ubyte **player_status, WARE **wares, int *n); +// Sets several pointers as appropriate to a ware type: the approp. player_struct +// arrays, the approp. global wares property array, and the number of different +// wares for that type + +char *get_ware_name(int waretype, int num, char *buf, int bufsz); +// Fills the buffer with the SHORT name of the ware, specified by +// one of the of the four ware types (hard,combat,def,misc), +// and "subtype" + +int get_ware_triple(int waretype, int num); +// converts a (waretype,num) pair into a triple. + +void use_ware(int waretype, int num); +// Uses a ware from the player's inventory, same format + +int get_player_ware_version(int waretype, int num); +// get_player_ware_version returns the version number +// of a ware in the player's inventory. zero means +// the player doesn't have it. + +void wares_init(); +// sets up the wares system + +void wares_update(); +// called from the game loop + +void hardware_closedown(uchar visible); +void hardware_startup(uchar visible); +void hardware_power_outage(void); +uchar is_passive_hardware(int n); + +bool is_oneshot_misc_software(int n); +int energy_cost(int warenum); + +//----------------------- +// CYBERSPACE ONESHOTS +//----------------------- +void do_turbo_stuff(uchar from_drug); +void turbo_turnon(uchar visible, uchar real_start); +void turbo_turnoff(uchar visible, uchar real_start); +void fakeid_turnon(uchar visible, uchar real_start); +void decoy_turnon(uchar visible, uchar real_start); +void decoy_turnoff(uchar visible, uchar real_stop); +void recall_turnon(uchar visible, uchar real_start); + +// --------------------- +// JUMP JET WARE +// --------------------- +void activate_jumpjets(fix *xcntl, fix *ycntl, fix *zcntl); + +// --------------- +// LANTERN WARE +// --------------- +void lamp_set_vals(void); +void lamp_set_vals_with_offset(byte offset); +void lamp_turnon(uchar visible, uchar real_start); +void lamp_change_setting(byte offset); +void lamp_turnoff(uchar visible, uchar real_stop); +uchar lantern_change_setting_hkey(ushort keycode, uint32_t context, intptr_t data); + +//-------------------------- +// SHIELD WARE +//-------------------------- +void shield_set_absorb(void); +void shield_toggle(uchar visible, uchar real); +uchar shield_change_setting_hkey(ushort keycode, uint32_t context, intptr_t data); + +// ------- +// Globals +// ------- + +// what mode are we using. +extern ubyte motionware_mode; +#define MOTION_INACTIVE 0 +#define MOTION_SKATES 1 +#define MOTION_BOOST 2 +#define MOTION_JUMP 3 + +#endif // __WARES_H diff --git a/engine/src/GameSrc/Headers/weapons.h b/engine/src/GameSrc/Headers/weapons.h new file mode 100644 index 0000000..e0e5858 --- /dev/null +++ b/engine/src/GameSrc/Headers/weapons.h @@ -0,0 +1,134 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __WEAPONS_H +#define __WEAPONS_H + +/* + * $Source: r:/prj/cit/src/inc/RCS/weapons.h $ + * $Revision: 1.40 $ + * $Author: minman $ + * $Date: 1994/09/06 02:46:08 $ + * + */ + +// Includes +#include "objwpn.h" // for GUN_SUBCLASS_BEAM +#include "player.h" + +// Defines + +#define MAX_HEAT 100 +#define OVERHEAT_THRESHOLD 80 +#define MINIMUM_OVERLOAD 30 +#define WARM_THRESHOLD 45 + +#define MIN_ENERGY_USE 10 + +#define OVERLOAD_VALUE(X) (X & 0x80) +#define OVERLOAD_SET(X) (X |= 0x80) +#define OVERLOAD_RESET(X) (X &= ~0x80) +#define BEAM_SETTING_VAL(X) (X & 0x7F) +#define BEAM_SETTING(X, Y) (OVERLOAD_VALUE(X) | (BEAM_SETTING_VAL(Y))) + +// moved WEAPON_COOL_OFF_TIME to weapons.c for easier tweaking +#define MAX_WEAPON_TYPE 6 +#define MAX_WEAPON_SUBTYPE 5 + +// slow projectile flags +#define PROJ_LIGHT_FLAG 0x01 +#define PROJ_PRESERVE_WALL 0x02 +#define PROJ_PRESERVE_HIT 0x04 +#define PROJ_PRESERVE_PROJ_HIT 0x08 + +// extern char ammo_type_letters[]; +#define AMMO_TYPE_LETTER(l) (get_temp_string(REF_STR_AmmoTypeLetters)[l]) +#define is_energy_weapon(wtype) (wtype == GUN_SUBCLASS_BEAM) +#define is_handtohand_weapon(wtype) (wtype == GUN_SUBCLASS_HANDTOHAND) + +#define AMMO_SUBCLASSES num_subclasses[CLASS_AMMO] + +#define AMMOTYPE_SUBCLASS(X) ((X) >= (AMMO_SUBCLASSES << 4) ? AMMO_SUBCLASSES - 1 : (X) >> 4) +#define AMMOTYPE_TYPE(X) ((X)&0xF) + +#define set_shield_rate(X) (player_struct.shield_absorb_rate = (X)) +#define get_shield_rate(X) (player_struct.shield_absorb_rate) + +#ifdef __WEAPONS_SRC +ubyte handart_show = 0; +ubyte handart_remainder = 0; +uchar handart_fire = FALSE; +#else +extern ubyte handart_show; +extern ubyte handart_remainder; +extern uchar handart_fire; +// extern handart_frame_info handart_info[NUM_HANDART_ANIM][HANDART_FRAMES]; +extern ubyte weapon_to_handart[NUM_GUN]; +#endif + +// Prototypes +// Get the name of a weapon, given its type and subtype +char *get_weapon_name(int type, int subtype, char *buf); +char *get_weapon_long_name(int type, int subtype, char *buf); + +// Get the fire rate of a weapon, given its type and subtype +#define weapon_fire_rate(WTYPE, SUBTYPE) (GunProps[CPTRIP(MAKETRIP(CLASS_GUN, WTYPE, SUBTYPE))].fire_rate) + +// Fire the player's current weapon. Pos is the cursor position, +// the routine will translate by itself into the x & y angles of +// direction to fire, +// in units of -100-+100 spanning the view cone. +// pull is true if the trigger was just pulled. +// returns - TRUE if player fired weapon +uchar fire_player_weapon(LGPoint *pos, LGRegion *r, uchar pull); + +// Set the maximum charge on a beam weapon. index is into player_struct.weapons, +// max_charge can't be higher than 100 +void set_beam_weapon_max_charge(ubyte index, ubyte max_charge); + +void get_available_ammo_type(int type, int subtype, int *num_ammo_types, ubyte *bitflag, int *ammo_subclass); +uchar change_ammo_type(ubyte ammo_type); + +// change_weapon() - changes the current selected weapon +void change_selected_weapon(int new_weapon); + +// Called at a constant factor to blow off heat on energy weapons +void cool_off_beam_weapons(); + +// This routine is used to jerk the cursor around, as a result of +// poor accuracy, or ammo recoil +void randomize_cursor_pos(LGPoint *cpos, LGRegion *r, ubyte percentage); + +// drain energy from central reservior, returns how much was actually drained +ubyte drain_energy(ubyte desired_energy); + +// return triple of currently-selected weapon, or -1 for none such. +int current_weapon_trip(void); + +uchar gun_takes_ammo(int guntrip, int ammotrip); + +uchar ready_to_draw_handart(void); + +uchar does_weapon_overload(int type, int subtype); + +void unload_current_weapon(void); + + +// Globals + +#endif // __WEAPONS_H diff --git a/engine/src/GameSrc/Headers/wrapper.h b/engine/src/GameSrc/Headers/wrapper.h new file mode 100644 index 0000000..45efeea --- /dev/null +++ b/engine/src/GameSrc/Headers/wrapper.h @@ -0,0 +1,126 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __WRAPPER_H +#define __WRAPPER_H + +/* + * $Source: n:/project/cit/src/inc/RCS/wrapper.h $ + * $Revision: 1.7 $ + * $Author: dc $ + * $Date: 1994/05/12 00:30:52 $ + * + * $Log: wrapper.h $ + * Revision 1.7 1994/05/12 00:30:52 dc + * comment defines + * + * Revision 1.6 1994/05/11 21:31:04 dc + * some externals, if i remember correctly + * + * Revision 1.5 1994/04/07 22:37:53 xemu + * new wrapper paradigm + * + * Revision 1.4 1994/02/25 15:43:58 mahk + * Added stupid terseness questbit. + * + * Revision 1.3 1993/09/02 23:08:56 xemu + * angle me baby + * + * Revision 1.2 1993/07/22 17:31:25 xemu + * some #defines + * + * Revision 1.1 1993/06/14 15:37:04 xemu + * Initial revision + * + * + */ + +// Includes + +// Defines +#define MAIN_PANEL 0 +#define SAVELOAD_PANEL 1 + +// questbit for terse text. +#define TERSENESS_QBIT 0x180 + +// Prototypes +uchar demo_quit_func(ushort keycode, uint32_t context, intptr_t data); + +errtype make_options_cursor(void); + +#ifdef AUDIOLOGS +void recompute_audiolog_level(ushort vol); +#endif +void recompute_digifx_level(ushort vol); +void recompute_music_level(ushort vol); + +void digichan_dealfunc(short val); + +void language_change(uchar lang); + +void screenmode_screen_init(void); + +void wrapper_start(void (*init)(void)); + +// Replaces the inventory panel with a wrapper input paneloid thing, +// which is 2 by width text buttons for the user to click on. When clicked, +// the passed callback is called with the number of the button clicked +// as an argument. +uchar wrapper_options_func(ushort keycode, uint32_t context, intptr_t data); + +errtype wrapper_create_mouse_region(LGRegion *root); + +#define NUM_SAVE_SLOTS 8 +#define SAVE_COMMENT_LEN 32 + +// Globals +extern char save_game_name[]; +extern char comments[NUM_SAVE_SLOTS + 1][SAVE_COMMENT_LEN]; + +#define Poke_SaveName(game_num) \ + { \ + save_game_name[6] = '0' + (game_num >> 3); \ + save_game_name[7] = '0' + (game_num & 7); \ + } + +enum TEMP_STR_ { + REF_STR_Renderer = 0x10000000, + REF_STR_Software, + REF_STR_OpenGL, + + REF_STR_TextFilt = 0x10000010, + REF_STR_TFUnfil, // unfiltered + REF_STR_TFBilin, // bilinear + + REF_STR_MousLook = 0x11000000, + REF_STR_MousNorm, + REF_STR_MousInv, + + REF_STR_Seqer = 0x20000000, + REF_STR_ADLMIDI, + REF_STR_NativeMI, +#ifdef USE_FLUIDSYNTH + REF_STR_FluidSyn, +#endif // USE_FLUIDSYNTH + REF_STR_MidiOut = 0x2fffffff, + + REF_STR_MidiOutX = 0x30000000 // 0x30000000-0x3fffffff are MIDI outputs +}; + +#endif // __WRAPPER_H diff --git a/engine/src/GameSrc/ai.c b/engine/src/GameSrc/ai.c new file mode 100644 index 0000000..85038f0 --- /dev/null +++ b/engine/src/GameSrc/ai.c @@ -0,0 +1,820 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/ai.c $ + * $Revision: 1.167 $ + * $Author: xemu $ + * $Date: 1994/10/18 19:50:51 $ + */ + +#include + +#include "ai.h" +#include "aiflags.h" +#include "objects.h" +#include "objsim.h" +#include "objcrit.h" +#include "objwpn.h" +#include "physics.h" +#include "player.h" +#include "damage.h" +#include "diffq.h" +#include "combat.h" +#include "grenades.h" +#include "musicai.h" +#include "otrip.h" +#include "objprop.h" +#include "objbit.h" +#include "mapflags.h" +#include "tilename.h" +#include "tools.h" +#include "safeedms.h" +#include "treasure.h" +#include "fullscrn.h" +#include "sfxlist.h" + +#include "ice.h" +#include "cyber.h" + +#define AI_EDMS + +// errtype ai_fire_slow_projectile(ObjID src, int proj_triple, ObjLoc src_loc, ObjLoc target_loc, uchar a, int +// duration); errtype ai_throw_grenade(ObjID src, int proj_triple, ObjLoc src_loc, ObjLoc target_loc); + +#define SLOW_PROJECTILE_DURATION 1000 +#define SLOW_PROJECTILE_SPEED fix_make(5, 0) +#define ATTACK_GRENADE_GRAVITY fix_make(0, 0x8000) +#define ATTACK_GRENADE_SPEED fix_make(7, 0) + +// tolerance to completion of an EDMS-driven AI maneuver +#define AI_COMPLETE_TOLERANCE fix_make(0, 0x5fff) + +// Number of frames beyond which, if we haven't seen the player, +// we stop trying to shoot at 'im. +#define UNSEEN_FIRE_THRESHOLD 10 + +fix there_yet; +ObjLoc last_known_loc; + + +#define AI_HEAD_HIT_CHANCE 0x40 +void ai_find_player(ObjID id) { + State st; + extern void state_to_objloc(State * s, ObjLoc * l); + extern void get_phys_state(int ph, State *new_state, ObjID id); + + if (global_fullmap->cyber) + last_known_loc = objs[PLAYER_OBJ].loc; + else { + uchar slow_proj = FALSE; + CritterProp cp = CritterProps[CPNUM(id)]; + // Some of the time they shoot at yer head, the other times at yer body. + // Unless they have a slow projectile, in which case they always shoot at yer head. + if ((cp.attacks[0].slow_proj != 0) || ((cp.alt_perc > 0) && (cp.attacks[1].slow_proj))) + slow_proj = TRUE; + if ((slow_proj) || ((rand() & 0xFF) < AI_HEAD_HIT_CHANCE) || (global_fullmap->cyber)) + get_phys_state(objs[PLAYER_OBJ].info.ph, &st, PLAYER_OBJ); + // EDMS_get_pelvic_viewpoint(objs[PLAYER_OBJ].info.ph, &st); + else + EDMS_get_state(objs[PLAYER_OBJ].info.ph, &st); + state_to_objloc(&st, &last_known_loc); + // Warning(("player loc = %x, %x, %x\n",last_known_loc.x,last_known_loc.y,last_known_loc.z)); + } +} + +errtype set_posture(ObjSpecID osid, ubyte new_pos) { + if (new_pos != get_crit_posture(osid)) { + set_crit_posture(osid, new_pos); + objs[objCritters[osid].id].info.current_frame = 0; + } + return (OK); +} + +errtype set_posture_safe(ObjSpecID osid, ubyte new_pos) { + if ((get_crit_posture(osid) == STANDING_CRITTER_POSTURE) || (get_crit_posture(osid) == MOVING_CRITTER_POSTURE)) + return (set_posture(osid, new_pos)); + return (OK); +} + +errtype set_posture_movesafe(ObjSpecID osid, ubyte new_pos) { + if ((get_crit_posture(osid) != STANDING_CRITTER_POSTURE) && (get_crit_posture(osid) != MOVING_CRITTER_POSTURE)) + return (set_posture(osid, new_pos)); + return (OK); +} + +errtype clear_critter_controls(ObjSpecID osid) { + objCritters[osid].des_heading = 0; + objCritters[osid].des_speed = 0; + objCritters[osid].urgency = 0; + objCritters[osid].sidestep = 0; + return (OK); +} + +// tolerance between "standing" and "walking" anims +// when all your dots is greater than this, you are considered moving +//#define MOVE_TOLERANCE fix_make(0,0x0700) +fix move_tolerance = fix_make(0, 0x0400); + +// copied in newai.c +#define DEFAULT_URGENCY fix_make(0x30, 0) + +errtype apply_EDMS_controls(ObjSpecID osid) { +#ifdef AI_EDMS + State crit_state; + ObjCritter *pcrit = &objCritters[osid]; + ObjID id = pcrit->id; + fix curr_ai_dist = FIX_UNIT; + fix use_speed, use_step; + + // Apply controls from last frame, see how close we came + if (CHECK_OBJ_PH(id)) { + safe_EDMS_get_state(objs[id].info.ph, &crit_state); + + // Hmm, I wonder whether there is a faster way to do this. + if (fix_abs(crit_state.X_dot) + fix_abs(crit_state.Y_dot) + fix_abs(crit_state.Z_dot) > move_tolerance) { + if ((get_crit_posture(osid) == STANDING_CRITTER_POSTURE) || + (get_crit_posture(osid) == ATTACK_REST_CRITTER_POSTURE)) { + set_posture_safe(osid, MOVING_CRITTER_POSTURE); + } + } else { + set_posture_safe(osid, STANDING_CRITTER_POSTURE); + } + + if ((pcrit->urgency == 0) && !(pcrit->flags & AI_FLAG_TRANQ)) + pcrit->urgency = DEFAULT_URGENCY; + + if (pcrit->orders == AI_ORDERS_NOMOVE) { + use_speed = 0; + use_step = 0; + } else { + use_speed = pcrit->des_speed; + use_step = pcrit->sidestep; + } + + safe_EDMS_ai_control_robot(objs[id].info.ph, pcrit->des_heading, use_speed, pcrit->sidestep, pcrit->urgency, + &there_yet, curr_ai_dist); + } +#endif + return (OK); +} + +#define SHODAN_AVATAR_HOSAGE_DISTANCE 0x4 +#define AGITATED_ICE_DIST 100 + +// percentage of hits to take in one attack in order to play SFX +#define HURT_SOUND_THRESHOLD 25 + +// All critters within ANGER_RADIUS of a critter that takes damage +// become angry unless their loner bit is set. +#define ANGER_RADIUS 3 + +void ai_critter_seen(void) { + extern int mai_combat_length; + mlimbs_combat = player_struct.game_time + mai_combat_length; +} + +errtype ai_critter_hit(ObjSpecID osid, short damage, uchar tranq, uchar stun) { + char i, j; + short r; + char x1, x2, y1, y2; + ObjRefID oref; + ObjID oid; + char diff; + + // become unconfused & untranqed + // woo hoo, watch me pull odds out of my butt + if ((objCritters[osid].flags & (AI_FLAG_CONFUSED | AI_FLAG_TRANQ)) && ((rand() & 0xF) <= 6)) + objCritters[osid].flags &= ~(AI_FLAG_CONFUSED | AI_FLAG_TRANQ); + + // If we are truly asleep, let the enemy pound on us... + if (ai_critter_sleeping(osid)) + return (OK); + + oid = objCritters[osid].id; + switch (ID2TRIP(oid)) { + case ROBOBABE_TRIPLE: { + extern uchar *shodan_bitmask; + extern void shodan_phase_in(uchar * bitmask, short x, short y, short w, short h, short num, uchar dir); + shodan_phase_in(shodan_bitmask, 0, 0, FULL_VIEW_WIDTH, FULL_VIEW_HEIGHT, damage << 4, FALSE); + } break; + } + + // Play a sound effect if hurt enough + if ((damage * 100 / ObjProps[OPNUM(objCritters[osid].id)].hit_points) > HURT_SOUND_THRESHOLD) + play_digi_fx_obj(CritterProps[CPNUM(objCritters[osid].id)].hurt_sound, 1, objCritters[osid].id); + + // Depending on how disruptable we are, we might be disrupted + // that means we have to restart our attack timer + r = rand() % 255; + // Spew(DSRC_AI_Combat, ("r = %d, dp = %d + %d = + // %d\n",r,CritterProps[CPNUM(objCritters[osid].id)].disrupt_perc,damage >> 2, + // CritterProps[CPNUM(objCritters[osid].id)].disrupt_perc + (damage >> 2))); + if (r < CritterProps[CPNUM(objCritters[osid].id)].disrupt_perc + (damage >> (5 - QUESTVAR_GET(COMBAT_DIFF_QVAR)))) { + set_posture(osid, DISRUPT_CRITTER_POSTURE); + objCritters[osid].attack_count = + player_struct.game_time + CritterProps[CPNUM(objCritters[osid].id)].attacks[0].speed; + } + + // And boy, are we ticked. + objCritters[osid].mood = AI_MOOD_HOSTILE; + + // We get to butt in line, and we know exactly where the player is... + ai_find_player(oid); + objCritters[osid].wait_frames = 0; + + if (tranq) { + objCritters[osid].flags |= AI_FLAG_TRANQ; + objCritters[osid].des_speed = 0; + objCritters[osid].urgency = 0; + objCritters[osid].sidestep = 0; + apply_gravity_to_one_object(oid, STANDARD_GRAVITY); + if (objs[oid].info.ph != -1) { + EDMS_control_robot(objs[oid].info.ph, fix_make(0, 0x0010), 0, 0); + } + if (get_crit_posture(osid) >= ATTACKING_CRITTER_POSTURE) { + set_crit_posture(osid, STANDING_CRITTER_POSTURE); + objs[oid].info.current_frame = 0; + } + } + if (stun) + objCritters[osid].flags |= AI_FLAG_CONFUSED; + + diff = QUESTVAR_GET((global_fullmap->cyber) ? CYBER_DIFF_QVAR : COMBAT_DIFF_QVAR); + if (diff > 0) { + // Hey, we'll get our buddies in on the matter too... + // We anger all critters within ANGER_RADIUS who are the same + // loner-ness as ourselves. + // The radius someday might want to be objprop dependant rather + // than a constant. + // Oh, and we also don't have any effect on critters with a mood of ISOLATION + + oid = objCritters[osid].id; + x1 = OBJ_LOC_BIN_X(objs[oid].loc) - ANGER_RADIUS; + x2 = x1 + (2 * ANGER_RADIUS); + y1 = OBJ_LOC_BIN_Y(objs[oid].loc) - ANGER_RADIUS; + y2 = y1 + (2 * ANGER_RADIUS); + + for (j = y1; j <= y2; j++) { + for (i = x1; i <= x2; i++) { + oref = me_objref(MAP_GET_XY(i, j)); + while (oref != OBJ_REF_NULL) { + oid = objRefs[oref].obj; + if ((objs[oid].ref = oref) && (objs[oid].obclass == CLASS_CRITTER)) { + if ((objCritters[objs[oid].specID].mood != AI_MOOD_ISOLATION) && (!ai_critter_sleeping(osid))) { + // Only get upset if loner-ness matches our own. + if ((objs[oid].info.inst_flags & CLASS_INST_FLAG) == + (objs[objCritters[osid].id].info.inst_flags & CLASS_INST_FLAG)) { + objCritters[objs[oid].specID].mood = AI_MOOD_HOSTILE; + } + } + } + oref = objRefs[oref].next; + } + } + } + } + return (OK); +} + +errtype ai_autobomb_explode(ObjID id, ObjSpecID osid); + +errtype ai_critter_die(ObjSpecID osid) { + extern ObjID damage_sound_id; + extern char damage_sound_fx; + ObjID id = objCritters[osid].id; + + if (ID2TRIP(id) == AUTOBOMB_TRIPLE) + ai_autobomb_explode(id, osid); + + set_posture(osid, DEATH_CRITTER_POSTURE); + if (CritterProps[CPNUM(id)].death_sound != 255) { + damage_sound_fx = CritterProps[CPNUM(id)].death_sound; + damage_sound_id = id; + } + // play_digi_fx_obj(CritterProps[CPNUM(id)].death_sound,1,id); + + // If we were flying, we ain't no more! + if (CritterProps[CPNUM(id)].flags & AI_FLAG_FLYING) + apply_gravity_to_one_object(id, STANDARD_GRAVITY); + return (OK); +} + +#define FIRST_CORPSE_TTYPE 11 + +// Each of NUM_TREASURE_TYPES has up to NUM_TREASURE_SLOTS slots with pairs of chances and reward +int treasure_table[NUM_TREASURE_TYPES][NUM_TREASURE_SLOTS][NUM_TREASURE_ENTRIES] = { + {// No Treasure, loser + {100, NOTHING_TRIPLE}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}}, + {// Humanoid (1) + {7, LSD_DRUG_TRIPLE}, + {15, MEDI_DRUG_TRIPLE}, + {13, BEV_CONT_TRIPLE}, + {65, NOTHING_TRIPLE}, + {0, 0}, + {0, 0}, + {0, 0}}, + {// Drone (2) + {14, SPAMMO_TRIPLE}, + {8, TEFAMMO_TRIPLE}, + {17, NNAMMO_TRIPLE}, + {6, MEDI_DRUG_TRIPLE}, + {5, FRAG_G_TRIPLE}, + {50, NOTHING_TRIPLE}, + {0, 0}}, + {// Assassin (3) + {40, SPAMMO_TRIPLE}, + {15, NNAMMO_TRIPLE}, + {8, TEFAMMO_TRIPLE}, + {7, TNAMMO_TRIPLE}, + {30, NOTHING_TRIPLE}, + {0, 0}, + {0, 0}}, + {// Warrior Cyborg (4) + {25, SPAMMO_TRIPLE}, + {25, TEFAMMO_TRIPLE}, + {15, HNAMMO_TRIPLE}, + {5, SPLAMMO_TRIPLE}, + {5, FRAG_G_TRIPLE}, + {25, NOTHING_TRIPLE}, + {0, 0}}, + {// Flier Bots (5) + {30, SPAMMO_TRIPLE}, + {10, NNAMMO_TRIPLE}, + {5, TEFAMMO_TRIPLE}, + {5, HNAMMO_TRIPLE}, + {50, NOTHING_TRIPLE}, + {0, 0}, + {0, 0}}, + {// Security 1 Bots (6) + {20, HNAMMO_TRIPLE}, + {20, HTAMMO_TRIPLE}, + {15, SPLAMMO_TRIPLE}, + {15, MRAMMO_TRIPLE}, + {10, TEFAMMO_TRIPLE}, + {10, HSAMMO_TRIPLE}, + {10, NOTHING_TRIPLE}}, + {// Exec Bots (7) + {25, NOTHING_TRIPLE}, + {25, SPLAMMO_TRIPLE}, + {15, HTAMMO_TRIPLE}, + {10, HNAMMO_TRIPLE}, + {10, MRAMMO_TRIPLE}, + {7, HSAMMO_TRIPLE}, + {8, NOTHING_TRIPLE}}, + {// Cyborg Enforcer (8) + {10, EMP_G_TRIPLE}, + {40, MRAMMO_TRIPLE}, + {13, SLGAMMO_TRIPLE}, + {12, BGAMMO_TRIPLE}, + {8, MEDI_DRUG_TRIPLE}, + {2, AIDKIT_TRIPLE}, + {15, STAMINA_DRUG_TRIPLE}}, + {// Security II Bot (9) + {40, HTAMMO_TRIPLE}, + {40, HSAMMO_TRIPLE}, + {10, MRAMMO_TRIPLE}, + {7, NOTHING_TRIPLE}, + {3, PRAMMO_TRIPLE}, // no not impart prammo in favor of nothing + {0, 0}, + {0, 0}}, + {// Elite Cyborg (10) + {15, RGAMMO_TRIPLE}, + {20, BGAMMO_TRIPLE}, + {20, HSAMMO_TRIPLE}, + {11, MEDI_DRUG_TRIPLE}, + {2, AIDKIT_TRIPLE}, + {27, NOTHING_TRIPLE}, + {5, PRAMMO_TRIPLE}}, // yeah, what he said + {// Standard Corpse (11) + {80, NOTHING_TRIPLE}, + {5, HELMET_TRIPLE}, + {5, BEV_CONT_TRIPLE}, + {5, WRAPPER_TRIPLE}, + {2, LSD_DRUG_TRIPLE}, + {2, PHASER_TRIPLE}, + {1, STAMINA_DRUG_TRIPLE}}, + {// loot-oriented corpse (12) + {10, SPAMMO_TRIPLE}, + {10, STAMINA_DRUG_TRIPLE}, + {5, BATTERY_TRIPLE}, + {11, MEDI_DRUG_TRIPLE}, + {64, NOTHING_TRIPLE}, + {0, 0}, + {0, 0}}, + {// electro-stuff treasure (maint & repair bots) + {5, EPICK_TRIPLE}, + {15, BATTERY_TRIPLE}, + {80, NOTHING_TRIPLE}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}}, + {// serv-bot treasure + {35, BEV_CONT_TRIPLE}, + {5, MEDI_DRUG_TRIPLE}, + {15, BEAKER_CONT_TRIPLE}, + {15, FLASK_CONT_TRIPLE}, + {12, BATTERY_TRIPLE}, + {1, SKULL_TRIPLE}, + {17, NOTHING_TRIPLE}}, +}; + +errtype roll_on_dnd_treasure_tables(int *pcont, char treasure_type) { + char perc; + char count = 0; + uchar give, done = FALSE; + int chance, trip; + + perc = rand() % 100; + while (!done && (count < NUM_TREASURE_SLOTS)) { + give = FALSE; + chance = treasure_table[treasure_type][count][0]; + trip = treasure_table[treasure_type][count][1]; + if (chance == 0) + done = TRUE; + else { + if (TRIP2CL(trip) == CLASS_AMMO) { + if (!player_struct.cartridges[get_nth_from_triple(trip)]) + give = TRUE; + } + if (give || perc < chance) { + if (trip != NOTHING_TRIPLE) { + if ((QUESTVAR_GET(COMBAT_DIFF_QVAR) <= 2) || ((rand() & 0xFF) < 0x80)) + *pcont = obj_create_base(trip); + } + done = TRUE; + } else + perc -= chance; + } + count++; + } + return (OK); +} + +// Distribute loot as appropriate. If we go to loot being contained +// "in" corpses, this is the procedure to change. +errtype do_regular_loot(ObjSpecID source_critter, ObjID corpse) { + ObjID l1, l2 = OBJ_NULL; + ObjSpecID osid = objs[corpse].specID; + + l1 = objCritters[source_critter].loot1; + if (objCritters[source_critter].orders != AI_ORDERS_HIGHWAY) + l2 = objCritters[source_critter].loot2; + + if (l2 != OBJ_NULL && objs[l2].active) { + objContainers[osid].contents1 = OBJ_NULL; // in case we had set it to a triple for later random generation + objContainers[osid].contents2 = l2; + // unset freshness flag + objs[corpse].info.inst_flags &= ~CLASS_INST_FLAG; + if (objs[l2].info.current_hp == 0) + objs[corpse].info.current_hp = 0; + } + + if (l1 != OBJ_NULL && objs[l1].active) { + objContainers[osid].contents1 = l1; + // unset freshness flag + objs[corpse].info.inst_flags &= ~CLASS_INST_FLAG; + if (objs[l1].info.current_hp == 0) + objs[corpse].info.current_hp = 0; + } + + return (OK); +} + +errtype do_random_loot(ObjID corpse) { + ObjSpecID osid = objs[corpse].specID; + int *pc1, *pc2; + uchar t_type; + // char buf[80]; + + if ((((ID2TRIP(corpse) >= MUT_CORPSE1_TRIPLE) && (ID2TRIP(corpse) <= OTH_CORPSE8_TRIPLE)) || + ((ID2TRIP(corpse) >= CORPSE1_TRIPLE) && (ID2TRIP(corpse) <= CORPSE8_TRIPLE))) && + (objs[corpse].info.inst_flags & CLASS_INST_FLAG)) { + switch (objs[corpse].obclass) { + case CLASS_CONTAINER: + t_type = CritterProps[CPTRIP(objContainers[osid].contents1)].treasure_type; + pc1 = &objContainers[osid].contents1; + pc2 = &objContainers[osid].contents2; + *pc1 = 0; + *pc2 = 0; + break; + case CLASS_SMALLSTUFF: + t_type = FIRST_CORPSE_TTYPE + objSmallstuffs[osid].cosmetic_value; + pc1 = &objSmallstuffs[osid].data1; + pc2 = &objSmallstuffs[osid].data2; + break; + } + + if (*pc1 == 0) { + roll_on_dnd_treasure_tables(pc1, t_type); + switch (QUESTVAR_GET(COMBAT_DIFF_QVAR)) { + case 0: + case 1: + if (*pc1 == 0) + roll_on_dnd_treasure_tables(pc1, t_type); + break; + } + } + if (*pc2 == 0) { + roll_on_dnd_treasure_tables(pc2, t_type); + switch (QUESTVAR_GET(COMBAT_DIFF_QVAR)) { + case 0: + case 1: + if (*pc2 == 0) + roll_on_dnd_treasure_tables(pc2, t_type); + break; + } + } + + // unset the freshness bit + objs[corpse].info.inst_flags &= ~CLASS_INST_FLAG; + } + return (OK); +} + +errtype ai_critter_really_dead(ObjSpecID osid) { + int corpse_trip; + char f; + extern errtype obj_floor_func(ObjID id); + + corpse_trip = CritterProps[CPNUM(objCritters[osid].id)].corpse; + if (corpse_trip != 0) { + ObjID new_obj; + new_obj = obj_create_base(corpse_trip); + if (new_obj) { + if ((f = FRAME_NUM_3D(ObjProps[OPNUM(new_obj)].bitmap_3d))) + objs[new_obj].info.current_frame = rand() % (f + 1); + else + objs[new_obj].info.current_frame = 0; + obj_move_to(new_obj, &objs[objCritters[osid].id].loc, TRUE); + // obj_floor_func(new_obj); + + if (objCritters[osid].flags & AI_FLAG_NOLOOT) { + objContainers[objs[new_obj].specID].contents1 = OBJ_NULL; + objs[new_obj].info.inst_flags &= ~CLASS_INST_FLAG; + } else { + // set our contents1 to the triple so that we know how to generate loot right later + objContainers[objs[new_obj].specID].contents1 = ID2TRIP(objCritters[osid].id); + + // fresh kill, yum! + objs[new_obj].info.inst_flags |= CLASS_INST_FLAG; + } + + // if we have regular loot, however, do what we do + do_regular_loot(osid, new_obj); + } + } + return (OK); +} + +uchar pacifism_on; +#define AUTOBOMB_RANGE fix_make(3, 0) + +void ai_misses(ObjSpecID osid) { + // We aren't hitting, so get closer sometimes + if (rand() % 4 == 1) { + objs[objCritters[osid].id].info.inst_flags |= CLASS_INST_FLAG2; + objCritters[osid].mood = AI_MOOD_HOSTILE; + } else + objs[objCritters[osid].id].info.inst_flags &= ~CLASS_INST_FLAG2; +} + +errtype ai_autobomb_explode(ObjID id, ObjSpecID osid) { + CritterAttack ca; + ExplosionData edata; + extern void critter_light_world(ObjID id); + + ca = CritterProps[CPNUM(id)].attacks[0]; + + edata.radius = AUTOBOMB_RANGE; + edata.radius_change = (AUTOBOMB_RANGE >> 1); + edata.damage_mod = ca.damage_modifier; + edata.damage_change = ca.damage_modifier >> 1; + edata.dtype = ca.damage_type; + edata.knock_mass = ca.attack_mass; + edata.offense = ca.offense_value; + edata.penet = ca.penetration; + + // Do an explosion! + do_explosion(objs[id].loc, id, FALSE, &edata); + play_digi_fx_obj(SFX_EXPLOSION_1, 1, id); + + // Make us dying.... + set_posture(osid, DEATH_CRITTER_POSTURE); + + // make us light up the world + critter_light_world(id); + + return (OK); +} + +errtype ai_attack_player(ObjSpecID osid, char a) { + int wpnflags, wpnpower; + ObjID hit_obj = OBJ_NULL; + ObjID id = objCritters[osid].id; + ObjLoc dest_loc; + int cp_num; + fix attack_mass; // cause of new ray casting prototype - minman + + if (pacifism_on) + return (OK); + + cp_num = CPNUM(objCritters[osid].id); + + // If we are an autobomb, don't attack as usual, instead go boom! + switch (ID2TRIP(id)) { + case AUTOBOMB_TRIPLE: { + return (ai_autobomb_explode(id, osid)); + } break; + } + + wpnpower = 100; // Full strength attack! + wpnflags = 0; // Normal attack + attack_mass = fix_make(CritterProps[cp_num].attacks[a].attack_mass, 0) * 20; + +#ifdef CRITTER_ALWAYS_ACCURATE + hit_obj = ray_cast_objects(objCritters[osid].id, PLAYER_OBJ, attack_mass, + fix_make(0, CritterProps[cp_num].attacks[a].attack_size), + fix_make(CritterProps[cp_num].attacks[a].attack_velocity, 0), + fix_make(CritterProps[cp_num].attacks[a].att_range, 0)); +#else + // If we have NO idea where the player is (all failed detection rolls) + // then don't bother firing. + if (last_known_loc.x != 255) { + short miss_amt = ((255 - CritterProps[cp_num].attacks[a].accuracy) - rand() % 255); + + // Play sound effect + play_digi_fx_obj(CritterProps[cp_num].attack_sound, 1, objCritters[osid].id); + objs[objCritters[osid].id].info.inst_flags |= UNLIT_FLAG; + + // Shoot at player's last known location. + // Also, modify where we fire by our accuracy variable + dest_loc = last_known_loc; + + if (miss_amt > 0) { + // We've failed our accuracy roll, so let's perturb + // our target by an amount proportional to the amount + // that we missed by. + dest_loc.x += (rand() % miss_amt - (miss_amt / 2)); + dest_loc.y += (rand() % miss_amt - (miss_amt / 2)); + dest_loc.z += rand() % miss_amt; + } + if (CritterProps[cp_num].attacks[a].slow_proj == 0) { +#ifdef PLAYTEST + extern uchar prevent_ray_spew; + prevent_ray_spew = FALSE; +#endif + hit_obj = ray_cast_attack(objCritters[osid].id, dest_loc, attack_mass, RAYCAST_ATTACK_SIZE, + fix_make(CritterProps[cp_num].attacks[a].attack_velocity, 0), + fix_make(CritterProps[cp_num].attacks[a].att_range, 0)); +#ifdef PLAYTEST + prevent_ray_spew = TRUE; +#endif + if (hit_obj != OBJ_NULL) { + attack_object(hit_obj, CritterProps[cp_num].attacks[a].damage_type, + CritterProps[cp_num].attacks[a].damage_modifier, + CritterProps[cp_num].attacks[a].offense_value, + CritterProps[cp_num].attacks[a].penetration, wpnflags, wpnpower, NULL, 0, 0, NULL); + + objs[objCritters[osid].id].info.inst_flags &= ~CLASS_INST_FLAG2; + } else + ai_misses(osid); + } else { + ai_fire_special(objCritters[osid].id, PLAYER_OBJ, CritterProps[cp_num].attacks[a].slow_proj, + objs[objCritters[osid].id].loc, dest_loc, a, SLOW_PROJECTILE_DURATION); + } + } else { + objCritters[osid].mood = AI_MOOD_HOSTILE; + } +#endif + return (OK); +} + +#define SLOW_PROJ_RAY_MASS (fix_make(0, 0x1000)) +#define SLOW_PROJ_RAY_SIZE (fix_make(0, 0x1800)) +#define SLOW_PROJ_RAY_RANGE (fix_make(20, 0)) +extern void get_phys_state(int ph, State *new_state, ObjID id); + +errtype ai_fire_special(ObjID src, ObjID target, int proj_triple, ObjLoc src_loc, ObjLoc target_loc, uchar a, + int duration) { + ObjID proj_id; + fix xvel, yvel, zvel; + fix xdiff, ydiff, zdiff; + fix dist; + fix fire_speed; + fixang new_angle; + ubyte head; + State new_state; + Robot da_robot; + extern void activate_grenade(ObjSpecID osid); + + if (!global_fullmap->cyber) { + // let's attack only if we think we're going to hit our target + if (ray_cast_attack(src, target_loc, SLOW_PROJ_RAY_MASS, SLOW_PROJ_RAY_SIZE, NO_RAYCAST_KICKBACK_SPEED, + SLOW_PROJ_RAY_RANGE) != target) + return (OK); + } + + proj_id = obj_create_base(proj_triple); + if (proj_id == OBJ_NULL) { + WARN("%s: Could not create slow projectile!", __FUNCTION__); + return (OK); + } + + if (TRIP2CL(proj_triple) == CLASS_PHYSICS) { + objPhysicss[objs[proj_id].specID].owner = src; + objPhysicss[objs[proj_id].specID].bullet_triple = a; + objPhysicss[objs[proj_id].specID].duration = player_struct.game_time + duration; + fire_speed = SLOW_PROJECTILE_SPEED; + } else { + activate_grenade(objs[proj_id].specID); + fire_speed = ATTACK_GRENADE_SPEED; + } + +#ifdef AI_EDMS + + if ((objs[src].obclass == CLASS_CRITTER) && (CritterProps[CPNUM(src)].proj_offset)) + src_loc.z += (CritterProps[CPNUM(src)].proj_offset >> (SLOPE_SHIFT_D - 2)); + + get_phys_state(objs[PLAYER_OBJ].info.ph, &new_state, PLAYER_OBJ); + + // Compute distance + xdiff = fix_from_obj_coord(target_loc.x) - fix_from_obj_coord(src_loc.x); + ydiff = fix_from_obj_coord(target_loc.y) - fix_from_obj_coord(src_loc.y); + zdiff = new_state.Z - fix_from_obj_height_val(src_loc.z); + dist = fix_fast_pyth_dist(xdiff, ydiff); + + if (global_fullmap->cyber) { + // let's get heading + new_angle = fix_atan2(ydiff, xdiff); + head = (ubyte)obj_angle_from_fixang(new_angle); //(fix_div(new_angle, FIXANG_PI) >> 9); + + // let's start the work for pitch + new_angle = fix_atan2(zdiff, dist); + // pitch = (ubyte) (fix_div(new_angle, FIXANG_PI) >> 9); + + // shift coordinate frames for heading + src_loc.h = (ubyte)((320L - head) % 256); + src_loc.p = obj_angle_from_fixang(new_angle); // pitch; + src_loc.b = 0; + } + + xvel = fix_mul_div(xdiff, fire_speed, dist); + yvel = fix_mul_div(ydiff, fire_speed, dist); + + // LET'S HACK HACK HACK THE WAY GRENADES ARE THROWN!!!! + // let's guess on the zvel! + if (TRIP2CL(proj_triple) == CLASS_PHYSICS) + zvel = fix_mul_div(zdiff, fire_speed, dist); + else + zvel = (zdiff < fix_make(0, 0x4000)) ? fix_mul(fix_make(0, 0x3800), dist) : fix_mul(zdiff, fix_make(4, 0)); + + obj_move_to_vel(proj_id, &src_loc, TRUE, xvel, yvel, zvel); + EDMS_ignore_collisions(objs[src].info.ph, objs[proj_id].info.ph); + if (TRIP2CL(proj_triple) == CLASS_PHYSICS) + apply_gravity_to_one_object(proj_id, SLOW_PROJECTILE_GRAVITY); + else + apply_gravity_to_one_object(proj_id, ATTACK_GRENADE_GRAVITY); + + // don't ask me why i have to do this - but i do + EDMS_get_robot_parameters(objs[proj_id].info.ph, &da_robot); + da_robot.cyber_space = -1; + EDMS_set_robot_parameters(objs[proj_id].info.ph, &da_robot); + +#endif + return (OK); +} + +/* KLC - these don't do anything. +errtype ai_freeze_tag() +{ + return(OK); +} + +errtype ai_time_passes(ulong *ticks_passed) +{ + return(OK); +} +*/ diff --git a/engine/src/GameSrc/airupt.c b/engine/src/GameSrc/airupt.c new file mode 100644 index 0000000..5846659 --- /dev/null +++ b/engine/src/GameSrc/airupt.c @@ -0,0 +1,480 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/airupt.c $ + * $Revision: 1.10 $ + * $Author: dc $ + * $Date: 1994/11/19 20:35:14 $ + */ + +// The interrupt-crunchy part of the music ai. + +#include + +#include "airupt.h" +#include "musicai.h" +#include "map.h" +#include "tools.h" +#include "mlimbs.h" + +struct mlimbs_request_info default_request = {0, 0, 4, 100, 0, 64, TRUE, 0, 0}; + +extern int ext_rp; +extern char mlimbs_machine; +extern char cyber_play; + +#define NUM_SNDMIDIDEVICES 2 +#define NUM_SNDMIDICHANNELS 16 +#define NUM_SNDSPEECHCHANNELS 32 +#define NUM_SNDSOUNDSOURCES 1 + +#define NUM_SCORES 8 +#define NUM_LAYERABLE_SUPERCHUNKS 22 +#define FIRST_SUPERCHUNK_LAYER 16 +#define NUM_LAYERS 32 +#define LAYER_BASE 32 + +#define SUPERCHUNKS_PER_SCORE 4 +//#define NUM_TRANSITIONS 8 +#define MAX_KEYS 10 +#define KEY_BAR_RESOLUTION 2 + +#define NUM_PARK_SOUNDS 10 +#define PARK_LAYER_BASE 32 + +#define CYBERSPACE_SCORE_BASE 10 +#define NUM_NODE_THEMES 2 + +#define DANGER_LAYER_BASE 10 // actually one less than Danger1 since a minimum of 1 gets added to it... +#define SUCCESS_LAYER_BASE (DANGER_LAYER_BASE + 2) +#define DECONSTRUCT_LAYER 15 +#define TRANSITION_LAYER_BASE 16 + +#define STANDARD_RAMP_TIME 2000 +#define SHORT_RAMP_TIME 1 + +#define PITCHBEND_CHUNK 15 + +extern uchar track_table[NUM_SCORES][SUPERCHUNKS_PER_SCORE]; +extern uchar transition_table[NUM_TRANSITIONS]; +extern uchar layering_table[NUM_LAYERS][MAX_KEYS]; +extern uchar key_table[NUM_LAYERABLE_SUPERCHUNKS][KEY_BAR_RESOLUTION]; + +extern char peril_bars; + +extern int new_theme; +extern int new_x, new_y; +extern int old_bore; +extern short mai_override; + +extern int layer_danger; +extern int layer_success; +extern int layer_transition; +extern int transition_count; +extern char tmode_time; +extern int actual_score; +extern uchar decon_count; +extern uchar decon_time; +extern uchar in_deconst, old_deconst; +extern uchar in_peril; +extern uchar just_started; +extern int score_playing; +extern short curr_ramp_time, curr_ramp; +extern char curr_prioritize, curr_crossfade; + +// extern int digifx_volume_shift(short x, short y, short z, short phi, short theta, short basevol); +// extern int digifx_pan_shift(short x, short y, short z, short phi, short theta); +extern uchar mai_semaphor; + +extern uchar park_random; +extern uchar park_playing; +extern uchar access_random; + +extern ulong last_damage_sum; +extern ulong last_vel_time; + +// Damage taken decay & quantity of decay +extern int danger_hp_level; +extern int danger_damage_level; +extern int damage_decay_time; +extern int damage_decay_amount; +extern int mai_damage_sum; + +// How long an attack keeps us in combat music mode +extern int mai_combat_length; + +extern uchar bad_digifx; + +extern uchar mlimbs_semaphore; + +uchar run_asynch_music_ai = FALSE; +uchar mai_semaphor = FALSE; + + +void music_ai() { + // mlimbs_semaphore = TRUE; + ai_cycle = TRUE; + // if ((run_asynch_music_ai) && (!mai_semaphor)) + if (!mai_semaphor) { + mai_semaphor = TRUE; + check_asynch_ai(FALSE); + mai_semaphor = FALSE; + ai_cycle = FALSE; + } +} + +void grind_credits_music_ai(void) { + int i; + if (mai_semaphor) { + current_request[0] = default_request; + current_request[0].pieceID = mlimbs_boredom; + } else + make_request(0, mlimbs_boredom); + // if (mlimbs_counter == 4) //KLC - Was 16 + // mlimbs_counter = 0; + // if ((mlimbs_counter % 4) == 3) + // { + mlimbs_boredom++; + if (mlimbs_boredom == 8) + mlimbs_boredom = 0; + // } + // Clear out other requests + for (i = 1; i < MLIMBS_MAX_CHANNELS - 1; i++) + current_request[i].pieceID = 255; // KLC - was -1 +} + +#define MAIN_THEME_WEIGHT 0 +void grind_music_ai(void) { + int i, open_track, r; + int current_key; + short play_me = -1, seq; + + if (mlimbs_counter == 4) // KLC - was 16 + { + mlimbs_counter = 0; + boring_count++; + } + + // Set the default values for the chunk parameters this time around + if (in_deconst) { + decon_count++; + if (decon_count > decon_time) + curr_crossfade = 0; + else + curr_crossfade = 1; + } else if (old_deconst) { + curr_crossfade = -1; + decon_count = 0; + } else { + curr_crossfade = 0; + decon_count = 0; + } + + if (new_theme > 1) { + new_theme--; + curr_ramp = -1; + curr_ramp_time = STANDARD_RAMP_TIME; + } else if (new_theme == 1) { + new_theme = -1; + } else if (new_theme == -2) { + new_theme = 0; + curr_ramp = 1; + curr_ramp_time = STANDARD_RAMP_TIME; + } else if (new_theme != -1) { + curr_ramp = 0; + curr_ramp_time = 0; + } + + if (score_playing == ELEVATOR_ZONE) { + curr_crossfade = 0; + in_deconst = FALSE; + old_deconst = FALSE; + grind_credits_music_ai(); + return; + } + if (current_transition == TRANS_DEATH) { + make_request(0, transition_table[TRANS_DEATH]); + return; + } + + // Change major score if required + if (!mai_semaphor) { + // We need a hack for inner bridge. Inner bridge? But I just met her! + if ((mlimbs_combat) && ((actual_score == PERIL_SCORE) || (actual_score == COMBAT_SCORE)) && (peril_bars > 2)) { + current_score = COMBAT_SCORE; + } else if ((mlimbs_peril > PERIL_THRESHOLD) || mlimbs_combat) { + current_score = PERIL_SCORE; + } else + current_score = WALKING_SCORE; + } + switch (actual_score) { + case COMBAT_SCORE: + break; + case PERIL_SCORE: + peril_bars++; + break; + default: + peril_bars = 0; + break; + } + + // Minor change maybe + if (!mai_semaphor) { + if ((boring_count > 0) && (mlimbs_counter == 0)) { + int rand_poss = 0; + int max_rand; + if (mlimbs_boredom > 0) + mlimbs_boredom = 0; + else { + if (actual_score == WALKING_SCORE) + max_rand = 3; + else + max_rand = 1; + for (i = 1; i <= max_rand; i++) + if (track_table[actual_score + i][0] != 255) + rand_poss++; + ext_rp = rand_poss; + if (rand_poss > 0) { + mlimbs_boredom = (rand() % (rand_poss + MAIN_THEME_WEIGHT + 1)) - MAIN_THEME_WEIGHT; + if (mlimbs_boredom < 0) + mlimbs_boredom = 0; + } else + mlimbs_boredom = 0; + if (track_table[actual_score + mlimbs_boredom][0] == 255) // KLC - was -1 + mlimbs_boredom = 0; + } + } + } + + // Major score transition? + if ((last_score != current_score) && (!mai_semaphor)) { + boring_count = 0; + switch (current_score) { + case WALKING_SCORE: + if (last_score == PERIL_SCORE) + mai_transition(TRANS_PERIL_TO_WALK); + else if (last_score == COMBAT_SCORE) + mai_transition(TRANS_COMB_TO_WALK); + break; + case PERIL_SCORE: + if (last_score == WALKING_SCORE) + mai_transition(TRANS_WALK_TO_PERIL); + else if (last_score == COMBAT_SCORE) + mai_transition(TRANS_COMB_TO_PERIL); + break; + case COMBAT_SCORE: + if (last_score == WALKING_SCORE) + mai_transition(TRANS_WALK_TO_COMB); + else if (last_score == PERIL_SCORE) + mai_transition(TRANS_PERIL_TO_COMB); + break; + } + + // if we aren't doing a layered transition, just jump + // over to new score if possible + if (transition_count == 0) { + // if (mlimbs_counter % 4 == 0) KLC - do it every time + // { + mlimbs_boredom = 0; + last_score = current_score; + actual_score = current_score; + // } + } + } + + if ((next_mode) /*&& ((mlimbs_counter % 4) == 0)*/) // KLC - do it every time. + { + // If we are coming out of death, shut down music + // Eventually this will segue back into the journey screen music, perhaps. + { + current_mode = next_mode; + next_mode = 0; + } + } + switch (current_mode) { + case TRANSITION_MODE: + if (!mai_semaphor) { + play_me = transition_table[current_transition]; + tmode_time--; + if (tmode_time == 0) { + next_mode = NORMAL_MODE; + mlimbs_counter = 3; // so that next_mode will take effect next ai loop KLC - was 15 + } + } + break; + case NORMAL_MODE: + // Play basic superchunk + seq = mlimbs_counter; // KLC - was mlimbs_counter / 4 + play_me = track_table[actual_score + mlimbs_boredom][seq]; + + break; + } + open_track = 0; + + /* KLC - no pb12 track in our stuff + // Play the pitchbend track if we are just starting out + if ((just_started) && (!mai_semaphor)) + { + make_request(open_track++, PITCHBEND_CHUNK); + just_started = FALSE; + } + */ + + if (mai_semaphor) { + current_request[0] = default_request; + current_request[0].pieceID = play_me; + } else { + if (global_fullmap->cyber) { + /* KLC - moved to mlimbs_do_ai. + MapElem *pme; + + // Deal with pitch bend?? + pme = MAP_GET_XY(PLAYER_BIN_X, PLAYER_BIN_Y); + if (!me_bits_peril(pme)) + play_me = NUM_NODE_THEMES + me_bits_music(pme); + else + play_me = me_bits_music(pme); + if (play_me != cyber_play) + { + musicai_shutdown(); + make_request(open_track++, play_me); + musicai_reset(FALSE); + MacTuneStartCurrentTheme(); + } + else + make_request(open_track++, play_me); + cyber_play = play_me; + */ + } else { + if (decon_count < decon_time) { + make_request(open_track++, play_me); + } + + // Don't layer over fullmode transitions + if (current_mode == NORMAL_MODE) { + current_key = key_table[play_me][mlimbs_counter / 2] - 1; // was (mlimbs_counter % 4) / 2 + + // Most layering is mutually exclusive with deconstructing + if (in_deconst && (layering_table[DECONSTRUCT_LAYER][current_key] != 0xFF)) { + make_request(open_track++, layering_table[DECONSTRUCT_LAYER][current_key]); + // if (layering_table[DECONSTRUCT_LAYER][current_key] == 0xFF) + // Warning(("playing decon layer, + // %d!\n",layering_table[DECONSTRUCT_LAYER][current_key])); + } else { + // Do layering + // Monsters... + if (actual_score != COMBAT_SCORE) { + if ((play_me < NUM_LAYERABLE_SUPERCHUNKS) && (mlimbs_monster != NO_MONSTER) && + (layering_table[mlimbs_monster][current_key] != 0xFF)) { + make_request(open_track++, layering_table[mlimbs_monster][current_key]); + } + } + + // Object-based "machine" layers + // if (mlimbs_machine != 0) + // Warning(("mlimbs_machine = %d table = + // %d!\n",mlimbs_machine,layering_table[mlimbs_machine][current_key])); + if ((mlimbs_machine != 0) && (layering_table[mlimbs_machine][current_key] != 255)) { + // KLC - no layering, force to track 0. + make_request(0, layering_table[mlimbs_machine][current_key]); + } + + // Transitions... + if (transition_count > 0) { + if (layering_table[TRANSITION_LAYER_BASE + current_transition][current_key] != 255) { + // KLC if ((((mlimbs_counter % 4) == 0) && (transition_count == 2)) || + // KLC (((mlimbs_counter % 4) == 1) && (transition_count == 1))) + { + if (current_score == actual_score) { + transition_count = 0; + } else { + make_request( + open_track++, + layering_table[TRANSITION_LAYER_BASE + current_transition][current_key]); + transition_count--; + if (transition_count == 0) { + last_score = actual_score = current_score; + mlimbs_counter = 3; // KLC was 15 + mlimbs_boredom = 0; + } + } + } + } + } + + // Feedback (Danger & Success) + if (layer_danger && (layering_table[DANGER_LAYER_BASE + layer_danger][current_key] != 255)) { + make_request(open_track++, layering_table[DANGER_LAYER_BASE + layer_danger][current_key]); + } + if (layer_success && (layering_table[SUCCESS_LAYER_BASE + layer_success][current_key] != 255)) { + make_request(open_track++, layering_table[SUCCESS_LAYER_BASE + layer_success][current_key]); + } + } + + // Some layers always play, independant of deconstruct + + // Current park sounds occur in peril, combat, and walking. Should this be just walking? + if (park_playing) { + make_request(open_track++, park_playing); + park_playing = 0; + } else if ((score_playing == PARK_ZONE) && (rand() % 100 < park_random)) + // KLC && ((mlimbs_counter % 4) == 0)) + { + r = rand() % NUM_PARK_SOUNDS; + park_playing = r + PARK_LAYER_BASE; + make_request(open_track++, park_playing); + } + } + } + } + + // why!!!! why at the end, why randomly, hate hate hate + // Clear out requests + for (i = open_track; i < MLIMBS_MAX_CHANNELS - 1; i++) + current_request[i].pieceID = 255; // was -1 + + // Some end of cycle admin stuff... + old_deconst = in_deconst; +} + +errtype check_asynch_ai(uchar new_score_ok) { + // extern uchar mlimbs_semaphore; + // if (ai_cycle) + // { + ai_cycle = 0; + grind_music_ai(); + // if (!run_asynch_music_ai) + // mlimbs_preload_requested_timbres(); + + // mlimbs_semaphore = FALSE; + + /* + // We need new theme loaded... + if (!mai_semaphor) + { + if (new_score_ok && (new_theme==-1)) + { + new_theme = -2; + load_score_for_location(new_x,new_y); + } + } + */ + // } + return (OK); +} diff --git a/engine/src/GameSrc/amap.c b/engine/src/GameSrc/amap.c new file mode 100644 index 0000000..8dc7999 --- /dev/null +++ b/engine/src/GameSrc/amap.c @@ -0,0 +1,1079 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/amap.c $ + * $Revision: 1.41 $ + * $Author: dc $ + * $Date: 1994/11/28 06:38:12 $ + * + * routines for creation and modification of the automap on a level + * uses the map's internal state for showing stuff + * as well as a list of objects (map notes) for highlights and such + * this is the core canvas rendering/wall stuff + * it is called by the mfd based system, or in full screen mode + */ + +// color set.... +// player/focused object - red +// elevators - dark brown +// walls - bright green, mid green, dim green +// security - cycling red +// bio - yellow +// radiation - flat red +// mutant - purple +// robot - metalblue +// cyborg - brightbrownx +// doors - maize? + +#include + +#include "cit2d.h" +#include "colors.h" +#include "cybstrng.h" +#include "frquad.h" +#include "gamescr.h" +#include "gamestrn.h" +#include "gr2ss.h" +#include "lvldata.h" +#include "map.h" +#include "mapflags.h" +#include "musicai.h" +#include "objbit.h" +#include "objgame.h" +#include "objprop.h" +#include "objuse.h" +#include "otrip.h" +#include "player.h" +#include "rcolors.h" +#include "refstuf.h" +#include "tilename.h" + +#define FMK_INT_XX (FMK_INT_NW || FMK_INT_SW || FMK_INT_EW || FMK_INT_WW) +#define FMK_INT_INT (1 << 8) + +// actual dimensions of a typical monitor, for calculating pixel ratio. +#define STD_SCR_WID 11 +#define STD_SCR_HGT 8 + +#define NORTH 0 +#define EAST 1 +#define SOUTH 2 +#define WEST 3 + +#define TERR_OBJ_PASS 0 +#define REAL_OBJ_PASS 1 +#define NOTE_OBJ_PASS 2 +#define NUM_OBJ_PASSES 3 + +#define FIN_SWEEP 0x5800 +#define FIN_NOSE fix_make(0, 0xB000) +#define FIN_TAIL fix_make(0, 0x4000) + +#define DRAW_MASK_SEEN 0x1 +#define DRAW_MASK_RAD 0x2 +#define DRAW_MASK_FULL 0x4 +#define DRAW_MASK_TERR 0x8 +#define DRAW_MASK_SENS 0x10 + +//#define AMAP_SENS_TILEBOUND +#define AMAP_SENS_CIRCLE + +#define CORRECT_PIXEL_RATIO +#define FIX_PIXRATIO + +// beware the shifting version. If pixratio_shf is negative, +// C does not define what happens. This does not happen to +// occur in any of the screen modes we plan to support, and +// might work right anyway. +// +#ifdef CORRECT_PIXEL_RATIO +#ifdef FIX_PIXRATIO +fix pixratio_yx = FIX_UNIT; +fix pixratio_xy = FIX_UNIT; +//#define coor_to_pix(y) fast_fix_mul(pixratio_yx,(y)) KLC - Changed this +//#define pix_to_coor(y) fast_fix_mul(pixratio_xy,(y)) +#define coor_to_pix(y) fix_mul(pixratio_yx, (y)) +#define pix_to_coor(y) fix_mul(pixratio_xy, (y)) +#else +int pixratio_shf = 0; +#define coor_to_pix(y) ((y) << pixratio_shf) +#define pix_to_coor(y) ((y) >> pixratio_shf) +#endif + +// fix times int can use regular multiply +#define am_vline(x, y0, y1) gr_vline(x, coor_to_pix(y0), coor_to_pix(y1)) +#define am_hline(x0, y, x1) gr_hline(x0, coor_to_pix(y), x1) +#define am_rect(x0, y0, x1, y1) gr_rect(x0, coor_to_pix(y0), x1, coor_to_pix(y1)) +#define am_int_line(x0, y0, x1, y1) gr_int_line(x0, coor_to_pix(y0), x1, coor_to_pix(y1)) +#define am_fix_line(x0, y0, x1, y1) gr_fix_line(x0, coor_to_pix(y0), x1, coor_to_pix(y1)) +#define am_int_circle(xc, yc, r) gr_int_circle(xc, coor_to_pix(yc), r) +#else +#define am_vline gr_vline +#define am_hline gr_hline +#define am_rect gr_rect +#define am_int_line gr_int_line +#define am_fix_line gr_fix_line +#define am_int_circle gr_int_circle +#endif + +//------------------- +// Prototypes +//------------------- +void obj_draw(int xm, int ym, Obj *cobj, int tsize, int so, int color); +void line_draw(int xm, int ym, Obj *cobj, int tsize, int full, int color); +void obj_mess(curAMap *amptr, MapElem *curmp, int drw, int xm, int ym, int tsize, int pass); +uchar wall_seen_p(int wallcode, int csbits, MapElem *cur); +void tile_draw(int xm, int ym, int tiletype, int size, int offs, int color); +void wall_draw(int xm, int ym, int wallcode, int size, MapElem *cur); +void draw_radius_obj(curAMap *amptr, short OtoF, int col, int zeroscrx, int zeroscry, int rad); +void draw_full_obj(curAMap *amptr, short OtoF, int col, int zeroscrx, int zeroscry); +void *amap_loc_to_sq(curAMap *amptr, int *x, int *y); +ObjID amap_loc_get_note(void *map_sq); +ObjID amap_loc_note_check(curAMap *amptr, void *curmp, int *x, int *y, int *to_do); +void amap_fixup_existing(int tolera, int delta); + +// for now, create a default automap thingy +void amap_version_set(int id, int new_ver) { + curAMap *amptr = oAMap(id); + + switch (new_ver) { + case -1: + amptr->flags = AMAP_TRACK_OBJ | AMAP_SHOW_SEC | AMAP_SHOW_CRIT; + amptr->sensor_rad = 0x800; + break; + case 0: + case 1: + amptr->flags = AMAP_TRACK_OBJ | AMAP_SHOW_SEC | AMAP_SHOW_MSG; + amptr->sensor_rad = 0x400; + break; + case 2: + amptr->flags = AMAP_TRACK_OBJ | AMAP_SHOW_SEC | AMAP_SHOW_ROB | AMAP_SHOW_MSG; + amptr->sensor_rad = 0x600; + break; + case 3: + amptr->flags = AMAP_TRACK_OBJ | AMAP_SHOW_SEC | AMAP_SHOW_CRIT | AMAP_SHOW_HAZ | AMAP_SHOW_MSG; + amptr->sensor_rad = 0x800; + break; + } + amptr->avail_flags = amptr->flags | AMAP_AVAIL_ALWAYS; + amptr->version_id = new_ver; +} + +void automap_init(int version, int id) { + curAMap *amptr; + amptr = oAMap(id); + amptr->xf = fix_make((MAP_YSIZE >> 1) - 1, 0x8000); + amptr->yf = fix_make((MAP_YSIZE >> 1) - 1, 0x8000); + amptr->obj_to_follow = amptr->sensor_obj = PLAYER_OBJ; + amptr->note_obj = 0; + amptr->zoom = 2; + amptr->lh = grd_bm.h; + amptr->lw = grd_bm.w; + amap_version_set(id, version); + amptr->init = TRUE; +} + +void amap_invalidate(int id) { oAMap(id)->init = FALSE; } + +void amap_settings_copy(curAMap *from, curAMap *to) { + to->flags = from->flags; + to->zoom = from->zoom; +} + +#ifdef USE_COMPILED_WALLS +uchar wall_seen_p(int wallcode, int csbits, MapElem *cur) { + // if(textprops[me_tmap_flr(cur)].force_dir==1) + // return 0; + + if (wallcode < FMK_INT_INT) { + return csbits & wallcode; + } else { + int mo1, mo2, lb1, lb2, ck1, ck2; + switch (me_tiletype(cur)) { + case TILE_SOLID_NW: + mo1 = -MAP_XSIZE; + mo2 = 1; + ck1 = FMK_NW; + ck2 = FMK_WW; + break; + case TILE_SOLID_NE: + mo1 = -MAP_XSIZE; + mo2 = -1; + ck1 = FMK_NW; + ck2 = FMK_EW; + break; + case TILE_SOLID_SE: + mo1 = MAP_XSIZE; + mo2 = 1; + ck1 = FMK_SW; + ck2 = FMK_WW; + break; + case TILE_SOLID_SW: + mo1 = MAP_XSIZE; + mo2 = -1; + ck1 = FMK_SW; + ck2 = FMK_EW; + break; + default: + return 0; + } + lb1 = me_clearsolid(cur + mo1); + lb2 = me_clearsolid(cur + mo2); + return ((csbits & FMK_INT_XX) || (((lb1 | lb2) != 0) && (((lb1 & ck1) != 0) || ((lb2 & ck2) != 0)))); + } +} +#else +#include "fredge.h" +uchar wall_seen_p(int wallcode, int csbits, MapElem *cur) { + // if(textprops[me_tmap_flr(cur)].force_dir) + // return 0; + + if (wallcode == FMK_INT_INT) { + switch (me_tiletype(cur)) { + case TILE_SOLID_NW: + return 1; + case TILE_SOLID_NE: + return 1; + case TILE_SOLID_SE: + return 1; + case TILE_SOLID_SW: + return 1; + } + } else { + switch (get_edge_code(cur, csbits)) { + case MEDGE_NO_EGRESS: + return 1; + case MEDGE_CLIFF_THING: + return 2; + case MEDGE_LARGE_STEP: + return 3; + case MEDGE_SMALL_STEP: + return 4; + } + } + return 0; +} +#endif + +#ifdef REAL_XIST_CHECK +uchar wall_xist_p(int wallcode, int csbits, MapElem *cur) { + if (wallcode < FMK_INT_INT) + return ((csbits & (wallcode >> 4)) == 0); // wow, this is super wacky (tm)? punt diags and go with? + else { + switch (me_tiletype(cur)) { + case TILE_SOLID_NW: + return 1; + case TILE_SOLID_NE: + return 1; + case TILE_SOLID_SE: + return 1; + case TILE_SOLID_SW: + return 1; + } + return 0; + } +} +#else +#define wall_xist_p(wc, cs, cur) (wallcode < FMK_INT_INT) +#endif + +void wall_draw(int xm, int ym, int wallcode, int size, MapElem *cur) { + switch (wallcode) { + case FMK_INT_SW: + am_hline(xm, ym, xm + size); + break; + case FMK_INT_NW: + am_hline(xm, ym - size, xm + size); + break; + case FMK_INT_WW: + am_vline(xm, ym, ym - size); + break; + case FMK_INT_EW: + am_vline(xm + size, ym, ym - size); + break; + case FMK_INT_INT: + if ((me_tiletype(cur) == TILE_SOLID_NW) || (me_tiletype(cur) == TILE_SOLID_SE)) + am_int_line(xm, ym, xm + size, ym - size); + else + am_int_line(xm, ym - size, xm + size, ym); + break; + } +} + +static void tri_draw(int x1, int y1, int x2, int y2, int x3, int y3, int color) { + grs_vertex vert[3], *pervert[3]; + + vert[0].x = fix_make(x1, 0); + vert[0].y = pix_to_coor(fix_make(y1, 0)); + vert[1].x = fix_make(x2, 0); + vert[1].y = pix_to_coor(fix_make(y2, 0)); + vert[2].x = fix_make(x3, 0); + vert[2].y = pix_to_coor(fix_make(y3, 0)); + + pervert[0] = &vert[0]; + pervert[1] = &vert[1]; + pervert[2] = &vert[2]; + + gr_poly(color, 3, pervert); +} + +void tile_draw(int xm, int ym, int tiletype, int size, int offs, int color) { + while ((offs > 0) && (size < 2 * offs)) + offs--; + switch (tiletype) { + case TILE_SOLID_NW: + tri_draw(xm + size + 1, ym - size + offs - 1, xm + size + 1, ym + 1, xm + offs - 1, ym + 1, color); + break; + case TILE_SOLID_SE: + tri_draw(xm + offs, ym - size + offs, xm + size, ym - size + offs, xm + offs, ym, color); + break; + case TILE_SOLID_SW: + tri_draw(xm + offs, ym - size + offs, xm + size + 1, ym - size + offs, xm + size + 1, ym + 1, color); + break; + case TILE_SOLID_NE: + tri_draw(xm + offs, ym - size + offs, xm + size + 1, ym + 1, xm + offs, ym + 1, color); + break; + default: + gr_set_fcolor(color); + am_rect(xm + offs, ym - size + offs, xm + size + 1, ym + 1); + } +} + +void obj_draw(int xm, int ym, Obj *cobj, int tsize, int so, int color) { + xm += ((cobj->loc.x & 0xff) * tsize) >> 8; + ym -= ((cobj->loc.y & 0xff) * tsize) >> 8; + gr_set_fcolor(color); + if (so <= 1) + so = 1; + am_rect(xm - so, ym - so, xm + so, ym + so); +} + +void line_draw(int xm, int ym, Obj *cobj, int tsize, int full, int color) { + if (cobj->loc.h & ~0xc0) + return; + gr_set_fcolor(color); + if (cobj->loc.h & 0x40) // ns + { + if (cobj->loc.x == 0xff) + xm += tsize + 1; + else + xm += ((cobj->loc.x & 0xff) * tsize) >> 8; + if (full) + am_vline(xm, ym, ym - tsize); + else + am_vline(xm, ym - (tsize >> 2), ym - 3 * (tsize >> 2)); + } else { + if (cobj->loc.y == 0xff) + ym -= tsize + 1; + else + ym -= ((cobj->loc.y & 0xff) * tsize) >> 8; + if (full) + am_hline(xm, ym, xm + tsize); + else + am_hline(xm + (tsize >> 2), ym, xm + 3 * (tsize >> 2)); + } +} + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) < (b) ? (b) : (a)) + +// if objs, look for creatures, security systems, messages +// need to add bridges... perhaps something for energy, switches and levers +// works in three sorting passes: terrain type stuff (e.g., doors), +// other "real" objects, and map notes and stuff. +void obj_mess(curAMap *amptr, MapElem *curmp, int drw, int xm, int ym, int tsize, int pass) { + Obj *cobj; + ObjID cobjid; + ObjRefID curORef; + int col, px, md; + short w, h; + char buf[50]; + grs_font *fon; + + curORef = curmp->objRef; + while (curORef != OBJ_REF_NULL) { + cobjid = objRefs[curORef].obj; + if ((amptr->flags & AMAP_TRACK_OBJ) && (cobjid == amptr->obj_to_follow)) { + curORef = objRefs[curORef].next; + continue; + } + if (CitrefCheckHomeSq(curORef)) { // this is the home square... so do stuff + cobj = &objs[cobjid]; + px = ObjProps[OPNUM(cobjid)].physics_xr; + px = (px << amptr->zoom) >> 6; + switch (cobj->obclass) { // critters, doors, elevators, or do we do that with music bits, yes!!! special + case CLASS_CRITTER: + if (pass != REAL_OBJ_PASS) + break; + if ((drw & (DRAW_MASK_FULL | DRAW_MASK_RAD)) && (amptr->flags & (AMAP_SHOW_CRIT | AMAP_SHOW_ROB))) { + static uchar base_col[3] = {PURPLE_8_BASE + 7, METALBLUE_8_BASE + 7, BRIGHTBROWN_8_BASE + 7}; + uchar col; + + if ((cobj->subclass != CRITTER_SUBCLASS_ROBOT) && ((amptr->flags & AMAP_SHOW_CRIT) == 0)) + break; + if (cobj->subclass < 3) + col = base_col[cobj->subclass]; + else + col = GRAY_8_BASE; + // pick color and intensity based on creature type and all + if (cobj->info.type < 5) + col -= cobj->info.type; + else + col -= 5; + // okay, this is a hack, but hey, I felt sorry for the + // poor guys. + if (ID2TRIP(cobjid) != ASSASSIN_TRIPLE) + obj_draw(xm, ym, cobj, tsize, px, col); + } + break; + case CLASS_DOOR: + if (pass != TERR_OBJ_PASS) + break; + // perhaps only draw physics doors??? + if ((ObjProps[OPNUM(cobjid)].flags & TERRAIN_OBJECT) != 0) + if (drw & DRAW_MASK_SEEN) // used to do FULL too + if ((cobj->loc.p | cobj->loc.b) == 0) // if pitched or banked, hit the road + { + int col = MAIZE_8_BASE; + int trip = ID2TRIP(cobjid); + if ((trip >= SECRET_DOOR1_TRIPLE) && (trip <= SECRET_DOOR3_TRIPLE)) + col = GREEN_BASE + 2; + if (USE_MODE(cobjid) == NULL_USE_MODE) + col = GREEN_BASE + 2; + // if (trip==INVISO_DOOR_TRIPLE) col=AQUA_8_BASE+2; + line_draw(xm, ym, cobj, tsize, cobj->info.current_frame > 0 ? 0 : 1, + col); // if frame, then open, else closed + } + break; + default: // special + switch (ID2TRIP(cobjid)) { // security cameras, automap notes + case LARGCPU_TRIPLE: + case CAMERA_TRIPLE: + if (pass != REAL_OBJ_PASS) + break; + if ((drw & DRAW_MASK_SEEN) && (amptr->flags & AMAP_SHOW_SEC)) { + px = tsize >> 3; + obj_draw(xm, ym, cobj, tsize, px, RED_8_BASE + 5); + } + break; + case MAPNOTE_TRIPLE: + if (pass != NOTE_OBJ_PASS) + break; + if (amptr->flags & AMAP_SHOW_MSG) { + if (amptr->note_obj == cobjid) { + px = tsize >> 1; + col = 2; + } else { + px = tsize >> 2; + col = 6; + } + obj_draw(xm, ym, cobj, tsize, px, AQUA_8_BASE + col); + + if (amptr->flags & AMAP_FULL_MSG) { + strncpy(buf, amap_note_string(cobjid), 49); + buf[49] = 0; + fon = gr_get_font(); + gr_set_font(ResLock(RES_tinyTechFont)); + md = tsize - 2; + gr_string_wrap(buf, md); + gr_string_size(buf, &w, &h); + if (h > md) { + // square up text iteratively + md = (w + h) / 2; + gr_font_string_unwrap(buf); + gr_string_wrap(buf, md); + gr_string_size(buf, &w, &h); + if (abs(w - h) > 4) { + md = (w + h) / 2; + gr_font_string_unwrap(buf); + gr_string_wrap(buf, md); + } + } + gr_set_fcolor(BLACK + 1); +#ifdef SVGA_SUPPORT + { + extern uchar shadow_scale; + shadow_scale = FALSE; +#endif +#ifdef CORRECT_PIXEL_RATIO + draw_shadowed_string(buf, xm + 1, coor_to_pix(ym - tsize + 1), AQUA_8_BASE + col); +#else + draw_shadowed_string(buf, xm + 1, ym - tsize + 1, AQUA_8_BASE + col); +#endif +#ifdef SVGA_SUPPORT + shadow_scale = TRUE; + } +#endif + ResUnlock(RES_tinyTechFont); + gr_set_font(fon); + } + } + break; + case ENRG_CHARGE_TRIPLE: + case CYB_TERM_TRIPLE: + default: + break; + } + break; + } + } // end of in home square + curORef = objRefs[curORef].next; + } // end of ORef loop +} // end of objs loop + +void draw_radius_obj(curAMap *amptr, short OtoF, int col, int zeroscrx, int zeroscry, int rad) { + int xm, ym; + + xm = zeroscrx + ((objs[OtoF].loc.x << amptr->zoom) >> 8); + ym = zeroscry - ((objs[OtoF].loc.y << amptr->zoom) >> 8); + + gr_set_fcolor(col); + am_int_circle(xm, ym, (rad << amptr->zoom) >> 8); +} + +void draw_full_obj(curAMap *amptr, short OtoF, int col, int zeroscrx, int zeroscry) { + int hd = objs[amptr->obj_to_follow].loc.h; // should add pitch and bank, really + int xm, ym; + fix tx, ty, lx, ly, rx, ry; + fix csin, ccos; + + xm = zeroscrx + ((objs[OtoF].loc.x << amptr->zoom) >> 8); + xm = fix_make(xm, 0); + ym = zeroscry - ((objs[OtoF].loc.y << amptr->zoom) >> 8); + ym = fix_make(ym, 0); + + hd = (((hd - 64) + 256) & 0xff) << 8; + + fix_sincos(hd, &csin, &ccos); + ty = ym + fast_fix_mul(csin, FIN_NOSE << amptr->zoom); + tx = xm + fast_fix_mul(ccos, FIN_NOSE << amptr->zoom); + + fix_sincos((hd + (FIN_SWEEP)) & 0xffff, &csin, &ccos); + ly = ym + fast_fix_mul(csin, FIN_TAIL << amptr->zoom); + lx = xm + fast_fix_mul(ccos, FIN_TAIL << amptr->zoom); + + fix_sincos((hd + (0x10000 - FIN_SWEEP)) & 0xffff, &csin, &ccos); + ry = ym + fast_fix_mul(csin, FIN_TAIL << amptr->zoom); + rx = xm + fast_fix_mul(ccos, FIN_TAIL << amptr->zoom); + + gr_set_fcolor(col); + + am_fix_line(tx, ty, lx, ly); + am_fix_line(lx, ly, xm, ym); + am_fix_line(xm, ym, rx, ry); + am_fix_line(rx, ry, tx, ty); +} + +#ifdef AMAP_SENS_TILEBOUND +static char facecheck[] = {(1 << NORTH) | (1 << WEST), (1 << NORTH) | (1 << EAST), (1 << SOUTH) | (1 << EAST), + (1 << SOUTH) | (1 << WEST)}; +#endif + +#ifdef CORRECT_PIXEL_RATIO +void amap_pixratio_set(fix ratio) { + if (ratio == 0) + ratio = fix_make(grd_screen_canvas->bm.h, 0) * STD_SCR_WID / (grd_screen_canvas->bm.w * STD_SCR_HGT); + +#ifdef FIX_PIXRATIO + pixratio_yx = ratio; + pixratio_xy = fix_div(FIX_UNIT, ratio); +#else + // note once again that this only works if pixratio_shf is only + // ever intended to be positive. + pixratio_shf = 0; + while (ratio > FIX_UNIT) { + ratio = ratio >> 1; + pixratio_shf++; + } + if (ratio < ((FIX_UNIT * 707) / 1000)) { // root(2)/2, or half a shift + pixratio_shf--; + } +#endif +} +#endif + +void amap_draw(curAMap *amptr, int expose) { + int xc, yc, xm, ym, drw, static_drw, cv; // loop control, so on + int zeroscrx, zeroscry, init_yc; // x and y screen coordinate for 0,0 of map + int tsize = 1 << amptr->zoom; +#ifdef AMAP_SENS_TILEBOUND + int facemask; +#endif + int mt, pass; + ushort sensor_x, sensor_y; + MapElem *curmp = MAP_GET_XY(0, 0); + fix amrh, amrw; + int max_xc, max_yc, xbase, crnr_x, crnr_y; // am w and h radius + MapElem *mapybase, *init_mapybase; + + if (amptr->flags & AMAP_TRACK_OBJ) { + amptr->xf = objs[amptr->obj_to_follow].loc.x << 8; + amptr->yf = fix_make(MAP_YSIZE, 0) - 1 - (objs[amptr->obj_to_follow].loc.y << 8); + } + + amptr->lw = grd_bm.w; + amptr->lh = grd_bm.h; + + zeroscrx = (grd_bm.w >> 1) - fix_int(amptr->xf << amptr->zoom); + zeroscry = pix_to_coor(grd_bm.h >> 1) - fix_int(amptr->yf << amptr->zoom); + zeroscry = zeroscry + (MAP_YSIZE << amptr->zoom); + if (amptr->sensor_obj != OBJ_NULL) { + sensor_x = objs[amptr->sensor_obj].loc.x; + sensor_y = objs[amptr->sensor_obj].loc.y; + } else + sensor_x = sensor_y = 0; + + amrw = (grd_bm.w << 15) >> amptr->zoom; // w radius of amap, in fix point tiles + amrh = pix_to_coor((grd_bm.h << 15) >> amptr->zoom); // h radius of amap, in fix point tiles + xc = fix_int(amptr->xf - amrw); + max_xc = 1 + fix_int(amptr->xf + amrw); + yc = MAP_YSIZE - (1 + fix_int(amptr->yf + amrh)); + max_yc = MAP_YSIZE - fix_int(amptr->yf - amrh); + if (xc < 0) + xc = 0; + if (max_xc >= MAP_XSIZE) + max_xc = MAP_XSIZE - 1; + if (yc < 0) + yc = 0; + if (max_yc >= MAP_XSIZE) + max_yc = MAP_XSIZE - 1; + xbase = xc; + crnr_x = zeroscrx + (xc << amptr->zoom); + crnr_y = zeroscry - (yc << amptr->zoom); + mapybase = curmp + (yc * MAP_XSIZE) + xc; + + // mprintf("rect %d %d and %d %d, crnr %d %d from rw and rh %.2q %.2q cnt %.2q + // %.2q....\n",xc,yc,max_xc,max_yc,crnr_x,crnr_y,amrw,amrh,amptr->xf,amptr->yf); + + // mprintf("Zero at %d %d...\n",zeroscrx,zeroscry); + + if (expose) + gr_clear(0xFF); + static_drw = 0; + if (amptr->flags & AMAP_SHOW_ALL) + static_drw |= DRAW_MASK_FULL | DRAW_MASK_TERR; + else if (amptr->flags & AMAP_SHOW_FLR) + static_drw |= DRAW_MASK_TERR; + if (amptr->flags & AMAP_SHOW_SENS) + static_drw |= DRAW_MASK_SENS; + gr_set_fcolor(GREEN_BASE + 2); + init_mapybase = mapybase; + init_yc = yc; + + // draw hazard/elevator floor colors BEFORE drawing walls. + for (ym = crnr_y; yc < max_yc; yc++, ym -= tsize, mapybase += MAP_XSIZE) + for (curmp = mapybase, xc = xbase, xm = crnr_x; xc < max_xc; xc++, xm += tsize, curmp++) { + drw = static_drw; + if (me_bits_seen(curmp)) + drw |= DRAW_MASK_SEEN; + if (fix_fast_pyth_dist((xc << 8) + 0x80 - sensor_x, (yc << 8) + 0x80 - sensor_y) < amptr->sensor_rad) + drw |= DRAW_MASK_RAD; + + if (((mt = me_tiletype(curmp)) != TILE_SOLID) && (drw != 0)) { + if (drw & (DRAW_MASK_SEEN | DRAW_MASK_RAD)) { + // if hazard or elevator, floor draw + // check music bits for elevators! + if (me_bits_music(curmp) == ELEVATOR_ZONE) + tile_draw(xm, ym, mt, tsize, 1, BROWN_8_BASE + 4); + if (amptr->flags & AMAP_SHOW_HAZ) { // bio, rad, both + static uchar col_map[] = {YELLOW_8_BASE + 6, RED_8_BASE + 6, ORANGE_8_BASE + 5}; + int hv = 0; + if (level_gamedata.hazard.zerogbio == 0) + if (me_hazard_bio_x(curmp)) + hv = 1; + if (me_hazard_rad_x(curmp)) + hv |= 2; + if (hv) + tile_draw(xm, ym, (drw & DRAW_MASK_SEEN) ? mt : TILE_OPEN, tsize, 1, col_map[hv - 1]); + } + } + } + } + mapybase = init_mapybase; + yc = init_yc; + // now draw walls and such + for (ym = crnr_y; yc < max_yc; yc++, ym -= tsize, mapybase += MAP_XSIZE) + for (curmp = mapybase, xc = xbase, xm = crnr_x; xc < max_xc; xc++, xm += tsize, curmp++) { + drw = static_drw; + if (me_bits_seen(curmp)) + drw |= DRAW_MASK_SEEN; + if (fix_fast_pyth_dist((xc << 8) + 0x80 - sensor_x, (yc << 8) + 0x80 - sensor_y) < amptr->sensor_rad) + drw |= DRAW_MASK_RAD; + + if ((me_tiletype(curmp) != TILE_SOLID) && (drw != 0)) { + int csbits = me_clearsolid(curmp), loop; + +#ifndef REAL_XIST_CHECK + if (drw & DRAW_MASK_TERR) { + gr_set_fcolor(GREEN_BASE + 9); + wall_draw(xm, ym, FMK_INT_NW, tsize, curmp); + wall_draw(xm, ym, FMK_INT_EW, tsize, curmp); + wall_draw(xm, ym, FMK_INT_SW, tsize, curmp); + wall_draw(xm, ym, FMK_INT_WW, tsize, curmp); + gr_set_fcolor(GREEN_BASE + 2); + } +#endif + + gr_set_fcolor(GREEN_BASE + 2); + +#ifdef USE_COMPILED_WALLS + for (loop = (1 << 4); loop <= FMK_INT_INT; loop <<= 1) + if (wall_seen_p(loop, csbits, curmp)) + put back in later.wall_draw(xm, ym, loop, tsize, curmp); +#else + for (loop = (1 << 4), csbits = 0; loop <= FMK_INT_INT; csbits++, loop <<= 1) + if ((drw & DRAW_MASK_SEEN) && + (cv = wall_seen_p(loop, csbits, curmp))) { // colors are gb+2,5,8 for wall,cliff,bigstep + gr_set_fcolor(GREEN_BASE + 2 + (2 * (cv - 1))); + wall_draw(xm, ym, loop, tsize, curmp); + } +#endif + +#ifdef REAL_XIST_CHECK + else if (amptr->flags & AMAP_SHOW_ALL) + if (wall_xist_p(loop, csbits, curmp)) { + gr_set_fcolor(GREEN_BASE + 9); + wall_draw(xm, ym, loop, tsize, curmp); + gr_set_fcolor(GREEN_BASE + 2); + } +#endif + + } // if !tile_solid +#ifdef AMAP_SENS_TILEBOUND + if ((drw & DRAW_MASK_RAD) && (drw & DRAW_MASK_SENS)) { + if (fix_fast_pyth_dist((xc << 8) + 0x80 - sensor_x, (yc << 8) + 0x80 - sensor_y) + (1 << 8) >= + amptr->sensor_rad) { + facemask = 0; + + if (fix_fast_pyth_dist((xc << 8) + 0x80 - sensor_x, ((yc + 1) << 8) + 0x80 - sensor_y) >= + amptr->sensor_rad) + facemask |= (1 << NORTH); + if (fix_fast_pyth_dist((xc << 8) + 0x80 - sensor_x, ((yc - 1) << 8) + 0x80 - sensor_y) >= + amptr->sensor_rad) + facemask |= (1 << SOUTH); + if (fix_fast_pyth_dist((xc + 1 << 8) + 0x80 - sensor_x, (yc << 8) + 0x80 - sensor_y) >= + amptr->sensor_rad) + facemask |= (1 << EAST); + if (fix_fast_pyth_dist((xc - 1 << 8) + 0x80 - sensor_x, (yc << 8) + 0x80 - sensor_y) >= + amptr->sensor_rad) + facemask |= (1 << WEST); + + if (facemask) { + gr_set_fcolor(GRAY_8_BASE + 3); + if (drw & DRAW_MASK_SEEN) { + mt = me_tiletype(curmp); + if (mt >= TILE_SOLID_NW && mt <= TILE_SOLID_SW) { + mt -= TILE_SOLID_NW; + if ((facemask & facecheck[mt]) == facecheck[mt]) { + wall_draw(xm, ym, FMK_INT_INT, tsize, curmp); + facemask ^= facecheck[mt]; + } + } + } + + if (facemask & (1 << NORTH)) + wall_draw(xm, ym, FMK_INT_NW, tsize, curmp); + if (facemask & (1 << SOUTH)) + wall_draw(xm, ym, FMK_INT_SW, tsize, curmp); + if (facemask & (1 << EAST)) + wall_draw(xm, ym, FMK_INT_EW, tsize, curmp); + if (facemask & (1 << WEST)) + wall_draw(xm, ym, FMK_INT_WW, tsize, curmp); + } + } + } +#endif + } // for y loop + + for (pass = 0; pass < NUM_OBJ_PASSES; pass++) { + mapybase = init_mapybase; + yc = init_yc; + for (ym = crnr_y; yc < max_yc; yc++, ym -= tsize, mapybase += MAP_XSIZE) + for (curmp = mapybase, xc = xbase, xm = crnr_x; xc < max_xc; xc++, xm += tsize, curmp++) { + drw = static_drw; + if (me_bits_seen(curmp)) + drw |= DRAW_MASK_SEEN; + if (fix_fast_pyth_dist((xc << 8) + 0x80 - sensor_x, (yc << 8) + 0x80 - sensor_y) < amptr->sensor_rad) + drw |= DRAW_MASK_RAD; + + obj_mess(amptr, curmp, drw, xm, ym, tsize, pass); + } + } + + // really should be in a "track_player" mode, not always... + draw_full_obj(amptr, PLAYER_OBJ, RED_BASE + 3, zeroscrx, zeroscry); + if ((amptr->flags & AMAP_TRACK_OBJ) && (amptr->obj_to_follow != PLAYER_OBJ)) + draw_full_obj(amptr, amptr->obj_to_follow, PURPLE_8_BASE + 3, zeroscrx, zeroscry); +#ifdef AMAP_SENS_CIRCLE + if (drw & DRAW_MASK_SENS) { + int r = amptr->sensor_rad; + // accound for needing to see the center of square and + // the fact that we're an octagon, not a circle. + r = (r * 89) / 100 - (1 << 8) * 707 / 1000; + draw_radius_obj(amptr, PLAYER_OBJ, GRAY_8_BASE, zeroscrx, zeroscry, r); + r = amptr->sensor_rad + (1 << 8) * 707 / 1000; + draw_radius_obj(amptr, PLAYER_OBJ, GRAY_8_BASE + 4, zeroscrx, zeroscry, r); + } +#endif +} + +// x and y are window relative, return the map square? +// returns NULL for not in map, else a mapelem * +// this can be checked for mapnotes, or one can be added +// functions to do these things exist as well +// Note side effects: can set amptr->xf and amptr->yf +// x and y set to map square coordinates on exit. +void *amap_loc_to_sq(curAMap *amptr, int *x, int *y) { + int offsx, offsy; + MapElem *curmp = MAP_MAP; + fix tmpy; + + if (amptr->flags & AMAP_TRACK_OBJ) { + amptr->xf = objs[amptr->obj_to_follow].loc.x << 8; + amptr->yf = fix_make(MAP_YSIZE, 0) - 1 - (objs[amptr->obj_to_follow].loc.y << 8); + } + tmpy = fix_make(MAP_YSIZE, 0) - 1 - amptr->yf; + + offsx = (*x) - (amptr->lw >> 1) + fix_int(amptr->xf << amptr->zoom); + offsy = -*y + pix_to_coor(amptr->lh >> 1) + fix_int(tmpy << amptr->zoom); + + offsx >>= amptr->zoom; + offsy >>= amptr->zoom; + *x = offsx; + *y = offsy; // sneaky sneaky + if ((offsx & (~((1 << MAP_XSHF) - 1))) | (offsy & (~((1 << MAP_YSHF) - 1)))) + return NULL; + else + return (void *)(curmp + offsx + (offsy << MAP_XSHF)); +} + +// Returns ObjID of note or OBJ_NULL if none is there +ObjID amap_loc_get_note(void *map_sq) { + ObjID cobjid; + ObjRefID curORef; + MapElem *curmp = (MapElem *)map_sq; + + curORef = curmp->objRef; + while (curORef != OBJ_REF_NULL) { + cobjid = objRefs[curORef].obj; + if (CitrefCheckHomeSq(curORef)) + if (ID2TRIP(cobjid) == MAPNOTE_TRIPLE) + return cobjid; + curORef = objRefs[curORef].next; + } // end of ORef loop + return OBJ_NULL; +} + +#define MAP_LOOK_AROUND +// sets to_do to AMAP_OFF_MAP, AMAP_HAVE_NOTE, AMAP_NO_NOTE +// returns OBJ_NULL if NO_NOTE or OFF_MAP +// returns Obj of the map_note if HAVE_NOTE +ObjID amap_loc_note_check(curAMap *amptr, void *curmp, int *x, int *y, int *to_do) { + ObjID map_note; + if (curmp == NULL) { + *to_do = AMAP_OFF_MAP; + return OBJ_NULL; + } +#ifdef MAP_LOOK_AROUND + map_note = amap_loc_get_note(curmp); + if (map_note == OBJ_NULL) { // check around, in the traditional way - big zoom = small map + int extloop, inloop, clen, dvec[2] = {0, 1}, rad = 2 * (3 - amptr->zoom), mx = *x, my = *y; + // mprintf("Looking around %d from %x %x dv %d %d\n",rad,mx,my,dvec[0],dvec[1]); + for (clen = 1; clen < rad; clen++) // for each radius + for (extloop = 0; extloop < 2; extloop++) // two of each + { + for (inloop = 0; inloop < clen; inloop++) { + mx += dvec[0]; + my += dvec[1]; + if (((mx >= 0) && (mx < MAP_XSIZE)) && ((my >= 0) && (my < MAP_YSIZE))) { + map_note = amap_loc_get_note(MAP_GET_XY(mx, my)); + if (map_note != OBJ_NULL) { + *x = mx; + *y = my; + goto hack_breakout; + } + } + } + if (dvec[0] != 0) { + dvec[1] = -dvec[0]; + dvec[0] = 0; + } else { + dvec[0] = dvec[1]; + dvec[1] = 0; + } + } + } +hack_breakout: +#else + map_note = amap_loc_get_note(curmp); +#endif + if (map_note != OBJ_NULL) { // a note is there... + *to_do = AMAP_HAVE_NOTE; + return map_note; + } else { + *to_do = AMAP_NO_NOTE; + return OBJ_NULL; + } +} + +uchar amap_flags(curAMap *amptr, int flags, int set) { + flags &= amptr->avail_flags; + if (flags == 0) + return FALSE; + switch (set) { + case AMAP_SET: + amptr->flags |= flags; + break; + case AMAP_UNSET: + amptr->flags &= ~flags; + break; + case AMAP_TOGGLE: + if (amptr->flags & flags) + amptr->flags &= ~flags; + else + amptr->flags |= flags; + break; + } + return TRUE; +} + +uchar amap_zoom(curAMap *amptr, uchar set, int zoom_delta) { + if (set) + amptr->zoom = zoom_delta; + else { + if (zoom_delta > 0) { + if (amptr->zoom + zoom_delta >= AMAP_MAX_ZOOM) + return FALSE; + } else { + if (amptr->zoom + zoom_delta < AMAP_MIN_ZOOM) + return FALSE; + } + amptr->zoom += zoom_delta; + } + return TRUE; +} + +void amap_pan(curAMap *amptr, int dir, int *dist) { + int d; + d = *dist >> amptr->zoom; + *dist -= d << amptr->zoom; + switch (dir) { + case AMAP_PAN_E: + amptr->xf += d; + break; + case AMAP_PAN_W: + amptr->xf -= d; + break; + case AMAP_PAN_N: + amptr->yf -= d; + break; + case AMAP_PAN_S: + amptr->yf += d; + break; + } + amptr->flags &= ~AMAP_TRACK_OBJ; +} + +void *amap_deal_with_map_click(curAMap *amptr, int *x, int *y) { + int todo; + // actually clicked on the map, we should deal... + MapElem *curmp = (MapElem *)amap_loc_to_sq(amptr, x, y); + ObjID note = amap_loc_note_check(amptr, curmp, x, y, &todo); + switch (todo) { + case AMAP_NO_NOTE: + amptr->note_obj = OBJ_NULL; + return curmp; + case AMAP_HAVE_NOTE: + amptr->note_obj = note; + return curmp; + } + // case AMAP_OFF_MAP: return NULL; + return NULL; +} + +// string hacks +char amap_strings[AMAP_STRING_SIZE]; +char *amap_str_ptr = &amap_strings[0]; + +char *amap_str_next(void) { return amap_str_ptr; } + +void amap_str_grab(char *str) { amap_str_ptr = str + strlen(str) + 1; } + +int amap_str_deref(char *str) { + return str - &amap_strings[0]; // get offset into string cluster +} + +char *amap_str_reref(int offs) { + return (&amap_strings[0]) + offs; // go offset into string cluster +} + +void amap_str_startup(int magic_num) { amap_str_ptr = &amap_strings[0] + magic_num; } + +void amap_fixup_existing(int tolera, int delta) { + ObjSpecID pmo; + ObjID cur_obj; + for (pmo = objTraps[OBJ_SPEC_NULL].id; pmo != OBJ_SPEC_NULL; pmo = objTraps[pmo].next) { + cur_obj = objTraps[pmo].id; + if (ID2TRIP(cur_obj) == MAPNOTE_TRIPLE) + if (amap_note_value(cur_obj) > tolera) + amap_note_value(cur_obj) -= delta; + } +} + +// simply recompact +void amap_str_delete(char *toast_str) { + int del_len = strlen(toast_str) + 1; // how much to delete + char *s = toast_str + del_len; // beginning of real data + int recompact_len = amap_str_ptr - s; // how much to copy around + if (s == amap_str_ptr) + amap_str_ptr = toast_str; // we are freeing the last created string + else { + LG_memmove(toast_str, s, recompact_len); // move over the data + amap_str_ptr -= del_len; // move the next pointer back + amap_fixup_existing(amap_str_deref(toast_str), del_len); // set current notes up right... + } +} + +uchar amap_get_note(curAMap *amptr, char *buf) { + uchar retval = TRUE; +// later, do this for real +// ie base on the string stuff +#ifdef USE_OBJ + if (amptr->note_obj != 0) { + strcpy(buf, "map note 0000"); + buf[9] = '0' + (((int)amptr->note_obj) / 1000) % 10; + buf[10] = '0' + (((int)amptr->note_obj) / 100) % 10; + buf[11] = '0' + (((int)amptr->note_obj) / 10) % 10; + buf[12] = '0' + (((int)amptr->note_obj) % 10); + } +#else + if (amptr->note_obj != 0) + strcpy(buf, amap_note_string(amptr->note_obj)); +#endif + else { + retval = FALSE; + strcpy(buf, get_temp_string(REF_STR_NoMapMessage)); + } + return retval; +} + +grs_bitmap *screen_automap_bitmap(char c) { + extern grs_bitmap *static_bitmap; + return (static_bitmap); +} diff --git a/engine/src/GameSrc/amaploop.c b/engine/src/GameSrc/amaploop.c new file mode 100644 index 0000000..e567120 --- /dev/null +++ b/engine/src/GameSrc/amaploop.c @@ -0,0 +1,863 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/amaploop.c $ + * $Revision: 1.42 $ + * $Author: tjs $ + * $Date: 1994/11/20 18:02:29 $ + * + * full screen map stuff + */ + +#include + +#include "ShockBitmap.h" + +#include "amaploop.h" +#include "audiolog.h" +#include "criterr.h" +#include "cybstrng.h" +#include "faketime.h" +#include "frflags.h" +#include "gamescr.h" +#include "gamestrn.h" +#include "gr2ss.h" +#include "lvldata.h" +#include "mainloop.h" +#include "mfdfunc.h" +#include "musicai.h" +#include "objgame.h" +#include "objsim.h" +#include "otrip.h" +#include "player.h" +#include "rcolors.h" +#include "wares.h" + +// octant-wise, that is... +#define NORTH 0 +#define EAST 2 +#define SOUTH 4 +#define WEST 6 + +// room for a message line on the bottom of the screen... + +#define AMAP_BUTTON_WIDTH 92 +#define AMAP_HEADER_HGT 11 +#define AMAP_BORDER 4 +#define AMAP_TOP(h) (AMAP_HEADER_HGT + AMAP_BORDER) +#define AMAP_LFT(w) (AMAP_BORDER) +#define AMAP_BOT(h) (h - 1 - AMAP_BORDER - AMAP_HEADER_HGT) +#define AMAP_RGT(w) (w - 1 - AMAP_BUTTON_WIDTH - AMAP_BORDER) +#define AMAP_HGT(h) (AMAP_BOT(h) - AMAP_TOP(h)) +#define AMAP_WID(w) (AMAP_RGT(w) - AMAP_LFT(w)) + +#define FSMAP_MAX_MSG 40 + +#define MOUSE_UPS (MOUSE_LUP | MOUSE_RUP | MOUSE_CUP) +#define MOUSE_DOWNS (MOUSE_LDOWN | MOUSE_RDOWN | MOUSE_CDOWN) + +// spoofs for calling kb callback to do stuff +#define DO_ZOOMOUT KEY_PGUP +#define DO_ZOOMIN KEY_PGDN +#define DO_RIGHT KEY_RIGHT +#define DO_LEFT KEY_LEFT +#define DO_UP KEY_UP +#define DO_DOWN KEY_DOWN +#define DO_QUIT KEY_ESC +//#define DO_MSG KEY_ENTER +#define DO_CHEAT 'f' +#define DO_SCAN 'r' +#define DO_CRITTER 'c' +#define DO_SECUR 's' +#define DO_RECENTER KEY_HOME +#define DO_FULLMSG 'm' + +#define BTN_RECENTER 2 +#define BTN_FULLMSG 3 +#define BTN_CHEAT 6 +#define BTN_SCAN 6 +#define BTN_CRITTER 5 +#define BTN_SECURE 4 +#define BTN_ZOOMIN 0 +#define BTN_ZOOMOUT 1 +#define BTN_PANREG 8 + +#define BTN_NUM_TOT 9 +#define BTN_NUM_REAL 7 + +#define BTN_TALK 0xdead // hack code for todo +#define BTN_PEND 0xbeef + +// limit framerate while scrolling to avoid flicker? +// should really do something real about flicker anyway. +#define SCROLL_FRATE 90 + +frc *full_map_context; +#define FULLMAP_CANVAS ((grs_canvas *)fr_get_canvas(full_map_context)) +// was full_game_fr_context + +#define FSMAP_OPP 0x8000 +// note this makes the init code 0b00010100000101, or 0x505 +static ushort btn_to_code[] = {DO_ZOOMIN, DO_ZOOMOUT, DO_RECENTER, DO_FULLMSG, DO_SECUR, DO_CRITTER, DO_SCAN, 0}; + +static ushort btn_to_amap[] = { + 0, 0, FSMAP_OPP | AMAP_TRACK_OBJ, AMAP_FULL_MSG, AMAP_SHOW_SEC, AMAP_SHOW_CRIT | AMAP_SHOW_ROB, AMAP_SHOW_SENS, 0}; + +#define NUM_SIDE_BUTTONS 7 + +#define BOTTOM_BUTTONS_INDEX 7 + +// should alloc and dealloc these?, or take them out of some memory pool +static grs_canvas fsmap_actual, fsmap_bregion; +static ushort fsmap_buttons, fsmap_btn_pending; +static uchar bcolor[] = {GREEN_8_BASE + 7, GREEN_8_BASE + 3, GREEN_8_BASE, GREEN_8_BASE + 1}; +static int cur_btn_hgt; +static char *cur_mapnote_base = NULL; +static char *cur_mapnote_ptr = NULL; +static uchar last_msg_ok = TRUE; +static ulong map_scrolltime = 0L; +static int map_scroll_d = 0; +static char map_scroll_code = 0; +static bool map_scroll_clicked = 0; + +uchar pend_check(void); + +// default units per second +// in defiance of Rob, I use the number 13. +#define MAP_SCROLL_SPEED 13 + +#define clear_cur_mapnote() cur_mapnote_base = cur_mapnote_ptr = NULL + +// -------------------- +// INTERNAL PROTOTYPES +// -------------------- +void trail_sp_punt(void); +void fsmap_button_redraw(void); +void fsmap_interface_draw(void); +void fsmap_message_redraw(void); +void fsmap_draw_map(void); +void fsmap_draw_screen(uint chng); +int s_bf(int btn_id, int todo); +void fsmap_new_msg(curAMap *amptr); +void edit_mapnote(curAMap *amptr); +uchar zoom_deal(curAMap *amptr, int btn); +uchar flags_deal(curAMap *amptr, int btn, int todo); +void btn_init(curAMap *amptr); + +// The devil drives a Buick +// He sits inside and eats lunch +// Then he sticks his pitchfork through the trunk and into the spare +// and he pull out True Love + +void fsmap_startup(void) { + int i, n = 0, f, b, todo; + grs_font *fsmap_font; + + // Do appropriate stuff to enter into amap mode here.... + automap_init(player_struct.hardwarez[HARDWARE_AUTOMAP], MFD_FULLSCR_MAP); + f = oAMap(MFD_FULLSCR_MAP)->flags; + oAMap(MFD_FULLSCR_MAP)->flags = 0; + oAMap(MFD_FULLSCR_MAP)->zoom = 3; // KLC - added for 640x480 Mac fullscreen map + for (i = 0; i < NUM_MFDS; i++) { + if (oAMap(i)) { + oAMap(MFD_FULLSCR_MAP)->flags |= oAMap(i)->flags; + n++; + } + } + if ((n == 0) || (oAMap(MFD_FULLSCR_MAP)->flags == 0)) + oAMap(MFD_FULLSCR_MAP)->flags = f; + + // Get the graphics system setup for fullscreen drawing. + full_map_context = + fr_place_view(FR_NEWVIEW, FR_DEFCAM, offscreenDrawSurface->pixels, FR_DOUBLEB_MASK | FR_WINDOWD_MASK, 0, 0, 0, + 0, grd_screen_canvas->bm.w, grd_screen_canvas->bm.h); + gr_set_canvas(FULLMAP_CANVAS); + gr_clear(0xff); + amap_pixratio_set(FIX_UNIT); + fsmap_font = (grs_font *)ResLock(RES_largeTechFont); // KLC - was RES_mfdFont + gr_set_font(fsmap_font); + gr_init_sub_canvas(FULLMAP_CANVAS, &fsmap_actual, AMAP_LFT(grd_bm.w), AMAP_TOP(grd_bm.h), AMAP_WID(grd_bm.w), + AMAP_HGT(grd_bm.h)); + gr_init_sub_canvas(FULLMAP_CANVAS, &fsmap_bregion, AMAP_RGT(grd_bm.w) + AMAP_BORDER, AMAP_TOP(grd_bm.h), + AMAP_BUTTON_WIDTH - 2 * AMAP_BORDER, AMAP_HGT(grd_bm.h)); + fsmap_actual.gc.font = fsmap_font; + fsmap_bregion.gc.font = fsmap_font; + fsmap_buttons = (1 << (2 * BTN_ZOOMIN)) | (1 << (2 * BTN_ZOOMOUT)); + for (i = 0; i < NUM_SIDE_BUTTONS; i++) { + b = btn_to_amap[i]; + if (b & oAMap(MFD_FULLSCR_MAP)->flags) { + if (i == BTN_RECENTER) + todo = AMAP_UNSET; + else + todo = AMAP_SET; + s_bf(i, todo); + pend_check(); + } + } + fsmap_btn_pending = 0; + // KLC - seems to be causing a problem btn_init(oAMap(MFD_FULLSCR_MAP)); + + cur_btn_hgt = (((AMAP_HGT(grd_bm.h)) * 3) >> 5); + + chg_set_flg(LL_CHG_MASK); +} + +void fsmap_free(void) { + int i; + for (i = 0; i < NUM_MFDS; i++) { + oAMap(i)->flags = oAMap(MFD_FULLSCR_MAP)->flags; + oAMap(i)->flags |= AMAP_TRACK_OBJ; + } + ResUnlock(RES_largeTechFont); // KLC - was RES_mfdFont + gr_set_canvas(grd_screen_canvas); +} + +void trail_sp_punt(void) { + if (cur_mapnote_ptr == NULL) + return; + if (*cur_mapnote_ptr == '\0') { + while (cur_mapnote_ptr > cur_mapnote_base) + if ((*(cur_mapnote_ptr - 1)) != ' ') + break; + else + cur_mapnote_ptr--; + *cur_mapnote_ptr = '\0'; + } +} + +#define BTN_HGT_MUL (cur_btn_hgt) +#define GET_BTN_TOP(x) ((x)*BTN_HGT_MUL) +#define GET_BTN_BOT(x) (GET_BTN_TOP(x + 1) - AMAP_BORDER) +#define BUTTON_BUF_SIZE 10 +#define AMAP_BUTTON_BASE REF_STR_AutomapButtons + +void fsmap_button_redraw(void) { + int i, cb, bsx, bsy, cx, cy; + char button_buf[BUTTON_BUF_SIZE]; + + gr_push_canvas(&fsmap_bregion); + for (cb = fsmap_buttons, i = 0; i < NUM_SIDE_BUTTONS; i++, cb >>= 2) { + gr_set_fcolor(bcolor[0]); + ss_box(AMAP_BORDER, GET_BTN_TOP(i), grd_bm.w - AMAP_BORDER + 3, GET_BTN_BOT(i)); + gr_set_fcolor(bcolor[cb & 3]); + ss_string(get_string(AMAP_BUTTON_BASE + i, button_buf, BUTTON_BUF_SIZE), 2 * AMAP_BORDER, + GET_BTN_TOP(i) + AMAP_BORDER + 10); + } + + i = BOTTOM_BUTTONS_INDEX; + + gr_set_fcolor(bcolor[0]); + + // draw the pan controller... or have mini map... + cx = AMAP_BORDER + 1; + cy = GET_BTN_TOP(i); + bsx = grd_bm.w - AMAP_BORDER + 3; + bsy = grd_bm.h - BTN_HGT_MUL - AMAP_BORDER; + + ss_box(cx, cy, bsx, bsy); + bsx -= cx; + bsy -= cy; + + ss_int_line(cx, cy, cx + bsx - 1, cy + bsy - 1); + ss_int_line(cx + bsx - 1, cy, cx, cy + bsy - 1); + ss_string(get_temp_string(REF_STR_DirectionAbbrev + NORTH), cx + (bsx >> 1) - 3, cy + (bsy >> 2) - 3); + ss_string(get_temp_string(REF_STR_DirectionAbbrev + EAST), cx + (bsx >> 1) + (bsx >> 2) - 2 + AMAP_BORDER, + cy + (bsy >> 1) - 3); + ss_string(get_temp_string(REF_STR_DirectionAbbrev + SOUTH), cx + (bsx >> 1) - 3, cy + bsy - (bsy >> 2) - 3); + gr_string(get_temp_string(REF_STR_DirectionAbbrev + WEST), cx + (bsx >> 3) - 2 + AMAP_BORDER, cy + (bsy >> 1) - 3); + + // done button + ss_box(AMAP_BORDER, grd_bm.h - BTN_HGT_MUL, grd_bm.w - AMAP_BORDER + 3, grd_bm.h); + gr_set_fcolor(bcolor[cb & 3]); + ss_string(get_string(AMAP_BUTTON_BASE + i, button_buf, BUTTON_BUF_SIZE), 2 * AMAP_BORDER, + (grd_bm.h) - BTN_HGT_MUL + AMAP_BORDER + 12); + + gr_pop_canvas(); + chg_unset_flg(AMAP_BUTTON_EV); +} + +char *fsmap_get_lev_str(char *buf, int siz) { + int l; + + get_string(REF_STR_Level, buf, siz); + l = strlen(buf); + if (l + 3 < siz) { + buf[l] = ' '; + level_to_floor(player_struct.level, buf + l + 1); + } + return buf; +} + +#define TRIOP_BUF_SIZE 50 +#define TRIOP_STRING_BASE REF_STR_AutomapSpew +void fsmap_interface_draw(void) { + char buf[TRIOP_BUF_SIZE]; + gr_set_fcolor(GREEN_8_BASE); + ss_box(0, 0, grd_bm.w - 1, grd_bm.h - 1); + ss_box(AMAP_LFT(grd_bm.w) - 1, AMAP_TOP(grd_bm.h) - 1, AMAP_RGT(grd_bm.w) + 1, AMAP_BOT(grd_bm.h) + 1); + + gr_set_fcolor(RED_8_BASE); + ss_string(get_string(TRIOP_STRING_BASE, buf, TRIOP_BUF_SIZE), AMAP_LFT(grd_bm.w), AMAP_BORDER); + + ss_string(fsmap_get_lev_str(buf, TRIOP_BUF_SIZE), AMAP_RGT(grd_bm.w) + 2 * AMAP_BORDER, AMAP_BORDER); + + chg_unset_flg(AMAP_FULLEXPOSE); +} + +#define MSG_BUF2_SIZE 25 +void fsmap_message_redraw(void) { + char buf[FSMAP_MAX_MSG]; + char buf2[MSG_BUF2_SIZE]; + short x, y, w, dummy; + + gr_set_font(fsmap_actual.gc.font); // so we dont need this as another global, ick + gr_set_fcolor(0xFF); // and someone keeps secretly reseting the font to ickiness + ss_rect(AMAP_LFT(grd_bm.w), AMAP_BOT(grd_bm.h) + 1 + 2, grd_bm.w - 2, grd_bm.h - 2); // -2 to miss the border + x = AMAP_LFT(grd_bm.w); + y = AMAP_BOT(grd_bm.h) + 1 + 2; + if (last_msg_ok) { + if (cur_mapnote_base == NULL) { + gr_set_fcolor(GREEN_8_BASE + 5); + } else { + gr_set_fcolor(RED_8_BASE + 2); + strcpy(buf, "> "); + gr_string_size(buf, &w, &dummy); + ss_string(buf, x, y); + ss_string(buf, x + 1, y); + x += w + 1; + } + amap_get_note(oAMap(MFD_FULLSCR_MAP), buf); + ss_string(buf, x, y); // +1 to get out of box, 2 for pad? + } else { + gr_set_fcolor(RED_8_BASE + 4); + ss_string(get_string(REF_STR_NoMessage, buf2, MSG_BUF2_SIZE), AMAP_LFT(grd_bm.w), AMAP_BOT(grd_bm.h) + 1 + 2); + } + if (cur_mapnote_base != NULL) { + gr_set_fcolor(PULSE_RED); + ss_vline(x + gr_string_width(buf) + 2, AMAP_BOT(grd_bm.h) + 3, AMAP_BOT(grd_bm.h) + 8); + ss_vline(x + gr_string_width(buf) + 2 + 1, AMAP_BOT(grd_bm.h) + 3, AMAP_BOT(grd_bm.h) + 8); + } + chg_unset_flg(AMAP_MESSAGE_EV); +} + +void fsmap_draw_map(void) { + FrameDesc *f; + // short w,h; + + // w=res_bm_width(REF_IMG_bmTriLogoBack); + // h=res_bm_height(REF_IMG_bmTriLogoBack); + gr_push_canvas(&fsmap_actual); + gr_clear(0xff); + + // KLC - changed to draw the background logo double size + // draw_res_bm(REF_IMG_bmTriLogoBack,(grd_bm.w-w)/2,(grd_bm.h-h)/2); + f = RefLock(REF_IMG_bmTriLogoBack); + if (f == NULL) + critical_error(CRITERR_MEM | 9); + f->bm.bits = (uchar *)(f + 1); + gr_scale_bitmap(&f->bm, (grd_bm.w - (f->bm.w * 2)) / 2, (grd_bm.h - (f->bm.h * 2)) / 2, f->bm.w * 2, f->bm.h * 2); + RefUnlock(REF_IMG_bmTriLogoBack); + + amap_draw(oAMap(MFD_FULLSCR_MAP), 0); + gr_pop_canvas(); + chg_unset_flg(AMAP_MAP_EV); +} + +#define AMAP_ALLEVS (AMAP_FULLEXPOSE | AMAP_MAP_EV | AMAP_BUTTON_EV | AMAP_MESSAGE_EV) + +void fsmap_draw_screen(uint chng) { + int l, t, r, b; + LGRect cr; + + gr_push_canvas(grd_screen_canvas); + if ((chng & AMAP_ALLEVS) == AMAP_MAP_EV) { + cr.ul.x = AMAP_LFT(grd_bm.w); + cr.ul.y = AMAP_TOP(grd_bm.h); + cr.lr.x = AMAP_LFT(grd_bm.w) + AMAP_WID(grd_bm.w); + cr.lr.y = AMAP_TOP(grd_bm.h) + AMAP_HGT(grd_bm.h); + gr_get_cliprect(&l, &t, &r, &b); + ss_safe_set_cliprect(cr.ul.x, cr.ul.y, cr.lr.x, cr.lr.y); + uiHideMouse(&cr); + ss_bitmap(&(FULLMAP_CANVAS->bm), 0, 0); + uiShowMouse(&cr); + ss_safe_set_cliprect(l, t, r, b); + } else { + uiHideMouse(NULL); + ss_bitmap(&(FULLMAP_CANVAS->bm), 0, 0); + uiShowMouse(NULL); + } + gr_pop_canvas(); +} + +void automap_loop(void) { + uint cf = _change_flag; + + // KLC - does nothing loopLine(GL|0x1D,synchronous_update()); + if (music_on) + loopLine(GL | 0x1C, mlimbs_do_ai()); + if (localChanges) { + if (_change_flag & AMAP_FULLEXPOSE) { + loopLine(AL | 0x1, fsmap_interface_draw()); + } + if (_change_flag & AMAP_MAP_EV) { + loopLine(AL | 0x2, fsmap_draw_map()); + } + if (_change_flag & AMAP_BUTTON_EV) { + loopLine(AL | 0x3, fsmap_button_redraw()); + } + if (_change_flag & AMAP_MESSAGE_EV) { + loopLine(AL | 0x4, fsmap_message_redraw()); + } + } + // if (pal_fx_on) { + // loopLine(AL|0x41,palette_advance_all_fx(*tmd_ticks)); + // } + audiolog_loop_callback(); + if (cf & (AMAP_FULLEXPOSE | AMAP_MAP_EV | AMAP_BUTTON_EV | AMAP_MESSAGE_EV)) { + fsmap_draw_screen(cf); + } +} + +int s_bf(int btn_id, int todo) { + int prtlmask = 1 << (btn_id << 1); + int fullmask = prtlmask + (prtlmask << 1); + switch (todo) { + case AMAP_TOGGLE: + fsmap_buttons = (fsmap_buttons & ~fullmask) + ((fsmap_buttons & fullmask) ^ prtlmask); + break; // xor on/off + case AMAP_SET: + fsmap_buttons = (fsmap_buttons & ~fullmask) + prtlmask; + break; // set on/off to on + case AMAP_UNSET: + fsmap_buttons = (fsmap_buttons & ~fullmask); + break; // set on/off to off + case BTN_TALK: + return fsmap_buttons & prtlmask; // return whether this is on or off + case BTN_PEND: + break; + } + fsmap_btn_pending |= (prtlmask << 1); + fsmap_buttons |= fsmap_btn_pending; + chg_set_flg(AMAP_BUTTON_EV); + return fsmap_buttons & fullmask; // heck, why not +} + +void fsmap_new_msg(curAMap *amptr) { + cur_mapnote_base = amap_str_next(); + amap_note_value(amptr->note_obj) = amap_str_deref(cur_mapnote_base); + cur_mapnote_ptr = cur_mapnote_base; + *cur_mapnote_ptr = '\0'; + chg_set_flg(AMAP_MAP_EV); +} + +uchar pend_check(void) { + if (fsmap_btn_pending) { + // mprintf("UnPend %x (c %x)...",fsmap_btn_pending,fsmap_buttons); + fsmap_buttons &= ~fsmap_btn_pending; + fsmap_btn_pending = 0; + chg_set_flg(AMAP_BUTTON_EV); + // mprintf("done %x\n",fsmap_buttons); + return TRUE; + } + return FALSE; +} + +#define hack_kb_callback(am, k) amap_kb_callback(am, k | KB_FLAG_DOWN) + +#define UP_ARROW_CODE 0x7E +#define DOWN_ARROW_CODE 0x7D +#define LEFT_ARROW_CODE 0x7B +#define RIGHT_ARROW_CODE 0x7C + +#define KP_UP_CODE 0x5B +#define KP_DOWN_CODE 0x54 +#define KP_LEFT_CODE 0x56 +#define KP_RIGHT_CODE 0x58 + +uchar amap_scroll_handler(uiEvent *ev, LGRegion *reg, intptr_t v) { + int elapsed, now; + short code; + curAMap *amptr = oAMap(MFD_FULLSCR_MAP); + + if (!map_scroll_code || !map_scroll_clicked) { + if (ev->type == UI_EVENT_KBD_POLL) { + code = ev->raw_key_data.scancode; + switch (code) { + case UP_ARROW_CODE: + case KP_UP_CODE: + map_scroll_code = AMAP_PAN_N; + break; + case DOWN_ARROW_CODE: + case KP_DOWN_CODE: + map_scroll_code = AMAP_PAN_S; + break; + case LEFT_ARROW_CODE: + case KP_LEFT_CODE: + map_scroll_code = AMAP_PAN_W; + break; + case RIGHT_ARROW_CODE: + case KP_RIGHT_CODE: + map_scroll_code = AMAP_PAN_E; + break; + } + } + } + + if (map_scroll_code == 0) + return FALSE; + + now = *tmd_ticks; + elapsed = now - map_scrolltime; + if (elapsed < (CIT_CYCLE / SCROLL_FRATE)) + return TRUE; + if (elapsed > 10) elapsed = 10; //prevent jumping of map + map_scrolltime = now; + if (map_scroll_clicked) elapsed *= 10; //clicking nswe acts like a longer elapsed time + map_scroll_d += (elapsed * MAP_SCROLL_SPEED * AMAP_DEF_DST) / CIT_CYCLE; + amap_pan(amptr, map_scroll_code, &map_scroll_d); + s_bf(BTN_RECENTER, AMAP_SET); + pend_check(); + chg_set_flg(AMAP_MAP_EV); + + map_scroll_code = 0; + map_scroll_clicked = FALSE; + + return TRUE; +} + +void edit_mapnote(curAMap *amptr) { + if ((cur_mapnote_ptr == NULL) && (amptr->note_obj)) { + char buf[FSMAP_MAX_MSG]; + strcpy(buf, amap_note_string(amptr->note_obj)); + amap_str_delete(amap_note_string(amptr->note_obj)); + fsmap_new_msg(amptr); + strcpy(cur_mapnote_base, buf); + cur_mapnote_ptr = cur_mapnote_base + strlen(cur_mapnote_base); + chg_set_flg(AMAP_MESSAGE_EV); + } +} + +uchar amap_ms_callback(curAMap *amptr, int x, int y, short action, ubyte b) { + int scregion = 0; + + if (action & (MOUSE_WHEELUP | MOUSE_WHEELDN)) { + if (zoom_deal(amptr, action & MOUSE_WHEELUP ? BTN_ZOOMIN : BTN_ZOOMOUT)) + chg_set_flg(AMAP_MAP_EV); + pend_check(); + return TRUE; + } + + if (action & MOUSE_UPS) { + mouse_unconstrain(); + pend_check(); + return TRUE; + } + + if (!(action & MOUSE_DOWNS)) + return FALSE; + + ui_mouse_constrain_xy(x, y, x, y); + + y -= AMAP_TOP(grd_bm.h); + + if (y > AMAP_HGT(grd_bm.h)) // if click in the message line + { + scregion = AMAP_MESSAGE_EV; + edit_mapnote(amptr); // start editing. + } + + else if (x > AMAP_RGT(grd_bm.w)) // If in the button regionÉ + { + if (y < GET_BTN_TOP(7) - AMAP_BORDER) // If in one of the upper buttons (above pan) + { + // KLC - changed to treat clicks and keyboard equivalents a little differently + // in the Mac version. Doesn't call the hack_kb_callback anymore. + + int btn = y / BTN_HGT_MUL; + char todo = AMAP_TOGGLE; + + if (btn == BTN_ZOOMIN || btn == BTN_ZOOMOUT) { + if (zoom_deal(amptr, btn)) + chg_set_flg(AMAP_MAP_EV); + } else { + if (btn == BTN_RECENTER) + todo = AMAP_UNSET; + if (flags_deal(amptr, btn, todo)) + chg_set_flg(AMAP_MAP_EV); + } + } + + else if (y > AMAP_HGT(grd_bm.h) - BTN_HGT_MUL) // If in the "Done" button + { + if (y < AMAP_HGT(grd_bm.h)) // quit automap... that was the done button kids. + hack_kb_callback(amptr, DO_QUIT); + } + + else // Else we must be in the pan region + { + x -= AMAP_RGT(grd_bm.w) + 2 * AMAP_BORDER + 1; // normalize to middle of pan region + x -= 38; + x *= 5; + y -= GET_BTN_TOP(7); + y -= 90; + y *= 2; + + if ((abs(abs(x) - abs(y))) < 3) + return TRUE; // null pan area... + if (abs(x) > abs(y)) // ew + if (x > 0) + map_scroll_code = AMAP_PAN_E; + else + map_scroll_code = AMAP_PAN_W; + else if (y > 0) + map_scroll_code = AMAP_PAN_S; + else + map_scroll_code = AMAP_PAN_N; + map_scroll_clicked = TRUE; + } + } else { + void *deal_data; + ObjID prev_note; + + scregion = AMAP_MAP_EV; + x -= AMAP_LFT(grd_bm.w); + if ((x <= 0) || (y <= 0)) + return TRUE; // not really in map + prev_note = amptr->note_obj; + // will be NULL if clicked outside map + if ((deal_data = amap_deal_with_map_click(amptr, &x, &y)) != NULL) { + if (amptr->note_obj != prev_note) { + trail_sp_punt(); + if (cur_mapnote_base != NULL) { + if (*cur_mapnote_base == '\0') { + obj_destroy(prev_note); + } else + amap_str_grab(cur_mapnote_base); + } + clear_cur_mapnote(); + } + last_msg_ok = TRUE; + chg_set_flg(AMAP_MESSAGE_EV | AMAP_MAP_EV); + if (amptr->note_obj == OBJ_NULL) { + trail_sp_punt(); + if ((amptr->note_obj = object_place(MAPNOTE_TRIPLE, MakePoint(x, y))) != OBJ_NULL) + fsmap_new_msg(amptr); + else { + last_msg_ok = FALSE; + clear_cur_mapnote(); + WARN("%s: No more mapnote space", __FUNCTION__); + } + } + // if we want to do things when you click on old map notes... + // else + // { + // mprintf("Clicked on old map note\n"); + // } + } else { + last_msg_ok = FALSE; + chg_set_flg(AMAP_MESSAGE_EV); + } + } + if (scregion != AMAP_MAP_EV && scregion != AMAP_MESSAGE_EV && amptr->note_obj != OBJ_NULL) { + trail_sp_punt(); + if (cur_mapnote_base != NULL) { + if (*cur_mapnote_base == '\0') { + obj_destroy(amptr->note_obj); + chg_set_flg(AMAP_MAP_EV); + } else + amap_str_grab(cur_mapnote_base); + } + clear_cur_mapnote(); + last_msg_ok = TRUE; + chg_set_flg(AMAP_MESSAGE_EV); + } + return TRUE; +} + +uchar zoom_deal(curAMap *amptr, int btn) { + int zfac; + uchar res; + // now, set up for real + if (btn == BTN_ZOOMIN) + zfac = 1; + else + zfac = -1; + res = amap_zoom(amptr, FALSE, zfac); + if (res) { + if (amptr->zoom + 1 >= AMAP_MAX_ZOOM) + s_bf(BTN_ZOOMIN, AMAP_UNSET); + else if (amptr->zoom <= AMAP_MIN_ZOOM) + s_bf(BTN_ZOOMOUT, AMAP_UNSET); + if ((btn == BTN_ZOOMIN) && (s_bf(BTN_ZOOMOUT, BTN_TALK) == 0)) + s_bf(BTN_ZOOMOUT, AMAP_SET); + if ((btn == BTN_ZOOMOUT) && (s_bf(BTN_ZOOMIN, BTN_TALK) == 0)) + s_bf(BTN_ZOOMIN, AMAP_SET); + } + s_bf(btn, BTN_PEND); + return res; +} + +uchar flags_deal(curAMap *amptr, int btn, int todo) { + short flgs = btn_to_amap[btn]; + int todo_bf = todo; + uchar res; + + // mprintf("Think deal with %x - %d\n",flgs,todo); + if (flgs == 0) + return FALSE; + + if (flgs & FSMAP_OPP) { + switch (todo) { + case AMAP_UNSET: + todo_bf = AMAP_SET; + break; + case AMAP_SET: + todo_bf = AMAP_UNSET; + break; + } + } + res = amap_flags(amptr, flgs, todo_bf); + if (res) + s_bf(btn, todo); + return res; +} + +void btn_init(curAMap *amptr) { + int i, j; + for (i = 3, j = (1 << (3 * 2)); i < BTN_NUM_REAL; i++, j <<= 2) + if (fsmap_buttons & j) { + if (btn_to_amap[i] != 0) + if (!flags_deal(amptr, i, AMAP_SET)) + fsmap_buttons &= ~j; + } else + flags_deal(amptr, i, AMAP_UNSET); +} + +uchar amap_kb_callback(curAMap *amptr, int code) { + // char codewas; + int exp = 0xff; // will get zeroed in case default for codes we ignore + + /*KLC - we're just forgetting most of the key equivalents for now + + if (code & KB_FLAG_DOWN) //KLC - we'll process on key down. + { + codewas=map_scroll_code; + map_scroll_code=0; + if(pend_check()) return TRUE; + if(!(codewas==0)) return TRUE; + } + */ + + // If we're currently editing a message... + + if (cur_mapnote_ptr != NULL) { + if (!(code & KB_FLAG_DOWN)) + return TRUE; + code = kb2ascii(code); + // KLC if ((code==KEY_ENTER)||(code==KEY_DEL)) + if (code == KEY_ENTER) // If we've pressed Enter + { + if (code == KEY_ENTER) { + trail_sp_punt(); // clear out any trailing spaces + if (amptr->flags & AMAP_FULL_MSG) // switch out of editing loop + chg_set_flg(AMAP_MAP_EV); + } + + // KLC if ((code==KEY_DEL)||(cur_mapnote_ptr==cur_mapnote_base)) + if (cur_mapnote_ptr == cur_mapnote_base) // If the map note string is empty + { // then delete the map note + obj_destroy(amptr->note_obj); + amptr->note_obj = 0; + chg_set_flg(AMAP_MAP_EV); + } else + amap_str_grab(cur_mapnote_base); + clear_cur_mapnote(); + } else if (isprint(code)) { // make sure it isnt too long + int clen = strlen(cur_mapnote_base) + 1; + + if (amap_str_deref(cur_mapnote_base) + clen < AMAP_STRING_SIZE) + if (clen < FSMAP_MAX_MSG) { + if (*cur_mapnote_ptr != '\0') + memcpy(cur_mapnote_ptr + 1, cur_mapnote_ptr, strlen(cur_mapnote_ptr)); + else + *(cur_mapnote_ptr + 1) = '\0'; + *cur_mapnote_ptr++ = (char)code; + } + } else if (code == KEY_BS) { + if (cur_mapnote_ptr > cur_mapnote_base) + *--cur_mapnote_ptr = '\0'; + } else + return FALSE; + chg_set_flg(AMAP_MESSAGE_EV); + if (amptr->flags & AMAP_FULL_MSG) + chg_set_flg(AMAP_MAP_EV); + return TRUE; + } + + // We're not editing. Keyboard equivalents for buttons. + + else { + char btn = -1, todo = AMAP_TOGGLE; + map_scroll_code = 0; + map_scroll_clicked = FALSE; + switch (code & (~KB_FLAG_DOWN)) { + // KLC case KEY_PGUP: case '[': if (!zoom_deal(amptr,BTN_ZOOMOUT)) exp=0; break; + // KLC case KEY_PGDN: case ']': if (!zoom_deal(amptr,BTN_ZOOMIN)) exp=0; break; + // KLC maybe put keyboard scrolling in later + // KLC case KEY_RIGHT: case 'l': map_scroll_code=AMAP_PAN_E; break; + // KLC case KEY_LEFT: case 'j': map_scroll_code=AMAP_PAN_W; break; + // KLC case KEY_UP: case 'i': map_scroll_code=AMAP_PAN_N; break; + // KLC case KEY_DOWN: case 'k': map_scroll_code=AMAP_PAN_S; break; + case KEY_ESC: + case 'q': + _new_mode = _last_mode; + chg_set_flg(GL_CHG_LOOP); + break; + // KLC case KEY_BS: edit_mapnote(amptr); break; + case KEY_BS: + obj_destroy(amptr->note_obj); // Delete the map note + amptr->note_obj = 0; + clear_cur_mapnote(); + last_msg_ok = TRUE; + chg_set_flg(AMAP_MESSAGE_EV | AMAP_MAP_EV); + break; + // KLC case DO_FULLMSG: btn=BTN_FULLMSG; break; + // KLC case DO_RECENTER: btn=BTN_RECENTER; todo=AMAP_UNSET; break; + // KLC case DO_SCAN: btn=BTN_SCAN; break; + // KLC case DO_CRITTER: btn=BTN_CRITTER; break; + // KLC case DO_SECUR: btn=BTN_SECURE; break; + default: + exp = 0; + break; + } + /* KLC - not used any more + if (btn!=-1) + if (!flags_deal(amptr,btn,todo)) exp=0; + if(map_scroll_code!=0) { + map_scrolltime=*tmd_ticks; + s_bf(BTN_RECENTER,AMAP_SET); + } + */ + if (exp) { + chg_set_flg(AMAP_MAP_EV); + return TRUE; + } + return FALSE; + } +} diff --git a/engine/src/GameSrc/ammomfd.c b/engine/src/GameSrc/ammomfd.c new file mode 100644 index 0000000..f6d757b --- /dev/null +++ b/engine/src/GameSrc/ammomfd.c @@ -0,0 +1,230 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/ammomfd.c $ + * $Revision: 1.11 $ + * $Author: xemu $ + * $Date: 1994/10/13 15:38:10 $ + * + * $Log: ammomfd.c $ + * Revision 1.11 1994/10/13 15:38:10 xemu + * SVGA interface + * + * Revision 1.10 1994/08/17 03:07:49 xemu + * dont' be affected by font change + * + * Revision 1.9 1994/06/02 03:39:54 mahk + * Fixes due to dummymfd bug. + * + * Revision 1.8 1994/05/31 18:38:24 tjs + * Fixed running/out/of/rectangles problem. + * + * Revision 1.7 1994/05/24 20:10:25 minman + * got rid of warning message whenever ammo page is drawn + * , + * + * Revision 1.6 1994/05/19 13:41:21 tjs + * Ammo MFD interface revision. + * + * Revision 1.5 1994/05/19 04:01:46 tjs + * Eliminated separate ammo mfd func. + * + * Revision 1.4 1994/05/12 16:11:38 tjs + * use string_replace_char + * use AMMO_TYPE_LETTER for consistency. + * + * Revision 1.3 1994/05/12 11:53:45 tjs + * Actually allocate enough space for "minibuf" + * + * Revision 1.2 1994/05/12 11:51:48 tjs + * Fixed RCS log. + * + * Revision 1.1 1994/05/10 02:52:34 tjs + * Initial revision + * + */ + +#include + +#include "mfdint.h" +#include "mfdext.h" +#include "mfddims.h" +#include "weapons.h" +#include "objsim.h" +#include "objprop.h" +#include "objwpn.h" +#include "gamestrn.h" +#include "objclass.h" +#include "colors.h" +#include "gamescr.h" +#include "cybstrng.h" +#include "tools.h" +#include "fullscrn.h" +#include "gr2ss.h" + +// ============================================================ +// THE AMMO MFD +// ============================================================ + +// ------- +// DEFINES +// ------- + +#define AMMO_LIST_X 3 +#define AMMO_LIST_Y 5 +#define AMMO_COUNT_X 73 + +// color for weapon not owned, weapon owned, selected weapon. +uchar ammo_line_colors[] = {GREEN_BASE + 6, GREEN_BASE + 2, GREEN_YELLOW_BASE + 1}; +#define AMMO_TITLE_COLOR (RED_BASE + 5) + +#define PLAYER_HASNT 0 +#define PLAYER_HAS 1 +#define PLAYER_HAS_SELECTED 2 + +// ---------- +// PROTOTYPES +// ---------- +uchar player_has_weapon(int trip); + +// return 0 if player does not have weapon of this type. +// returns 1 if player does have one, but not selected +// returns 2 if player has one selected. +// +uchar player_has_weapon(int trip) { + int num; + uchar retval = PLAYER_HASNT; + weapon_slot *wp = player_struct.weapons; + + for (num = 0; num < NUM_WEAPON_SLOTS && wp[num].type != EMPTY_WEAPON_SLOT; num++) { + if (MAKETRIP(CLASS_GUN, wp[num].type, wp[num].subtype) == trip) { + if (player_struct.actives[ACTIVE_WEAPON] == num) + retval = PLAYER_HAS_SELECTED; + else if (retval != PLAYER_HAS_SELECTED) + retval = PLAYER_HAS; + } + } + return (retval); +} + +#define HARDWIRED_HEIGHT_CONSTANT 5 +void mfd_ammo_expose(ubyte control) { + uchar full = control & MFD_EXPOSE_FULL; + if (control & MFD_EXPOSE) { + // Time to draw stuff + gr_set_font((grs_font *)ResGet(MFD_FONT)); + + // Clear the canvas by drawing the background bitmap + if (!full_game_3d) + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + + { + int num, guntrip, typemask, type, subc, ammonum, count; + short w, h, ypos = 0, y_list; + uchar col, has; + uchar gotammo; + char ammoline[30], weapline[30], minibuf[3] = " /", *title; + + if (full) { + title = get_temp_string(REF_STR_AmmoMFDWeaps); + mfd_draw_string(title, AMMO_LIST_X, AMMO_LIST_Y + ypos, AMMO_TITLE_COLOR, TRUE); + title = get_temp_string(REF_STR_AmmoMFDClips); + gr_string_size(title, &w, &h); + mfd_draw_string(title, AMMO_COUNT_X - w, AMMO_LIST_Y + ypos, AMMO_TITLE_COLOR, TRUE); + } else { + gr_string_size(minibuf, &w, &h); + } + ypos += HARDWIRED_HEIGHT_CONSTANT; + y_list = ypos + HARDWIRED_HEIGHT_CONSTANT; + + // iterate through gun types + for (num = 0; num < NUM_GUN; num++) { + guntrip = get_triple_from_class_nth_item(CLASS_GUN, num); + typemask = GunProps[CPTRIP(guntrip)].useable_ammo_type; + subc = AMMOTYPE_SUBCLASS(typemask); + typemask = AMMOTYPE_TYPE(typemask); // type type type + + if (typemask != 0) { + gotammo = FALSE; + ammoline[0] = '\0'; + for (type = 0; typemask != 0; typemask = typemask >> 1, type++) { + if (typemask & 0x1) { // that's 1 HEX, mind you + ammonum = get_nth_from_triple(MAKETRIP(CLASS_AMMO, subc, type)); + count = player_struct.cartridges[ammonum]; + gotammo = gotammo || count; + sprintf(ammoline + strlen(ammoline), "%d", count); + minibuf[0] = AMMO_TYPE_LETTER(ammonum); + strcat(ammoline, minibuf[0] == ' ' ? minibuf + 1 : minibuf); + } + } + + has = player_has_weapon(guntrip); + if (has || gotammo) { + get_object_short_name(guntrip, weapline, sizeof(weapline)); + col = ammo_line_colors[has]; + mfd_draw_string(weapline, AMMO_LIST_X, AMMO_LIST_Y + ypos, col, TRUE); + + // get rid of trailing slash + ammoline[strlen(ammoline) - 1] = '\0'; + // shamefully, replace '1' with 'I' to save space onscreen + string_replace_char(ammoline, '1', 'I'); + gr_string_size(ammoline, &w, &h); + mfd_draw_string(ammoline, AMMO_COUNT_X - w, AMMO_LIST_Y + ypos, col, TRUE); + // glue together ammo rects so as not to + // run out of mfd rects, goddamnit. + mfd_add_rect(AMMO_COUNT_X - 1, y_list, AMMO_COUNT_X, AMMO_LIST_Y + ypos); + ypos += HARDWIRED_HEIGHT_CONSTANT; + } + } + } + } + + // on a full expose, make sure to draw everything + + if (full) + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + } +} + +uchar mfd_ammo_handler(MFD *m, uiEvent *ev) { + LGPoint pos; + short w, h, line; + int guntrip; + uchar has; + + if (ev->type != UI_EVENT_MOUSE || !(ev->subtype & MOUSE_LDOWN)) + return FALSE; + + pos = MakePoint(ev->pos.x - m->rect.ul.x, ev->pos.y - m->rect.ul.y); + gr_font_char_size((grs_font *)ResGet(MFD_FONT), 'X', &w, &h); + + line = (pos.y - AMMO_LIST_Y) / h; + if (line <= 0) + return FALSE; + guntrip = get_triple_from_class_nth_item(CLASS_GUN, line - 1); + if (guntrip < 0) + return FALSE; + has = player_has_weapon(guntrip); + if (has == PLAYER_HAS_SELECTED) { + mfd_change_slot(m->id, MFD_WEAPON_SLOT); + return TRUE; + } + return FALSE; +} diff --git a/engine/src/GameSrc/anim.c b/engine/src/GameSrc/anim.c new file mode 100644 index 0000000..d54706f --- /dev/null +++ b/engine/src/GameSrc/anim.c @@ -0,0 +1,153 @@ +/* + +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#include + +#include "Shock.h" +#include "anim.h" +#include "gr2ss.h" +#include "tools.h" +#include "sdl_events.h" + +ActAnim current_anim; +bool done_playing_anim = false; + +// play +void AnimRecur() { + int x, y = 0; + + if (done_playing_anim) + return; + + AnimCodeData *data = ¤t_anim.pah->data[current_anim.curSeq]; + grs_bitmap unpackBM; + + // stop drawing the mouse for now, switch to video canvas + uiHideMouse(NULL); + gr_push_canvas(¤t_anim.cnv); + + // might need to draw a background before the first frame + if (current_anim.frameNum == 0) { + if (current_anim.composeFunc != NULL) + current_anim.composeFunc(current_anim.reg->r, 0x04); + } + + short a, b, c, d; + STORE_CLIP(a, b, c, d); + + // grab this frame + FrameDesc *f = RefLock(current_anim.currFrameRef); + + if (f != NULL) { + f->bm.bits = (uchar *)(f + 1); + f->bm.flags = BMF_TRANS; + + gr_set_cliprect(f->updateArea.ul.x, f->updateArea.ul.y, f->updateArea.lr.x, f->updateArea.lr.y); + gr_rsd8_bitmap((grs_bitmap *)f, 0, 0); + } else { + TRACE("Done playing anim!"); + done_playing_anim = true; + } + + RefUnlock(current_anim.currFrameRef); + + RESTORE_CLIP(a, b, c, d); + gr_pop_canvas(); + + // Draw the scaled up movie + x = current_anim.loc.x; + y = current_anim.loc.y; + ss_bitmap(¤t_anim.cnv.bm, x, y); + + long time = SDL_GetTicks(); + if (time >= current_anim.timeContinue) { + current_anim.currFrameRef++; + current_anim.frameNum++; + current_anim.timeContinue = time + 100; + + if (current_anim.frameNum > data->frameRunEnd + 1) { + current_anim.curSeq++; + } + } + + // safe to draw the mouse again + uiShowMouse(NULL); + + if (done_playing_anim) { + AnimKill(¤t_anim); + } + + // Make SDL happy + pump_events(); + SDLDraw(); +} + +void AnimSetAnimPall(Ref animRef) {} + +bool AnimPreloadFrames(ActAnim *paa, Ref animRef) { return 1; } + +ActAnim *AnimPlayRegion(Ref animRef, LGRegion *region, LGPoint loc, char unknown, + void (*composeFunc)(LGRect *area, ubyte flags)) { + // start playing + DEBUG("Playing animation: %x", animRef); + + AnimHead *head = (AnimHead *)RefGet(animRef); + if (head != NULL) { + TRACE("Animation frames at %x", head->frameSetId); + } + + done_playing_anim = false; + current_anim.reg = region; + current_anim.pah = head; + current_anim.currFrameRef = MKREF(head->frameSetId, 0); + current_anim.curSeq = 0; + current_anim.frameNum = 0; + current_anim.composeFunc = composeFunc; + current_anim.timeContinue = SDL_GetTicks() + 100; + current_anim.loc = loc; + + // Initialize canvas for this animation + grs_bitmap bm; + uchar *bptr = (uchar *)malloc(head->size.x * head->size.y * 2); + + gr_init_bm(&bm, bptr, BMT_FLAT8, BMF_TRANS, head->size.x, head->size.y); + gr_make_canvas(&bm, ¤t_anim.cnv); + gr_push_canvas(¤t_anim.cnv); + gr_clear(0); + gr_pop_canvas(); + + return ¤t_anim; +} + +void AnimSetNotify(ActAnim *paa, void *powner, AnimCode mask, + void (*func)(ActAnim *, AnimCode ancode, AnimCodeData *animData)) { + + // user callback function + paa->notifyFunc = func; +} + +void AnimKill(ActAnim *paa) { + // Stop animation + AnimCodeData data; + + if (current_anim.notifyFunc != NULL) + current_anim.notifyFunc(¤t_anim, ANCODE_KILL, &data); + + free(current_anim.cnv.bm.bits); +} diff --git a/engine/src/GameSrc/archiveformat.c b/engine/src/GameSrc/archiveformat.c new file mode 100644 index 0000000..607fdd4 --- /dev/null +++ b/engine/src/GameSrc/archiveformat.c @@ -0,0 +1,909 @@ +/* + +Copyright (C) 2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#include "archiveformat.h" +#include "effect.h" +#include "lvldata.h" +#include "map.h" +#include "objcrit.h" +#include "objects.h" +#include "objgame.h" +#include "objstuff.h" +#include "objwpn.h" +#include "objwarez.h" +#include "pathfind.h" +#include "schedtyp.h" +#include "schedule.h" +#include "textmaps.h" +#include "trigger.h" + +#define RES_FORMAT(layout) \ + { ResDecode, ResEncode, (UserDecodeData)&layout, NULL } + +const ResLayout U32Layout = { + 4, sizeof(uint32_t), 0, { + { RFFT_UINT32, 0 }, + { RFFT_END, 0 } + } +}; +const ResourceFormat U32Format = RES_FORMAT(U32Layout); + +const ResLayout U16Layout = { + 2, sizeof(uint16_t), 0, { + { RFFT_UINT16, 0 }, + { RFFT_END, 0 } + } +}; + +// Schedule layout. +const ResLayout ScheduleLayout = { + 22, sizeof(Schedule), 0, { + { RFFT_UINT32, offsetof(Schedule, queue.size) }, + { RFFT_UINT32, offsetof(Schedule, queue.fullness) }, + { RFFT_UINT32, offsetof(Schedule, queue.elemsize) }, + { RFFT_UINT8, offsetof(Schedule, queue.grow) }, + { RFFT_PAD, 1 }, // alignment + { RFFT_PAD, 4 }, // pointer member + { RFFT_PAD, 4 }, // pointer member + { RFFT_END, 0 } + } +}; +const ResourceFormat ScheduleFormat = RES_FORMAT(ScheduleLayout); + +// Schedule queue element. This has several possible formats which I'm treating +// as plain binary for the time being. +const ResLayout ScheduleQueueLayout = { + 8, sizeof(SchedEvent), LAYOUT_FLAG_ARRAY, { + { RFFT_UINT16, offsetof(SchedEvent, timestamp) }, + { RFFT_UINT16, offsetof(SchedEvent, type) }, + { RFFT_BIN(SCHED_DATASIZ), offsetof(SchedEvent, data) }, + { RFFT_END, 0 } + } +}; +const ResourceFormat ScheduleQueueFormat = RES_FORMAT(ScheduleQueueLayout); + +// Describe the layout of the map info structure (FullMap). While technically +// this ends in an array, in practice it only ever has a single entry so just +// treat it as a flat structure. +const ResLayout FullMapLayout = { + 58, // size on disc + sizeof(FullMap), // size in memory + 0, // flags + { + { RFFT_UINT32, offsetof(FullMap, x_size) }, + { RFFT_UINT32, offsetof(FullMap, y_size) }, + { RFFT_UINT32, offsetof(FullMap, x_shft) }, + { RFFT_UINT32, offsetof(FullMap, y_shft) }, + { RFFT_UINT32, offsetof(FullMap, z_shft) }, + { RFFT_PAD, 4 /* map elems pointer */ }, + { RFFT_UINT8, offsetof(FullMap, cyber) }, + { RFFT_UINT32, offsetof(FullMap, x_scale) }, + { RFFT_UINT32, offsetof(FullMap, y_scale) }, + { RFFT_UINT32, offsetof(FullMap, z_scale) }, + { RFFT_UINT32, offsetof(FullMap, sched[0].queue.size) }, + { RFFT_UINT32, offsetof(FullMap, sched[0].queue.fullness) }, + { RFFT_UINT32, offsetof(FullMap, sched[0].queue.elemsize) }, + { RFFT_UINT8, offsetof(FullMap, sched[0].queue.grow) }, + { RFFT_PAD, 12 /* 3 pointers at end */ }, + { RFFT_END, 0 } + } +}; +const ResourceFormat FullMapFormat = RES_FORMAT(FullMapLayout); + +// Describe the layout of a map element (tile; MapElem). +const ResLayout MapElemLayout = { + 16, // size on disc + sizeof(MapElem), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT8, offsetof(MapElem, tiletype) }, + { RFFT_UINT8, offsetof(MapElem, flr_rotnhgt) }, + { RFFT_UINT8, offsetof(MapElem, ceil_rotnhgt) }, + { RFFT_UINT8, offsetof(MapElem, param) }, + { RFFT_UINT16, offsetof(MapElem, objRef) }, + { RFFT_UINT16, offsetof(MapElem, tmap_ccolor) }, + { RFFT_UINT8, offsetof(MapElem, flag1) }, + { RFFT_UINT8, offsetof(MapElem, flag2) }, + { RFFT_UINT8, offsetof(MapElem, flag3) }, + { RFFT_UINT8, offsetof(MapElem, flag4) }, + { RFFT_UINT8, offsetof(MapElem, sub_clip) }, + { RFFT_UINT8, offsetof(MapElem, clearsolid) }, + { RFFT_UINT8, offsetof(MapElem, flick_qclip) }, + { RFFT_UINT8, offsetof(MapElem, templight) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of level texture info (array of 16-bit texture IDs). +const ResLayout TextureInfoLayout = { + 2, // size on disc + sizeof(short), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, 0 }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the objects table in a resfile. (27-byte PC record). +const ResLayout ObjV11Layout = { + 27, // size on disc + sizeof(Obj), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT8, offsetof(Obj, active) }, + { RFFT_UINT8, offsetof(Obj, obclass) }, + { RFFT_UINT8, offsetof(Obj, subclass) }, + { RFFT_UINT16, offsetof(Obj, specID) }, + { RFFT_UINT16, offsetof(Obj, ref) }, + { RFFT_UINT16, offsetof(Obj, next) }, + { RFFT_UINT16, offsetof(Obj, prev) }, + { RFFT_UINT16, offsetof(Obj, loc.x) }, + { RFFT_UINT16, offsetof(Obj, loc.y) }, + { RFFT_UINT8, offsetof(Obj, loc.z) }, + { RFFT_UINT8, offsetof(Obj, loc.p) }, + { RFFT_UINT8, offsetof(Obj, loc.h) }, + { RFFT_UINT8, offsetof(Obj, loc.b) }, + { RFFT_UINT8, offsetof(Obj, info.ph) }, + { RFFT_UINT8, offsetof(Obj, info.type) }, + { RFFT_UINT16, offsetof(Obj, info.current_hp) }, + { RFFT_UINT8, offsetof(Obj, info.make_info) }, + { RFFT_UINT8, offsetof(Obj, info.current_frame) }, + { RFFT_UINT8, offsetof(Obj, info.time_remainder) }, + { RFFT_UINT8, offsetof(Obj, info.inst_flags) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the objects table in a resfile. ("Easysaves" v12 +// record; has an extra byte of padding due to 16-bit alignment on Mac). +const ResLayout ObjV12Layout = { + 28, // size on disc + sizeof(Obj), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT8, offsetof(Obj, active) }, + { RFFT_UINT8, offsetof(Obj, obclass) }, + { RFFT_UINT8, offsetof(Obj, subclass) }, + { RFFT_PAD, 1 }, // 2-byte alignment + { RFFT_UINT16, offsetof(Obj, specID) }, + { RFFT_UINT16, offsetof(Obj, ref) }, + { RFFT_UINT16, offsetof(Obj, next) }, + { RFFT_UINT16, offsetof(Obj, prev) }, + { RFFT_UINT16, offsetof(Obj, loc.x) }, + { RFFT_UINT16, offsetof(Obj, loc.y) }, + { RFFT_UINT8, offsetof(Obj, loc.z) }, + { RFFT_UINT8, offsetof(Obj, loc.p) }, + { RFFT_UINT8, offsetof(Obj, loc.h) }, + { RFFT_UINT8, offsetof(Obj, loc.b) }, + { RFFT_UINT8, offsetof(Obj, info.ph) }, + { RFFT_UINT8, offsetof(Obj, info.type) }, + { RFFT_UINT16, offsetof(Obj, info.current_hp) }, + { RFFT_UINT8, offsetof(Obj, info.make_info) }, + { RFFT_UINT8, offsetof(Obj, info.current_frame) }, + { RFFT_UINT8, offsetof(Obj, info.time_remainder) }, + { RFFT_UINT8, offsetof(Obj, info.inst_flags) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the object cross-refs table in a resfile. +const ResLayout ObjRefLayout = { + 10, // size on disc + sizeof(ObjRef), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(ObjRef, state.bin.sq.x) }, + { RFFT_UINT16, offsetof(ObjRef, state.bin.sq.y) }, + { RFFT_UINT16, offsetof(ObjRef, obj) }, + { RFFT_UINT16, offsetof(ObjRef, next) }, + { RFFT_UINT16, offsetof(ObjRef, nextref) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the gun class info in a resfile. +const ResLayout GunLayout = { + 8, // size on disc + sizeof(ObjGun), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(ObjGun, id) }, + { RFFT_UINT16, offsetof(ObjGun, next) }, + { RFFT_UINT16, offsetof(ObjGun, prev) }, + { RFFT_UINT8, offsetof(ObjGun, ammo_type) }, + { RFFT_UINT8, offsetof(ObjGun, ammo_count) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the ammo class info in a resfile. +const ResLayout AmmoLayout = { + 6, // size on disc + sizeof(ObjAmmo), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(ObjAmmo, id) }, + { RFFT_UINT16, offsetof(ObjAmmo, next) }, + { RFFT_UINT16, offsetof(ObjAmmo, prev) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the physics object class info in a resfile. +const ResLayout PhysicsLayout = { + 40, // size on disc + sizeof(ObjPhysics), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(ObjPhysics, id) }, + { RFFT_UINT16, offsetof(ObjPhysics, next) }, + { RFFT_UINT16, offsetof(ObjPhysics, prev) }, + { RFFT_UINT16, offsetof(ObjPhysics, owner) }, + { RFFT_UINT32, offsetof(ObjPhysics, bullet_triple) }, + { RFFT_UINT32, offsetof(ObjPhysics, duration) }, + { RFFT_UINT16, offsetof(ObjPhysics, p1.x) }, + { RFFT_UINT16, offsetof(ObjPhysics, p1.y) }, + { RFFT_UINT8, offsetof(ObjPhysics, p1.z) }, + { RFFT_UINT8, offsetof(ObjPhysics, p1.p) }, + { RFFT_UINT8, offsetof(ObjPhysics, p1.h) }, + { RFFT_UINT8, offsetof(ObjPhysics, p1.b) }, + { RFFT_UINT16, offsetof(ObjPhysics, p2.x) }, + { RFFT_UINT16, offsetof(ObjPhysics, p2.y) }, + { RFFT_UINT8, offsetof(ObjPhysics, p2.z) }, + { RFFT_UINT8, offsetof(ObjPhysics, p2.p) }, + { RFFT_UINT8, offsetof(ObjPhysics, p2.h) }, + { RFFT_UINT8, offsetof(ObjPhysics, p2.b) }, + { RFFT_UINT16, offsetof(ObjPhysics, p3.x) }, + { RFFT_UINT16, offsetof(ObjPhysics, p3.y) }, + { RFFT_UINT8, offsetof(ObjPhysics, p3.z) }, + { RFFT_UINT8, offsetof(ObjPhysics, p3.p) }, + { RFFT_UINT8, offsetof(ObjPhysics, p3.h) }, + { RFFT_UINT8, offsetof(ObjPhysics, p3.b) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the grenade class info in a resfile. +const ResLayout GrenadeLayout = { + 12, // size on disc + sizeof(ObjGrenade), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(ObjGrenade, id) }, + { RFFT_UINT16, offsetof(ObjGrenade, next) }, + { RFFT_UINT16, offsetof(ObjGrenade, prev) }, + { RFFT_UINT8, offsetof(ObjGrenade, unique_id) }, + { RFFT_UINT8, offsetof(ObjGrenade, walls_hit) }, + { RFFT_UINT16, offsetof(ObjGrenade, flags) }, + { RFFT_UINT16, offsetof(ObjGrenade, timestamp) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the drug class info in a resfile. +const ResLayout DrugLayout = { + 6, // size on disc + sizeof(ObjDrug), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(ObjDrug, id) }, + { RFFT_UINT16, offsetof(ObjDrug, next) }, + { RFFT_UINT16, offsetof(ObjDrug, prev) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the hardware class info in a resfile (original 7-byte +// v11 struct). +const ResLayout HardwareV11Layout = { + 7, // size on disc + sizeof(ObjHardware), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(ObjHardware, id) }, + { RFFT_UINT16, offsetof(ObjHardware, next) }, + { RFFT_UINT16, offsetof(ObjHardware, prev) }, + { RFFT_UINT8, offsetof(ObjHardware, version) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the hardware class info in a resfile ("easysaves" +// 8-byte v12 struct). +const ResLayout HardwareV12Layout = { + 8, // size on disc + sizeof(ObjHardware), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(ObjHardware, id) }, + { RFFT_UINT16, offsetof(ObjHardware, next) }, + { RFFT_UINT16, offsetof(ObjHardware, prev) }, + { RFFT_UINT8, offsetof(ObjHardware, version) }, + { RFFT_PAD, 1 }, // alignment + { RFFT_END, 0 } + } +}; + +// Describe the layout of the software class info in a resfile (original 9-byte +// v11 struct). +const ResLayout SoftwareV11Layout = { + 9, // size on disc + sizeof(ObjSoftware), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(ObjSoftware, id) }, + { RFFT_UINT16, offsetof(ObjSoftware, next) }, + { RFFT_UINT16, offsetof(ObjSoftware, prev) }, + { RFFT_UINT8, offsetof(ObjSoftware, version) }, + { RFFT_UINT16, offsetof(ObjSoftware, data_munge) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the software class info in a resfile (10-byte v12 +// struct). +const ResLayout SoftwareV12Layout = { + 10, // size on disc + sizeof(ObjSoftware), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(ObjSoftware, id) }, + { RFFT_UINT16, offsetof(ObjSoftware, next) }, + { RFFT_UINT16, offsetof(ObjSoftware, prev) }, + { RFFT_UINT8, offsetof(ObjSoftware, version) }, + { RFFT_PAD, 1 }, // alignment + { RFFT_UINT16, offsetof(ObjSoftware, data_munge) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the "bigstuff" class info in a resfile. +const ResLayout BigStuffLayout = { + 16, // size on disc + sizeof(ObjBigstuff), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(ObjBigstuff, id) }, + { RFFT_UINT16, offsetof(ObjBigstuff, next) }, + { RFFT_UINT16, offsetof(ObjBigstuff, prev) }, + { RFFT_UINT16, offsetof(ObjBigstuff, cosmetic_value) }, + { RFFT_UINT32, offsetof(ObjBigstuff, data1) }, + { RFFT_UINT32, offsetof(ObjBigstuff, data2) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the "smallstuff" class info in a resfile. +const ResLayout SmallStuffLayout = { + 16, // size on disc + sizeof(ObjSmallstuff), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(ObjSmallstuff, id) }, + { RFFT_UINT16, offsetof(ObjSmallstuff, next) }, + { RFFT_UINT16, offsetof(ObjSmallstuff, prev) }, + { RFFT_UINT16, offsetof(ObjSmallstuff, cosmetic_value) }, + { RFFT_UINT32, offsetof(ObjSmallstuff, data1) }, + { RFFT_UINT32, offsetof(ObjSmallstuff, data2) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the fixture class info in a resfile. +const ResLayout FixtureLayout = { + 30, // size on disc + sizeof(ObjFixture), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(ObjFixture, id) }, + { RFFT_UINT16, offsetof(ObjFixture, next) }, + { RFFT_UINT16, offsetof(ObjFixture, prev) }, + { RFFT_UINT8, offsetof(ObjFixture, trap_type) }, + { RFFT_UINT8, offsetof(ObjFixture, destroy_count) }, + { RFFT_UINT32, offsetof(ObjFixture, comparator) }, + { RFFT_UINT32, offsetof(ObjFixture, p1) }, + { RFFT_UINT32, offsetof(ObjFixture, p2) }, + { RFFT_UINT32, offsetof(ObjFixture, p3) }, + { RFFT_UINT32, offsetof(ObjFixture, p4) }, + { RFFT_UINT16, offsetof(ObjFixture, access_level) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the door class info in a resfile. +const ResLayout DoorLayout = { + 14, // size on disc + sizeof(ObjDoor), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(ObjDoor, id) }, + { RFFT_UINT16, offsetof(ObjDoor, next) }, + { RFFT_UINT16, offsetof(ObjDoor, prev) }, + { RFFT_UINT16, offsetof(ObjDoor, locked) }, + { RFFT_UINT8, offsetof(ObjDoor, stringnum) }, + { RFFT_UINT8, offsetof(ObjDoor, cosmetic_value) }, + { RFFT_UINT8, offsetof(ObjDoor, access_level) }, + { RFFT_UINT8, offsetof(ObjDoor, autoclose_time) }, + { RFFT_UINT16, offsetof(ObjDoor, other_half) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the animating class info in a resfile. +const ResLayout AnimatingLayout = { + 10, // size on disc + sizeof(ObjAnimating), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(ObjAnimating, id) }, + { RFFT_UINT16, offsetof(ObjAnimating, next) }, + { RFFT_UINT16, offsetof(ObjAnimating, prev) }, + { RFFT_UINT8, offsetof(ObjAnimating, start_frame) }, + { RFFT_UINT8, offsetof(ObjAnimating, end_frame) }, + { RFFT_UINT16, offsetof(ObjAnimating, owner) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the trap class info in a resfile. +const ResLayout TrapLayout = { + 28, // size on disc + sizeof(ObjTrap), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(ObjTrap, id) }, + { RFFT_UINT16, offsetof(ObjTrap, next) }, + { RFFT_UINT16, offsetof(ObjTrap, prev) }, + { RFFT_UINT8, offsetof(ObjTrap, trap_type) }, + { RFFT_UINT8, offsetof(ObjTrap, destroy_count) }, + { RFFT_UINT32, offsetof(ObjTrap, comparator) }, + { RFFT_UINT32, offsetof(ObjTrap, p1) }, + { RFFT_UINT32, offsetof(ObjTrap, p2) }, + { RFFT_UINT32, offsetof(ObjTrap, p3) }, + { RFFT_UINT32, offsetof(ObjTrap, p4) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the container class info in a resfile (original 21- +// byte struct). +const ResLayout ContainerV11Layout = { + 21, // size on disc + sizeof(ObjContainer), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(ObjContainer, id) }, + { RFFT_UINT16, offsetof(ObjContainer, next) }, + { RFFT_UINT16, offsetof(ObjContainer, prev) }, + { RFFT_UINT32, offsetof(ObjContainer, contents1) }, + { RFFT_UINT32, offsetof(ObjContainer, contents2) }, + { RFFT_UINT8, offsetof(ObjContainer, dim_x) }, + { RFFT_UINT8, offsetof(ObjContainer, dim_y) }, + { RFFT_UINT8, offsetof(ObjContainer, dim_z) }, + { RFFT_UINT32, offsetof(ObjContainer, data1) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the container class info in a resfile (22-byte v12 +// struct). +const ResLayout ContainerV12Layout = { + 22, // size on disc + sizeof(ObjContainer), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(ObjContainer, id) }, + { RFFT_UINT16, offsetof(ObjContainer, next) }, + { RFFT_UINT16, offsetof(ObjContainer, prev) }, + { RFFT_UINT32, offsetof(ObjContainer, contents1) }, + { RFFT_UINT32, offsetof(ObjContainer, contents2) }, + { RFFT_UINT8, offsetof(ObjContainer, dim_x) }, + { RFFT_UINT8, offsetof(ObjContainer, dim_y) }, + { RFFT_UINT8, offsetof(ObjContainer, dim_z) }, + { RFFT_PAD, 1 }, // alignment + { RFFT_UINT32, offsetof(ObjContainer, data1) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the critter class info in a resfile. +const ResLayout CritterLayout = { + 46, // size on disc + sizeof(ObjCritter), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(ObjCritter, id) }, + { RFFT_UINT16, offsetof(ObjCritter, next) }, + { RFFT_UINT16, offsetof(ObjCritter, prev) }, + { RFFT_UINT32, offsetof(ObjCritter, des_heading) }, + { RFFT_UINT32, offsetof(ObjCritter, des_speed) }, + { RFFT_UINT32, offsetof(ObjCritter, urgency) }, + { RFFT_UINT16, offsetof(ObjCritter, wait_frames) }, + { RFFT_UINT16, offsetof(ObjCritter, flags) }, + { RFFT_UINT32, offsetof(ObjCritter, attack_count) }, + { RFFT_UINT8, offsetof(ObjCritter, ai_mode) }, + { RFFT_UINT8, offsetof(ObjCritter, mood) }, + { RFFT_UINT8, offsetof(ObjCritter, orders) }, + { RFFT_UINT8, offsetof(ObjCritter, current_posture) }, + { RFFT_UINT8, offsetof(ObjCritter, x1) }, + { RFFT_UINT8, offsetof(ObjCritter, y1) }, + { RFFT_UINT8, offsetof(ObjCritter, dest_x) }, + { RFFT_UINT8, offsetof(ObjCritter, dest_y) }, + { RFFT_UINT8, offsetof(ObjCritter, pf_x) }, + { RFFT_UINT8, offsetof(ObjCritter, pf_y) }, + { RFFT_UINT8, offsetof(ObjCritter, path_id) }, + { RFFT_UINT8, offsetof(ObjCritter, path_tries) }, + { RFFT_UINT16, offsetof(ObjCritter, loot1) }, + { RFFT_UINT16, offsetof(ObjCritter, loot2) }, + { RFFT_UINT32, offsetof(ObjCritter, sidestep) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of animation textures in a resfile (7-byte v11 struct). +const ResLayout AnimTextureV11Layout = { + 7, // size on disc + sizeof(AnimTextureData), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(AnimTextureData, anim_speed) }, + { RFFT_UINT16, offsetof(AnimTextureData, time_remainder) }, + { RFFT_UINT8, offsetof(AnimTextureData, current_frame) }, + { RFFT_UINT8, offsetof(AnimTextureData, num_frames) }, + { RFFT_UINT8, offsetof(AnimTextureData, flags) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of animation textures in a resfile (8-byte v12 struct). +const ResLayout AnimTextureV12Layout = { + 8, // size on disc + sizeof(AnimTextureData), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(AnimTextureData, anim_speed) }, + { RFFT_UINT16, offsetof(AnimTextureData, time_remainder) }, + { RFFT_UINT8, offsetof(AnimTextureData, current_frame) }, + { RFFT_UINT8, offsetof(AnimTextureData, num_frames) }, + { RFFT_UINT8, offsetof(AnimTextureData, flags) }, + { RFFT_PAD, 1 }, // alignment + { RFFT_END, 0 } + } +}; + +// Describes the layout of the hack cameras / hack surrogates tables. (Each is +// just an array of 16-bit ObjIDs.) +const ResLayout HackCameraLayout = { + 2, // size on disc + sizeof(ObjID), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, 0 }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the level data in a resfile. +// FIXME explicitly copies the 3 automap info structs. Should support sub- +// arrays somehow in the layout table. +const ResLayout LevelDataV11Layout = { + 94, // size on disc + sizeof(LevelData), // size in memory + 0, // flags + { + { RFFT_UINT16, offsetof(LevelData, size) }, + { RFFT_UINT8, offsetof(LevelData, mist) }, + { RFFT_UINT8, offsetof(LevelData, gravity) }, + { RFFT_UINT8, offsetof(LevelData, hazard.rad) }, + { RFFT_UINT8, offsetof(LevelData, hazard.bio) }, + { RFFT_UINT8, offsetof(LevelData, hazard.zerogbio) }, + { RFFT_UINT8, offsetof(LevelData, hazard.bio_h) }, + { RFFT_UINT8, offsetof(LevelData, hazard.rad_h) }, + { RFFT_UINT32, offsetof(LevelData, exit_time) }, + { RFFT_UINT8, offsetof(LevelData, auto_maps[0].init) }, + { RFFT_UINT8, offsetof(LevelData, auto_maps[0].zoom) }, + { RFFT_UINT32, offsetof(LevelData, auto_maps[0].xf) }, + { RFFT_UINT32, offsetof(LevelData, auto_maps[0].yf) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[0].lw) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[0].lh) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[0].obj_to_follow) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[0].sensor_obj) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[0].note_obj) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[0].flags) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[0].avail_flags) }, + { RFFT_UINT8, offsetof(LevelData, auto_maps[0].version_id) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[0].sensor_rad) }, + { RFFT_UINT8, offsetof(LevelData, auto_maps[1].init) }, + { RFFT_UINT8, offsetof(LevelData, auto_maps[1].zoom) }, + { RFFT_UINT32, offsetof(LevelData, auto_maps[1].xf) }, + { RFFT_UINT32, offsetof(LevelData, auto_maps[1].yf) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[1].lw) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[1].lh) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[1].obj_to_follow) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[1].sensor_obj) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[1].note_obj) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[1].flags) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[1].avail_flags) }, + { RFFT_UINT8, offsetof(LevelData, auto_maps[1].version_id) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[1].sensor_rad) }, + { RFFT_UINT8, offsetof(LevelData, auto_maps[2].init) }, + { RFFT_UINT8, offsetof(LevelData, auto_maps[2].zoom) }, + { RFFT_UINT32, offsetof(LevelData, auto_maps[2].xf) }, + { RFFT_UINT32, offsetof(LevelData, auto_maps[2].yf) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[2].lw) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[2].lh) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[2].obj_to_follow) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[2].sensor_obj) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[2].note_obj) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[2].flags) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[2].avail_flags) }, + { RFFT_UINT8, offsetof(LevelData, auto_maps[2].version_id) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[2].sensor_rad) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the level data in a resfile (v12 data structure). +// FIXME explicitly copies the 3 automap info structs. Should support sub- +// arrays somehow in the layout table. +const ResLayout LevelDataV12Layout = { + 98, // size on disc + sizeof(LevelData), // size in memory + 0, // flags + { + { RFFT_UINT16, offsetof(LevelData, size) }, + { RFFT_UINT8, offsetof(LevelData, mist) }, + { RFFT_UINT8, offsetof(LevelData, gravity) }, + { RFFT_UINT8, offsetof(LevelData, hazard.rad) }, + { RFFT_UINT8, offsetof(LevelData, hazard.bio) }, + { RFFT_UINT8, offsetof(LevelData, hazard.zerogbio) }, + { RFFT_UINT8, offsetof(LevelData, hazard.bio_h) }, + { RFFT_UINT8, offsetof(LevelData, hazard.rad_h) }, + { RFFT_PAD, 1 }, // alignment + { RFFT_UINT32, offsetof(LevelData, exit_time) }, + { RFFT_UINT8, offsetof(LevelData, auto_maps[0].init) }, + { RFFT_UINT8, offsetof(LevelData, auto_maps[0].zoom) }, + { RFFT_UINT32, offsetof(LevelData, auto_maps[0].xf) }, + { RFFT_UINT32, offsetof(LevelData, auto_maps[0].yf) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[0].lw) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[0].lh) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[0].obj_to_follow) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[0].sensor_obj) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[0].note_obj) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[0].flags) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[0].avail_flags) }, + { RFFT_UINT8, offsetof(LevelData, auto_maps[0].version_id) }, + { RFFT_PAD, 1 }, // alignment + { RFFT_UINT16, offsetof(LevelData, auto_maps[0].sensor_rad) }, + { RFFT_UINT8, offsetof(LevelData, auto_maps[1].init) }, + { RFFT_UINT8, offsetof(LevelData, auto_maps[1].zoom) }, + { RFFT_UINT32, offsetof(LevelData, auto_maps[1].xf) }, + { RFFT_UINT32, offsetof(LevelData, auto_maps[1].yf) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[1].lw) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[1].lh) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[1].obj_to_follow) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[1].sensor_obj) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[1].note_obj) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[1].flags) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[1].avail_flags) }, + { RFFT_UINT8, offsetof(LevelData, auto_maps[1].version_id) }, + { RFFT_PAD, 1 }, // alignment + { RFFT_UINT16, offsetof(LevelData, auto_maps[1].sensor_rad) }, + { RFFT_UINT8, offsetof(LevelData, auto_maps[2].init) }, + { RFFT_UINT8, offsetof(LevelData, auto_maps[2].zoom) }, + { RFFT_UINT32, offsetof(LevelData, auto_maps[2].xf) }, + { RFFT_UINT32, offsetof(LevelData, auto_maps[2].yf) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[2].lw) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[2].lh) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[2].obj_to_follow) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[2].sensor_obj) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[2].note_obj) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[2].flags) }, + { RFFT_UINT16, offsetof(LevelData, auto_maps[2].avail_flags) }, + { RFFT_UINT8, offsetof(LevelData, auto_maps[2].version_id) }, + { RFFT_PAD, 1 }, // alignment + { RFFT_UINT16, offsetof(LevelData, auto_maps[2].sensor_rad) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of a path. +const ResLayout PathLayout = { + 28, // size on disc + sizeof(Path), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(Path, source.x) }, + { RFFT_UINT16, offsetof(Path, source.y) }, + { RFFT_UINT16, offsetof(Path, dest.x) }, + { RFFT_UINT16, offsetof(Path, dest.y) }, + { RFFT_UINT8, offsetof(Path, dest_z) }, + { RFFT_UINT8, offsetof(Path, start_z) }, + { RFFT_UINT8, offsetof(Path, num_steps) }, + { RFFT_UINT8, offsetof(Path, curr_step) }, + { RFFT_BIN(NUM_PATH_STEPS / 4), offsetof(Path, moves) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the anims table in a resfile (15-byte version 11 +// structure). +const ResLayout AnimV11Layout = { + 15, // size on disc + sizeof(AnimListing), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(AnimListing, id) }, + { RFFT_UINT8, offsetof(AnimListing, flags) }, + { RFFT_UINT16, offsetof(AnimListing, cbtype) }, + { RFFT_UINT32, offsetof(AnimListing, callback) }, + { RFFT_INTPTR, offsetof(AnimListing, user_data) }, + { RFFT_UINT16, offsetof(AnimListing, speed) }, + { RFFT_END, 0 } + } +}; + +// Describe the layout of the anims table in a resfile (16-byte version 12 +// structure). +// The AnimListing struct wasn't given a specific packing. This is my best +// guess at a 32-bit one. Savefile compatibility may be a bit dodgy. +const ResLayout AnimV12Layout = { + 20, // size on disc + sizeof(AnimListing), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT16, offsetof(AnimListing, id) }, + { RFFT_UINT8, offsetof(AnimListing, flags) }, + { RFFT_PAD, 1 }, // alignment + { RFFT_UINT16, offsetof(AnimListing, cbtype) }, + { RFFT_PAD, 2 }, // alignment + { RFFT_UINT32, offsetof(AnimListing, callback) }, + { RFFT_INTPTR, offsetof(AnimListing, user_data) }, + { RFFT_UINT16, offsetof(AnimListing, speed) }, + { RFFT_PAD, 2 }, // alignment + { RFFT_END, 0 } + } +}; + +// Describe the layout of the height semaphores table in a resfile. +const ResLayout HeightSemaphoreLayout = { + 4, // size on disc + sizeof(height_semaphor), // size in memory + LAYOUT_FLAG_ARRAY, // flags + { + { RFFT_UINT8, offsetof(height_semaphor, x) }, + { RFFT_UINT8, offsetof(height_semaphor, y) }, + { RFFT_UINT8, offsetof(height_semaphor, floor_key) }, + { RFFT_UINT8, offsetof(height_semaphor, inuse) }, + { RFFT_END, 0 } + } +}; + +// Version 11 level archives table. +const ResourceFormat LevelVersion11Format[MAX_LEVEL_INDEX+1] = { + RES_FORMAT(U32Layout), // 02 map version number + RES_FORMAT(U32Layout), // 03 object version number + RES_FORMAT(FullMapLayout), // 04 fullmap info + RES_FORMAT(MapElemLayout), // 05 map tiles (MapElem) + { NULL, NULL, 0, NULL}, // 06 FIXME placeholder for schedules + RES_FORMAT(TextureInfoLayout), // 07 loved textures + RES_FORMAT(ObjV11Layout), // 08 objects + RES_FORMAT(ObjRefLayout), // 09 objrefs + RES_FORMAT(GunLayout), // 10 gun object info + RES_FORMAT(AmmoLayout), // 11 ammo object info + RES_FORMAT(PhysicsLayout), // 12 physics object info + RES_FORMAT(GrenadeLayout), // 13 grenade object into + RES_FORMAT(DrugLayout), // 14 drug object info + RES_FORMAT(HardwareV11Layout), // 15 hardware object info + RES_FORMAT(SoftwareV11Layout), // 16 software object info + RES_FORMAT(BigStuffLayout), // 17 bigstuff object info + RES_FORMAT(SmallStuffLayout), // 18 smallstuff object info + RES_FORMAT(FixtureLayout), // 19 fixture object info + RES_FORMAT(DoorLayout), // 20 door object info + RES_FORMAT(AnimatingLayout), // 21 animating object info + RES_FORMAT(TrapLayout), // 22 trap object info + RES_FORMAT(ContainerV11Layout), // 23 container object info + RES_FORMAT(CritterLayout), // 24 critter object info + RES_FORMAT(GunLayout), // 25 default gun (as 10 but single entry) + RES_FORMAT(AmmoLayout), // 26 default ammo + RES_FORMAT(PhysicsLayout), // 27 default physics + RES_FORMAT(GrenadeLayout), // 28 default grenade + RES_FORMAT(DrugLayout), // 29 default drug + RES_FORMAT(HardwareV11Layout), // 30 default hardware + RES_FORMAT(SoftwareV11Layout), // 31 default software + RES_FORMAT(BigStuffLayout), // 32 default bigstuff + RES_FORMAT(SmallStuffLayout), // 33 default smallstuff + RES_FORMAT(FixtureLayout), // 34 default fixture + RES_FORMAT(DoorLayout), // 35 default door + RES_FORMAT(AnimatingLayout), // 36 default animating + RES_FORMAT(TrapLayout), // 37 default trap + RES_FORMAT(ContainerV11Layout), // 38 default container + RES_FORMAT(CritterLayout), // 39 default critter + RES_FORMAT(U32Layout), // 40 misc version (not used) + { NULL, NULL, 0, NULL }, // 41 not used + RES_FORMAT(AnimTextureV11Layout), // 42 anim textures + RES_FORMAT(HackCameraLayout), // 43 hack camera objects + RES_FORMAT(HackCameraLayout), // 44 hack camera surrogates + RES_FORMAT(LevelDataV11Layout), // 45 level data + { NULL, NULL, 0, NULL }, // 46 map strings (character array) + RES_FORMAT(U32Layout), // 47 map magic (next available offset) + { NULL, NULL, 0, NULL }, // 48 not used + RES_FORMAT(PathLayout), // 49 paths + RES_FORMAT(U16Layout), // 50 used paths + RES_FORMAT(AnimV11Layout), // 51 anim list + RES_FORMAT(U16Layout), // 52 anim counter + RES_FORMAT(HeightSemaphoreLayout) // 53 semaphores +}; + +// Version 12 level archives table. +const ResourceFormat LevelVersion12Format[MAX_LEVEL_INDEX+1] = { + RES_FORMAT(U32Layout), // 02 map version number + RES_FORMAT(U32Layout), // 03 object version number + RES_FORMAT(FullMapLayout), // 04 fullmap info + RES_FORMAT(MapElemLayout), // 05 map tiles (MapElem) + { NULL, NULL, 0, NULL}, // 06 FIXME placeholder for schedules + RES_FORMAT(TextureInfoLayout), // 07 loved textures + RES_FORMAT(ObjV12Layout), // 08 objects + RES_FORMAT(ObjRefLayout), // 09 objrefs + RES_FORMAT(GunLayout), // 10 gun object info + RES_FORMAT(AmmoLayout), // 11 ammo object info + RES_FORMAT(PhysicsLayout), // 12 physics object info + RES_FORMAT(GrenadeLayout), // 13 grenade object into + RES_FORMAT(DrugLayout), // 14 drug object info + RES_FORMAT(HardwareV12Layout), // 15 hardware object info + RES_FORMAT(SoftwareV12Layout), // 16 software object info + RES_FORMAT(BigStuffLayout), // 17 bigstuff object info + RES_FORMAT(SmallStuffLayout), // 18 smallstuff object info + RES_FORMAT(FixtureLayout), // 19 fixture object info + RES_FORMAT(DoorLayout), // 20 door object info + RES_FORMAT(AnimatingLayout), // 21 animating object info + RES_FORMAT(TrapLayout), // 22 trap object info + RES_FORMAT(ContainerV12Layout), // 23 container object info + RES_FORMAT(CritterLayout), // 24 critter object info + RES_FORMAT(GunLayout), // 25 default gun (as 10 but single entry) + RES_FORMAT(AmmoLayout), // 26 default ammo + RES_FORMAT(PhysicsLayout), // 27 default physics + RES_FORMAT(GrenadeLayout), // 28 default grenade + RES_FORMAT(DrugLayout), // 29 default drug + RES_FORMAT(HardwareV12Layout), // 30 default hardware + RES_FORMAT(SoftwareV12Layout), // 31 default software + RES_FORMAT(BigStuffLayout), // 32 default bigstuff + RES_FORMAT(SmallStuffLayout), // 33 default smallstuff + RES_FORMAT(FixtureLayout), // 34 default fixture + RES_FORMAT(DoorLayout), // 35 default door + RES_FORMAT(AnimatingLayout), // 36 default animating + RES_FORMAT(TrapLayout), // 37 default trap + RES_FORMAT(ContainerV12Layout), // 38 default container + RES_FORMAT(CritterLayout), // 39 default critter + RES_FORMAT(U32Layout), // 40 misc version (not used) + { NULL, NULL, 0, NULL }, // 41 not used + RES_FORMAT(AnimTextureV12Layout), // 42 anim textures + RES_FORMAT(HackCameraLayout), // 43 hack camera objects + RES_FORMAT(HackCameraLayout), // 44 hack camera surrogates + RES_FORMAT(LevelDataV12Layout), // 45 level data + { NULL, NULL, 0, NULL }, // 46 map strings (character array) + RES_FORMAT(U32Layout), // 47 map magic (next available offset) + { NULL, NULL, 0, NULL }, // 48 not used + RES_FORMAT(PathLayout), // 49 paths + RES_FORMAT(U16Layout), // 50 used paths + RES_FORMAT(AnimV12Layout), // 51 anim list + RES_FORMAT(U16Layout), // 52 anim counter + RES_FORMAT(HeightSemaphoreLayout) // 53 semaphores +}; diff --git a/engine/src/GameSrc/audiolog.c b/engine/src/GameSrc/audiolog.c new file mode 100644 index 0000000..b8536f8 --- /dev/null +++ b/engine/src/GameSrc/audiolog.c @@ -0,0 +1,227 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/audiolog.c $ + * $Revision: 1.17 $ + * $Author: dc $ + * $Date: 1994/11/19 20:35:27 $ + */ +// Mac version by Ken Cobb, 2/9/95 + +#include +#include + +#include "MacTune.h" +#include "afile.h" +#include "movie.h" +#include "audiolog.h" +#include "map.h" +#include "tools.h" +#include "musicai.h" +#include "mainloop.h" +#include "bark.h" +#include "miscqvar.h" + +#define AUDIOLOG_BASE_ID 2741 +#define AUDIOLOG_BARK_BASE_ID 3100 + +#define ALOG_MUSIC_DUCK 0.7 + +extern SDL_AudioStream *cutscene_audiostream; // see cutsloop.c + +static uint8_t *audiolog_audiobuffer = NULL; +static uint8_t *audiolog_audiobuffer_pos = NULL; +static int audiolog_audiobuffer_size; // in blocks of MOVIE_DEFAULT_BLOCKLEN + +int curr_alog = -1; +int alog_fn = -1; +uchar audiolog_setting = 1; +char secret_pending_hack; + +char *bark_files[] = {"res/data/citbark.res", "res/data/frnbark.res", "res/data/gerbark.res"}; +char *alog_files[] = {"res/data/citalog.res", "res/data/frnalog.res", "res/data/geralog.res"}; + +extern uchar curr_vol_lev; +extern uchar curr_alog_vol; +extern char which_lang; + +extern SDL_AudioDeviceID device; + +errtype audiolog_init(void) { return OK; } + +errtype audiolog_play(int email_id) { + int new_alog_fn; + Afile *palog; + + if (!sfx_on || !audiolog_setting) + return ERR_NOEFFECT; + + // KLC - Big-time hack to prevent bark #389 from trying to play twice (and thus skipping). + if (email_id == 389 && curr_alog == email_id) + return ERR_NOEFFECT; + + // Stop any currently playing alogs. + audiolog_stop(); + + // woo hoo, what a hack! + // this is for the player's log-to-self which has no audiolog + if (email_id == 0x44) + return ERR_NOEFFECT; + + begin_wait(); + + // Open up the appropriate sound-only movie file. + if (email_id > (AUDIOLOG_BARK_BASE_ID - AUDIOLOG_BASE_ID)) + new_alog_fn = ResOpenFile(bark_files[which_lang]); + else + new_alog_fn = ResOpenFile(alog_files[which_lang]); + + // Make sure this is a thing we have an audiolog for... + if (!ResInUse(AUDIOLOG_BASE_ID + email_id)) { + ResCloseFile(new_alog_fn); + end_wait(); + return ERR_FREAD; + } + + alog_fn = new_alog_fn; + + palog = malloc(sizeof(Afile)); + if (AfilePrepareRes(AUDIOLOG_BASE_ID + email_id, palog) < 0) { + WARN("%s: Cannot open Afile by id $%x", __FUNCTION__, AUDIOLOG_BASE_ID + email_id); + free(palog); + return ERR_FREAD; + } + + audiolog_audiobuffer_size = AfileAudioLength(palog); + audiolog_audiobuffer = (uint8_t *)malloc(audiolog_audiobuffer_size * MOVIE_DEFAULT_BLOCKLEN); + AfileGetAudio(palog, audiolog_audiobuffer); + + DEBUG("%s: Playing email", __FUNCTION__); + + SDL_PauseAudioDevice(device, 1); + SDL_Delay(1); + + cutscene_audiostream = SDL_NewAudioStream(AUDIO_U8, 1, fix_int(palog->a.sampleRate), AUDIO_S16SYS, 2, 48000); + + audiolog_audiobuffer_pos = audiolog_audiobuffer; + + end_wait(); + + // bureaucracy + curr_alog = email_id; + + // Duck the music + if (music_on) { + curr_vol_lev = QVAR_TO_VOLUME(QUESTVAR_GET(MUSIC_VOLUME_QVAR)); + curr_vol_lev = curr_vol_lev * ALOG_MUSIC_DUCK; + MacTuneUpdateVolume(); + } + + return OK; +} + +void audiolog_stop(void) { + if (alog_fn < 0) + return; + + ResCloseFile(alog_fn); + alog_fn = -1; + + // Restore music volume + if (music_on) { + curr_vol_lev = QVAR_TO_VOLUME(QUESTVAR_GET(MUSIC_VOLUME_QVAR)); + MacTuneUpdateVolume(); + } + + if (cutscene_audiostream != NULL) { + SDL_PauseAudioDevice(device, 1); + SDL_Delay(1); + + SDL_FreeAudioStream(cutscene_audiostream); + cutscene_audiostream = NULL; + + if (audiolog_audiobuffer) { + free(audiolog_audiobuffer); + audiolog_audiobuffer = NULL; + } + } + + curr_alog = -1; + + if (secret_pending_hack) { + INFO("Game over."); + + secret_pending_hack = 0; + + // Back to the main menu + _new_mode = SETUP_LOOP; + chg_set_flg(GL_CHG_LOOP); + } +} + +errtype audiolog_loop_callback(void) { + if (cutscene_audiostream) { + SDL_PauseAudioDevice(device, 0); + + if (audiolog_audiobuffer_size > 0) { + int i, vol = curr_alog_vol * 127 / 100; // convert from 0-100 to 0-127 + + for (i = 0; i < MOVIE_DEFAULT_BLOCKLEN; i++) + audiolog_audiobuffer_pos[i] = 128 + ((int)audiolog_audiobuffer_pos[i] - 128) * vol / 128; + + SDL_AudioStreamPut(cutscene_audiostream, audiolog_audiobuffer_pos, MOVIE_DEFAULT_BLOCKLEN); + audiolog_audiobuffer_pos += MOVIE_DEFAULT_BLOCKLEN; + audiolog_audiobuffer_size--; + } + + if (SDL_AudioStreamAvailable(cutscene_audiostream) == 0) + audiolog_stop(); + } + + return OK; +} + +//------------------------------------------------------------- +// if email_id is -1, returns whether or not anything is playing +// if email_id != -1, matches whether or not that specific email_id is playing +//------------------------------------------------------------- +bool audiolog_playing(int email_id) { + if (email_id == -1) + return (curr_alog != -1); + else + return (curr_alog == email_id); +} + +//------------------------------------------------------------- +// Start playing a bark file. +//------------------------------------------------------------- +errtype audiolog_bark_play(int bark_id) { + if (global_fullmap->cyber) + return ERR_NOEFFECT; + else + return (audiolog_play(bark_id + (AUDIOLOG_BARK_BASE_ID - AUDIOLOG_BASE_ID))); +} + +//------------------------------------------------------------- +// Stop playing audiolog (in response to a hotkey). +//------------------------------------------------------------- +uchar audiolog_cancel_func(ushort s, uint32_t l, intptr_t v) { + audiolog_stop(); + return TRUE; +} diff --git a/engine/src/GameSrc/automap.c b/engine/src/GameSrc/automap.c new file mode 100644 index 0000000..abe2833 --- /dev/null +++ b/engine/src/GameSrc/automap.c @@ -0,0 +1,332 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/automap.c $ + * $Revision: 1.43 $ + * $Author: xemu $ + * $Date: 1994/10/16 15:56:00 $ + */ + +#include + +#include "amaploop.h" +#include "player.h" +#include "newmfd.h" +#include "mfdint.h" +#include "mfddims.h" +#include "automap.h" +#include "colors.h" +#include "rcolors.h" +#include "tools.h" +#include "mainloop.h" +#include "wares.h" +#include "lvldata.h" +#include "cit2d.h" +#include "gr2ss.h" +#include "gamestrn.h" +#include "sfxlist.h" +#include "musicai.h" + +#include "mfdart.h" +#include "gamescr.h" +#include "cybstrng.h" + +#define WORKING_INC_MFDS + +#define AUTOMAP_ZOOM 0 +#define AUTOMAP_STATION 1 +#define AUTOMAP_STATES 2 + +#define ZOOM_STR get_temp_string(REF_STR_AutomapMFDButtons) +#define FULL_STR get_temp_string(REF_STR_AutomapMFDButtons + 1) +#define SIDE_STR get_temp_string(REF_STR_AutomapMFDButtons + 2) + +#define BTXT_HGT (MFD_VIEW_HGT - 7) + +// what is all this bullshit, anyway? + +#define SetAutomapMode(m, mode) \ + player_struct.mfd_func_data[MFD_MAP_FUNC][(m)] = (player_struct.mfd_func_data[MFD_MAP_FUNC][(m)] & 0xfc) + (mode) +#define GetAutomapMode(m) (player_struct.mfd_func_data[MFD_MAP_FUNC][(m)] & 0x03) + +#define SetLastAutomapMode(m, mode) \ + player_struct.mfd_func_data[MFD_MAP_FUNC][(m)] = \ + (player_struct.mfd_func_data[MFD_MAP_FUNC][(m)] & 0xf3) + ((mode) << 2) +#define GetLastAutomapMode(m) ((player_struct.mfd_func_data[MFD_MAP_FUNC][(m)] & 0x0c) >> 2) + +#define AutomapLastUpdated (player_struct.mfd_func_data[MFD_MAP_FUNC][2]) + +// ----------- +// PROTOTYPES +// ----------- +int mfd_to_map(int mid); +void automap_expose_cross_section(MFD *m, ubyte tic); +void automap_expose_zoom(MFD *m, ubyte tac); + +// ------- +// Globals +// ------- +static long last_update = 0; +extern uchar full_game_3d; + +// =========================================================================== +// * THE AUTOMAP CODE * +// =========================================================================== + +// --------------------------------------------------------------------------- +// mfd_map_init() +// +// Initializes the automap settings on either side to appropriate values. + +errtype mfd_map_init(MFD_Func *mfd) { + // set them up to be different cur and last, and left zoom, right station + player_struct.mfd_func_data[MFD_MAP_FUNC][MFD_LEFT] = (AUTOMAP_STATION << 2) + AUTOMAP_ZOOM; + player_struct.mfd_func_data[MFD_MAP_FUNC][MFD_RIGHT] = (AUTOMAP_ZOOM << 2) + AUTOMAP_STATION; + AutomapLastUpdated = 1; +#ifdef WORKING_INC_MFDS + player_struct.mfd_func_status[MFD_MAP_FUNC] |= 1 << 4; +#endif + return OK; +} + +int mfd_to_map(int mid) { + int i = 0, m; + + for (m = 0; m < mid; m++) { + if (mfd_get_func(m, player_struct.mfd_current_slots[m]) == MFD_MAP_FUNC) + i++; + } + return i; +} + + // --------------------------------------------------------------------------- + // mfd_map_handler() + // + // Simply toggles a bit which tells the expose function whether to draw + // the overhead map view, or the cross-level segment of the station + +#define MODE_RIGHT 25 +#define OPT_LEFT (MFD_VIEW_WID - 25) + +uchar mfd_map_handler(MFD *m, uiEvent *e) { + uchar retval = FALSE; + ubyte map_state; + int mapid = mfd_to_map(m->id); + + // If we don't have an automap, we shouldn't do shit. + if (player_struct.hardwarez[HARDWARE_AUTOMAP] == 0) + return FALSE; + uiMouseData *mouse = &e->mouse_data; + + if (mouse->action & (MOUSE_WHEELUP | MOUSE_WHEELDN)) { + if (!digi_fx_playing(SFX_MAP_ZOOM, NULL)) + play_digi_fx(SFX_MAP_ZOOM, 1); + amap_zoom(oAMap(mapid), FALSE, mouse->action & MOUSE_WHEELUP ? 1 : -1); + return TRUE; + } + + if (!(mouse->action & MOUSE_LDOWN)) + return FALSE; // ignore click releases + + if (e->pos.y > m->rect.lr.y - 8) // bottom row + { + int xp = e->pos.x - m->rect.ul.x; + + if (xp < MODE_RIGHT) { + map_state = GetAutomapMode(mapid); + SetAutomapMode(mapid, (map_state + 1) % AUTOMAP_STATES); + SetLastAutomapMode(mapid, map_state); + mfd_notify_func(MFD_MAP_FUNC, MFD_MAP_SLOT, FALSE, MFD_ACTIVE, FALSE); + last_update = 0; + play_digi_fx(SFX_MAP_ZOOM, 1); + return TRUE; + } else if (xp > OPT_LEFT) { + _new_mode = AUTOMAP_LOOP; + chg_set_flg(GL_CHG_LOOP); + play_digi_fx(SFX_MAP_ZOOM, 1); + } else if (GetAutomapMode(mapid) == AUTOMAP_ZOOM) { + int zfac; + play_digi_fx(SFX_MAP_ZOOM, 1); + if (xp < (MFD_VIEW_WID / 2)) + zfac = 1; + else + zfac = -1; + amap_zoom(oAMap(mapid), FALSE, zfac); + } + } else if (GetAutomapMode(mapid) != AUTOMAP_STATION) { + char buf[80]; + int tmpx = e->pos.x - m->rect.ul.x, tmpy = e->pos.y - m->rect.ul.y; + if (amap_deal_with_map_click(oAMap(mapid), &tmpx, &tmpy) != NULL) { + retval = amap_get_note(oAMap(mapid), buf); + if (full_game_3d && !retval) { + retval = mfd_scan_opacity(m->id, e->pos); + } + if (retval) { + strtoupper(buf); + message_info(buf); + retval = TRUE; + } + } + } + + return retval; +} + +// --------------------------------------------------------------------------- +// mfd_map_expose() +// +#include "fullscrn.h" + +void mfd_map_expose(MFD *m, ubyte control) { + ubyte map_state, last_map_state; + int mapid = mfd_to_map(m->id); + + if ((control & MFD_EXPOSE) && ((!full_game_3d) || (full_visible & (visible_mask(m->id))))) { + // If we don't have an automap, we shouldn't do shit. + if (player_struct.hardwarez[HARDWARE_AUTOMAP] == 0) { + char buf[128]; + short w, h; + gr_push_canvas(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + if (!full_game_3d) + draw_res_bm(REF_IMG_bmBlankMFD, 0, 0); + // draw_hires_resource_bm(REF_IMG_bmBlankMFD, 0, 0); + draw_raw_resource_bm(MKREF(RES_mfdArtOverlays, MFD_ART_TRIOP), 0, 0); + get_string(REF_STR_NoAutomap, buf, sizeof(buf)); + gr_set_font(ResLock(MFD_FONT)); + gr_string_wrap(buf, MFD_VIEW_WID - 2); + gr_string_size(buf, &w, &h); + gr_set_fcolor(RED_BASE + 5); + draw_shadowed_string(buf, (MFD_VIEW_WID - w) / 2, (MFD_VIEW_HGT - h) / 2, full_game_3d); + ResUnlock(MFD_FONT); + gr_pop_canvas(); + mfd_update_display(m, 0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + return; + } + + // what is all this stuff??? + map_state = GetAutomapMode(mapid); + last_map_state = GetLastAutomapMode(mapid); + + if (!(control & MFD_EXPOSE_FULL) && map_state == last_map_state) { + if (map_state == AUTOMAP_STATION) { + if (!(control & MFD_EXPOSE_FULL)) + return; + } else { +#define WORKING_INC_MFDS +#ifndef WORKING_INC_MFDS + if (last_update + (CIT_CYCLE >> 3) > (*tmd_ticks)) { + mfd_notify_func(MFD_MAP_FUNC, MFD_MAP_SLOT, FALSE, MFD_ACTIVE, FALSE); + return; + } else +#endif + { + if ((AutomapLastUpdated == mapid) && (GetAutomapMode((mapid == 0) ? 1 : 0) == + AUTOMAP_ZOOM)) { // we were last done, and both of us need to + // be done, so give the other guy a chance... + AutomapLastUpdated = 0xff; // neither of us, so next time either will work +#ifndef WORKING_INC_MFDS + mfd_notify_func(MFD_MAP_FUNC, MFD_MAP_SLOT, FALSE, MFD_ACTIVE, FALSE); +#endif + return; + } + AutomapLastUpdated = mapid; +#ifndef WORKING_INC_MFDS + last_update = *tmd_ticks; +#endif + } + } + } else { + if (map_state == AUTOMAP_ZOOM) + if (!oAMap(mapid)->init) { + automap_init(player_struct.hardwarez[HARDWARE_AUTOMAP], mapid); + oAMap(mapid)->zoom++; + } + } + SetLastAutomapMode(mapid, map_state); + gr_push_canvas(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + if (!full_game_3d) + draw_res_bm(REF_IMG_bmBlankMFD, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + switch (map_state) { + case AUTOMAP_ZOOM: + automap_expose_zoom(m, control); + break; + case AUTOMAP_STATION: + automap_expose_cross_section(m, control); + break; + } + gr_pop_canvas(); + mfd_update_display(m, 0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + } +} + + // --------------------------------------------------------------------------- + // automap_expose_cross_section() + // + // The expose function for drawing the cross-section mode for the automap + +#define BUF_SIZE 10 + +void automap_expose_cross_section(MFD *mfd, ubyte tic) { + grs_font *mfdamapfont; + char buf[BUF_SIZE]; + short w, h; + + draw_res_bm(MKREF(RES_mfdArtOverlays, MFD_ART_STATN), 0, 0); + draw_res_bm(MKREF(RES_mfdArtOverlays, MFD_ART_LVL(player_struct.level)), 0, 0); + mfdamapfont = ResLock(RES_tinyTechFont); + gr_set_font(mfdamapfont); + gr_set_fcolor(ORANGE_8_BASE + 2); + fsmap_get_lev_str(buf, BUF_SIZE); + gr_string_size(buf, &w, &h); + // KLC draw_shadowed_string(fsmap_get_lev_str(buf,BUF_SIZE),MFD_VIEW_WID-w-2,1,full_game_3d); + draw_shadowed_string(buf, MFD_VIEW_WID - w - 2, 1, full_game_3d); + ResUnlock(RES_tinyTechFont); + + mfdamapfont = ResLock(RES_mfdFont); + gr_set_font(mfdamapfont); + gr_set_fcolor(ORANGE_8_BASE); + draw_shadowed_string(ZOOM_STR, 1, BTXT_HGT, full_game_3d); + draw_shadowed_string(FULL_STR, MFD_VIEW_WID - 25, BTXT_HGT, full_game_3d); + ResUnlock(RES_mfdFont); +} + +// --------------------------------------------------------------------------- +// automap_expose_zoom_in() +// +// The expose function for drawing the zoomed in mode for the automap + +void automap_expose_zoom(MFD *m, ubyte tac) { + grs_font *mfdamapfont; + int mapid = mfd_to_map(m->id); + + amap_draw(oAMap(mapid), 0); + mfdamapfont = ResLock(RES_mfdFont); + gr_set_font(mfdamapfont); + gr_set_fcolor(ORANGE_8_BASE); + draw_shadowed_string(SIDE_STR, 1, BTXT_HGT, full_game_3d); + draw_shadowed_string("+ -", (MFD_VIEW_WID / 2) - 10, BTXT_HGT, full_game_3d); + draw_shadowed_string(FULL_STR, MFD_VIEW_WID - 25, BTXT_HGT, full_game_3d); + ResUnlock(RES_mfdFont); +#ifndef WORKING_INC_MFDS + mfd_notify_func(MFD_MAP_FUNC, MFD_MAP_SLOT, FALSE, MFD_ACTIVE, FALSE); +#endif +} diff --git a/engine/src/GameSrc/bark.c b/engine/src/GameSrc/bark.c new file mode 100644 index 0000000..cecccd9 --- /dev/null +++ b/engine/src/GameSrc/bark.c @@ -0,0 +1,154 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: u://RCS/bark.c $ + * $Revision: 1.33 $ + * $Author: xemu $ + * $Date: 1994/10/27 04:52:50 $ + * + * + */ + +// Includes for example mfd. +#include + +#include "bark.h" +#include "mfdext.h" +#include "mfddims.h" +#include "tools.h" +#include "gamestrn.h" +#include "objects.h" +#include "mfdart.h" +#include "gamescr.h" +#include "shodan.h" +#include "fullscrn.h" +#include "cit2d.h" +#include "audiolog.h" +#include "gr2ss.h" + +// ============================================================ +// MFD BARK +// ============================================================ + +#define BARK_MARGIN 2 + +void mfd_bark_expose(MFD *mfd, ubyte control) { + uchar full = control & MFD_EXPOSE_FULL; + if (control == 0) // MFD is drawing stuff + { + // Do unexpose stuff here. + } + if (control & MFD_EXPOSE) // Time to draw stuff + { + // clear update rects + mfd_clear_rects(); + // set up canvas + gr_push_canvas(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + // Clear the canvas by drawing the background bitmap + if (!full_game_3d) + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + + if (mfd_bark_mug > 0) { + FrameDesc *mug = RefLock(REF_IMG_EmailMugShotBase + mfd_bark_mug); + if (mug != NULL) { + ss_bitmap(&mug->bm, (MFD_VIEW_WID - mug->bm.w) / 2, (MFD_VIEW_HGT - mug->bm.h) / 2); + RefUnlock(REF_IMG_EmailMugShotBase + mfd_bark_mug); + } else { + WARN("mfd_bark_expose(): could not load mugshot ", mug); + } + } else if (!full_game_3d) { + draw_raw_resource_bm(MKREF(RES_mfdArtOverlays, MFD_ART_TRIOP), 0, 0); + } + if (full && global_fullmap->cyber && mfd->id == MFD_RIGHT && (full_visible & visible_mask(mfd->id)) == 0) { +#ifdef STEREO_SUPPORT + if (convert_use_mode == 5) + full_visible = visible_mask(mfd->id); + else +#endif + full_visible |= visible_mask(mfd->id); + mfd_notify_func(MFD_BARK_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, TRUE); + } + + if (full && mfd_bark_string != REF_STR_Null) { + char buf[256]; + short x, y; + short w, h; + //RefTable *prt = (RefTable *)ResLock(REFID(mfd_bark_string)); + RefTable *prt = ResReadRefTable(REFID(mfd_bark_string)); + + if (RefIndexValid(prt, REFINDEX(mfd_bark_string))) { + gr_set_font((grs_font *)ResLock(MFD_FONT)); + hyphenated_wrap_text(get_temp_string(mfd_bark_string), buf, MFD_VIEW_WID - 2); + gr_string_size(buf, &w, &h); + gr_set_fcolor(mfd_bark_color); + x = (MFD_VIEW_WID - w) / 2; + if (mfd_bark_mug > 0) + y = BARK_MARGIN; + else + y = (MFD_VIEW_HGT - h) / 2; + draw_shadowed_string(buf, x, y, mfd_bark_mug > 0 || full_game_3d); + ResUnlock(MFD_FONT); + } + //ResUnlock(REFID(mfd_bark_string)); + ResFreeRefTable(prt); + } + if (full) + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + // Pop the canvas + gr_pop_canvas(); + // Now that we've popped the canvas, we can send the + // updated mfd to screen + mfd_update_rects(mfd); + } +} + +void long_bark(ObjID speaker_id, uchar mug_id, int string_id, ubyte color) { + short mfd_id = mfd_grab_func(MFD_BARK_FUNC, MFD_INFO_SLOT); +#ifdef AUDIOLOGS + errtype alog_rv = ERR_NOEFFECT; +#endif + + mfd_bark_string = string_id; + mfd_bark_speaker = speaker_id; + mfd_bark_color = color; + mfd_bark_mug = mug_id; +#ifdef AUDIOLOGS + if ((audiolog_setting) && (REFID(string_id) == RES_traps)) + alog_rv = audiolog_bark_play(string_id - REF_STR_TrapZeroMessage); +#else + if ((mug_id >= FIRST_SHODAN_BARK) && (mug_id <= FIRST_SHODAN_BARK + NUM_SHODAN_MUGS - 1)) + play_digi_fx(SFX_SHODAN_BARK, 1); +#endif + +#ifdef AUDIOLOGS + if ((alog_rv != OK) || (audiolog_setting == 2)) +#endif + { + mfd_notify_func(MFD_BARK_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, TRUE); + if (speaker_id > 0) { + save_mfd_slot(mfd_id); + player_struct.panel_ref = speaker_id; + } + mfd_change_slot(mfd_id, MFD_INFO_SLOT); + } +} diff --git a/engine/src/GameSrc/biohelp.c b/engine/src/GameSrc/biohelp.c new file mode 100644 index 0000000..50da0a8 --- /dev/null +++ b/engine/src/GameSrc/biohelp.c @@ -0,0 +1,281 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/biohelp.c $ + * $Revision: 1.9 $ + * $Author: xemu $ + * $Date: 1994/10/20 18:59:37 $ + * + */ + +#include "mfdint.h" +#include "mfdext.h" +#include "mfddims.h" +#include "mfdgadg.h" +#include "newmfd.h" +#include "status.h" +#include "gamestrn.h" +#include "tools.h" +#include "citres.h" +#include "fullscrn.h" + +#include "cybstrng.h" +#include "mfdart.h" +#include "gr2ss.h" + +// ============================================================ +// THE BIO HELP MFD +// ============================================================ + +// ------- +// DEFINES +// ------- +uchar status_track_free(int track); +uchar status_track_active(int track); +void status_track_activate(int track, uchar active); + +uchar mfd_biohelp_button_handler(MFD *m, LGPoint bttn, uiEvent *ev, void *data); +uchar biohelp_region_mouse_handler(uiEvent *ev, LGRegion *r, intptr_t data); +errtype biohelp_create_mouse_region(LGRegion *root); + +// --------------- +// EXPOSE FUNCTION +// --------------- + +/* This gets called whenever the MFD needs to redraw or + undraw. + The control value is a bitmask with the following bits: + MFD_EXPOSE: Update the mfd, if MFD_EXPOSE_FULL is not set, + update incrementally. + MFD_EXPOSE_FULL: Fully redraw the mfd, implies MFD_EXPOSE + + if no bits are set, the mfd is being "unexposed;" its display + being pulled off the screen to make room for a different func. +*/ + +#define MFD_BIOHELP_FUNC 25 + +#define ARROW_WID 5 +#define ARROW_X (MFD_VIEW_WID - 1 - ARROW_WID) +#define ARROW_Y (MFD_VIEW_HGT - 10) + +#define LEFT_MARGIN 1 +#define TOP_MARGIN 1 +#define NUM_BUTTONS 4 +#define BARRY_HGT (MFD_VIEW_HGT - 2 * TOP_MARGIN) +#define BARRY_WID (ARROW_X - LEFT_MARGIN) +#define BUTTON_WID 12 +#define BUTTON_HGT 11 +#define TEXT_HGT 5 + +#define ITEM_COLOR (0x5A) + +#define STATUS_X 4 +#define GAMESCR_BIO_WIDTH 131 +#define GAMESCR_BIO_HEIGHT 17 + +#define LAST_ACTIVE_BITS(mfd) (player_struct.mfd_func_data[MFD_BIOHELP_FUNC][mfd]) +#define LAST_USED_BITS(mfd) (player_struct.mfd_func_data[MFD_BIOHELP_FUNC][mfd + 6]) +#define BIOHELP_PAGE (player_struct.mfd_func_data[MFD_BIOHELP_FUNC][2]) +#define NUM_TRACKS (player_struct.mfd_func_data[MFD_BIOHELP_FUNC][3]) + +void mfd_biohelp_expose(MFD *mfd, ubyte control) { + uchar full = control & MFD_EXPOSE_FULL; + if (control == 0) // MFD is drawing stuff + { + // Do unexpose stuff here. + } + if (control & MFD_EXPOSE) // Time to draw stuff + { + int i; + int firsttrack = 0; + int track = 0; + ubyte bits = 0; + // clear update rects + mfd_clear_rects(); + // set up canvas + gr_push_canvas(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + // Clear the canvas by drawing the background bitmap + if (!full_game_3d) + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + + // figure out where to start, and what tracks are active. + for (track = 0, i = 0; track < NUM_BIO_TRACKS; track++) { + if (status_track_active(track)) { + bits |= 1 << track; + } + if (!status_track_free(track)) { + if (i == NUM_BUTTONS * BIOHELP_PAGE) + firsttrack = track; + LAST_USED_BITS(mfd->id) |= 1 << track; + i++; + } else + LAST_USED_BITS(mfd->id) &= ~(1 << track); + } + if (i > NUM_BUTTONS) { + int id = REF_IMG_TinyArrowUp + BIOHELP_PAGE; + draw_raw_resource_bm(id, ARROW_X, ARROW_Y); + } + NUM_TRACKS = i; + full = full || bits != LAST_ACTIVE_BITS(mfd->id); + if (full) + for (i = 0, track = firsttrack; i < NUM_BUTTONS && track < NUM_BIO_TRACKS; i++, track++) { + char buf[50]; + short x, y; + while (status_track_free(track)) { + track++; + if (track >= NUM_BIO_TRACKS) + goto break_out; + } + x = LEFT_MARGIN; + y = TOP_MARGIN + BARRY_HGT * i / NUM_BUTTONS; + draw_raw_resource_bm(REF_IMG_BioIcon1 + track, x, y); + if (!(bits & (1 << track))) + draw_raw_resource_bm(REF_IMG_BioIconNot, x, y); + mfd_add_rect(x, y, x + BARRY_WID, y + BARRY_HGT); + x += BUTTON_WID + LEFT_MARGIN; + y += (BUTTON_HGT - TEXT_HGT) / 2; + get_string(REF_STR_BioHelpBase + track, buf, sizeof(buf)); + mfd_draw_string(buf, x, y, ITEM_COLOR, TRUE); + } + break_out: + LAST_ACTIVE_BITS(mfd->id) = bits; + + // on a full expose, make sure to draw everything + + if (full) + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + // Pop the canvas + gr_pop_canvas(); + // Now that we've popped the canvas, we can send the + // updated mfd to screen + mfd_update_rects(mfd); + } +} + +// -------- +// HANDLERS +// -------- + +uchar mfd_biohelp_button_handler(MFD *mfd, LGPoint bttn, uiEvent *ev, void *data) { + int track = -1; + int i = 0; + if (!(ev->subtype & MOUSE_LDOWN)) + return FALSE; + while (i <= bttn.y + NUM_BUTTONS * BIOHELP_PAGE) { + track++; + if (track >= NUM_BIO_TRACKS) + return FALSE; + if (!status_track_free(track)) + i++; + } + status_track_activate(track, !status_track_active(track)); + if (status_track_active(track)) + player_struct.active_bio_tracks |= 1 << track; + else + player_struct.active_bio_tracks &= ~(1 << track); + mfd_notify_func(MFD_BIOHELP_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); + return TRUE; +} + +uchar mfd_biohelp_handler(MFD *m, uiEvent *e) { + uchar retval = FALSE; + LGPoint pos = e->pos; + if (NUM_TRACKS <= NUM_BUTTONS) + return FALSE; + if (!(e->subtype & MOUSE_LDOWN)) + return FALSE; + pos.x -= m->rect.ul.x; + pos.y -= m->rect.ul.y; + if (pos.x > ARROW_X && pos.y > ARROW_Y) { + BIOHELP_PAGE = !BIOHELP_PAGE; + mfd_notify_func(MFD_BIOHELP_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, TRUE); + retval = TRUE; + } + return retval; +} + +uchar biohelp_region_mouse_handler(uiEvent *ev, LGRegion *reg, intptr_t data) { + if (ev->mouse_data.action & (MOUSE_LDOWN | MOUSE_RDOWN)) { + LGRect start = {{-5, -5}, {5, 5}}; + int mfd = mfd_grab_func(MFD_BIOHELP_FUNC, MFD_INFO_SLOT); + RECT_MOVE(&start, ev->pos); + mfd_zoom_rect(&start, mfd); + + mfd_notify_func(MFD_BIOHELP_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, TRUE); + mfd_change_slot(mfd, MFD_INFO_SLOT); + return TRUE; + } + return FALSE; +} + +LGCursor biohelp_cursor; +grs_bitmap biohelp_cursor_bmap; + +errtype biohelp_load_cursor(void) { + if (biohelp_cursor_bmap.bits != NULL) { + free(biohelp_cursor_bmap.bits); + + memset(&biohelp_cursor, 0, sizeof(LGCursor)); + memset(&biohelp_cursor_bmap, 0, sizeof(grs_bitmap)); + } + + load_res_bitmap_cursor(&biohelp_cursor, &biohelp_cursor_bmap, REF_IMG_QuestionCursor, TRUE); + + return OK; +} + +errtype biohelp_create_mouse_region(LGRegion *root) { + errtype err; + int id; + LGRect r = {{STATUS_X, 0}, {STATUS_X + GAMESCR_BIO_WIDTH, GAMESCR_BIO_HEIGHT}}; + LGRegion *reg = (LGRegion *)malloc(sizeof(LGRegion)); + + if (reg == NULL) + return ERR_NOMEM; + err = region_create(root, reg, &r, 2, 0, REG_USER_CONTROLLED | AUTODESTROY_FLAG, NULL, NULL, NULL, NULL); + if (err != OK) + return err; + err = uiInstallRegionHandler(reg, UI_EVENT_MOUSE, biohelp_region_mouse_handler, 0, &id); + if (err != OK) + return err; + biohelp_load_cursor(); + uiSetRegionDefaultCursor(reg, &biohelp_cursor); + return OK; +} + +errtype mfd_biohelp_init(MFD_Func *f) { + errtype err; + LGPoint bsize = {BARRY_WID, BUTTON_HGT}; + LGPoint bdims = {1, NUM_BUTTONS}; + LGRect r = {{LEFT_MARGIN, TOP_MARGIN}, {ARROW_X, TOP_MARGIN + BARRY_HGT}}; + extern LGRegion *root_region; + err = biohelp_create_mouse_region(root_region); + if (err != OK) + return err; + err = MFDBttnArrayInit(&f->handlers[0], &r, bdims, bsize, mfd_biohelp_button_handler, NULL); + if (err != OK) + return err; + f->handler_count = 1; + return OK; +} diff --git a/engine/src/GameSrc/cardmfd.c b/engine/src/GameSrc/cardmfd.c new file mode 100644 index 0000000..e16233f --- /dev/null +++ b/engine/src/GameSrc/cardmfd.c @@ -0,0 +1,138 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/cardmfd.c $ + * $Revision: 1.9 $ + * $Author: xemu $ + * $Date: 1994/10/16 15:51:58 $ + * + */ + +#include + +#include "mfdint.h" +#include "mfdext.h" +#include "mfddims.h" +#include "mfdfunc.h" +#include "objsim.h" +#include "gamestrn.h" +#include "tools.h" +#include "objprop.h" +#include "colors.h" +#include "fullscrn.h" + +#include "gamescr.h" +#include "otrip.h" +#include "cybstrng.h" +#include "gr2ss.h" + + +// ============================================================ +// ACCESS CARD MFD +// ============================================================ + +// --------------- +// EXPOSE FUNCTION +// --------------- + +#define MFD_CARD_FUNC 24 + +#define Y_STEP 5 +#define DISPLAY_TOP_MARGIN 15 +#define LEFT_X 2 +#define RIGHT_X (MFD_VIEW_WID - 2) +#define CODES_WID (RIGHT_X - LEFT_X + 1) +#define LAST_BITS(mfd) (*(uint32_t *)&(player_struct.mfd_func_data[MFD_CARD_FUNC][mfd * sizeof(uint32_t)])) +#define ITEM_COLOR 0x5A + +void mfd_accesscard_expose(MFD *mfd, ubyte control) { + uchar full = control & MFD_EXPOSE_FULL; + if (control == 0) // MFD is drawing stuff + { + // Do unexpose stuff here. + } + if (control & MFD_EXPOSE) // Time to draw stuff + { + int i; + uint32_t lastbits = LAST_BITS(mfd->id); + uint32_t bits = 0; + // clear update rects + mfd_clear_rects(); + // set up canvas + gr_push_canvas(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + if (!full_game_3d) + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + if (full) { + mfd_draw_string(get_object_long_name(GENCARDS_TRIPLE, NULL, 0), 1, 1, GREEN_YELLOW_BASE, TRUE); + } + i = mfd_bmap_id(GENCARDS_TRIPLE); + draw_raw_resource_bm(i, (MFD_VIEW_WID - res_bm_width(i)) / 2, + DISPLAY_TOP_MARGIN + (MFD_VIEW_HGT - DISPLAY_TOP_MARGIN - res_bm_height(i)) / 2); + + // find the "access cards" object + for (i = 0; i < NUM_GENERAL_SLOTS; i++) { + ObjID obj = player_struct.inventory[i]; + if (obj != OBJ_NULL && ID2TRIP(obj) == GENCARDS_TRIPLE) { + bits = objSmallstuffs[objs[obj].specID].data1; + break; + } + } + + // I wonder if the ||= operator exists. + full = full || bits != lastbits; + // Lets see what access codes we have. + if (full) { + short x = LEFT_X; + short y = DISPLAY_TOP_MARGIN; + short w, h; + uchar old_wrap = mfd_string_wrap; + char buf[256] = ""; + char *s = buf; + for (i = 1; i <= sizeof(uint32_t) * 8; i++) { + if (bits & (1 << i)) { + strcpy(s, get_temp_string(MKREF(RES_accessCards, i << 1))); + s += strlen(s); + *(s++) = ' '; + } + } + *s = '\0'; + gr_set_font((grs_font *)ResLock(MFD_FONT)); + mfd_string_wrap = FALSE; + gr_string_wrap(buf, CODES_WID); + gr_string_size(buf, &w, &h); + x += (CODES_WID - w) / 2; + y += (MFD_VIEW_HGT - h) / 2 - Y_STEP; + mfd_full_draw_string(buf, x, y, ITEM_COLOR, MFD_FONT, TRUE, TRUE); + mfd_string_wrap = old_wrap; + ResUnlock(MFD_FONT); + LAST_BITS(mfd->id) = bits; + } + if (full) + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + // Pop the canvas + gr_pop_canvas(); + // Now that we've popped the canvas, we can send the + // updated mfd to screen + mfd_update_rects(mfd); + } +} diff --git a/engine/src/GameSrc/citres.c b/engine/src/GameSrc/citres.c new file mode 100644 index 0000000..7048c8a --- /dev/null +++ b/engine/src/GameSrc/citres.c @@ -0,0 +1,230 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/citres.c $ + * $Revision: 1.20 $ + * $Author: xemu $ + * $Date: 1994/11/07 13:22:58 $ + * + */ + +#include + +#include "citres.h" +#include "criterr.h" +#include "gr2ss.h" +#include "statics.h" + +// Internal Prototypes +errtype master_load_bitmap_from_res(grs_bitmap *bmp, Id id_num, int i, LGRect *anchor, uchar *p); + +grs_bitmap *lock_bitmap_from_ref_anchor(Ref r, LGRect *anchor) { + FrameDesc *f; + if (r == 0) + return (NULL); + f = RefLock(r); + if (f == NULL) { + // Warning(("Could not lock bitmap %d!",r)); + return (NULL); + } + f->bm.bits = (uchar *)(f + 1); + if (anchor != NULL) + *anchor = f->anchorArea; + // DBG((DSRC_GFX_Anim), + // { + // ss_bitmap(&(f->bm),0,0); + // }); + return (&(f->bm)); +} + +grs_bitmap *get_bitmap_from_ref_anchor(Ref r, LGRect *anchor) { + grs_bitmap *retval = lock_bitmap_from_ref_anchor(r, anchor); + RefUnlock(r); + return retval; +} + +#pragma mark - + +#pragma scheduling off +#pragma global_optimizer off + +errtype master_load_bitmap_from_res(grs_bitmap *bmp, Id id_num, int i, LGRect *anchor, uchar *p) { + extern int memcount; + Ref rid = MKREF(id_num, i); + FrameDesc *f = RefGet(rid); + + if (f == NULL) { + // Warning(("Could not load bitmap from resource #%d!\n",id_num)); + printf("Could not load bitmap from resource #%d!\n", id_num); + return (ERR_FREAD); + } + + if (p == NULL) { + // Caller wants us to allocate a framebuffer. + p = malloc(f->bm.w * f->bm.h); + } + + if (anchor != NULL) + *anchor = f->anchorArea; + + // Copy the bitmap structure across. + if (bmp == NULL) + DEBUG("%s: Trying to assign to a null bmp pointer!", __FUNCTION__); + *bmp = f->bm; + + // Copy the bits. + memcount += f->bm.w * f->bm.h; // FIXME is this needed any more? + if (f->bm.type == BMT_RSD8) { + gr_rsd8_convert(&f->bm, bmp); + // gr_rsd8_convert uses its own buffer, so copy it back. + memcpy(p, bmp->bits, f->bm.w * f->bm.h); + } else { + memcpy(p, f->bm.bits, f->bm.w * f->bm.h); + } + bmp->bits = p; + + return (OK); +} + +#pragma scheduling reset +#pragma global_optimizer reset + +#pragma mark - + +errtype load_bitmap_from_res(grs_bitmap *bmp, Id id_num, int i, uchar transp, LGRect *anchor) { + return master_load_bitmap_from_res(bmp, id_num, i, anchor, NULL); +} + +errtype load_res_bitmap(grs_bitmap *bmp, Ref rid, uchar alloc) { + errtype retval; + + // printf("load_res_bitmap %x : %x\n", REFID(rid), REFINDEX(rid)); + retval = master_load_bitmap_from_res(bmp, REFID(rid), REFINDEX(rid), NULL, (alloc) ? NULL : bmp->bits); + + return (retval); +} + +#ifdef SIMPLER_NONEXTRACTING_WAY +errtype load_res_bitmap(grs_bitmap *bmp, Ref rid, uchar alloc) { + errtype retval = OK; + char *bits = bmp->bits; + FrameDesc *f; + int sz; + extern int memcount; + + f = RefLock(rid); + sz = f->bm.w * f->bm.h; + if (alloc) { + bits = malloc(sz); + if (bits == NULL) { + retval = ERR_NOMEM; + goto out; + } + } + LG_memcpy(bits, (char *)(f + 1), sz); + *bmp = f->bm; + bmp->bits = bits; +out: + RefUnlock(rid); + return retval; +} +#endif + +errtype simple_load_res_bitmap(grs_bitmap *bmp, Ref rid) { return load_res_bitmap(bmp, rid, TRUE); } + +#pragma mark - + +errtype load_res_bitmap_cursor(LGCursor *c, grs_bitmap *bmp, Ref rid, uchar alloc) { + errtype retval = OK; + LGRect anchor; + +#ifdef SVGA_SUPPORT + short w, h; + short temp; + uchar *bits; + grs_bitmap temp_bmp; + grs_canvas temp_canv; + uchar old_over = gr2ss_override; + ss_set_hack_mode(2, &temp); + + gr2ss_override = OVERRIDE_ALL; + master_load_bitmap_from_res(&temp_bmp, REFID(rid), REFINDEX(rid), &anchor, NULL); + w = temp_bmp.w; + h = temp_bmp.h; + ss_point_convert(&w, &h, FALSE); + if (alloc) + bits = (uchar *)malloc(sizeof(char) * w * h); + else + bits = bmp->bits; + if (temp_bmp.bits == NULL) + critical_error(CRITERR_MEM | 5); + gr_init_bm(bmp, bits, BMT_FLAT8, BMF_TRANS, w, h); + gr_make_canvas(bmp, &temp_canv); + gr_push_canvas(&temp_canv); + gr_clear(0); + ss_bitmap(&temp_bmp, 0, 0); + free(temp_bmp.bits); + if (convert_use_mode) { + anchor.ul.x = (SCONV_X(anchor.ul.x) + SCONV_X(anchor.ul.x + 1)) / 2; + anchor.ul.y = (SCONV_Y(anchor.ul.y) + SCONV_Y(anchor.ul.y + 1)) / 2; + } + // gr_set_pixel(34,anchor.ul.x,anchor.ul.y); // test test test + gr_pop_canvas(); + retval = uiMakeBitmapCursor(c, bmp, anchor.ul); + ss_set_hack_mode(0, &temp); + gr2ss_override = old_over; +#else + retval = master_load_bitmap_from_res(bmp, REFID(rid), REFINDEX(rid), + &anchor, (alloc) ? NULL : bmp->bits); + if (retval == OK) { + retval = uiMakeBitmapCursor(c, bmp, anchor.ul); + } +#endif + return retval; +} + +errtype simple_load_res_bitmap_cursor(LGCursor *c, grs_bitmap *bmp, Ref rid) { + return load_res_bitmap_cursor(c, bmp, rid, TRUE); +} + +errtype load_hires_bitmap_cursor(LGCursor *c, grs_bitmap *bmp, Ref rid, uchar alloc) { + errtype retval = OK; + LGRect anchor; + + retval = master_load_bitmap_from_res(bmp, REFID(rid), REFINDEX(rid), + &anchor, (alloc) ? NULL : bmp->bits); + if (retval == OK) { + retval = uiMakeBitmapCursor(c, bmp, anchor.ul); + } + + return retval; +} + +/* +void *CitMalloc(int n) +{ + return(Malloc(n)); +} + +void CitFree(void *p) +{ + Free(p); +} + +*/ diff --git a/engine/src/GameSrc/combat.c b/engine/src/GameSrc/combat.c new file mode 100644 index 0000000..d0129d1 --- /dev/null +++ b/engine/src/GameSrc/combat.c @@ -0,0 +1,303 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/combat.c $ + * $Revision: 1.77 $ + * $Author: minman $ + * $Date: 1994/09/06 17:32:11 $ + * + */ + +#include "combat.h" +#include "objsim.h" +#include "objprop.h" +#include "tools.h" +#include "effect.h" +#include "otrip.h" + +#include "mainloop.h" +#include "player.h" + +#include "frtypes.h" +#include "cybrnd.h" +#include "physunit.h" + +uchar bullet_debug = FALSE; +ObjID terrain_hit_obj = OBJ_NULL; +ObjID terrain_hit_exclusion = OBJ_NULL; + +ObjID simple_ray_caster(Combat_Ray *ray); + +physics_handle ray_cast_wrapper(fix X[3], fix D[3], fix speed, fix mass, fix size, fix range, + physics_handle exclusion); + +physics_handle ray_cast_wrapper(fix X[3], fix D[3], fix speed, fix mass, fix size, fix range, + physics_handle exclusion) { + physics_handle ph; + physics_handle source; + + // give it a little more distance + range += fix_make(0, 0x4000); + + if (exclusion == objs[PLAYER_OBJ].info.ph) + source = objs[PLAYER_OBJ].info.ph; + else + source = -1; + + ph = EDMS_beam_weapon(X, D, speed, mass, size, range, exclusion, source); + + return (ph); +} + +// -------------------------------------------------------------------------------------------------- +// simple_ray_caster() +// +// NOTE: assumes that the ray to be casted is normalized! +// + +ObjID simple_ray_caster(Combat_Ray *ray) { + physics_handle ph; + fix src[3]; + fix dest[3]; + + // copy over the source of the raycast + src[0] = ray->origin.x; + src[1] = ray->origin.y; + src[2] = ray->origin.z; + dest[0] = ray->dx; + dest[1] = ray->dy; + dest[2] = ray->dz; + + terrain_hit_obj = OBJ_NULL; + // terrain_hit_exclusion = physics_handle_to_id(ray->exclusion); + + ph = ray_cast_wrapper(src, dest, ray->speed, ray->mass, ray->size, ray->range, ray->exclusion); + terrain_hit_exclusion = OBJ_NULL; + + ray->origin.x = src[0]; + ray->origin.y = src[1]; + ray->origin.z = src[2]; + + if (ph == -1) // physics handle NULL is valid...... + { + return (terrain_hit_obj); // if we didn't hit terrain object - it'll be OBJ_NULL; + } else { + ray->origin.x = src[0]; + ray->origin.y = src[1]; + ray->origin.z = src[2]; + + return (physics_handle_to_id(ph)); + } +} + +// ------------------------------------------------- +// ray_cast_attack() +// + +ObjID ray_cast_attack(ObjID src, ObjLoc dest, fix bullet_mass, fix bullet_size, fix bullet_speed, fix bullet_range) { + Combat_Ray ray; + Combat_Pt vector; + fix dist; + + // compute the location of the source object + ray.origin.x = fix_from_obj_coord(objs[src].loc.x); + ray.origin.y = fix_from_obj_coord(objs[src].loc.y); + ray.origin.z = fix_from_obj_height(src); + + // compute the vector to the destination object + vector.x = fix_from_obj_coord(dest.x) - ray.origin.x; + vector.y = fix_from_obj_coord(dest.y) - ray.origin.y; + vector.z = fix_from_obj_height_val(dest.z) - ray.origin.z; + + // normalize it baby! + dist = fix_sqrt(fix_mul(vector.x, vector.x) + fix_mul(vector.y, vector.y) + fix_mul(vector.z, vector.z)); + ray.dx = fix_div(vector.x, dist); + ray.dy = fix_div(vector.y, dist); + ray.dz = fix_div(vector.z, dist); + + ray.mass = bullet_mass; + ray.size = bullet_size; + ray.speed = bullet_speed; + ray.range = bullet_range; + ray.exclusion = (src == OBJ_NULL) ? -1 : objs[src].info.ph; + + return (simple_ray_caster(&ray)); +} + +// ------------------------------------------- +// ray_cast_points() +// + +ObjID ray_cast_points(ObjID exclusion, Combat_Pt src, Combat_Pt dest, fix bullet_mass, fix bullet_size, + fix bullet_speed, fix bullet_range) { + Combat_Ray ray; + fix dist; + ObjID target; + + ray.origin = src; + ray.dx = dest.x - src.x; + ray.dy = dest.y - src.y; + ray.dz = dest.z - src.z; + + // normalize vector and convert it + dist = fix_sqrt(fix_mul(ray.dx, ray.dx) + fix_mul(ray.dy, ray.dy) + fix_mul(ray.dz, ray.dz)); + ray.dx = fix_div(ray.dx, dist); + ray.dy = fix_div(ray.dy, dist); + ray.dz = fix_div(ray.dz, dist); + + ray.mass = bullet_mass; + ray.size = bullet_size; + ray.speed = bullet_speed; + ray.range = bullet_range; + ray.exclusion = (exclusion == OBJ_NULL) ? -1 : objs[exclusion].info.ph; + + target = simple_ray_caster(&ray); + return (target); +} + +// ------------------------------------------- +// ray_cast_vector() +// + +ObjID ray_cast_vector(ObjID exclusion, Combat_Pt *src, Combat_Pt vector, fix bullet_mass, fix bullet_size, + fix bullet_speed, fix bullet_range) { + Combat_Ray ray; + ObjID target; + + ray.origin = *src; + ray.dx = vector.x; + ray.dy = vector.y; + ray.dz = vector.z; + + ray.mass = bullet_mass; + ray.size = bullet_size; + ray.speed = bullet_speed; + ray.range = bullet_range; + ray.exclusion = (exclusion == OBJ_NULL) ? -1 : objs[exclusion].info.ph; + + target = simple_ray_caster(&ray); + + // save the location of the hit + src->x = ray.origin.x; + src->y = ray.origin.y; + src->z = ray.origin.z; + + return (target); +} + +// ------------------------------------------- +// ray_cast_objects() +// +ObjID ray_cast_objects(ObjID src, ObjID dest, fix bullet_mass, fix bullet_size, fix bullet_speed, fix bullet_range) { + Combat_Ray ray; + Combat_Pt vector; + fix dist; + Combat_Pt target_loc; + + // compute the location of the source object + ray.origin.x = fix_from_obj_coord(objs[src].loc.x); + ray.origin.y = fix_from_obj_coord(objs[src].loc.y); + + // shift up by half it's radius + ray.origin.z = + fix_from_obj_height(src) + (fix_make(ObjProps[OPNUM(src)].physics_xr, 0) / (PHYSICS_RADIUS_UNIT << 1)); + + if (objs[dest].info.ph != -1) { + State new_state; + void get_phys_state(int ph, State *new_state, ObjID id); + + get_phys_state(objs[dest].info.ph, &new_state, dest); + target_loc.x = new_state.X; + target_loc.y = new_state.Y; + target_loc.z = new_state.Z; + } else { + // ray cast - even though since the object has no physics handle, we won't hit it. + // but we might hit something in the way. + + target_loc.x = fix_from_obj_coord(objs[dest].loc.x); + target_loc.y = fix_from_obj_coord(objs[dest].loc.y); + target_loc.z = + fix_from_obj_height(dest) + (fix_make(ObjProps[OPNUM(src)].physics_xr, 0) / (PHYSICS_RADIUS_UNIT << 1)); + } + + // compute the vector to the destination object + vector.x = target_loc.x - ray.origin.x; + vector.y = target_loc.y - ray.origin.y; + vector.z = target_loc.z - ray.origin.z; + + // normalize vector and convert it + dist = fix_sqrt(fix_mul(vector.x, vector.x) + fix_mul(vector.y, vector.y) + fix_mul(vector.z, vector.z)); + ray.dx = fix_div(vector.x, dist); + ray.dy = fix_div(vector.y, dist); + ray.dz = fix_div(vector.z, dist); + + ray.mass = bullet_mass; + ray.size = bullet_size; + ray.speed = bullet_speed; + ray.range = bullet_range; + ray.exclusion = (src == OBJ_NULL) ? -1 : objs[src].info.ph; + + return (simple_ray_caster(&ray)); +} + +extern g3s_vector main_view_vectors[]; + +// ---------------------------------------------- +// find_fire_vector() +// + +void find_fire_vector(LGPoint *pt, Combat_Pt *vector) { + fix x1, x2, y1, y2, z1, z2; + fix dist; + int x, y; + + x = pt->x - ((fauxrend_context *)_current_fr_context)->xtop; + y = pt->y - ((fauxrend_context *)_current_fr_context)->ytop; + + // view vectors go something like this + // vector[0] - upper right + // vector[1] - lower right + // vector[2] - lower left + // vector[3] - upper left + + x1 = main_view_vectors[2].gX + (fix_mul((main_view_vectors[1].gX - main_view_vectors[2].gX), fix_make(x, 0))) / + ((fauxrend_context *)_current_fr_context)->xwid; + x2 = main_view_vectors[3].gX + (fix_mul((main_view_vectors[0].gX - main_view_vectors[3].gX), fix_make(x, 0))) / + ((fauxrend_context *)_current_fr_context)->xwid; + + // negative because we're changing coordinate frames + y1 = -main_view_vectors[2].gY + (fix_mul((main_view_vectors[2].gY - main_view_vectors[1].gY), fix_make(x, 0))) / + ((fauxrend_context *)_current_fr_context)->xwid; + y2 = -main_view_vectors[3].gY + (fix_mul((main_view_vectors[3].gY - main_view_vectors[0].gY), fix_make(x, 0))) / + ((fauxrend_context *)_current_fr_context)->xwid; + + z1 = main_view_vectors[2].gZ + (fix_mul((main_view_vectors[1].gZ - main_view_vectors[2].gZ), fix_make(x, 0))) / + ((fauxrend_context *)_current_fr_context)->xwid; + z2 = main_view_vectors[3].gZ + (fix_mul((main_view_vectors[0].gZ - main_view_vectors[3].gZ), fix_make(x, 0))) / + ((fauxrend_context *)_current_fr_context)->xwid; + + vector->x = x2 + (fix_mul((x1 - x2), fix_make(y, 0))) / ((fauxrend_context *)_current_fr_context)->ywid; + vector->y = z2 + (fix_mul((z1 - z2), fix_make(y, 0))) / ((fauxrend_context *)_current_fr_context)->ywid; + vector->z = y2 + (fix_mul((y1 - y2), fix_make(y, 0))) / ((fauxrend_context *)_current_fr_context)->ywid; + + dist = fix_sqrt(fix_mul(vector->x, vector->x) + fix_mul(vector->y, vector->y) + fix_mul(vector->z, vector->z)); + vector->x = fix_div(vector->x, dist); + vector->y = fix_div(vector->y, dist); + vector->z = fix_div(vector->z, dist); +} diff --git a/engine/src/GameSrc/cone.c b/engine/src/GameSrc/cone.c new file mode 100644 index 0000000..4777a63 --- /dev/null +++ b/engine/src/GameSrc/cone.c @@ -0,0 +1,1040 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/cone.c $ + * $Revision: 1.48 $ + * $Author: tjs $ + * $Date: 1994/08/29 00:18:59 $ + */ + +#include +#include + +#include "cone.h" +#include "map.h" + +#include "fr3d.h" +#include "frparams.h" +#include "frspans.h" +#include "frflags.h" +#include "tools.h" + +extern uint _fr_curflags; +#define PRINT_PYRAMID + +// temp macros +#define GAME_HEIGHT (fix_make(-(1 << SLOPE_SHIFT_D), 0)) + +#define MAP_X (fix_make(MAP_XSIZE, 0) - fix_make(0, 1)) +#define MAP_Y (fix_make(MAP_YSIZE, 0) - fix_make(0, 1)) +#define FIX_ZERO (fix_make(0, 0)) +#define MAX_PTS 8 + +#define FIX_EPSILON (0x00000010) + +#define FIX_SQRT_MAX 0x005a8279 +#define THREE_QUARTERS_PI ((FIXANG_PI * 3) / 4) + +#define print_point(pt) (print_fix_point(pt.x, pt.y)) + +// Returns the z component of a cross product +#define FIX_CROSS_DIRECTION(line, point) \ + (fix_mul((line.end.x - line.start.x), (point.y - line.start.y)) - \ + fix_mul((point.x - line.start.x), (line.end.y - line.start.y))) + +// Does a (x <= y <= z) check +//#define IN_BETWEEN(x,y,z) (((x) <= (y)) && ((y) <= (z))) +#define IN_BETWEEN(x, y, z) ((((x) <= (y)) && ((y) <= (z))) || (((x) >= (y)) && ((y) >= (z)))) + +typedef struct { + fix x; + fix y; +} fix_point; + +typedef struct { + fix_point start; + fix_point end; +} fix_line; + +// Allocation for the view cone list +fix view_cone_list[MAX_PTS * 2]; +int view_count = 0; +g3s_vector main_view_vectors[4]; + +// wow is this ugly +extern g3s_vector viewer_position; +extern g3s_angvec viewer_orientation; + +// prototypes +void reverse_poly_list(int index, fix *new_pts); +uchar clockwise_poly(int index, fix *poly_pts); +int insert_viewer_position(int index, fix *new_pts, fix_point viewer_point); +int radius_fix(int index, fix *new_pts, fix_point viewer); +void intersect_cone_sides(fix *vlist, int n, fix y_min, fix y_max, int v_left, int v_right, int v_max); + +// ----------------------------------------------------- +// reverse_poly_list() +// +// reverses the poly list + +void reverse_poly_list(int index, fix *new_pts) { + fix temp_pts[MAX_PTS * 2]; + int i; + int n; + + // Copy over the raw data to the temp list + LG_memcpy(temp_pts, new_pts, sizeof(fix) * 2 * index); + + // copy the data back, but in reverse order + // (fastest way????) + for (i = 0, n = (index - 1); i < index; i++, n--) { + new_pts[i * 2] = temp_pts[n * 2]; + new_pts[i * 2 + 1] = temp_pts[n * 2 + 1]; + } +} + +// ------------------------------------------- +// clockwise_poly() +// +// Returns TRUE if polygon's verticies are in clockwise order. +// Special cases: If there are less than 3 verticies, will return TRUE. +// If there are three colinear points, returns TRUE. +// +// Note: Does not handle points that are really really close together. + +uchar clockwise_poly(int index, fix *poly_pts) { + fix temp_pts[MAX_PTS * 2]; + fix_line poly_line; + fix_point point; + fix cross_prd; + uchar clockwise; + uchar extra_div = TRUE; + + if (index < 3) + return (TRUE); // Is a point or line clockwise??? - hmmmmmm, why not? + + LG_memcpy(temp_pts, poly_pts, sizeof(fix) * 2 * index); + while (extra_div) { + int i; + + extra_div = FALSE; + for (i = 0; i < (index * 2); i++) + if (temp_pts[i] > FIX_SQRT_MAX) { + extra_div = TRUE; + break; + } + if (extra_div) + for (i = 0; i < (index * 2); i++) + temp_pts[i] = (temp_pts[i] > FIX_ZERO) ? temp_pts[i] >> 4 : -((fix_abs(temp_pts[i])) >> 4); + } + + poly_line.start.x = temp_pts[0]; + poly_line.start.y = temp_pts[1]; // fix_div(poly_pts[1], FIX_SQRT_MAX); + poly_line.end.x = temp_pts[2]; // fix_div(poly_pts[2], FIX_SQRT_MAX); + poly_line.end.y = temp_pts[3]; // fix_div(poly_pts[3], FIX_SQRT_MAX); + point.x = temp_pts[4]; // fix_div(poly_pts[4], FIX_SQRT_MAX); + point.y = temp_pts[5]; // fix_div(poly_pts[5], FIX_SQRT_MAX); + + cross_prd = FIX_CROSS_DIRECTION(poly_line, point); + if (cross_prd == FIX_ZERO) { + if (index == 3) // Another Straight line - this is different though + clockwise = TRUE; + else { + point.x = temp_pts[6]; // fix_div(poly_pts[6], FIX_SQRT_MAX); + point.y = temp_pts[7]; // fix_div(poly_pts[7], FIX_SQRT_MAX); + cross_prd = FIX_CROSS_DIRECTION(poly_line, point); + if (cross_prd == FIX_ZERO) { + // Warning(("We've got a problem: Four colinear points.\n")); + clockwise = TRUE; + } else + clockwise = (cross_prd < FIX_ZERO); + } + } else + clockwise = (cross_prd < FIX_ZERO); + + return (clockwise); +} + +// -------------------------------------------------------------------- +// insert_viewer_position() +// +// Inserts the viewer_point into the polygon, if the viewer_point does +// not lie within the polygon. +// +// Requires: verticies of polygon to be in clockwise order + +int insert_viewer_position(int index, fix *new_pts, fix_point viewer_point) { + fix_line poly_line; + fix_point vpoint; + fix temp_pts[MAX_PTS * 2]; + fix *current_pt; + fix cross_prd; + int i; + int insert; + + uchar extra_div = TRUE; + uchar inside = FALSE; + + // Reduce values to a reasonable number, so cross product is happy + // Copy over the raw data to the temp list + LG_memcpy(temp_pts, new_pts, sizeof(fix) * 2 * index); + vpoint.x = viewer_point.x; + vpoint.y = viewer_point.y; + + while (extra_div) { + extra_div = FALSE; + for (i = 0; i < (index * 2); i++) + if (temp_pts[i] > FIX_SQRT_MAX) { + extra_div = TRUE; + break; + } + if ((!extra_div) && ((viewer_point.x > FIX_SQRT_MAX) || (viewer_point.y > FIX_SQRT_MAX))) + extra_div = TRUE; + + if (extra_div) { + // Reduce values to a reasonable number, so cross product is happy + for (i = 0; i < (index * 2); i++) + temp_pts[i] = (temp_pts[i] > FIX_ZERO) ? temp_pts[i] >> 4 : -((fix_abs(temp_pts[i])) >> 4); + + vpoint.x = (vpoint.x > FIX_ZERO) ? vpoint.x >> 4 : -((fix_abs(vpoint.x)) >> 4); + vpoint.y = (vpoint.y > FIX_ZERO) ? vpoint.y >> 4 : -((fix_abs(vpoint.y)) >> 4); + } + } + + poly_line.end.x = temp_pts[(index - 1) * 2]; + poly_line.end.y = temp_pts[(index - 1) * 2 + 1]; + + insert = index; // Represents the point being in the polygon. + + for (i = 0, current_pt = temp_pts; i < index; i++) { + poly_line.start = poly_line.end; + poly_line.end.x = *(current_pt++); + poly_line.end.y = *(current_pt++); + + cross_prd = FIX_CROSS_DIRECTION(poly_line, vpoint); + if (cross_prd == FIX_ZERO) { + // Do something smart about colinear stuff. + if ((IN_BETWEEN(poly_line.start.x, vpoint.x, poly_line.end.x)) && + (IN_BETWEEN(poly_line.start.y, vpoint.y, poly_line.end.y))) { + return (index); + } else if ((IN_BETWEEN(vpoint.x, poly_line.start.x, poly_line.end.x)) && + (IN_BETWEEN(vpoint.y, poly_line.start.y, poly_line.end.y))) { + current_pt -= 4; + *(current_pt++) = vpoint.x; + *(current_pt++) = vpoint.y; + return (index); + } else { + current_pt -= 2; + *(current_pt++) = vpoint.x; + *(current_pt++) = vpoint.y; + return (index); + } + } else if (cross_prd > FIX_ZERO) { + fix slope, inv_slope; + fix inter, inv_inter; + fix interx, intery; + uchar local_inside; + + // check if we don't have to compute the intersection point + if (poly_line.end.x == poly_line.start.x) { + interx = poly_line.start.x; + intery = vpoint.y; + } else if (poly_line.end.y == poly_line.start.y) { + interx = vpoint.x; + intery = poly_line.start.y; + } else { + slope = fix_div((poly_line.end.y - poly_line.start.y), (poly_line.end.x - poly_line.start.x)); + inv_slope = -fix_div(fix_make(1, 0), slope); + inter = poly_line.end.y - fix_mul(poly_line.end.x, slope); + inv_inter = vpoint.y - fix_mul(vpoint.x, inv_slope); + + interx = fix_div((inter - inv_inter), (inv_slope - slope)); + intery = fix_mul(slope, interx) + inter; + } + + local_inside = (IN_BETWEEN(poly_line.start.x, interx, poly_line.end.x) && + IN_BETWEEN(poly_line.start.y, intery, poly_line.end.y)); + + if (inside) { + if (local_inside) { + // Warning(("Outside two vectors of polygon: Insert - %d. Index - %d.\n", insert, + // i)); + // Warning(("vpoint:\n")); + // warning_fix_point(vpoint.x, vpoint.y); + // Warning(("Modified Poly List:\n")); + // warning_poly_list(index, temp_pts); + // Warning(("Viewer Point:\n")); + // warning_fix_point(viewer_point.x, viewer_point.gY); + // Warning(("Original Poly List:\n")); + // warning_poly_list(index, new_pts); + // Warning(("Finding Art would probably be a very good thing!!!!\n")); + // mprintf(("View vectors:\n")); + // print_view_vectors(0, 0, 0L); + } + } else { + insert = i; + inside = local_inside; + } + } + } + + if (insert != index) { + // shift data over, so we can insert the viewer point + + current_pt = new_pts + (insert * 2); + LG_memmove(current_pt + 2, current_pt, sizeof(fix) * 2 * (index - insert)); + *(current_pt) = viewer_point.x; + *(current_pt + 1) = viewer_point.y; + insert = index + 1; + } + return (insert); +} + +// -------------------------------------------------------- +// radius_fix() +// +// Takes the points and a center view point and rearranges the +// points so they are in order in a circle (counter-clockwise) + +int radius_fix(int index, fix *new_pts, fix_point viewer) { + fix_line poly_line; + fix_point test_pt; + int i, j, k; + int new_index; + int counter; + uchar middle; + uchar second, third; + fix x, y, x2, y2; + fix *current_pt; + fix *insert_pt; + fix tempx, tempy; + fix temp_pts[MAX_PTS * 2]; + fixang pt_ang[MAX_PTS]; + fixang tempang; + fix cross_prd; + + if (index > 4) + ; // Warning(("Too many verticies\n")); + if (index < 3) + return (index); + + // Find the ArcTans for each vertex, but also divide it by the + // larger of the two values to get values under 1. Don't normalize + // cause it's not necessary and it's expensive. + for (i = 0; i < index; i++) { + x = (new_pts[i * 2] - viewer.x); + y = (new_pts[(i * 2) + 1] - viewer.y); + if (fix_abs(y) > fix_abs(x)) { + x = fix_div(x, fix_abs(y)); + y = (y < FIX_ZERO) ? fix_make(-1, 0) : fix_make(1, 0); + } else { + y = fix_div(y, fix_abs(x)); + x = (x < FIX_ZERO) ? fix_make(-1, 0) : fix_make(1, 0); + } + pt_ang[i] = fix_atan2(y, x); + } + + // Sort the list of angles + for (i = 1; i < index; i++) { + for (j = (index - 1); j >= i; j--) { + if (pt_ang[j - 1] > pt_ang[j]) { + k = j * 2; + tempx = new_pts[k - 2]; + tempy = new_pts[k - 1]; + tempang = pt_ang[j - 1]; + new_pts[k - 2] = new_pts[k]; + new_pts[k - 1] = new_pts[k + 1]; + pt_ang[j - 1] = pt_ang[j]; + new_pts[k] = tempx; + new_pts[k + 1] = tempy; + pt_ang[j] = tempang; + } + } + } + + // Check for overlap + LG_memcpy(temp_pts, new_pts, sizeof(fix) * 2 * index); + for (i = 1, j = 0; i < index; i++) { + if (pt_ang[i - 1] >= FIXANG_PI) + break; + if ((pt_ang[i - 1] + THREE_QUARTERS_PI) < pt_ang[i]) { + for (j = 0; j < (index - i); j++) { + new_pts[j * 2] = temp_pts[(i + j) * 2]; + new_pts[j * 2 + 1] = temp_pts[(i + j) * 2 + 1]; + } + for (k = 0; k < (index - j); k++) { + new_pts[(j + k) * 2] = temp_pts[k * 2]; + new_pts[(j + k) * 2 + 1] = temp_pts[k * 2 + 1]; + } + + break; + } + } + + // Check the first pair of verticies for colinearity. (is that a word??) + current_pt = new_pts; + x = *(current_pt); + y = *(current_pt + 1); + x2 = *(current_pt + 2); + y2 = *(current_pt + 3); + + new_index = 1; + insert_pt = current_pt + 2; + middle = FALSE; + if (!((x == x2) || (y == y2))) { + if (index == 3) { + // If there are 3 verticies, then we save the middle to be + // checked with the last vertex. + middle = TRUE; + current_pt += 2; + } else { + new_index++; + current_pt += 4; + insert_pt += 2; + } + } else { + // Remove colinear vertex + current_pt += 4; + } + + // Check the last pair of verticies. + // If we only have three verticies, and we have already removed + // one vertex, we do not need to do the following since the last + // vertex does not belong to a pair. + // Unless of course, we did not remove a vertex, and therefore + // the middle vertex joins the last vertex as the pair. + if ((index != 3) || middle) { + x = *(current_pt); + y = *(current_pt + 1); + x2 = *(current_pt + 2); + y2 = *(current_pt + 3); + + // Again, check if we have a vertex on the same axis, and punt if the + // "first" is inward of the second one(outer most). + if ((x == x2) || (y == y2)) { + // Punt the inner vertex + current_pt += 2; + *(insert_pt++) = *(current_pt++); + *(insert_pt++) = *(current_pt++); + new_index++; + } else { + for (i = 0; i < 4; i++) + *(insert_pt++) = *(current_pt++); + new_index += 2; + } + } else // We have three verticies and we removed one vertex. + { + *(insert_pt++) = *(current_pt++); + *(insert_pt++) = *(current_pt++); + new_index++; + } + + if (new_index > 2) { + uchar extra_div = TRUE; + + // This will remove convexness from the polygon + // We are assuming counter-clockwise, due to sorting by angles. + + second = third = TRUE; + + LG_memcpy(temp_pts, new_pts, sizeof(fix) * 2 * index); + + // shrink down values to compensate for fix-point limitations + while (extra_div) { + extra_div = FALSE; + for (i = 0; i < (index * 2); i++) + if (temp_pts[i] > FIX_SQRT_MAX) { + extra_div = TRUE; + break; + } + if (extra_div) + for (i = 0; i < (index * 2); i++) + temp_pts[i] /= 10; + } + + poly_line.start.x = temp_pts[0]; + poly_line.start.y = temp_pts[1]; + poly_line.end.x = temp_pts[4]; + poly_line.end.y = temp_pts[5]; + test_pt.x = temp_pts[2]; + test_pt.y = temp_pts[3]; + + cross_prd = FIX_CROSS_DIRECTION(poly_line, test_pt); + if (cross_prd >= FIX_ZERO) + second = FALSE; + + if (new_index == 4) { + poly_line.end.x = temp_pts[6]; + poly_line.end.y = temp_pts[7]; + + cross_prd = FIX_CROSS_DIRECTION(poly_line, test_pt); + if (cross_prd >= FIX_ZERO) + second = FALSE; + + test_pt.x = temp_pts[4]; + test_pt.y = temp_pts[5]; + + cross_prd = FIX_CROSS_DIRECTION(poly_line, test_pt); + if (cross_prd >= FIX_ZERO) + third = FALSE; + else { + poly_line.start.x = temp_pts[2]; + poly_line.start.y = temp_pts[3]; + + cross_prd = FIX_CROSS_DIRECTION(poly_line, test_pt); + if (cross_prd >= FIX_ZERO) + third = FALSE; + } + } + + counter = (second) ? 2 : 1; + insert_pt = new_pts + (2 * counter); + + if (third) { + *(insert_pt++) = new_pts[4]; + *(insert_pt++) = new_pts[5]; + counter++; + } + + if (new_index == 4) { + *(insert_pt++) = new_pts[6]; + *(insert_pt++) = new_pts[7]; + counter++; + } + } else + counter = new_index; + + return (counter); +} + +// ---------------------------------------------------------------- +// find_view_area() +// +// modifies an array of points to represents the view area in clockwise order +// *count will have the number of points in the array. + +uchar find_view_area(fix *cone_list, fix floor_val, fix roof_val, int *count, fix radius) { + int i; + fix tx, tz; + fix *new_pts; + int index = 0; + fix ratiox, ratioy, ratioz; + g3s_vector my_view[4]; + fix radius_square; + fix x_val; + fix z_val; + fix len; + fix *current_pt; + fix_point viewer_point; + grs_clip old_clip; + fix height = 0; + // char fix_buffer[80]; + + if (radius <= fix_make(0, 0)) { + *count = 0; + return (FALSE); + } + + radius_square = (radius >= FIX_SQRT_MAX) ? FIX_MAX : fix_mul(radius, radius); + + new_pts = cone_list; + viewer_point.x = viewer_position.gX; + viewer_point.y = viewer_position.gZ; + + g3_get_view_pyramid(my_view); + + if (((_fr_curflags & FR_CURVIEW_MASK) == FR_CURVIEW_STRT) && !(_fr_curflags & FR_HACKCAM_FLAG)) + LG_memcpy(main_view_vectors, my_view, sizeof(g3s_vector) * 4); + + // check if we're looking completely up, or completely down + // if so, we can do something fast + if ((my_view[0].gY > FIX_ZERO) && (my_view[1].gY > FIX_ZERO) && (my_view[2].gY > FIX_ZERO) && + (my_view[3].gY > FIX_ZERO)) { + height = floor_val; + for (i = 0; i < 4; i++) { + if (my_view[i].gY < fix_make(0, 0x0080)) + my_view[i].gY = fix_make(0, 0x0080); + } + } else if ((my_view[0].gY < FIX_ZERO) && (my_view[1].gY < FIX_ZERO) && (my_view[2].gY < FIX_ZERO) && + (my_view[3].gY < FIX_ZERO)) { + height = roof_val; + for (i = 0; i < 4; i++) { + if (my_view[i].gY > -fix_make(0, 0x0080)) + my_view[i].gY = -fix_make(0, 0x0080); + } + } + + if (height != 0) { + index = 4; + // Find all the "raw" values without clipping + for (i = 0; i < 4; i++) { + ratioy = fix_div((height - viewer_position.gY), my_view[i].gY); + x_val = fix_mul(ratioy, my_view[i].gX); + z_val = fix_mul(ratioy, my_view[i].gZ); + + if (radius_square == FIX_MAX) { + new_pts[i * 2] = viewer_position.gX + x_val; + new_pts[i * 2 + 1] = viewer_position.gZ + z_val; + } else { + // deals with fix_point limitations - must shift down so we don't square over 65536 + if ((fix_abs(x_val) > FIX_SQRT_MAX) || (fix_abs(z_val) > FIX_SQRT_MAX)) { + len = fix_mul((fix_abs(x_val) >> 9), (fix_abs(x_val) >> 9)) + + fix_mul((fix_abs(z_val) >> 9), (fix_abs(z_val) >> 9)); + if (len <= (radius_square >> 18)) { + new_pts[i * 2] = viewer_position.gX + x_val; + new_pts[i * 2 + 1] = viewer_position.gZ + z_val; + } else { + len = fix_sqrt(len) << 9; + new_pts[i * 2] = viewer_position.gX + fix_mul(fix_div(radius, len), x_val); + new_pts[i * 2 + 1] = viewer_position.gZ + fix_mul(fix_div(radius, len), z_val); + } + } else { + len = fix_mul(x_val, x_val) + fix_mul(z_val, z_val); + if (len <= radius_square) { + new_pts[i * 2] = viewer_position.gX + x_val; + new_pts[i * 2 + 1] = viewer_position.gZ + z_val; + } else { + len = fix_sqrt(len); + new_pts[i * 2] = viewer_position.gX + + fix_mul(fix_div(radius, len), x_val); //(fix_div(fix_mul(x_val, radius), len); + new_pts[i * 2 + 1] = + viewer_position.gZ + + fix_mul(fix_div(radius, len), z_val); // fix_div(fix_mul(z_val, radius), len); + } + } + } + } + // Make the polygon clockwise, if it isn't already + if (!clockwise_poly(index, new_pts)) { + reverse_poly_list(index, new_pts); + } + } else { + index = 4; + current_pt = new_pts; + for (i = 0; i < 4; i++) { + // Check for duplicate verticies + if ((my_view[i].gX == my_view[(i + 1) % 4].gX) && (my_view[i].gZ == my_view[(i + 1) % 4].gZ)) { + index--; + continue; + } + if (radius_square == FIX_MAX) { + // Find direction of this vector + if (my_view[i].gX < FIX_ZERO) { + tx = FIX_ZERO; + ratiox = fix_div(FIX_ZERO - viewer_position.gX, my_view[i].gX); + } else if (my_view[i].gX > FIX_ZERO) { + tx = MAP_X; + ratiox = fix_div(MAP_X - viewer_position.gX, my_view[i].gX); + } else { + tx = 0; + ratiox = FIX_MIN; + } + + if (my_view[i].gZ < FIX_ZERO) { + tz = FIX_ZERO; + ratioz = fix_div(FIX_ZERO - viewer_position.gZ, my_view[i].gZ); + } else if (my_view[i].gZ > FIX_ZERO) { + tz = MAP_Y; + ratioz = fix_div(MAP_Y - viewer_position.gZ, my_view[i].gZ); + } else { + tz = 0; + ratioz = FIX_MIN; + } + if ((ratiox == FIX_MIN) && (ratioz == FIX_MIN)) { + index--; + continue; + } + + if ((ratiox < FIX_ZERO) && (ratioz < FIX_ZERO)) { + if ((viewer_position.gX < MAP_X) && (viewer_position.gX > FIX_ZERO) && + (viewer_position.gZ < MAP_Y) && (viewer_position.gZ > FIX_ZERO)) { + // Warning(("Negative Ratios for cone clip - inside the map!!!!\n")); + // print_view_vectors(0, 0L, 0); + // Warning(("Go Find Art!\n")); + } + index--; + continue; + } + + if (ratiox < ratioz) + tx = viewer_position.gX + fix_mul(ratioz, my_view[i].gX); + else if (ratiox > ratioz) + tz = viewer_position.gZ + fix_mul(ratiox, my_view[i].gZ); + } else { + if (fix_abs(my_view[i].gX) > fix_abs(my_view[i].gZ)) { + tx = (my_view[i].gX < 0) ? -radius : radius; + tz = fix_mul(fix_div(tx, my_view[i].gX), my_view[i].gZ); + } else { + tz = (my_view[i].gZ < 0) ? -radius : radius; + tx = fix_mul(fix_div(tz, my_view[i].gZ), my_view[i].gX); + } + + len = fix_sqrt(fix_mul(tx, tx) + fix_mul(tz, tz)); + tx = viewer_position.gX + fix_mul(fix_div(radius, len), tx); // fix_div(fix_mul(tx, radius), len); + tz = viewer_position.gZ + fix_mul(fix_div(radius, len), tz); // fix_mul(tz, radius), len); + } + *(current_pt++) = tx; + *(current_pt++) = tz; + } + + if (index < 2) { + WARN("%s: HEY - Only one point for cone - this is bad...", __FUNCTION__); + } + + index = radius_fix(index, new_pts, viewer_point); + reverse_poly_list(index, new_pts); + } + + index = insert_viewer_position(index, new_pts, viewer_point); + + // Clip the polygon + old_clip.f = grd_fix_clip; + gr_set_fix_cliprect(FIX_ZERO, FIX_ZERO, MAP_X, MAP_Y); + + index = gr_clip_fix_poly(index, new_pts, new_pts); + grd_fix_clip = old_clip.f; + + // I don't think we need this stuff, and when it gets called - it + // causes bad things to happen!!! + // if (!clockwise_poly(index, new_pts)) + // { + // reverse_poly_list(index, new_pts); + // mprintf("I guess we need this darn thing, or is it a bug????\n\n"); + // } + + *count = index; + + if (index > 2) + return (TRUE); + else + return (FALSE); +} + +// --------------------------------------------- +// intersect_cone_sides +// + +fix span_lines[8]; +byte span_index[2]; +fix span_intersect[4]; + +void intersect_cone_sides(fix *vlist, int n, fix y_min, fix y_max, int v_left, int v_right, int v_max) { + fix deltax, deltay; + int s_left, s_right; + fix y; + + // get the viewer's position - and take the bottom + // y = fix_trunc(viewer_position.gZ); + y = viewer_position.gZ; + + if ((y_min == y) || (y_max == y)) { + if (y_max == y) { + v_left = v_right = v_max; + + while (vlist[2 * ((v_left + n - 1) % n) + 1] == y_max) + v_left = (v_left + n - 1) % n; + while (vlist[2 * ((v_right + 1) % n) + 1] == y_max) + v_right = (v_right + 1) % n; + } + // do the left side + span_lines[0] = vlist[2 * ((v_left + n - 1) % n)] - vlist[2 * v_left]; + span_lines[1] = vlist[2 * ((v_left + n - 1) % n) + 1] - vlist[2 * v_left + 1]; + span_lines[2] = vlist[2 * ((v_left + 1) % n)] - vlist[2 * v_left]; + span_lines[3] = vlist[2 * ((v_left + 1) % n) + 1] - vlist[2 * v_left + 1]; + span_index[0] = -(v_left + 1); + span_intersect[0] = vlist[2 * v_left]; + span_intersect[1] = vlist[2 * v_left + 1]; + + // do the right side + span_lines[4] = vlist[2 * ((v_right + n - 1) % n)] - vlist[2 * v_right]; + span_lines[5] = vlist[2 * ((v_right + n - 1) % n) + 1] - vlist[2 * v_right + 1]; + span_lines[6] = vlist[2 * ((v_right + 1) % n)] - vlist[2 * v_right]; + span_lines[7] = vlist[2 * ((v_right + 1) % n) + 1] - vlist[2 * v_right + 1]; + span_index[1] = -(v_right + 1); + span_intersect[2] = vlist[2 * v_right]; + span_intersect[3] = vlist[2 * v_right + 1]; + } else { + s_left = v_left; + s_right = v_right; + + while (vlist[2 * ((s_left + 1) % n) + 1] < (y - FIX_EPSILON)) + s_left = (s_left + 1) % n; + + while (vlist[2 * ((s_right + n - 1) % n) + 1] < (y - FIX_EPSILON)) + s_right = (s_right + n - 1) % n; + + // first check if crossing is at upper point of vector + if (y == vlist[2 * ((s_left + 1) % n) + 1]) { + span_lines[0] = vlist[2 * s_left] - vlist[2 * ((s_left + 1) % n)]; + span_lines[1] = vlist[2 * s_left + 1] - vlist[2 * ((s_left + 1) % n) + 1]; + span_lines[2] = vlist[2 * ((s_left + 2) % n)] - vlist[2 * ((s_left + 1) % n)]; + span_lines[3] = vlist[2 * ((s_left + 2) % n) + 1] - vlist[2 * ((s_left + 1) % n) + 1]; + span_index[0] = -((s_left + 1) % n) - 1; + span_intersect[0] = vlist[2 * ((s_left + 1) % n)]; + span_intersect[1] = vlist[2 * ((s_left + 1) % n) + 1]; + } else { + // do the left side first + deltax = vlist[2 * ((s_left + 1) % n)] - vlist[2 * s_left]; + deltay = vlist[2 * ((s_left + 1) % n) + 1] - vlist[2 * s_left + 1]; + + span_lines[0] = -deltax; + span_lines[1] = -deltay; + span_lines[2] = deltax; + span_lines[3] = deltay; + span_index[0] = s_left; + + span_intersect[0] = vlist[2 * s_left] + fix_mul(fix_div((y - vlist[2 * s_left + 1]), deltay), deltax); + // fix_div(fix_mul((y-vlist[2*s_left+1]), deltax), deltay); + span_intersect[1] = y; + } + + // first check if crossing is at upper point of vector + if (y == vlist[2 * ((s_right + n - 1) % n) + 1]) { + span_lines[4] = vlist[2 * ((s_right + n - 2) % n)] - vlist[2 * ((s_right + n - 1) % n)]; + span_lines[5] = vlist[2 * ((s_right + n - 2) % n) + 1] - vlist[2 * ((s_right + n - 1) % n) + 1]; + span_lines[6] = vlist[2 * s_right] - vlist[2 * ((s_right + n - 1) % n)]; + span_lines[7] = vlist[2 * s_right + 1] - vlist[2 * ((s_right + n - 1) % n) + 1]; + span_index[1] = -((s_right + n - 1) % n) - 1; + span_intersect[2] = vlist[2 * ((s_right + n - 1) % n)]; + span_intersect[3] = vlist[2 * ((s_right + n - 1) % n) + 1]; + } else { + // do the right side next + deltax = vlist[2 * ((s_right + n - 1) % n)] - vlist[2 * s_right]; + deltay = vlist[2 * ((s_right + n - 1) % n) + 1] - vlist[2 * s_right + 1]; + + span_lines[4] = deltax; + span_lines[5] = deltay; + span_lines[6] = -deltax; + span_lines[7] = -deltay; + span_index[1] = (s_right + n - 1) % n; + + span_intersect[2] = vlist[2 * s_right] + fix_mul(fix_div((y - vlist[2 * s_right + 1]), deltay), deltax); + // fix_div(fix_mul((y - vlist[2*s_right+1]), deltax), deltay); + span_intersect[3] = y; + } + } +} + +// -------------------------------------------- +// simple_cone_clip_pass() +// +// Cone clips the area, and calls store_x_span on the +// contents of the cone. +// + +void simple_cone_clip_pass(void) { + int n; + int i; + byte v_min; // vertex with smallest y coord + byte v_max; // vertex with largest y coord + byte v_left, v_right; // current left & right vertices + // byte v_prev; // previous vertex + int y; // current scanline + int y_top; + fix left, right; // the left and right values on scan line, making sure scan line does not go past end points + fix y_min, y_max; // min & max vertex y coords + fix y_left, y_right; // ending y for left & right edges + fix x_min, x_max; // min & max x coords + fix x_left, x_right; // scanline x intersections + fix m_prev; // previous slopes + fix m_left = fix_make(-1, 0); + fix m_right = fix_make(1, 0); // look - slopes for right/left edges + fix d; // difference for slope computations + fix x_abs_left, x_abs_right; // min or max value of endpoint for that line of the polygon + fix x_outer_left, x_outer_right; // used to determine if that line is horizontal + fix x_shift; + uchar right_line, left_line; // looking for line + uchar right_repeat, left_repeat; // looking for repeat on the line + + // get the view polygon - if there's not a valid cone, then just return. + if (!find_view_area(view_cone_list, fix_make(0, 0), GAME_HEIGHT, &n, fix_make(_frp.view.radius, 0))) { + // so if we don't have a valid cone, let's check if we're in the map first before + // spewing a warning message + + if ((viewer_position.gX < MAP_X) && (viewer_position.gX > FIX_ZERO) && (viewer_position.gZ < MAP_Y) && + (viewer_position.gZ > FIX_ZERO)) { + WARN("%s: Not a valid cone found and we're inside the map!", __FUNCTION__); + } + return; + } + + view_count = n; + + // initialize these to weenie values. + x_min = y_min = FIX_MAX; + x_max = y_max = 0; + + // find the y coordinate of the highest and lowest vertices; save the + // vertex number of the highest. + for (i = 0; i < n; i++) { + if (view_cone_list[2 * i] < x_min) + x_min = view_cone_list[2 * i]; + if (view_cone_list[2 * i] > x_max) + x_max = view_cone_list[2 * i]; + if (view_cone_list[2 * i + 1] < y_min) { + y_min = view_cone_list[2 * i + 1]; + v_min = i; + } + if (view_cone_list[2 * i + 1] > y_max) { + y_max = view_cone_list[2 * i + 1]; + v_max = i; + } + } + + // printf("y_min: %f, y_max: %f\n", fix_float(y_min), fix_float(y_max)); + + /* check if this is a horizontal line. */ + if (fix_int(y_min) == fix_int(y_max)) { + cone_span_set(fix_int(y_min), fix_int(x_min), fix_int(x_max)); + return; + } + + /* we want to set v_left and v_right to be leftmost and rightmost vertices + with y = y_min. usually, both are v_min, but if there is a horizontal + edge at y = y_min, they will be different. */ + + v_left = v_right = v_min; + while (view_cone_list[2 * ((v_left + 1) % n) + 1] == y_min) + v_left = (v_left + 1) % n; + while (view_cone_list[2 * ((v_right + n - 1) % n) + 1] == y_min) + v_right = (v_right + n - 1) % n; + + // printf("v_left: %i, v_right: %i\n", fix_float(v_left), fix_float(v_right)); + + intersect_cone_sides(view_cone_list, n, y_min, y_max, v_left, v_right, v_max); + + // Check if top line is the max line - if so then decrement; + y_top = fix_int(y_max); + if (y_top == MAP_YSIZE) + y_top = MAP_YSIZE - 1; + + /* draw each span, starting at y_min. */ + for (y = fix_int(y_min); y < y_top; y++) { + /* process completed left edge(s). */ + left_line = left_repeat = FALSE; + x_outer_left = fix_make(-1, 0); + while (fix_int(view_cone_list[2 * v_left + 1]) == y) { + m_prev = m_left; + x_left = view_cone_list[2 * v_left]; + y_left = view_cone_list[2 * v_left + 1]; + v_left = (v_left + 1) % n; + if (left_repeat) { + x_outer_left = lg_min(x_outer_left, x_left); + left_line = TRUE; + } else { + x_outer_left = x_left; + if (m_prev > fix_make(0, 0)) { + left_line = TRUE; + x_outer_left -= fix_mul(fix_frac(y_left), m_prev); // save the left point's X coordinate + } + + left_repeat = TRUE; // signal that if we do this again - we've repeated + } + x_abs_left = lg_min(view_cone_list[2 * v_left], x_left); + d = view_cone_list[2 * v_left + 1] - y_left; + + // if the next point is above the current - calculate the slope + if (fix_int(view_cone_list[2 * v_left + 1]) > fix_int(y_left)) { + m_left = fix_div(view_cone_list[2 * v_left] - x_left, d); + + x_shift = 0; + + // this gets to the leftmost point of the line - either on top or bottom of the pixel + d = (m_left < 0) ? (fix_make(1, 0) - fix_frac(y_left)) : fix_frac(y_left); + x_shift = fix_abs(fix_mul(d, m_left)); + + // shift over - if the line before does for any left over + if (m_prev > fix_make(0, 0)) { + d = fix_abs(fix_mul(fix_frac(y_left), m_prev)); + x_shift = lg_max(d, x_shift); + } + x_left -= x_shift; + } + } + + right_line = right_repeat = FALSE; + x_outer_right = fix_make(-1, 0); + + /* process completed right edge(s). */ + while (fix_int(view_cone_list[2 * v_right + 1]) == y) { + m_prev = m_right; + x_right = view_cone_list[2 * v_right]; + y_right = view_cone_list[2 * v_right + 1]; + v_right = (v_right + n - 1) % n; + if (right_repeat) { + x_outer_right = lg_max(x_outer_right, x_right); + right_line = TRUE; + } else { + x_outer_right = x_right; + if (m_prev < fix_make(0, 0)) { + x_outer_right += fix_abs(fix_mul(fix_frac(y_right), m_prev)); + right_line = TRUE; + } + right_repeat = TRUE; + } + x_abs_right = lg_max(view_cone_list[2 * v_right], x_right); + d = view_cone_list[2 * v_right + 1] - y_right; + + // if the next point is above the current - calculate the slope + if (fix_int(view_cone_list[2 * v_right + 1]) > fix_int(y_right)) { + m_right = fix_div(view_cone_list[2 * v_right] - x_right, d); + + d = (m_right > 0) ? (fix_make(1, 0) - fix_frac(y_right)) : fix_frac(y_right); + x_shift = fix_abs(fix_mul(d, m_right)); + + if (m_prev < fix_make(0, 0)) { + d = fix_abs(fix_mul(fix_frac(y_right), m_prev)); + x_shift = lg_max(d, x_shift); + } + x_right += x_shift; + } + } + /* draw this scanline and calculate x intersections with next. */ + if (fix_int(x_left) <= fix_int(x_right)) { + left = x_left; + right = x_right; + + // checking that we don't go pass the endpoints - x_abs_left + left = lg_max(x_left, x_abs_left); + right = lg_min(x_right, x_abs_right); + + // checking for horizontal lines (using x_outer_left) + // if so, take the leftmost or rightmost point. + if (left_line) + left = lg_min(left, x_outer_left); + if (right_line) + right = lg_max(right, x_outer_right); + + cone_span_set(y, fix_int(left), fix_int(right)); + } + x_left += m_left; + x_right += m_right; + } + + // This is for the top scan line - which shouldn't be done with the + // above, because we'd get just the vertex point, if we've got + // a broad angle. (kindof difficult to explain) + if (fix_int(x_left) <= fix_int(x_right)) { + // checking that we don't go pass the endpoints - x_abs_left + left = lg_max(x_left, x_abs_left); + right = lg_min(x_right, x_abs_right); + + cone_span_set(y, fix_int(left), fix_int(right)); + } +} diff --git a/engine/src/GameSrc/criterr.c b/engine/src/GameSrc/criterr.c new file mode 100644 index 0000000..b9b06f0 --- /dev/null +++ b/engine/src/GameSrc/criterr.c @@ -0,0 +1,213 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include +#include + +#include "Shock.h" +#include "InitMac.h" +#include "criterr.h" + +/* + * $Source: r:/prj/cit/src/RCS/criterr.c $ + * $Revision: 1.22 $ + * $Author: xemu $ + * $Date: 1994/11/26 03:37:32 $ + * + * + */ + +// ------- +// DEFINES +// ------- +#define GAME_NAME "System Shock" +#define CLASS(x) ((x) >> 12) +#define TYPE(x) ((x)&0xFFF) + +// ------------------------------ +// THE GLOBAL ERROR STRING ARRAYS +// ------------------------------ + +static char *criterr_type_messages[CRITERR_CLASSES] = {"Test", + "Configuration error", + "Resource error", + "Memory error", + "File error", + "Execution error", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "General failure"}; + +typedef struct _code_string { + unsigned short code; + char *message; +} _code_string; + +static _code_string code_messages[] = { + {CRITERR_TEST | 1, ": There is no cause for alarm, return to your homes."}, + {CRITERR_CFG | 0, ": Mouse driver not installed."}, + {CRITERR_RES | 0, ": Could not open string resource."}, + {CRITERR_RES | 1, ": Could not open game screen resource."}, + {CRITERR_RES | 2, ": Could not open MFD resource."}, + {CRITERR_RES | 3, ": Could not find hand art."}, + {CRITERR_RES | 4, ": Could not find palette."}, + {CRITERR_RES | 5, ": Could not open object art resource."}, + {CRITERR_RES | 6, ": Could not open side icon art resource."}, + {CRITERR_RES | 7, ": Could not open texture map resource."}, + {CRITERR_RES | 8, ": Could not find cache-loaded object art."}, + {CRITERR_RES | 9, ": Could not open 3d model resource!"}, + {CRITERR_RES | 0xA, ": Could not initialize popup cursors!"}, + {CRITERR_RES | 0x10, ": Could not find level archive!"}, + {CRITERR_MISC | 0, ": Bad object properties version number."}, + {CRITERR_MEM | 0, ": Out of memory allocating map."}, + {CRITERR_MEM | 1, ": Not enough memory to run game!"}, + {CRITERR_MEM | 2, ": Not enough memory to create critter caches!"}, + {CRITERR_MEM | 3, ": Not enough memory to load texture maps!"}, + {CRITERR_MEM | 4, ": Not enough memory to make popup cursors!"}, + {CRITERR_MEM | 5, ": Not enough memory to create SVGA cursor!"}, + {CRITERR_MEM | 7, ": Not enough memory to make email cursor!"}, + {CRITERR_MEM | 8, ": Frame buffer too small for vmail!"}, + {CRITERR_MEM | 9, ": Not enough cache memory to load bitmap!"}, + {CRITERR_FILE | 0, ": Could not load level file!"}, + {CRITERR_FILE | 1, ": Could not load level from archive!"}, + {CRITERR_FILE | 2, ": Could not find configuration file!"}, + {CRITERR_FILE | 3, ": Unknown Save Game failure!"}, + {CRITERR_FILE | 4, ": Unknown level-change writing failure!"}, + {CRITERR_FILE | 5, ": Corrupted save game!!"}, + {CRITERR_FILE | 6, ": Error saving game - likely insufficient disk space."}, + {CRITERR_FILE | 7, ": Error creating initial game - likely insufficient disk space."}, + {CRITERR_EXEC | 1, ": Cannot run directly from CD. Run executable from hard drive."}, + {CRITERR_EXEC | 2, ": Unknown physics error!"}, +}; + +#define NUM_CODE_MESSAGES (sizeof(code_messages) / sizeof(_code_string)) +/* +char *help_messages[] = { +"", +"Common problem solutions:", +"* Increase FILES in config.sys to 30 or more.", +"* Disable SMARTDRV write caching", +"* Use a minimal config.sys and autoexec.bat", +"", +"If none of these work, call Origin Tech Support", +"(512) 335-0440" +}; + +#define NUM_HELP_MESSAGES 8 + +// -------------------------------- +// THE GLOBAL ERROR STATUS VARIABLE +// -------------------------------- + +static int error_status = NO_CRITICAL_ERROR; + +// --------- +// INTERNALS +// --------- + +void handle_critical_error(void) +{ + int i; + char* s; + char buf[256]; + if (error_status == NO_CRITICAL_ERROR) return; + strcpy(buf,GAME_NAME); + strcat(buf," can no longer run due to a fatal error."); + puts(buf); + strcpy(buf,"Error code "); itoa(error_status,buf+strlen(buf),16); + puts(buf); + s = criterr_type_messages[CLASS(error_status)]; + if (s != NULL) { puts(s);}; + for (i = 0; i < NUM_CODE_MESSAGES; i++) + { + if (code_messages[i].code == error_status) + { + puts(code_messages[i].message); + } + } + for (i=0; i < NUM_HELP_MESSAGES; i++) + puts(help_messages[i]); +} +*/ +// --------- +// EXTERNALS +// --------- + +/* KLC - Not needed for Mac +void criterr_init(void) +{ + error_status = NO_CRITICAL_ERROR; + atexit(handle_critical_error); +} +*/ + +void critical_error(unsigned short code) { + char buf[256]; + char explain[256]; + char *s; + int i, len; + + if (code == NO_CRITICAL_ERROR) + return; + +#if 1 + STUB_ONCE("Maybe use SDL_ShowSimpleMessageBox() ?"); + + printf("A fatal error has occured in System Shock. Error code %d.\n", code); + + s = criterr_type_messages[CLASS(code)]; // Specific error message. + if (s != NULL) + strcpy(explain, s); + for (i = 0; i < NUM_CODE_MESSAGES; i++) + if (code_messages[i].code == code) + strcat(explain, code_messages[i].message); + + printf(" %s\n", explain); + +#else + sprintf(buf, "A fatal error has occurred in System Shock. Error code %d.", code); + len = strlen(buf); // Convert to p-string. + BlockMove(buf, buf + 1, 255); + buf[0] = len; + + s = criterr_type_messages[CLASS(code)]; // Specific error message. + if (s != NULL) + strcpy(explain, s); + for (i = 0; i < NUM_CODE_MESSAGES; i++) + if (code_messages[i].code == code) + strcat(explain, code_messages[i].message); + len = strlen(explain); // Convert to p-string. + BlockMove(explain, explain + 1, 255); + explain[0] = len; + + ParamText((uchar *)buf, (uchar *)explain, "", ""); // Show the error. + if (len > 0) + StopAlert(1001, nil); + else + StopAlert(1000, nil); +#endif + + CleanupAndExit(); // Get out of here. +} diff --git a/engine/src/GameSrc/cutsloop.c b/engine/src/GameSrc/cutsloop.c new file mode 100644 index 0000000..4a1ddd2 --- /dev/null +++ b/engine/src/GameSrc/cutsloop.c @@ -0,0 +1,337 @@ +/* + +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#include + +#include "Shock.h" +#include "cutsloop.h" +#include "mainloop.h" +#include "fullscrn.h" +#include "game_screen.h" +#include "gamescr.h" +#include "tools.h" +#include "statics.h" + +#include "MacTune.h" +#include "gr2ss.h" + +#include "afile.h" +#include "movie.h" + +SDL_AudioStream *cutscene_audiostream = NULL; + +static uint8_t *cutscene_audiobuffer = NULL; +static uint8_t *cutscene_audiobuffer_pos = NULL; +static int cutscene_audiobuffer_size; //in blocks of MOVIE_DEFAULT_BLOCKLEN + + + +static int cutscene_filehandle; + +static uiSlab cutscene_slab; +static LGRegion cutscene_root_region; + +static Afile *amovie = NULL; +static Apalette cutscene_pal; +static grs_bitmap movie_bitmap; +static long next_time; + +static bool is_first_frame; +static bool done_playing_movie; +static long start_time; +static long next_draw_time; + +static char *cutscene_files[3] = +{ + "res/data/svgaintr.res", + "res/data/svgadeth.res", + "res/data/svgaend.res" +}; + +static Ref cutscene_anims[3] = +{ + 0xbd6, + 0xbd7, + 0xbd8 +}; + + + +extern uchar sfx_on; +extern char which_lang; + +extern bool UseCutscenePalette; //see Shock.c + +//filled in amov.c when chunk contains subtitle data +extern char EngSubtitle[256]; +extern char FrnSubtitle[256]; +extern char GerSubtitle[256]; + +extern SDL_AudioDeviceID device; + + +void AudioStreamCallback(void *userdata, unsigned char *stream, int len) +{ + SDL_AudioStream *as = *(SDL_AudioStream **)userdata; + + if (as != NULL && SDL_AudioStreamAvailable(as) > 0) + SDL_AudioStreamGet(as, stream, len); +} + + + +uchar cutscene_key_handler(uiEvent *ev, LGRegion *r, intptr_t user_data) +{ + uiCookedKeyData *kd = &ev->cooked_key_data; + int code = kd->code & ~(KB_FLAG_DOWN | KB_FLAG_2ND); + + if (kd->code & KB_FLAG_DOWN) + { + switch (code) + { + case KEY_ESC: + case KEY_ENTER: + case KEY_SPACE: + // Go back to the main menu + _new_mode = SETUP_LOOP; + chg_set_flg(GL_CHG_LOOP); + break; + } + } + + return TRUE; +} + + + +uchar cutscene_mouse_handler(uiEvent *ev, LGRegion *r, intptr_t user_data) +{ + return TRUE; +} + + + +void cutscene_start(void) +{ + DEBUG("Cutscene start"); + +#ifdef SVGA_SUPPORT + change_svga_screen_mode(); +#endif + + generic_reg_init(TRUE, &cutscene_root_region, NULL, &cutscene_slab, cutscene_key_handler, cutscene_mouse_handler); + + _current_view = &cutscene_root_region; + uiSetCurrentSlab(&cutscene_slab); + + uiHideMouse(NULL); + + CaptureMouse(FALSE); +} + + + +void cutscene_exit(void) +{ + DEBUG("Cutscene exit"); + + if (cutscene_audiostream != NULL) + { + SDL_PauseAudioDevice(device, 1); + SDL_Delay(1); + + SDL_FreeAudioStream(cutscene_audiostream); + cutscene_audiostream = NULL; + + if (cutscene_audiobuffer) + { + free(cutscene_audiobuffer); + cutscene_audiobuffer = NULL; + } + } + + if (cutscene_filehandle > 0) {ResCloseFile(cutscene_filehandle); cutscene_filehandle = 0;} + + if (movie_bitmap.bits != NULL) {free(movie_bitmap.bits); movie_bitmap.bits = NULL;} + + if (amovie != NULL) {free(amovie); amovie = NULL;} +} + + + +void cutscene_loop(void) +{ + fix time; + long cur_time = SDL_GetTicks(); + + static uint8_t palette[3*256]; + + if (cutscene_audiostream) + { + SDL_PauseAudioDevice(device, 0); + + if (cutscene_audiobuffer_size > 0) + { + // === adjust volume in buffer here === + + SDL_AudioStreamPut(cutscene_audiostream, cutscene_audiobuffer_pos, MOVIE_DEFAULT_BLOCKLEN); + cutscene_audiobuffer_pos += MOVIE_DEFAULT_BLOCKLEN; + cutscene_audiobuffer_size--; + } + } + + if (is_first_frame) + { + is_first_frame = FALSE; + next_time = cur_time; + start_time = SDL_GetTicks(); + + // Read the first frame + AfileReadFullFrame(amovie, &movie_bitmap, &time); + next_draw_time = start_time + fix_float(time) * 1000.0; + + // Set the initial palette + memcpy(palette, amovie->v.pal.rgb, 3*256); + + gr_clear(0x00); + } + + if (cur_time > next_draw_time) + { + // Get the palette for this frame, if any + if (AfileGetFramePal(amovie, &cutscene_pal)) + memcpy(palette+3*cutscene_pal.index, cutscene_pal.rgb, 3*cutscene_pal.numcols); + + UseCutscenePalette = TRUE; //see Shock.c + gr_set_pal(0, 256, palette); + UseCutscenePalette = FALSE; //see Shock.c + + gr_set_fcolor(255); + + // Draw this frame + gr_clear(0x00); + + float vscale = (float)amovie->v.height / (float)amovie->v.width; + + int offset = (320 - (amovie->v.width / 2)) / 2; + ss_scale_bitmap(&movie_bitmap, offset, offset / 1.25, amovie->v.width / 2, amovie->v.height / 2); + + //draw subtitles + + char *buf = 0; + switch (which_lang) + { + case 0: default: buf = EngSubtitle; break; + case 1: buf = FrnSubtitle; break; + case 2: buf = GerSubtitle; break; + } + + if (buf && *buf) + { + short w, h, x, y; + grs_font *fon = gr_get_font(); + gr_set_font((grs_font *)ResLock(RES_cutsceneFont)); + gr_string_size(buf, &w, &h); + x = (320-w)/2; + y = 158+(200-158-h)/2; + ss_string(buf, x, y); + ResUnlock(RES_cutsceneFont); + gr_set_font(fon); + } + + if (done_playing_movie) + { + UseCutscenePalette = TRUE; //see Shock.c + extern void palfx_fade_down(void); + palfx_fade_down(); + UseCutscenePalette = FALSE; //see Shock.c + + // Go back to the main menu + _new_mode = SETUP_LOOP; + chg_set_flg(GL_CHG_LOOP); + return; + } + + // Read the next frame + if (AfileReadFullFrame(amovie, &movie_bitmap, &time) == -1) + { + DEBUG("Done playing movie!"); + done_playing_movie = TRUE; + // Still want a bit of a delay before finishing + next_draw_time += 5200; + } + else next_draw_time = start_time + fix_float(time) * 1000.0; + } +} + + + +short play_cutscene(int id, bool show_credits) +{ + MacTuneKillCurrentTheme(); + + cutscene_filehandle = ResOpenFile(cutscene_files[id]); + if (cutscene_filehandle <= 0) { + // If we failed to play the cutscene, go to setup / credits. + _new_mode = SETUP_LOOP; + chg_set_flg(GL_CHG_LOOP); + return 0; + } + + INFO("Playing Cutscene %i", id); + + *EngSubtitle = 0; + *FrnSubtitle = 0; + *GerSubtitle = 0; + + _new_mode = CUTSCENE_LOOP; + chg_set_flg(GL_CHG_LOOP); + + is_first_frame = TRUE; + done_playing_movie = FALSE; + + amovie = malloc(sizeof(Afile)); + memset(amovie, 0, sizeof(Afile)); + + if (AfilePrepareRes(cutscene_anims[id], amovie) < 0) + { + WARN("%s: Cannot open Afile by id $%x", __FUNCTION__, cutscene_anims[id]); + free(amovie); + amovie = NULL; + return ERR_FREAD; + } + + cutscene_audiobuffer_size = AfileAudioLength(amovie); + cutscene_audiobuffer = (uint8_t *)malloc(cutscene_audiobuffer_size * MOVIE_DEFAULT_BLOCKLEN); + AfileGetAudio(amovie, cutscene_audiobuffer); + + AfileReadReset(amovie); + + if (sfx_on) + { + SDL_PauseAudioDevice(device, 1); + SDL_Delay(1); + + cutscene_audiostream = SDL_NewAudioStream(AUDIO_U8, 1, fix_int(amovie->a.sampleRate), AUDIO_S16SYS, 2, 48000); + + cutscene_audiobuffer_pos = cutscene_audiobuffer; + } + + return 1; +} diff --git a/engine/src/GameSrc/cyber.c b/engine/src/GameSrc/cyber.c new file mode 100644 index 0000000..5531913 --- /dev/null +++ b/engine/src/GameSrc/cyber.c @@ -0,0 +1,233 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include + +#include "player.h" +#include "objsim.h" +#include "objects.h" +#include "map.h" +#include "faketime.h" +#include "mfdext.h" +#include "fullscrn.h" +#include "cyber.h" +#include "hud.h" +#include "invent.h" +#include "invpages.h" +#include "input.h" +#include "mainloop.h" +#include "hkeyfunc.h" +#include "rendfx.h" +#include "gr2ss.h" +#include "drugs.h" +#include "init.h" +#include "musicai.h" +#include "saveload.h" +#include "wares.h" + +#define FIRST_CSPACE_LEVEL 14 +#define MIN_CSPACE_EXIT_HP 10 +#define MAX_FATIGUE 10000 + +#define MAX_SHODAN_FAILURES 10 +#define GAME_OVER_HACK 0x6 + +extern uchar *shodan_bitmask; + +ObjID shodan_avatar_id = OBJ_NULL; +uint32_t time_until_shodan_avatar = 0; + +ObjID cspace_decoy_obj = OBJ_NULL; +ObjLoc recall_objloc; + +ulong cspace_effect_times[NUM_CS_EFFECTS] = {0, 0, 0}; +ulong cspace_effect_durations[NUM_CS_EFFECTS] = {CIT_CYCLE * 30, CIT_CYCLE * 15, CIT_CYCLE}; +void (*cspace_effect_turnoff[])(uchar visible, uchar real) = {turbo_turnoff, decoy_turnoff, NULL}; + +uchar cyber_nodie = FALSE; +// FILE *gCyberHdl; + +errtype check_cspace_death() { + if (global_fullmap->cyber) { + if (player_struct.cspace_hp == 1) + hud_set(HUD_CYBERDANGER); + else if (player_struct.cspace_hp == 0) { + // If we're in endgame mode, we lose. + if (shodan_bitmask != NULL) { + extern char thresh_fail; + if (!cyber_nodie) { + cyber_nodie = TRUE; + mai_player_death(); + time_until_shodan_avatar = player_struct.game_time + (CIT_CYCLE * 8); + } + if (thresh_fail <= MAX_SHODAN_FAILURES) { + memset(shodan_bitmask, 0xFF, SHODAN_BITMASK_SIZE / 8); + thresh_fail = MAX_SHODAN_FAILURES + 1; + } + } else if (!cyber_nodie) { + // Delete the shodan_object, if there is one. + if (shodan_avatar_id != OBJ_NULL) { + obj_destroy(shodan_avatar_id); + shodan_avatar_id = OBJ_NULL; + } + + // boot player out of cspace + player_struct.cspace_time_base = + lg_max(CSPACE_MIN_TIME, player_struct.cspace_time_base - CSPACE_DEATH_PENALTY); + go_to_different_level(player_struct.realspace_level); + obj_move_to(PLAYER_OBJ, &player_struct.realspace_loc, TRUE); + reset_input_system(); + + // make him tired & hurt + player_struct.hit_points = player_struct.hit_points * 1 / 2; + if (player_struct.hit_points < MIN_CSPACE_EXIT_HP) + player_struct.hit_points = MIN_CSPACE_EXIT_HP; + player_struct.fatigue = MAX_FATIGUE; + } + } + } + return (OK); +} + +MFD_Status status_back[MFD_NUM_REAL_SLOTS]; +int old_loop; + +errtype enter_cyberspace_stuff(char dest_lev) { + int i; + cyber_nodie = FALSE; + old_loop = _current_loop; + + // Store away our realspace info + player_struct.realspace_loc = objs[PLAYER_OBJ].loc; + player_struct.realspace_level = player_struct.level; + player_struct.cspace_hp = PLAYER_MAX_HP; + + if (input_cursor_mode == INPUT_OBJECT_CURSOR) { + player_struct.save_obj_cursor = object_on_cursor; + pop_cursor_object(); + } else + player_struct.save_obj_cursor = OBJ_NULL; + + // Set timer for Avatar O'SHODAN +// Warning(("player_struct.csp_time_base = %d\n",player_struct.cspace_time_base)); +#ifdef STUPID_HACK + player_struct.cspace_time_base = CIT_CYCLE * 3; +#else + if (player_struct.cspace_time_base > CSPACE_MAX_TIME) + player_struct.cspace_time_base = CSPACE_MAX_TIME; +#endif + time_until_shodan_avatar = player_struct.game_time + player_struct.cspace_time_base; + + // Clear effect timers + for (i = 0; i < NUM_CS_EFFECTS; i++) + cspace_effect_times[i] = 0; + + // MFD hacks + + for (i = 0; i < NUM_MFDS; i++) + save_mfd_slot(i); + + // full_visible = full_visible | FULL_R_MFD_MASK | FULL_INVENT_MASK; + + for (i = 0; i < MFD_NUM_REAL_SLOTS; i++) + status_back[i] = player_struct.mfd_slot_status[i]; + player_struct.mfd_slot_status[MFD_WEAPON_SLOT] = MFD_UNAVAIL; + player_struct.mfd_slot_status[MFD_ITEM_SLOT] = MFD_UNAVAIL; + player_struct.mfd_slot_status[MFD_MAP_SLOT] = MFD_UNAVAIL; + player_struct.mfd_slot_status[MFD_TARGET_SLOT] = MFD_UNAVAIL; + + inventory_page = INV_SOFTWARE_PAGE; + set_inventory_mfd(MFD_INV_SOFT_COMBAT, player_struct.actives[ACTIVE_COMBAT_SOFT], TRUE); + player_struct.current_active = ACTIVE_COMBAT_SOFT; + + // mfd_notify_func(MFD_CSPACE_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, TRUE); + mfd_notify_func(MFD_EMPTY_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, TRUE); + mfd_change_slot(MFD_LEFT, MFD_INFO_SLOT); + mfd_change_slot(MFD_RIGHT, MFD_INFO_SLOT); + +#ifdef STEREO_SUPPORT + if (convert_use_mode == 5) + full_visible = FULL_R_MFD_MASK | FULL_INVENT_MASK; + else +#endif + full_visible |= FULL_R_MFD_MASK | FULL_INVENT_MASK; + hardware_closedown(TRUE); + drug_closedown(TRUE); + change_mode_func(0, 0, FULLSCREEN_LOOP); + + // Hud stuff + if (dest_lev >= FIRST_CSPACE_LEVEL) + hud_set(HUD_CYBERTIME); + + // gCyberHdl = shock_alloc_ipal(); // KLC - keep the handle around. + shock_alloc_ipal(); + + return (OK); +} + +errtype early_exit_cyberspace_stuff() { + // Delete the shodan_object, if there is one. + if (shodan_avatar_id != OBJ_NULL) { + obj_destroy(shodan_avatar_id); + shodan_avatar_id = OBJ_NULL; + } + return (OK); +} + +errtype exit_cyberspace_stuff() { + int i; + + // Blast away the cspace MFD + mfd_notify_func(MFD_EMPTY_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, TRUE); + + // Restore old MFD button state + for (i = 0; i < MFD_NUM_REAL_SLOTS; i++) + player_struct.mfd_slot_status[i] = status_back[i]; + + // these visibilities will be set for real by the restore, but + // the restore will punt unless they are set. + full_visible = full_visible | FULL_R_MFD_MASK | FULL_L_MFD_MASK; + for (i = 0; i < NUM_MFDS; i++) + restore_mfd_slot(i); + + // Clear out all the various cspace state variables + time_until_shodan_avatar = 0; + hud_unset(HUD_TURBO | HUD_FAKEID | HUD_DECOY | HUD_CYBERTIME | HUD_CYBERDANGER | HUD_SHIELD); + if (cspace_decoy_obj != OBJ_NULL) + obj_destroy(cspace_decoy_obj); + cspace_decoy_obj = OBJ_NULL; + + if (player_struct.save_obj_cursor != OBJ_NULL) { + push_cursor_object(player_struct.save_obj_cursor); + player_struct.save_obj_cursor = OBJ_NULL; + } + drug_startup(TRUE); + hardware_startup(TRUE); + if (old_loop != FULLSCREEN_LOOP) + change_mode_func(0, 0, old_loop); + inventory_draw_new_page(0); + + /* if (gCyberHdl != NULL) + { + // reclaim the memory, fight the power + fclose(gCyberHdl); + }*/ + grd_ipal = NULL; // hack hack hack hack + + return (OK); +} diff --git a/engine/src/GameSrc/cybermfd.c b/engine/src/GameSrc/cybermfd.c new file mode 100644 index 0000000..0068b43 --- /dev/null +++ b/engine/src/GameSrc/cybermfd.c @@ -0,0 +1,92 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/cybermfd.c $ + * $Revision: 1.5 $ + * $Author: xemu $ + * $Date: 1994/10/13 15:50:50 $ + * + * + */ + +#include "mfdint.h" +#include "mfdext.h" +#include "mfddims.h" +#include "colors.h" +#include "cit2d.h" +#include "fullscrn.h" + +// Includes for example mfd. +#include + +#include "gr2ss.h" + +// ------- +// DEFINES +// ------- +#define GOOD_RED (RED_BASE + 5) + +// --------------- +// EXPOSE FUNCTION +// --------------- + +/* This gets called whenever the MFD needs to redraw or + undraw. + The control value is a bitmask with the following bits: + MFD_EXPOSE: Update the mfd, if MFD_EXPOSE_FULL is not set, + update incrementally. + MFD_EXPOSE_FULL: Fully redraw the mfd, implies MFD_EXPOSE + + if no bits are set, the mfd is being "unexposed;" its display + being pulled off the screen to make room for a different func. +*/ + +void mfd_cspace_expose(MFD *mfd, ubyte control) { + uchar full = control & MFD_EXPOSE_FULL; + if (control == 0) // MFD is drawing stuff + { + // Do unexpose stuff here. + } + if (control & MFD_EXPOSE) // Time to draw stuff + { + // clear update rects + mfd_clear_rects(); + // set up canvas + gr_push_canvas(pmfd_canvas); + safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + // Clear the canvas by drawing the background bitmap + if (!full_game_3d) + ss_bitmap(&mfd_background, 0, 0); + + // INSERT GRAPHICS CODE HERE + mfd_draw_string("CYBERSPACE", 5, 3, GOOD_RED, TRUE); + mfd_draw_string("MFD", 5, 13, GOOD_RED, TRUE); + // on a full expose, make sure to draw everything + + if (full) + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + // Pop the canvas + gr_pop_canvas(); + // Now that we've popped the canvas, we can send the + // updated mfd to screen + mfd_update_rects(mfd); + } +} diff --git a/engine/src/GameSrc/cybmem.c b/engine/src/GameSrc/cybmem.c new file mode 100644 index 0000000..5049177 --- /dev/null +++ b/engine/src/GameSrc/cybmem.c @@ -0,0 +1,163 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/cybmem.c $ + * $Revision: 1.40 $ + * $Author: xemu $ + * $Date: 1994/11/28 06:40:01 $ + * + */ + +// Memory management and manipulation functions +// for Cyberia +#define __CYBMEM_SRC + +#include "cybmem.h" +#include "tools.h" +#include "textmaps.h" +#include "objcrit.h" +#include "dynmem.h" +#include "Shock.h" +#include "sideicon.h" +#include "criterr.h" +#include "OpenGL.h" + +uint32_t loadcount = 0; + +extern Id critter_id_table[NUM_CRITTER][NUM_CRITTER_POSTURES]; +extern Id posture_bases[]; + +int hand_fnum, digi_fnum, critter_fnum, critter_fnum2, texture_fnum; + +int flush_resource_cache(void) { + Id curr_id = ID_MIN; + int count = 0; + while (curr_id < resDescMax) { + if (ResInUse(curr_id) && ResPtr(curr_id) && !ResLocked(curr_id)) { + ResDrop(curr_id); + count++; + } + curr_id++; + } + return (count); +} + +errtype free_dynamic_memory(int mask) { + // Release textures + if (loadcount & DYNMEM_TEXTURES & mask) { + free_textures(); + ResCloseFile(texture_fnum); + } + + if (loadcount & mask & DYNMEM_SIDEICONS) { + side_icon_free_bitmaps(); + } + + if (loadcount & mask & DYNMEM_FHANDLE_1) { + ResCloseFile(hand_fnum); + } + + // digifx used to be fhandle 2 + if (loadcount & mask & DYNMEM_FHANDLE_3) { + ResCloseFile(critter_fnum); + } + + if (loadcount & mask & DYNMEM_FHANDLE_4) { + ResCloseFile(critter_fnum2); + } + + loadcount &= ~mask; + return (OK); +} + +errtype load_dynamic_memory(int mask) { + extern short _new_mode; + + if (_new_mode != -1) { + if ((~loadcount) & mask & DYNMEM_TEXTURES) { + texture_fnum = ResOpenFile("res/data/texture.res"); + load_textures(); + + if (texture_fnum < 0) + critical_error(CRITERR_RES | 7); + } + if ((~loadcount) & mask & DYNMEM_SIDEICONS) { + side_icon_load_bitmaps(); + } + + if ((~loadcount) & mask & DYNMEM_FHANDLE_1) { + hand_fnum = ResOpenFile("res/data/handart.res"); + if (hand_fnum < 0) + critical_error(CRITERR_RES | 3); + } + + // digifx used to be FHANDLE_2 + if ((~loadcount) & mask & DYNMEM_FHANDLE_3) { + critter_fnum = ResOpenFile("res/data/objart2.res"); + if (critter_fnum < 0) + critical_error(CRITERR_RES | 8); + } + + if ((~loadcount) & mask & DYNMEM_FHANDLE_4) { + critter_fnum2 = ResOpenFile("res/data/objart3.res"); + if (critter_fnum2 < 0) + critical_error(CRITERR_RES | 8); + } + + loadcount |= mask; + } + + opengl_clear_texture_cache(); + + return (OK); +} + +#define LARGEST_GUESS 8000000 +#define DECREMENT_INTERVAL 10000 +#define MAX_PTRS 25 +#define MINIMUM_SLORK_SIZE 100000 + +int slorkatron_memory_check() { + int retval, size; + int ptr_count, i; + uchar *mem_ptrs[MAX_PTRS]; + + for (ptr_count = 0; ptr_count < MAX_PTRS; ptr_count++) + mem_ptrs[ptr_count] = NULL; + + ptr_count = 0; + + size = LARGEST_GUESS + DECREMENT_INTERVAL; + retval = 0; + + while ((size > MINIMUM_SLORK_SIZE) && (ptr_count < MAX_PTRS)) { + mem_ptrs[ptr_count] = (uchar *)malloc(size); // mem_ptrs[ptr_count] = Malloc(size); + if (mem_ptrs[ptr_count] == NULL) + size -= DECREMENT_INTERVAL; + else { + retval += size; + ptr_count++; + } + } + for (i = ptr_count - 1; i >= 0; i--) + if (mem_ptrs[i] != NULL) + free(mem_ptrs[i]); // Free(mem_ptrs[i]); + + return (retval); +} diff --git a/engine/src/GameSrc/cybrnd.c b/engine/src/GameSrc/cybrnd.c new file mode 100644 index 0000000..8956d3f --- /dev/null +++ b/engine/src/GameSrc/cybrnd.c @@ -0,0 +1,87 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/cit/src/RCS/cybrnd.c $ + * $Revision: 1.6 $ + * $Author: minman $ + * $Date: 1994/01/25 00:12:05 $ + * + * $Log: cybrnd.c $ + * Revision 1.6 1994/01/25 00:12:05 minman + * added config vars for the random seeds and + * does a random selection of seeds if seeds are + * not specified + * + * Revision 1.5 1993/12/15 22:57:49 minman + * added effect var + * + * Revision 1.4 1993/09/02 23:00:21 xemu + * angle! + * + * Revision 1.3 1993/08/17 14:17:23 minman + * added make-info random number + * + * Revision 1.2 1993/08/12 19:44:56 minman + * added grenade random no + * + * Revision 1.1 1993/08/12 19:22:41 minman + * Initial revision + * + * + */ + +#define __CYBRND_SRC + +#include "cybrnd.h" +#include "tickcount.h" + +RNDSTREAM_STD(start_rnd); + +void rnd_init(void) { + long random_seed; + long seed; + + // try to get a completely random value - take the bottom two bytes to be + // the random seed + // _bios_timeofday(0, &random_seed); + random_seed = TickCount(); + RndSeed(&start_rnd, (random_seed & 0x0000FFFF)); + + // use config seed - otherwise use a random seed + // KLC - For Mac, just hard-code values from the config file. + // seed = (config_get_value("damage_seed", CONFIG_INT_TYPE, &data, &count)) ? + // data : RndRange(&start_rnd, 0, 0xFFFF); + seed = RndRange(&start_rnd, 0, 0xFFFF); + RndSeed(&damage_rnd, seed); + + // seed = (config_get_value("grenade_seed", CONFIG_INT_TYPE, &data, &count)) ? + // data : RndRange(&start_rnd, 0, 0xFFFF); + seed = RndRange(&start_rnd, 0, 0xFFFF); + RndSeed(&grenade_rnd, seed); + + // seed = (config_get_value("obj_make_seed", CONFIG_INT_TYPE, &data, &count)) ? + // data : RndRange(&start_rnd, 0, 0xFFFF); + seed = RndRange(&start_rnd, 0, 0xFFFF); + RndSeed(&obj_make_rnd, seed); + + // seed = (config_get_value("effect_seed", CONFIG_INT_TYPE, &data, &count)) ? + // data : RndRange(&start_rnd, 0, 0xFFFF); + seed = RndRange(&start_rnd, 0, 0xFFFF); + RndSeed(&effect_rnd, seed); +} diff --git a/engine/src/GameSrc/damage.c b/engine/src/GameSrc/damage.c new file mode 100644 index 0000000..31205d1 --- /dev/null +++ b/engine/src/GameSrc/damage.c @@ -0,0 +1,1245 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/damage.c $ + * $Revision: 1.206 $ + * $Author: xemu $ + * $Date: 1994/10/27 04:56:12 $ + */ + +#include + +#include "Shock.h" + +#include "effect.h" +#include "newmfd.h" +#include "damage.h" +#include "objwpn.h" +#include "objects.h" +#include "objsim.h" +#include "objprop.h" +#include "weapons.h" +#include "player.h" +#include "combat.h" +#include "cybrnd.h" +#include "mainloop.h" +#include "trigger.h" +#include "musicai.h" +#include "sfxlist.h" +#include "ai.h" +#include "frflags.h" +#include "gamerend.h" +#include "gameloop.h" +#include "target.h" +#include "objbit.h" +#include "physunit.h" +#include "hud.h" +#include "faketime.h" +#include "otrip.h" +#include "grenades.h" +#include "softdef.h" +#include "aiflags.h" +#include "input.h" +#include "mapflags.h" +#include "wares.h" +#include "drugs.h" +#include "diffq.h" +#include "hudobj.h" +#include "amap.h" +#include "cutsloop.h" + +#define SQUARE(x) ((x) * (x)) +#define PLAYER_DEFENSE_VALUE 4 +#define MAX_DAMAGE 2500 + + +short destroyed_obj_count = 0; +ObjID destroyed_ids[MAX_DESTROYED_OBJS]; + +//---------------- +// Internal Prototypes +//---------------- +int random_bell_modifier(uchar attack_on_player); +int randomize_damage(int damage, uchar attack_on_player); +int armor_absorption(int raw_damage, int obj_triple, ubyte penetrate); +int shield_absorb_damage(int damage, ubyte dtype, byte shield_absorb, ubyte shield_threshold); +void player_dies(); +ubyte damage_player(int damage, ubyte dtype, ubyte flags); +void critter_hit_effect(ObjID target, ubyte effect, Combat_Pt location, int damage, int max_damage); + +// ------------------------------------------- +// destroy_destroyed_objects() +// + +void destroy_destroyed_objects(void) { + int i, j; + uchar change_target = FALSE; + uchar dupe; + ObjID id; + + if (destroyed_obj_count != 0) { + for (i = 0; i < destroyed_obj_count; i++) { + // Look through the rest of the list for duplicates + // if so, don't delete us now since we'll get what's coming + // to us later... + // Yeah, yeah, so this is an N-squared algorithm, but hopefully the size of + // the list is pretty damn short... + dupe = FALSE; + id = destroyed_ids[i]; + + for (j = i + 1; j < destroyed_obj_count; j++) { + if (id == destroyed_ids[j]) { + dupe = TRUE; + break; + } + } + if (!dupe) { + { + // check if the physics object contributed to the lighting of the map + if (objs[id].obclass == CLASS_PHYSICS) { + ObjSpecID osid = objs[id].specID; + if (objPhysicss[osid].p2.x) { + MapElem *mmp; + mmp = MAP_GET_XY(OBJ_LOC_TO_LIGHT_LOC(objPhysicss[osid].p1.x), + OBJ_LOC_TO_LIGHT_LOC(objPhysicss[osid].p1.y)); + me_rend3_set(mmp, me_bits_rend3(mmp) - 1); + objPhysicss[osid].p2.x = 0; + } + } else if (objs[id].obclass == CLASS_GRENADE) { + do_grenade_explosion(id, TRUE); + } + + // If we destroyed a creature which was being targeted, we + // need to switch the current target + if (destroyed_ids[i] == player_struct.curr_target) + change_target = TRUE; + + obj_destroy(destroyed_ids[i]); + + if (change_target) { + toggle_current_target(); + change_target = FALSE; + } + } + } + + // object_dead(); + } + + chg_set_flg(DEMOVIEW_UPDATE); + + // "clear" the list + destroyed_obj_count = 0; + } +} + +// ------------------------------------------- +// is_obj_destroyed() +// + +uchar is_obj_destroyed(ObjID id) { + int i; + uchar found = FALSE; + + for (i = 0; i < destroyed_obj_count; i++) { + if (destroyed_ids[i] == id) { + found = TRUE; + break; + } + } + return (found); +} + +// --------------------------------------------- +// random_bell_modifier() +// + +int random_bell_modifier(uchar attack_on_player) { + int rtotal; + int i; + int rval; + int retval; + ubyte dies, die_value, handicap; + ubyte difficulty = (global_fullmap->cyber) ? player_struct.difficulty[CYBER_DIFF_INDEX] + : player_struct.difficulty[COMBAT_DIFF_INDEX]; + + if (attack_on_player) + difficulty = (difficulty == 0) ? 3 : 4 - difficulty; + + switch (difficulty) { + case (0): + dies = 2; + die_value = 50; + handicap = 15; + break; + case (1): + dies = 3; + die_value = 33; + handicap = 6; + break; + case (2): + dies = 5; + die_value = 20; + handicap = 0; + break; + case (3): + dies = 8; + die_value = 12; + handicap = 0; + break; + } + + rtotal = handicap; + for (i = 0; i < dies; i++) { + rval = RndRange(&damage_rnd, 0, die_value); + rtotal += rval; + } + + if (rtotal <= 2) + retval = -12; // 00-02 + else if (rtotal <= 8) + retval = -8; // 03-08 + else if (rtotal <= 16) + retval = -6; // 09-16 + else if (rtotal <= 28) + retval = -3; // 17-28 + else if (rtotal <= 40) + retval = -1; // 29-40 + else if (rtotal <= 60) + retval = 0; // 41-60 + else if (rtotal <= 72) + retval = 1; // 61-72 + else if (rtotal <= 84) + retval = 3; // 73-84 + else if (rtotal <= 92) + retval = 6; // 85-92 + else if (rtotal <= 98) + retval = 8; // 93-98 + else + retval = 12; // 99-100 + + return (retval); +} + +// ------------------------------------------- +// randomize_damage() +// + +int randomize_damage(int damage, uchar attack_on_player) { + int dtotal; + ubyte iterations; + int i; + ubyte difficulty = (global_fullmap->cyber) ? player_struct.difficulty[CYBER_DIFF_INDEX] + : player_struct.difficulty[COMBAT_DIFF_INDEX]; + + dtotal = damage / 2; + iterations = damage / 8; + + // if damage div 8 is non-zero add an extra iteration + // yes, we might have a value that's a little bigger than normal + if (damage % 8) + iterations++; + + for (i = 0; i < iterations; i++) + dtotal += RndRange(&damage_rnd, 1, 7); + + // if we're playing on difficulty 3 - reduce damage by a third + if (!attack_on_player && (difficulty == 3)) + dtotal = (dtotal << 1) / 3; + + return (dtotal); +} + +// --------------------------------------------- +// object_affect() +// +// there has been a hit, now we check how much an object is affected +// + +ubyte object_affect(ObjID target_id, short dtype) { + ubyte affected = 0; + int resis; + + if (!target_id) { + return 0; + } + + resis = ObjProps[OPNUM(target_id)].resistances; + affected = (resis & dtype & DAMAGE_TYPE_FIELD) ? 1 : 0; + if (affected) { + // are the super damage the same, and we have a non-zero primary damage + if ((SUPER_DAMAGE(resis) == SUPER_DAMAGE(dtype)) && (SUPER_DAMAGE(resis))) { + ubyte difficulty = (global_fullmap->cyber) ? player_struct.difficulty[CYBER_DIFF_INDEX] + : player_struct.difficulty[COMBAT_DIFF_INDEX]; + + // don't do as much super damage if we're on combat diff 3 + affected = (difficulty == 3) ? 3 : 4; + } + // are the primary damage the same, and we have a non-zero primary damage + else if ((PRIMARY_DAMAGE(resis) == PRIMARY_DAMAGE(dtype)) && (PRIMARY_DAMAGE(resis))) { + affected = 2; + } + } + // mprintf("attack on %d, affect : %d\n", target_id, affected); + return (affected); +} + +// ----------------------------------------------------------------------------------------- +// armor_absorption() +// + +int armor_absorption(int raw_damage, int obj_triple, ubyte penetrate) { + short real_penetration = (((short)penetrate * (90 + RndRange(&damage_rnd, 0, 20))) / 100); + int damage; + + damage = (ObjProps[OPTRIP(obj_triple)].armor - real_penetration); + damage = (damage > 0) ? raw_damage - damage : raw_damage; + + if (damage < 0) + damage = 0; + + return (damage); +} + +// some globals +uchar sound_hurt_threshold = 10; +uchar static_pain_time = 64; +uchar static_pain_base = 30; +uchar static_pain_delta = 30; +uchar shield_blowout_threshold = 15; + +short fr_solidfr_time; +short fr_sfx_time; + +// this is the voodoo threshold for shields, so that we have a reference +// to do effects with. (static) +#define VOODOO_SHIELD_THRESHOLD 100 + +// ------------------------------------------------------------------------------------------- +// shield_absorb_damage() +// +// returns damage to the player after shields + +short shield_absorb_perc = 0; +uchar shield_used; + +int shield_absorb_damage(int damage, ubyte dtype, byte shield_absorb, ubyte shield_threshold) { + ubyte shield_drain = 0; + + if ((damage | player_struct.hit_points) == 0) + return 0; // 0 hp is already dead, eh?, 0 damage no matter + + if (damage > shield_threshold) { + // absorption rate will be given a +/- 8 percentage for variation - unless we're at zero already + shield_absorb = (shield_absorb) ? shield_absorb + RndRange(&damage_rnd, 0, 16) - 8 : 0; + + if (shield_absorb > 0) { + // figure out how much damage is absorbed by shields + // hey - it's even a percentage + // WHY - make it 0-255, dont divide needlessly, arghgqghgghdsghgfdgfjfghdfgj + shield_drain = (damage * shield_absorb) / 100; + // let's absorb the damage... now! + damage -= shield_drain; + } else + shield_absorb = 0; + } else { + if (shield_absorb) { + // if we're below threshold, we've absorbed all damage... + shield_absorb = 100; + shield_drain = damage; + } else + shield_drain = 0; + } + + // Hud me baby + // oh,and sound too. + + if (shield_absorb > 0) { + shield_absorb_perc = shield_absorb; + hud_set_time(HUD_SHIELD, CIT_CYCLE); + + // do the sound if we're not in cyberspace + if (!global_fullmap->cyber) + (shield_absorb > 80) ? play_digi_fx(SFX_SHIELD_2, 1) : play_digi_fx(SFX_SHIELD_1, 1); + } else if (((rand() & 0x1A) < damage) && !global_fullmap->cyber) + play_digi_fx(SFX_PLAYER_HURT, 1); + + // let's make sure that we don't go over the 100% shield flash + if (shield_drain > VOODOO_SHIELD_THRESHOLD) + shield_drain = VOODOO_SHIELD_THRESHOLD; + + // let's reuse the shield_drain variable + // let's get the percentage absorbed (w.r.t. the VOODOO_SHIELD_THRESHOLD) + shield_drain = (ubyte)(((int)shield_drain << 8) / VOODOO_SHIELD_THRESHOLD); + + if ((shield_used = (shield_drain > 0)) == TRUE) + set_dmg_percentage(DMG_SHIELD, shield_drain); + + return (damage); +} + +uchar alternate_death = FALSE; + +// kill_player() +// kills the player, checks for traps and stuff, so on +// returns true if player is really dead dead dead +uchar kill_player(void) { + ObjSpecID osid; + uchar quick_death = TRUE; + uchar dummy; + extern uchar clear_player_data; + + INFO("Player died!\n"); + + // Look for a player death trigger. If so, do it. + // If not, play appropriate dying cutscene. + osid = objTraps[0].id; + while (osid != OBJ_SPEC_NULL) { + if (ID2TRIP(objTraps[osid].id) == PLRDETH_TRIG_TRIPLE) + if (trap_activate(objTraps[osid].id, &dummy)) + quick_death = FALSE; + osid = objTraps[osid].next; + } + + // if we died not from a trap - then we should clear the player data +#ifdef TEST_REBIRTH + clear_player_data = FALSE; + return FALSE; +#else + clear_player_data = (quick_death | alternate_death); + + if (quick_death) { + amap_reset(); + secret_render_fx = 0; + play_cutscene(DEATH_CUTSCENE, FALSE); + } + + return (quick_death | alternate_death); +#endif +} + +void regenerate_player(void) { + extern void wear_off_drug(int i); + extern void regenetron_door_hack(void); + int i; + for (i = 0; i < NUM_DAMAGE_TYPES; i++) + player_struct.hit_points_lost[i] = 0; + hud_unset(HUD_RADPOISON | HUD_BIOPOISON); + player_struct.curr_target = OBJ_NULL; + player_struct.fatigue = 0; + + // clear physics state + player_struct.posture = 0; + player_struct.foot_planted = 0; + player_struct.leanx = 0; + player_struct.leany = 0; + player_struct.eye_pos = 0; + + for (i = 0; i < NUM_DRUGZ; i++) + wear_off_drug(i); + regenetron_door_hack(); +} + + // ----------------------------------------------------------------------------------------- + // damage_player() + // + // returns 0 if player is still alive + // returns 1 if player is dead + +#define CSHIELD_THRESHOLDS(lev) 0 + +#define MAX_CSHIELD_ABSORB 120 +#define NUM_CSHIELD_LEVELS 10 +#define CSHIELD_ABSORB_RATES(lev) ((lev) == 0) ? 0 : (MAX_CSHIELD_ABSORB / (NUM_CSHIELD_LEVELS - (lev))) + +#define MAX_FATIGUE 10000 +#define DEATH_TICKS CIT_CYCLE + +ulong player_death_time = 0; + +// Something has caused the player to become a fatality +// typically this is damage, but can be delayed-death due to craze +void player_dies() { + extern void physics_zero_all_controls(); + extern void clear_digi_fx(); + extern short inventory_page; +#ifdef AUDIOLOGS + extern char secret_pending_hack; + secret_pending_hack = 0; +#endif + + // we should play funky death music + mai_player_death(); + reset_input_system(); + chg_set_sta(GL_CHG_2); // disable the input system + + extern uchar weapon_button_up; + weapon_button_up = TRUE; + + // clear off hud & prep for funky regen FX + // hud_unset(HUD_ALL); + physics_zero_all_controls(); + + // clear edms_state + LG_memset(player_struct.edms_state, 0, sizeof(fix) * 12); + + // reset the inventory page + inventory_page = 0; + + // hey no more things to do to player while he's dead + // it's really a secret - don't do things to player + // while there are cool secret_render_fx going on + player_struct.dead = TRUE; + set_dmg_percentage(DMG_BLOOD, 100); // 100 is an arbitrary number - we're trying to get it to do red static + secret_render_fx = DYING_REND_SFX; + + clear_digi_fx(); + player_struct.num_deaths++; +} + + // ------------------------------------------------------- + // damage_player() + // + +#define DAMAGE_DIFFICULTY ((global_fullmap->cyber) ? 3 : 0) + +ubyte damage_player(int damage, ubyte dtype, ubyte flags) { + ubyte *cur_hp; + short rawval; + uchar dead = 0, damage_dealt = FALSE; + char dlev, dmg_type = DMG_BLOOD; + + if (secret_render_fx > 0) + return 0; + + if ((damage <= 0) || (player_struct.hit_points == 0)) // 0 hp is already dead, eh? + return 0; + + if (global_fullmap->cyber && !player_struct.difficulty[CYBER_DIFF_INDEX]) + return 0; + + cur_hp = (global_fullmap->cyber) ? &player_struct.cspace_hp : &player_struct.hit_points; + + shield_used = FALSE; + + // check if shields should play a role + if ((dtype == RADIATION_TYPE) || (dtype == BIO_TYPE)) + dmg_type = DMG_RAD; + else if (!(flags & NO_SHIELD_ABSORBTION)) { + byte absorb_rate, thresh_val; + + // compensate for difficulty level + dlev = player_struct.difficulty[DAMAGE_DIFFICULTY]; + if (dlev != 3) + damage >>= (2 - dlev); + if (global_fullmap->cyber) { + if (player_struct.softs.defense[SOFTWARE_CSHIELD]) { + absorb_rate = CSHIELD_ABSORB_RATES(player_struct.softs.defense[SOFTWARE_CSHIELD]); + thresh_val = CSHIELD_THRESHOLDS(player_struct.softs.defense[SOFTWARE_CSHIELD]); + } else + absorb_rate = thresh_val = 0; + } else { + absorb_rate = (byte)player_struct.shield_absorb_rate; + thresh_val = player_struct.shield_threshold; + } + damage = shield_absorb_damage(damage, dtype, absorb_rate, thresh_val); + } + + if (damage <= 0) + return 0; + +#ifdef WACKY_STATIC_USAGE + // Play digi FX should go in here when we have appropriate SFX + if ((!global_fullmap->cyber) && (damage > static_pain_base + rand() % static_pain_delta)) { + extern char static_density, static_color, static_grouping; + // Turn on fullscreen static & turn off any SFX that might be otherwise going on. + fr_global_mod_flag(FR_SOLIDFR_STATIC, FR_SOLIDFR_MASK | FR_SFX_MASK); + fr_solidfr_time = (static_pain_time); + play_digi_fx(SFX_STATIC, -1); + } +#endif + + // did we take more damage than hit points?? - eeeegggads! we're dead + if ((*cur_hp) <= damage) { + damage_dealt = TRUE; + { + if (global_fullmap + ->cyber) { // No matter what, the player can't be killed in one shot.....it takes at least two shots + player_struct.cspace_hp = (player_struct.cspace_hp == 1) ? 0 : 1; + } else // normal (non-cyberspace) damage - player's dead dead dead + { + if (*cur_hp > 0) { +#ifdef CRAZE_NODEATH + if ((player_struct.drug_status[DRUG_LSD] > 0) && (QUESTVAR_GET(COMBAT_DIFF_QVAR) < 3)) + *cur_hp = 1; + else +#endif + { + *cur_hp = 0; + dead = TRUE; + + // if we're carrying an object - let's drop it here! + if (object_on_cursor != OBJ_NULL) { + obj_move_to(object_on_cursor, &objs[PLAYER_OBJ].loc, TRUE); + pop_cursor_object(); + } + + // signal for the effects that happen with death + player_dies(); + } + } + } + } + } + if (!damage_dealt) { + extern int mai_damage_sum; + + *cur_hp -= damage; + mai_damage_sum += damage; + } + + if (*cur_hp == 0) + rawval = damage << 8; + else + rawval = ((damage << 8) / (*cur_hp)); // what is this minmax3/20xff thing? + // it's my voodoo - minman + if (!shield_used) + set_dmg_percentage(dmg_type, (ubyte)lg_min(lg_max(rawval, ((damage * 3) / 2)), 0x00FF)); + + // makes sure gamescreen knows that it should be updated + chg_set_flg(VITALS_UPDATE); + return (dead); +} + +// -------------------------------------------------- +// damage_object() +// +// return 0 - if object is still alive after damage +// return 1 - if object has been destroyed +// +// also does the appropriate texture map change if +// object is destroyed??????? + +ubyte damage_object(ObjID target_id, int damage, int dtype, ubyte flags) { + int obclass = objs[target_id].obclass; + int dead = 0; + uchar tranq = FALSE; + uchar stun = FALSE; + short target_hp = ObjProps[OPNUM(target_id)].hit_points; + + // If we've already been destroyed, or don't care, thendon't bother us. + if ((objs[target_id].info.inst_flags & INDESTRUCT_FLAG) || + ((target_id != player_struct.rep) && (objs[target_id].info.current_hp == 0))) + return (0); + + // let the player get his/her own special treatment + if (target_id == PLAYER_OBJ) + dead = damage_player(damage, (ubyte)PRIMARY_DAMAGE(dtype), flags); + else { + // are we still alive - then do special stuff that we only care about if + // we're still alive, makes too much sense + if (objs[target_id].info.current_hp > damage) { + // damage object - but it's not dead yet. + dead = 0; + if (obclass == CLASS_CRITTER) { + int pct = (450L * damage) / objs[target_id].info.current_hp; + tranq = dtype & TRANQ_FLAG; + stun = (flags & STUN_ATTACK); + + if (tranq) { + tranq = FALSE; + // okay tranq - only if we've done damage, and we're lucky and the damage we're doing + // is a decent amount of the remaining life + if (damage) + tranq = (RndRange(&damage_rnd, 0, 100) < pct); + } else if (stun) { + if (objs[target_id].subclass == CRITTER_SUBCLASS_ROBOT) + stun = FALSE; + else + stun = (RndRange(&damage_rnd, 0, 100) < pct); + } + ai_critter_hit(objs[target_id].specID, damage, tranq, stun); + } + // get rid of the hit points + objs[target_id].info.current_hp -= damage; + } else { + objs[target_id].info.current_hp = 0; + dead = 1; + + // Check to see whether or not there is cool special stuff to do when this thing + // gets destroyed. obj_combat_destroy returns whether or not to go ahead and + // continue the destruction process + if (obj_combat_destroy(target_id)) + ADD_DESTROYED_OBJECT(target_id); + + if (DESTROY_SOUND_EFFECT(ObjProps[OPNUM(target_id)].destroy_effect)) { + extern ObjID damage_sound_id; + extern char damage_sound_fx; + + damage_sound_fx = SFX_CPU_EXPLODE; + damage_sound_id = target_id; + } + } + + if ((obclass == CLASS_CRITTER) && !global_fullmap->cyber) { + ubyte seriousness = 0; + extern void hud_report_damage(ObjID target, byte seriousness); + + // marc's desired code + if (stun) + seriousness = 7; + else if (tranq) + seriousness = 6; + else if (!object_affect(target_id, dtype)) + seriousness = 5; + else if (damage > target_hp) + seriousness = 4; + else if (damage > (target_hp * 4) / 5) + seriousness = 3; + else if (damage > target_hp / 5) + seriousness = 2; + else if (damage > 0) + seriousness = 1; + + hud_report_damage(target_id, seriousness); + } + + // If we damaged the currently targeted creature, let mfds know... + if ((target_id == player_struct.curr_target) && !global_fullmap->cyber) + mfd_notify_func(MFD_TARGET_FUNC, MFD_TARGET_SLOT, FALSE, MFD_ACTIVE, TRUE); + } + return (dead); +} + +// ----------------------------------------------- +// simple_damage_object() takes damage of a particular type with particular flags, +// and applies it to the object if it is vulnerable to the type. +// returns whether the object was destroyed. + +uchar simple_damage_object(ObjID target, int damage, ubyte dtype, ubyte flags) { + if (object_affect(target, 1 << (dtype - 1))) + return damage_object(target, damage, dtype, flags); + return FALSE; +} + +#define FIRST_ENRG_PROJ_TYPE 7 +#define DAMAGE_PROJ_DISTANCE (fix_make(0, 0x6000)) + +// OBJ_NULL victim means terrain +void slow_proj_hit(ObjID id, ObjID victim) { + Combat_Pt origin; + ObjLoc loc = objs[id].loc; + ObjRefID current_ref; + ObjID current_id; + ubyte affect; + ubyte dtype; + ubyte proj_power; + ubyte special_effect = EFFECT_VAL(ObjProps[OPNUM(id)].destroy_effect); + int a; + int weapon_triple; + + current_ref = MAP_GET_XY(OBJ_LOC_BIN_X(objs[id].loc), OBJ_LOC_BIN_Y(objs[id].loc))->objRef; + + if (objPhysicss[objs[id].specID].owner != PLAYER_OBJ) { + if (special_effect) + do_special_effect_location(id, special_effect, 0xFF, &loc, 0); + return; + } + + a = objPhysicss[objs[id].specID].bullet_triple; + if (objs[id].info.type >= FIRST_ENRG_PROJ_TYPE) { + weapon_triple = MAKETRIP(CLASS_GUN, GUN_SUBCLASS_BEAMPROJ, TRIP2TY(a)); + proj_power = TRIP2CL(a); + dtype = BeamprojGunProps[SCTRIP(weapon_triple)].damage_type; + } else { + weapon_triple = MAKETRIP(CLASS_GUN, GUN_SUBCLASS_SPECIAL, TRIP2TY(a)); + proj_power = 100; + dtype = SpecialGunProps[SCTRIP(weapon_triple)].damage_type; + } + if (weapon_triple == RAILGUN_TRIPLE) { + do_explosion(loc, id, 0, &(game_explosions[1])); + play_digi_fx_obj(SFX_EXPLOSION_1, 1, id); + } else if (victim == OBJ_NULL) { + while (current_ref != OBJ_REF_NULL) { + current_id = objRefs[current_ref].obj; + + if (current_id != id) { + affect = object_affect(current_id, dtype); + if (affect) { + fix dist, deltax, deltay, deltaz; + + deltax = fix_from_obj_coord(objs[id].loc.x - objs[current_id].loc.x); + deltay = fix_from_obj_coord(objs[id].loc.y - objs[current_id].loc.y); + deltaz = fix_from_obj_height_val(objs[id].loc.z - objs[current_id].loc.z); + dist = fix_mul(deltax, deltax) + fix_mul(deltay, deltay) + fix_mul(deltaz, deltaz); + + if (dist < DAMAGE_PROJ_DISTANCE) { + + origin.x = fix_make(-1, 0); + origin.y = fix_make(-1, 0); + origin.z = fix_make(-1, 0); + + player_attack_object(current_id, weapon_triple, proj_power, origin); + } + } + } + current_ref = objRefs[current_ref].next; + } + } else { + origin.x = fix_from_obj_coord(loc.x); + origin.y = fix_from_obj_coord(loc.y); + origin.z = fix_from_obj_height(loc.z); + + player_attack_object(victim, weapon_triple, proj_power, origin); + } + if (special_effect) + do_special_effect_location(id, special_effect, 0xFF, &loc, 0); +} + +// returns whether it is being killed... +uchar special_terrain_hit(ObjID cobjid) { + if (is_obj_destroyed(cobjid)) + return TRUE; + + if (objs[cobjid].obclass == CLASS_GRENADE) { + if (objGrenades[objs[cobjid].specID].flags & GREN_ACTIVE_FLAG) + ADD_DESTROYED_OBJECT(cobjid); + else { + return FALSE; // dead grenades stay alive + // in the sense that they are not live, but should remain physics live, see + } + } else if (objs[cobjid].obclass == CLASS_PHYSICS) { + // only one terrain hit per turn! + if (objPhysicss[objs[cobjid].specID].p3.x) + return FALSE; + else + objPhysicss[objs[cobjid].specID].p3.x = 3; + + slow_proj_hit(cobjid, OBJ_NULL); + EDMS_obey_collisions(objs[cobjid].info.ph); + + if (PhysicsProps[CPNUM(cobjid)].flags & PROJ_PRESERVE_WALL) + return FALSE; + + // Signal a miss to our controller + ai_misses(objs[objPhysicss[objs[cobjid].specID].owner].specID); + } + ADD_DESTROYED_OBJECT(cobjid); + return TRUE; +} + +#define DMG_THRESH 0x60000 +#define HACK_THRESH 0x1800 +#define SPCL_THRESH 0x80 + +// HEY COMMENTED OUT PROCEDURE +#ifdef CALLS_WERENT_SLOW +uchar terrain_damage_object(physics_handle ph, fix raw_damage) { + uchar dead = FALSE; + ObjID target = physics_handle_to_id(ph); + + if (ObjProps[OPNUM(cobjid)].flags & SPCL_TERR_DMG) { + if (raw_damage > SPCL_THRESH) { + objs[target].info.current_hp = 0; + ADD_DESTROYED_OBJECT(target); + dead = TRUE; + } + } else + dead = simple_damage_object(target, (raw_damage - HACK_THRESH) >> 10, EXPLOSION_FLAG, NO_SHIELD_ABSORBTION); + + return (dead); +} +#endif + +// ------------------------------ +// compute_damage() +// +// computes damage to be inflicted on target +// +// target ObjID of object that will be damaged +// damage_type damage type of the attack example : energy, explosion, physical, needle, etc... +// damage_mod raw value of damage inflicted +// offense offensive value of the attack +// penet penetration value of the attack +// power_level percentage of damage to be inflicted +// *effect returns the effect number to be played +// *effect_row a pointer to the effect row + +int compute_damage(ObjID target, int damage_type, int damage_mod, ubyte offense, ubyte penet, int power_level, + ubyte *effect, ubyte *effect_row, ubyte attack_effect_type) { + int damage = 0; + int delta; + int modifier; + ubyte affect; + + // AFFECTIVENESS + // + affect = object_affect(target, damage_type); + if (affect) { + damage = (damage_mod * power_level * affect) / 100; + + damage = armor_absorption(damage, ID2TRIP(target), penet); + + if (!global_fullmap->cyber) { + // Compute the CRITICAL HIT affector + // + delta = random_bell_modifier((target == PLAYER_OBJ)); + modifier = (target == PLAYER_OBJ) ? (offense + delta - PLAYER_DEFENSE_VALUE) + : ((offense - ObjProps[OPNUM(target)].defense_value) + delta); + + if (modifier < -3) + damage /= SQUARE(modifier + 3); // plus because it's negative + else if ((modifier > 3) && (ObjProps[OPNUM(target)].defense_value != 0xFF)) { + // we are going to do critical damage, but we must check that the defense value + // isn't 0xFF (meaning it's invulnerable to critical hits). + + // just put an upper bound to be safe. + if (modifier > 12) + modifier = 12; + damage = (damage * modifier) / 3; + } + } + + // TOUGHNESS + if (ObjProps[OPNUM(target)].toughness != 3) + damage >>= ObjProps[OPNUM(target)].toughness; + else + damage = 0; + + if (damage < 0) { + damage = 0; + } else { + damage = randomize_damage(damage, target == PLAYER_OBJ); + + // bound to realistic max + if (damage > MAX_DAMAGE) + damage = MAX_DAMAGE; + } + + // take either the normal effect, unless we did lots of damage - then play bigger one + if ((effect_row != NULL) && (attack_effect_type != SPECIAL_TYPE) && (effect != NULL) && + !global_fullmap->cyber) { + // check to see if we did damage - if so - do appropriate hit effect + // otherwise do a wall hit effect - MEANS NO DAMAGE/EFFECT + if (damage) + *effect = (damage < (damage_mod * 7) / 6) ? *(effect_row) : *(effect_row + 1); + else + *effect = effect_matrix[NON_CRITTER_EFFECT][attack_effect_type][0]; + } else if (effect != NULL) + *effect = 0; + } else if (attack_effect_type != SPECIAL_TYPE) { + // we didn't affect - so do the no effect one! + if (effect) + *effect = (!global_fullmap->cyber) ? effect_matrix[NON_CRITTER_EFFECT][attack_effect_type][0] : 0; + } else { + // Special damage type with no damage = no effect. + if (effect) { + *effect = 0; + } + } + + return (damage); +} + +// -------------------------------------------------------------- +// critter_hit_effect() +// +void critter_hit_effect(ObjID target, ubyte effect, Combat_Pt location, int damage, int max_damage) { + fix radius, height; + byte ht; + ObjLoc loc = objs[target].loc; + + // temporary - to hit effect_center - will take care of later + SET_EFFECT_LOC(target, EFFECT_CENTER); + + SET_EFFECT_NUM(target, effect); + SET_EFFECT_FRAME(target, 0); + + radius = fix_make(ObjProps[OPNUM(target)].physics_xr, 0) / (PHYSICS_RADIUS_UNIT * 4); + + height = fix_from_obj_height_val(loc.z); + ht = ((height - location.z) / radius) + 4; + if (ht < 1) + ht = 1; + else if (ht > 7) + ht = 7; + + SET_EFFECT_HEIGHT(target, ht); + + if (damage < (max_damage / 3)) { + SET_EFFECT_DUAL(target, 0); + SET_EFFECT_SCALE(target, 1); + } else if (damage < max_damage) { + SET_EFFECT_DUAL(target, 0); + SET_EFFECT_SCALE(target, 2); + } else { + SET_EFFECT_DUAL(target, 1); + SET_EFFECT_SCALE(target, 3); + } +} + +// --------------------------------------------------------------------------- +// get_damage_estimate() +// +// Returns a number from DAMAGE_MIN to DAMAGE_MAX indicating how wounded +// a creature is. + +int get_damage_estimate(ObjSpecID osid) { + ObjID id = objCritters[osid].id; + int triple = ID2TRIP(id); + + return (DAMAGE_MAX - ((objs[id].info.current_hp * DAMAGE_MAX) / ObjProps[OPTRIP(triple)].hit_points)); +} + +// ------------------------------------------------------- +// attack_object() +// +// target ObjID of object that will be damaged +// damage_type damage type of the attack example : energy, explosion, physical, needle, etc... +// damage_mod raw value of damage inflicted +// offense offensive value of the attack +// penet penetration value of the attack +// flags flags for the attack (currently just to see if player's shields absorb damage) +// power_level percentage of damage to be inflicted +// *effect returns the effect number to be played +// *effect_row a pointer to the effect row +// +// returns whether target died + +ubyte attack_object(ObjID target, int damage_type, int damage_mod, ubyte offense, ubyte penet, ubyte flags, + int power_level, ubyte *effect_row, ubyte *effect, ubyte attack_effect_type, + int *damage_inflicted) { + int damage; + char diff; + + if (effect) + *effect = 0; + // check to see if we have a valid target + if (target == OBJ_NULL) + return (0); + else if (is_obj_destroyed(target)) + return (0); + // the next is same as dead! + else if ((objs[target].info.current_hp == 0) && DESTROY_OBJ_EFFECT(ObjProps[OPNUM(target)].destroy_effect)) + return (0); + + // get the difficulty level + diff = (global_fullmap->cyber) ? player_struct.difficulty[CYBER_DIFF_INDEX] + : player_struct.difficulty[COMBAT_DIFF_INDEX]; + + // look combat difficult 0 setting - KILL OBJECTS IN ONE SHOT!!! - if object's toughness isn't 3 + if ((ObjProps[OPNUM(target)].toughness != 3) && !diff && (target != PLAYER_OBJ)) { + damage = objs[target].info.current_hp; + if (effect) + *effect = (effect_row) ? *(effect_row + 1) : 0; + } else { +#ifdef SELFRUN // we do max damage if we're in self run + damage = ((objs[target].obclass == CLASS_CRITTER) && (target != PLAYER_OBJ)) ? 0xFF : 0; +#else + damage = compute_damage(target, damage_type, damage_mod, offense, penet, power_level, effect, effect_row, + attack_effect_type); +#endif + } + + if (damage_inflicted) + *damage_inflicted = damage; + + // okay let's check if we're destroying an object that has a flagged destroy effect + // if the high bit is flagged then we will destroy the object during the animation + if ((objs[target].info.current_hp <= damage) && DESTROY_OBJ_EFFECT(ObjProps[OPNUM(target)].destroy_effect)) { + objs[target].info.current_hp = 0; + if (effect) + *effect = ObjProps[OPNUM(target)].destroy_effect; + return (TRUE); + } + return (damage_object(target, damage, damage_type, flags)); +} + + // ------------------------------------------------------------- + // player_attack_object() + // + +#define CRAZE_DAMAGE_MOD 2 + +ubyte player_attack_object(ObjID target, int wpn_triple, int power_level, Combat_Pt origin) { + ubyte offense; + int damage_mod; + int wpn_class = TRIP2CL(wpn_triple); + int dtype; + int damage_inflicted; + int prop_val; + ubyte penet; + ubyte effect; + ubyte *effect_row; + ubyte attack_effect_type; + ubyte special_effect = 0; + ubyte flags = 0; + ObjID effect_id = OBJ_NULL; + uchar dead = FALSE; + uchar new_loc = FALSE; + ObjLoc loc; + ubyte effect_class = + (objs[target].obclass == CLASS_CRITTER) ? CritterProps[CPNUM(target)].hit_effect : NON_CRITTER_EFFECT; + + // Special targeting ware hack + if ((objs[target].obclass == CLASS_CRITTER) && (get_player_ware_version(WARE_HARD, HARDWARE_TARGET) > 3) && + (player_struct.curr_target == OBJ_NULL) && (!global_fullmap->cyber)) { + select_current_target(target, FALSE); + } + + switch (wpn_class) { + case (CLASS_GUN): // Beam weapon is the only gun with damage type + switch (TRIP2SC(wpn_triple)) { + case (GUN_SUBCLASS_HANDTOHAND): + prop_val = SCTRIP(wpn_triple); + damage_mod = HandtohandGunProps[prop_val].damage_modifier; + offense = HandtohandGunProps[prop_val].offense_value; + if (player_struct.drug_status[DRUG_LSD] > 0) { + damage_mod <<= CRAZE_DAMAGE_MOD; + offense += CRAZE_DAMAGE_MOD; + } + dtype = HandtohandGunProps[prop_val].damage_type; + penet = HandtohandGunProps[prop_val].penetration; + attack_effect_type = HAND_TYPE; + break; + case (GUN_SUBCLASS_BEAM): + prop_val = SCTRIP(wpn_triple); + damage_mod = BeamGunProps[prop_val].damage_modifier; + offense = BeamGunProps[prop_val].offense_value; + dtype = BeamGunProps[prop_val].damage_type; + penet = BeamGunProps[prop_val].penetration; + attack_effect_type = BEAM_TYPE; + break; + case (GUN_SUBCLASS_SPECIAL): + prop_val = SCTRIP(wpn_triple); + damage_mod = SpecialGunProps[prop_val].damage_modifier; + offense = SpecialGunProps[prop_val].offense_value; + dtype = SpecialGunProps[prop_val].damage_type; + penet = SpecialGunProps[prop_val].penetration; + attack_effect_type = SPECIAL_TYPE; + special_effect = EFFECT_VAL(ObjProps[OPTRIP(SpecialGunProps[prop_val].proj_triple)].destroy_effect); + break; + case (GUN_SUBCLASS_BEAMPROJ): + prop_val = SCTRIP(wpn_triple); + damage_mod = BeamprojGunProps[prop_val].damage_modifier; + offense = BeamprojGunProps[prop_val].offense_value; + dtype = BeamprojGunProps[prop_val].damage_type; + penet = BeamprojGunProps[prop_val].penetration; + attack_effect_type = SPECIAL_TYPE; + special_effect = EFFECT_VAL(ObjProps[OPTRIP(BeamprojGunProps[prop_val].proj_triple)].destroy_effect); + if (BeamprojGunProps[prop_val].flags & 0x02) + flags |= STUN_ATTACK; + break; + } + break; + case (CLASS_AMMO): + damage_mod = AmmoProps[CPTRIP(wpn_triple)].damage_modifier; + offense = AmmoProps[CPTRIP(wpn_triple)].offense_value; + dtype = AmmoProps[CPTRIP(wpn_triple)].damage_type; + penet = AmmoProps[CPTRIP(wpn_triple)].penetration; + attack_effect_type = PROJ_TYPE; + break; + case (CLASS_GRENADE): + damage_mod = GrenadeProps[CPTRIP(wpn_triple)].damage_modifier; + offense = GrenadeProps[CPTRIP(wpn_triple)].offense_value; + dtype = GrenadeProps[CPTRIP(wpn_triple)].damage_type; + penet = GrenadeProps[CPTRIP(wpn_triple)].penetration; + attack_effect_type = GREN_TYPE; + break; + default: + return (0); + break; + } + effect_row = effect_matrix[effect_class][attack_effect_type]; + dead = attack_object(target, dtype, damage_mod, offense, penet, flags, power_level, effect_row, &effect, + attack_effect_type, &damage_inflicted); + + // this is for a slow projectile spang - not for objects exploding + if (attack_effect_type == SPECIAL_TYPE) + effect = 0; // special_effect; + + if (dead) { + ubyte old_effect = effect; + + // check to see if we're suppose to play an animation if object is destroyed.... + if (EFFECT_VAL(ObjProps[OPNUM(target)].destroy_effect)) + effect = ObjProps[OPNUM(target)].destroy_effect; + + if (old_effect != effect) { + new_loc = TRUE; + loc = objs[target].loc; + } + } + + if (effect != 0) { + // if (objs[target].obclass == CLASS_CRITTER) + // critter_hit_effect(target, effect, origin, damage_inflicted, damage_mod); + //#ifdef REMOVE_OLD_EFFECT + // else + //#endif + { + fix deltax, deltay, dist; + + // if it's a 3-d model - let's get the right place + if (ObjProps[OPNUM(target)].render_type == 1) { + loc = objs[target].loc; + + if (ObjProps[OPNUM(target)].physics_xr > 20) { + // let's randomize the hit by a bit if object is big enough + loc.x += (RndRange(&damage_rnd, 0, 0x40) - 0x20); + loc.y += (RndRange(&damage_rnd, 0, 0x40) - 0x20); + loc.z += + ((RndRange(&damage_rnd, 0, 0x10) - 0x08) + + obj_height_from_fix(fix_make(ObjProps[OPNUM(target)].physics_xr, 0) / PHYSICS_RADIUS_UNIT)); + } + + // and in the end - let's bring the effect up to the center of the object + } else if (!new_loc) { + // if we've been given bad info - let's just go on + if ((origin.x < 0) || (fix_int(origin.y) > 64)) { + return (dead); + } + + loc.x = obj_coord_from_fix(origin.x); + loc.y = obj_coord_from_fix(origin.y); + loc.z = obj_height_from_fix(origin.z); + } + + deltax = OBJ_LOC_VAL_TO_FIX(objs[PLAYER_OBJ].loc.x - loc.x); + deltay = OBJ_LOC_VAL_TO_FIX(objs[PLAYER_OBJ].loc.y - loc.y); + dist = fix_fast_pyth_dist(deltax, deltay) << 2; + + // move explosion towards player + loc.x += obj_coord_from_fix(fix_div(deltax, dist)); + loc.y += obj_coord_from_fix(fix_div(deltay, dist)); + + effect_id = do_special_effect_location(target, effect, 0xFF, &loc, 0); + } + } + + if (attack_effect_type == BEAM_TYPE) { + extern ObjID beam_effect_id; + if (effect_id != OBJ_NULL) { + beam_effect_id = effect_id; + hudobj_set_id(beam_effect_id, TRUE); + } + } + + return (dead); +} diff --git a/engine/src/GameSrc/digifx.c b/engine/src/GameSrc/digifx.c new file mode 100644 index 0000000..772d5c9 --- /dev/null +++ b/engine/src/GameSrc/digifx.c @@ -0,0 +1,364 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/digifx.c $ + * $Revision: 1.48 $ + * $Author: dc $ + * $Date: 1994/11/28 08:38:42 $ + */ + +#include "criterr.h" +#include "objects.h" +#include "player.h" +#include "musicai.h" +#include "faketime.h" +#include "sndcall.h" +#include "tools.h" +#include "trigger.h" +#ifdef AUDIOLOGS +#include "audiolog.h" +#endif + +#define NUM_DIGI_FX 114 + +char volumes[NUM_DIGI_FX]; +char flags[NUM_DIGI_FX]; +char priorities[NUM_DIGI_FX]; + +// This has to be changed if the resource changes location! +#define SFX_BASE 201 + +extern uchar curr_alog_vol; + +#ifdef NOT_YET + +//#define ASYNCH_DIGI + +int digi_timer_id; +void start_asynch_digi_fx() { +#ifdef ASYNCH_DIGI + if (sfx_on) + tm_activate_process(digi_timer_id); +#endif +} + +void stop_asynch_digi_fx() { +#ifdef ASYNCH_DIGI + if (sfx_on) + tm_deactivate_process(digi_timer_id); +#endif +} + +#endif + +errtype stop_digi_fx() { +#ifdef AUDIOLOGS + if (audiolog_setting) + audiolog_stop(); +#endif + if (sfx_on) { + snd_kill_all_samples(); + sound_frame_update(); + } + return (ERR_NOEFFECT); +} + +void clear_digi_fx() { stop_digi_fx(); } + +//#define SND_TEST + +errtype digifx_init() { + + FILE *fp = fopen_caseless("res/data/digiparm.bin", "rb"); + if (fp == NULL) { + printf("Failed to open digiparm.bin\n"); + return ERR_FOPEN; + } + + fread(volumes, NUM_DIGI_FX, 1, fp); + fread(flags, NUM_DIGI_FX, 1, fp); + fread(priorities, NUM_DIGI_FX, 1, fp); + fclose(fp); + // clear_digi_fx(); + + // snd_finish = digifx_EOS_callback; + + /* KLC - not needed now. + #ifdef ASYNCH_DIGI + extern void asynch_digi_fx(void); + digi_timer_id = tm_add_process(asynch_digi_fx, 0, CIT_FREQ << 2); + #endif + */ + return (OK); +} + +#define DIGIFX_TIMEOUT_TICKS (CIT_CYCLE * 3) >> 1 +#define DIGIFX_DUPE_TICKS CIT_CYCLE >> 2 + +// Returns a 0-255 factor of how loud the sound should be. +#define VOL_FULL 0xFF +#define FIX_VOL_FULL (fix_make(VOL_FULL, 0)) +#define MAX_DIGIFX_DIST fix_make(15, 0) +#define MIN_DIGIFX_DIST fix_make(2, 0) + +extern int curr_alog; + +// ------------ +// PROTOYTPES +// ------------ + +int compute_sfx_vol(ushort x1, ushort y1, ushort x2, ushort y2) { + fix dx, dy, dist; + int retval; + + dx = fix_from_obj_coord(x1) - fix_from_obj_coord(x2); + dy = fix_from_obj_coord(y1) - fix_from_obj_coord(y2); + dist = fix_fast_pyth_dist(dx, dy); + + if (dist > MAX_DIGIFX_DIST) + retval = 0; + else if (dist < MIN_DIGIFX_DIST) + retval = VOL_FULL; + else + // What, no fix_mul_div_int? + retval = fix_int(fix_mul_div(FIX_VOL_FULL, MAX_DIGIFX_DIST - dist, MAX_DIGIFX_DIST - MIN_DIGIFX_DIST)); + return (retval); +} + +// i should just fix this.... +int compute_sfx_pan(ushort x1, ushort y1, ushort x2, ushort y2, fixang our_ang) { + fixang sfx_ang; + fix dx, dy; + int retval; + + dx = fix_from_obj_coord(x1) - fix_from_obj_coord(x2); + dy = fix_from_obj_coord(y1) - fix_from_obj_coord(y2); + + // Do some trig, and move the angle into our relative frame + // wait, our_ang is 0-65536 from North, clockwise // so, ah, what is going on???? + our_ang = 0x4000 - our_ang; + sfx_ang = fix_atan2(dy, dx) - our_ang; + // Now we have an angle, sfx_ang, which supposedly represents the angle of the source relative to our facing... + retval = fix_int(fix_mul(fix_fastsin(sfx_ang), fix_make(-63, 0))) + + 64; // was cos,i made it sin, flipped to be left-right + return (retval); +} + +// Returns whether or not in the humble opinion of the +// sound system, the sample should be politely obliterated out of existence +uchar set_sample_pan_gain(snd_digi_parms *sdp) { + uchar temp_vol, vol; + uint raw_data = (uint)sdp->data; + extern uchar curr_sfx_vol; + + if (raw_data & 0x80000000) { + //terrain elevator + short x = (raw_data & 0x7FFF0000) >> 16; + short y = (raw_data & 0xFFFF); + + temp_vol = compute_sfx_vol(x, y, objs[PLAYER_OBJ].loc.x, objs[PLAYER_OBJ].loc.y); + sdp->pan = compute_sfx_pan(x, y, objs[PLAYER_OBJ].loc.x, objs[PLAYER_OBJ].loc.y, objs[PLAYER_OBJ].loc.h << 8); + } else if (raw_data != 0) { + ObjID id; + id = (ObjID)raw_data; + temp_vol = compute_sfx_vol(objs[id].loc.x, objs[id].loc.y, objs[PLAYER_OBJ].loc.x, objs[PLAYER_OBJ].loc.y); + sdp->pan = compute_sfx_pan(objs[id].loc.x, objs[id].loc.y, objs[PLAYER_OBJ].loc.x, objs[PLAYER_OBJ].loc.y, + objs[PLAYER_OBJ].loc.h << 8); + } else if (sdp->snd_ref == 0) { // audiolog + // following is temp sdp->vol=curr_alog_vol; + sdp->vol = curr_alog_vol; + snd_sample_reload_parms(sdp); + return (FALSE); + } else { + // sdp->pan=SND_DEF_PAN; + temp_vol = VOL_FULL; + } + vol = volumes[sdp->snd_ref - SFX_BASE]; + + /* + * shamaz: the origial condition was (vol == -1) which is always + * false because vol is unsigned. Perhaps the author was thinking + * of something like this: + */ + if (vol == VOL_FULL) + vol = 127; + vol = vol * curr_sfx_vol / 100; + sdp->vol = vol * temp_vol / VOL_FULL; + snd_sample_reload_parms(sdp); + return (FALSE); +} + +void stop_terrain_elevator_sound(short sem) +{ + int i; + + //stop all sound channels that are playing terrain elevator sound with semaphore index sem + for (i = 0; i < SND_MAX_SAMPLES; i++) + { + snd_digi_parms *sdp = snd_sample_parms(i); + uint raw_data = (uint)sdp->data; + + if (raw_data & 0x80000000) + { + short x = (raw_data & 0x7FFF0000) >> 16; + short y = (raw_data & 0xFFFF); + + extern height_semaphor h_sems[NUM_HEIGHT_SEMAPHORS]; + + if (h_sems[sem].x == (x >> 8) && h_sems[sem].y == (y >> 8) && h_sems[sem].inuse) + snd_end_sample(i); + } + } +} + +#ifdef NOT_YET // + +#pragma disable_message(202) +int digifx_volume_shift(short x, short y, short z, short phi, short theta, int basevol) { + int retval; + // Note that "x" is really the object ID of the thing we care about + // unless phi is set, in which case phi and theta are a literal location to use + if (x != OBJ_NULL) + retval = compute_sfx_vol(objs[x].loc.x, objs[x].loc.y, objs[PLAYER_OBJ].loc.x, objs[PLAYER_OBJ].loc.y); + else if (phi != 0) + retval = compute_sfx_vol(phi, theta, objs[PLAYER_OBJ].loc.x, objs[PLAYER_OBJ].loc.y); + else + retval = VOL_FULL; + + // Now normalize vs basevol + retval = basevol * retval / VOL_FULL; + return (retval); +} + +int digifx_pan_shift(short x, short y, short z, short phi, short theta) { + int retval; + // Note that "x" is really the object ID of the thing we care about + // unless phi is set, in which case phi and theta are a literal location to use + if (x != OBJ_NULL) + retval = compute_sfx_pan(objs[x].loc.x, objs[x].loc.y, objs[PLAYER_OBJ].loc.x, objs[PLAYER_OBJ].loc.y, + objs[PLAYER_OBJ].loc.h << 8); + else if (phi != 0) + retval = + compute_sfx_pan(phi, theta, objs[PLAYER_OBJ].loc.x, objs[PLAYER_OBJ].loc.y, objs[PLAYER_OBJ].loc.h << 8); + else + retval = 128; // is this right?? + retval = (retval + 64) >> 1; + Spew(DSRC_AUDIO_Testing, ("Modified PAN value=%d\n", retval)); + return (retval); +} +#pragma enable_message(202) + +#endif // NOT_YET + +uchar sfx_volume_levels[] = {0, 0x9, 0xF}; +#define ALWAYS_QUEUE_TOLERANCE 2 +#define NO_GAIN_THRESHOLD 0x6A +#define HARSH_GAIN_THRESHOLD 0xBA + +snd_digi_parms s_dprm; +char secret_global_pan = SND_DEF_PAN; + +int play_digi_fx_master(int sfx_code, int num_loops, ObjID id, ushort x, ushort y) { + Id vocRes; + int retval, real_code = sfx_code, len; + uchar *addr; + extern uchar sfx_on; + extern uchar curr_sfx_vol; + + if (!sfx_on) + return -2; + if ((sfx_code == -1) || (sfx_code == 255)) + return -1; // why do we call this with things we dont use? + +#ifdef AUDIOLOGS + if (sfx_code > 255) + sfx_code = 0; + if (audiolog_playing(-1)) // what is this, really? + if (sfx_code != real_code) + audiolog_stop(); + if (sfx_code == real_code) +#endif + { + // If the sound effect is too far away, don't even bother us + if (id != OBJ_NULL) { + if (compute_sfx_vol(objs[id].loc.x, objs[id].loc.y, objs[PLAYER_OBJ].loc.x, objs[PLAYER_OBJ].loc.y) == 0) + return -1; + } else if (x != 0) + if (compute_sfx_vol(x, y, objs[PLAYER_OBJ].loc.x, objs[PLAYER_OBJ].loc.y) == 0) + return -1; + } + + vocRes = SFX_BASE + real_code; + + // s_dprm is the static data set for the parameters + s_dprm.loops = num_loops; + s_dprm.pri = priorities[sfx_code]; + s_dprm.pan = secret_global_pan; +#ifdef AUDIOLOGS + if (sfx_code != real_code) { + s_dprm.data = 0; + s_dprm.vol = volumes[sfx_code] * curr_sfx_vol / 100; + } else +#endif + { + if (id != OBJ_NULL) + s_dprm.data = id; + else if (x != 0) + s_dprm.data = (0x80000000 | (x << 16) | y); + else + s_dprm.data = 0; + s_dprm.snd_ref = vocRes; // okay, I'm cheating a little here + set_sample_pan_gain(&s_dprm); + } + + // have to hash x,y no id to a secret ID code, eh? + s_dprm.flags = 0; + addr = (uchar *)ResLock(vocRes); + len = ResSize(vocRes); + if (addr != NULL) { + retval = snd_sample_play(vocRes, len, addr, &s_dprm); + } else + critical_error(CRITERR_MEM | 9); + if (retval == SND_PERROR) { + ResUnlock(vocRes); + return -3; + } + return retval; // which sample id +} + +// scan through the whole list +uchar digi_fx_playing(int fx_id, int *handle_ptr) { + snd_digi_parms *sdp; + + if (fx_id == -1) + return FALSE; + + // should scan all current sfx's for snd_ref=fx_id+SFX_BASE + for (int i = 0; i < SND_MAX_SAMPLES; i++) { + if (snd_sample_playing(i)) { + sdp = snd_sample_parms(i); + if (sdp->snd_ref == fx_id + SFX_BASE) { + if (handle_ptr != NULL) + *handle_ptr = i; + return TRUE; + } + } + } + return FALSE; +} diff --git a/engine/src/GameSrc/drugs.c b/engine/src/GameSrc/drugs.c new file mode 100644 index 0000000..9ddbefa --- /dev/null +++ b/engine/src/GameSrc/drugs.c @@ -0,0 +1,709 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/drugs.c $ + * $Revision: 1.43 $ + * $Author: mahk $ + * $Date: 1994/09/06 12:05:30 $ + * + */ + +// Drugs.c Drugs Module +// Elisha Wiesel (SPAZ) +// +// The main C file for drug effects, updates, (un)installs. + +#include +#include + +#include "Prefs.h" +#include "cybstrng.h" +#include "drugs.h" +#include "fatigue.h" +#include "gameloop.h" +#include "gamestrn.h" +#include "init.h" +#include "mainloop.h" +#include "miscqvar.h" +#include "musicai.h" +#include "newmfd.h" +#include "objwarez.h" +#include "player.h" +#include "sfxlist.h" +#include "tools.h" + +#define MAX_UBYTE 0xFF + +#define INTENSITY(x) (player_struct.drug_intensity[x]) +#define STATUS(x) (player_struct.drug_status[x]) + +// --------- +// Constants +// --------- + +#define DRUG_UPDATE_FREQ 1024 // # of ticks in between updates ~3.5 secs +#define DRUG_DECAY 8 // Std chance a drug wears off each +#define DRUG_DECAY_MAX 16 // update is DECAY over DECAY_MAX +#define DRUG_DEFAULT_TIME \ + 8 // relative "duration" of a drug + // duration unit is 7 * 16/8 = 14 secs + +// Flags +#define DRUG_NONE 0x00 // No flags +#define DRUG_LONGER_DOSE 0x01 // If set, taking 2+ of a drug ups duration + +// ---------- +// Structures +// ---------- + +typedef struct { + uint8_t duration; // Preset duration of given drug + uint8_t flags; + void (*use)(); // Function slots for take, effect, wear off + void (*effect)(); + void (*wearoff)(); + void (*startup)(void); + void (*closedown)(bool visible); + void (*after_effect)(); +} DRUG; + +// ------- +// Globals +// ------- + +extern DRUG Drugs[NUM_DRUGZ]; // Global array of drugs + +// ---------- +// Prototypes +// ---------- +void drug_detox_effect(); + +// =========================================================================== +// INFRASTRUCTURE +// =========================================================================== + +// --------------------------------------------------------------------------- +// drug2triple() MAHK 7/27 +// +// Maps drug types (indices in to player_struct.drugs) into objtriples + +int drug2triple(int type) { return MAKETRIP(CLASS_DRUG, DRUG_SUBCLASS_STATS, type); } + +// --------------------------------------------------------------------------- +// triple2drug() MAHK 7/27 +// +// Maps triples onto drug types + +int triple2drug(int triple) { return TRIP2TY(triple); } + +// --------------------------------------------------------------------------- + +// get_drug_name() +// +// Returns the stringname of drug n + +char *get_drug_name(int n, char *buf) { + int triple; + + triple = drug2triple(n); + get_object_short_name(triple, buf, 50); + + return buf; +} + +// --------------------------------------------------------------------------- +// drug_use() +// +// Use an instance of drug n, install it as appropriate. + +void drug_use(int n) { + char buf[80]; + if (player_struct.drugs[n] == 0) + return; + + play_digi_fx(SFX_PATCH_USE, 1); + get_drug_name(n, buf); + get_string(REF_STR_Applied, buf + strlen(buf), sizeof(buf) - strlen(buf)); + message_info(buf); + player_struct.drugs[n]--; + + if (Drugs[n].flags & DRUG_LONGER_DOSE) + player_struct.drug_status[n] = lg_min((short)player_struct.drug_status[n] + Drugs[n].duration, 0x7F); + else + player_struct.drug_status[n] = Drugs[n].duration; + + if (Drugs[n].use) + Drugs[n].use(); + + mfd_notify_func(MFD_BIOWARE_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); + if (_current_loop <= FULLSCREEN_LOOP) + chg_set_flg(INVENTORY_UPDATE); +} + +// --------------------------------------------------------------------------- +// drug_wear_off() +// +// Uninstall the effect of drug n as appropriate. + +void drug_wear_off(int n) { + player_struct.drug_intensity[n] = 0; + player_struct.drug_status[n] = 0; // in case not called from update loop + + if (Drugs[n].wearoff) + Drugs[n].wearoff(); + + mfd_notify_func(MFD_BIOWARE_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); +} + +// -------------------------------------------------------------------------- +// drugs_update() +// +// This loop should get called as part of the main loop. It keeps track of +// the last time it was called, such that it actually executes only +// once every FREQ game ticks, as defined above + +void drugs_update() { + int i, decay; + + // Only update drugs once every FREQ game ticks + if ((player_struct.game_time - player_struct.last_drug_update) >= DRUG_UPDATE_FREQ) { + + // Reset the last update + player_struct.last_drug_update = player_struct.game_time; + + // drugs should wear out faster if health=low or fatigue=high + // THIS FORMULA SHOULD BE MADE INTELLIGENT. + decay = DRUG_DECAY + (player_struct.fatigue >> 10) / 16; + + // Iterate through drugs array + for (i = 0; i < NUM_DRUGZ; i++) { + // If effect is active... + if (player_struct.drug_status[i] > 0) { + + // Do something, if drug has continual effects + if (Drugs[i].effect) + Drugs[i].effect(); + + // Figure out if the drug wore off a little + if ((rand() % DRUG_DECAY_MAX) < decay) { + + // it did + player_struct.drug_status[i]--; + + if (player_struct.drug_status[i] == 0) // Totally wore off? + drug_wear_off(i); // yes + } + } else if (player_struct.drug_status[i] < 0) // after affect + { + + // Do something, if drug has continual effects + + // Figure out if the drug wore off a little + if ((rand() % DRUG_DECAY_MAX) < decay) { + + // it did + player_struct.drug_status[i]++; + } + if (Drugs[i].after_effect) + Drugs[i].after_effect(); + } + } + } +} + +// --------------------------------------------------------------------------- +// drugs_init() +// +// initialize drug system. + +void drugs_init() {} + +//--------------------------------------------------------------------------- +// drug_startup() +// +// do drug startup on game load. + +void drug_startup(bool visible) { + int i; + for (i = 0; i < NUM_DRUGZ; i++) + if (Drugs[i].startup != NULL) + Drugs[i].startup(); +} + +void drug_closedown(bool visible) { + int i; + for (i = 0; i < NUM_DRUGZ; i++) + if (Drugs[i].closedown != NULL) + Drugs[i].closedown(visible); +} + + //--------------------------------------------------------------------------- + // + + // =========================================================================== + // ACTUAL DRUG FUNCTIONS + // =========================================================================== + + // --------------------------------------------------------------------------- + // DUMMY FUNCTIONS + // --------------------------------------------------------------------------- + +#ifdef USE_DUMMY_FUNCS +// --------------------------------------------------------------------------- +// dummy_use_drug() +// +// A dummy function for using drugs. + +void dummy_use_drug() { return; } + +// --------------------------------------------------------------------------- +// dummy_effect_drug() +// +// A dummy function for continual drug effects + +void dummy_effect_drug() { return; } + +// --------------------------------------------------------------------------- +// dummy_wearoff_drug() +// +// A dummy function for drugs wearing off. + +void dummy_wearoff_drug() { return; } +#endif + +// -------------------------------------------------------------------------- +// LSD +// -------------------------------------------------------------------------- +void drug_lsd_effect(); +void drug_lsd_startup(void); +void drug_lsd_wearoff(); +void drug_lsd_closedown(bool visible); + +// --------------------------------------------------------------------------- +// drug_lsd_effect() +// +// Carry out a random palette swap, as an LSD effect. + +void drug_lsd_effect() { + int i; + uchar lsd_palette[768]; + int lsd_first, lsd_last; + + // Generate criteria for a random palette shift + lsd_first = (rand() % 253) + 1; + lsd_last = (rand() % 253) + 1; + if (lsd_last < lsd_first) { + i = lsd_first; + lsd_first = lsd_last; + lsd_last = i; + } + for (i = 0; i <= ((lsd_last - lsd_first) * 3); i++) + lsd_palette[i] = (uchar)(rand() % 256); + + gr_set_pal(lsd_first, (lsd_last - lsd_first + 1), lsd_palette); + + return; +} + +void drug_lsd_startup(void) { + if (STATUS(DRUG_LSD) > 0) { +#ifdef CRAZE_NODEATH + // super-secret craze hack setup + if (player_struct.hit_points == 1) + player_struct.hit_points = 2; +#endif // CRAZE_NODEATH + drug_lsd_effect(); + } +} + +// -------------------------------------------------------------------------- +// drug_lsd_wearoff() +// +// Uninstall the LSD effect. + +void drug_lsd_wearoff() { + // Return from palette shift + gr_set_pal(0, 256, ppall); + // KLC gamma_dealfunc(QUESTVAR_GET(GAMMACOR_QVAR)); + gamma_dealfunc(gShockPrefs.doGamma); +} + +void drug_lsd_closedown(bool visible) { + if (visible && STATUS(DRUG_LSD) > 0) + gr_set_pal(0, 256, ppall); +} + +// -------------------------------------------------------------------------- +// STAMINUP +// -------------------------------------------------------------------------- +void drug_staminup_use(); +void drug_staminup_effect(); +void drug_staminup_wearoff(); + +// --------------------------------------------------------------------------- +// drug_staminup_use() +// +// Initial effects of the staminup drug. + +extern uchar fatigue_warning; + +void drug_staminup_use() { + player_struct.fatigue = 0; +} + +// --------------------------------------------------------------------------- +// drug_staminup_effect() +// +// Continual effects of the staminup drug. + +void drug_staminup_effect() { + player_struct.fatigue = 0; +} + +// --------------------------------------------------------------------------- +// drug_staminup_wearoff() +// +// Final effects of the staminup drug. + +void drug_staminup_wearoff() { + player_struct.fatigue = MAX_FATIGUE; +} + +// -------------------------------------------------------------------------- +// SIGHT +// -------------------------------------------------------------------------- +void drug_sight_use(); +void drug_sight_startup(); +void drug_sight_effect(); +void drug_sight_wearoff(); +void drug_sight_after_effect(void); +void drug_sight_closedown(bool visible); + +extern void set_global_lighting(short); +#define SIGHT_LIGHT_LEVEL (4 << 8) + +// --------------------------------------------------------------------------- +// drug_sight_use() +// +// Initial effects of the sight drug. + +void drug_sight_use() { + if (INTENSITY(DRUG_SIGHT) == 0) + set_global_lighting(SIGHT_LIGHT_LEVEL); + INTENSITY(DRUG_SIGHT) = 1; +} + +void drug_sight_startup() { + if (STATUS(DRUG_SIGHT) > 0) { + set_global_lighting(SIGHT_LIGHT_LEVEL); + } else if (STATUS(DRUG_SIGHT) < 0) { + set_global_lighting(-SIGHT_LIGHT_LEVEL); + } +} + +// --------------------------------------------------------------------------- +// drug_sight_effect() +// +// Continual effects of the sight drug. +void drug_sight_effect() {} + +// --------------------------------------------------------------------------- +// drug_sight_wearoff() +// +// Final effects of the sight drug. + +void drug_sight_wearoff() { + INTENSITY(DRUG_SIGHT) = 0; + set_global_lighting(-2 * SIGHT_LIGHT_LEVEL); + STATUS(DRUG_SIGHT) = -Drugs[DRUG_SIGHT].duration / 4; +} + +void drug_sight_after_effect(void) { + if (STATUS(DRUG_SIGHT) == 0) + set_global_lighting(SIGHT_LIGHT_LEVEL); +} + +void drug_sight_closedown(bool visible) { + if (!visible) + return; + if (STATUS(DRUG_SIGHT) > 0) + set_global_lighting(-SIGHT_LIGHT_LEVEL); + else if (STATUS(DRUG_SIGHT) < 0) + set_global_lighting(SIGHT_LIGHT_LEVEL); +} + +// -------------------------------------------------------------------------- +// MEDIC +// -------------------------------------------------------------------------- +void drug_medic_use(); +void drug_medic_effect(); +void drug_medic_wearoff(); + +#define MEDIC_DECAY_RATE 8 +#define MEDIC_HEAL_STEPS 10 +// ushort medic_heal_rates[] = { 10,50,55,105,210} ; +ushort medic_heal_rates[] = {5, 5, 25, 25, 25, 30, 50, 55, 100, 110}; +#define MEDIC_HEAL_RATE 430 + +// --------------------------------------------------------------------------- +// drug_medic_use() +// +// Initial effects of the medic drug. + +void drug_medic_use() { + INTENSITY(DRUG_MEDIC) = lg_min(INTENSITY(DRUG_MEDIC) + MEDIC_HEAL_STEPS, MAX_UBYTE); + player_struct.hit_points_regen += MEDIC_HEAL_RATE; + chg_set_flg(VITALS_UPDATE); +} + +// --------------------------------------------------------------------------- +// drug_medic_effect() +// +// Continual effects of the medic drug. + +void drug_medic_effect() { + ubyte n = INTENSITY(DRUG_MEDIC); + if (n > 0) { + short delta; + if (n > MEDIC_HEAL_STEPS) { + delta = MEDIC_HEAL_RATE; + n -= MEDIC_HEAL_STEPS; + } else { + n--; + delta = medic_heal_rates[n % MEDIC_HEAL_STEPS]; + } + player_struct.hit_points_regen -= delta; + } + INTENSITY(DRUG_MEDIC) = n; + if (n == 0) + STATUS(DRUG_MEDIC) = 0; +} + +// --------------------------------------------------------------------------- +// drug_medic_wearoff() +// +// Final effects of the medic drug. + +void drug_medic_wearoff() { + while (INTENSITY(DRUG_MEDIC) > 0) + drug_medic_effect(); + + INTENSITY(DRUG_MEDIC) = 0; +} + +// -------------------------------------------------------------------------- +// REFLEX +// -------------------------------------------------------------------------- +void drug_reflex_use(); +void drug_reflex_effect(); +void drug_reflex_wearoff(); + +// --------------------------------------------------------------------------- +// drug_reflex_use() +// +// Initial effects of the reflex drug. + +void drug_reflex_use() { + extern char reflex_remainder; + reflex_remainder = 0; +} + +// --------------------------------------------------------------------------- +// drug_reflex_effect() +// +// Continual effects of the reflex drug. + +void drug_reflex_effect() {} + +// --------------------------------------------------------------------------- +// drug_reflex_wearoff() +// +// Final effects of the reflex drug. + +void drug_reflex_wearoff() {} + +// -------------------------------------------------------------------------- +// GENIUS +// -------------------------------------------------------------------------- +void drug_genius_use(); +void drug_genius_effect(); +void drug_genius_wearoff(); + +// --------------------------------------------------------------------------- +// drug_genius_use() +// +// Initial effects of the genius drug. + +void drug_genius_use() { + void mfd_gridpanel_set_winmove(uchar check); + + mfd_gridpanel_set_winmove(true); +} + +// --------------------------------------------------------------------------- +// drug_genius_effect() +// +// Continual effects of the genius drug. + +void drug_genius_effect() {} + +// --------------------------------------------------------------------------- +// drug_genius_wearoff() +// +// Final effects of the genius drug. + +void drug_genius_wearoff() {} + +// -------------------------------------------------------------------------- +// DETOX +// -------------------------------------------------------------------------- +void drug_detox_use(); +void wear_off_drug(int i); +void drug_detox_wearoff(); + +uchar detox_drug_order[] = { + DRUG_LSD, DRUG_SIGHT, DRUG_GENIUS, DRUG_STAMINUP, DRUG_REFLEX, DRUG_MEDIC, +}; + +#define NUM_DETOX_DRUGS (sizeof(detox_drug_order) / sizeof(detox_drug_order[0])) + +// --------------------------------------------------------------------------- +// drug_detox_use() +// +// Initial effects of the detox drug. + +void drug_detox_use() { + INTENSITY(DRUG_DETOX) += 2; + if (INTENSITY(DRUG_DETOX) == 2) + drug_detox_effect(); +} + +// --------------------------------------------------------------------------- +// drug_detox_effect() +// +// Continual effects of the detox drug. + +void wear_off_drug(int i) { + int laststat = STATUS(i); + STATUS(i) = 0; + if (laststat > 0 && Drugs[i].wearoff != NULL) + Drugs[i].wearoff(); + else if (laststat < 0 && Drugs[i].after_effect != NULL) + Drugs[i].after_effect(); +} + +void drug_detox_effect() { + int i, stack; + for (stack = 0; stack < INTENSITY(DRUG_DETOX); stack += 2) { + for (i = 0; i < NUM_DETOX_DRUGS; i++) { + int d = detox_drug_order[i]; + if (STATUS(d) < 0) { + wear_off_drug(d); + goto found; + } + } + for (i = 0; i < NUM_DETOX_DRUGS; i++) { + int d = detox_drug_order[i]; + if (STATUS(d) > 0) { + wear_off_drug(d); + goto found; + } + } + found:; + } + for (i = 0; i < NUM_DAMAGE_TYPES; i++) { + player_struct.hit_points_lost[i] /= INTENSITY(DRUG_DETOX) + 1; + } +} + +// --------------------------------------------------------------------------- +// drug_detox_wearoff() +// +// Final effects of the detox drug. + +void drug_detox_wearoff() { + INTENSITY(DRUG_DETOX) = 0; +} + +// --------------------------------------------------------------------------- + +DRUG Drugs[NUM_DRUGZ] = { + // Staminup + { + 10, // duration + DRUG_LONGER_DOSE, // flags + &drug_staminup_use, // use + &drug_staminup_effect, // effect + &drug_staminup_wearoff // wearoff + }, + // Sight + { + 20, // duration + DRUG_LONGER_DOSE, // flags + &drug_sight_use, // use + &drug_sight_effect, // effect + &drug_sight_wearoff, // wearoff + &drug_sight_startup, // startup + &drug_sight_closedown, // closedown + &drug_sight_after_effect, // aftereffect + }, + // Berserk + { + DRUG_DEFAULT_TIME / 2, // duration + DRUG_LONGER_DOSE, // flags + NULL, // use + &drug_lsd_effect, // effect + &drug_lsd_wearoff, // wearoff + &drug_lsd_startup, // startup + &drug_lsd_closedown // closedown + }, + // Medic + { + 10, // duration + DRUG_NONE, // flags + &drug_medic_use, // use + &drug_medic_effect, // effect + &drug_medic_wearoff // wearoff + }, + // Reflex + { + DRUG_DEFAULT_TIME, // duration + DRUG_LONGER_DOSE, // flags + &drug_reflex_use, // use + &drug_reflex_effect, // effect + &drug_reflex_wearoff // wearoff + }, + + // Genius + { + DRUG_DEFAULT_TIME / 2, // duration + DRUG_NONE, // flags + &drug_genius_use, // use + &drug_genius_effect, // effect + &drug_genius_wearoff // wearoff + }, + // Detox + { + DRUG_DEFAULT_TIME, // duration + DRUG_NONE, // flags + &drug_detox_use, // use + &drug_detox_effect, // effect + &drug_detox_wearoff, // wearoff + }, +}; diff --git a/engine/src/GameSrc/effect.c b/engine/src/GameSrc/effect.c new file mode 100644 index 0000000..dbd4e3d --- /dev/null +++ b/engine/src/GameSrc/effect.c @@ -0,0 +1,772 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/effect.c $ + * $Revision: 1.111 $ + * $Author: xemu $ + * $Date: 1994/11/21 21:06:39 $ + * + */ + +#include + +#include "grenades.h" +#include "objgame.h" +#include "objuse.h" +#include "trigger.h" +#include "effect.h" +#include "weapons.h" // for handart stuff +#include "objprop.h" +#include "objwpn.h" +#include "objsim.h" +#include "damage.h" +#include "player.h" +#include "mainloop.h" +#include "ai.h" +#include "objbit.h" +#include "otrip.h" +#include "cybrnd.h" +#include "schedule.h" +#include "wares.h" +#include "textmaps.h" +#include "frparams.h" +#include "gamesys.h" +#include "hudobj.h" +#include "aiflags.h" +#include "mapflags.h" +#include "doorparm.h" + +#define HANDART_SPEED 85 + +short anim_counter = 0; +AnimListing animlist[MAX_ANIMLIST_SIZE]; + +#define MAX_ANIMLIST_CALLBACKS 10 +AnimlistCB animlist_callbacks[MAX_ANIMLIST_CALLBACKS]; + +ubyte effect_matrix[CRIT_HIT_NUM][AMMO_TYPES][SEVERITIES] = { + { + // soft mutants - blood + {BLOOD_LIGHT, BLOOD_LIGHT}, // projectiles + {BLOOD_LIGHT, BLOOD_LIGHT}, // beam + {BLOOD_LIGHT, BLOOD_LIGHT}, // hand-to-hand + {M_EXPL2, M_EXPL2} // grenades + }, { + // plant mutant + {PLNT_EXPL, PLNT_EXPL}, // projectiles + {PLNT_EXPL, PLNT_EXPL}, // beam + {PLNT_EXPL, PLNT_EXPL}, // hand-to-hand + {PLNT_EXPL, PLNT_EXPL} // grenades + }, { + // robot + {BULLET_ROBOT, BULLET_ROBOT}, // projectile + {BEAM_ROBOT_LT, BEAM_ROBOT_HVY}, // beam guns + {IMPACT, IMPACT}, // hand-to-hand + {M_EXPL2, M_EXPL2} // grenades + }, { + // cyborgs + {BLOOD_LIGHT, BLOOD_LIGHT}, // flechette bullets + {BEAM_ROBOT_HVY, BEAM_ROBOT_HVY}, // beam guns + {IMPACT, IMPACT}, // hand-to-hand + {M_EXPL2, M_EXPL2} + }, { + // all other objects + {BULL_HIT_WALL, BULL_HIT_WALL}, + {BEAM_HIT_WALL, BEAM_HIT_WALL}, + {IMPACT, IMPACT}, + {M_EXPL2, M_EXPL2} + } +}; + + +// Internal Prototypes +void critter_light_world(ObjID id); +void critter_unlight_world(ObjID id); +int anim_frames(ObjID id); +errtype increment_anim(ulong num_units); +void init_animlist(void); + +// ----------------------------------------------------------------- +// do_special_effect_location +// + +ObjID beam_effect_id = OBJ_NULL; + +ObjID do_special_effect_location(ObjID owner, ubyte effect, ubyte start, ObjLoc *loc, short s) { + ObjID new_id = OBJ_NULL; + ObjSpecID osid; + int triple = 0; + + // are we suppose to destroy the attached object? + uchar special = DESTROY_OBJ_EFFECT(effect); + + // strip out the extra stuff + effect = EFFECT_VAL(effect); + + if ((effect < 1) || (effect > EFFECT_NUMS)) + return (OBJ_NULL); + + triple = EFFT2TRIP(effect); + if (triple) { + new_id = obj_create_base(triple); + if (new_id == OBJ_NULL) { + // make sure that if we're suppose to do an explosion - we do it! + if (ExplosionAnimatingProps[SCTRIP(triple)].frame_explode && special) + do_object_explosion(owner); + return (OBJ_NULL); + } + osid = objs[new_id].specID; + obj_move_to(new_id, loc, TRUE); + if (start == 0xFF) + start = START_FRAME(objAnimatings[osid].start_frame); + objAnimatings[osid].owner = owner; + objs[new_id].info.current_frame = start; + if (TRIP2SC(triple) == ANIMATING_SUBCLASS_EXPLOSION) { + if (ExplosionAnimatingProps[SCTRIP(triple)].frame_explode && special) + SET_EFFECT_DESTROY_OBJ(objAnimatings[osid].start_frame); + } + if (AnimatingProps[CPNUM(new_id)].flags & EFFECT_LIGHT_FLAG) { + MapElem *mmp; + ubyte i; + ubyte x = OBJ_LOC_BIN_X(*loc); + ubyte y = OBJ_LOC_BIN_Y(*loc); + ubyte light_bits = 0; + + for (i = 0; i < 4; i++) { + mmp = MAP_GET_XY(x + (i / 2), y + (i % 2)); + if (!me_bits_rend3(mmp)) { + me_rend3_set(mmp, me_bits_rend3(mmp) + 1); + light_bits |= (1 << i); + } + } + SET_EFFECT_LIGHT_MAP(objAnimatings[osid].start_frame, light_bits); + } + } + return (new_id); +} + +// -------------------------------------------------------------- +// do_special_effect() +// + +ObjID do_special_effect(ObjID owner, ubyte effect, ubyte start, ObjID target_id, short location) { + ObjLoc loc = objs[target_id].loc; + + return (do_special_effect_location(owner, effect, start, &loc, location)); +} + +void critter_light_world(ObjID id) { + int j; + ubyte light_bits = 0; + ubyte x = OBJ_LOC_BIN_X(objs[id].loc); + ubyte y = OBJ_LOC_BIN_Y(objs[id].loc); + MapElem *mmp; + + for (j = 0; j < 4; j++) { + mmp = MAP_GET_XY(x + (j / 2), y + (j % 2)); + if (!me_bits_rend3(mmp)) { + me_rend3_set(mmp, me_bits_rend3(mmp) + 1); + light_bits |= (1 << j); + } + } + + if (light_bits) { + SET_CRITLOCX(id, x); + SET_CRITLOCY(id, y); + SET_CRITTER_LAMP(id, light_bits); + } +} + +void critter_unlight_world(ObjID id) { + ubyte light_bits = CRITTER_LAMP(id); + ubyte x = CRITLOCX(id); + ubyte y = CRITLOCY(id); + ubyte j; + MapElem *mmp; + + if (light_bits) { + for (j = 0; j < 4; j++) { + if (light_bits & (1 << j)) { + mmp = MAP_GET_XY(x + (j / 2), y + (j % 2)); + me_rend3_set(mmp, me_bits_rend3(mmp) - 1); + } + } + CLEAR_CRITTER_LAMP(id); + } +} + +#define DEFAULT_ANIMLIST_SPEED 128 +// --------------------------------------------------------- +// anim_frames() +// + +// in gameobj.c also +#define MAX_TELEPORT_FRAME 10 +#define DIEGO_DEATH_BATTLE_LEVEL 8 + +int anim_frames(ObjID id) { + int retval = 1; + RefTable *prt; + + switch (objs[id].obclass) { + case CLASS_DOOR: + // prt = ResReadRefTable(door_id(id)); + prt = (RefTable *)ResLock(door_id(id)); + retval = prt->numRefs; + // ResFreeRefTable(prt); + ResUnlock(door_id(id)); + break; + default: + switch (objs[id].obclass) { + case CLASS_BIGSTUFF: + retval = objBigstuffs[objs[id].specID].cosmetic_value; + if (retval == 0) + retval = 1; + break; + case CLASS_SMALLSTUFF: + retval = objSmallstuffs[objs[id].specID].cosmetic_value; + if (retval == 0) + retval = 4; + break; + case CLASS_CRITTER: + if ((ID2TRIP(id) == DIEGO_TRIPLE) && (get_crit_posture(objs[id].specID) == DEATH_CRITTER_POSTURE) && + (player_struct.level != DIEGO_DEATH_BATTLE_LEVEL)) + retval = MAX_TELEPORT_FRAME; + break; + default: + retval = FRAME_NUM_3D(ObjProps[OPNUM(id)].bitmap_3d); + break; + } + break; + } + return (retval); +} + +// Light #defines +#define BRIGHT_LIGHT_FLASH 60L // brightness of flash +#define LIGHT_DELTA 4 // time length of flash + +extern ubyte energy_expulsion; +extern ubyte handart_count; +extern uchar handart_flash; + +#define DEFAULT_ANIMATION_SPEED 32 + +#ifdef USE_ANIMCRIT_DEFS +#define STANDARD_CRITTER_SPEED fix_make(0, 0x4000) +#define MIN_CRITTER_ANIM_SPEED 25 +#define MAX_CRITTER_ANIM_SPEED 200 +#define MIN_MOJO fix_make(0, 0x1A00) +#else +fix standard_critter_speed = fix_make(0, 0x5800); +fix min_mojo = fix_make(0, 0x1a00); +int min_critter_anim_speed = 35; +int max_critter_anim_speed = 170; +int attacking_anim_speed = 45; +#endif + +errtype increment_anim(ulong num_units) { + ObjSpecID osid; + ObjID id; + uchar anim_rem[MAX_ANIMLIST_SIZE]; + uchar cb_list[MAX_ANIMLIST_SIZE]; + int triple, num_frames; + char curr_frames; + int post, i; + short rem_num = 0, cb_num = 0; + int interval; + ulong new_units; + ulong hand_speed = HANDART_SPEED; + ubyte old_handart; + LightSchedEvent new_event; + extern uchar anim_on; +#ifdef SPEW_ON + char ft1[30]; +#endif + + // ************************************************************* + // HAND ART + // ************************************************************* + + if (handart_show > 1) { + old_handart = handart_show; + new_units = num_units + handart_remainder; + if (handart_count != 2) { + hand_speed = hand_speed / 2; + } + handart_show += (new_units / hand_speed); + handart_remainder = new_units % hand_speed; + if (old_handart != handart_show) + chg_set_flg(_current_3d_flag); + + // check to see if we're going to skip the fire frame, if so, don't skip it! + // - this is so we definitely show the fire frame, otherwise it would look very goooooofy - minman + + if (handart_show > (handart_count & 0x7F)) + handart_show = (handart_fire) ? 1 : 2; + } + if ((handart_show != 1) && handart_flash) { + byte light_val; + ubyte slot = player_struct.actives[ACTIVE_WEAPON]; + extern byte gun_fire_offset; + + switch (player_struct.weapons[slot].type) { + case (GUN_SUBCLASS_BEAM): + if (energy_expulsion) + light_val = (ubyte)((BRIGHT_LIGHT_FLASH * energy_expulsion) / 100); + break; + case (GUN_SUBCLASS_HANDTOHAND): + light_val = 0; + break; + case (GUN_SUBCLASS_PISTOL): + if (player_struct.weapons[slot].subtype == 1) { + light_val = 0; + break; + } + default: + light_val = BRIGHT_LIGHT_FLASH; + break; + } + + if (light_val) { + light_val += gun_fire_offset; + + new_event.timestamp = TICKS2TSTAMP(player_struct.game_time) + LIGHT_DELTA; + new_event.type = LIGHT_SCHED_EVENT; + new_event.light_value = light_val; + new_event.previous = (player_struct.hardwarez_status[CPTRIP(LANTERN_HARD_TRIPLE)] & WARE_ON); + { errtype err = schedule_event(&game_seconds_schedule, (SchedEvent *)&new_event); } + lamp_change_setting(light_val); + _frp_light_bits_set(LIGHT_BITS_CAM); + handart_flash = FALSE; + } + } + + if (!anim_on) + return (OK); + + // **************************************************** + // Class Animating + // **************************************************** + + osid = objAnimatings[0].id; + while (osid != OBJ_SPEC_NULL) { + short dest_frame; + + id = objAnimatings[osid].id; + if (id == OBJ_NULL) { + osid = OBJ_SPEC_NULL; + } else { + ubyte spd; + int cptrip; + + triple = MAKETRIP(objs[id].obclass, objs[id].subclass, objs[id].info.type); + new_units = num_units + objs[id].info.time_remainder; + cptrip = CPTRIP(triple); + spd = AnimatingProps[cptrip].speed; + if (spd == 0) { + spd = DEFAULT_ANIMATION_SPEED; + } + interval = new_units / spd; + objs[id].info.time_remainder = new_units % spd; + objs[id].info.current_frame += interval; + if (objs[id].subclass == ANIMATING_SUBCLASS_EXPLOSION) { + if (EFFECT_DESTROY_OBJ(objAnimatings[osid].start_frame) && + (objs[id].info.current_frame >= ExplosionAnimatingProps[SCNUM(id)].frame_explode)) { + do_object_explosion(objAnimatings[osid].owner); + CLEAR_EFFECT_DESTROY_OBJ(objAnimatings[osid].start_frame); + } + } + // dest_frame = objAnimatings[osid].end_frame; + // if (dest_frame==0) + dest_frame = FRAME_NUM_3D(ObjProps[OPTRIP(triple)].bitmap_3d); + if ((objs[id].info.current_frame != 255) && (objs[id].info.current_frame > dest_frame)) { + if (AnimatingProps[CPNUM(id)].flags & EFFECT_LIGHT_FLAG) { + ubyte i; + ubyte light_bits = EFFECT_LIGHT_MAP(objAnimatings[objs[id].specID].start_frame); + ubyte x = OBJ_LOC_BIN_X(objs[id].loc); + ubyte y = OBJ_LOC_BIN_Y(objs[id].loc); + MapElem *mmp; + + for (i = 0; i < 4; i++) { + if (light_bits & (1 << i)) { + mmp = MAP_GET_XY(x + (i / 2), y + (i % 2)); + me_rend3_set(mmp, me_bits_rend3(mmp) - 1); + } + } + CLEAR_EFFECT_LIGHT_MAP(objAnimatings[objs[id].specID].start_frame); + } + + switch (objs[id].subclass) { + case ANIMATING_SUBCLASS_TRANSITORY: + case ANIMATING_SUBCLASS_EXPLOSION: + ADD_DESTROYED_OBJECT(id); + if (id == beam_effect_id) { + hudobj_set_id(id, FALSE); + beam_effect_id = OBJ_NULL; + } + break; + default: + objs[id].info.current_frame = START_FRAME(objAnimatings[osid].start_frame); + break; + } + } + osid = objAnimatings[osid].next; + } + } + + // Objects on the animation list + LG_memset(anim_rem, 0, MAX_ANIMLIST_SIZE); + LG_memset(cb_list, 0, MAX_ANIMLIST_SIZE); + for (i = 0; i < anim_counter; i++) { + id = animlist[i].id; + num_frames = anim_frames(id); + new_units = num_units + objs[id].info.time_remainder; + interval = new_units / animlist[i].speed; + objs[id].info.time_remainder = new_units % animlist[i].speed; + if (animlist[i].flags & ANIMFLAG_REVERSE) { + switch (objs[id].obclass) { + case CLASS_DOOR: + if ((objs[id].info.current_frame - interval < DOOR_OPEN_FRAME) && + (objs[id].info.current_frame >= DOOR_OPEN_FRAME)) { + obj_physics_refresh_area(OBJ_LOC_BIN_X(objs[id].loc), OBJ_LOC_BIN_Y(objs[id].loc), TRUE); + } + break; + } + if (objs[id].info.current_frame < interval) { + if (animlist[i].flags & ANIMFLAG_CYCLE) { + if ((animlist[i].callback != 0) && (animlist[i].cbtype & ANIMCB_CYCLE)) + cb_list[cb_num++] = i; + objs[id].info.current_frame = 0; + // turn around + animlist[i].flags &= ~ANIMFLAG_REVERSE; + } else if (animlist[i].flags & ANIMFLAG_REPEAT) { + if ((animlist[i].callback != 0) && (animlist[i].cbtype & ANIMCB_REPEAT)) + cb_list[cb_num++] = i; + objs[id].info.current_frame = num_frames - 1; + } else { + objs[id].info.current_frame = 0; + anim_rem[rem_num++] = i; + switch (objs[id].obclass) { + case CLASS_DOOR: + // if we are a kind of door that blocks the renderer, and we are closed, then + // set our instance bit so that the renderer can know about it. + if (RENDER_BLOCK & ObjProps[OPNUM(id)].flags) + objs[id].info.inst_flags |= RENDER_BLOCK_FLAG; + break; + } + } + } else + objs[id].info.current_frame -= interval; + } else { + switch (objs[id].obclass) { + case CLASS_DOOR: + if (((objs[id].loc.p != 0) || (objs[id].loc.b != 0)) && + (objs[id].info.current_frame < DOOR_OPEN_FRAME) && + (objs[id].info.current_frame + interval >= DOOR_OPEN_FRAME)) { + obj_physics_refresh_area(OBJ_LOC_BIN_X(objs[id].loc), OBJ_LOC_BIN_Y(objs[id].loc), TRUE); + } + break; + } + objs[id].info.current_frame += interval; + + if (objs[id].info.current_frame >= num_frames) { + if (animlist[i].flags & ANIMFLAG_CYCLE) { + if ((animlist[i].callback != 0) && (animlist[i].cbtype & ANIMCB_CYCLE)) + cb_list[cb_num++] = i; + objs[id].info.current_frame = num_frames - 1; + // turn around + animlist[i].flags |= ANIMFLAG_REVERSE; + } else if (animlist[i].flags & ANIMFLAG_REPEAT) { + if ((animlist[i].callback != 0) && (animlist[i].cbtype & ANIMCB_REPEAT)) + cb_list[cb_num++] = i; + objs[id].info.current_frame = 0; + } else { + objs[id].info.current_frame = num_frames - 1; + anim_rem[rem_num++] = i; + } + } + } + } + + for (i = 0; i < cb_num; i++) + animlist_callbacks[animlist[cb_list[i]].callback](animlist[cb_list[i]].id, animlist[cb_list[i]].user_data); + for (i = 0; i < rem_num; i++) + remove_obj_from_animlist(animlist[anim_rem[i]].id); + + // Class Critter + // iterate through all the critters, and update their frames... + osid = objCritters[0].id; + while (osid != OBJ_SPEC_NULL) { + int cptripnum, asp; + id = objCritters[osid].id; + triple = MAKETRIP(objs[id].obclass, objs[id].subclass, objs[id].info.type); + if ((triple == DIEGO_TRIPLE) && (get_crit_posture(objs[id].specID) == DEATH_CRITTER_POSTURE) && + (player_struct.level != DIEGO_DEATH_BATTLE_LEVEL)) { + osid = objCritters[osid].next; + continue; + } + if (EFFECT_LOC(id)) { + // ulong time = (player_struct.game_time & 0x03); + // if ((time/32) != EFFECT_EIGHTH(id)) + // { + // SET_EFFECT_FRAME(id,EFFECT_FRAME(id)+(((time/32)+8-EFFECT_EIGHTH(id))%8)); + // if (EFFECT_FRAME(id) > FRAME_NUM_3D(ObjProps[OPTRIP(EFFT2TRIP(EFFECT_NUM(id)))].bitmap_3d)) + // SET_EFFECT_LOC(id,0); + // SET_EFFECT_EIGHTH(id, (time/32)); + // } + } + if ((id != PLAYER_OBJ) && (!ai_critter_sleeping(osid) || (get_crit_posture(osid) == DEATH_CRITTER_POSTURE))) { + new_units = num_units + objs[id].info.time_remainder; + cptripnum = CPTRIP(triple); + post = get_crit_posture(osid); + asp = CritterProps[cptripnum].anim_speed; + if (asp == 0) { + asp = 1; + } else { + if ((post == MOVING_CRITTER_POSTURE) && (objs[id].info.ph != -1)) { + State s; + fix pd; +#ifdef USE_PHYS_STATE + extern void get_phys_state(int ph, State *new_state, ObjID id); + get_phys_state(objs[id].info.ph, &s, id); +#else + EDMS_get_state(objs[id].info.ph, &s); +#endif + pd = fix_fast_pyth_dist(s.X_dot, s.Y_dot); + if (pd > min_mojo) { + asp = fix_int(fix_mul_div(fix_make(asp, 0), standard_critter_speed, pd)); + if (asp < min_critter_anim_speed) + asp = min_critter_anim_speed; + else if (asp > max_critter_anim_speed) + asp = max_critter_anim_speed; + } + } else if ((post == ATTACKING_CRITTER_POSTURE) || (post == ATTACKING2_CRITTER_POSTURE)) { + asp = attacking_anim_speed; + } + } + interval = new_units / asp; + objs[id].info.time_remainder = new_units % asp; + objs[id].info.inst_flags &= ~(UNLIT_FLAG); + + if (CRITTER_LAMP(id) && (ID2TRIP(id) != AUTOBOMB_TRIPLE)) + critter_unlight_world(id); + + // Do attack if at right point in anim + curr_frames = (objs[id].subclass == CRITTER_SUBCLASS_CYBER) ? 4 : CritterProps[CPTRIP(triple)].frames[post]; + if ((post == ATTACKING_CRITTER_POSTURE) || (post == ATTACKING2_CRITTER_POSTURE)) { + short att_frame; + + att_frame = (CritterProps[CPTRIP(triple)].fire_frame == 0) ? (curr_frames * 3) / 4 + : CritterProps[CPTRIP(triple)].fire_frame; + + // Only attack when first reaching or passing sancted attack frame + if ((objs[id].info.current_frame < att_frame) && + (objs[id].info.current_frame + interval >= att_frame)) { + ai_attack_player(osid, (post == ATTACKING2_CRITTER_POSTURE)); + // make sure we see the right attack frame + objs[id].info.current_frame = att_frame; + if (((ObjProps[OPTRIP(triple)].flags & LIGHT_TYPE) >> LIGHT_TYPE_SHF) == 3) { + objs[id].info.inst_flags |= UNLIT_FLAG; + if (!CRITTER_LAMP(id) && (ID2TRIP(id) != AUTOBOMB_TRIPLE)) + critter_light_world(id); + } + } else + objs[id].info.current_frame += interval; + } else + objs[id].info.current_frame += interval; + + // If past end of cycle, wrap around + if (objs[id].info.current_frame >= curr_frames) { + // If dying, remove at end of cycle + if (post == DEATH_CRITTER_POSTURE) { + objs[id].info.current_frame = curr_frames - 1; + ai_critter_really_dead(objs[id].specID); + + // turn off the darned autobomb before we kill it + if (CRITTER_LAMP(id) && (ID2TRIP(id) == AUTOBOMB_TRIPLE)) + critter_unlight_world(id); + + ADD_DESTROYED_OBJECT(id); + } else if ((post == ATTACKING_CRITTER_POSTURE) || (post == ATTACKING2_CRITTER_POSTURE) || + (post == DISRUPT_CRITTER_POSTURE) || (post == KNOCKBACK_CRITTER_POSTURE)) { + set_posture(objs[id].specID, ATTACK_REST_CRITTER_POSTURE); + } else { + // Otherwise, loop around again + objs[id].info.current_frame = 0; + objs[id].info.time_remainder = 0; + } + } + } + + // Go to next critter + osid = objCritters[osid].next; + } + + // Animating Textures + // Start at 1, since 0 is the normal texture group + for (i = 1; i < NUM_ANIM_TEXTURE_GROUPS; i++) { + if (animtextures[i].num_frames > 0) { + new_units = num_units + animtextures[i].time_remainder; + interval = new_units / animtextures[i].anim_speed; + animtextures[i].time_remainder = new_units % animtextures[i].anim_speed; + while (interval > 0) { + if (animtextures[i].flags & ANIMTEXTURE_REVERSED) { + // Currently, REVERSED implies CYCLE. Maybe this should change + // in the future. + animtextures[i].current_frame--; + if (animtextures[i].current_frame < 0) { + animtextures[i].flags &= ~(ANIMTEXTURE_REVERSED); + animtextures[i].current_frame = 0; + } + } else { + animtextures[i].current_frame++; + if (animtextures[i].current_frame >= animtextures[i].num_frames) { + if (animtextures[i].flags & ANIMTEXTURE_CYCLE) { + animtextures[i].flags |= ANIMTEXTURE_REVERSED; + animtextures[i].current_frame = animtextures[i].num_frames - 1; + } else { + animtextures[i].current_frame = 0; + } + } + } + interval--; + } + } + } + destroy_destroyed_objects(); + return (OK); +} + +void advance_animations(void) { + ulong time_diff; + + time_diff = (player_struct.game_time - player_struct.last_anim_check); + increment_anim(time_diff); + player_struct.last_anim_check = player_struct.game_time; +} + +// fills in requested types of information about an animating object, +// returning FALSE iff that object is not in the anim list. Pass a +// NULL pointer about a piece of data if you don't want it. +uchar anim_data_from_id(ObjID id, bool *reverse, bool *cycle) { + int i; + + for (i = 0; i < anim_counter; i++) { + if (animlist[i].id == id) { + if (reverse) + *reverse = (animlist[i].flags & ANIMFLAG_REVERSE) != 0; + if (cycle) + *cycle = (animlist[i].flags & ANIMFLAG_CYCLE) != 0; + return TRUE; + } + } + return FALSE; +} + +#define CHECK_ANIM_SPEED +errtype add_obj_to_animlist(ObjID id, uchar repeat, uchar reverse, uchar cycle, short speed, int cb_id, intptr_t user_data, + short cbtype) { + int i = 0; + uchar replace_me = FALSE; + int use_counter = anim_counter; +#ifdef CHECK_ANIM_SPEED + char count = 0; +#endif + + if (anim_counter == MAX_ANIMLIST_SIZE) { + return (ERR_NOMEM); + } + + for (i = 0; i < anim_counter; i++) { + if (animlist[i].id == id) { + replace_me = TRUE; + use_counter = i; + } + } + animlist[use_counter].id = id; + + // Set flags + animlist[use_counter].flags = 0; + if (repeat) + animlist[use_counter].flags |= ANIMFLAG_REPEAT; + if (reverse) + animlist[use_counter].flags |= ANIMFLAG_REVERSE; + if (cycle) + animlist[use_counter].flags |= ANIMFLAG_CYCLE; + if (speed) + animlist[use_counter].speed = speed; + else + animlist[use_counter].speed = DEFAULT_ANIMLIST_SPEED; +#ifdef CHECK_ANIM_SPEED + // Hmm, there's probably a better way to check for power-of-2-ness + for (i = 0; i < 16; i++) { + if (animlist[use_counter].speed & (1 << i)) + count++; + if (count > 1) { + break; + } + } +#endif + + animlist[use_counter].cbtype = cbtype; + animlist[use_counter].callback = cb_id; + animlist[use_counter].user_data = user_data; + if (!replace_me) + anim_counter++; + + objs[id].info.time_remainder = 0; + return (OK); +} + +errtype remove_obj_from_animlist(ObjID id) { + int i = 0; + AnimlistCB cb = NULL; + intptr_t ud; + + for (i = 0; i < anim_counter; i++) { + if (animlist[i].id == id) { + if ((animlist[i].callback != 0) && (animlist[i].cbtype & ANIMCB_REMOVE)) { + cb = animlist_callbacks[animlist[i].callback]; + ud = animlist[i].user_data; + } + anim_counter--; + animlist[i] = animlist[anim_counter]; + if (cb != NULL) + cb(id, ud); + return (OK); + } + } + return (ERR_NOEFFECT); +} + +errtype animlist_clear() { + LG_memset(animlist, 0, sizeof(AnimListing) * MAX_ANIMLIST_SIZE); + anim_counter = 0; + return (OK); +} + +void init_animlist(void) { + animlist_callbacks[1] = diego_teleport_callback; + animlist_callbacks[2] = destroy_screen_callback_func; + animlist_callbacks[3] = unshodanizing_callback; + animlist_callbacks[4] = unmulti_anim_callback; + animlist_callbacks[5] = multi_anim_callback; + animlist_callbacks[6] = animate_callback_func; +} diff --git a/engine/src/GameSrc/email.c b/engine/src/GameSrc/email.c new file mode 100644 index 0000000..b93b1e0 --- /dev/null +++ b/engine/src/GameSrc/email.c @@ -0,0 +1,1114 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/email.c $ + * $Revision: 1.116 $ + * $Author: xemu $ + * $Date: 1994/11/25 08:14:55 $ + */ + +#include +#include +#include + +#include "Prefs.h" + +#include "invdims.h" +#include "invent.h" +#include "mfdint.h" +#include "mfdext.h" +#include "mfddims.h" +#include "tools.h" +#include "gamestrn.h" +#include "wares.h" +#include "mfdgadg.h" +#include "input.h" +#include "emailbit.h" +#include "cit2d.h" +#include "criterr.h" + +#include "cybstrng.h" +#include "email.h" +#include "gamescr.h" +#include "mfdart.h" +#include "mfdfunc.h" +#include "newmfd.h" +#include "otrip.h" +#include "invpages.h" +#include "shodan.h" +#include "sfxlist.h" +#include "musicai.h" +#include "faketime.h" +#include "popups.h" +#include "loops.h" +#include "gr2ss.h" + +#include "vmail.h" + +#ifdef AUDIOLOGS +#include "audiolog.h" +#endif + +// ------- +// DEFINES +// ------- + +extern uchar full_game_3d; +LGCursor email_cursor; +grs_bitmap email_cursor_bitmap; +uchar email_cursor_currently = FALSE; +uchar shodan_sfx_go = FALSE; + +#define MFD_EMAILMUG_FUNC 14 +#define EMAIL_BASE_ID RES_email0 + +#define CHAR_SOFTSP 2 + +#define BASE_VMAIL 256 + +#define MUGSHOT_IDX 0 +#define TITLE_IDX 1 +#define SENDER_IDX 2 +#define SUBJECT_IDX 3 +#define MESSAGE_IDX 4 +#define EMAIL_MESSAGE_IDX (really_an_email ? MESSAGE_IDX : 0) + +#define EMAIL_INACTIVE 0xFF + +#define FOOTER_MORE_MASK 0x1u +#define FOOTER_PAGE_MASK 0x2u + +#define BUFSZ 50 + +// ------- +// GLOBALS +// ------- + +//#define current_email (player_struct.actives[ACTIVE_EMAIL]) +#define current_email (player_struct.current_email) + +//========================================== +// INVENTORY PANEL TEXT DISPLAY +//========================================== + +extern LGRegion *inventory_region; +extern grs_canvas *pinv_canvas; + +#define MESSAGE_COLOR 0x5A +#define MORE_COLOR 0x4C + +#define MESSAGE_X (1) +#define MESSAGE_Y (2) + +#define BOTTOM_MARGIN 10 +#define EMAIL_INTERCEPT 0xFE +#define EMAIL_DONE 0xFF + +uchar email_big_font = TRUE; + +char email_buffer[256]; +#define EMAIL_BUFSIZ (sizeof(email_buffer)) +ubyte next_text_line = EMAIL_DONE; +ubyte last_text_line = 0; +ubyte email_curr_page; +short old_invent_page = 0; +Id current_email_base = EMAIL_BASE_ID; + +static uchar intercept_hack_num; +static uchar email_flags; + +#define EMAIL_FLAG_BEEN_READ 0x1u +#define EMAIL_FLAG_TRANSITORY 0x2u + +Id email_font = RES_tinyTechFont; + +// ------------ +// PROTOTYPES +// ------------ +char *get_email_string(int id, char *text, int siz); +int get_sender_emailnum(int num); +char *get_email_title_string(int n, char *text, int siz); +void apply_email_macros(char *text, char *newval); +void email_intercept(void); +char *email_draw_string(char *text, short *x, short *y, bool last); +void free_email_buffer(void); +void draw_more_string(int x, int y, uchar footermask); +void email_draw_text(Id email_id, bool really_an_email); +uchar email_invpanel_input_handler(uiEvent *ev, LGRegion *r, intptr_t data); +void parse_email_mugs(char *mug, uchar *mcolor, ushort mugnums[NUM_MFDS], uchar setup); +uchar mfd_email_button_handler(MFD *m, LGPoint bttn, uiEvent *ev, void *data); +void email_slam_hack(short which); + +char *get_email_string(int id, char *text, int siz) { + get_string(id, text, siz); +#ifdef TOUPPER_EMAILS + strtoupper(text); +#endif + return text; +} + +#define MAX_SENDERS (sizeof(player_struct.email_sender_counts) / sizeof(player_struct.email_sender_counts[0])) + +#define NULL_SENDER MAX_SENDERS + +// scan through the mugshot for the super-secret "sender" code, which +// starts with an S and is followed by a decimal number. +// if we don't find one, return NULL_SENDER. + +int get_sender_emailnum(int num) { + char *s = get_temp_string(MKREF(EMAIL_BASE_ID + num, MUGSHOT_IDX)); + + for (; *s != '\0'; s++) + if (toupper(*s) == 'S') { + int sid = atoi(s + 1); + return (sid < MAX_SENDERS && sid >= 0) ? sid : NULL_SENDER; + } + return NULL_SENDER; +} + +void set_email_flags(int n) { + int sender_num = get_sender_emailnum(n); + uint32_t cnt; + + if (((player_struct.email[n] & EMAIL_SEQ) >> EMAIL_SEQ_SHF) > 0 || sender_num == NULL_SENDER) + return; + cnt = ++player_struct.email_sender_counts[sender_num]; + player_struct.email[n] &= ~(EMAIL_SEQ << EMAIL_SEQ_SHF); + player_struct.email[n] |= cnt << EMAIL_SEQ_SHF; +} + +char *get_email_title_string(int n, char *text, int siz) { + int cnt = (player_struct.email[n] & EMAIL_SEQ) >> EMAIL_SEQ_SHF; // get the email's sequence number. + Ref title = MKREF(EMAIL_BASE_ID + n, TITLE_IDX); + + get_string(title, text, siz); + if (cnt > 0) // if in fact the email has a sequence number, tack it on the end. + { + sprintf(text, get_temp_string(title), cnt); + } + return text; +} + +//#define EMAIL_MACRO_PAD_CHARS 20 +void apply_email_macros(char *text, char *newval) { + short cold = 0, cnew = 0, len; + char buf[256]; + char i, stupid; + int score; + + len = strlen(text); + + while (cold < len) { + if (text[cold] == '$') { + switch (text[cold + 1]) { + // player's name + case 'N': + case 'n': + strcpy(newval + cnew, player_struct.name); +#ifdef TOUPPER_EMAILS + if (email_big_font) + strtoupper(newval + cnew); +#else + if (islower(*(newval + cnew))) + *(newval + cnew) -= 'a' - 'A'; +#endif + cnew += strlen(player_struct.name); + break; + // number of kills + case 'K': + case 'k': + sprintf(buf, "%d", player_struct.num_victories); + strcpy(newval + cnew, buf); + cnew += strlen(buf); + break; + // time playing + case 'T': + case 't': + second_format(player_struct.game_time / CIT_CYCLE, buf); + strcpy(newval + cnew, buf); + cnew += strlen(buf); + break; + // number of revivals + case 'D': + case 'd': + sprintf(buf, "%d", player_struct.num_deaths); + strcpy(newval + cnew, buf); + cnew += strlen(buf); + break; + // difficulty index + case 'C': + case 'c': + stupid = 0; + for (i = 0; i < 4; i++) + stupid += (player_struct.difficulty[i] * player_struct.difficulty[i]); + sprintf(buf, "%d", stupid); + strcpy(newval + cnew, buf); + cnew += strlen(buf); + break; + // score + case 'S': + case 's': + stupid = 0; + for (i = 0; i < 4; i++) + stupid += (player_struct.difficulty[i] * player_struct.difficulty[i]); + // death is 10 anti-kills, but you always keep at least a third of your kills. + score = player_struct.num_victories - + lg_min(player_struct.num_deaths * 10, player_struct.num_victories * 2 / 3); + score = score * 10000; + score = score - lg_min(score * 2 / 3, ((player_struct.game_time / (CIT_CYCLE * 36)) * 100)); + score = score * (stupid + 1) / 37; // 9 * 4 + 1 is best difficulty factor + if (stupid == 36) + score += 2222222; // secret kevin bonus + sprintf(buf, "%d", score); + strcpy(newval + cnew, buf); + cnew += strlen(buf); + break; + default: + newval[cnew] = '$'; + newval[cnew + 1] = text[cold + 1]; + break; + } + cold += 2; + } else { + newval[cnew] = text[cold]; + cold++; + cnew++; + } + } + newval[cnew] = '\0'; +} + +void email_intercept(void) { + inventory_clear(); + next_text_line = EMAIL_DONE; + pop_inventory_cursors(); + email_cursor_currently = FALSE; + // Free(email_cursor_bitmap.bits); + read_email(0, intercept_hack_num); + shodan_sfx_go = TRUE; +#ifdef AUDIOLOGS + if (!audiolog_setting) +#endif + { + if (!digi_fx_playing(SFX_SHODAN_STRONG, NULL)) + play_digi_fx(SFX_SHODAN_STRONG, -1); + } +} + +char *email_draw_string(char *text, short *x, short *y, bool last) { + short w, h; + gr_set_fcolor(MESSAGE_COLOR); + gr_char_size('X', &w, &h); + while (isspace(*text)) { + if (*text == '\n') { + *y += h, *x = 0; + } else + *x += gr_char_width(*text); + text++; + } + while (*y + BOTTOM_MARGIN < INVENTORY_PANEL_HEIGHT) { + while (*x < INVENTORY_PANEL_WIDTH) { + char temp = '\0'; + char *end; + end = text; + while (!isspace(*end) && *end != '\0' && *end != '\n') + end++; + temp = *end; + *end = '\0'; + gr_string_size(text, &w, &h); + if (*x + w > INVENTORY_PANEL_WIDTH) { + short hyphensiz, dum; + char sav; + + *end = temp; + gr_char_size('-', &hyphensiz, &dum); + for (; end > text; end--) { + if (*end == CHAR_SOFTSP || *end == '-') { + sav = *end; + *end = '\0'; + gr_string_size(text, &w, &h); + if (*x + w + hyphensiz <= INVENTORY_PANEL_WIDTH) { + draw_shadowed_string(text, MESSAGE_X + *x, MESSAGE_Y + *y, full_game_3d); + draw_shadowed_string("-", MESSAGE_X + *x + w, MESSAGE_Y + *y, full_game_3d); + *end = sav; + text = end + 1; + break; + } else + *end = sav; + } + } + break; + } + draw_shadowed_string(text, MESSAGE_X + *x, MESSAGE_Y + *y, full_game_3d); + *x += w; + *end = temp; + if (temp == '\0' && end == text) + return NULL; + text = end; + if (temp == '\n') { + text++; + break; + } + + // now, assess the length of the whitespace after the token. + while (isspace(*end) && *end != '\n') + end++; + if (*end == '\n') { + text = end + 1; + break; + } + temp = *end; + *end = '\0'; + w = gr_string_width(text); + *x += w; + *end = temp; + if (*end == '\0') + return NULL; + text = end; + } + *x = 0; + *y += h; + } + if (last) { + gr_string_size(text, &w, &h); + if (w < INVENTORY_PANEL_WIDTH) // && *y + h < INVENTORY_PANEL_HEIGHT) + { + draw_shadowed_string(text, MESSAGE_X, *y + MESSAGE_Y, full_game_3d); + *x = w; + *y += h; + return NULL; + } + } + return text; +} + +void free_email_buffer(void) { email_buffer[0] = '\0'; } + +#define PAGE_STR_BUFSIZE 16 + +// Footer mask should have 0x1 set to print "MORE", 0x2 set to +// print page number. + +void draw_more_string(int x, int y, uchar footermask) { + gr_set_fcolor(MORE_COLOR); + + if (footermask & FOOTER_MORE_MASK) + res_draw_string(email_font, REF_STR_More, x + MESSAGE_X, y + MESSAGE_Y); + + // This part of code is never used (no FOOTER_PAGE_MASK invocation) + if (footermask & FOOTER_PAGE_MASK) { + // print page number + // in the future, this string will be in messages.txt + // and everyone will drive electric cars. + char pagen[PAGE_STR_BUFSIZE]; + short w, h; + sprintf(pagen, "%s %d", get_email_string(REF_STR_WordPage, NULL, PAGE_STR_BUFSIZE), email_curr_page); + gr_string_size(pagen, &w, &h); + draw_shadowed_string(pagen, INVENTORY_PANEL_WIDTH - w - 2, y + MESSAGE_Y, full_game_3d); + } +} + +void email_draw_text(Id email_id, bool really_an_email) { + short x = 0, y = 0; + char *remains = NULL; + char buf[256] = ""; +#ifdef SVGA_SUPPORT + uchar old_over; +#endif + + email_curr_page++; + + uiHideMouse(NULL); + make_email_cursor(&email_cursor, &email_cursor_bitmap, email_curr_page, email_curr_page == 1); + if (!email_cursor_currently) { + push_inventory_cursors(&email_cursor); + email_cursor_currently = TRUE; + } + uiShowMouse(NULL); + + if (!ResInUse(email_id)) { + return; + } + if (really_an_email) { + if (current_email == EMAIL_INACTIVE) { + current_email = EMAIL_INACTIVE; + return; + } + if (next_text_line == EMAIL_INTERCEPT) { + email_intercept(); + return; + } + } + if (next_text_line == EMAIL_DONE) { + return; + } + last_text_line = next_text_line; +#ifdef SVGA_SUPPORT + old_over = gr2ss_override; + gr2ss_override = OVERRIDE_ALL; +#endif + gr_push_canvas(pinv_canvas); + gr_set_font((grs_font *)ResLock(email_font)); + if (!full_game_3d) + uiHideMouse(inventory_region->r); + inventory_clear(); + if (*email_buffer != '\0') { + ubyte line = EMAIL_MESSAGE_IDX + next_text_line; + char *next = get_temp_string(MKREF(email_id, line)); + bool last; + + last = (next == NULL); + if ((remains = email_draw_string(email_buffer, &x, &y, last)) != NULL) { + strncpy(buf, remains, sizeof(buf)); + remains = buf; + goto more; + } + *email_buffer = '\0'; + x += gr_char_width(' '); + if (last) { + next_text_line = EMAIL_DONE; + current_email_base = EMAIL_BASE_ID; + goto done; + } + } + while (remains == NULL || gr_string_width(remains) < INVENTORY_PANEL_WIDTH) { + int len; + char *next; + bool last; + char tmp[256]; + ubyte line = EMAIL_MESSAGE_IDX + next_text_line++; + if (remains == NULL) + get_email_string(MKREF(email_id, line), buf, sizeof(buf)); + else + get_email_string(MKREF(email_id, line), buf + strlen(remains), sizeof(buf) - strlen(remains)); + apply_email_macros(buf, tmp); + strcpy(buf, tmp); + if (buf[0] == '\0') { + next_text_line = EMAIL_DONE; + current_email_base = EMAIL_BASE_ID; + goto done; + } + len = strlen(buf); + if (!isspace(buf[len - 1])) + strcpy(buf + len, " "); + next = get_temp_string(MKREF(email_id, line + 1)); + last = (next == NULL); + remains = email_draw_string(buf, &x, &y, last); + if (last) { + if (remains == NULL) { + ResUnlock(email_id); // KLC - we're done with it. + next_text_line = EMAIL_DONE; + current_email_base = EMAIL_BASE_ID; + goto done; + } else + goto more; + } + if (remains != NULL) { + char buf2[sizeof(buf)]; + strcpy(buf2, remains); + strcpy(buf, buf2); + remains = buf; + } + } + // Print the "more" message. +more: + if (remains != NULL) { + if (strlen(remains) >= EMAIL_BUFSIZ) { + critical_error(0x3005); + } + strcpy(email_buffer, remains); + } + draw_more_string(x, y, FOOTER_MORE_MASK); +done: + if (next_text_line == EMAIL_DONE) { + short w, h; + + if (email_flags & EMAIL_FLAG_TRANSITORY) { + player_struct.email[current_email] &= ~(EMAIL_GOT | EMAIL_READ); + } + gr_char_size('X', &w, &h); + x = 0; + y += h; + if (intercept_hack_num > 0) { + next_text_line = EMAIL_INTERCEPT; + gr_set_fcolor(MORE_COLOR); + draw_more_string(x, y, FOOTER_MORE_MASK); + } else if (email_curr_page > 1) + draw_more_string(x, y, 0); + } + ResUnlock(email_font); + if (!full_game_3d) + uiShowMouse(inventory_region->r); + gr_pop_canvas(); +#ifdef SVGA_SUPPORT + gr2ss_override = old_over; +#endif +} + +//#define BAD_EMAIL_KEYFLAGS (KB_FLAG_SHIFT | KB_FLAG_CTRL | KB_FLAG_ALT | KB_FLAG_SPECIAL) + +void email_page_exit(void) { + uint8_t mid; + + current_email = EMAIL_INACTIVE; + pop_inventory_cursors(); + email_cursor_currently = FALSE; + // Free(email_cursor_bitmap.bits); + next_text_line = EMAIL_DONE; + for (mid = 0; mid < NUM_MFDS; mid++) { + if (mfd_get_func(mid, player_struct.mfd_current_slots[mid]) == MFD_EMAILMUG_FUNC) + restore_mfd_slot(mid); + } +} + +uchar email_invpanel_input_handler(uiEvent *ev, LGRegion *r, intptr_t data) { + if (input_cursor_mode == INPUT_OBJECT_CURSOR) + return FALSE; + if (inventory_page != INV_EMAILTEXT_PAGE) { + if (email_cursor_currently) { + email_page_exit(); + } + return FALSE; + } + if (ev->type == UI_EVENT_MOUSE_MOVE) + return TRUE; + if (current_email == EMAIL_INACTIVE) + return FALSE; + if (ev->type == UI_EVENT_MOUSE && !(ev->subtype & (MOUSE_LDOWN | MOUSE_RDOWN | MOUSE_CDOWN))) + return TRUE; + // if (ev->type == UI_EVENT_KBD_COOKED && (ev->subtype & BAD_EMAIL_KEYFLAGS)) + // return FALSE; + if (ev->type == UI_EVENT_KBD_COOKED && !((ev->subtype & KB_FLAG_DOWN) != 0 && (ev->subtype & 0xFF) == ' ')) + return FALSE; + if (next_text_line == EMAIL_DONE) { + email_page_exit(); + inventory_draw_new_page(old_invent_page); + } else + email_draw_text(current_email_base + current_email, current_email_base == EMAIL_BASE_ID); + return TRUE; +} + +// ============================================ +// THE SELECTED EMAIL MFD +// ============================================ + +#define EMAILMUG_SLOT MFD_INFO_SLOT + +#define EMAIL_SUBJECT_Y (MFD_VIEW_HGT - 1) + +#define LAST_MUG(mfd) (*(short *)&player_struct.mfd_func_data[MFD_EMAILMUG_FUNC][mfd * 2]) + +#define COLOR_ESC_CHAR 'c' +#define INTERCEPT_ESC_CHAR 'i' +#define TRANSITORY_ESC_CHAR 't' +#define WHOAMI_ESC_CHAR 's' + +void parse_email_mugs(char *mug, uchar *mcolor, ushort mugnums[NUM_MFDS], uchar setup) { + short i, fwid; + char *s; + // char *sfront; + short lastmug = -1; + uchar esc_param, different; + char buf[64]; + + s = buf; + // sfront = s; + strcpy(s, mug); + + if (mug && *mug) { + + intercept_hack_num = 0; + while (!isdigit(*s) && *s != '\0') { + + fwid = 0; + + if (*s == COLOR_ESC_CHAR || *s == INTERCEPT_ESC_CHAR || *s == WHOAMI_ESC_CHAR) { + // goofy 2-digit hex parse + esc_param = 0; + if (*(s + 1) && *(s + 2)) { + esc_param = (str_to_hex(*(s + 1)) << 4) + str_to_hex(*(s + 2)); + fwid = 2; + } + } + + switch (*s) { + case TRANSITORY_ESC_CHAR: + email_flags |= EMAIL_FLAG_TRANSITORY; + break; + case COLOR_ESC_CHAR: + if (mcolor) { + *mcolor = esc_param; + } + break; + case INTERCEPT_ESC_CHAR: + if (!(email_flags & EMAIL_FLAG_BEEN_READ)) + intercept_hack_num = esc_param; + break; + default:; + } + s += fwid + 1; + while (!isalpha(*s) && !isdigit(*s) && *s != '\0') + s++; + } + } + different = 0; + for (i = 0; i < NUM_MFDS; i++) { + if (!isdigit(*s)) { + mugnums[i] = lastmug; + continue; + } + mugnums[i] = atoi(s); + if (mugnums[i] != lastmug) + different++; + lastmug = mugnums[i]; + while (isdigit(*s)) + s++; + while (!isdigit(*s) && *s != '\0') + s++; + } + if (setup) + cap_mfds_with_func(MFD_EMAILMUG_FUNC, different); +} + +void mfd_emailmug_expose(MFD *mfd, ubyte control) { + uchar full = control & MFD_EXPOSE_FULL; + int msg = current_email_base + current_email; + if (control == 0) // MFD is drawing stuff + { + int hnd; + // Do unexpose stuff here. + if (shodan_sfx_go) + shodan_sfx_go = FALSE; + if (digi_fx_playing(SFX_SHODAN_STRONG, &hnd)) + snd_end_sample(hnd); + return; + } + if (!ResInUse(msg)) { + current_email = EMAIL_INACTIVE; + } + if (current_email == EMAIL_INACTIVE) { + mfd_notify_func(MFD_EMPTY_FUNC, EMAILMUG_SLOT, TRUE, MFD_EMPTY, TRUE); + return; + } + if (control & MFD_EXPOSE) // Time to draw stuff + { + char buf[256]; + ushort mnums[NUM_MFDS]; + ushort mugnum; + short mid = NUM_MFDS; + int mug; + uchar mcolor = MESSAGE_COLOR; + parse_email_mugs((char *)RefGet(MKREF(msg, MUGSHOT_IDX)), &mcolor, mnums, FALSE); + for (mid = 0; mid < NUM_MFDS; mid++) + if (player_struct.mfd_current_slots[mid] == EMAILMUG_SLOT) { + break; + } + if (mid > mfd->id) + mid = 0; + mugnum = mnums[mfd->id - mid]; + if (mugnum != LAST_MUG(mfd->id)) + full = TRUE; + LAST_MUG(mfd->id) = mugnum; + if (!full) + goto out; + + mug = REF_IMG_EmailMugShotBase + mugnum; + // clear update rects + mfd_clear_rects(); + // set up canvas + gr_push_canvas(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + // Clear the canvas by drawing the background bitmap + if (!full_game_3d) + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + + // Slam in the mug shot, centered. + if ((mugnum < BASE_VMAIL) +#ifdef PLAYTEST + && RefIndexValid((RefTable *)ResGet(REFID(mug)), REFINDEX(mug))) +#else + ) // god, I love this job +#endif + { + FrameDesc *f = RefLock(mug); + if (f != NULL) { + ss_bitmap(&f->bm, (MFD_VIEW_WID - f->bm.w) / 2, (MFD_VIEW_HGT - f->bm.h) / 2); + RefUnlock(mug); + } else { + WARN("mfd_emailmug_expose(): could not load mugshot ", mug); + } + } + +#ifdef AUDIOLOGS + if (!audiolog_setting) +#endif + if (shodan_sfx_go) { + if (!digi_fx_playing(SFX_SHODAN_STRONG, NULL)) + play_digi_fx(SFX_SHODAN_STRONG, -1); + } + // Now, the text + if (mugnum == mnums[0]) { + char *sub; + short w, h; + + get_email_title_string(current_email, buf, sizeof(buf)); + strcat(buf, "\n"); + get_email_string(MKREF(msg, SENDER_IDX), buf + strlen(buf), sizeof(buf) - strlen(buf)); + mfd_full_draw_string(buf, 0, 0, mcolor, email_font, TRUE, TRUE); + get_email_string(REF_STR_MessageSubject, buf, sizeof(buf)); + sub = buf + strlen(buf); + get_email_string(MKREF(msg, SUBJECT_IDX), sub, sizeof(buf) - strlen(buf)); + // draw subject field only if subject string is non-null. + if (*sub) { + gr_string_wrap(buf, MFD_VIEW_WID - 1); + gr_string_size(buf, &w, &h); + gr_font_string_unwrap(buf); + mfd_full_draw_string(buf, 0, EMAIL_SUBJECT_Y - h, mcolor, email_font, TRUE, TRUE); + } + } + + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + if (mfd->id == MFD_LEFT && player_struct.mfd_current_slots[MFD_RIGHT] == EMAILMUG_SLOT) { + mfd_notify_func(MFD_EMAILMUG_FUNC, EMAILMUG_SLOT, FALSE, MFD_ACTIVE, TRUE); + } + + // Pop the canvas + gr_pop_canvas(); + // Now that we've popped the canvas, we can send the + // updated mfd to screen + mfd_update_rects(mfd); + } +out: + return; +} + +uchar mfd_emailmug_handler(MFD *mfd, uiEvent *ev) { + if (ev->type != UI_EVENT_MOUSE || !(ev->subtype & (MOUSE_LDOWN | MOUSE_RDOWN | MOUSE_CDOWN))) + return FALSE; + if (player_struct.hardwarez[HARDWARE_EMAIL] == 0) { + string_message_info(REF_STR_NoDataReader); + return TRUE; + } + read_email(0, current_email); + return TRUE; +} + +//========================================================== +// DISPLAY A MESSAGE +//========================================================== + +void select_email(int num, uchar scr) { + int id; + int mug_num; + current_email_base = EMAIL_BASE_ID; + id = current_email_base + num; + if (!ResInUse(id)) { + current_email = EMAIL_INACTIVE; + return; + } + + mug_num = atoi((char *)RefGet(MKREF(id, MUGSHOT_IDX))); + ResUnlock(id); + + if (mug_num >= BASE_VMAIL) + read_email(current_email_base, num); + if (scr) { + current_email = num; + next_text_line = EMAIL_DONE; + mfd_notify_func(MFD_EMAILMUG_FUNC, EMAILMUG_SLOT, TRUE, MFD_ACTIVE, TRUE); + if (mug_num < BASE_VMAIL && inventory_page == INV_EMAILTEXT_PAGE) + read_email(0, num); + } +} + +#define FIRST_CDATA_NUM 0x10f +void read_email(Id new_base, int num) { + int id; + int mug_num; +#ifdef AUDIOLOGS + errtype alog_rv = ERR_NOEFFECT; +#endif + // KLC - use a global preference now ubyte terseness = player_struct.terseness; + ubyte terseness = gShockPrefs.goMsgLength; + + email_curr_page = 0; + if (new_base != 0) + current_email_base = new_base; + // no intercept if we are reading a paper. Bit sloppy to do it + // this way, but no sloppier than papers in general. + // And if you thought the hack for papers was bad, wait until you see the one for datas... -- X + if ((current_email_base == RES_paper0) || (num >= FIRST_CDATA_NUM)) { + intercept_hack_num = 0; + } + id = current_email_base + num; + if (!ResInUse(id)) { + current_email = EMAIL_INACTIVE; + return; + } + + email_flags = 0; + if (current_email_base == EMAIL_BASE_ID) { +#ifdef AUDIOLOGS + alog_rv = audiolog_play(num); +#endif + if (player_struct.email[num] & EMAIL_READ) + email_flags |= EMAIL_FLAG_BEEN_READ; + player_struct.email[num] |= EMAIL_READ; + } else + terseness = 0; + current_email = num; + if (inventory_page >= 0) + old_invent_page = inventory_page; +#ifdef AUDIOLOGS + if ((alog_rv != OK) || (audiolog_setting == 2)) { +#endif + inventory_draw_new_page(INV_EMAILTEXT_PAGE); + next_text_line = 0; + if (terseness > 0) // let's be terse + { + // skip ahead to the terse version + while (*get_temp_string(MKREF(current_email_base + num, MESSAGE_IDX + next_text_line)) != '\0') + next_text_line++; + next_text_line++; + } + free_email_buffer(); +#ifdef AUDIOLOGS + } +#endif + + if (current_email_base == EMAIL_BASE_ID) { + player_struct.hardwarez_status[HARDWARE_EMAIL] &= ~(WARE_FLASH); + QUESTBIT_OFF(0x12c); + + mug_num = atoi((char *)RefGet(MKREF(current_email_base + num, MUGSHOT_IDX))); + ResUnlock(current_email_base + num); + + if (mug_num >= BASE_VMAIL) // video email + { +#ifdef AUDIOLOGS + if ((alog_rv != OK) || (audiolog_setting == 2)) { +#endif + // draw the text for the vmail before playing vmail + email_draw_text(current_email_base + current_email, current_email_base == EMAIL_BASE_ID); + play_vmail(mug_num - BASE_VMAIL); +#ifdef AUDIOLOGS + } else { + } +#endif + } else { + mfd_notify_func(MFD_EMAILMUG_FUNC, EMAILMUG_SLOT, TRUE, MFD_ACTIVE, TRUE); + if (current_email != EMAIL_INACTIVE) { + int i; + ushort mnums[NUM_MFDS]; + uchar grab = TRUE; + parse_email_mugs((char *)RefGet(MKREF(current_email_base + num, MUGSHOT_IDX)), NULL, mnums, TRUE); + for (i = 1; i < NUM_MFDS; i++) { + if (mnums[i] != mnums[i - 1]) { + save_mfd_slot(i); + mfd_change_slot(i, EMAILMUG_SLOT); + grab = FALSE; + } + } + if (grab) { + i = mfd_grab_func(MFD_EMAILMUG_FUNC, EMAILMUG_SLOT); + save_mfd_slot(i); + mfd_change_slot(i, EMAILMUG_SLOT); + } else { + save_mfd_slot(0); + mfd_change_slot(0, EMAILMUG_SLOT); + } + } + } + } +#ifdef AUDIOLOGS + if ((alog_rv != OK) || (audiolog_setting == 2)) { +#endif + email_draw_text(current_email_base + current_email, current_email_base == EMAIL_BASE_ID); +#ifdef AUDIOLOGS + } else { + if (_current_loop <= FULLSCREEN_LOOP) + chg_set_flg(INVENTORY_UPDATE); + } +#endif +} + +//======================================================= +// INITIALIZATION +//======================================================= + +void add_email_handler(LGRegion *r) { + int id; + uiInstallRegionHandler(r, UI_EVENT_MOUSE_MOVE | UI_EVENT_MOUSE | UI_EVENT_KBD_COOKED, email_invpanel_input_handler, + 0, &id); +} + +//===================================================== +// THE EMAIL PAGE SELECT MFD +//===================================================== +#define MFD_EMAILWARE_FUNC 15 +#define NUM_EMAIL_BUTTONS 3 +#define BUTTON_LIT_STATE(mfd, butt) \ + (player_struct.mfd_func_data[MFD_EMAILWARE_FUNC][NUM_EMAIL_BUTTONS * (mfd) + (butt)]) + +#define EMAIL_BARRAY_X 0 +#define EMAIL_BARRAY_WD (MFD_VIEW_WID) +#define EMAIL_BARRAY_Y (MFD_VIEW_HGT - res_bm_height(REF_IMG_EmailButt0) - 2) + +static ubyte email_pages[] = {50, 7, 8}; +#define STRINGS_PER_WARE (REF_STR_wareSpew1 - REF_STR_wareSpew0) + +void mfd_emailware_expose(MFD *mfd, ubyte control) { + uchar full = control & MFD_EXPOSE_FULL; + uchar on = (control & (MFD_EXPOSE | MFD_EXPOSE_FULL)) != 0; + ubyte s = player_struct.hardwarez_status[HARDWARE_EMAIL]; + if (control == 0) { + int mfd_id = NUM_MFDS; + + // if we aren't showing the email hardware mfd any more, then + // turn off the MFD + while (mfd_yield_func(MFD_EMAILWARE_FUNC, &mfd_id)) { + if (mfd_id != mfd->id) + on = TRUE; + } + } + if (((s & WARE_ON) != 0) != on) { + use_ware(WARE_HARD, HARDWARE_EMAIL); + } + + if (control == 0) + return; + + mfd_clear_rects(); + gr_push_canvas(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + // Lay down the "background" + mfd_item_micro_expose(TRUE, VIDTEX_HARD_TRIPLE); + // mfd_item_micro_hires_expose(TRUE,VIDTEX_HARD_TRIPLE); + if (full) { + uchar n = HARDWARE_EMAIL; + uchar v = player_struct.hardwarez[n]; + draw_mfd_item_spew(REF_STR_wareSpew0 + STRINGS_PER_WARE * n, v); + } + + // clear rects so that we don't draw it if we don't have to + if (!full) + mfd_clear_rects(); + + for (uint8_t i = 0; i < NUM_EMAIL_BUTTONS; i++) { + uchar lit = inventory_page == email_pages[i]; + if (full || BUTTON_LIT_STATE(mfd->id, i) != lit) { + int id = (lit) ? REF_IMG_LitEmailButt0 + i : REF_IMG_EmailButt0 + i; + short x = EMAIL_BARRAY_WD * i / NUM_EMAIL_BUTTONS + EMAIL_BARRAY_X; + short y = EMAIL_BARRAY_Y; + draw_res_bm(id, x, y); + mfd_add_rect(x, y, x + res_bm_width(id), y + res_bm_height(id)); + BUTTON_LIT_STATE(mfd->id, i) = lit; + } + } + gr_pop_canvas(); + mfd_update_rects(mfd); +} + +uchar mfd_email_button_handler(MFD *m, LGPoint bttn, uiEvent *ev, void *data) { + current_email_base = EMAIL_BASE_ID; + old_invent_page = bttn.x; + inventory_draw_new_page(email_pages[bttn.x]); + mfd_notify_func(MFD_EMAILWARE_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + return TRUE; +} + +errtype mfd_emailware_init(MFD_Func *f) { + int cnt = 0; + LGPoint bsize; + LGPoint bdims; + LGRect r; + errtype err; + bsize.x = res_bm_width(REF_IMG_EmailButt0); + bsize.y = res_bm_height(REF_IMG_EmailButt0); + bdims.x = NUM_EMAIL_BUTTONS; + bdims.y = 1; + RECT_FILL(&r, EMAIL_BARRAY_X, EMAIL_BARRAY_Y, EMAIL_BARRAY_X + EMAIL_BARRAY_WD, EMAIL_BARRAY_Y + bsize.y) + err = MFDBttnArrayInit(&f->handlers[cnt++], &r, bdims, bsize, mfd_email_button_handler, NULL); + if (err != OK) + return err; + f->handler_count = cnt; + return OK; +} +//===================================================== +// THE EMAIL WARE +//===================================================== +short last_email_taken = 0; + +void email_turnon(uchar visible, uchar real_start) { + uchar flash = player_struct.hardwarez_status[HARDWARE_EMAIL] & WARE_FLASH; + current_email_base = EMAIL_BASE_ID; + player_struct.hardwarez_status[HARDWARE_EMAIL] &= ~(WARE_FLASH); + QUESTBIT_OFF(0x12c); + if (real_start) { + inventory_draw_new_page(email_pages[flash ? EMAIL_VER : last_email_taken]); + set_inventory_mfd(MFD_INV_HARDWARE, HARDWARE_EMAIL, TRUE); + mfd_change_slot(mfd_grab_func(MFD_EMAILWARE_FUNC, MFD_ITEM_SLOT), MFD_ITEM_SLOT); + } +} + +void email_turnoff(uchar visible, uchar real_stop) { + if (real_stop) { + int mfd_id = NUM_MFDS; + while (mfd_yield_func(MFD_EMAILWARE_FUNC, &mfd_id)) { + restore_mfd_slot(mfd_id); + } + } +} + +// every 30 nerd seconds, check to see if we have unread email and flash the email ware +// if we do. + +#define FLASH_TIME_INTERVAL 30 + +void update_email_ware() { + if ((player_struct.game_time >> APPROX_CIT_CYCLE_SHFT) % FLASH_TIME_INTERVAL != 0) + return; + + for (uint8_t i = 0; i < NUM_EMAIL_PROPER; i++) { + uint8_t s = player_struct.email[i]; + if ((s & EMAIL_GOT) != 0 && (s & EMAIL_READ) == 0) { + player_struct.hardwarez_status[HARDWARE_EMAIL] |= WARE_FLASH; + return; + } + } +} + +//===================================================== +// THE EMAIL INVENTORY PAGE +//===================================================== + +char *email_name_func(void *dp, int num, char *buf) { return get_email_title_string(num, buf, BUFSZ); } + +uchar email_color_func(void *dp, int num) { return (player_struct.email[num] & EMAIL_READ ? 0x5C : 0x59); } + +// SHODAN wacky earth destroying hacking +void email_slam_hack(short which) { + void add_email_datamunge(short munge, uchar select); + + add_email_datamunge(which, TRUE); + read_email(EMAIL_BASE_ID, which); +} diff --git a/engine/src/GameSrc/faceobj.c b/engine/src/GameSrc/faceobj.c new file mode 100644 index 0000000..8071074 --- /dev/null +++ b/engine/src/GameSrc/faceobj.c @@ -0,0 +1,347 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/faceobj.c $ + * $Revision: 1.28 $ + * $Author: dc $ + * $Date: 1994/09/08 06:30:25 $ + */ + +#include + +#include "gameobj.h" + +#include "fr3d.h" +#include "frintern.h" +#include "frtables.h" + +#include "map.h" + +#include "objsim.h" +#include "objprop.h" +#include "objclass.h" + +#include "otrip.h" +#include "objbit.h" +#include "trigger.h" +#include "frflags.h" +#include "doorparm.h" + +#include "tfdirect.h" +#include "ss_flet.h" + +#define height_step fix_make(0, 0x010000 >> SLOPE_SHIFT) + +// for raycast exclusion and wackiness +extern ObjID terrain_hit_obj; +extern ObjID terrain_hit_exclusion; + +static int _n_o_rad; +static Obj *_n_fr_cobj; + +void terrain_object_collide(physics_handle src, ObjID target); + +#define start_facelet(which) (&facelets[which][0][0]) + +// Internal Prototypes +fix *localize_object(fix *ax_pt); +uchar _axial_relativize(fix *src_pts, fix *targ_pts, uchar vec); +uchar setup_cube_face(fix ndist, fix xp, fix yp, fix xhlf, fix yhlf, int nrm_cmp); +uchar _face_parm_cube(fix *cntr, int x, int y, int z); +void _face_secret_repulsor_hack(void); +void facelet_obj(ObjID cobjid); + +// should these become static or pass around, or make relativize a #define +static void _1d_relativize(fixang a_v, fix *plst, fix *tpts, uchar t1, uchar t2, uchar t3) { + fix s, c; + fix_fastsincos(a_v, &s, &c); + tpts[t1] = fix_mul(plst[t1], c) + fix_mul(plst[t2], s); + tpts[t2] = fix_mul(plst[t1], -s) + fix_mul(plst[t2], c); + tpts[t3] = plst[t3]; +} + +fix *localize_object(fix *ax_pt) { + ax_pt[0] = tf_raw_pt[0] - ((fix)(_n_fr_cobj->loc.x << 8)); + ax_pt[1] = tf_raw_pt[1] - ((fix)(_n_fr_cobj->loc.y << 8)); + ax_pt[2] = tf_raw_pt[2] - ((fix)(_n_fr_cobj->loc.z * (height_step >> 3))); + return &ax_pt[0]; +} + +// convert a point into a relative FoRef +// pass in nothing, oh well +// implied are the current object, which has a location and an x,y,z +uchar _axial_relativize(fix *src_pts, fix *targ_pts, uchar vec) // (fixang l_h, fixang l_p, fixang l_b) +{ + // fixang l_h, fixang l_p, fixang l_b) + fixang l_h, l_p, l_b; + // tweak if necessary for non-standard object centers, i guess + if (vec) { + l_h = ((_n_fr_cobj->loc.h << 8)); + l_p = (-(_n_fr_cobj->loc.p << 8)); + l_b = (-(_n_fr_cobj->loc.b << 8)); + } else { + l_h = (-(_n_fr_cobj->loc.h << 8)); + l_p = ((_n_fr_cobj->loc.p << 8)); + l_b = ((_n_fr_cobj->loc.b << 8)); + } + + // now go relativize + if ((l_p) | (l_b)) { // if any pitch of bank + if (((l_b) | (l_h)) == 0) + _1d_relativize(l_p, src_pts, targ_pts, 1, 2, 0); + else if ((l_b) == 0) { // 2d transform, just pitch and heading.. + // Warning(("Obj with pitch and heading changed\n")); + return FALSE; + } else { // unsupported case for now + // Warning(("Obj w/all 3 axis changed\n")); + return FALSE; + } + } else if (l_h) + _1d_relativize(l_h, src_pts, targ_pts, 0, 1, 2); + else + + *(g3s_vector *)targ_pts = *(g3s_vector *)src_pts; // _memcpy32l(targ_pts,src_pts,3); + + // mprintf("Rtv'd %x %x %x to %x %x %x from %x %x %x\n", + // src_pts[0],src_pts[1],src_pts[2],targ_pts[0],targ_pts[1],targ_pts[2],l_h,l_p,l_b); + return TRUE; +} + +uchar setup_cube_face(fix ndist, fix xp, fix yp, fix xhlf, fix yhlf, int nrm_cmp) { + fix unit_norm[3]; + fix l_pt[3], walls[4][2], nrm[3]; + int flg = SS_BCD_PRIM_MULTI | TF_FLG_BOX_FULL; + + walls[0][0] = -xhlf; + walls[0][1] = yhlf; + walls[1][0] = xhlf; + walls[1][1] = yhlf; + walls[2][0] = xhlf; + walls[2][1] = -yhlf; + walls[3][0] = -xhlf; + walls[3][1] = -yhlf; + l_pt[0] = xp; + l_pt[1] = yp; + l_pt[2] = ndist; + + LG_memset(unit_norm, 0, 3 * 4); // _memset32l(unit_norm,0,3); + if (nrm_cmp < 0) + unit_norm[(-nrm_cmp) - 1] = -fix_1; + else + unit_norm[nrm_cmp - 1] = fix_1; + _axial_relativize(unit_norm, nrm, TRUE); + if (nrm[2] > fix_make(0, 0xC000)) // sure, why not... + flg |= SS_BCD_TYPE_FLOOR; + else + flg |= SS_BCD_TYPE_WALL; + // mprintf("SCF: dist %x, pos %x %x, size %x %x, norm %x %x %x f + // %d\n",ndist,xp,yp,xhlf,yhlf,nrm[0],nrm[1],nrm[2],nrm_cmp); + return tf_solve_aligned_face(l_pt, walls, flg, nrm); +} + +//#define MIN_SIZE 0x0500 +#define MIN_SIZE 0x0050 +uchar _face_parm_cube(fix *cntr, int x, int y, int z) { + uchar rv = FALSE; + // mprintf("FDPC: o %x %x %x ph %x %x %x, at %x %x %x, size %x %x %x, rad %x\n", + // _n_fr_p.x,_n_fr_p.y,_n_fr_p.z,tf_raw_pt[0],tf_raw_pt[1],tf_raw_pt[2],cntr[0],cntr[1],cntr[2],x,y,z,tf_rad); + if ((y > MIN_SIZE) && (z > MIN_SIZE)) { + if ((cntr[0] > x) && (cntr[0] < x + tf_rad)) + rv |= setup_cube_face(cntr[0] - x, cntr[1], cntr[2], y, z, 1); + else if ((cntr[0] < -x) && (cntr[0] > -x - tf_rad)) + rv |= setup_cube_face(-cntr[0] - x, -cntr[1], cntr[2], y, z, -1); + } + if ((x > MIN_SIZE) && (z > MIN_SIZE)) { + if ((cntr[1] > y) && (cntr[1] < y + tf_rad)) + rv |= setup_cube_face(cntr[1] - y, -cntr[0], cntr[2], x, z, 2); + else if ((cntr[1] < -y) && (cntr[1] > -y - tf_rad)) + rv |= setup_cube_face(-cntr[1] - y, cntr[0], cntr[2], x, z, -2); + } + if ((x > MIN_SIZE) && (y > MIN_SIZE)) { + if ((cntr[2] > z) && (cntr[2] < z + tf_rad)) + rv |= setup_cube_face(cntr[2] - z, cntr[0], cntr[1], x, y, 3); + else if ((cntr[2] < -z) && (cntr[2] > -z - tf_rad)) + rv |= setup_cube_face(-cntr[2] - z, cntr[0], -cntr[1], x, y, -3); + } + return rv; +} + +void _face_secret_repulsor_hack(void) { + int comparator = objTraps[_n_fr_cobj->specID].comparator, r_prm, flg, r_top, r_bot; + uchar special; + + if (tf_ph == -1) + return; // dont fuck with bullets or L-O-Sight + if ((tf_loc_pt[0] < 0) || (tf_loc_pt[1] < 0) || // if we are not in the main square for + (tf_loc_pt[0] >= fix_1) || (tf_loc_pt[1] >= fix_1)) + return; // the object, dont repulse it.. + + if (!comparator_check(comparator, OBJ_NULL, &special)) + return; // make sure the repulsor is active + + if ((r_bot = objTraps[_n_fr_cobj->specID].p2) != 0) // if lower bound + if (objTraps[_n_fr_cobj->specID].p2 > tf_raw_pt[2]) + return; // too low + if ((r_top = objTraps[_n_fr_cobj->specID].p3) != 0) // if upper bound + if (r_top + (tf_rad >> 1) + (tf_rad >> 2) < tf_raw_pt[2]) + return; // too high + flg = (objTraps[_n_fr_cobj->specID].p4 + 1) << SS_BCD_REPUL_SHF; + if ((flg & SS_BCD_REPUL_TYPE) == SS_BCD_REPUL_UP) { + if (r_top == 0) + r_prm = fix_make(7453, 0); // that should be pretty darn high, folks + else + r_prm = r_top + (tf_rad >> 1) + (tf_rad >> 2); + } else + r_prm = r_bot; + + tf_global_bcd_add(flg | TF_FLG_HPARAM, r_prm); +} + +void facelet_obj(ObjID cobjid) { + short objtrip; + int obj_type = FAUBJ_UNKNOWN; + char scale = 0; + uchar tfimp = FALSE; + + // This should do something to distinguish between wall-like terrain and complex terrain + // values, but I'm not sure what. Right now they just both keep cranking, although in + // reality we probably want to filter out everything but the complex terrain type, but hey + // that's easy. -- Rob + objtrip = OPNUM(cobjid); + if ((ObjProps[objtrip].flags & TERRAIN_OBJECT) == 0) + return; + if (cobjid == terrain_hit_exclusion) + return; + + _n_fr_cobj = &objs[cobjid]; + obj_type = ObjProps[objtrip].render_type; + + _n_o_rad = fix_make(ObjProps[objtrip].physics_xr, 0) / 96; // for reanchoring + + // I just ripped out all the types for which it is meaningless to generate terrain data + // from/about, that you had already just had them break immediately. -- Rob + switch (obj_type) { + case FAUBJ_FLATPOLY: + case FAUBJ_ANIMPOLY: + case FAUBJ_TEXTPOLY: + + case FAUBJ_BITMAP: + case FAUBJ_NOOBJ: { + fix loc_pts[3], h, r = _n_o_rad >> 1; + localize_object(loc_pts); // fill in local frame + if (ObjProps[objtrip].physics_z) + h = fix_make(ObjProps[objtrip].physics_z, 0) / 96; // for reanchoring + else + h = r << 1; + switch (ID2TRIP(cobjid)) { + case REPULSOR_TRIPLE: + _face_secret_repulsor_hack(); + break; + case ENERGY_MINE_TRIPLE: + // mprintf("Mine at %x %x %x, %x %x %x...%x %x %x, loc %x %x %x\n", + // _n_fr_cobj->loc.x,_n_fr_cobj->loc.y,_n_fr_cobj->loc.z, + // objs[physics_handle_to_id(0)].loc.x,objs[physics_handle_to_id(0)].loc.y,objs[physics_handle_to_id(0)].loc.z, + // tf_raw_pt[0],tf_raw_pt[1],tf_raw_pt[2],loc_pts[0],loc_pts[1],loc_pts[2]); + // gruesome hack due to other gruesome hack.... + // r=0x7800; h=0x8000; + loc_pts[2] += (h >> 1); + r = -r; + default: + // mprintf("at %x %x %x, vs %x %x\n",loc_pts[0],loc_pts[1],loc_pts[2],r,h); + // mprintf("ot Oid %x\n",cobjid); + tfimp = tf_solve_cylinder(loc_pts, r, h); + break; + } + } break; + case FAUBJ_SPECIAL: + // Hey Doug! This is where I figured I'd put the special-case renderer stuff. + // I dunno whether or not you think this is a little too hardwired...this should + // probably be a gamerend call, I think, but for now here's a hook you can use. + // If you have suggestions, etc. for how to do it better, I bet you know how to + // get me.... + switch (ID2TRIP(cobjid)) { + case TRIPBEAM_TRIPLE: + break; + // Hmm, these really want to be dealt with as tpolys I guess for terms of + // facelets... -- Rob + case LABFORCE_TRIPLE: + case RESFORCE_TRIPLE: + case GENFORCE_TRIPLE: + break; + case SML_CRT_TRIPLE: + case LG_CRT_TRIPLE: + case SECURE_CONTR_TRIPLE: + break; + case FORCE_BRIJ_TRIPLE: + case FORCE_BRIJ2_TRIPLE: + case BRIDGE_TRIPLE: + case CATWALK_TRIPLE: + case PILLAR_TRIPLE: + case BARRICADE_TRIPLE: { + Ref r1 = 0, r2 = 0; + grs_bitmap *b1; + fix fx, fy, fz, loc_pts[3], cube_pts[3]; + + // r1 and r2 are cleared so that we don't lock the resources! + // NULL so we don't load a bitmap + b1 = obj_get_model_data(cobjid, &fx, &fy, &fz, NULL, &r1, &r2); + if (b1 != NULL) { + localize_object(loc_pts); // fill in local frame + if (_axial_relativize(loc_pts, cube_pts, FALSE)) { + cube_pts[2] -= fz; + tfimp = _face_parm_cube(cube_pts, fx, fy, fz); + } + } + } break; + } + break; + + // this is all outrageously horrible, as we dont know what we really need to deal with here + + case FAUBJ_TL_POLY: + case FAUBJ_TEXBITMAP: + case FAUBJ_TPOLY: { + fix fix_xoff, fix_yoff, loc_pts[3], door_pts[3]; + + scale = 0; + if (objs[cobjid].obclass == CLASS_DOOR) + if (objs[cobjid].info.current_frame >= DOOR_OPEN_FRAME) // the door is open + return; // which means no facelets for now + fix_xoff = fix_make(0, 0x0200) << 6; + fix_yoff = fix_make(0, 0x0200) << 6; + if (scale > 0) { + fix_xoff <<= scale; + fix_yoff <<= scale; + } else { + fix_xoff >>= -scale; + fix_yoff >>= -scale; + } + localize_object(loc_pts); // fill in local frame + if (_axial_relativize(loc_pts, door_pts, FALSE)) + tfimp = _face_parm_cube(door_pts, fix_xoff, 0, fix_yoff); + break; + } + } + if (tfimp) { + if (tf_ph != -1) + terrain_object_collide(tf_ph, cobjid); + else if (terrain_hit_obj == OBJ_NULL) + terrain_hit_obj = cobjid; + } +} diff --git a/engine/src/GameSrc/fixtrmfd.c b/engine/src/GameSrc/fixtrmfd.c new file mode 100644 index 0000000..a5e437e --- /dev/null +++ b/engine/src/GameSrc/fixtrmfd.c @@ -0,0 +1,176 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/fixtrmfd.c $ + * $Revision: 1.13 $ + * $Author: xemu $ + * $Date: 1994/10/16 15:52:03 $ + * + * + */ + +#include "gamescr.h" +#include "mfdint.h" +#include "mfdext.h" +#include "mfddims.h" +#include "tools.h" +#include "mfdart.h" +#include "gamestrn.h" +#include "cybstrng.h" +#include "objuse.h" +#include "objbit.h" +#include "fullscrn.h" +#include "gr2ss.h" + +// ============================================================ +// THE FIXTURE MFD +// ============================================================ + +/* This is the MFD for buttons that zoom into the MFD. */ + +// ------- +// DEFINES +// ------- + +#define TEXT_LABEL_X 6 +#define TEXT_LABEL_Y 5 +#define TEXT_LABEL_W 62 +#define TEXT_LABEL_H 17 + +#define BUTTON_STATE_X 27 +#define BUTTON_STATE_Y 32 +#define BUTTON_STATE_W 20 +#define BUTTON_STATE_H 15 + +#define TEXT_COLOR 0x4C + +typedef struct _fixture_data { + ObjID last_obj; + uchar last_state; +} fixture_data; + +#define MFD_FIXTURE_DATA(lr) ((fixture_data *)&mfd_fdata[MFD_FIXTURE_FUNC][lr * 3]) +#define FIXTURE_STATE (mfd_fdata[MFD_FIXTURE_FUNC][7]) + + +// --------------- +// EXPOSE FUNCTION +// --------------- + +/* This gets called whenever the MFD needs to redraw or + undraw. + The control value is a bitmask with the following bits: + MFD_EXPOSE: Update the mfd, if MFD_EXPOSE_FULL is not set, + update incrementally. + MFD_EXPOSE_FULL: Fully redraw the mfd, implies MFD_EXPOSE + + if no bits are set, the mfd is being "unexposed;" its display + being pulled off the screen to make room for a different func. +*/ + +void mfd_fixture_expose(MFD *mfd, ubyte control) { + uchar full = control & MFD_EXPOSE_FULL; + if (control == 0) // MFD is drawing stuff + { + panel_ref_unexpose(mfd->id, MFD_FIXTURE_FUNC); + return; + } + if (control & MFD_EXPOSE) // Time to draw stuff + { + fixture_data *fd = MFD_FIXTURE_DATA(mfd->id); + + // set panel_ref so that we get pulled away if we travel too far. + + // clear update rects + mfd_clear_rects(); + // set up canvas + gr_push_canvas(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + // Clear the canvas by drawing the background bitmap + if (!full_game_3d) + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + draw_res_bm(REF_IMG_MFDButtonBack, 0, 0); + if (full || player_struct.panel_ref != fd->last_obj) { + short w, h; + uchar wrap = mfd_string_wrap; + char buf[256]; + if (objs[player_struct.panel_ref].info.make_info != 0) + get_string(REF_STR_Name0 + objs[player_struct.panel_ref].info.make_info, buf, sizeof(buf)); + else + get_object_long_name(ID2TRIP(player_struct.panel_ref), buf, sizeof(buf)); + gr_set_font((grs_font *)ResLock(MFD_FONT)); + gr_string_wrap(buf, TEXT_LABEL_W); + mfd_string_wrap = FALSE; + gr_string_size(buf, &w, &h); + w = (TEXT_LABEL_W - w) / 2; + h = (TEXT_LABEL_H - h) / 2; + mfd_draw_string(buf, TEXT_LABEL_X + w, TEXT_LABEL_Y + h, TEXT_COLOR, TRUE); + ResUnlock(MFD_FONT); + fd->last_obj = player_struct.panel_ref; + mfd_string_wrap = wrap; + } + // this is button code. + FIXTURE_STATE = objs[player_struct.panel_ref].info.current_frame; + if (objs[player_struct.panel_ref].info.inst_flags & CLASS_INST_FLAG2) + FIXTURE_STATE = !FIXTURE_STATE; + if (full || FIXTURE_STATE != fd->last_state) { + // Hey, this is all button code that's going to have to be + // yanked out and moved elsewhere + int ref = REF_IMG_On + ((FIXTURE_STATE == 0) ? 1 : 0); + short xoff = (BUTTON_STATE_W - res_bm_width(ref)) / 2; + short yoff = (BUTTON_STATE_H - res_bm_height(ref)) / 2; + + draw_res_bm(ref, BUTTON_STATE_X + xoff, BUTTON_STATE_Y + yoff); + mfd_add_rect(BUTTON_STATE_X, BUTTON_STATE_Y, BUTTON_STATE_X + BUTTON_STATE_W, + BUTTON_STATE_Y + BUTTON_STATE_H); + fd->last_state = FIXTURE_STATE; + } + + // on a full expose, make sure to draw everything + + if (full) + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + // Pop the canvas + gr_pop_canvas(); + // Now that we've popped the canvas, we can send the + // updated mfd to screen + mfd_update_rects(mfd); + } +} + +// ---------------- +// HANDLER FUNCTION +// ---------------- + +uchar mfd_fixture_handler(MFD *m, uiEvent *e) { + LGRect brect = {{BUTTON_STATE_X, BUTTON_STATE_Y}, + {BUTTON_STATE_X + BUTTON_STATE_W, BUTTON_STATE_Y + BUTTON_STATE_H}}; + LGPoint pos = e->pos; + pos.x -= m->rect.ul.x; + pos.y -= m->rect.ul.y; + if (e->mouse_data.action & MOUSE_LDOWN && RECT_TEST_PT(&brect, pos)) { + object_use(player_struct.panel_ref, TRUE, OBJ_NULL); + mfd_notify_func(MFD_FIXTURE_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, TRUE); + return TRUE; + } + return FALSE; +} diff --git a/engine/src/GameSrc/frcamera.c b/engine/src/GameSrc/frcamera.c new file mode 100644 index 0000000..4b04c19 --- /dev/null +++ b/engine/src/GameSrc/frcamera.c @@ -0,0 +1,231 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * FrCamera.c + * + * $Source: r:/prj/cit/src/RCS/frcamera.c $ + * $Revision: 1.15 $ + * $Author: dc $ + * $Date: 1994/07/15 13:58:34 $ + * + * Citadel Renderer + * camera position/modification/creation system + * + * uchar fr_camera_create (cams *camtype, int *arg1, int *arg2) + * int fr_camera_update (cams *cam, int *arg1, int *arg2) + * void fr_camera_slewone(cams *cam, int which, int how) + * fix *fr_camera_getpos (cams *cam) + * void fr_camera_setdef (cams *cam) + * void fr_camera_slewcam(cams *cam, int which, int how) + * + * $Log: frcamera.c $ + * Revision 1.15 1994/07/15 13:58:34 dc + * check for cameras off the map at getpos time + * + * Revision 1.14 1994/06/28 20:07:16 dc + * allow null cameras without default cameras without crashing the game + * + * Revision 1.13 1994/05/03 15:58:57 dc + * flatten other 360 side views.... + * + * Revision 1.12 1994/04/10 05:15:26 dc + * support for cyberman, vfx1, other 6d control structure, inc. HEAD_H + * + * Revision 1.11 1994/04/02 03:43:07 dc + * clean up errors, so on + * + * Revision 1.10 1994/01/31 05:31:37 dc + * various hacks for reality, new actual use of camera system + * + * Revision 1.9 1994/01/06 10:35:40 xemu + * self/run + * + * Revision 1.8 1994/01/02 17:11:25 dc + * New renderer + * + * Revision 1.7 1993/12/08 22:01:36 unknown + * yea yea yea + * + * Revision 1.6 1993/12/08 21:38:08 unknown + * player model + * + * Revision 1.5 1993/09/19 19:09:49 xemu + * made _def_cam externally accessible + * + * Revision 1.4 1993/09/17 16:57:47 mahk + * Added 360 support + * + * Revision 1.3 1993/09/16 23:54:54 dc + * Yo, use cameras for real, allow type mods on the fly + * + * Revision 1.2 1993/09/05 20:54:06 dc + * new regieme for real + * + * Revision 1.1 1993/09/05 20:21:57 dc + * Initial revision + * + */ + +#include +#include // for abs, of course + +#include "frcamera.h" +#include "froslew.h" // has objects +#include "map.h" +#include "physics.h" + +fix fr_camera_last[CAM_COOR_CNT] = {0, 0, 0, 0, 0, 0}; +fix cam_slew_scale[CAM_COOR_CNT] = {fix_make(4, 0), fix_make(4, 0), fix_make(4, 0), 128, 128, 128}; +cams *_def_cam = NULL; + +#define _cam_top(cam) \ + cams *_cam = (cam == NULL) ? _def_cam : cam; \ + if (_cam == NULL) \ + return + +void fr_camera_setdef(cams *cam) { _def_cam = cam; } + +cams *fr_camera_getdef(void) { return _def_cam; } + +uchar fr_camera_create(cams *cam, int camtype, ushort oid, fix *coor, fix *args) { + DEBUG("Creating camera"); + _cam_top(cam) FALSE; + _cam->type = camtype; + + if (camtype & CAMBIT_OBJ) + _cam->obj_id = oid; + else + LG_memcpy(_cam->coor, coor, sizeof(fix) * CAM_COOR_CNT); + + if (args != NULL) + LG_memcpy(_cam->args, args, sizeof(fix) * CAM_ARGS_CNT); + + return TRUE; +} + +uchar fr_camera_modtype(cams *cam, uchar type_on, uchar type_off) { + uchar ret; + _cam_top(cam) 0; + ret = _cam->type; + _cam->type &= ~type_off; + _cam->type |= type_on; + return ret; +} + +// i'll give you fish, i'll give you candy, i'll give you, everything I have in my hand +int fr_camera_update(cams *cam, uintptr_t arg1, int whicharg, uintptr_t arg2) { + _cam_top(cam) FALSE; + if (arg1 != 0) { + if (_cam->type & CAMBIT_OBJ) + _cam->obj_id = (unsigned int)arg1; + else + LG_memcpy(_cam->coor, (void*)arg1, sizeof(fix) * CAM_COOR_CNT); + } + + if (whicharg == CAM_UPDATE_ALL) + LG_memcpy(_cam->args, (void*)arg2, sizeof(fix) * CAM_ARGS_CNT); + else if (whicharg < CAM_ARGS_CNT) + _cam->args[whicharg] = (fix)arg2; + return TRUE; +} + +void fr_camera_setone(cams *cam, int which, int newone) { + _cam_top(cam); + if (_cam->type & CAMBIT_OBJ) + fr_objslew_setone(which, newone); + else + _cam->coor[which] = newone; +} + +void fr_camera_slewone(cams *cam, int which, int how) { + uchar cv[3] = {0, 2, 1}; + _cam_top(cam); + if (which >= 3) /* angles */ + { + if (which == EYE_RESET) + _cam->coor[4] = _cam->coor[5] = 0; + else + _cam->coor[which] += how * cam_slew_scale[which]; + } else /* actual move */ + { + g3s_vector v[3]; + fix tot, _cammul; + + // this just doesnt work, really + + tot = how * cam_slew_scale[which]; + g3_get_slew_step(tot, v + 0, v + 1, v + 2); + // if (which==2) how*=-1; + if (_cam->type & CAMFLT_FLAT) { + _cammul = fix_sqrt(fix_mul(tot, tot) - (fix_mul(v[cv[which]].gY, v[cv[which]].gY))); + _cammul = fix_div(abs(tot), _cammul); + // Warning(("mul %x from %x and %x\n",_cammul,tot,v[cv[which]])); + } else { + _cam->coor[2] -= fix_int((v[cv[which]].gY)) << 8; + _cammul = fix_make(1, 0); + } + _cam->coor[0] += fix_mul(_cammul, ((v[cv[which]].gX) >> 8)); + _cam->coor[1] += fix_mul(_cammul, ((v[cv[which]].gZ) >> 8)); + } +} + +// also in init.c +#define MAGIC_SELFRUN_OBJID 0xC3 + +void fr_camera_getobjloc(int oid, fix *store) { + Obj *cobj = &objs[oid]; + + if (cobj->info.ph != -1) { + get_phys_info(cobj->info.ph, store, 6); + } else { + store[0] = cobj->loc.x << 8; + store[1] = cobj->loc.y << 8; + store[2] = cobj->loc.z << (8 + SLOPE_SHIFT_D); + store[3] = (cobj->loc.h << 8); + store[4] = (cobj->loc.p << 8); + store[5] = (cobj->loc.b << 8); + } +} + +fix *fr_camera_getpos(cams *cam) { + _cam_top(cam) NULL; + if (_cam->type & CAMBIT_OBJ) /* set fix x,y,z etc from the object positions */ + fr_camera_getobjloc(_cam->obj_id, _cam->coor); + + LG_memcpy(fr_camera_last, _cam->coor, sizeof(fix) * CAM_COOR_CNT); + if (_cam->type & CAMBIT_MOD) + fr_camera_last[3] = (fr_camera_last[3] + eye_mods[0]) & 0xffff; + if ((_cam->type & (CAMBIT_OFF | CAMBIT_ANG)) != 0) { + fr_camera_last[3] += (0x10000) - ((1 << (14 - CAMANG_S)) * (_cam->type & CAMBIT_ANG)); + } // for now 360 view will stay body flat.... + else if (_cam->type & CAMBIT_MOD) { + // fr_camera_last[3]=(fr_camera_last[3]+eye_mods[0])&0xffff; + fr_camera_last[4] = (fr_camera_last[4] + eye_mods[1]) & 0xffff; + fr_camera_last[5] = (fr_camera_last[5] + eye_mods[2]) & 0xffff; + } + return &fr_camera_last[0]; +} + +void fr_camera_slewcam(cams *cam, int which, int how) { + _cam_top(cam); + if (_cam->type & CAMBIT_OBJ) + fr_objslew_moveone(NULL, _cam->obj_id, which, how, TRUE); + else + fr_camera_slewone(_cam, which, how); +} diff --git a/engine/src/GameSrc/frclip.c b/engine/src/GameSrc/frclip.c new file mode 100644 index 0000000..991ee77 --- /dev/null +++ b/engine/src/GameSrc/frclip.c @@ -0,0 +1,1174 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * FrClip.c + * + * $Source: r:/prj/cit/src/RCS/frclip.c $ + * $Revision: 1.12 $ + * $Author: dc $ + * $Date: 1994/09/10 00:30:09 $ + * + * Citadel Renderer + * various clippers for terrain, including basic cone clip and the later day + * tile based clipper + * + * $Log: frclip.c $ + * Revision 1.12 1994/09/10 00:30:09 dc + * this time i really think i fixed the malloc's + * + * Revision 1.11 1994/09/06 03:14:58 dc + * do cspace a little more correctly, eh? + * + * Revision 1.10 1994/09/05 08:33:35 dc + * hey how about not forgetting about cyberspace this time, eh? + * + * Revision 1.9 1994/09/05 06:43:26 dc + * span parse fixes, clear as we go + * + * Revision 1.8 1994/08/30 05:51:01 dc + * fix vanish if home square not drawn bug, othe rstuff + * + * Revision 1.7 1994/08/21 03:10:06 dc + * duh. + * + * Revision 1.6 1994/08/21 03:06:55 dc + * parameterize spawn_check, save a bit of code space + * + * Revision 1.5 1994/07/28 05:53:42 dc + * protection from span clip nightmare, what is cone doing... + * + * Revision 1.4 1994/03/13 17:18:04 dc + * doors take 38, still doesnt do minimal inclusion clips right + * + * Revision 1.3 1994/03/10 00:11:03 dc + * better door stuff, still need some stuff, though.... + * + * Revision 1.2 1994/01/22 18:57:39 dc + * fix objclip to set correct bits of subclip, next switch it to flick_qclip + */ + +#define __FRCLIP_SRC + +#include +#include + +#include "frintern.h" +#include "frspans.h" +#include "frtables.h" +#include "fr3d.h" +#include "frquad.h" +#include "frflags.h" +#include "frparams.h" +#include "frsubclp.h" +#include "frshipm.h" + +#include "cone.h" + +#include "map.h" +#include "tilename.h" + +// pre prototyping +static void _fr_rebuild_nVecWork(void); +static void _fr_init_vecwork(void); +static void clear_clip_bits(void); + +// for the subtile clipper +ushort sc_reg[NUM_SUBCLIP][SC_VEC_COUNT]; +ushort *cur_sc_ptr; +uint cur_sc_reg; + +// Prototypes +void _fr_sclip_line(MapElem *sp_base, int len, int val); +void _fr_sclip_line_check_solid(MapElem *sp_base, int len, int val); +void fr_span_parse(void); +void span_fixup(void); +int fr_clip_show_all(void); +// uchar _fr_move_ccv_x(struct _nVecWork *nvp); +void _fr_move_along_dcode(int dircode); + +#ifndef MAP_RESIZING +static uchar real_x_spans[(1 << DEFAULT_YSHF) * SPAN_MEM]; +static uchar real_cone_spans[(1 << DEFAULT_YSHF) * 2]; +#endif + +int fr_clip_freemem(void) { + if (x_span_lists != NULL) + free(x_span_lists); + _fr_ret; +} + +int fr_clip_resize(int x, int y) // x, y +{ + int i; +#ifdef MAP_RESIZING + if (x_span_lists != NULL) + free(x_span_lists); + if (cone_span_list != NULL) + free(cone_span_list); + x_span_lists = (uchar *)malloc(y * SPAN_MEM * sizeof(uchar)); + cone_span_list = (uchar *)malloc(y * 2 * sizeof(uchar)); +#else + x_span_lists = &real_x_spans[0]; + cone_span_list = &real_cone_spans[0]; +#endif + _fr_rebuild_nVecWork(); + _fr_init_vecwork(); + _fr_dbg(if (x_span_lists == NULL) _fr_ret_val(FR_NOMEM)); + _fr_dbg(if (cone_span_list == NULL) _fr_ret_val(FR_NOMEM)); + for (i = 0; i < fr_map_y; i++) + span_count(i) = 0; + _fr_ret; +} + +int fr_clip_frame_start(void) { + // setup real span lists + // setup real obj stack + // _fr_init_vecwork(); + // hmm... is this really necessary???? + LG_memset(cone_span_list, 0xff, fr_map_y * 2 * sizeof(uchar)); + _fr_sdbg(SANITY, _fr_init_vecwork()); // hey, why not? + _fr_ret; +} + +int fr_clip_frame_end(void) { +#ifndef CLEAR_AS_WE_GO + clear_clip_bits(); +#endif + _fr_ret; +} + +// cradle every word the falls +// into the arms of failure +void store_x_span(int y, int lx, int rx) { + int c_span; + + c_span = span_count(y)++; + if (c_span >= MAX_SPANS) { + // Warning(("HEY too many spans!!! y %d from %d to %d\n",y,lx,rx)); // should probably solve the problem + span_count(y)--; + c_span--; + span_right(y, c_span) = rx; // just add a new right edge + } else { + span_left(y, c_span) = lx; + span_right(y, c_span) = rx; + } + _fr_sdbg(VECSPEW, mprintf("Put %d->%d at span %d of %d\n", lx, rx, c_span, y)); + +#ifdef _FR_TILEMAP + if (fr_highlights) { + LGPoint p; + for (p.x = lx, p.y = y; p.x <= rx; p.x++) + TileMapSetHighlight(NULL, p, 0, TRUE); + } +#endif // _FR_TILEMAP +} + +void _fr_sclip_line(MapElem *sp_base, int len, int val) { + _fr_sdbg(SPAN_PARSE, mprintf("yep, at %x, len %x, val %x\n", sp_base, len, val)); + for (; len > 0; len--, sp_base++) + me_subclip(sp_base) = val; +} + +void _fr_sclip_line_check_solid(MapElem *sp_base, int len, int val) { + _fr_sdbg(SPAN_PARSE, mprintf("yep, at %x, len %x, val %x\n", sp_base, len, val)); + for (; len > 0; len--, sp_base++) + if (me_tiletype(sp_base) == TILE_SOLID) + me_subclip(sp_base) = SUBCLIP_OUT_OF_CONE; + else + me_subclip(sp_base) = val; +} + +// compute bounding box as well +extern MapElem *fr_map_base; +ushort frpipe_dist; +void fr_span_parse(void) { + int y, dist; + MapElem *cur_span = fr_map_base; + uchar *cur_span_cnt = &(span_count(0)), *cur_cone_span = &cone_span_list[0]; + + frpipe_dist = 0; + for (y = 0; y < fr_map_y; y++, cur_span += fr_map_x, cur_span_cnt += (1 << SPAN_SHIFT), cur_cone_span += 2) + if (*cur_span_cnt) { + if ((*cur_span_cnt) == 1) { + _fr_sdbg(SPAN_PARSE, mprintf("sc 1 at %x, going from %x to %x and %x to %x\n", y, *cur_cone_span, + span_left(y, 0), span_right(y, 0) + 1, *(cur_cone_span + 1))); + _fr_sclip_line(cur_span + (*cur_cone_span), span_left(y, 0) - *cur_cone_span, SUBCLIP_OUT_OF_CONE); + _fr_sclip_line(cur_span + span_right(y, 0) + 1, (*(cur_cone_span + 1)) - (span_right(y, 0) + 1) + 1, + SUBCLIP_OUT_OF_CONE); + } else { // first merge wacky scenes.... + int cur_l, cur_r, span_id = 0, off_l; + _fr_sdbg(SPAN_PARSE, for (cur_l = 0; cur_l < *cur_span_cnt; cur_l++) + mprintf("at %x MultiSpan %d from %x to %x\n", y, cur_l, span_left(y, cur_l), + span_right(y, cur_l))); + cur_l = span_left(y, 0); + cur_r = span_right(y, 0); + off_l = *cur_cone_span; + while (++span_id < (*cur_span_cnt)) { + if (span_left(y, span_id) <= + cur_r) { // merge the spans into 1, but sclip clean any overlap pass first + _fr_sdbg(SPAN_PARSE, mprintf("at %x Cleanup from %x to %x\n", y, span_left(y, span_id), cur_r)); + _fr_sclip_line(cur_span + span_left(y, span_id), cur_r - span_left(y, span_id) + 1, + SUBCLIP_FULL_TILE); // set them to no subclip + if (cur_r < span_right(y, span_id)) { + _fr_sdbg(SPAN_PARSE, + mprintf("span merge right to %x from %x\n", span_right(y, span_id), cur_r)); + cur_r = span_right(y, span_id); + } + } else { + _fr_sdbg(SPAN_PARSE, mprintf("sent old span %x from %x to %x\n", y, off_l, cur_l)); + _fr_sclip_line(cur_span + off_l, cur_l - off_l, SUBCLIP_OUT_OF_CONE); + off_l = cur_r + 1; + cur_l = span_left(y, span_id); + cur_r = span_right(y, span_id); + } + } + _fr_sdbg(SPAN_PARSE, mprintf("loop done at %x, now sending %x -> %x and %x -> %x\n", y, off_l, cur_l, + cur_r + 1, *(cur_cone_span + 1))); + _fr_sclip_line(cur_span + off_l, cur_l - off_l, SUBCLIP_OUT_OF_CONE); + _fr_sclip_line(cur_span + cur_r + 1, (*(cur_cone_span + 1)) - (cur_r + 1) + 1, SUBCLIP_OUT_OF_CONE); + } + dist = abs(y - _fr_y_cen); + if ((dist + abs(span_left(y, 0) - _fr_x_cen)) > frpipe_dist) + frpipe_dist = dist + abs(span_left(y, 0) - _fr_x_cen); + if ((dist + abs(span_right(y, (*cur_span_cnt) - 1) - _fr_x_cen)) > frpipe_dist) + frpipe_dist = dist + abs(span_right(y, (*cur_span_cnt) - 1) - _fr_x_cen); +#ifdef CLEAR_AS_WE_GO + *cur_span_cnt = 0; +#endif + } else if (*cur_cone_span != 0xff) { + _fr_sdbg(SPAN_PARSE, mprintf("Cleaning up %x from %x to %x\n", y, *cur_cone_span, *(cur_cone_span + 1))); + _fr_sclip_line(cur_span + (*cur_cone_span), (*(cur_cone_span + 1)) - (*(cur_cone_span)) + 1, + SUBCLIP_OUT_OF_CONE); + } // note the ()'s are right, it is (right edge) - (left edge) + 1 +} + +void span_fixup(void) { + _fr_sdbg(VECSPEW, + mprintf("Center was %d spans, %d -> %d and %d -> %d\n", span_count(_fr_y_cen), span_left(_fr_y_cen, 0), + span_right(_fr_y_cen, 0), span_left(_fr_y_cen, 1), span_right(_fr_y_cen, 1))); + if (span_count(_fr_y_cen) > 1) { + span_count(_fr_y_cen)--; + span_left(_fr_y_cen, 0) = lg_min(span_left(_fr_y_cen, 0), span_left(_fr_y_cen, 1)); + span_right(_fr_y_cen, 0) = lg_max(span_right(_fr_y_cen, 0), span_right(_fr_y_cen, 1)); + } else + WARN("%s: Only one span at y center", __FUNCTION__); + fr_span_parse(); +#if _fr_defdbg(VECSPEW) + if (_fr_dbgflg_chk(VECSPEW)) { + int i, j, lc; + mprintf("At %d %d\n", _fr_x_cen, _fr_y_cen); + for (i = 0, lc = -1; i < fr_map_y; i++) { + if (span_count(i) == 0) { + if (lc == -1) + lc = i; + } else { + if (lc != -1) { + mprintf("y %d-%d have none\n", lc, i); + lc = -1; + } + mprintf("y%d c%d: ", i, span_count(i)); + for (j = 0; j < span_count(i); j++) + mprintf(" %d->%d", span_left(i, j), span_right(i, j)); + mprintf("\n"); + } + } + if (lc != -1) + mprintf("y %d-%d have none\n", lc, i - 1); + } +#endif +} + +#define STAY_ON_MAP +void cone_span_set(int y, int l, int r) { + cone_span_list[y + y] = l; + cone_span_list[y + y + 1] = r; +} + +#ifndef CLEAR_AS_WE_GO +static void clear_clip_bits(void) { + int i, j; + MapElem *mbptr = MAP_MAP, *mptr; + for (i = 0; i < fr_map_y; i++, mbptr += fr_map_x) + if (cone_span_left(i) != 0xff) + for (j = cone_span_left(i), mptr = mbptr + j; j <= cone_span_right(i); j++, mptr++) + _me_subclip(mptr) = SUBCLIP_OUT_OF_CONE; +} +#endif + +static void set_clip_bits(void) { + int i; + MapElem *mbptr = MAP_MAP, *mptr, *rptr; + for (i = 0; i < fr_map_y; i++, mbptr += fr_map_x) + if (cone_span_left(i) != 0xff) + for (mptr = mbptr + cone_span_left(i), rptr = mbptr + cone_span_right(i); mptr <= rptr; mptr++) + _me_subclip(mptr) = SUBCLIP_FULL_TILE; +} + +#if _fr_defdbg(NO_CONE) +void set_full_cone(void) {} +#endif + +// satan got her tongue +// now, it's undone +int fr_clip_cone(void) { + simple_cone_clip_pass(); + // _fr_ndbg(NO_CONE,simple_cone_clip_pass()); + // _fr_sdbg(NO_CONE,set_full_cone()); + set_clip_bits(); + +#if _fr_defdbg(VECSPEW) + if (_fr_dbgflg_chk(VECSPEW)) { + int i, lc; + mprintf("Cone from %d %d\n", _fr_x_cen, _fr_y_cen); + for (i = 0, lc = -1; i < fr_map_y; i++) { + if (cone_span_left(i) == 0xff) { + if (lc == -1) + lc = i; + } else { + if (lc != -1) { + mprintf("y %d-%d have none\n", lc, i); + lc = -1; + } + mprintf("y%d: %d->%d\n", i, cone_span_left(i), cone_span_right(i)); + } + } + if (lc != -1) + mprintf("y %d-%d have none\n", lc, i - 1); + mprintf("left (%x,%x), right (%x,%x)\n", span_intersect[0], span_intersect[1], span_intersect[2], + span_intersect[3]); + mprintf("vec tl (%x,%x), vec tr (%x,%x)\n", span_lines[2], span_lines[3], span_lines[4], span_lines[5]); + mprintf("vec bl (%x,%x), vec br (%x,%x)\n", span_lines[0], span_lines[1], span_lines[6], span_lines[7]); + mprintf("Note eye @ %x %x, ang %x %x %x\n", coor(EYE_X), coor(EYE_Y), coor(EYE_H), coor(EYE_P), coor(EYE_B)); + } +#endif + _fr_ret; +} + +// these two are a little unstoked at the moment +int fr_clip_show_all(void) { + MapElem *cur_span = fr_map_base; +#ifndef REALLY_ALL + uchar *cur_cone_span = &cone_span_left(0); + int i, dist; + for (i = 0; i < fr_map_y; i++, cur_span += fr_map_x, cur_cone_span += 2) + if ((*cur_cone_span) != 0xff) { +#ifndef CLEAR_AS_WE_GO + store_x_span(i, *cur_cone_span, *(cur_cone_span + 1)); +#endif + _fr_sclip_line_check_solid(cur_span + (*cur_cone_span), *(cur_cone_span + 1) - *(cur_cone_span) + 1, + SUBCLIP_FULL_TILE); + dist = abs(i - _fr_y_cen); + if ((dist + abs(*cur_cone_span - _fr_x_cen)) > frpipe_dist) + frpipe_dist = dist + abs(*cur_cone_span - _fr_x_cen); + if ((dist + abs(*(cur_cone_span + 1) - _fr_x_cen)) > frpipe_dist) + frpipe_dist = dist + abs(*(cur_cone_span - 1) - _fr_x_cen); + } +#else + int y; + for (y = 0; y < fr_map_y; y++) { +#ifndef CLEAR_AS_WE_GO + store_x_span(y, 0, fr_map_x - 1); +#endif + _fr_sclip_line_check_solid(cur_span + (*cur_cone_span), *(cur_cone_span + 1) - *(cur_cone_span) + 1, + SUBCLIP_FULL_TILE); + } + if (_fr_x_cen < (fr_map_x >> 1)) + dist = fr_map_x - _fr_x_cen; + else + dist = _fr_x_cen - fr_map_x; + if (_fr_y_cen < (fr_map_y >> 1)) + dist += fr_map_y - _fr_y_cen; + else + dist = _fr_y_cen - fr_map_y; +#endif + _fr_ret; +} + +// when i hear the word security, i reach for my shotgun +typedef struct { + fix loc[3]; // current location of vector + fix deltas[3]; // current steps for vector + MapElem *mptr; // current map pointer + uchar flags; // inuse, LR, pointer to self + uchar nxtv; // for now, points to next + fix oldx; // x at last y crossing + fix len; // current vector length +} FrClipVec; + +struct _nVecWork { + fix remx; // how far to go in x + short mapstep[2]; // how to move in map when going by x, y + uchar inface[2]; // inface for x and y + fix stepy; // how far to go in y for a remx + uchar move_x; // do we move in x? + uchar dircode; // so we can get back out +}; + +#define nVW_XDIR 2 +#define nVW_YDIR 1 + +#define nVW_NXNY 0 +#define nVW_NXPY 1 +#define nVW_PXNY 2 +#define nVW_PXPY 3 + +#define _fixp1 (fix_make(1, 0)) +#define _fixn1 (-fix_make(1, 0)) +static fix locstep[4][2] = {{_fixn1, _fixn1}, {_fixn1, _fixp1}, {_fixp1, _fixn1}, {_fixp1, _fixp1}}; +static fix edgestep[4][2] = {{0, 0}, {0, _fixp1 - 1}, {_fixp1 - 1, 0}, {_fixp1 - 1, _fixp1 - 1}}; +static char rmmod[4] = {-1, -1, 1, 1}; /* mod to rem to get to next full square */ +static short new_del_mapstep[2] = {-32, 32}; /* wants to be wall_adds[2] and then wall_adds[0] */ +static MapElem *eye_mptr; + +#define FRVECSELF 0x3F +#define FRVECMASK 0x3F +#define FRVECUSE 0x80 +#define FRVECL 0x40 +#define FRVECR 0x00 +#define FRVECDIR 0x40 + +#define MS_X 0 +#define MS_Y 1 + +#define MAX_CLIP_VEC 16 +static FrClipVec allclipv[MAX_CLIP_VEC]; +static FrClipVec *ccv; +static char vechead = 0, ffreevec = 0, lastvec = -1, endvec = MAX_CLIP_VEC - 1; + +struct _nVecWork _nVP[4] = {{0xdeadbeef, -1, -64, 3, 2, 0xdeadbeef, 0xff, nVW_NXNY}, + {0xdeadbeef, -1, 64, 3, 0, 0xdeadbeef, 0xff, nVW_NXPY}, + {0xdeadbeef, 1, -64, 1, 2, 0xdeadbeef, 0xff, nVW_PXNY}, + {0xdeadbeef, 1, 64, 1, 0, 0xdeadbeef, 0xff, nVW_PXPY}}; + +// this is a gross way to do this... +#define _fr_build_clip_vec(cv, org, ray, aflags) \ + (cv)->len = 0; \ + (cv)->flags = ((cv)->flags & FRVECMASK) + aflags; \ + (cv)->mptr = MAP_GET_XY(fix_int(org[0]), fix_int(org[1])); \ + (cv)->loc[0] = org[0]; \ + (cv)->loc[1] = org[1]; \ + (cv)->loc[2] = org[2]; \ + cv->oldx = org[0]; \ + (cv)->deltas[0] = ray[0]; \ + (cv)->deltas[1] = ray[1]; \ + (cv)->deltas[2] = ray[2] + +// More prototypes +uchar _fr_skip_solid_right_n_back(FrClipVec *cv, fix max_loc, int y_map_step); +uchar _fr_skip_space_right_n_back(FrClipVec *cv, fix max_loc, int y_map_step); +uchar _fr_skip_solid_left_n_back(FrClipVec *cv, fix min_loc, int y_map_step); +void _fr_del_compute(FrClipVec *v1, FrClipVec *v2); +void _fr_spawn_check_one(FrClipVec *lv, FrClipVec *rv, uchar northward); +uchar _fr_move_new_dels(FrClipVec *lv, FrClipVec *rv, uchar northward); +void _fr_kill_pair(FrClipVec *lv, FrClipVec *rv); +uchar _fr_setup_first_pair(uchar headnorth); + +// these dont really work, the *org, *ray doesnt move all 3 +// (*((cv)->loc))=*org; (*((cv)->deltas))=*ray; +// this gets a missing lvalue stupidity +// (cv)->loc=org; (cv)->deltas=ray; + +static void _fr_rebuild_nVecWork(void) { + _nVP[nVW_NXNY].mapstep[MS_X] = _nVP[nVW_NXPY].mapstep[MS_X] = wall_adds[3]; + _nVP[nVW_PXNY].mapstep[MS_X] = _nVP[nVW_PXPY].mapstep[MS_X] = wall_adds[1]; + _nVP[nVW_NXNY].mapstep[MS_Y] = _nVP[nVW_PXNY].mapstep[MS_Y] = wall_adds[2]; + _nVP[nVW_NXPY].mapstep[MS_Y] = _nVP[nVW_PXPY].mapstep[MS_Y] = wall_adds[0]; + new_del_mapstep[0] = wall_adds[2]; + new_del_mapstep[1] = wall_adds[0]; +} + +static void _fr_init_vecwork(void) { + int i; + for (i = 0; i < MAX_CLIP_VEC; i++) // point at self and next + { + allclipv[i].flags = i; + allclipv[i].nxtv = (i + 1) & (MAX_CLIP_VEC - 1); + } + vechead = 0; + ffreevec = 0; + lastvec = -1; + endvec = MAX_CLIP_VEC - 1; +} + +#if _fr_defdbg(VECSPEW) +void print_nvp(struct _nVecWork *nvp) { + static char *nvm_str[4] = {"nxny", "nxpy", "pxny", "pxpy"}; + static char ifc_str[4] = {'n', 'e', 's', 'w'}; + mprintf(" _nVP xrem %x stepy %x. move_x %d std %s %c%c %d %d\n", nvp->remx, nvp->stepy, nvp->move_x, + nvm_str[nvp->dircode], ifc_str[nvp->inface[0]], ifc_str[nvp->inface[1]], nvp->mapstep[0], nvp->mapstep[1]); +} + +void print_fcv(FrClipVec *_ccv, int dim) { + if (dim == 3) + mprintf(" _ccv @(%x,%x,%x),d(%x,%x,%x)..", _ccv->loc[0], _ccv->loc[1], _ccv->loc[2], _ccv->deltas[0], + _ccv->deltas[1], _ccv->deltas[2]); + else + mprintf(" _ccv @(%x,%x),d(%x,%x)..", _ccv->loc[0], _ccv->loc[1], _ccv->deltas[0], _ccv->deltas[1]); + mprintf("ox%x mp%x fl%xnx%x\n", _ccv->oldx, _ccv->mptr, _ccv->flags, _ccv->nxtv); +} +#else +#define print_nvp(a) +#define print_fcv(a, b) +#endif + +static uchar *_face_curedge, *_face_nxtedge; +static uchar _face_curmask, _face_nxtmask; +static uchar _face_topmask, _face_botmask; + +//#define out_of_cone (me_bits_seen_p(mp)==0) +#define out_of_cone(mp) (me_subclip(mp) == SUBCLIP_OUT_OF_CONE) + +// indexed by (vecside)+(dircode<<2)+xdir/ydir, with do nothings for second at Y +static uchar _sclip_major_mask[9][2] = {{FMK_SW, FMK_EW}, {FMK_SW, FMK_WW}, {FMK_NW, FMK_EW}, + {FMK_NW, FMK_WW}, {FMK_NW, FMK_WW}, {FMK_NW, FMK_EW}, + {FMK_SW, FMK_WW}, {FMK_SW, FMK_EW}, {0, 0}}; + +static uchar _sclip_door_mask[9][2] = {{FMK_INT_NW, FMK_INT_EW}, {FMK_INT_NW, FMK_INT_WW}, {FMK_INT_SW, FMK_INT_EW}, + {FMK_INT_SW, FMK_INT_WW}, {FMK_INT_SW, FMK_INT_WW}, {FMK_INT_SW, FMK_INT_EW}, + {FMK_INT_NW, FMK_INT_WW}, {FMK_INT_NW, FMK_INT_EW}, {0, 0}}; + +static uchar *_sclip_mask, *_sclip_door; + +// assumes ccv is us +// moves until next y span is hit, i guess +// perhaps will have to learn about it's partner vector? +// strip the gloss from a beauty queen +uchar _fr_move_ccv_x(struct _nVecWork *nvp) { + int tt, oflow; + uchar move_in_y = TRUE, move_in_x = TRUE; + + // should be saving off texture cuts some day!! + ccv->loc[1] += nvp->stepy; + tt = me_tiletype(ccv->mptr); + if (fr_obj_block(ccv->mptr, _sclip_door, (int *)ccv->loc) || + ((_face_curedge[tt << 2] == 0xff) || + (me_clearsolid(ccv->mptr) & + _face_curmask))) { // these really have to get wacky and learn about partial obscuration + move_in_x = move_in_y = FALSE; + _fr_sdbg(VECSPEW, mprintf("move_x(top): hit our tile\n")); + } else { + _fr_sdbg(VECSPEW, mprintf("move_x(top): sub_clip or %x, old %x\n", _sclip_mask[0], me_subclip(ccv->mptr))); + _me_subclip(ccv->mptr) |= _sclip_mask[0]; + ccv->mptr += nvp->mapstep[0]; + tt = me_tiletype(ccv->mptr); + if ((_face_nxtedge[tt << 2] == 0xff) || (me_clearsolid(ccv->mptr) & _face_nxtmask) || + out_of_cone(ccv->mptr)) { // these really have to get wacky and learn about partial obscuration + move_in_x = move_in_y = FALSE; + ccv->mptr -= nvp->mapstep[0]; + _fr_sdbg(VECSPEW, mprintf("move_x top: hit other tile\n")); + } else { // correct for new setup + ccv->loc[0] += nvp->remx + rmmod[nvp->dircode]; + nvp->remx = locstep[nvp->dircode][0]; + nvp->stepy = fix_mul_div(ccv->deltas[1], nvp->remx, ccv->deltas[0]); + _fr_sdbg(VECSPEW, mprintf("move_x top: move and recompute step\n")); + } + } + _fr_sdbg(VECSPEW, { + mprintf("move_x top:\n"); + print_fcv(ccv, 2); + print_nvp(nvp); + }); + + oflow = ccv->loc[1] & 0xffff; + while (move_in_x) { + oflow += nvp->stepy; + if (oflow & 0xffff0000) { // perhaps should get more elegant, but i mean really, how could it? + move_in_x = FALSE; + move_in_y = TRUE; + _fr_sdbg(VECSPEW, mprintf("move_x(while): reached y\n")); + } else { + ccv->loc[1] += nvp->stepy; + // can we leave the square there? + tt = me_tiletype(ccv->mptr); + // no matter what, we can get out of ourselves? + if (fr_obj_block(ccv->mptr, _sclip_door, (int *)ccv->loc) || + ((_face_curedge[tt << 2] == 0xff) || + (me_clearsolid(ccv->mptr) & + _face_curmask))) { // these really have to get wacky and learn about partial obscuration + move_in_x = move_in_y = FALSE; + _fr_sdbg(VECSPEW, mprintf("move_x(while): hit our tile\n")); + } else { + _fr_sdbg(VECSPEW, + mprintf("move_x(while): sub_clip or %x, old %x\n", _sclip_mask[0], me_subclip(ccv->mptr))); + _me_subclip(ccv->mptr) |= _sclip_mask[0]; + ccv->mptr += nvp->mapstep[0]; + tt = me_tiletype(ccv->mptr); + if ((_face_nxtedge[tt << 2] == 0xff) || (me_clearsolid(ccv->mptr) & _face_nxtmask) || + out_of_cone(ccv->mptr)) { // these really have to get wacky and learn about partial obscuration + move_in_x = move_in_y = FALSE; + ccv->mptr -= nvp->mapstep[0]; + _fr_sdbg(VECSPEW, mprintf("move_x(while): hit other tile\n")); + } else { + ccv->loc[0] += locstep[nvp->dircode][0]; + _fr_sdbg(VECSPEW, mprintf("move_x: moving ccv\n")); + } + } + } + _fr_sdbg(VECSPEW, { + mprintf("move_x(while):\n"); + print_fcv(ccv, 2); + print_nvp(nvp); + }); + } + + // should go back to this, it should add to oflow, non add to loc[1] in loop, then do loc[1]|=(oflow-stepy) + // if (move_in_y) ccv->loc[1]-=nvp->stepy; // undo the step that went too far(tm) + + return move_in_y; + // compute intersection + // if not clear internally, migrate to clear point, recompute + // if now clear internally, move mptr and vec to next square + // if not, backup pointers, return FALSE + // check crossin clear, if clear return TRUE + // if not clear, migrate to next clear point, recompute + // if now clear, return TRUE + // if not, backup pointers if appropriate, return FALSE +} + +void _fr_move_along_dcode(int dircode) { + uchar move_in_y = TRUE; + struct _nVecWork *nvp = &_nVP[dircode]; + + if (dircode & nVW_XDIR) + nvp->remx = _fixp1 - 1 - fix_frac(ccv->loc[0]); + else + nvp->remx = -fix_frac(ccv->loc[0]); + + if (ccv->deltas[1] == 0) // no y delta + { + nvp->move_x = TRUE; + nvp->stepy = 0; + } // lets do the flat line thing + else if (ccv->deltas[0] == 0) // just do a single y step + nvp->move_x = FALSE; // no need to set step or anything + else // replace with wacky table neg? + { // get the signs right, as it were + if (nvp->remx) + switch (dircode) { // these just arent right + case nVW_NXNY: + nvp->move_x = (-nvp->remx * ccv->deltas[1] > (fix_frac(ccv->loc[1])) * (ccv->deltas[0])); + break; // flip sign for -- + case nVW_NXPY: + nvp->move_x = (nvp->remx * ccv->deltas[1] > (_fixp1 - 1 - fix_frac(ccv->loc[1])) * (ccv->deltas[0])); + break; // flip xd, sign + case nVW_PXNY: + nvp->move_x = (-nvp->remx * ccv->deltas[1] < (fix_frac(ccv->loc[1])) * ccv->deltas[0]); + break; // flip yd, sign + case nVW_PXPY: + nvp->move_x = (nvp->remx * ccv->deltas[1] < (_fixp1 - 1 - fix_frac(ccv->loc[1])) * ccv->deltas[0]); + break; // all things good + } + else + nvp->move_x = TRUE; + if (nvp->move_x) + nvp->stepy = fix_mul_div(nvp->remx, ccv->deltas[1], ccv->deltas[0]); + } + + // this is dumb, we want the second to be based on primary + if (ccv->mptr == eye_mptr) // ( () &&((nvp->dircode&nVW_YDIR)==0)) + { + _sclip_mask = &_sclip_major_mask[8][0]; + // _sclip_door=&_sclip_door_mask[8][0]; + } else { + _sclip_mask = (&_sclip_major_mask[0][0]) + ((ccv->flags & FRVECDIR) >> 3) + (dircode + dircode); + // _sclip_door=(&_sclip_door_mask[0][0])+((ccv->flags&FRVECDIR)>>3)+(dircode+dircode); + } + _sclip_door = (&_sclip_door_mask[0][0]) + ((ccv->flags & FRVECDIR) >> 3) + (dircode + dircode); + + _fr_sdbg(VECSPEW, { + mprintf("move_along(new_vec): sclip offs %d d%d (%d,%d)\n", _sclip_mask - _sclip_major_mask, dircode, + *_sclip_mask, *(_sclip_mask + 1)); + print_fcv(ccv, 2); + print_nvp(nvp); + }); + + if (nvp->move_x) { + if (dircode & nVW_XDIR) { + _face_curedge = &face_obstruct[0][1]; + _face_nxtedge = &face_obstruct[0][3]; + _face_curmask = FMK_INT_EW; + _face_nxtmask = FMK_INT_WW; + } else { + _face_curedge = &face_obstruct[0][3]; + _face_nxtedge = &face_obstruct[0][1]; + _face_curmask = FMK_INT_WW; + _face_nxtmask = FMK_INT_EW; + } + move_in_y = _fr_move_ccv_x(nvp); + } + + // the more he likes me, the more i drink + // i think the more i drink the more he likes me + if (move_in_y) { // actually move in y + fix nstp; + if (dircode & nVW_YDIR) + nstp = _fixp1 - 1 - fix_frac(ccv->loc[1]); + else + nstp = -fix_frac(ccv->loc[1]); + nstp = fix_mul_div(nstp, ccv->deltas[0], ccv->deltas[1]); + ccv->loc[0] += nstp; + ccv->loc[1] &= 0xffff0000; // this will go away in asm, as all we need to do is set the bottom + ccv->loc[1] += edgestep[dircode][1]; + _fr_sdbg(VECSPEW, mprintf("move_y(if): sub_clip or %x, old %x\n", _sclip_mask[1], me_subclip(ccv->mptr))); + _me_subclip(ccv->mptr) |= _sclip_mask[1]; + _fr_sdbg(VECSPEW, { + mprintf("move_y(if): nstp %x\n", nstp); + print_fcv(ccv, 2); + }); + } else { // pop up to top of square, for now, no f_o usage + // wow this is a slow dumb way to do this, eh? + ccv->loc[1] &= 0xffff0000; // the ands go away in assembler, as we need only set the bottom + ccv->loc[0] &= 0xffff0000; + ccv->loc[0] += edgestep[dircode][0]; + ccv->loc[1] += edgestep[dircode][1]; +#ifdef SWITCH_IS_FASTER_MAYBE + switch (dircode) { + case nVW_NXNY: + ccv->loc[0] &= 0xffff0000; + ccv->loc[1] &= 0xffff0000; + break; + case nVW_NXPY: + ccv->loc[0] &= 0xffff0000; + ccv->loc[1] |= 0x0000ffff; + break; + case nVW_PXNY: + ccv->loc[0] |= 0x0000ffff; + ccv->loc[1] &= 0xffff0000; + break; + case nVW_PXPY: + ccv->loc[0] |= 0x0000ffff; + ccv->loc[1] |= 0x0000ffff; + break; + } +#endif +#ifdef ANOTHER_WACKY_WAY + if (dircode & nVW_XDIR) + ccv->loc[0] |= 0x0000ffff; + else + ccv->loc[0] &= 0xffff0000; + if (dircode & nVW_YDIR) + ccv->loc[1] |= 0x0000ffff; + else + ccv->loc[1] &= 0xffff0000; +#endif + _fr_sdbg(VECSPEW, mprintf("move_y(else): sub_clip or %x, old %x\n", _sclip_mask[0], me_subclip(ccv->mptr))); + _me_subclip(ccv->mptr) |= _sclip_mask[0]; + _fr_sdbg(VECSPEW, { + mprintf("move_y(else):\n"); + print_fcv(ccv, 2); + }); + } + _fr_sdbg(VECSPEW, { + mprintf("move_along(end):\n"); + print_fcv(ccv, 2); + print_nvp(nvp); + }); +} + +#define _fr_move_along_hn_p(x) _fr_move_along_dcode(((ccv->deltas[0] > 0) ? nVW_XDIR : 0) + x) + +static uchar *_face_topedge, *_face_botedge; + +// partial tiles? who knows when + +// note fullwise, at the moment the subclip&face_topmask in middle gets doors, whereas the +// subclip==SUBCLIP_OUT_OF_CONE gets itself, though in reality and face_botmask would do same thing, i think + +// wow is this a total mess, goddamn +#define is_solid() \ + ((_face_topedge[(me_tiletype(cv->mptr) << 2)] == 0xff) || (me_clearsolid(cv->mptr) & _face_topmask) || \ + (me_subclip(cv->mptr) & _face_topmask) || (_face_botedge[(me_tiletype((cv->mptr + y_map_step)) << 2)] == 0xff) || \ + (me_clearsolid((cv->mptr + y_map_step)) & _face_botmask) || \ + (me_subclip(cv->mptr + y_map_step) == SUBCLIP_OUT_OF_CONE)) + +#define is_space() \ + ((_face_topedge[(me_tiletype(cv->mptr) << 2)] != 0xff) && ((me_clearsolid(cv->mptr) & _face_topmask) == 0) && \ + ((me_subclip(cv->mptr) & _face_topmask) == 0) && \ + (_face_botedge[(me_tiletype((cv->mptr + y_map_step)) << 2)] != 0xff) && \ + ((me_clearsolid((cv->mptr + y_map_step)) & _face_botmask) == 0)) + +// note how pretty this looks till you look at the is_solid macro +uchar _fr_skip_solid_right_n_back(FrClipVec *cv, fix max_loc, int y_map_step) { + if (is_solid()) { + cv->loc[0] &= 0xffff0000; + cv->loc[0] += _fixp1; + cv->mptr += 1; + while (is_solid() && (cv->loc[0] < max_loc)) { + cv->loc[0] += _fixp1; + cv->mptr += 1; + } + } + return (cv->loc[0] >= max_loc); +} + +uchar _fr_skip_space_right_n_back(FrClipVec *cv, fix max_loc, int y_map_step) { + if (is_space()) { + cv->loc[0] &= 0xffff0000; + cv->loc[0] += _fixp1; + cv->mptr += 1; + while (is_space() && (cv->loc[0] < max_loc)) { + cv->loc[0] += _fixp1; + cv->mptr += 1; + } + } + return (cv->loc[0] >= max_loc); +} + +uchar _fr_skip_solid_left_n_back(FrClipVec *cv, fix min_loc, int y_map_step) { + if (is_solid()) { + cv->loc[0] |= 0x0000ffff; + cv->loc[0] += _fixn1; + cv->mptr += -1; + while (is_solid() && (min_loc < cv->loc[0])) { + cv->loc[0] += _fixn1; + cv->mptr += -1; + } + } + return (cv->loc[0] <= min_loc); +} + +#define DT_SHFT (8) +#define DT_FAKE ((1 << DT_SHFT) - 1) +#define fixup_delta(dlta) \ + if ((dlta) > 0) \ + dlta = ((dlta) + DT_FAKE) >> DT_SHFT; \ + else \ + (dlta) = ((dlta)-DT_FAKE) >> DT_SHFT; + +void _fr_del_compute(FrClipVec *v1, FrClipVec *v2) { + v1->deltas[0] = v1->loc[0] - coor(EYE_X); + v1->deltas[1] = v1->loc[1] - coor(EYE_Y); + fixup_delta(v1->deltas[0]); + fixup_delta(v1->deltas[1]); + v2->deltas[0] = v2->loc[0] - coor(EYE_X); + v2->deltas[1] = v2->loc[1] - coor(EYE_Y); + fixup_delta(v2->deltas[0]); + fixup_delta(v2->deltas[1]); + v1->oldx = v1->loc[0]; + v2->oldx = v2->loc[0]; +} + +void _fr_spawn_check_one(FrClipVec *lv, FrClipVec *rv, uchar northward) { + FrClipVec *tmpr, *tmpl; + short cur_mapstep = new_del_mapstep[northward]; + int nxts; + + while (1) // really, run till we find the right edge + { + tmpr = allclipv + ffreevec; + nxts = tmpr->nxtv; + *tmpr = *lv; // perhaps should back up a mapstep and check for other half tiles first + tmpr->nxtv = nxts; + tmpr->flags = ffreevec; // fixup next and self + if (!_fr_skip_space_right_n_back(tmpr, rv->loc[0], cur_mapstep)) // really should do render bit setup here... + { // ok lv->rv->?, and tmpr->tmpl->vecx, ffreevec->tmpr... we want lv->tmpr->tmpl->rv, ffreevec->vecx + _fr_sdbg(VECSPEW, { + mprintf("new_dels(spawn): found "); + print_fcv(tmpr, 2); + }); + tmpl = allclipv + nxts; // tmpl is next free vec, as nxts is tmpr->nxtv and tmpr was ffreevec + ffreevec = tmpl->nxtv; // point ffreevec at tmpl's old next, as tmpl and tmpr are from free list + *tmpl = *tmpr; + tmpl->nxtv = lv->nxtv; // point tmpl->rv, lv's old friend + lv->nxtv = tmpr->flags; // we havent or'red in real flag data, so flags is currently just self for tmpr + tmpl->flags = + tmpr->nxtv; // tmpr->nxtv never changes, it is always pointing at tmpl, so tmpl can self set with it +#if _fr_defdbg(SANITY) + if (_fr_skip_solid_right_n_back(tmpl, rv->loc[0], cur_mapstep)) // this is true + mprintf("new_dels(spawn) ERR: found middle with no right edge\n"); +#else + _fr_skip_solid_right_n_back(tmpl, rv->loc[0], cur_mapstep); +#endif + tmpr->loc[0]--; + tmpr->mptr--; // note that tmpl is a-ok + if (northward) { + tmpr->flags |= FRVECUSE | FRVECL; + tmpl->flags |= FRVECUSE | FRVECR; + } else { + tmpl->flags |= FRVECUSE | FRVECL; + tmpr->flags |= FRVECUSE | FRVECR; + } + _fr_sdbg(VECSPEW, { + mprintf("new_dels(spawn): generated\n"); + print_fcv(tmpr, 2); + print_fcv(tmpl, 2); + }); + (allclipv + lastvec)->nxtv = ffreevec; + _fr_del_compute(tmpl, tmpr); + lv = tmpl; // move scan start over appropriately + } else + return; + } +} + +// returns FALSE if there are no vectors left in this branch, else true +// ok. northward is done super grossly at the moment +// really, this has to spawn new vectors as well + +// i want no part of their death culture +// i just want to go the beach +uchar _fr_move_new_dels(FrClipVec *lv, FrClipVec *rv, uchar northward) { + int lm, rm; + + lm = lg_min(lv->oldx, lv->loc[0]); + rm = lg_max(rv->oldx, rv->loc[0]); + store_x_span(fix_int(lv->loc[1]), fix_int(lm), fix_int(rm)); + _fr_sdbg(VECSPEW, mprintf("new_dels: setting span %d to %x,%x - o: %x %x c: x %x %x y %x %x m %x\n", + span_count(fix_int(lv->loc[1])), lm, rm, lv->oldx, rv->oldx, lv->loc[0], rv->loc[0], + lv->loc[1], rv->loc[1], lv->mptr)); + + if (_fr_skip_solid_right_n_back(lv, rv->loc[0], new_del_mapstep[northward])) + return FALSE; + if (_fr_skip_solid_left_n_back(rv, lv->loc[0], new_del_mapstep[northward])) { + _fr_sdbg(SANITY, mprintf("new_dels ERR: closure from right\n")); + return FALSE; + } + + lv->loc[1] += rmmod[northward + 1]; // if we are keeping the vectors, move their y coordinates appropriately + rv->loc[1] += rmmod[northward + 1]; + _fr_del_compute(lv, rv); + + // (*_fr_spawn_check)(lv,rv); + _fr_spawn_check_one(lv, rv, northward); + + lm = lv->flags & FRVECSELF; // now move everyones map coordinates + rm = rv->nxtv; + for (; lm != rm; lm = (allclipv + lm)->nxtv) + (allclipv + lm)->mptr += new_del_mapstep[northward]; + return TRUE; +} + +// remembering you fallen into my arms +// crying for the death of your heart +// you were stone white so delicate lost in the cold +// you were always so lost in the dark +void _fr_kill_pair(FrClipVec *lv, FrClipVec *rv) { + int lft_self = lv->flags & FRVECSELF, lft_pt = vechead; + // should sanity check for too many vectors here and in spawn code + if (vechead == lft_self) { + if (rv->nxtv != ffreevec) { + vechead = rv->nxtv; // if not, leave vechead at base for simplicities sake, i guess? + allclipv[endvec].nxtv = vechead; + } else + lastvec = -1; + } else { + while ((lft_pt != ffreevec) && (allclipv[lft_pt].nxtv != lft_self)) + lft_pt = allclipv[lft_pt].nxtv; // go find who points at lv + _fr_sdbg(SANITY, if (lft_pt == ffreevec) mprintf("kill_pair(lft_pt) ERR: no lft pt\n")); + allclipv[lft_pt].nxtv = rv->nxtv; + if (lastvec == lv->nxtv) + lastvec = lft_pt; + } + rv->nxtv = ffreevec; + ffreevec = lv->flags & FRVECMASK; + rv->flags &= FRVECSELF; + lv->flags &= FRVECSELF; + if (lastvec != -1) + (allclipv + lastvec)->nxtv = ffreevec; +} + +// it's a wonderful world, with a lot of strange men +// who are standing around, and they're all wearing towels +uchar _fr_setup_first_pair(uchar headnorth) { + fix org[3], ray[3]; + int flags; + + _fr_sdbg(VECSPEW, mprintf("setup_first_pair: note vh %d ff %d\n", vechead, ffreevec)); +#define FULL_360_VECTORS +#ifdef FULL_360_VECTORS + org[0] = coor(EYE_X); + org[1] = coor(EYE_Y); + org[2] = coor(EYE_Z); + ray[1] = fix_make(0, 0); + ray[2] = fix_make(0, 0); + // ray[0]=fix_make((headnorth)?-1:1,0); + ray[0] = fix_make(-1, 0); + ccv = allclipv + ffreevec; + flags = FRVECUSE; + flags |= headnorth ? FRVECR : FRVECL; + _fr_build_clip_vec(ccv, org, ray, flags); + ccv = allclipv + ccv->nxtv; + ray[0] = fix_make(1, 0); + // ray[0]=fix_make((headnorth)?1:-1,0); + flags = FRVECUSE; + flags |= headnorth ? FRVECL : FRVECR; + _fr_build_clip_vec(ccv, org, ray, flags); + ffreevec = ccv->nxtv; + lastvec = ccv->flags & FRVECSELF; +#else + if (headnorth) { + if (span_lines[3] < 0) + return FALSE; + ray[0] = span_lines[2]; + ray[1] = span_lines[3]; + } else { + if (span_lines[1] > 0) + return FALSE; + ray[0] = span_lines[0]; + ray[1] = span_lines[1]; + } + org[2] = coor(EYE_Z); // these are constant + ray[2] = fix_make(0, 0); + org[0] = span_intersect[0]; // these are true for both north and south left vecs + org[1] = span_intersect[1]; + + ccv = allclipv + ffreevec; + flags = FRVECUSE; + flags |= headnorth ? FRVECR : FRVECL; + _fr_build_clip_vec(ccv, org, ray, flags); + ccv = allclipv + ccv->nxtv; + + if (headnorth) { + ray[0] = span_lines[4]; + ray[1] = span_lines[5]; + } else { + ray[0] = span_lines[6]; + ray[1] = span_lines[7]; + } + + org[0] = span_intersect[2]; + org[1] = span_intersect[3]; + + flags = FRVECUSE; + flags |= headnorth ? FRVECL : FRVECR; + _fr_build_clip_vec(ccv, org, ray, flags); + ffreevec = ccv->nxtv; + lastvec = ccv->flags & FRVECSELF; +#endif + + // setup various revectorings + if (headnorth) { + _face_topedge = &face_obstruct[0][0]; + _face_botedge = &face_obstruct[0][2]; + _face_topmask = FMK_INT_NW; + _face_botmask = FMK_INT_SW; + } else { + _face_topedge = &face_obstruct[0][2]; + _face_botedge = &face_obstruct[0][0]; + _face_topmask = FMK_INT_SW; + _face_botmask = FMK_INT_NW; + } + fr_clip_start(headnorth); + return TRUE; +} + +#if _fr_defdbg(VECTRACK) +void _fr_show_veclist(void) { + int i, cv; + mprintf("Vec(ff%d): ", ffreevec); + for (i = 0, cv = vechead; i < MAX_CLIP_VEC; i++, cv = allclipv[cv].nxtv) + mprintf("%1.1X%c%c ", cv, (allclipv[cv].flags & FRVECUSE) ? 'U' : 'x', + (allclipv[cv].flags & FRVECL) ? 'L' : 'R'); + if (cv != vechead) + mprintf("ERROR %d!%d\n", cv, vechead); + else + mprintf("\n"); +} +#else +#define _fr_show_veclist() +#endif + +// stains on the carpet and stains on the memory +// and both of us know, how the end always is +int fr_clip_tile(void) { + FrClipVec *_v1, *_v2; + int northward, nxtvec; + // also have to do exact correct reverse order, so obj_stack works, so go north first, then south + // sadly, new render order invalidates this + + // Just draw everything if physics is disabled + if (global_fullmap->cyber) { + fr_clip_show_all(); + _fr_ret; + } + + // next, do each direction + if (_fr_curflags & FR_SHOWALL_MASK) { + fr_clip_show_all(); + _fr_ret; + } // fill in all things + _fr_sdbg(VECSPEW, mprintf("Frame start at %x %x\n", coor(EYE_X), coor(EYE_Y))); + eye_mptr = MAP_GET_XY(_fr_x_cen, _fr_y_cen); + + for (northward = 1; northward >= 0; northward--) { + // set up the initial vectors and list + if (!_fr_setup_first_pair(northward)) + continue; + _fr_sdbg(VECSPEW, mprintf("clip_tile(for): heading %d\n", northward)); + // for each line + do { + _v1 = ccv = allclipv + vechead; + while (ccv->flags & FRVECUSE) { + // move out left vector + _fr_move_along_hn_p(northward); + // at each square, code objects + // keep a right edge for internal? ick! + + // move out right vector + _v2 = ccv = allclipv + ccv->nxtv; + nxtvec = ccv->nxtv; // so if we new_dels more vecs, or kill our vec, we have the next ptr ready, eh? + _fr_move_along_hn_p(northward); // do same things, but in reverse + + if (!_fr_move_new_dels(_v1, _v2, northward)) // kill off the vectors + { // wow, can we do multiple here... i guess so + _fr_sdbg(VECSPEW, { + mprintf("clip_tile(while): killing vector pair\n"); + print_fcv(_v1, 2); + print_fcv(_v2, 2); + }); + _fr_kill_pair(_v1, _v2); + } else { + nxtvec = _v2->nxtv; // could have changed + _fr_sdbg(VECSPEW, { + mprintf("clip_tile(while): moving on to %d after pair\n", nxtvec); + print_fcv(_v1, 2); + print_fcv(_v2, 2); + }); + } +#if _fr_defdbg(VECTRACK) + _fr_sdbg(VECTRACK, _fr_show_veclist()); +#endif + // store off new base span + // spawn/collect vectors + // move to next span line + _v1 = ccv = allclipv + nxtvec; + } + } while (ffreevec != vechead); + } + // hit the fucking road + span_fixup(); + _fr_ret; +} + +// can you see? +// see into the back of a long black car +// pulling away from a funeral of flowers +// with my hand, between your legs +// melting + +// fills dst with a wall hit by a ray cast from orig along ray +// a len!=0 is stopped at, 0 goes forever or until page fault +// each fix* is assumed to be a 3 element array +#ifdef WE_WERE_COOL +fix *fr_ray_cast(fix *org, fix *ray, fix *dst, fix len) { + MapElem *cur_us; + + _fr_build_clip_vec(&scratchvec, org, ray, len); +} +#endif diff --git a/engine/src/GameSrc/frcompil.c b/engine/src/GameSrc/frcompil.c new file mode 100644 index 0000000..64f633a --- /dev/null +++ b/engine/src/GameSrc/frcompil.c @@ -0,0 +1,82 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * FrCompil.c + * + * $Source: n:/project/cit/src/RCS/frcompil.c $ + * $Revision: 1.8 $ + * $Author: dc $ + * $Date: 1994/04/23 09:21:25 $ + * + * Citadel Renderer + * various clippers for terrain, including basic cone clip and the later day + * tile based clipper + * + * $Log: frcompil.c $ + * Revision 1.8 1994/04/23 09:21:25 dc + * stuff for map clear state + * + * Revision 1.7 1994/01/02 17:11:37 dc + * Initial revision + * + */ +#define __FRCOMPIL_SRC +#include "frintern.h" +#include "map.h" +#include "mapflags.h" + +void fr_compile_rect(fmp *fmptr, int llx, int lly, int ulx, int uly, uchar seen_bits) { + FullMap *fm = (FullMap *)fmptr; + int x, y; + MapElem *mptr; + + if (lly > 0) + lly--; + else + lly = 0; + if (llx > 0) + llx--; + else + llx = 0; + if (uly < fm_y_sz(fm) - 1) + uly++; + else + uly = fm_y_sz(fm) - 1; + if (ulx < fm_x_sz(fm) - 1) + ulx++; + else + ulx = fm_x_sz(fm) - 1; + y = lly; + x = llx; + for (; y <= uly; y++) { + x = llx; + mptr = FULLMAP_GET_XY(fm, x, y); + for (; x <= ulx; x++, mptr++) { + me_clearsolid_set(mptr, 0); // we know nothing + if (seen_bits) + me_bits_seen_clear(mptr); + } + } +} + +void fr_compile_restart(fmp *fmptr) { + FullMap *fm = (FullMap *)fmptr; + fr_pipe_resize(fm_x_sz(fm), fm_y_sz(fm), fm_z_shft(fm), fm_map(fm)); + fr_compile_rect(fm, 0, 0, fm_x_sz(fm), fm_y_sz(fm), FALSE); +} diff --git a/engine/src/GameSrc/frmain.c b/engine/src/GameSrc/frmain.c new file mode 100644 index 0000000..3789b7b --- /dev/null +++ b/engine/src/GameSrc/frmain.c @@ -0,0 +1,134 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * FrMain.c + * + * $Source: r:/prj/cit/src/RCS/frmain.c $ + * $Revision: 1.6 $ + * $Author: xemu $ + * $Date: 1994/10/08 04:02:08 $ + * + * Citadel Renderer + * main routines to render and interact with high level renderer data + * + * $Log: frmain.c $ + * Revision 1.6 1994/10/08 04:02:08 xemu + * audiolog updates + * + * Revision 1.5 1994/09/08 04:08:50 kevin + * Turn off blending in pickup mode. + * + * Revision 1.4 1994/09/05 06:43:36 dc + * call pipe_go_3, add dummy test_me code + * + * Revision 1.3 1994/03/09 03:01:56 xemu + * c:\bin\more.exe frequent updating + * + * Revision 1.2 1994/01/23 14:02:55 dc + * static and other solid effects + * + * Revision 1.1 1994/01/02 17:11:53 dc + * Initial revision + * + */ + +#ifdef AUDIOLOGS +#include "audiolog.h" +#endif + +#include "frtypes.h" +#include "frintern.h" +#include "frparams.h" +#include "frflags.h" +#include "gr2ss.h" + +int fr_pipe_go_2(void); +int fr_pipe_go_3(void); + +// extern "C" +//{ +// void ClearCache (unsigned char* theAddress, unsigned long numBlocks); +//} + +int fr_rend(frc *view) { + fr_prepare_view(view); /* init _fr, load flags, so on */ + if (!fr_start_view()) + return -1; /* broken broken - but what to really return */ + ss_safe_set_cliprect(0, 0, 640, 480); + if (_fr_curflags & (FR_NORENDR_MASK | FR_SOLIDFR_MASK)) /* dont really render, call a game thing */ + { + if (_fr->render_call) + _fr->render_call(&_fr->draw_canvas.bm, _fr_curflags); + } else { /* actually do the 3d thang */ + extern uchar _g3d_enable_blend; + extern bool DoubleSize; + uchar save_blend_flag; + + if ((_fr_curflags & FR_PICKUPM_MASK) || DoubleSize) { + save_blend_flag = _g3d_enable_blend; + _g3d_enable_blend = FALSE; + } + + // CC: Do we really need to do this? + // gr_clear(0x0); + + // MLA - does nothing! + // synchronous_update(); // Make sure our time-sensitive updater gets run +#ifdef AUDIOLOGS + audiolog_loop_callback(); +#endif + // printf(" fr_pipe_start\n"); + fr_pipe_start(-1); /* set environment up */ + + // printf(" fr_clip_cone\n"); + fr_clip_cone(); /* generate basic spans */ + + // printf(" fr_clip_tile\n"); + fr_clip_tile(); /* clipping and obj sort pass */ + + // MLA - does nothing! + // synchronous_update(); // One more time + // ClearCache(_fr->draw_canvas.bm.bits, (_fr->draw_canvas.bm.row >> 5) * _fr->ywid); + // printf(" fr_pipe_go_3\n"); + fr_pipe_go_3(); /* actually render the stuff */ + + // printf(" fr_pipe_end\n"); + fr_pipe_end(); /* clean environment up */ + + // MLA - does nothing! + // synchronous_update(); // And one for the road. +#ifdef AUDIOLOGS + audiolog_loop_callback(); +#endif + + if ((_fr_curflags & FR_PICKUPM_MASK) || DoubleSize) { + _g3d_enable_blend = save_blend_flag; + } + } + + // printf(" fr_pipe_end\n"); + fr_send_view(); /* send it, whether it came from 3d or special */ + if ((_fr->flags & FR_CURVIEW_MASK) == FR_CURVIEW_STRT) + _frp.time.last_frame_cnt++; + return 1; +} + +#ifdef DEBUG_STUFF_FOR_LATER +void fr_show_stats(frc *view) +#endif diff --git a/engine/src/GameSrc/frobj.c b/engine/src/GameSrc/frobj.c new file mode 100644 index 0000000..7fe5df7 --- /dev/null +++ b/engine/src/GameSrc/frobj.c @@ -0,0 +1,143 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * FrObj.c + * + * $Source: r:/prj/cit/src/RCS/frobj.c $ + * $Revision: 1.7 $ + * $Author: dc $ + * $Date: 1994/08/30 05:50:47 $ + * + * Citadel Renderer + * object draw/setup code + */ + +#include + +#include "map.h" +#include "objects.h" +#include "refstuf.h" +#include "faceobj.h" +#include "frintern.h" +#include "frparams.h" +#include "frsubclp.h" +#include "frflags.h" +#include "gamesort.h" +#include "tilename.h" + +uchar pick_best_ref(ObjRefID cRef); + +uchar pick_best_ref(ObjRefID cRef) { + int bdist, cdist, ldist; + ObjRefID curLRef, BRef; + + // really, need to sort so SCOOC should do it once we are done with clip + pipe + if ((me_subclip(_fdt_mptr) != SUBCLIP_OUT_OF_CONE) && (me_tiletype(_fdt_mptr) != TILE_SOLID)) { + bdist = cdist = _fdt_dist; + BRef = cRef; // find correct version of object, initially, first guess is best + _fr_sdbg(OBJ_TALK, mprintf("First version ok %d at %d %d\n", bdist, _fdt_x, _fdt_y)); + } else + bdist = cdist = 0xffff; + curLRef = objRefs[cRef].nextref; // init the examine others loop + while (curLRef != cRef) { // this should know to check the map for not actually seen + int x, y; + MapElem *mp; + + x = objRefs[curLRef].state.bin.sq.x; + y = objRefs[curLRef].state.bin.sq.y; + + mp = MAP_GET_XY(x, y); + + _fr_sdbg(OBJ_TALK, mprintf("check %d %d..sc %x\n", x, y, me_subclip(mp))); + + if ((me_subclip(mp) != SUBCLIP_OUT_OF_CONE) && (me_tiletype(mp) != TILE_SOLID)) { + // this is super gross, but what to do + ldist = abs(x - _fr_x_cen) + abs(y - _fr_y_cen); + if (ldist < bdist) { + bdist = ldist; + BRef = curLRef; + } + _fr_sdbg(OBJ_TALK, mprintf("tried it got %d bdist %d\n", ldist, bdist)); + } + curLRef = objRefs[curLRef].nextref; /* we are us */ + } + if (bdist != 0xffff) { + CitrefSetDealt(BRef); + return (BRef == cRef); + } else + return FALSE; +} + +// this is a total mess +// should use seen bit and objRefdone and a bit in the objRefs +// but for now, we just have to get all objs in the square sorting +// we can deal with speeding this up later +void render_parse_obj(void) { +#ifndef __RENDTEST__ + ObjRefID curORef; + ObjID cobjid; + + curORef = _fdt_mptr->objRef; + while (curORef != OBJ_REF_NULL) { + uchar show_here; + cobjid = objRefs[curORef].obj; + if (!ObjCheckDealt(cobjid)) { + show_here = pick_best_ref(curORef); + ObjSetDealt(cobjid); + } else + show_here = CitrefCheckDealt(curORef); + + if (show_here) { + _fr_sdbg(OBJ_TALK, mprintf("Rendering %d at %d %d\n", curORef, _fdt_x, _fdt_y)); + sort_show_obj(cobjid); + } + // else + // mprintf("not rend %d @ %d %d\n",curORef,_fdt_x,_fdt_y); + curORef = objRefs[curORef].next; + } + render_sorted_objs(); +#else + ushort curORef; + curORef = _fdt_mptr->objRef; + _fr_sdbg(OBJ_TALK, mprintf("Rendering %d at %d %d\n", curORef, _fdt_x, _fdt_y)); + // perhaps draw a box or something +#endif +} + +void facelet_parse_obj(void) { +#ifndef __RENDTEST__ + ObjRefID curORef; + ObjID cobjid; + + curORef = _fdt_mptr->objRef; + while (curORef != OBJ_REF_NULL) { + cobjid = objRefs[curORef].obj; + if (!ObjCheckDealt(cobjid)) { + facelet_obj(cobjid); + ObjSetDealt(cobjid); + } + curORef = objRefs[curORef].next; + } +#else + ushort curORef; + curORef = _fdt_mptr->objRef; + _fr_sdbg(OBJ_TALK, mprintf("Rendering %d at %d %d\n", curORef, _fdt_x, _fdt_y)); + // perhaps draw a box or something +#endif +} diff --git a/engine/src/GameSrc/froslew.c b/engine/src/GameSrc/froslew.c new file mode 100644 index 0000000..a42faf7 --- /dev/null +++ b/engine/src/GameSrc/froslew.c @@ -0,0 +1,248 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * FrOslew.c + * + * $Source: n:/project/cit/src/RCS/froslew.c $ + * $Revision: 1.8 $ + * $Author: dc $ + * $Date: 1994/04/10 05:34:29 $ + * + * Citadel Renderer + * object slew system controllers/prototypes/vars + * + * $Log: froslew.c $ + * Revision 1.8 1994/04/10 05:34:29 dc + * hack return codes to avoid move_to teleport and physics vector reset... + * + * Revision 1.7 1994/04/10 05:15:41 dc + * support for cyberman, vfx1, other 6d control structure, inc. HEAD_H + * + * Revision 1.6 1994/01/02 17:12:02 dc + * Initial revision + * + * Revision 1.5 1993/12/05 05:43:57 mahk + * Fixed player height thing for slew mode. + * + * Revision 1.4 1993/11/04 16:09:50 dc + * fix flat floor slewing + * + * Revision 1.3 1993/09/14 05:41:49 dc + * new fr/camera regieme + * + * Revision 1.2 1993/09/05 20:54:16 dc + * new regieme for real, or at least more so + * + * Revision 1.1 1993/09/05 20:21:41 dc + * Initial revision + * + */ + +#include "fauxrint.h" +#include "froslew.h" +#ifndef __RENDTEST__ +#include "objsim.h" +#endif +#include "map.h" +#include "tilename.h" +#include "mapflags.h" + +int32_t eye_mods[3] = {0, 0, 0}; + +// prototype +int32_t *fr_objslew_obj_to_list(int32_t *flist, Obj *cobj, int count); +Obj *fr_objslew_list_to_obj(int32_t *flist, Obj *cobj, int count); + +int32_t *fr_objslew_obj_to_list(int32_t *flist, Obj *cobj, int count) { + switch (count - 1) { + case 5: + flist[5] = (cobj->loc.b << 8); + case 4: + flist[4] = (cobj->loc.p << 8); + case 3: + flist[3] = (cobj->loc.h << 8); + case 2: + flist[2] = (cobj->loc.z << SLOPE_SHIFT_D); + default: + case 1: + flist[1] = cobj->loc.y; + case 0: + flist[0] = cobj->loc.x; + break; + } + return flist; +} + +Obj *fr_objslew_list_to_obj(int32_t *flist, Obj *cobj, int count) { + switch (count - 1) { + case 5: + cobj->loc.b = (flist[5] >> 8); + case 4: + cobj->loc.p = (flist[4] >> 8); + case 3: + cobj->loc.h = (flist[3] >> 8); + case 2: + cobj->loc.z = (flist[2] >> SLOPE_SHIFT_D); + default: + case 1: + cobj->loc.y = flist[1]; + case 0: + cobj->loc.x = flist[0]; + break; + } + return cobj; +} + +// returns whether the camera moved or not +uchar fr_objslew_go_real_height(Obj *cobj, int32_t *eye) { + int32_t leye[3]; + int x, y, z; + MapElem *o_t; + + if (cobj != NULL) { + eye = leye; + fr_objslew_obj_to_list(eye, cobj, 3); + } + x = eye[0] >> MAP_SH; + y = eye[1] >> MAP_SH; + o_t = MAP_GET_XY(x, y); + z = me_height_flr(o_t); + z *= MAP_SC >> SLOPE_SHIFT; + // someday should teach this about other tile types.... + if (me_bits_mirror(o_t) != MAP_FFLAT) + if ((me_tiletype(o_t) >= TILE_SLOPEUP_N) && (me_tiletype(o_t) <= TILE_SLOPEUP_W)) { + int diff; + switch (me_tiletype(o_t)) { + case TILE_SLOPEUP_N: + diff = eye[1] & MAP_MK; + break; + case TILE_SLOPEUP_E: + diff = eye[0] & MAP_MK; + break; + case TILE_SLOPEUP_S: + diff = MAP_MK - (eye[1] & MAP_MK); + break; + case TILE_SLOPEUP_W: + diff = MAP_MK - (eye[0] & MAP_MK); + break; + } + z += (diff * me_param(o_t)) >> SLOPE_SHIFT; + } + // now add height of current posture... + eye[2] = z + PLAYER_HEIGHT / 2; /* this should probably be fixed */ + if (cobj != NULL) + fr_objslew_list_to_obj(eye, cobj, 3); + return TRUE; +} + +uchar fr_objslew_allowed(Obj *cobj, int32_t *eye) { + int x, y; // z + MapElem *o_t; + + if (cobj != NULL) + fr_objslew_obj_to_list(eye, cobj, 3); + x = eye[0] >> MAP_SH; + y = eye[1] >> MAP_SH; + if ((x < 0) || (x >= MAP_XSIZE) || (y < 0) || (y >= MAP_YSIZE)) + return FALSE; + o_t = MAP_GET_XY(x, y); + if (me_tiletype(o_t) == TILE_SOLID) + return FALSE; + if (cobj != NULL) + fr_objslew_list_to_obj(eye, cobj, 3); + return TRUE; +} + +// to physics teleport or not +// should teach it not to slam all velocities! +uchar fr_objslew_moveone(Obj *cobj, ObjID objnum, int which, int how, uchar conform) { + int32_t eye[4]; + uchar valid_pos = TRUE; + + if (cobj == NULL) + cobj = &objs[objnum]; + fr_objslew_obj_to_list(eye, cobj, 4); + switch (which) { + case EYE_HEADH: + eye_mods[0] += how * cam_slew_scale[which]; + return valid_pos; // break; + case EYE_H: + cobj->loc.h += (how * cam_slew_scale[which]) >> 8; + break; + case EYE_RESET: + eye_mods[0] = eye_mods[1] = eye_mods[2] = 0; + return valid_pos; // break; + case EYE_B: + case EYE_P: + eye_mods[which - 3] += how * cam_slew_scale[which]; + return valid_pos; // break; + case EYE_Z: + eye[2] += how << SLOPE_SHIFT_D; + break; + case EYE_Y: + case EYE_X: { + fix v[2], tot; + tot = how * cam_slew_scale[which]; + fix_sincos((fixang)(eye[3] + (which == EYE_X ? 0x4000 : 0)), v + 0, v + 1); + eye[0] += fix_int(fix_mul(v[0], tot)); + eye[1] += fix_int(fix_mul(v[1], tot)); + if (conform) + if ((valid_pos = fr_objslew_allowed(NULL, eye)) == TRUE) + fr_objslew_go_real_height(NULL, eye); + break; + } + } + fr_objslew_list_to_obj(eye, cobj, 3); + obj_move_to(cobj - objs, &cobj->loc, TRUE); + return valid_pos; +} + +// to physics teleport or not +//#pragma disable_message(202) +uchar fr_objslew_setone(int which, int l_new) { + switch (which) { + case EYE_HEADH: + eye_mods[0] = l_new; + return TRUE; + case EYE_H: + break; + case EYE_RESET: + eye_mods[0] = eye_mods[1] = eye_mods[2] = 0; + return TRUE; + case EYE_P: + eye_mods[1] = l_new; + return TRUE; + case EYE_B: + eye_mods[2] = l_new; + return TRUE; + case EYE_Z: + case EYE_Y: + case EYE_X: + break; + } + return TRUE; +} +//#pragma enable_message(202) + +/* KLC - not used +uchar fr_objslew_tele_to(Obj *, int , int ) +{ + return TRUE; +} +*/ diff --git a/engine/src/GameSrc/frpipe.c b/engine/src/GameSrc/frpipe.c new file mode 100644 index 0000000..807a31a --- /dev/null +++ b/engine/src/GameSrc/frpipe.c @@ -0,0 +1,401 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * FrPipe.c + * + * $Source: r:/prj/cit/src/RCS/frpipe.c $ + * $Revision: 1.8 $ + * $Author: dc $ + * $Date: 1994/09/06 03:15:09 $ + * + * Citadel Renderer + * pipeline controller + * + * $Log: frpipe.c $ + * Revision 1.8 1994/09/06 03:15:09 dc + * well, why not try and sort a little better, eh + * really, though, it just doesnt work + * + * Revision 1.7 1994/09/05 06:43:47 dc + * diamond pipe, second wrong pipe, various fixes + * + * Revision 1.6 1994/08/19 05:03:54 dc + * diagonals with the correct height... how odd + * + * Revision 1.5 1994/08/04 23:48:56 dc + * no seen bits through hack cameras... + * + * Revision 1.4 1994/04/23 09:06:49 dc + * seen bit stuff + * + * Revision 1.3 1994/04/06 07:19:14 dc + * seen bits, clear dealt bits... soon we will use dealt bits in frobj and life will be good + * + * Revision 1.2 1994/02/13 05:46:22 dc + * sort + * + * Revision 1.1 1994/01/02 17:12:05 dc + * Initial revision + * + */ + +#include +#include + +#include "map.h" +#include "mapflags.h" +#include "frsubclp.h" + +#include "frintern.h" +#include "frspans.h" +#include "frtables.h" +#include "frquad.h" +#include "frparams.h" +#include "frflags.h" +#include "fr3d.h" // for coor + +#include "refstuf.h" + +#include "OpenGL.h" + +// tell me tell me what you're after +// cause i just want to get there faster + +// externed for others in frintern +MapElem *fr_map_base; +int fr_map_x, fr_map_y, fr_map_z; +uchar *x_span_lists; +uchar *cone_span_list; + +int _fr_x_cen, _fr_y_cen; /* center tile for eye */ + +// static uchar cyber_on; + +/* +static uchar hack_off; +*/ + +#ifdef _FR_TILEMAP +static int tile_x, tile_y; /* tilemap x,y */ +#endif + +#ifdef PIPE_POINTUP +// set_point_parms +int (*set_point_parms)(g3s_phandle phd, int trans_off, int flrciel, int hgt); +// actual point parm thingies +int set_cspace_color(g3s_phandle phd, int trans_off, int flrciel, int hgt); +int set_texture_i(g3s_phandle phd, int trans_off, int flrciel, int hgt); +int set_null_vrtx(g3s_phandle phd, int trans_off, int flrciel, int hgt); +#endif + +// see header file for defines/layout graph +uchar quad_code_to_mask_2[] = {FMK_NW | FMK_EW | FMK_WW, FMK_NW | FMK_WW, FMK_NW | FMK_EW, + FMK_EW | FMK_NW | FMK_SW, FMK_EW | FMK_NW, FMK_EW | FMK_SW, + FMK_SW | FMK_EW | FMK_WW, FMK_SW | FMK_EW, FMK_SW | FMK_WW, + FMK_WW | FMK_NW | FMK_SW, FMK_WW | FMK_SW, FMK_WW | FMK_NW, + FMK_EW | FMK_WW | FMK_SW | FMK_NW}; + +static char diag_moves[4][2] = {{1, 1}, {1, -1}, {-1, -1}, {-1, 1}}; +static short diag_map_moves[4] = {0xdead, 0xbeef, 0xdead, 0xbeef}; +static char diag_stupid[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; +static char diag_dirsets[4][2] = {{2, 1}, {3, 2}, {0, 3}, {1, 0}}; + +// Protoypes +void _fr_init_slopes(int zshf); +void do_seen_pass(void); +void draw_dir_dir(int dir, int st, int len); +int fr_pipe_go_3(void); + +// the triangle is parm*n, 1<> zshf); + lvl = fix_make((1 << zshf), 0); + for (loop = 0; loop < HGT_STEPS; loop++) { + parm = fix_make(loop, 0); + tmp = fix_mul(parm, parm) + fix_mul(lvl, lvl); + n = fix_sqrt(tmp); + slope_norm[loop][0] = fix_div(parm, n); + slope_norm[loop][1] = fix_div(lvl, n); + slope_norm[loop][2] = -slope_norm[loop][0]; // negative horiz for speed + _fr_fhgt_list[loop] = _fr_fhgt_step * loop; + _fr_sfuv_list[loop] = (_fr_fhgt_step * (HGT_STEPS - loop)) >> 8; + _fr_sfuv_list[HGT_STEPS + loop] = (_fr_fhgt_step * (-loop)) >> 8; + } + _fr_fhgt_list[loop] = _fr_fhgt_step * loop; // make sure we get parm 32 for ceil + _fr_sfuv_list[HGT_STEPS + loop] = (_fr_fhgt_step * (-HGT_STEPS)) >> 8; + outer_wall[0][0][0] = outer_wall[0][3][0] = 0; + outer_wall[0][1][0] = outer_wall[0][2][0] = fix_make(1, 0); + outer_wall[0][0][1] = outer_wall[0][1][1] = _fr_fhgt_list[loop]; +} + +/* called by fr_compile_restart when size changes + * this knows size of world and stuff like that + */ +int fr_pipe_resize(int x, int y, int z, void *mptr) { + int i; + extern fix tf_diag_walls[4][2]; + fr_map_y = y; + csp_trans_add[1] = wall_adds[0] = fr_map_x = x; + wall_adds[2] = -wall_adds[0]; + csp_trans_add[2] = wall_adds[0] + wall_adds[1]; + _fr_init_slopes(fr_map_z = z); + fr_pts_resize(x, y); + fr_clip_resize(x, y); + tf_diag_walls[0][1] = tf_diag_walls[1][1] = fix_make(HGT_STEPS, 0) >> z; + fr_map_base = (MapElem *)mptr; + for (i = 0; i < 4; i++) + diag_map_moves[i] = diag_moves[i][0] + (fr_map_x * diag_moves[i][1]); + _fr_ret; +} + +// currently all pipe memory is static, so this is easy +int fr_pipe_freemem(void) { _fr_ret; } + +/* called at the beginning of every frame, sets up 3d variables for the world + * also sets up the globals used in the clippers + */ +//#pragma disable_message(202) +int fr_pipe_start(int rad) { + _fr_x_cen = coor(EYE_X) >> (8 + MAP_SH); + _fr_y_cen = coor(EYE_Y) >> (8 + MAP_SH); +#ifdef _FR_TILEMAP + { + LGPoint p; + TileMapGetCursor(NULL, &p); + tile_x = p.x; + tile_y = p.y; + if (fr_highlights) { + TileMapClearHighlights(NULL); + TileMapRedrawSquares(NULL, NULL); + } + } +#endif // _FR_TILEMAP +#ifdef NOT_IMPLEMENTED +// hack_off=fr_detail_master; if (hack_off==3) hack_off=2; +#endif + fr_terr_frame_start(); + fr_clip_frame_start(); + fr_pts_frame_start(); + ObjsClearDealt(); + CitrefsClearDealt(); + + _fr_ret; +} +//#pragma enable_message(202) + +/* cleanup any memory or variable space used by the pipeline */ +int fr_pipe_end(void) { + fr_terr_frame_end(); + fr_clip_frame_end(); + _fr_ret; +} + +// claiming i stepped out of line +// which forced you to leave me +// as if that idea was mine +// oh you stupid thing, speaking of course as your dear departed +// oh you stupid thing, it wasnt me that you outsmarted +// oh you stupid thing, stopping it all before it even started +extern void dumb_hack_for_now(int x, int y); +#define SEEN_SIZE 1 +void do_seen_pass(void) { + int lx, ly, tx, ty, x; + MapElem *cur_mpt, *base_mpt; + lx = _fr_x_cen - SEEN_SIZE; + tx = lx + (SEEN_SIZE << 1); + if (lx < 0) + lx = 0; + if (tx >= MAP_XSIZE) + tx = MAP_XSIZE - 1; + ly = _fr_y_cen - SEEN_SIZE; + ty = ly + (SEEN_SIZE << 1); + if (ly < 0) + ly = 0; + if (ty >= MAP_YSIZE) + ty = MAP_YSIZE - 1; + base_mpt = MAP_GET_XY(lx, ly); + for (; ly <= ty; ly++, base_mpt += MAP_XSIZE) + for (x = lx, cur_mpt = base_mpt; x <= tx; x++, cur_mpt++) + if (me_subclip(cur_mpt) != SUBCLIP_OUT_OF_CONE) + me_bits_seen_set(cur_mpt); +} + +/* Hey + * new diamond scan pipeline, eh? + * direction codes are 1 NE, 2 SE, 3 SW, 4 NW + */ +void draw_dir_dir(int dir, int st, int len) { + short dmm = diag_map_moves[dir], dmx = diag_moves[dir][0], dmy = diag_moves[dir][1]; + if (st != 0) { + _fdt_mptr += dmm * st; + _fdt_x += dmx * st; + _fdt_y += dmy * st; + } + while (len-- > 0) { + if ((_fdt_x >= 0) && (_fdt_x < fr_map_x) && (_fdt_y >= 0) && (_fdt_y < fr_map_y)) + if (me_subclip(_fdt_mptr) != SUBCLIP_OUT_OF_CONE) { + dumb_hack_for_now(_fdt_x, _fdt_y); + fr_draw_tile(); + } + _fdt_mptr += dmm; + _fdt_x += dmx; + _fdt_y += dmy; + } +} + +extern ushort frpipe_dist; // furtherest walking distance away + +#define Q_N 0 +#define Q_E 1 +#define Q_S 2 +#define Q_W 3 +#define QoL(a, b, c, d) \ + { Q_##a, Q_##b, Q_##c, Q_##d } + +uchar quad_order_lists[8][4] = {QoL(S, W, E, N), QoL(W, S, N, E), QoL(W, N, S, E), QoL(N, W, E, S), + QoL(N, E, W, S), QoL(E, N, S, W), QoL(E, S, N, W), QoL(S, E, W, N)}; + +int fr_pipe_go_3(void) { + + //_fr_x_cen = fix_make(0, 2); + + int i, j, p_dir; // p_dir is how many per diagonal element + uchar *loc_code_ptr, *quad_order; + short clip_len[4]; // , clip_contrib[4]; + MapElem *endcaps[4], *center = MAP_GET_XY(_fr_x_cen, _fr_y_cen); + + // printf(" pipedist %d center %x %x\n",frpipe_dist,_fr_x_cen,_fr_y_cen); + + // mprintf("\npipedist %d center %x %x\n",frpipe_dist,_fr_x_cen,_fr_y_cen); + fr_rend_start(); + if ((_fr_curflags & FR_HACKCAM_MASK) == 0) + do_seen_pass(); + + // use clip len as temp for delta within the square + clip_len[0] = (short)((fr_camera_last[0] & 0xffff) - 0x8000); + clip_len[1] = (short)((fr_camera_last[1] & 0xffff) - 0x8000); + + if (abs(clip_len[1]) > abs(clip_len[0])) + if (clip_len[0] > 0) + if (clip_len[1] > 0) + p_dir = 0; + else + p_dir = 3; + else if (clip_len[1] > 0) + p_dir = 7; + else + p_dir = 4; + else if (clip_len[1] > 0) + if (clip_len[0] > 0) + p_dir = 2; + else + p_dir = 6; + else if (clip_len[0] > 0) + p_dir = 3; + else + p_dir = 5; + quad_order = quad_order_lists[p_dir]; + + _fdt_dist = frpipe_dist; + // initialize our wacked out setup + endcaps[0] = center + (fr_map_x * frpipe_dist); + endcaps[2] = center - (fr_map_x * frpipe_dist); + endcaps[1] = center + frpipe_dist; + endcaps[3] = center - frpipe_dist; + clip_len[0] = _fr_y_cen + _fdt_dist - fr_map_y; + clip_len[2] = _fdt_dist - _fr_y_cen; + clip_len[1] = _fr_x_cen + _fdt_dist - fr_map_x; + clip_len[3] = _fdt_dist - _fr_x_cen; + + while (_fdt_dist > 0) // move in from the maximal distance + { + p_dir = (_fdt_dist + 1) >> 1; // how many per diagonal grouping, including + + for (i = 0; i < 4; i++) { + j = quad_order[i]; + loc_code_ptr = &quad_code_to_mask_2[j * QUAD2_DELTA]; + _fdt_x = _fr_x_cen + (diag_stupid[j][0] * _fdt_dist); + _fdt_y = _fr_y_cen + (diag_stupid[j][1] * _fdt_dist); + _fdt_mptr = endcaps[j]; + if (clip_len[j] < 0) { + if (me_subclip(_fdt_mptr) != SUBCLIP_OUT_OF_CONE) { + _fdt_mask = (int)*loc_code_ptr; // the endcap + dumb_hack_for_now(_fdt_x, _fdt_y); + fr_draw_tile(); + } + } + if (clip_len[j] < p_dir) { + loc_code_ptr++; // go to the right fork... + _fdt_mask = (int)*loc_code_ptr; + draw_dir_dir(diag_dirsets[j][0], 1, p_dir - 1); + + // for now just hard restore these three, get cooler later + _fdt_x = _fr_x_cen + (diag_stupid[j][0] * _fdt_dist); + _fdt_y = _fr_y_cen + (diag_stupid[j][1] * _fdt_dist); + _fdt_mptr = endcaps[j]; + + loc_code_ptr++; // and then the left fork + _fdt_mask = (int)*loc_code_ptr; + draw_dir_dir(diag_dirsets[j][1], 1, p_dir - 1); + } + clip_len[j]--; + endcaps[j] -= wall_adds[j]; + } + if ((_fdt_dist & 1) == 0) // do the perfect diagonal + { + int d_dist = _fdt_dist >> 1; + // mprintf("Gonna try diagonals, uh-huh dist %d dd %d\n",_fdt_dist,d_dist); + for (i = 0; i < 4; i++) { // go through each quadrant, dealing + j = quad_order[i]; + loc_code_ptr = &quad_code_to_mask_2[(j * QUAD2_DELTA) + QUAD2_LEFT_FORK]; + _fdt_x = _fr_x_cen + (diag_moves[j][0] * d_dist); + _fdt_y = _fr_y_cen + (diag_moves[j][1] * d_dist); + if ((_fdt_x >= 0) && (_fdt_x < fr_map_x) && (_fdt_y >= 0) && (_fdt_y < fr_map_y)) { + _fdt_mptr = center + diag_map_moves[j] * d_dist; + if (me_subclip(_fdt_mptr) != SUBCLIP_OUT_OF_CONE) { + _fdt_mask = quad_code_to_mask_2[(j * QUAD2_DELTA) + QUAD2_LEFT_FORK]; + dumb_hack_for_now(_fdt_x, _fdt_y); + fr_draw_tile(); + } + } + } + } + _fdt_dist--; + } + + if (me_subclip(center) != SUBCLIP_OUT_OF_CONE) { + _fdt_mask = (int)quad_code_to_mask_2[QUAD2_CENTER]; + _fdt_x = _fr_x_cen; + _fdt_y = _fr_y_cen; + _fdt_mptr = center; + dumb_hack_for_now(_fdt_x, _fdt_y); + fr_draw_tile(); + } +#ifndef CLEAR_AS_WE_GO + for (j = 0; j < 64; j++) + span_count(j) = 0; +#endif + + _fr_ret; +} diff --git a/engine/src/GameSrc/frpts.c b/engine/src/GameSrc/frpts.c new file mode 100644 index 0000000..1234346 --- /dev/null +++ b/engine/src/GameSrc/frpts.c @@ -0,0 +1,187 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * FrPts.c + * + * $Source: r:/prj/cit/src/RCS/frpts.c $ + * $Revision: 1.3 $ + * $Author: dc $ + * $Date: 1994/09/05 06:43:59 $ + * + * Citadel Renderer + * point allocation, delta list maintenance, so on + * + * $Log: frpts.c $ + * Revision 1.3 1994/09/05 06:43:59 dc + * diamond pipe, second wrong pipe, various fixes + * + * Revision 1.2 1994/04/21 06:34:09 dc + * this might fix the wacky memory trash problem + * + * Revision 1.1 1994/01/02 17:12:08 dc + * Initial revision + * + */ + +#include + +#include "map.h" + +#include "frintern.h" +#include "frparams.h" +#include "frflags.h" + +#define ptLst(i) pt_lsts[i] +#define ptLstx(i, x) (*(ptLst(i) + x)) +#define ptRow(i) pt_rowv[i] +#define ptRowx(i, x) (*(ptRow(i) + x)) + +#define pt_set_vec(x, y, z) \ + _pt_vec.xyz[0] = fix_make(x, 0); \ + _pt_vec.xyz[1] = fix_make(0, 0); \ + _pt_vec.xyz[2] = fix_make(z, 0) +#define pt_mk_point(pt) pt = g3_transform_point(&_pt_vec) + +#ifndef MAP_RESIZING +#define _fr_pt_wid (fm_x_sz(moose) + 1) // note we secretly know this arg wont be used +static g3s_phandle pt_lsts[2][_fr_pt_wid]; +static ushort pt_rowv[2][_fr_pt_wid]; +#else +static g3s_phandle *pt_lsts[2]; +static ushort *pt_rowv[2]; +static int _fr_pt_wid = 0; +#endif +g3s_phandle *_fr_ptbase, *_fr_ptnext; /* global place to get points from */ + +// i drank so much tea, i wrote my letters in kanji +// round and round the block i walked, pretending you were with me +int fr_pts_frame_start(void) { + LG_memset(*(pt_rowv + 0), 0xffff, _fr_pt_wid * sizeof(ushort)); + LG_memset(*(pt_rowv + 1), 0xffff, _fr_pt_wid * sizeof(ushort)); + _fr_ret; +} + +int fr_pts_freemem(void) { +#ifdef MAP_RESIZING + int i; + if (_fr_pt_wid == 0) + _fr_ret_val(FR_NO_NEED); + for (i = 0; i < 2; i++) { + free(*(pt_rowv + i)); + free(*(pt_lsts + i)); + } + _fr_pt_wid = 0; +#endif + _fr_ret; +} + +//#pragma disable_message(202) +int fr_pts_resize(int x, int y) // x, y +{ +#ifdef MAP_RESIZING + int i; + fr_pts_freemem(); + _fr_pt_wid = x + 1; + for (i = 0; i < 2; i++) { + if (((*(pt_rowv + i)) = Malloc(x * sizeof(ushort))) == NULL) + _fr_ret_val(FR_NOMEM); + if (((*(pt_lsts + i)) = Malloc(x * sizeof(g3s_phandle))) == NULL) + _fr_ret_val(FR_NOMEM); + } +#endif + _fr_ret; +} +//#pragma enable_message(202) + +// something goofy used by renderer +void dumb_hack_for_now(int x, int y); +void dumb_hack_for_now(int x, int y) { + uchar tran_sv = FALSE, d = TRUE; + ushort *_cur_rowv, *_nxt_rowv; + g3s_phandle *_cur_pt, *_fr_curb, *_fr_curn; + g3s_vector _pt_vec; + + _fr_sdbg(NEW_PTS, mprintf("dhon %d %d...", x, y)); + + if (pt_rowv[0][x] == y) // go from [0] + tran_sv = TRUE; + else if (pt_rowv[1][x] == y) { + tran_sv = TRUE; + d = FALSE; + } else { + if (*(pt_rowv[0] + x) == 0xffff) // create a new one + { + pt_set_vec(x, 0, y); + pt_mk_point(ptLstx(0, x)); + _fr_sdbg(NEW_PTS, mprintf("New pt... ")); + } else { + fix df = fix_make(y - *(pt_rowv[0] + x), 0); + _cur_pt = ptLst(0) + x; + g3_add_delta_z(*_cur_pt, df); + _fr_sdbg(NEW_PTS, mprintf("Move pt by %x at %d last %d ", df, x, *(pt_rowv[0] + x))); + } + } + _fr_sdbg(NEW_PTS, if (tran_sv) mprintf("Found %d...", d)); + + _fr_ptbase = ptLst(d ? 0 : 1); + _cur_rowv = ptRow(d ? 0 : 1) + x; + _fr_ptnext = ptLst(d ? 1 : 0); + _nxt_rowv = ptRow(d ? 1 : 0) + x; + _fr_curb = _fr_ptbase + x; + _fr_curn = _fr_ptnext + x; + _fr_sdbg(NEW_PTS, mprintf("rose %d %d %d %d...", *_cur_rowv, *(_cur_rowv + 1), *_nxt_rowv, *(_nxt_rowv + 1))); + + // now *_fr_ptbase is at (x,y) + if (*(_cur_rowv + 1) != y) { + if (*(_cur_rowv + 1) != 0xffff) { + g3_replace_add_delta_x(*_fr_curb, *(_fr_curb + 1), fix_make(1, 0)); + _fr_sdbg(NEW_PTS, mprintf("x+1 replace")); + } else { + *(_fr_curb + 1) = g3_copy_add_delta_x(*_fr_curb, fix_make(1, 0)); + _fr_sdbg(NEW_PTS, mprintf("x+1 copy")); + } + } + + if (*(_nxt_rowv) != y + 1) { + if (*(_nxt_rowv) != 0xffff) { + g3_replace_add_delta_z(*_fr_curb, *_fr_curn, fix_make(1, 0)); + _fr_sdbg(NEW_PTS, mprintf("y replace")); + } else { + *_fr_curn = g3_copy_add_delta_z(*_fr_curb, fix_make(1, 0)); + _fr_sdbg(NEW_PTS, mprintf("y copy")); + } + } + + if (*(_nxt_rowv + 1) != y + 1) { + if (*(_nxt_rowv + 1) != 0xffff) { + g3_replace_add_delta_x(*_fr_curn, *(_fr_curn + 1), fix_make(1, 0)); + _fr_sdbg(NEW_PTS, mprintf("y+1 replace")); + } else { + *(_fr_curn + 1) = g3_copy_add_delta_x(*_fr_curn, fix_make(1, 0)); + _fr_sdbg(NEW_PTS, mprintf("y+1 copy")); + } + } + + *_cur_rowv = y; + *(_cur_rowv + 1) = y; + *_nxt_rowv = y + 1; + *(_nxt_rowv + 1) = y + 1; + + _fr_sdbg(NEW_PTS, mprintf("\n")); +} diff --git a/engine/src/GameSrc/frsetup.c b/engine/src/GameSrc/frsetup.c new file mode 100644 index 0000000..61aacd8 --- /dev/null +++ b/engine/src/GameSrc/frsetup.c @@ -0,0 +1,906 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * FrSetup.c + * + * $Source: r:/prj/cit/src/RCS/frsetup.c $ + * $Revision: 1.34 $ + * $Author: dc $ + * $Date: 1994/11/25 16:57:55 $ + * + * Citadel Renderer + * setup and modification calls for the view + * + * $Log: frsetup.c $ + * Revision 1.34 1994/11/25 16:57:55 dc + * force remake due to 2d.h change + * + * Revision 1.33 1994/11/22 16:52:37 dc + * setvmode 23 hack camera taking over stereo views + * + * Revision 1.32 1994/11/21 09:02:30 dc + * new olh stuff + * + * Revision 1.31 1994/11/04 13:08:34 xemu + * aspect ratio fix for VFX + * + * Revision 1.30 1994/10/27 04:58:16 xemu + * stero + * + * Revision 1.29 1994/09/06 18:40:01 xemu + * moved fr_closedown + * + * Revision 1.28 1994/09/06 12:04:55 mahk + * game closedown for renderer. + * + * Revision 1.27 1994/09/06 05:03:27 jaemz + * Added star support + * + * Revision 1.26 1994/08/28 16:54:35 kevin + * got rid of old no longer functional mapper switching routines. + * replaced with new 3d mapper switching routines. + * + * Revision 1.25 1994/08/27 02:13:45 kevin + * Min detail level uses clut lighting always. + * + * Revision 1.24 1994/08/16 07:21:37 dc + * new terrain function + * + * Revision 1.23 1994/08/09 20:09:32 tjs + * Oops. + * + * Revision 1.22 1994/08/09 02:45:20 tjs + * End of view radius no longer same as tmap zero. + * + * Revision 1.21 1994/08/04 23:49:18 dc + * tweak some parameters in view structure + * + * Revision 1.20 1994/07/29 18:11:27 roadkill + * *** empty log message *** + * + * Revision 1.19 1994/07/20 23:05:41 dc + * odrop... + * + * Revision 1.18 1994/07/14 01:59:56 xemu + * shaking only half as extreme + * + * Revision 1.17 1994/06/02 23:34:27 kevin + * Changed min detail level to use 1d walls when applicable. + * + * Revision 1.16 1994/05/25 21:36:14 xemu + * default to max detail + * + * Revision 1.15 1994/05/19 14:51:12 kevin + * frsetup now initializes terrian rendering function pointers to + * take advantage of wall and floor special cases when possible. + * + * Revision 1.14 1994/05/11 21:19:21 xemu + * got rid of random setting + * + * Revision 1.13 1994/05/09 06:00:51 dc + * bits for cnvs in place_view, fix global flags stuff... + * + * Revision 1.12 1994/04/23 10:19:47 dc + * ship NO_FAKE_TMAPS fix... + * + * Revision 1.11 1994/04/23 08:12:13 dc + * hack for vcolors... + * + * Revision 1.10 1994/04/14 20:16:45 kevin + * use zoom hack only with doubling. + * + * Revision 1.9 1994/04/14 15:01:28 kevin + * New detail stuff. + * + * Revision 1.8 1994/03/20 21:14:40 xemu + * hack cameras + * + * Revision 1.7 1994/03/13 17:18:56 dc + * obj_block, /#def for blender... + * + * Revision 1.6 1994/03/08 03:27:29 dc + * free the fake tmaps if they exist.... + * + * Revision 1.5 1994/03/03 12:18:11 dc + * pass in canvas to place view + * + */ + +#include // I HATE THIS + +#include "frcamera.h" +#include "fr3d.h" +#include "frtypes.h" +#include "frintern.h" +#include "frshipm.h" +#include "frflags.h" +#include "frparams.h" +#include "player.h" +#include "FrUtils.h" +#include "fullscrn.h" +#include "star.h" + +#ifdef STEREO_SUPPORT +#include +#include +#endif + +#include "OpenGL.h" + +// Internal Prototypes +void fr_tfunc_grab_start(void); +void fr_set_default_ptrs(void); +void _fr_update_context(int det); +void _fr_change_detail(int det); + +// Globals +void (*fr_mouse_hide)(void), (*fr_mouse_show)(void); +int (*fr_get_idx)(void); +uchar (*fr_obj_block)(void *vmptr, uchar *_sclip, int *loc); +void (*fr_clip_start)(uchar headnorth); +void (*fr_rend_start)(void); +grs_bitmap *(*fr_get_tmap)(void); + +// Set by machine type +bool DoubleSize = false; +bool SkipLines = false; + +int (*_fr_glob_draw_call)(void *dest_canvas, void *dest_bm, int x, int y, int flags) = NULL; +void (*_fr_glob_horizon_call)(void *dest_bm, int flags) = NULL; +void (*_fr_glob_render_call)(void *dest_bm, int flags) = NULL; + +//#define DOUBLE_DEF_STUPID_BLEND +#ifdef DOUBLE_DEF_STUPID_BLEND +uchar det_sizing[4][2] = {{0, 0}, {1, 0}, {0, 0}, {0, 0}}; /* setup for detail modes */ +#else +uchar det_sizing[4][2] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}}; /* sizing for detail modes */ +#endif + +/* KLC - no stereo in Mac version +extern uchar inp6d_headset; +extern uchar inp6d_stereo_active; +extern int inp6d_stereo_div; +*/ +fauxrend_context *_fr, *_sr; /* current and default fauxrend contexts */ +uint _fr_glob_flags; /* global flag settings */ +uint _fr_curflags; /* settings for current rendering */ +uchar *_fr_clut_list[4]; /* various lighting fills */ +// the default setup/configuration for fr parameters, modifiable + +fauxrend_parameters _frp = { + {1, 1, 1, 1, 0, 0}, + {0, 0, 0}, + {LIGHT_BITS_TERR | LIGHT_BITS_ANY, 2, {0, 3}, {7, 0}, -fix_make(2, 0x1500), fix_make(7, 0x0000)}, + {1, 2, 0, 3, 0xff, {1, 4, 9}, {1, 4, 9}, 18, 0, 0}, // MLA - 0xff is clear color, used to be 0 + {0, 0, 0, 0}}; +int _fr_last_detail = -1; +int _fr_default_detail = 0; +int _fr_global_detail = 3; +#define FR_USE_GLOBAL_DETAIL 4 + +//======== global initialization +// startup and closedown functions, misc initialization and setup + +// first, for now, we make sure we have full texture context, and eat 8K to boot +#define NO_FAKE_TMAPS + +void fr_closedown(void) { + fr_global_mod_flag(0, 0xFFFFFFFF); // not totally sure this is right. + _frp.lighting.global_mod = 0; +} + +#ifndef NO_FAKE_TMAPS +#include "3dinterp.h" + +grs_bitmap tmap_bm[FAKE_TMAPS]; // this is dumb, yea yea + +void _fr_init_all_tmaps(void) { + uchar *dummy_tm; + int i, x, y; + + for (i = 0; i < FAKE_TMAPS; i++) { + int v1 = (rand() & 0xff), v2 = (rand() & 0xff), v3 = (rand() & 0xff), v4 = (rand() & 0xff); + dummy_tm = (uchar *)malloc(16 * 16); + for (x = 0; x < 16; x++) + for (y = 0; y < 16; y++) + dummy_tm[(x * 16) + y] = + (((x >> 1) + (y >> 1)) & 1) ? ((2 * abs(8 - x)) > y) ? v1 : v2 : ((2 * abs(8 - x)) > y) ? v3 : v4; + gr_init_bm(tmap_bm + i, dummy_tm, BMT_FLAT8, 0, 16, 16); + g3_set_vtext(i, tmap_bm + i); + } +#ifdef RANDOMLY_SET_VCOLORS + for (i = 0; i < 16; i++) // hack hack hack + { + g3_set_vcolor(i, 0x33 + (i << 2)); + } +#endif +} + +void _fr_free_all_tmaps(void) { + int i; + for (i = 0; i < FAKE_TMAPS; i++) + free(tmap_bm[i].bits); +} +#else +#define _fr_init_all_tmaps() +#define _fr_free_all_tmaps() +#endif + +extern int _game_fr_tmap; +void fr_default_mouse(void) {} +int fr_default_idx(void) { return (64 + (((8 * _fdt_y) + _fdt_x) & 0x3f)); } +int fr_pickup_idx(void) { + gr_set_fill_parm(_game_fr_tmap + 1); + return _game_fr_tmap + 1; +} +#ifndef NO_FAKE_TMAPS +grs_bitmap *fr_default_tmap(void) { return &tmap_bm[fr_default_idx() % FAKE_TMAPS]; } +#else +grs_bitmap *fr_default_tmap(void) { return NULL; } +#endif +uchar fr_default_block(void *v, uchar *u, int *i) { return FALSE; } +void fr_default_clip_start(uchar u) {} +void fr_default_rend_start(void) {} + +void fr_set_default_ptrs(void) { + fr_mouse_hide = fr_mouse_show = fr_default_mouse; + fr_get_idx = fr_default_idx; + fr_get_tmap = fr_default_tmap; + fr_obj_block = fr_default_block; + fr_clip_start = fr_default_clip_start; + fr_rend_start = fr_default_rend_start; +} + +// actually init the 3d, as one might expect, also set up global statics for the renderer +void fr_startup(void) { +// should be dynamic and flippable.... +#ifdef STEREO_SUPPORT + g3_init_stereo(FR_PT_CNT, AXIS_ORDER); + g3_set_eyesep(FIX_UNIT / 35); +#else + g3_init(FR_PT_CNT, AXIS_ORDER); +#endif + _fr_init_all_tmaps(); + fr_tables_build(); + _fr_glob_flags = 0; + _fr = _sr = NULL; + fr_set_default_ptrs(); + fr_tfunc_grab_start(); +#ifdef _FR_PIXPROF + pixprof_setup(); +#endif +} + +// lets hit the fucking road +void fr_shutdown(void) { + _fr_free_all_tmaps(); + g3_shutdown(); +} + +// you taught me everything about a poison apple +void fr_set_cluts(uchar *base, uchar *bwclut, uchar *greenclut, uchar *amberclut) { + _fr_clut_list[0] = base; + _fr_clut_list[1] = bwclut; + _fr_clut_list[2] = greenclut; + _fr_clut_list[3] = amberclut; +} + +//======== view control/setup +// variables and functions for construction and maintenance (care and feeding of) views + +// its okay to kill in the name of democracy +g3s_vector viewer_position; +g3s_angvec viewer_orientation; + +// set the default view for NULL argument passing to the system +int fr_set_view(frc *view) { + _chkNull(view); + _sr = (fauxrend_context *)view; + + if (_sr == NULL) { + printf("HOW IS _sr NULL?!\n"); + } + + _fr_ret; +} + +// free the memory (frame buffers+structures) associated with view view +int fr_free_view(frc *view) { + _fr_top(view); + if (_fr != NULL) { + if (_fr == _sr) + _sr = NULL; /* no longer a default rendering context */ + if ((_fr->flags & FR_DOUBLEB_MASK) && ((_fr->flags & FR_OWNBITS_MASK) == 0)) { + // free(_fr->main_canvas.bm.bits); // deal w/any other canvii + DEBUG("%s: Hey! We shouldn't ever be doing this!", __FUNCTION__); + free(_fr->realCanvasPtr); + } + free(_fr); + } + _fr_ret; +} + +int fr_mod_cams(frc *fr, void *v_cam, int mod_fac) { + cams *cam = (cams *)v_cam; + + _fr_top(fr); + _fr->viewer_zoom = fix_mul(_fr->viewer_zoom, mod_fac); + if (_fr->viewer_zoom == 0) + _fr->viewer_zoom = 1; + if ((unsigned long)_fr->viewer_zoom > 0x7fffffff) + _fr->viewer_zoom = 0x7fffffff; + if ((long)cam != -1) { + if (cam == NULL) + _fr->camptr = fr_camera_getdef(); + else + _fr->camptr = cam; + } + _fr_ret; +} +// we put +// eachother +// down +int fr_context_mod_flag(frc *fr, int pflags_on, int pflags_off) // change flags +{ + _fr_top(fr); + _fr->flags &= ~pflags_off; + _fr->flags |= pflags_on; + _fr_ret; +} + +#if _fr_defdbg(ALTCAM) +extern int _fr_altcamx, _fr_altcamy; +int fr_mod_xtracam(frc *fr, void *v_xtra_cam) { + cams *xtra_cam = (cams *)v_xtra_cam; + _fr_top(fr); + _fr->xtracam = xtra_cam; + _fr_ret; +} +#endif + +// we never want the truth.... to be found +int fr_global_mod_flag(int flags_on, int flags_off) { + _fr_glob_flags &= ~flags_off; + _fr_glob_flags |= flags_on; + _fr_ret; +} + +// we are all bigots +// so filled with hatred +// we release our poisons +int fr_mod_size(frc *view, int xc, int yc, int wid, int hgt) // move us around +{ + int detail; + _fr_top(view); + // should leard to deal with built zoom and such, so on + detail = _fr->detail; + fr_place_view(_fr, _fr->camptr, NULL, _fr->flags, _fr->axis, _fr->fov, xc, yc, wid, hgt); + _fr->detail = detail; + _fr_ret; +} + +// like styrofoam +int fr_set_callbacks(frc *view, int (*draw)(void *dstc, void *dstbm, int x, int y, int flg), + void (*horizon)(void *dstbm, int flg), + void (*render)(void *dstbm, int flg)) { // build local context setup for render + _fr_top(view); + _fr->draw_call = draw; + _fr->horizon_call = horizon; + _fr->render_call = render; + _fr_ret; +} + +// like styrofoam +int fr_set_global_callbacks(int (*draw)(void *dstc, void *dstbm, int x, int y, int flg), + void (*horizon)(void *dstbm, int flg), + void (*render)(void *dstbm, int flg)) { // build local context setup for render + _fr_glob_draw_call = draw; + _fr_glob_horizon_call = horizon; + _fr_glob_render_call = render; + _fr_ret; +} + +//--------------------------------------------------------------------------------- +// The big dude that sets up a rendering context. Changed in Mac version to use our already allocated main +// offscreen buffer. +//--------------------------------------------------------------------------------- +frc *fr_place_view(frc *view, void *v_cam, void *cnvs, int pflags, char axis, int fov, int xc, int yc, int wid, + int hgt) { + cams *cam = (cams *)v_cam; + fauxrend_context *fr; + + if (view == NULL) + fr = (fauxrend_context *)malloc(sizeof(fauxrend_context)); + /* KLC - this never actually happens + else // are their other canvii to free here... + { + fr = (fauxrend_context *)view; + if (fr->flags & FR_DOUBLEB_MASK) + // free(fr->main_canvas.bm.bits); + free(fr->realCanvasPtr); + } + */ + + if (pflags & FR_DOUBLEB_MASK) { + if (cnvs == NULL) { + uchar *p; + int rowbytes = (wid + 31) & 0xFFFFFFE0; + + // if the number of cache lines is even, then add a cache line width to improve + // even/odd line usage for vertical drawing. + if (!((rowbytes / 64) & 1)) + rowbytes += 64; + p = (uchar *)malloc(rowbytes * hgt + 32); + if (p == NULL) { + free(fr); + return NULL; + } + fr->realCanvasPtr = (char*)p; + gr_init_canvas(&fr->main_canvas, (uchar *)((ulong)(p + 31) & 0xFFFFFFE0), BMT_FLAT8, wid, hgt); + fr->main_canvas.bm.row = rowbytes; + } else { + // lets pretend we are getting a bitmap instead, eh? + // fr->main_canvas=*((grs_canvas *)cnvs); + gr_init_canvas(&fr->main_canvas, (uchar *)cnvs, BMT_FLAT8, wid, hgt); + pflags |= FR_OWNBITS_MASK; + } + } else { + gr_init_sub_canvas(grd_screen_canvas, &fr->main_canvas, xc, yc, wid, hgt); + } + + gr_init_sub_canvas(grd_screen_canvas, &fr->hack_canvas, xc, yc, wid, hgt); + // set everything and its brothers brother, first inherit global callbacks, then set up axis and window and all + fr->draw_call = _fr_glob_draw_call; + fr->horizon_call = _fr_glob_horizon_call; + fr->render_call = _fr_glob_render_call; + fr->draw_canvas = fr->main_canvas; + fr->axis = axis; + fr->fov = fov; + fr->xtop = xc; + fr->ytop = yc; + fr->xwid = wid; + fr->ywid = hgt; + fr->flags = pflags; + fr->camptr = cam; + if (fov == 0) + fov = FR_DEF_FOV; + if (axis == 0) + axis = FR_DEF_AXIS; + fr->viewer_zoom = g3_get_zoom(axis, build_fix_angle(fov), wid, hgt); + fr->detail = _fr_default_detail; /* default to lowest detail level. */ + fr->last_detail = -1; /* always need to init detail. */ + return (frc *)fr; +} + +void fr_use_global_detail(frc *view) { + if (view != NULL) + ((fauxrend_context *)view)->detail = FR_USE_GLOBAL_DETAIL; +} + +int fr_view_resize(frc *view, int wid, int hgt) { + int nw, nh, nxt, nyt; + int detail; + _fr_top(view); + nw = _fr->xwid; + nh = _fr->ywid; + nxt = _fr->xtop; + nyt = _fr->ytop; /* get base new coors */ + if ((nw + nxt <= wid) && (nh + nyt <= hgt)) + ; /* all ok... */ + else { + if (nw < wid) + nxt = (wid - nw) / 2; + else { + nw = wid; + nxt = 0; + } /* either center old size, or fill new */ + if (nh < hgt) + nyt = (hgt - nh) / 2; + else { + nh = hgt; + nyt = 0; + } /* either center old size, or fill new */ + } + detail = _fr->detail; + fr_place_view(_fr, _fr->camptr, NULL, _fr->flags, _fr->axis, _fr->fov, nxt, nyt, nw, nh); + _fr->detail = detail; + _fr_ret; +} + +int fr_view_full(frc *view, int wid, int hgt) { + int detail; + _fr_top(view); + detail = _fr->detail; + fr_place_view(_fr, _fr->camptr, NULL, _fr->flags, _fr->axis, _fr->fov, 0, 0, wid, hgt); + _fr->detail = detail; + _fr_ret; +} + +void *fr_get_canvas(frc *view) { + _fr_top_cast(view, (void *)); + return &_fr->draw_canvas; +} + +// they're all talking bout, beatles songs +// written a hundred years before they were born +// yea they're all talking bout, the round and round +// but whose got the real, anti-parent culture sound + +// run when context detail has changed. +void _fr_update_context(int det) { + if (_fr->flags & FR_DOUBLEB_MASK) + gr_init_canvas(&_fr->draw_canvas, _fr->main_canvas.bm.bits, BMT_FLAT8, _fr->xwid >> det_sizing[det][0], + _fr->ywid >> det_sizing[det][1]); + else // 0,0 was xtop,ytop + gr_init_sub_canvas(&_fr->main_canvas, &_fr->draw_canvas, 0, 0, _fr->xwid >> det_sizing[det][0], + _fr->ywid >> det_sizing[det][1]); + _fr->last_detail = det; +} + + +void _fr_change_detail(int det) { + // note: pixel_ratio 5 data types before scrw, if order is preserved + int tmpz, fov; +#ifdef DOUBLE_DEF_STUPID_BLEND + if ((det == 1) && (_fr_last_detail != 1)) { /*_fr->viewer_zoom<<=1; */ + *(fix *)((&scrw) - 5) >>= 1; + } + if ((det != 1) && (_fr_last_detail == 1)) { /*_fr->viewer_zoom>>=1; */ + *(fix *)((&scrw) - 5) <<= 1; + } +#endif + switch (det) { + case 0: + g3_set_tmaps_linear(); + gr_set_per_detail_level(GR_LOW_PER_DETAIL); + break; + case 1: + g3_reset_tmaps(); + gr_set_per_detail_level(GR_LOW_PER_DETAIL); + break; + case 2: + g3_reset_tmaps(); + gr_set_per_detail_level(GR_MEDIUM_PER_DETAIL); + break; + case 3: + g3_reset_tmaps(); + gr_set_per_detail_level(GR_HIGH_PER_DETAIL); + } + if (_fr->fov == 0) + fov = FR_DEF_FOV; + else + fov = _fr->fov; + tmpz = g3_get_zoom(FR_DEF_AXIS, fov, _fr->draw_canvas.bm.w, _fr->draw_canvas.bm.h); + // mprintf("Tmpz %x, vz %x (%d <- %d), ps. %x\n",tmpz,_fr->viewer_zoom,_fr->detail,_fr_last_detail,*(fix + // *)((&scrw)-5)); + _fr_last_detail = det; +} + +/* sets global fr + * masks in the global rendering context + * deals with any change in detail settings + */ +int fr_prepare_view(frc *view) { + int det; + _fr_top(view); + + if (_fr == NULL) { + printf("ERROR DID NOT SET VIEW!\n"); + } + + _fr_curflags = _fr_glob_flags | _fr->flags; // for now, simply merge + if (_fr->detail == FR_USE_GLOBAL_DETAIL) + det = _fr_global_detail; + else + det = _fr->detail; + if (_fr_last_detail != det) + _fr_change_detail(det); + if (_fr->last_detail != det) + _fr_update_context(det); + _fr_ret; +} + +#ifdef STEREO_SUPPORT +extern uchar hack_cameras_needed; +#endif + +/* sets the 3d system up based upon the prepared context */ +#define FIXANG_EPS (FIXANG_PI >> 5) +#define FIXANG_MASK (2 * FIXANG_PI - 1) +extern int (*_fr_lit_floor_func)(int, g3s_phandle *, grs_bitmap *); +extern int (*_fr_floor_func)(int, g3s_phandle *, grs_bitmap *); +extern int (*_fr_lit_wall_func)(int, g3s_phandle *, grs_bitmap *); +extern int (*_fr_wall_func)(int, g3s_phandle *, grs_bitmap *); +extern int (*_fr_lit_per_func)(int, g3s_phandle *, grs_bitmap *); +extern int (*_fr_per_func)(int, g3s_phandle *, grs_bitmap *); +int fr_start_view(void) { + g3s_matrix system_matrix; + int use_zoom; + uchar old_cam_type; + int detail; + + if(should_opengl_swap()) { + opengl_start_frame(); + } + + // check detail for canvas sizing + gr_set_canvas(&_fr->draw_canvas); + if (_fr_curflags & FR_PICKUPM_MASK) { + gr_set_fill_type(FILL_SOLID); + gr_set_fill_parm(0); + } else + gr_set_fill_type(FILL_NORM); + if (_fr_curflags & FR_CURVIEW_MASK) + old_cam_type = + fr_camera_modtype(_fr->camptr, ((_fr->flags & FR_CURVIEW_MASK) >> FR_CURVIEW_SHFT) << CAMANG_S, CAMBIT_ANG); + fr_camera_getpos(_fr->camptr); /* loads into camera_last */ + // this is a total hack, goddamn.... + if ((_fr_curflags & FR_SFX_MASK) == FR_SFX_SHAKE) { + fr_camera_last[4] += (rand() & 0x03ff) - 0x200; + fr_camera_last[5] += (rand() & 0x03ff) - 0x200; + } + if (_fr_curflags & FR_CURVIEW_MASK) + fr_camera_modtype(_fr->camptr, old_cam_type & CAMANG_S, CAMBIT_ANG); + viewer_position.xaxis = coor(EYE_X); + viewer_position.yaxis = -coor(EYE_Z); + viewer_position.zaxis = coor(EYE_Y); + viewer_orientation.pitch = ang(EYE_P); + viewer_orientation.bank = ang(EYE_B); + viewer_orientation.head = ang(EYE_H); + + if (_fr->detail == FR_USE_GLOBAL_DETAIL) + detail = _fr_global_detail; + else + detail = _fr->detail; + if (use_opengl()) { + _fr_per_func = _fr_floor_func = _fr_wall_func = opengl_draw_tmap; + _fr_lit_per_func = _fr_lit_floor_func = _fr_lit_wall_func = opengl_light_tmap; + extern int (*g3_tmap_func)(int n, g3s_phandle *vp, grs_bitmap *bm); + g3_tmap_func = opengl_light_tmap; + + opengl_set_viewport(_fr->xtop, _fr->ytop, _fr->xwid, _fr->ywid); + } else if (detail != 0) { + /* check viewer orientation. Use wall/floor/full perspective texture maps accordingly. */ + _fr_lit_per_func = g3_light_tmap; + _fr_per_func = g3_draw_tmap; + if (((viewer_orientation.bank + (FIXANG_EPS / 2)) & FIXANG_MASK) < FIXANG_EPS) { + _fr_lit_floor_func = g3_light_floor_map; + _fr_floor_func = g3_draw_floor_map; + if (((viewer_orientation.pitch + (FIXANG_EPS / 2)) & FIXANG_MASK) < FIXANG_EPS) { + _fr_lit_wall_func = g3_light_wall_map; + _fr_wall_func = g3_draw_wall_map; + } else { + _fr_lit_wall_func = g3_light_tmap; + _fr_wall_func = g3_draw_tmap; + } + } else { + _fr_lit_floor_func = g3_light_tmap; + _fr_floor_func = g3_draw_tmap; + _fr_lit_wall_func = g3_light_tmap; + _fr_wall_func = g3_draw_tmap; + } + g3_reset_tmaps(); + } else { + /* Use linear texture maps unless 1d wall applicable. */ + if ((((viewer_orientation.bank + (FIXANG_EPS / 2)) & FIXANG_MASK) < FIXANG_EPS) && + (((viewer_orientation.pitch + (FIXANG_EPS / 2)) & FIXANG_MASK) < FIXANG_EPS)) { + _fr_lit_wall_func = g3_light_wall_map; + _fr_wall_func = g3_draw_wall_map; + } else { + _fr_lit_wall_func = g3_light_lmap; + _fr_wall_func = g3_draw_lmap; + } + _fr_lit_floor_func = g3_light_lmap; + _fr_floor_func = g3_draw_lmap; + _fr_lit_per_func = g3_light_lmap; + _fr_per_func = g3_draw_lmap; + g3_set_tmaps_linear(); + } + +#ifdef _FR_PIXPROF + gr_start_frame(); +#endif + +#ifdef STEREO_SUPPORT + if (((_fr_curflags & (FR_PICKUPM_MASK | FR_HACKCAM_MASK)) == 0) && inp6d_stereo_active && + ((_fr_curflags & FR_CURVIEW_MASK) == FR_CURVIEW_STRT)) { + extern uchar g3d_stereo; + i6_video(I6VID_FRM_START, NULL); // lets go + i6_video(I6VID_FRM_INFIN, NULL); // begin infinite region + gr_set_canvas(i6d_ss->cf_infin); + // gr_clear(0x78); + // gr_clear(0); + i6_video(I6VID_FRM_STEREO, NULL); // now, the stereo set + gr_set_canvas(i6d_ss->cf_left); + if (i6d_device == I6D_CTM) + grd_cap->aspect <<= 1; + g3_set_eyesep(inp6d_stereo_div / 96); // stereo div is in fix inches... + g3_start_stereo_frame(i6d_ss->cf_right); + // g3d_stereo=0; + } else +#endif + g3_start_frame(); + + /*KLC - stereo + if (inp6d_headset) + { + extern int inp6d_curr_fov; + use_zoom=g3_get_zoom(FR_DEF_AXIS,build_fix_angle(inp6d_curr_fov),320,200); + std_size=2; + } + else*/ + { + use_zoom = _fr->viewer_zoom; + std_size = 1; + } + + if (_frp.faces.cyber) { + // Grab the Dirac from EDMS and copy to the system matrix. + fix *basis = EDMS_Dirac_basis(); + + system_matrix.m1 = basis[0]; + system_matrix.m2 = basis[1]; + system_matrix.m3 = basis[2]; + system_matrix.m4 = basis[3]; + system_matrix.m5 = basis[4]; + system_matrix.m6 = basis[5]; + system_matrix.m7 = basis[6]; + system_matrix.m8 = basis[7]; + system_matrix.m9 = basis[8]; + + g3_set_view_matrix(&viewer_position, &system_matrix, _fr->viewer_zoom); + // g3_set_view_angles(&viewer_position,&viewer_orientation,ANGLE_ORDER,use_zoom); + } else + g3_set_view_angles(&viewer_position, &viewer_orientation, ANGLE_ORDER, use_zoom); + + g3_set_bitmap_scale(fix_make(0, (int)(2048 / 3)), fix_make(0, (int)(2048 / 3))); + // g3_get_FOV(&x_fov,&y_fov); + if (!(_fr->flags & FR_DOUBLEB_MASK)) + (*fr_mouse_hide)(); + else if (_fr->horizon_call) + _fr->horizon_call(&_fr->draw_canvas.bm, _fr_curflags); + // KLC else if (global_fullmap->cyber) + + gr_clear(_frp.view.clear_color); + + // HAX HAX HAX Why is this not 0 already? + // gr_clear(0); + + // now have everything set up for 3d view + // if wacky secondary camera mode, set up +#if _fr_defdbg(ALTCAM) + _fr_sdbg(ALTCAM, + { + fr_camera_getpos(_fr->xtracam); + _fr_altcamx = coor(EYE_X) >> (16); + _fr_altcamy = coor(EYE_Y) >> (16); + }) +#endif + return TRUE; +} + +//#define JUST_SHOW_THE_THING + +/* send the actual frame out a here.... */ +// you're so kind when it serves you well +uchar smooth_double = FALSE; +g3s_vector zvec = {0, 0, 0}; + +extern uchar view360_is_rendering; + +int fr_send_view(void) { + uchar snd_frm = TRUE; + bool ok_to_double; + + // printf("fr_send_view\n"); + + // JAEMZ JAEMZ JAEMZ + // render the stars, if there were + // no stars in this scene it simply returns + // spin it, spin it more when reactor blown + // rotation every 20 minutes, every 1 minute after explosion + // with OpenGL, the starts have already been rendered before everything else + + g3_start_object_angles_y(&zvec, QUESTBIT_GET(0x14) ? player_struct.game_time * 3 : player_struct.game_time / 5); + star_render(); + g3_end_object(); + + g3_end_frame(); + + if(should_opengl_swap()) { + opengl_end_frame(); + } + + // stereo support - closedown ?? +#ifdef STEREO_SUPPORT + if (((_fr_curflags & (FR_PICKUPM_MASK | FR_HACKCAM_MASK)) == 0) && inp6d_stereo_active && + ((_fr_curflags & FR_CURVIEW_MASK) == FR_CURVIEW_STRT)) { + gr_set_canvas(grd_screen_canvas); + if (_fr->draw_call) + snd_frm = _fr->draw_call(grd_screen_canvas, &_fr->draw_canvas.bm, _fr->xtop, _fr->ytop, _fr_curflags); + gr_set_canvas(i6d_ss->cf_left); + (*fr_mouse_show)(); + gr_set_canvas(i6d_ss->cf_right); + (*fr_mouse_show)(); + i6_video(I6VID_FRM_DONE, NULL); + i6_video(I6VID_FRM_COPY, NULL); // send it's butt + gr_set_canvas(i6d_ss->cf_left); + if (i6d_device == I6D_CTM) + grd_cap->aspect >>= 1; + _fr_ret; + } +#endif + + // If we're rendering just the quick mono bitmap (for clicking on items, on-line help, etc), + // then return here. + if (_fr_curflags & FR_PICKUPM_MASK) { + gr_set_canvas(grd_screen_canvas); + _fr_ret; + } + + // Determine if it's okay to double (it's not okay when rendering the 360 view). + ok_to_double = (DoubleSize && !view360_is_rendering); + + // If double sizing, double-size the rendered bitmap onto the intermediate buffer, + // before doing any overlays. Don't do it if rendering the 360 view. + if (ok_to_double) { + gr_set_canvas(&gDoubleSizeOffCanvas); + if (full_game_3d) + FastFullscreenDouble2Canvas(&_fr->draw_canvas.bm, &gDoubleSizeOffCanvas, _fr->xwid, _fr->ywid); + else + FastSlotDouble2Canvas(&_fr->draw_canvas.bm, &gDoubleSizeOffCanvas, _fr->xwid, _fr->ywid); + } + + // Draw the overlays + if (_fr->draw_call) + snd_frm = _fr->draw_call(grd_screen_canvas, (ok_to_double) ? &gDoubleSizeOffCanvas.bm : &_fr->draw_canvas.bm, + _fr->xtop, _fr->ytop, _fr_curflags); + + if (snd_frm) { + (*fr_mouse_hide)(); // This actually draws the mouse into the rendered canvas. + gr_set_canvas(grd_screen_canvas); // Now set us to the screen canvas. + + if (_fr_curflags & FR_DOUBLEB_MASK) // If we're double-buffered (which we always are) + { + // if (_fr_curflags&FR_DOHFLIP_MASK) // Does this ever occur? + // gr_hflip_ubitmap(&_fr->draw_canvas.bm,_fr->xtop,_fr->ytop); + if (view360_is_rendering) + gr_ubitmap(&_fr->draw_canvas.bm, _fr->xtop, _fr->ytop); + else { + if (ok_to_double) // If double-sizing, just copy the already double-sized + { // scene from the temp offscreen canvas to the screen. + if (full_game_3d) + Fast_FullScreen_Copy(&gDoubleSizeOffCanvas.bm); + else + Fast_Slot_Copy(&gDoubleSizeOffCanvas.bm); + } else // For high-res, just copy from the draw canvas. + { + if (full_game_3d) + Fast_FullScreen_Copy(&_fr->draw_canvas.bm); + else + Fast_Slot_Copy(&_fr->draw_canvas.bm); + } + } + } + (*fr_mouse_show)(); + } else + gr_set_canvas(grd_screen_canvas); + + _fr_ret; +} diff --git a/engine/src/GameSrc/frtables.c b/engine/src/GameSrc/frtables.c new file mode 100644 index 0000000..8464921 --- /dev/null +++ b/engine/src/GameSrc/frtables.c @@ -0,0 +1,528 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * FrTables.c + * + * $Source: n:/project/cit/src/RCS/frtables.c $ + * $Revision: 1.2 $ + * $Author: dc $ + * $Date: 1994/01/12 22:06:34 $ + * + * Citadel Renderer + * tables for the tile definitions/quadrant codes + * + * $Log: frtables.c $ + * Revision 1.2 1994/01/12 22:06:34 dc + * facelet hacking, new terrain function setup + * + * Revision 1.1 1994/01/02 17:12:13 dc + * Initial revision + * + */ + +#include "frintern.h" +#include "frtables.h" +#include "tilename.h" + +//======== Points +pt_mods pt_deref[FRPTSUNIQUE] = { + {0, 0, 0}, + {0, 1, fix_make(0, 0x2000)}, + {0, 1, fix_make(0, 0x8000)}, + {0, 1, fix_make(0, 0xE000)}, + {1, 0, 0}, + {3, 1, fix_make(0, 0x2000)}, + {3, 1, fix_make(0, 0x8000)}, + {3, 1, fix_make(0, 0xE000)}, + {2, 0, 0}, + {0, 2, fix_make(0, 0x2000)}, + {0, 2, fix_make(0, 0x8000)}, + {0, 2, fix_make(0, 0xE000)}, + {3, 0, 0}, + {1, 2, fix_make(0, 0x2000)}, + {1, 2, fix_make(0, 0x8000)}, + {1, 2, fix_make(0, 0xE000)}, + {0, 1, fix_make(0, FROCTNUM)}, + {0, 1, fix_make(0, 0x10000 - FROCTNUM)}, + {3, 1, fix_make(0, FROCTNUM)}, + {3, 1, fix_make(0, 0x10000 - FROCTNUM)}, + {0, 2, fix_make(0, FROCTNUM)}, + {0, 2, fix_make(0, 0x10000 - FROCTNUM)}, + {1, 2, fix_make(0, FROCTNUM)}, + {1, 2, fix_make(0, 0x10000 - FROCTNUM)} +}; + +#define _uv1 0x100 +#define _uva 0x020 +#define _uvb (FROCTNUM >> 8) +#define _uvc 0x080 +#define _uvd ((0x10000 - FROCTNUM) >> 8) +#define _uve 0x0E0 +// points verse rotation vs unique +ushort pt_uv[FRPTSUNIQUE][4][2] = { + {{0, _uv1}, {_uv1, _uv1}, {_uv1, 0}, {0, 0}}, + {{0, _uve}, {_uve, _uv1}, {_uv1, _uva}, {_uva, 0}}, + {{0, _uvc}, {_uvc, _uv1}, {_uv1, _uvc}, {_uvc, 0}}, + {{0, _uva}, {_uva, _uv1}, {_uv1, _uve}, {_uve, 0}}, + {{0, 0}, {0, _uv1}, {_uv1, _uv1}, {_uv1, 0}}, + {{_uv1, _uve}, {_uve, 0}, {0, _uva}, {_uva, _uv1}}, + {{_uv1, _uvc}, {_uvc, 0}, {0, _uvc}, {_uvc, _uv1}}, + {{_uv1, _uva}, {_uva, 0}, {0, _uve}, {_uve, _uv1}}, + {{_uv1, 0}, {0, 0}, {0, _uv1}, {_uv1, _uv1}}, + {{_uva, _uv1}, {_uv1, _uve}, {_uve, 0}, {0, _uva}}, + {{_uvc, _uv1}, {_uv1, _uvc}, {_uvc, 0}, {0, _uvc}}, + {{_uve, _uv1}, {_uv1, _uva}, {_uva, 0}, {0, _uve}}, + {{_uv1, _uv1}, {_uv1, 0}, {0, 0}, {0, _uv1}}, + {{_uva, 0}, {0, _uve}, {_uve, _uv1}, {_uv1, _uva}}, + {{_uvc, 0}, {0, _uvc}, {_uvc, _uv1}, {_uv1, _uvc}}, + {{_uve, 0}, {0, _uva}, {_uva, _uv1}, {_uv1, _uve}}, + {{0, _uvd}, {_uvd, _uv1}, {_uv1, _uvb}, {_uvb, 0}}, + {{0, _uvb}, {_uvb, _uv1}, {_uv1, _uvd}, {_uvd, 0}}, + {{_uv1, _uvd}, {_uvd, 0}, {0, _uvb}, {_uvb, _uv1}}, + {{_uv1, _uvb}, {_uvb, 0}, {0, _uvd}, {_uvd, _uv1}}, + {{_uvb, _uv1}, {_uv1, _uvd}, {_uvd, 0}, {0, _uvb}}, + {{_uvd, _uv1}, {_uv1, _uvb}, {_uvb, 0}, {0, _uvd}}, + {{_uvb, 0}, {0, _uvd}, {_uvd, _uv1}, {_uv1, _uvb}}, + {{_uvd, 0}, {0, _uvb}, {_uvb, _uv1}, {_uv1, _uvd}} +}; + +fix pt_offs[FRPTSOFFS] = {fix_make(0, 0), fix_make(0, 0x2000), fix_make(0, FROCTNUM), fix_make(0, 0x8000), + fix_make(0, -FROCTNUM), fix_make(0, 0xE000), fix_make(1, 0), 0xffffffff}; + +uchar pt_from_faceoff[4][FRPTSOFFS] = {{0x4, 0xD, 0x16, 0xE, 0x17, 0xF, 0x8, 0x4}, + {0x8, 0x7, 0x13, 0x6, 0x12, 0x5, 0xC, 0x8}, + {0xC, 0xB, 0x15, 0xA, 0x14, 0x9, 0x0, 0xC}, + {0x0, 0x1, 0x10, 0x2, 0x11, 0x3, 0x4, 0x0}}; + +// #define fxptoff(n) fix_make(0,0x2000*n) +// {fxptoff(0),fxptoff(1),fxptoff(2),fxptoff(3),fxptoff(4),fxptoff(5),fxptoff(6),fxptoff(7)}; +// {fix_make(0,0),fix_make(0,0x2000),fix_make(0,0x8000),fix_make(0,0xe000)}; + +//======== WallstoPts +// convience macros for building the pt_wall table +#define zBo FRPTSZBASE +#define zBU FRPTSZBASEU +#define zTo FRPTSZCEIL +#define zTD FRPTSZCEILD +// actual wall list builders +#define wall_pt(z1, z2, z3, z4, h1, h2, h3, h4) \ + { z1 | h1, z2 | h2, z3 | h3, z4 | h4 } +#define norm_wall(lft, rgt) wall_pt(zTo, zTo, zBo, zBo, lft, rgt, rgt, lft) +#define oct_wall(tl, tr, br, bl) \ + wall_pt(zTo, zTo, zTD, zTD, tl, tr, br, bl), wall_pt(zTD, zTD, zBU, zBU, bl, br, br, bl), \ + wall_pt(zBU, zBU, zBo, zBo, bl, br, tr, tl) +#define parm_walls(lft, rgt) \ + wall_pt(zBU, zBU, zBo, zBo, lft, rgt, rgt, lft), wall_pt(zTo, zTo, zTD, zTD, lft, rgt, rgt, lft) + +WallsToPts wall_pts[FRWALLPTSCNT] = { + // main diagonals + norm_wall(12, 4), norm_wall(0, 8), norm_wall(8, 0), norm_wall(4, 12), + // slope 1/2 quarter diagonals + norm_wall(6, 0), norm_wall(0, 6), norm_wall(8, 2), norm_wall(2, 8), + norm_wall(12, 2), norm_wall(2, 12), norm_wall(6, 4), norm_wall(4, 6), + // slope 2 quarter diagonals + norm_wall(14, 0), norm_wall(0, 14), norm_wall(8, 10), norm_wall(10, 8), + norm_wall(10, 4), norm_wall(4, 10), norm_wall(12, 14), norm_wall(14, 12), + // halve tiles + norm_wall(6, 2), norm_wall(2, 6), norm_wall(14, 10), norm_wall(10, 14), + // one foot walls + norm_wall(5, 1), norm_wall(3, 7), norm_wall(15, 11), norm_wall(9, 13), + // triangle + wall_pt(zTo, zTo, zBo, zBo, 10, 14, 4, 0), + wall_pt(zTo, zTo, zBo, zBo, 14, 10, 12, 8), + wall_pt(zTo, zTo, zBo, zBo, 6, 2, 0, 12), + wall_pt(zTo, zTo, zBo, zBo, 2, 6, 8, 4), + // oct NS (note each macro expands to 3 walls) + oct_wall(0x14, 0x16, 4, 0), oct_wall(0x17, 0x15, 12, 8), + oct_wall(0x12, 0x10, 0, 12), oct_wall(0x11, 0x13, 8, 4), + // parm diagonals, (each is 2 walls) + parm_walls(12, 4), parm_walls(0, 8), parm_walls(8, 0), parm_walls(4, 12), + // normal edges (n,e,s,w) + norm_wall(4, 8), norm_wall(8, 12), norm_wall(12, 0), norm_wall(0, 4) +}; + +//======== TilestoWalls +// shorthands for tile tables +#define wN FRWALLNORTH +#define wE FRWALLEAST +#define wS FRWALLSOUTH +#define wW FRWALLWEST +#define wI(c) (c) + +#define tTw(ext, intcnt, intbase) \ + { ext | wI(intcnt), intbase } + +TilesToWalls tile_walls[FRTILEWALLCNT] = { + // solid, open + tTw(0, 0, 0), tTw(wN | wE | wS | wW, 0, 0), + // main diagonals + tTw(wE | wS, 1, 1), tTw(wS | wW, 1, 3), tTw(wW | wN, 1, 2), tTw(wN | wE, 1, 0), + // basic slopes + tTw(wN | wE | wS | wW, 0, 0), tTw(wN | wE | wS | wW, 0, 0), + tTw(wN | wE | wS | wW, 0, 0), tTw(wN | wE | wS | wW, 0, 0), + // zany slopes + tTw(wN | wE | wS | wW, 0, 0), tTw(wN | wE | wS | wW, 0, 0), + tTw(wN | wE | wS | wW, 0, 0), tTw(wN | wE | wS | wW, 0, 0), + tTw(wN | wE | wS | wW, 0, 0), tTw(wN | wE | wS | wW, 0, 0), + tTw(wN | wE | wS | wW, 0, 0), tTw(wN | wE | wS | wW, 0, 0), + // diagonal splits + tTw(wN | wE | wS | wW, 2, 46), tTw(wN | wE | wS | wW, 2, 50), + tTw(wN | wE | wS | wW, 2, 48), tTw(wN | wE | wS | wW, 2, 44), + // oct + tTw(wN | wS, 6, 32), tTw(wE | wW, 6, 38), + // tri + tTw(wN | wS, 2, 28), tTw(wE | wW, 2, 30), + // heinous 1/4 diagonals + tTw(wE | wS | wW, 1, 11), tTw(wN | wE | wS, 1, 16), tTw(wN | wE | wW, 1, 4), tTw(wN | wE | wS, 1, 13), + tTw(wE | wS | wW, 1, 7), tTw(wN | wS | wW, 1, 14), tTw(wN | wE | wW, 1, 8), tTw(wN | wS | wW, 1, 19), + // heinous 3/4 diagonals, note strange symmetry, ie. 1and4 are -1, 2and3 +1, faces rev. + tTw(wN | wE, 1, 10), tTw(wS | wW, 1, 17), tTw(wE | wS, 1, 5), tTw(wN | wW, 1, 12), + tTw(wN | wW, 1, 6), tTw(wE | wS, 1, 15), tTw(wS | wW, 1, 9), tTw(wN | wE, 1, 18), + // vertical split + tTw(wN | wE | wS | wW, 0, 0), + // halves + tTw(wE | wS | wW, 1, 21), tTw(wN | wS | wW, 1, 22), tTw(wN | wE | wW, 1, 20), tTw(wN | wE | wS, 1, 23), + // thin walls + tTw(wE | wS | wW, 1, 25), tTw(wN | wS | wW, 1, 26), tTw(wN | wE | wW, 1, 24), tTw(wN | wE | wS, 1, 27) +}; + +//======== TilesToFloors + +#define zNo (0) +#define zPa (FRFLRDATA_ZMOD) + +#define flg2 (FRFLRFLG_2ELEM) +#define flgP (FRFLRFLG_USEPR) +#define flgNT (FRFLRFLG_NOTOP) + +#define fF3(p1, p2, p3) \ + { 0, 3, 0x##p1, 0x##p2, 0x##p3, 0, 0, 0 } +#define fF4(p1, p2, p3, p4) \ + { 0, 4, 0x##p1, 0x##p2, 0x##p3, 0x##p4, 0, 0 } +#define fT4(p1, p2, p3, p4) \ + { flgNT, 4, 0x##p1, 0x##p2, 0x##p3, 0x##p4, 0, 0 } + +#define fS3cc(pl, p2, p3, p4, p5) \ + { flg2 | flgP, 3, zPa | (0x##p2), zPa | (0x##p3), 0x##pl, zPa | (0x##p4), zPa | (0x##p5), 0x##pl } +#define fS3cv(ph, p2, p3, p4, p5) \ + { flg2 | flgP, 3, 0x##p2, 0x##p3, zPa | (0x##ph), 0x##p4, 0x##p5, zPa | (0x##ph) } +#define fS4(p1, p2, p3, p4) \ + { flgP, 4, zPa | (0x##p1), zPa | (0x##p2), 0x##p3, 0x##p4, 0, 0 } +#define fSspl(pla, pha, pm1, pm2) \ + { flg2 | flgP, 3, 0x##pla, 0x##pm1, 0x##pm2, zPa | (0x##pha), zPa | (0x##pm2), zPa | (0x##pm1) } + +TilesToFloors tile_floors[FRTILEFLOORCNT] = { + // solid, open + {0, 0, 0, 0, 0, 0, 0, 0}, fF4(4, 8, C, 0), + // main diagonals + fF3(8, C, 0), fF3(C, 0, 4), fF3(4, 8, 0), fF3(4, 8, C), + // basic slopes + fS4(4, 8, C, 0), fS4(8, C, 0, 4), fS4(C, 0, 4, 8), fS4(0, 4, 8, C), + // zany slopes + fS3cc(C, 4, 8, 0, 4), fS3cc(0, 8, C, 4, 8), fS3cc(4, C, 0, 8, C), fS3cc(8, 0, 4, C, 0), + fS3cv(C, 4, 8, 0, 4), fS3cv(0, 8, C, 4, 8), fS3cv(4, C, 0, 8, C), fS3cv(8, 0, 4, C, 0), + // diagonal splits + fSspl(C, 4, 0, 8), fSspl(0, 8, 4, C), fSspl(4, C, 8, 0), fSspl(8, 0, C, 4), + // oct + fF4(16, 17, 15, 14), fF4(13, 12, 10, 11), + // tri + fT4(4, 8, C, 0), fT4(4, 8, C, 0), + // heinous 1/4 diagonals + fF4(4, 6, C, 0), fF4(4, 8, C, A), fF4(4, 8, 6, 0), fF4(E, 8, C, 0), + fF4(8, C, 0, 2), fF4(4, 8, A, 0), fF4(4, 8, C, 2), fF4(E, C, 0, 4), + // heinous 3/4 diagonals + fF3(4, 8, 6), fF3(4, A, 0), fF3(6, C, 0), fF3(4, E, 0), + fF3(4, 8, 2), fF3(8, C, A), fF3(2, C, 0), fF3(8, C, E), + // vertical split + {FRFLRFLG_DBL, 4, 4, 8, 0xC, 0, 0, 0}, + // halves + fF4(2, 6, C, 0), fF4(4, E, A, 0), fF4(4, 8, 6, 2), fF4(E, 8, C, A), + // thin walls + fF4(3, 7, C, 0), fF4(4, F, B, 0), fF4(4, 8, 5, 1), fF4(D, 8, C, 9) +}; + +//======== Normals + +//#define _fp1 fix_make(1,0) +//#define _fpdg fix_make(0,46340) +//#define _fphx fix_make(0,29308) +//#define _fphy fix_make(0,58616) + +#define ff1 0 // fix_make(1,0) +#define fhy 1 // fix_make(0,58616) +#define fdg 2 // fix_make(0,46340) +#define fhx 3 // fix_make(0,29308) + +#define snp 4 +#define sng 0 + +#define pff1 (ff1 | snp) +#define nff1 (ff1 | sng) +#define pfhy (fhy | snp) +#define nfhy (fhy | sng) +#define pfdg (fdg | snp) +#define nfdg (fdg | sng) +#define pfhx (fhx | snp) +#define nfhx (fhx | sng) +#define npnp (8) + +#define zm 0x4000 +#define ym 0x2000 +#define xm 0x1000 + +#define hZ (0x2 << 12) +#define hY (0x1 << 12) +#define hYZ (0x3 << 12) +#define hX (0x4 << 12) +#define hXZ (0x6 << 12) +#define hXY (0x5 << 12) +#define hXYZ (0x7 << 12) + +#define mk_mnorm(m, xn, yn, zn) (m | (xn << 8) | (yn << 4) | zn) +#define mk_hnorm(h, xn, yn, zn) (h | (xn << 8) | (yn << 4) | zn) +#define mk_norm(xn, yn, zn) ((xn << 8) | (yn << 4) | zn) + +fix fr_norm_elements[9] = {-fix_make(1, 0), -fix_make(0, 58616), -fix_make(0, 46340), + -fix_make(0, 29308), fix_make(1, 0), fix_make(0, 58616), + fix_make(0, 46340), fix_make(0, 29308), fix_make(0, 0)}; + +ushort fr_wnorm_list[FRWALLPTSCNT] = { + // main diagonals + mk_hnorm(hXY, pfdg, pfdg, npnp), mk_hnorm(hXY, pfdg, nfdg, npnp), mk_hnorm(hXY, nfdg, pfdg, npnp), + mk_hnorm(hXY, nfdg, nfdg, npnp), + // slope 1/2 quarter diagonals + mk_hnorm(hXY, nfhx, pfhy, npnp), mk_hnorm(hXY, pfhx, nfhy, npnp), mk_hnorm(hXY, nfhx, pfhy, npnp), + mk_hnorm(hXY, pfhx, nfhy, npnp), mk_hnorm(hXY, pfhx, pfhy, npnp), mk_hnorm(hXY, nfhx, nfhy, npnp), + mk_hnorm(hXY, pfhx, pfhy, npnp), mk_hnorm(hXY, nfhx, nfhy, npnp), + // slope 2 quarter diagonals + mk_hnorm(hXY, nfhy, pfhx, npnp), mk_hnorm(hXY, pfhy, nfhx, npnp), mk_hnorm(hXY, nfhy, pfhx, npnp), + mk_hnorm(hXY, pfhy, nfhx, npnp), mk_hnorm(hXY, pfhy, pfhx, npnp), mk_hnorm(hXY, nfhy, nfhx, npnp), + mk_hnorm(hXY, pfhy, pfhx, npnp), mk_hnorm(hXY, nfhy, nfhx, npnp), + // halve tiles + mk_hnorm(hY, npnp, pff1, npnp), mk_hnorm(hY, npnp, nff1, npnp), mk_hnorm(hX, nff1, npnp, npnp), + mk_hnorm(hX, pff1, npnp, npnp), + // one foot walls + mk_hnorm(hY, npnp, pff1, npnp), mk_hnorm(hY, npnp, nff1, npnp), mk_hnorm(hX, nff1, npnp, npnp), + mk_hnorm(hX, pff1, npnp, npnp), + // triangle + mk_hnorm(hXZ, pfhy, npnp, pfhx), mk_hnorm(hXZ, nfhy, npnp, pfhx), mk_hnorm(hYZ, npnp, pfhy, pfhx), + mk_hnorm(hYZ, npnp, nfhy, pfhx), + // oct NS + mk_hnorm(hXZ, pfdg, npnp, pfdg), mk_hnorm(hX, pff1, npnp, npnp), mk_hnorm(hXZ, pfdg, npnp, nfdg), + mk_hnorm(hXZ, nfdg, npnp, pfdg), mk_hnorm(hX, nff1, npnp, npnp), mk_hnorm(hXZ, nfdg, npnp, nfdg), + mk_hnorm(hYZ, npnp, pfdg, pfdg), mk_hnorm(hY, npnp, pff1, npnp), mk_hnorm(hYZ, npnp, pfdg, nfdg), + mk_hnorm(hYZ, npnp, nfdg, pfdg), mk_hnorm(hY, npnp, nff1, npnp), mk_hnorm(hYZ, npnp, nfdg, nfdg), + // parm diagonals, (each is 2 walls) + mk_hnorm(hXY, pfdg, pfdg, npnp), mk_hnorm(hXY, pfdg, nfdg, npnp), mk_hnorm(hXY, nfdg, pfdg, npnp), + mk_hnorm(hXY, nfdg, nfdg, npnp), mk_hnorm(hXY, pfdg, pfdg, npnp), mk_hnorm(hXY, pfdg, nfdg, npnp), + mk_hnorm(hXY, nfdg, pfdg, npnp), mk_hnorm(hXY, nfdg, nfdg, npnp), + // normal edges (n,s,e,w) + mk_hnorm(hY, npnp, nff1, npnp), mk_hnorm(hX, nff1, npnp, npnp), mk_hnorm(hY, npnp, pff1, npnp), + mk_hnorm(hX, pff1, npnp, npnp) +}; + +// direction the vector heads, ie the low side +#define slpN FRFNORM_SLPN +#define slpE FRFNORM_SLPE +#define slpS FRFNORM_SLPS +#define slpW FRFNORM_SLPW +#define vzero FRFNORM_VZERO +#define vfull FRFNORM_VFULL +#define sl2N (0 << 4) +#define sl2E (1 << 4) +#define sl2S (2 << 4) +#define sl2W (3 << 4) +#define vrealfull ((vfull << 4) | vfull) + +uchar fr_fnorm_list[FRTILEFLOORCNT] = { + // solid, open + vfull, vfull, + // main diagonals + vfull, vfull, vfull, vfull, + // basic slopes + slpS, slpW, slpN, slpE, + // zany slopes + sl2E | slpS, sl2S | slpW, sl2W | slpN, sl2N | slpE, + sl2W | slpN, sl2N | slpE, sl2E | slpS, sl2S | slpW, + // diagonal splits + vrealfull, vrealfull, vrealfull, vrealfull, + // oct + vfull, vfull, + // tri + vfull, vfull, + // heinous 1/4 diagonals + vfull, vfull, vfull, vfull, vfull, vfull, vfull, vfull, + // heinous 3/4 diagonals + vfull, vfull, vfull, vfull, vfull, vfull, vfull, vfull, + // vertical split + vfull, + // halves + vfull, vfull, vfull, vfull, + // thin walls + vfull, vfull, vfull, vfull +}; + +//======== Obstruct + +#define ZP (1) +#define lS (7) +#define rS (6) +#define foNul (0xff) // secret no freespace, ie. cant get through +#define foClr (0x06) // pt0 to 6, ie. fully empty +#define foLft (0x03) +#define foRgt (0x1E) + +#define fo(l, r) ((l << FO_L_SHFT) | r) +#define fo4(l1, r1, l2, r2, l3, r3, l4, r4) { fo(l1, r1), fo(l2, r2), fo(l3, r3), fo(l4, r4) } +#define zclr(p1, p2) (p1 << lS | p2 << rS | foClr) +#define foF(p1, p2, p3, p4, p5, p6, p7, p8) { zclr(p1, p2), zclr(p3, p4), zclr(p5, p6), zclr(p7, p8) } +#define fofo(f1, f2, f3, f4) { f1, f2, f3, f4 } +#define foC() { foClr, foClr, foClr, foClr } +#define foN() { foNul, foNul, foNul, foNul } + +uchar face_obstruct[FRFACEOBSTRUCTCNT][FACE_CNT] = { + // solid, open + foN(), foC(), + // main diagonals + fofo(foNul, foClr, foClr, foNul), + fofo(foNul, foNul, foClr, foClr), + fofo(foClr, foNul, foNul, foClr), + fofo(foClr, foClr, foNul, foNul), + // basic slopes + foF(ZP, ZP, ZP, 0, 0, 0, 0, ZP), + foF(0, ZP, ZP, ZP, ZP, 0, 0, 0), + foF(0, 0, 0, ZP, ZP, ZP, ZP, 0), + foF(ZP, 0, 0, 0, 0, ZP, ZP, ZP), + // zany slopes + foF(ZP, ZP, ZP, 0, 0, ZP, ZP, ZP), + foF(ZP, ZP, ZP, ZP, ZP, 0, 0, ZP), + foF(0, ZP, ZP, ZP, ZP, ZP, ZP, 0), + foF(ZP, 0, 0, ZP, ZP, ZP, ZP, ZP), + foF(0, 0, 0, ZP, ZP, 0, 0, 0), + foF(0, 0, 0, 0, 0, ZP, ZP, 0), + foF(ZP, 0, 0, 0, 0, 0, 0, ZP), + foF(0, ZP, ZP, 0, 0, 0, 0, 0), + // diagonal splits + foF(ZP, ZP, 0, 0, 0, 0, ZP, ZP), + foF(ZP, ZP, ZP, ZP, 0, 0, 0, 0), + foF(0, 0, ZP, ZP, ZP, ZP, 0, 0), + foF(0, 0, 0, 0, ZP, ZP, ZP, ZP), + // oct + fofo(foClr, foNul, foClr, foNul), + fofo(foNul, foClr, foNul, foClr), + // tri + fofo(foClr, foNul, foClr, foNul), + fofo(foNul, foClr, foNul, foClr), + // heinous 1/4 diagonals + fofo(foNul, foRgt, foClr, foClr), + fofo(foClr, foClr, foLft, foNul), + fofo(foClr, foLft, foNul, foClr), + fofo(foRgt, foClr, foClr, foNul), + fofo(foNul, foClr, foClr, foLft), + fofo(foClr, foNul, foRgt, foClr), + fofo(foClr, foClr, foNul, foRgt), + fofo(foLft, foNul, foClr, foClr), + // heinous 3/4 diagonals + fofo(foClr, foLft, foNul, foNul), + fofo(foNul, foNul, foRgt, foClr), + fofo(foNul, foRgt, foClr, foNul), + fofo(foLft, foNul, foNul, foClr), + fofo(foClr, foNul, foNul, foRgt), + fofo(foNul, foClr, foLft, foNul), + fofo(foNul, foNul, foClr, foLft), + fofo(foRgt, foClr, foNul, foNul), + // vertical split + foC(), + // halves + fofo(foNul, foRgt, foClr, foLft), + fofo(foLft, foNul, foRgt, foClr), + fofo(foClr, foLft, foNul, foRgt), + fofo(foRgt, foClr, foLft, foNul), + // thin walls + fo4(0x1f, 0xf, 1, 6, 0, 6, 0, 5), + fo4(0, 5, 0x1f, 0xf, 1, 6, 0, 6), + fo4(0, 6, 0, 5, 0x1f, 0xf, 1, 6), + fo4(1, 6, 0, 6, 0, 5, 0x1f, 0xf) +}; + +//======== Merger +// for floor point mods, masking to floor code +// based on actual instance + +#define gtF (0x00) +#define gtC (0x40) +#define nFlp (0x00) +#define dFlp (0x80) +#define nFlt (0x80) +#define iFlt (0x00) + +#define mm(x, y) \ + { x | 0, (uchar)(y | (~nFlt)) } + +// note we flip match, because in new data structure, we go down from ceiling always + +// mirror by flr/ciel by xor/and +uchar merge_masks[5][2][2] = { + {mm(gtF | nFlp, nFlt), mm(gtC | dFlp, nFlt)}, // MAP_MATCH + {mm(gtF | nFlp, nFlt), mm(gtC | nFlp, nFlt)}, // MAP_MIRROR + {mm(gtF | nFlp, nFlt), mm(gtC | nFlp, iFlt)}, // MAP_CFLAT + {mm(gtF | nFlp, iFlt), mm(gtC | dFlp, nFlt)}, // MAP_FFLAT + {mm(gtF | nFlp, nFlt), mm(gtC | nFlp, nFlt)} // secret no parameter +}; + +#define nfFlp (0x00) +#define dfFlp (0xC0) +#define nfFlt (0xC0) +#define ifFlt (0x00) + +#define mf(x, y) \ + { x | 0, (uchar)(y | (~nfFlt)) } + +uchar mmask_facelet[5][2][2] = { + {mf(nfFlp, nfFlt), mf(dfFlp, nfFlt)}, // MAP_MATCH + {mf(nfFlp, nfFlt), mf(nfFlp, nfFlt)}, // MAP_MIRROR + {mf(nfFlp, nfFlt), mf(nfFlp, ifFlt)}, // MAP_CFLAT + {mf(nfFlp, ifFlt), mf(dfFlp, nfFlt)}, // MAP_FFLAT + {mf(nfFlp, nfFlt), mf(nfFlp, nfFlt)} // secret no parameter +}; + +// filled in functionally +fix fo_unpack[FOBASECODES][2]; +fix fo_anti_unpack[FOBASECODES][2]; +uchar face_baseobstruct[FRFACEOBSTRUCTCNT][FACE_CNT]; + +int fr_tables_build(void) { + int l, r, i, j; + for (l = 0; l < 7; l++) + for (r = 0; r < 7; r++) { + fo_unpack[(l << FO_L_SHFT) | r][0] = pt_offs[l]; + fo_unpack[(l << FO_L_SHFT) | r][1] = pt_offs[r]; + fo_anti_unpack[(l << FO_L_SHFT) | r][0] = fix_make(1, 0) - pt_offs[l]; + fo_anti_unpack[(l << FO_L_SHFT) | r][1] = fix_make(1, 0) - pt_offs[r]; + } + for (i = 0; i < FRFACEOBSTRUCTCNT; i++) + for (j = 0; j < FACE_CNT; j++) { + if ((j == 1) || (j == 2)) + face_baseobstruct[i][j] = + (face_obstruct[i][j] ^ 0x3F) - 0x9; /* xors to get 7complement, then sub 0b01001 to get 6comp */ + else + face_baseobstruct[i][j] = face_obstruct[i][j]; + } + _fr_ret; +} diff --git a/engine/src/GameSrc/frterr.c b/engine/src/GameSrc/frterr.c new file mode 100644 index 0000000..b59d5c6 --- /dev/null +++ b/engine/src/GameSrc/frterr.c @@ -0,0 +1,2136 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * FrTerr.c + * + * $Source: r:/prj/cit/src/RCS/frterr.c $ + * $Revision: 1.27 $ + * $Author: jaemz $ + * $Date: 1994/09/06 05:03:37 $ + * + * Citadel Renderer + * terrain parsing, internal and external faces, facelet + * physics interaction? + */ + +#include +#include + +#include "map.h" +#include "mapflags.h" +#include "frintern.h" +#include "frtables.h" +#include "tilename.h" +#include "frparams.h" +#include "frflags.h" +#include "frquad.h" +#include "frsubclp.h" +#include "rendtool.h" +#include "textmaps.h" // pain, sadness + +#define FLIP_BITS + +// for the game system to grab and deparse for distance and all +int _game_fr_tmap; + +// these should go somewhere else... +#define MAX_HGT HGT_STEPS + +#define FDT_TMPPTCNT 8 + +#define T_LEFT 0 +#define T_RIGHT 1 + +// really need oct_low and oct_high mirrors for fhgt_list so we can +/- 33 as oct_parm + +// globals +int _fdt_dist; // distance in hv squares to us +fix _fr_fhgt_step; // single hgt step +fix _fr_fhgt_list[MAX_HGT + 1]; // all hgt steps fix mapping +sfix _fr_sfuv_list[(2 * MAX_HGT) + 1]; // all hgt steps uv mapping +fix slope_norm[MAX_HGT][3]; // table for sloped floors + +// these example values are for a 32wide map, they get set in frpipe when map is resized +int wall_adds[] = {DEF_MAP_SZ, 1, -DEF_MAP_SZ, -1}; // how to get from one tile to next ptr math +int csp_trans_add[] = {0, DEF_MAP_SZ, DEF_MAP_SZ + 1, 1}; // alternate mapping of same deltas + +int _fdt_x, _fdt_y, _fdt_mask; // implicit parameters to draw tile +MapElem *_fdt_mptr; // more implicit parameters + +// texture mapping function pointers. +void (*_fr_lit_floor_func)(int, g3s_phandle *, grs_bitmap *); +void (*_fr_floor_func)(int, g3s_phandle *, grs_bitmap *); +void (*_fr_lit_wall_func)(int, g3s_phandle *, grs_bitmap *); +void (*_fr_wall_func)(int, g3s_phandle *, grs_bitmap *); +void (*_fr_lit_per_func)(int, g3s_phandle *, grs_bitmap *); +void (*_fr_per_func)(int, g3s_phandle *, grs_bitmap *); + +#if _fr_defdbg(CURSOR) +int _fr_cursorx, _fr_cursory; +#endif + +#if _fr_defdbg(ALTCAM) +int _fr_altcamx, _fr_altcamy; +#endif + +// various nested locals, read static globals in C +static uchar _fdt_tt; // local storage of our current tile's tiletype +static TilesToWalls _fdt_ttw; // secret tiles to walls code +static TilesToFloors *_fdt_ttf; // pointer to current floor spew +static uchar _fdt_hgts[5]; // parm, flr, ceil, fprm, cprm +static fix _fdt_fix_parm; // fix rep for parameter +static fix _fdt_cur_parm; // dont pass around, as it is constant per tile +static char _fdt_icnt; // internal walls count - can be made a local again, i think +static uchar *_fdt_fo; // current tile's fo data +static fix _fdt_hgt_val; // last point hgt world coordinate +static uchar _fdt_whichpt; // fhgt_list_entry 0-MAX_HGT for last point looked up height w/parm adjust +static uchar _fdt_hgt_pt; // actual fdt_hgts value for this whichpt, since we use it 4-8 times for lit pts +g3s_phandle _fdt_tmppts[8]; // these are used for all temporary point sets +static int _fdt_wallid; // current external wall id +static g3s_phandle *_fdt_lcore; // left core for external walls +static g3s_phandle *_fdt_rcore; // right core for external walls +static int _fdt_rbase; // value of pbase at right of external walls, we use pbase itself for left +static int _fdt_me_flags; // hold store the current map flags +static int _fdt_wmap; // current tmap family to use +static sfix _fdt_slock; // sfix value for vlock in square + +int _fdt_pbase; // where pbase is, for corner for idx lookup - sadly global for object lighting + +static uchar last_csp_fr = 0; // last frame annoyance +static uchar _fdt_flip; // tile flippitude of cur tile +static bool _fdt_terr; // are we doing terrain or physics + +// indirections for low level render functions +static void (*_fr_terr_int_wall)(int wall_id); +static void (*_fr_terr_ext_wall)(fix pt_list[4][2]); +static void (*_fr_terr_flr)(void); +static void (*_fr_terr_ceil)(void); +// how to render a composite inner wall +static void (*_fr_render_walls)(int which, int cnt); +// how to deal w/the object chain at a square +static void (*_fr_parse_obj)(void); + +// in rendtool.c +#define TIM_WERE_AWAKE +#ifdef TIM_WERE_AWAKE +#define IsTpropNotStars() (textprops[_game_fr_tmap].force_dir == 0) +#else +#define IsTpropNotStars() (_game_fr_tmap >= 4) +#endif +#define quik_draw_tmap_p(ptcnt) ((IsTpropNotStars()) || (draw_tmap_p(ptcnt))) +//#define quik_draw_tmap_p(ptcnt) (TRUE) + +// lookups into arrays +#define FDT_LK_FLR 0 +#define FDT_LK_CEIL 1 + +// points in hgt structures +#define FDT_PT_PARM 0 +#define FDT_PT_FLR 1 +#define FDT_PT_CEIL 2 +#define FDT_PT_FPRM 3 +#define FDT_PT_CPRM 4 + +int _fr_terr_prim = 1; + +// Internal prototypes (for Mac version) +void _fr_figure_pt(g3s_phandle tmp, int pt_code); +fix get_light(fix dist_to); +int _fr_do_light_val(int which, fix dist_val); +int _obj_do_light(int which, fix dist); +void _fr_do_cspace(g3s_phandle wrk, int which); +void _fr_draw_wire_cpoly_4(void); +void _fr_draw_wire_cpoly_3or4(int cnt); +void flip_setup(int wall_code); +int fr_get_ext_gap(fix *face_l, uchar in_fo, uchar *hgts, uchar *mmptr); +int _fr_get_anti_gap(fix *face_l, uchar in_fo, uchar *hgts, uchar *mmptr); +void _render_3d_walls(int which, int cnt); +int merge_walls(fix *dst_wall, fix *i_wall, int i_cnt, fix *o_wall, int o_cnt); +void parse_clip_tile(); +void fr_terr_cspace_pick(uchar do_wall); +int _fr_tfunc_flr_normal(fix *vec, int *lflg, int fnorm_entry); +int _fr_tfunc_ceil_normal(fix *vec, int *lflg, int fnorm_entry); +int do_fc_trans(fix *xy, fix *sc, fix *vals); +void do_floor_element(int nrm_mask); +void do_ceil_element(int nrm_mask); +void fr_tfunc_grab_start(void); +void fr_tfunc_grab_fast(int mask); + +///// AAHRRHRHGGHGHG switch to secondary data cache!!! + +// probably should have 3 of these, one for cspace, one for unlit, one for lantern, so on, swap in pointers, +// and for asm have a table of calls to this which we switch if necessary at start of frame + +// oh oh oh baby oh baby oh self modify height delta step baby now baby please +// really, this wants to become the threaded nightmare assembler point coder from hell +// and to use the 4 respts per map pt idea so we can reuse too +// 3 tests, +// either succeed jump out and reuse +// or fail all 3 (no jumps taken) and optimally build new one + +// deal with optimality of data structures when rewriting in asm + +// SPEED + +// the phone is ringing its 4 am +// if it's your friends then i dont want to hear from them +// please leave a number and a message at the tone +// or you can just go on and leave me alone +// and i... dont want to know if you are lonely + +// hmm.. have to deal with halve points and such +void _fr_figure_pt(g3s_phandle tmp, int pt_code) { + g3s_phandle *core; + pt_mods *ptm; + + ptm = &pt_deref[pt_code & FRPTSPTOFF]; + switch (_fdt_pbase = ptm->base) { // bad bad bad - hmmm... arrays? + case 0: + core = _fr_ptbase; + break; + case 1: + core = _fr_ptnext; + break; + case 2: + core = _fr_ptnext + 1; + break; + case 3: + core = _fr_ptbase + 1; + break; + //#ifndef SHIP + // default: Warning(("_fr_terr_poly_wall: bad point base code %x gives %d\n",pt_code,ptm->base)); break; + //#endif + } + core += _fdt_x; + _fdt_whichpt = ((pt_code & FRPTSZMASK) >> FRPTSZSHF); + _fdt_hgt_pt = _fdt_hgts[_fdt_whichpt + 1]; + _fdt_hgt_val = _fr_fhgt_list[_fdt_hgt_pt]; + g3_replace_add_delta_y(*core, tmp, -_fdt_hgt_val); + switch (ptm->modcnt) { // 1 function table + case FRMODYAXIS: + g3_add_delta_z(tmp, ptm->arg); + break; + case FRMODXAXIS: + g3_add_delta_x(tmp, ptm->arg); + break; + case FRMODNONE: + break; + } +} + +fix get_light(fix dist_to) // , fix dot_prod) +{ + // if (fr_normal_lights) + // dist_to+=fix_mul(dist_to>>fr_normal_shf,fix_make(1,0)-dot_prod); // 1/(1< fix_make((int)_frp.lighting.rad[1], 0)) + return fix_make((int)_frp.lighting.base[1], 0); + return fix_mul(dist_to, _frp.lighting.slope) + _frp.lighting.yint; +} + +#define get_light_t(targ, dist_to) \ + if (dist_to < fix_make((int)_frp.lighting.rad[0], 0)) \ + targ = ((int)_frp.lighting.base[0]) << 8; \ + else if (dist_to > fix_make((int)_frp.lighting.rad[1], 0)) \ + targ = ((int)_frp.lighting.base[1]) << 8; \ + else \ + targ = (fix_mul(dist_to, _frp.lighting.slope) + _frp.lighting.yint) >> 8 + +#ifndef NEW_WAY +#define set_terr_light(our_mp, which) \ + if (which == FRPTSZCEIL_DN) \ + i = (int)me_light_ceil(our_mp) - (int)me_templight_ceil(our_mp); \ + else \ + i = (int)me_light_flr(our_mp) - (int)me_templight_flr(our_mp); \ + if (i < 0) \ + i = 0 +#else +#define set_terr_light(our_mp, which) \ + { \ + int j; \ + if (which == FRPTSZCEIL_DN) { \ + i = (int)me_light_ceil(our_mp); \ + j = (int)me_templight_ceil(our_mp); \ + } else { \ + i = (int)me_light_flr(our_mp); \ + j = (int)me_templight_flr(our_mp); \ + } \ + i = min(i, j); \ + } +#endif + +// should redo these to setup sfix with punch in not shift +// for now hack with which to get stuff running +// later we want an interpolator +// rewrite this in assembler +int _fr_do_light_val(int which, fix dist_val) { + MapElem *our_mp = _fdt_mptr; + int i; + + our_mp += csp_trans_add[_fdt_pbase]; + set_terr_light(our_mp, which); + i <<= 8; + + // speed this up, add fast dist check to trivial reject it.. ick, is this correct + // secret sacred secret sacred secret gnosis... neat, eh? + if ((_fr_curflags & FR_HACKCAM_MASK) == 0) { + if (_frp_light_bits_cam()) + if ((dist_val >> 16) <= _frp.lighting.rad[1] + 1) { + fix light_res; + get_light_t(light_res, dist_val); + if (light_res < i) + i -= light_res; + else + i = 0; + } + } + + if (_frp.lighting.global_mod) { + i -= _frp.lighting.global_mod; + if (i < 0) + i = 0; + else if (i > (15 << 8)) + i = (15 << 8); + } + + if (me_bits_rend3_x(our_mp)) { + i = 0; + // i-=(6<<8); // ah-yep.... + // if (i<0) i=0; + } + + // printf("Lighting! %x\n", i); + + return i; + + // if (_frp_light_bits_norm()) + // wrk->i=i; + // *((uchar *)&wrk->i)=i; + // return wrk->i; +} + +int _fr_do_light(g3s_phandle wrk, int which) { + // fix dval=abs(wrk->x)+abs(wrk->y)+abs(wrk->z)); + // fix dval=fix_fast_pyth_dist(fix_fast_pyth_dist(wrk->x,wrk->y),wrk->z); + // really, we want the original, not transformed, points + // how bout this mess, eh? + static int x_mod[] = {0, 0, 1, 1}, y_mod[] = {0, 1, 1, 0}; + int _lgt_x, _lgt_y; + fix dval; + _lgt_x = _fdt_x + x_mod[_fdt_pbase]; + _lgt_y = _fdt_y + y_mod[_fdt_pbase]; + dval = + fix_fast_pyth_dist(fix_fast_pyth_dist((_lgt_x << 16) - fr_camera_last[0], (_lgt_y << 16) - fr_camera_last[1]), + fr_camera_last[2] - _fdt_hgt_val); + wrk->i = _fr_do_light_val(which, dval); + return wrk->i; +} + +int _obj_do_light(int which, fix dist) { return _fr_do_light_val(which, dist); } + +void _fr_do_cspace(g3s_phandle wrk, int which) { + int col, dst, ncval; + MapElem *our_mp = _fdt_mptr + csp_trans_add[_fdt_pbase]; + if (which == FRPTSZCEIL_DN) + col = me_cybcolor_ceil(our_mp); + else + col = me_cybcolor_flr(our_mp); + // light table based on radius here, somehow.... + dst = _fdt_dist - 6; // dimming code. i guess + if (dst < 0) + dst = 0; + else if (dst > 10) + dst = 10; + dst <<= 8; + ncval = *((_fr_clut_list[0]) + dst + col); + wrk->rgb = grd_bpal[ncval]; // col + wrk->p3_flags |= PF_RGB; +} + +#define dlC FRPTSZCEIL_DN +#define dlF FRPTSZFLR_DN +#define _fr_pt_light(phnd) _fr_do_light(phnd, _fdt_whichpt &FRPTSZPICK_DN) +#define _fr_pt_cspace(phnd) _fr_do_cspace((phnd), _fdt_whichpt &FRPTSZPICK_DN) + +void _fr_draw_wire_cpoly_4(void) { + g3_draw_cline(_fdt_tmppts[0], _fdt_tmppts[1]); + g3_draw_cline(_fdt_tmppts[1], _fdt_tmppts[2]); + g3_draw_cline(_fdt_tmppts[2], _fdt_tmppts[3]); + g3_draw_cline(_fdt_tmppts[3], _fdt_tmppts[0]); +} + +void _fr_draw_wire_cpoly_3or4(int cnt) { + g3_draw_cline(_fdt_tmppts[0], _fdt_tmppts[1]); + g3_draw_cline(_fdt_tmppts[1], _fdt_tmppts[2]); + if (cnt == 3) + g3_draw_cline(_fdt_tmppts[2], _fdt_tmppts[0]); + else { + g3_draw_cline(_fdt_tmppts[2], _fdt_tmppts[3]); + g3_draw_cline(_fdt_tmppts[3], _fdt_tmppts[0]); + } +} + +void flip_setup(int wall_code) { + int lflags = _fdt_me_flags; + if (lflags & MAP_FLIP_FNCY_MASK) { + lflags &= ~MAP_FLIP_FNCY_MASK; /* clear fancy bit */ + lflags += (((_fdt_x + _fdt_y + wall_code) & 1) << MAP_FLIP_PRTY_SHF); /* add in current parity */ + lflags &= ~MAP_FLIP_FNCY_MASK; /* mod 1 within the bit field */ + } + _fdt_flip = ((lflags & MAP_FLIP_MASK) >> MAP_FLIP_SHF); +#ifdef FLIP_SPEW + if (_fdt_me_flags & MAP_FLIP_MASK) + mprintf("Flip now set to %x from %x,%x at %x %x t %x\n", _fdt_flip, lflags, _fdt_me_flags, _fdt_x, _fdt_y, + wall_code); + else if (_fdt_flip) + mprintf("Hey, flip set, huh? %x %x %x\n", _fdt_x, _fdt_y, wall_code); +#endif +} + +#ifdef FLIP_BITS +#define WALL_L_SET(pt) pt->uv.u = (_fdt_flip) << 8 +#define WALL_R_SET(pt) pt->uv.u = ((1 + _fdt_flip) & 1) << 8 +#else +#define WALL_L_SET(pt) pt->uv.u = 0 << 8 +#define WALL_R_SET(pt) pt->uv.u = 1 << 8 +#endif + +// uv for a wall +/// ARRRGH, sfix points suck.. make this a look up through _fdt_hgts+vhold? +#define wall_uv_l(tmp) \ + tmp->uv.v = _fr_sfuv_list[_fdt_hgt_pt] + _fdt_slock; \ + tmp->p3_flags |= PF_U | PF_V; \ + WALL_L_SET(tmp) + +#define wall_uv_r(tmp) \ + tmp->uv.v = _fr_sfuv_list[_fdt_hgt_pt] + _fdt_slock; \ + tmp->p3_flags |= PF_U | PF_V; \ + WALL_R_SET(tmp) + +#define wall_uv_l_i(tmp) \ + tmp->uv.v = _fr_sfuv_list[_fdt_hgt_pt] + _fdt_slock; \ + tmp->p3_flags |= PF_U | PF_V | PF_I; \ + WALL_L_SET(tmp) + +#define wall_uv_r_i(tmp) \ + tmp->uv.v = _fr_sfuv_list[_fdt_hgt_pt] + _fdt_slock; \ + tmp->p3_flags |= PF_U | PF_V | PF_I; \ + WALL_R_SET(tmp) + + // if ((pt_num+1)&2) tmp->u=0; else tmp->u=1<<8; + +#ifndef PARTIAL_TILES +#define EXT_WALL_L_SET(pt) WALL_L_SET(pt) +#define EXT_WALL_R_SET(pt) WALL_R_SET(pt) +#else +#ifdef FLIP_BITS +#define EXT_WALL_L_SET(pt) pt->uv.u = (fix_flip + (flip_sign)*hgt_data[0]) >> 8 +#define EXT_WALL_R_SET(pt) pt->uv.u = (fix_flip + (flip_sign)*hgt_data[0]) >> 8 +#else +#define EXT_WALL_L_SET(pt) pt->uv.u = hgt_data[0] >> 8 +#define EXT_WALL_R_SET(pt) pt->uv.u = hgt_data[0] >> 8 +#endif +#endif + +// really, these could use 0 and 1<<8 for u coordinates, since why not without partial tiles +#define ext_wall_uv_l(tmp, hgt_data) \ + tmp->uv.v = ((fix_make(4, 0) - hgt_data[1]) >> 8) + _fdt_slock; \ + tmp->p3_flags |= PF_U | PF_V; \ + EXT_WALL_L_SET(tmp) + +#define ext_wall_uv_r(tmp, hgt_data) \ + tmp->uv.v = ((fix_make(4, 0) - hgt_data[1]) >> 8) + _fdt_slock; \ + tmp->p3_flags |= PF_U | PF_V; \ + EXT_WALL_R_SET(tmp) + +#define ext_wall_uv_l_i(tmp, hgt_data) \ + tmp->uv.v = ((fix_make(4, 0) - hgt_data[1]) >> 8) + _fdt_slock; \ + tmp->p3_flags |= PF_U | PF_V | PF_I; \ + EXT_WALL_L_SET(tmp) + +#define ext_wall_uv_r_i(tmp, hgt_data) \ + tmp->uv.v = ((fix_make(4, 0) - hgt_data[1]) >> 8) + _fdt_slock; \ + tmp->p3_flags |= PF_U | PF_V | PF_I; \ + EXT_WALL_R_SET(tmp) + +// i can feel it in my bones +// im gonna spend my whole life alone + +// tabs_int_wall +//#pragma disable_message(202) +static void _fr_null_int_wall(int wall_id) {} +//#pragma enable_message(202) + +static void _fr_flat_int_wall(int wall_id) { + WallsToPts *wpt = &wall_pts[wall_id]; + // need to have face_code set and be ready with flip and hold and all that jazz + _fr_figure_pt(_fdt_tmppts[0], wpt->ul); + _fr_figure_pt(_fdt_tmppts[1], wpt->ur); + _fr_figure_pt(_fdt_tmppts[2], wpt->lr); + _fr_figure_pt(_fdt_tmppts[3], wpt->ll); + _fr_ndbg(NO_REND, g3_draw_poly((*fr_get_idx)(), 4, _fdt_tmppts)); + _fr_sdbg(STATS, _frp.stats.int_wall++); +} + +#ifdef FLAT_SUPPORT +static void _fr_flat_lit_int_wall(int wall_id) { + WallsToPts *wpt = &wall_pts[wall_id]; + // need to have face_code set and be ready with flip and hold and all that jazz + _fr_figure_pt(_fdt_tmppts[0], wpt->ul); + _fr_pt_light(_fdt_tmppts[0]); + _fdt_tmppts[0]->p3_flags |= PF_I; + _fr_figure_pt(_fdt_tmppts[1], wpt->ur); + _fr_pt_light(_fdt_tmppts[1]); + _fdt_tmppts[1]->p3_flags |= PF_I; + _fr_figure_pt(_fdt_tmppts[2], wpt->lr); + _fr_pt_light(_fdt_tmppts[2]); + _fdt_tmppts[2]->p3_flags |= PF_I; + _fr_figure_pt(_fdt_tmppts[3], wpt->ll); + _fr_pt_light(_fdt_tmppts[3]); + _fdt_tmppts[3]->p3_flags |= PF_I; + gr_set_fcolor((*fr_get_idx)()); + _fr_ndbg(NO_REND, g3_draw_spoly(4, _fdt_tmppts)); + _fr_sdbg(STATS, _frp.stats.int_wall++); +} +#else +#define _fr_flat_lit_int_wall _fr_tmap_lit_int_wall +#endif + +static void _fr_tmap_int_wall(int wall_id) { + WallsToPts *wpt = &wall_pts[wall_id]; + // need to have face_code set and be ready with flip and hold and all that jazz + _fr_figure_pt(_fdt_tmppts[0], wpt->ul); + wall_uv_l(_fdt_tmppts[0]); + _fr_figure_pt(_fdt_tmppts[1], wpt->ur); + wall_uv_r(_fdt_tmppts[1]); + _fr_figure_pt(_fdt_tmppts[2], wpt->lr); + wall_uv_r(_fdt_tmppts[2]); + _fr_figure_pt(_fdt_tmppts[3], wpt->ll); + wall_uv_l(_fdt_tmppts[3]); + if (quik_draw_tmap_p(4)) { + _fr_ndbg(NO_REND, _fr_wall_func(4, _fdt_tmppts, (*fr_get_tmap)())); + } + _fr_sdbg(STATS, _frp.stats.int_wall++); +} + +static void _fr_tmap_lit_int_wall(int wall_id) { + WallsToPts *wpt = &wall_pts[wall_id]; + // need to have face_code set and be ready with flip and hold and all that jazz + _fr_figure_pt(_fdt_tmppts[0], wpt->ul); + wall_uv_l_i(_fdt_tmppts[0]); + _fr_pt_light(_fdt_tmppts[0]); + _fr_figure_pt(_fdt_tmppts[1], wpt->ur); + wall_uv_r_i(_fdt_tmppts[1]); + _fr_pt_light(_fdt_tmppts[1]); + _fr_figure_pt(_fdt_tmppts[2], wpt->lr); + wall_uv_r_i(_fdt_tmppts[2]); + _fr_pt_light(_fdt_tmppts[2]); + _fr_figure_pt(_fdt_tmppts[3], wpt->ll); + wall_uv_l_i(_fdt_tmppts[3]); + _fr_pt_light(_fdt_tmppts[3]); + if (quik_draw_tmap_p(4)) { + _fr_ndbg(NO_REND, _fr_lit_wall_func(4, _fdt_tmppts, (*fr_get_tmap)())); + } + _fr_sdbg(STATS, _frp.stats.int_wall++); +} + +static void _fr_cspace_wire_int_wall(int wall_id) { + WallsToPts *wpt = &wall_pts[wall_id]; + // need to have face_code set and be ready with flip and hold and all that jazz + _fr_figure_pt(_fdt_tmppts[0], wpt->ul); + _fr_pt_cspace(_fdt_tmppts[0]); + _fr_figure_pt(_fdt_tmppts[1], wpt->ur); + _fr_pt_cspace(_fdt_tmppts[1]); + _fr_figure_pt(_fdt_tmppts[2], wpt->lr); + _fr_pt_cspace(_fdt_tmppts[2]); + _fr_figure_pt(_fdt_tmppts[3], wpt->ll); + _fr_pt_cspace(_fdt_tmppts[3]); + _fr_ndbg(NO_REND, _fr_draw_wire_cpoly_4()); + _fr_sdbg(STATS, _frp.stats.int_wall++); +} + +static void _fr_cspace_full_int_wall(int wall_id) { + WallsToPts *wpt = &wall_pts[wall_id]; + // need to have face_code set and be ready with flip and hold and all that jazz + _fr_figure_pt(_fdt_tmppts[0], wpt->ul); + _fr_pt_cspace(_fdt_tmppts[0]); + _fr_figure_pt(_fdt_tmppts[1], wpt->ur); + _fr_pt_cspace(_fdt_tmppts[1]); + _fr_figure_pt(_fdt_tmppts[2], wpt->lr); + _fr_pt_cspace(_fdt_tmppts[2]); + _fr_figure_pt(_fdt_tmppts[3], wpt->ll); + _fr_pt_cspace(_fdt_tmppts[3]); + _fr_ndbg(NO_REND, g3_draw_cpoly(4, _fdt_tmppts)); + _fr_sdbg(STATS, _frp.stats.int_wall++); +} + +// tabs_ext_wall + +// this takes a wacky hgt data set +// uses _fdt_wallid, _fdt_lcore, _fdt_rcore +//#pragma disable_message(202) +static void _fr_null_ext_wall(fix pt_list[4][2]) {} +//#pragma enable_message(202) + +static void _fr_flat_ext_wall( + fix pt_list[4][2]) { // note we do this out of order so we can have left left right right, ie. note their indicies + g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[0], -pt_list[0][1]); + g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[3], -pt_list[3][1]); + _fdt_pbase = _fdt_rbase; + g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[1], -pt_list[1][1]); + g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[2], -pt_list[2][1]); + _fr_ndbg(NO_REND, g3_draw_poly((*fr_get_idx)(), 4, _fdt_tmppts)); + _fr_sdbg(STATS, _frp.stats.ext_wall++); +} + +#ifdef FLAT_SUPPORT +static void _fr_flat_lit_ext_wall(fix pt_list[4][2]) { + g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[0], -pt_list[0][1]); + _fdt_hgt_val = -pt_list[0][1]; + _fr_do_light(_fdt_tmppts[0], dlC); + _fdt_tmppts[0]->p3_flags |= PF_I; + g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[3], -pt_list[3][1]); + _fdt_hgt_val = -pt_list[3][1]; + _fr_do_light(_fdt_tmppts[3], dlF); + _fdt_tmppts[3]->p3_flags |= PF_I; + _fdt_pbase = _fdt_rbase; + g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[1], -pt_list[1][1]); + _fdt_hgt_val = -pt_list[1][1]; + _fr_do_light(_fdt_tmppts[1], dlC); + _fdt_tmppts[1]->p3_flags |= PF_I; + g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[2], -pt_list[2][1]); + _fdt_hgt_val = -pt_list[2][1]; + _fr_do_light(_fdt_tmppts[2], dlF); + _fdt_tmppts[2]->p3_flags |= PF_I; + gr_set_fcolor((*fr_get_idx)()); + _fr_ndbg(NO_REND, g3_draw_spoly(4, _fdt_tmppts)); + _fr_sdbg(STATS, _frp.stats.ext_wall++); +} +#else +#define _fr_flat_lit_ext_wall _fr_tmap_lit_ext_wall +#endif + +static void _fr_tmap_ext_wall(fix pt_list[4][2]) { + g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[0], -pt_list[0][1]); + ext_wall_uv_l(_fdt_tmppts[0], pt_list[0]); + g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[3], -pt_list[3][1]); + ext_wall_uv_l(_fdt_tmppts[3], pt_list[3]); + _fdt_pbase = _fdt_rbase; + g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[1], -pt_list[1][1]); + ext_wall_uv_r(_fdt_tmppts[1], pt_list[1]); + g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[2], -pt_list[2][1]); + ext_wall_uv_r(_fdt_tmppts[2], pt_list[2]); + if (quik_draw_tmap_p(4)) { + _fr_ndbg(NO_REND, _fr_wall_func(4, _fdt_tmppts, (*fr_get_tmap)())); + } + _fr_sdbg(STATS, _frp.stats.ext_wall++); +} + +static void _fr_tmap_lit_ext_wall(fix pt_list[4][2]) { + g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[0], -pt_list[0][1]); + ext_wall_uv_l_i(_fdt_tmppts[0], pt_list[0]); + _fdt_hgt_val = pt_list[0][1]; + _fr_do_light(_fdt_tmppts[0], dlC); + g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[3], -pt_list[3][1]); + ext_wall_uv_l_i(_fdt_tmppts[3], pt_list[3]); + _fdt_hgt_val = pt_list[3][1]; + _fr_do_light(_fdt_tmppts[3], dlF); + _fdt_pbase = _fdt_rbase; + g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[1], -pt_list[1][1]); + ext_wall_uv_r_i(_fdt_tmppts[1], pt_list[1]); + _fdt_hgt_val = pt_list[1][1]; + _fr_do_light(_fdt_tmppts[1], dlC); + g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[2], -pt_list[2][1]); + ext_wall_uv_r_i(_fdt_tmppts[2], pt_list[2]); + _fdt_hgt_val = pt_list[2][1]; + _fr_do_light(_fdt_tmppts[2], dlF); + if (quik_draw_tmap_p(4)) { + _fr_ndbg(NO_REND, _fr_lit_wall_func(4, _fdt_tmppts, (*fr_get_tmap)())); + } + _fr_sdbg(STATS, _frp.stats.ext_wall++); +} + +static void _fr_cspace_wire_ext_wall(fix pt_list[4][2]) { + g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[0], -pt_list[0][1]); + _fr_do_cspace(_fdt_tmppts[0], dlC); + g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[3], -pt_list[3][1]); + _fr_do_cspace(_fdt_tmppts[3], dlF); + _fdt_pbase = _fdt_rbase; + g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[1], -pt_list[1][1]); + _fr_do_cspace(_fdt_tmppts[1], dlC); + g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[2], -pt_list[2][1]); + _fr_do_cspace(_fdt_tmppts[2], dlF); + _fr_ndbg(NO_REND, _fr_draw_wire_cpoly_4()); + _fr_sdbg(STATS, _frp.stats.ext_wall++); +} + +static void _fr_cspace_full_ext_wall(fix pt_list[4][2]) { + g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[0], -pt_list[0][1]); + _fr_do_cspace(_fdt_tmppts[0], dlC); + g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[3], -pt_list[3][1]); + _fr_do_cspace(_fdt_tmppts[3], dlF); + _fdt_pbase = _fdt_rbase; + g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[1], -pt_list[1][1]); + _fr_do_cspace(_fdt_tmppts[1], dlC); + g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[2], -pt_list[2][1]); + _fr_do_cspace(_fdt_tmppts[2], dlF); + _fr_ndbg(NO_REND, g3_draw_cpoly(4, _fdt_tmppts)); + _fr_sdbg(STATS, _frp.stats.ext_wall++); +} + +// tabs_flr + +// should store pt_code, and look up in there.... +#define floor_uv(pt, pn) \ + { \ + (pt)->uv.u = pt_uv[pn][pt_rot][0]; \ + (pt)->uv.v = pt_uv[pn][pt_rot][1]; \ + (pt)->p3_flags |= PF_U | PF_V; \ + } +#define ceil_uv(pt, pn) \ + { \ + (pt)->uv.u = pt_uv[pn][pt_rot][0]; \ + (pt)->uv.v = pt_uv[pn][pt_rot][1]; \ + (pt)->p3_flags |= PF_U | PF_V; \ + } + +#define floor_uv_i(pt, pn) \ + { \ + (pt)->uv.u = pt_uv[pn][pt_rot][0]; \ + (pt)->uv.v = pt_uv[pn][pt_rot][1]; \ + (pt)->p3_flags |= PF_U | PF_V | PF_I; \ + } +#define ceil_uv_i(pt, pn) \ + { \ + (pt)->uv.u = pt_uv[pn][pt_rot][0]; \ + (pt)->uv.v = pt_uv[pn][pt_rot][1]; \ + (pt)->p3_flags |= PF_U | PF_V | PF_I; \ + } + +static void _fr_null_flrceil(void) {} + +// lets start a war, jack up the dow jones +static void _fr_flat_flr(void) { + uchar *ptdat = _fdt_ttf->data, *pt_merge_mask; + int i, pt_code, loopcnt; + g3s_phandle *pb; + + loopcnt = _fdt_ttf->flags >> FRFLRSHF_2ELEM; // 0 or 1 + pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_FLR]; + do { + for (pb = &_fdt_tmppts[0], i = 0; i < _fdt_ttf->ptsper; i++) { + pt_code = *ptdat++; + pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1]; + _fr_figure_pt(*pb++, pt_code); + } + //¥¥ bug?? _fr_ndbg(NO_REND,g3_draw_poly((*fr_get_idx)(),_fdt_ttf->ptsper,&_fdt_tmppts)); + _fr_ndbg(NO_REND, g3_draw_poly((*fr_get_idx)(), _fdt_ttf->ptsper, _fdt_tmppts)); + } while (loopcnt-- > 0); + _fr_sdbg(STATS, _frp.stats.flr++); +} + +#ifdef FLAT_SUPPORT +static void _fr_flat_lit_flr(void) { + uchar *ptdat = _fdt_ttf->data, *pt_merge_mask; + int i, pt_code, loopcnt; + g3s_phandle *pb; + + loopcnt = _fdt_ttf->flags >> FRFLRSHF_2ELEM; // 0 or 1 + pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_FLR]; + gr_set_fcolor((*fr_get_idx)()); + do { + for (pb = &_fdt_tmppts[0], i = 0; i < _fdt_ttf->ptsper; i++) { + pt_code = *ptdat++; + pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1]; + _fr_figure_pt(*pb, pt_code); + _fr_pt_light(*pb); + (*pb++)->p3_flags |= PF_I; + } + _fr_ndbg(NO_REND, g3_draw_spoly(_fdt_ttf->ptsper, &_fdt_tmppts)); + } while (loopcnt-- > 0); + _fr_sdbg(STATS, _frp.stats.flr++); +} +#else +#define _fr_flat_lit_flr _fr_tmap_lit_flr +#endif + +static void _fr_tmap_flr(void) { + uchar nrm_mask = fr_fnorm_list[_fdt_tt]; + int i, pt_code, loopcnt; + g3s_phandle *pb; + uchar *ptdat = _fdt_ttf->data, *pt_merge_mask; + int pt_rot = me_rotflr(_fdt_mptr); + + loopcnt = _fdt_ttf->flags >> FRFLRSHF_2ELEM; // 0 or 1 + pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_FLR]; + do { + for (pb = &_fdt_tmppts[0], i = 0; i < _fdt_ttf->ptsper; i++) { + pt_code = *ptdat++; + pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1]; + _fr_figure_pt(*pb, pt_code); + pt_code &= FRPTSPTOFF; + floor_uv(*pb, pt_code); + pb++; + } + if (quik_draw_tmap_p(_fdt_ttf->ptsper)) { + if ((nrm_mask & 0xf) == FRFNORM_VFULL) { + _fr_ndbg(NO_REND, _fr_floor_func(_fdt_ttf->ptsper, _fdt_tmppts, (*fr_get_tmap)())); + } else { + _fr_ndbg(NO_REND, _fr_per_func(_fdt_ttf->ptsper, _fdt_tmppts, (*fr_get_tmap)())); + } + } + } while (loopcnt-- > 0); + _fr_sdbg(STATS, _frp.stats.flr++); +} + +static void _fr_tmap_lit_flr(void) { + uchar nrm_mask = fr_fnorm_list[_fdt_tt]; + int i, pt_code, loopcnt; + g3s_phandle *pb; + uchar *ptdat = _fdt_ttf->data, *pt_merge_mask; + int pt_rot = me_rotflr(_fdt_mptr); + + loopcnt = _fdt_ttf->flags >> FRFLRSHF_2ELEM; // 0 or 1 + pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_FLR]; + do { + for (pb = &_fdt_tmppts[0], i = 0; i < _fdt_ttf->ptsper; i++) { + pt_code = *ptdat++; + pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1]; + _fr_figure_pt(*pb, pt_code); + pt_code &= FRPTSPTOFF; + floor_uv_i(*pb, pt_code); + _fr_pt_light(*pb++); + } + if (quik_draw_tmap_p(_fdt_ttf->ptsper)) { + if ((nrm_mask & 0xf) == FRFNORM_VFULL) { + _fr_ndbg(NO_REND, _fr_lit_floor_func(_fdt_ttf->ptsper, _fdt_tmppts, (*fr_get_tmap)())); + } else { + _fr_ndbg(NO_REND, _fr_lit_per_func(_fdt_ttf->ptsper, _fdt_tmppts, (*fr_get_tmap)())); + } + } + } while (loopcnt-- > 0); + _fr_sdbg(STATS, _frp.stats.flr++); +} + +static void _fr_cspace_wire_flr(void) { + uchar *ptdat = _fdt_ttf->data, *pt_merge_mask; + int i, pt_code, loopcnt; + g3s_phandle *pb; + + loopcnt = _fdt_ttf->flags >> FRFLRSHF_2ELEM; // 0 or 1 + pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_FLR]; + gr_set_fcolor((*fr_get_idx)()); + do { + for (pb = &_fdt_tmppts[0], i = 0; i < _fdt_ttf->ptsper; i++) { + pt_code = *ptdat++; + pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1]; + _fr_figure_pt(*pb, pt_code); + _fr_pt_cspace(*pb); + pb++; + } + _fr_ndbg(NO_REND, _fr_draw_wire_cpoly_3or4(_fdt_ttf->ptsper)); + } while (loopcnt-- > 0); + _fr_sdbg(STATS, _frp.stats.flr++); +} + +static void _fr_cspace_full_flr(void) { + uchar *ptdat = _fdt_ttf->data, *pt_merge_mask; + int i, pt_code, loopcnt; + g3s_phandle *pb; + + loopcnt = _fdt_ttf->flags >> FRFLRSHF_2ELEM; // 0 or 1 + pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_FLR]; + gr_set_fcolor((*fr_get_idx)()); + do { + for (pb = &_fdt_tmppts[0], i = 0; i < _fdt_ttf->ptsper; i++) { + pt_code = *ptdat++; + pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1]; + _fr_figure_pt(*pb, pt_code); + _fr_pt_cspace(*pb); + pb++; + } + _fr_ndbg(NO_REND, g3_draw_cpoly(_fdt_ttf->ptsper, _fdt_tmppts)); + } while (loopcnt-- > 0); + _fr_sdbg(STATS, _frp.stats.flr++); +} + +// tabs_ceil +static void _fr_flat_ceil(void) { + int i, pt_code, loopcnt = 1; + g3s_phandle *pb; + uchar *ptdat = _fdt_ttf->data, *pt_merge_mask; + + if (_fdt_ttf->flags & FRFLRFLG_NOTOP) + return; + pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_CEIL]; + do { + for (pb = (&_fdt_tmppts[0]) + _fdt_ttf->ptsper - 1, i = 0; i < _fdt_ttf->ptsper; i++) { + pt_code = *ptdat++; + pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1]; + _fr_figure_pt(*pb--, pt_code); + } + _fr_ndbg(NO_REND, g3_draw_poly((*fr_get_idx)(), _fdt_ttf->ptsper, _fdt_tmppts)); + } while ((_fdt_ttf->flags & FRFLRFLG_2ELEM) && (loopcnt-- > 0)); + _fr_sdbg(STATS, _frp.stats.ceil++); +} + +#ifdef FLAT_SUPPORT +static void _fr_flat_lit_ceil(void) { + int i, pt_code, loopcnt = 1; + g3s_phandle *pb; + uchar *ptdat = _fdt_ttf->data, *pt_merge_mask; + + if (_fdt_ttf->flags & FRFLRFLG_NOTOP) + return; + pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_CEIL]; + do { + for (pb = (&_fdt_tmppts[0]) + _fdt_ttf->ptsper - 1, i = 0; i < _fdt_ttf->ptsper; i++) { + pt_code = *ptdat++; + pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1]; + _fr_figure_pt(*pb, pt_code); + _fr_pt_light(*pb); + (*pb--)->p3_flags |= PF_I; + } + gr_set_fcolor((*fr_get_idx)()); + _fr_ndbg(NO_REND, g3_draw_spoly(_fdt_ttf->ptsper, _fdt_tmppts)); + } while ((_fdt_ttf->flags & FRFLRFLG_2ELEM) && (loopcnt-- > 0)); + _fr_sdbg(STATS, _frp.stats.ceil++); +} +#else +#define _fr_flat_lit_ceil _fr_tmap_lit_ceil +#endif + +static void _fr_tmap_ceil(void) { + uchar nrm_mask = fr_fnorm_list[_fdt_tt]; + int i, pt_code, loopcnt = 1; + g3s_phandle *pb; + uchar *ptdat = _fdt_ttf->data, *pt_merge_mask; + int pt_rot = me_rotceil(_fdt_mptr); + + if (_fdt_ttf->flags & FRFLRFLG_NOTOP) + return; + pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_CEIL]; + do { + for (pb = (&_fdt_tmppts[0]) + _fdt_ttf->ptsper - 1, i = 0; i < _fdt_ttf->ptsper; i++) { + pt_code = *ptdat++; + pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1]; + _fr_figure_pt(*pb, pt_code); + pt_code &= FRPTSPTOFF; + ceil_uv(*pb, pt_code); + pb--; + } + if (quik_draw_tmap_p(_fdt_ttf->ptsper)) { + if ((nrm_mask & 0xf) == FRFNORM_VFULL) { + _fr_ndbg(NO_REND, _fr_floor_func(_fdt_ttf->ptsper, _fdt_tmppts, (*fr_get_tmap)())); + } else { + _fr_ndbg(NO_REND, _fr_per_func(_fdt_ttf->ptsper, _fdt_tmppts, (*fr_get_tmap)())); + } + } + } while ((_fdt_ttf->flags & FRFLRFLG_2ELEM) && (loopcnt-- > 0)); + _fr_sdbg(STATS, _frp.stats.ceil++); +} + +static void _fr_tmap_lit_ceil(void) { + uchar nrm_mask = fr_fnorm_list[_fdt_tt]; + int i, pt_code, loopcnt = 1; + g3s_phandle *pb; + uchar *ptdat = _fdt_ttf->data, *pt_merge_mask; + int pt_rot = me_rotceil(_fdt_mptr); + + if (_fdt_ttf->flags & FRFLRFLG_NOTOP) + return; + pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_CEIL]; + do { + for (pb = (&_fdt_tmppts[0]) + _fdt_ttf->ptsper - 1, i = 0; i < _fdt_ttf->ptsper; i++) { + pt_code = *ptdat++; + pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1]; + _fr_figure_pt(*pb, pt_code); + pt_code &= FRPTSPTOFF; + ceil_uv_i(*pb, pt_code); + _fr_pt_light(*pb--); + } + if (quik_draw_tmap_p(_fdt_ttf->ptsper)) { + if ((nrm_mask & 0xf) == FRFNORM_VFULL) { + _fr_ndbg(NO_REND, _fr_lit_floor_func(_fdt_ttf->ptsper, _fdt_tmppts, (*fr_get_tmap)())); + } else { + _fr_ndbg(NO_REND, _fr_lit_per_func(_fdt_ttf->ptsper, _fdt_tmppts, (*fr_get_tmap)())); + } + } + } while ((_fdt_ttf->flags & FRFLRFLG_2ELEM) && (loopcnt-- > 0)); + _fr_sdbg(STATS, _frp.stats.ceil++); +} + +//#pragma disable_message(202) +static void _fr_cspace_wire_ceil(void) { + int i, pt_code, loopcnt = 1; + g3s_phandle *pb; + uchar *ptdat = _fdt_ttf->data, *pt_merge_mask; + + if (_fdt_ttf->flags & FRFLRFLG_NOTOP) + return; + pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_CEIL]; + do { + for (pb = (&_fdt_tmppts[0]) + _fdt_ttf->ptsper - 1, i = 0; i < _fdt_ttf->ptsper; i++) { + pt_code = *ptdat++; + pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1]; + _fr_figure_pt(*pb, pt_code); + _fr_pt_cspace(*pb); + pb--; + } + _fr_ndbg(NO_REND, _fr_draw_wire_cpoly_3or4(_fdt_ttf->ptsper)); + } while ((_fdt_ttf->flags & FRFLRFLG_2ELEM) && (loopcnt-- > 0)); + _fr_sdbg(STATS, _frp.stats.ceil++); +} + +static void _fr_cspace_full_ceil(void) { + int i, pt_code, loopcnt = 1; + g3s_phandle *pb; + uchar *ptdat = _fdt_ttf->data, *pt_merge_mask; + + if (_fdt_ttf->flags & FRFLRFLG_NOTOP) + return; + pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_CEIL]; + do { + for (pb = (&_fdt_tmppts[0]) + _fdt_ttf->ptsper - 1, i = 0; i < _fdt_ttf->ptsper; i++) { + pt_code = *ptdat++; + pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1]; + _fr_figure_pt(*pb, pt_code); + _fr_pt_cspace(*pb); + pb--; + } + _fr_ndbg(NO_REND, g3_draw_cpoly(_fdt_ttf->ptsper, _fdt_tmppts)); + } while ((_fdt_ttf->flags & FRFLRFLG_2ELEM) && (loopcnt-- > 0)); + _fr_sdbg(STATS, _frp.stats.ceil++); +} +//#pragma enable_message(202) + +// reoptimize these to _NOT_ do all the boneheaded stuff + +// i ask for nothing, for myself +// for i am dead, for i am dead +int fr_get_ext_gap(fix *face_l, uchar in_fo, uchar *hgts, uchar *mmptr) { + int fo_t, fo_b; + if (in_fo == 0xff) + return 0; + // if (in_fo&FO_HT_MSK) + fo_t = (in_fo ^ mmptr[2]) & mmptr[3]; + fo_b = (in_fo ^ mmptr[0]) & mmptr[1]; + // if (_fdt_ttf->flags&FRFLRFLG_USEPR) + if ((fo_t | fo_b) & FO_HT_MSK) { + // uchar cur_fo; + // cur_fo=(in_fo^mmptr[2])&mmptr[3]; + *(face_l + 0) = fo_unpack[fo_t & FO_LR_MSK][T_LEFT]; // set x for left ceil + *(face_l + 2) = fo_unpack[fo_t & FO_LR_MSK][T_RIGHT]; // set x for right ceil + *(face_l + 1) = *(face_l + 3) = _fr_fhgt_list[hgts[FDT_PT_CEIL]]; // set y's + if (fo_t & FO_L_PARM) + *(face_l + 1) -= _fr_fhgt_list[hgts[FDT_PT_PARM]]; + if (fo_t & FO_R_PARM) + *(face_l + 3) -= _fr_fhgt_list[hgts[FDT_PT_PARM]]; + // cur_fo=(in_fo^mmptr[0])&mmptr[1]; + *(face_l + 4) = fo_unpack[fo_b & FO_LR_MSK][T_RIGHT]; // set x for right flr + *(face_l + 6) = fo_unpack[fo_b & FO_LR_MSK][T_LEFT]; // set x for left flr + *(face_l + 5) = *(face_l + 7) = _fr_fhgt_list[hgts[FDT_PT_FLR]]; // set y's + if (fo_b & FO_R_PARM) + *(face_l + 5) += _fr_fhgt_list[hgts[FDT_PT_PARM]]; + if (fo_b & FO_L_PARM) + *(face_l + 7) += _fr_fhgt_list[hgts[FDT_PT_PARM]]; + if ((*(face_l + 3) == *(face_l + 5)) && (*(face_l + 1) == *(face_l + 7))) + return 0; + } else { + *(face_l + 0) = fo_unpack[in_fo & FO_LR_MSK][T_LEFT]; // set x for left ceil + *(face_l + 2) = fo_unpack[in_fo & FO_LR_MSK][T_RIGHT]; // set x for right ceil + *(face_l + 1) = *(face_l + 3) = _fr_fhgt_list[hgts[FDT_PT_CEIL]]; // set y's + *(face_l + 4) = fo_unpack[in_fo & FO_LR_MSK][T_RIGHT]; // set x for right flr + *(face_l + 6) = fo_unpack[in_fo & FO_LR_MSK][T_LEFT]; // set x for left flr + *(face_l + 5) = *(face_l + 7) = _fr_fhgt_list[hgts[FDT_PT_FLR]]; // set y's + if (*(face_l + 1) == *(face_l + 5)) + return 0; + } + return 1; +} + +#define ANTI_MASK_COUNT 0x0fff +#define ANTI_MASK_PUNTTOP 0x1000 +#define ANTI_MASK_PUNTBOT 0x2000 +// or in 1, since there is still the top face, though it is to be punted +#define ANTI_RET_PUNTALL (ANTI_MASK_PUNTTOP | ANTI_MASK_PUNTBOT | 1) + +// connect the god damn dots +// connect the god damn dots +// connect the god damn dots +// who am i trying to impress +// who could care less +int _fr_get_anti_gap(fix *face_l, uchar in_fo, uchar *hgts, uchar *mmptr) { + int fo_t, fo_b; + int facecnt = 1; + // more importantly, we think face_l is starting 4 in, we fill normally which works + // also note that if no height changes, it could mean we have half walls + // so we check in that case, and if so, add it in place + _fr_sdbg(ANAL_CHK, if (in_fo == 0xff) mprintf("_fr_get_anti_gap: hey in_fo is 0xff\n")); + fo_t = (in_fo ^ mmptr[2]) & mmptr[3]; + fo_b = (in_fo ^ mmptr[0]) & mmptr[1]; + if ((fo_t | fo_b) & FO_HT_MSK) { // these cant be at top, since they have parameter + // uchar cur_fo; // unless of course it masks out.... hmmm + // cur_fo=(in_fo^mmptr[2])&mmptr[3]; + if (((fo_t & FO_HT_MSK) == 0) && (hgts[FDT_PT_CEIL] == MAX_HGT)) // we are fully at top + facecnt |= ANTI_MASK_PUNTTOP; + else { + *(face_l + 5) = *(face_l + 7) = _fr_fhgt_list[hgts[FDT_PT_CEIL]]; // set y's + if (fo_t & FO_L_PARM) + *(face_l + 5) -= _fr_fhgt_list[hgts[FDT_PT_PARM]]; + if (fo_t & FO_R_PARM) + *(face_l + 7) -= _fr_fhgt_list[hgts[FDT_PT_PARM]]; + } + face_l += 8; + // cur_fo=(in_fo^mmptr[0])&mmptr[1]; + if (((fo_b & FO_HT_MSK) == 0) && (hgts[FDT_PT_FLR] == 0)) // we are fully at bottom + facecnt |= ANTI_MASK_PUNTBOT; + else { + *(face_l + 1) = *(face_l + 3) = _fr_fhgt_list[hgts[FDT_PT_FLR]]; // set y's + if (fo_b & FO_R_PARM) + *(face_l + 1) += _fr_fhgt_list[hgts[FDT_PT_PARM]]; + if (fo_b & FO_L_PARM) + *(face_l + 3) += _fr_fhgt_list[hgts[FDT_PT_PARM]]; + } + } else { + if (hgts[FDT_PT_CEIL] == MAX_HGT) // already at top + facecnt |= ANTI_MASK_PUNTTOP; + else // correct x's for this have been set already in _fr_init_slopes in frpipe + *(face_l + 5) = *(face_l + 7) = _fr_fhgt_list[hgts[FDT_PT_CEIL]]; // set y's + face_l += 8; // skip to 2nd entry, as we have finshed or punted the first + if ((in_fo & FO_LR_MSK) != 0x06) // are we not fully free, tm? (ie. foClr) + { // add the anti of the current here... + facecnt++; + // im sure this is wrong + *(face_l + 0) = fo_unpack[in_fo & FO_LR_MSK][T_LEFT]; // flip since this is real order + *(face_l + 2) = fo_unpack[in_fo & FO_LR_MSK][T_RIGHT]; + *(face_l + 1) = *(face_l + 3) = _fr_fhgt_list[hgts[FDT_PT_CEIL]]; // set y's + *(face_l + 4) = fo_unpack[in_fo & FO_LR_MSK][T_RIGHT]; // har har, same flip + *(face_l + 6) = fo_unpack[in_fo & FO_LR_MSK][T_LEFT]; + *(face_l + 7) = *(face_l + 5) = _fr_fhgt_list[hgts[FDT_PT_FLR]]; // set y's + face_l += 8; // skip to 3rd, as we have just done middle + } + if (hgts[FDT_PT_FLR] == 0) + facecnt |= ANTI_MASK_PUNTBOT; + else + *(face_l + 1) = *(face_l + 3) = _fr_fhgt_list[hgts[FDT_PT_FLR]]; // set y's + } + return facecnt; +} + +// amazes me the will of instinct + +// keep these in place so the compiler can deal +// these are of wall_id vs. point_id vs. x and y (as in u,v) +static fix inner_wall[2][4][2], final_wall[4][4][2]; +fix outer_wall[3][4][2]; // base is set up in frpipe.c +static fix *use_outer_wall; // pointer to the outer wall area to really use + +fix tf_diag_walls[4][2] = {{0, fix_make(4, 0)}, {fix_make(1, 0), fix_make(4, 0)}, {fix_make(1, 0), 0}, {0, 0}}; +static fix diag_norms[3] = {0, 0, 0}; + +#ifdef HACK_SHOW +void hack_show(fix edward[4][2]) { + mprintf(" %8.8x %8.8x %8.8x %8.8x\n", edward[0][0], edward[0][1], edward[1][0], edward[1][1]); + mprintf(" %8.8x %8.8x %8.8x %8.8x\n", edward[3][0], edward[3][1], edward[2][0], edward[2][1]); +} +#endif + +// renders icnt of final_walls +void _render_3d_walls(int which, int cnt) { + WallsToPts *wpt = &wall_pts[FROUTERWALLS + which]; + _fdt_wallid = which; + flip_setup(which); + + switch (_fdt_pbase = pt_deref[(wpt->ul & FRPTSPTOFF)].base) { + case 0: + _fdt_lcore = _fr_ptbase; + break; + case 1: + _fdt_lcore = _fr_ptnext; + break; + case 2: + _fdt_lcore = _fr_ptnext + 1; + break; + case 3: + _fdt_lcore = _fr_ptbase + 1; + break; + } + switch (_fdt_rbase = pt_deref[(wpt->ur & FRPTSPTOFF)].base) { + case 0: + _fdt_rcore = _fr_ptbase; + break; + case 1: + _fdt_rcore = _fr_ptnext; + break; + case 2: + _fdt_rcore = _fr_ptnext + 1; + break; + case 3: + _fdt_rcore = _fr_ptbase + 1; + break; + } + _fdt_lcore += _fdt_x; + _fdt_rcore += _fdt_x; + switch (cnt) { + case 4: + _fr_terr_ext_wall(final_wall[3]); + case 3: + _fr_terr_ext_wall(final_wall[2]); + case 2: + _fr_terr_ext_wall(final_wall[1]); + case 1: + _fr_terr_ext_wall(final_wall[0]); + case 0: + break; + } +} + + // currently always assumes full width... ie. is really broken + +#define sgn(x, y) (x > y ? 1 : (x == y ? 0 : -1)) + +#define io_wall_cmp(iwp, cmp, owp) ((*(i_wall + (iwp << 1) + 1)) cmp(*(o_wall + (owp << 1) + 1))) +#define io_wall_sgn(iwp, owp) (sgn((*(i_wall + (iwp << 1) + 1)), (*(o_wall + (owp << 1) + 1)))) +#define dst_from_i(dwp, iwp) \ + { \ + (*(dst_wall + (dwp << 1) + 1)) = (*(i_wall + (iwp << 1) + 1)); \ + (*(dst_wall + (dwp << 1))) = (*(i_wall + (iwp << 1))); \ + } +#define dst_from_o(dwp, owp) \ + { \ + (*(dst_wall + (dwp << 1) + 1)) = (*(o_wall + (owp << 1) + 1)); \ + (*(dst_wall + (dwp << 1))) = (*(o_wall + (owp << 1))); \ + } + +int f_is_i; + +/* + * currently broken when the following is true: 40000 40000 + * /| as the outside gap, and | | inside ie. 10000 20000 20000 20000 + * / | | | o_wall 10000 10000 i_wall 10000 10000 + * 00000 00000 + * the problem is 40 40 > 10 20, and 10 <= 10 but 20 ! <= 10 so it thinks bottom crossing failure + */ +// a hack is now in to fix this, should be rewritten in assembler as well +int merge_walls(fix *dst_wall, fix *i_wall, int i_cnt, fix *o_wall, int o_cnt) { + int cur_i = 0, cur_o = 0, f_cnt = 0, tmp1, tmp2; + int sgn0, sgn1, sgn2, sgn3; + + f_is_i = TRUE; + while (cur_i < i_cnt) { + while (io_wall_cmp(0, <=, 3) && io_wall_cmp(1, <=, 2)) // skip to next outer wall, this one is above our gap + { + o_wall += 8; + if (++cur_o >= o_cnt) + return f_cnt; + } + if (io_wall_cmp(3, >=, 0) && io_wall_cmp(2, >=, 1)) // if gap bottom above current outer wall, skip to next gap + { + i_wall += 8; + f_is_i = FALSE; + if (++cur_i >= i_cnt) + return f_cnt; + else + continue; + } + f_cnt++; + if ((sgn0 = io_wall_sgn(0, 0)) != (sgn1 = io_wall_sgn(1, 1))) // tmp is gap top below outer wall top + { + if (sgn0 == 0) + tmp1 = (sgn1 <= 0); + else if (sgn1 == 0) + tmp1 = (sgn0 <= 0); + else { + // Warning(("top crossing case\n")); // crossing case sucks rocks + tmp1 = 0; + } + } else + tmp1 = (sgn0 <= 0); + if (tmp1) { + dst_from_i(0, 0); + dst_from_i(1, 1); + } else { + dst_from_o(0, 0); + dst_from_o(1, 1); + } + if ((sgn3 = io_wall_sgn(3, 3)) != (sgn2 = io_wall_sgn(2, 2))) // tmp is gap bottom above outer wall bottom + { + if (sgn2 == 0) + tmp2 = (sgn3 >= 0); + else if (sgn3 == 0) + tmp2 = (sgn2 >= 0); + else { + // Warning(("bottom crossing case\n")); + tmp2 = 0; + } + } else + tmp2 = (sgn2 >= 0); + f_is_i = (f_is_i && tmp1 && tmp2); + if (tmp2) { + dst_from_i(2, 2); + dst_from_i(3, 3); + i_wall += 8; + if (++cur_i >= i_cnt) + break; + } else { + dst_from_o(2, 2); + dst_from_o(3, 3); + o_wall += 8; + if (++cur_o >= o_cnt) + break; + } + dst_wall += 8; + } + return f_cnt; +} + +uchar solid_chk[4] = {FMK_INT_NW, FMK_INT_EW, FMK_INT_SW, FMK_INT_WW}; +uchar clear_chk[4] = {FMK_NW, FMK_EW, FMK_SW, FMK_WW}; + +// local statics dont actually work, since i define static null in debug, so this is here +static int last_ocnt = -1; + +// look around at all my playthings +// eighteen percent a year, for nothing +static void _fr_parse_wall(int which) { + int icnt, fcnt, ocnt, useocnt, are_solid; + MapElem *oth_mptr; + uchar oth_hgts[3], oth_fo; + + oth_mptr = _fdt_mptr + wall_adds[which]; + + if (_fdt_me_flags & MAP_FRIEND_FULL_MASK) + _game_fr_tmap = me_tmap_wall(oth_mptr); + else + _game_fr_tmap = _fdt_wmap; + + if (me_clearsolid(_fdt_mptr) & solid_chk[which]) { + _fr_sdbg(STATS, _frp.stats.hitsolid++); + are_solid = 2; + } else { + oth_fo = face_obstruct[me_tiletype(oth_mptr)][(which + 2) & 3]; + are_solid = (oth_fo == 0xff); + } + + if (are_solid) // totally solid on other side, just do us, baby baby + { + _fr_sdbg(STATS, if ((me_clearsolid(_fdt_mptr) & solid_chk[which]) == 0) _frp.stats.setsolid++); + _me_clearsolid(_fdt_mptr) |= solid_chk[which]; + // int walls doesnt know about parameters, so we lost + // if (_fdt_ttf->flags&FRFLRFLG_USEPR) + // { + fcnt = fr_get_ext_gap(&final_wall[0][0][0], _fdt_fo[which], _fdt_hgts, + (uchar *)mmask_facelet[me_bits_mirror(_fdt_mptr)]); + if (fcnt == 0) { + _me_clearsolid(_fdt_mptr) |= clear_chk[which]; + _fr_sdbg(STATS, _frp.stats.setclear++); + } else + (*_fr_render_walls)(which, fcnt); + // } + // else + // _fr_terr_int_wall(FROUTERWALLS+which); + return; + } + _fr_sdbg(NO_RTF, return ); + // otherwise, grind away + oth_hgts[FDT_PT_FLR] = me_height_flr(oth_mptr); + oth_hgts[FDT_PT_CEIL] = MAX_HGT - me_height_ceil(oth_mptr); + oth_hgts[FDT_PT_PARM] = me_param(oth_mptr); + ocnt = _fr_get_anti_gap(&outer_wall[0][0][0], oth_fo, oth_hgts, (uchar *)mmask_facelet[me_bits_mirror(oth_mptr)]); + if (ocnt != ANTI_RET_PUNTALL) { + useocnt = (ocnt & ANTI_MASK_COUNT); + if ((ocnt & ANTI_MASK_PUNTBOT) == 0) { // touch up the bottom edge + if (useocnt != last_ocnt) { // we want to store the ocnt we actually filled, not ended with + last_ocnt = useocnt; + outer_wall[useocnt][1][0] = outer_wall[useocnt][2][0] = fix_make(1, 0); + outer_wall[useocnt][0][0] = outer_wall[useocnt][2][1] = outer_wall[useocnt][3][0] = + outer_wall[useocnt][3][1] = 0; + } + useocnt++; + } + if ((ocnt & ANTI_MASK_PUNTTOP) == 0) + use_outer_wall = &outer_wall[0][0][0]; + else { + use_outer_wall = &outer_wall[1][0][0]; + useocnt--; + } + _fr_sdbg(SANITY, if (useocnt == 0) mprintf("_fr_parse_wall: have 0 usecnt, but didnt punt yet\n")); + _fr_sdbg(SANITY, + if (_fdt_fo[which] == 0xff) mprint("_fr_parse_wall: local fo==0xff, wallbits should have been 0")); + icnt = fr_get_ext_gap(&inner_wall[0][0][0], _fdt_fo[which], _fdt_hgts, + (uchar *)mmask_facelet[me_bits_mirror(_fdt_mptr)]); + + fcnt = merge_walls(&final_wall[0][0][0], &inner_wall[0][0][0], icnt, use_outer_wall, useocnt); + if ((f_is_i) && (fcnt == icnt)) { + _fr_sdbg(STATS, _frp.stats.fisisolid++); + _me_clearsolid(_fdt_mptr) |= solid_chk[which]; + } + (*_fr_render_walls)(which, fcnt); + +#ifdef HACK_SHOW + { + int i; + mprintf("%d outer (%d) are:\n", useocnt, me_tiletype(oth_mptr)); + for (i = 0; i < useocnt; i++) + hack_show(use_outer_wall + (i * 8)); + mprintf("%d inner (%d) are:\n", icnt, me_tiletype(_fdt_mptr)); + for (i = 0; i < icnt; i++) + hack_show(inner_wall[i]); + mprintf("%d final walls be:\n", fcnt); + for (i = 0; i < fcnt; i++) + hack_show(final_wall[i]); + } +#endif + + } else + fcnt = 0; + if (fcnt == 0) { + _me_clearsolid(_fdt_mptr) |= clear_chk[which]; // or just shift here? who can tell + _fr_sdbg(STATS, _frp.stats.setclear++); + } +} + +// take the mptr, decode it's clip vectors, and set initial wall masks for the tile +void parse_clip_tile() {} + + // so, ahh, should have wall bits for floor and ceiling, so we can start clipping + +#if _fr_defdbg(STATS) +#define wall_check(wmask1, wmask2, w_id) \ + if ((_fdt_mask & wmask1) && (_fdt_ttw.wallbits & wmask2)) \ + if ((me_clearsolid(_fdt_mptr) & wmask1) != 0) { \ + _fr_sdbg(STATS, _frp.stats.hitclear++); \ + } else if ((me_subclip(_fdt_mptr) & wmask1) != 0) { \ + _fr_sdbg(STATS, _frp.stats.hitsc++); \ + } else \ + _fr_parse_wall(w_id) +#else // do wacky shit to make this work +#define wall_check(wmask1, wmask2, w_id) \ + if ((_fdt_mask & wmask1) && (_fdt_ttw.wallbits & wmask2) && ((me_clearsolid(_fdt_mptr) & wmask1) == 0) && \ + ((me_subclip(_fdt_mptr) & wmask1) == 0)) \ + _fr_parse_wall(w_id) +#endif + +#include "tilename.h" + +// if you're so special, why aren't you dead +// implicit parameters are _fdt_x,_fdt_y,_fdt_mptr,_fdt_mask +void fr_draw_tile(void) { + if ((_fdt_tt = me_tiletype(_fdt_mptr)) == TILE_SOLID) // really, clip should deal + { + me_subclip_set(_fdt_mptr, SUBCLIP_OUT_OF_CONE); // sure, deal with it... + return; // so make this a warn later + } + if (me_subclip(_fdt_mptr) == SUBCLIP_OUT_OF_CONE) { + return; + } + + _fr_ndbg(NO_SUB_CLIP, parse_clip_tile()); + _fr_sdbg(SHOW_BASE, if (_fr_terr_prim) _fr_terr_base(0); else _fr_terr_dumb_base(0)); + _fdt_ttw = tile_walls[_fdt_tt]; + _fdt_ttf = &tile_floors[_fdt_tt]; + _fdt_hgts[FDT_PT_FLR] = me_height_flr(_fdt_mptr); + _fdt_hgts[FDT_PT_CEIL] = MAX_HGT - me_height_ceil(_fdt_mptr); + _fdt_hgts[FDT_PT_PARM] = me_param(_fdt_mptr); + _fdt_hgts[FDT_PT_FPRM] = me_height_flr(_fdt_mptr) + me_param(_fdt_mptr); + _fdt_hgts[FDT_PT_CPRM] = MAX_HGT - me_height_ceil(_fdt_mptr) - me_param(_fdt_mptr); + _fdt_cur_parm = _fdt_fix_parm = _fr_fhgt_list[_fdt_hgts[FDT_PT_PARM]]; + _fdt_fo = face_obstruct[_fdt_tt]; + _fdt_wmap = me_tmap_wall(_fdt_mptr); + _fdt_me_flags = me_flags(_fdt_mptr); + _fdt_slock = _fr_fhgt_list[_fdt_me_flags & MAP_VLOCK_MASK] >> 8; + + if (_frp.faces.cyber && _fdt_terr) { + int tmp = me_bits_flip(_fdt_mptr); + void fr_terr_cspace_pick(uchar do_w); + + if (tmp != last_csp_fr) + fr_terr_cspace_pick(last_csp_fr = tmp); + } + + // how about external walls, eh? + wall_check(FMK_NW, FRWALLNORTH, 0); + wall_check(FMK_EW, FRWALLEAST, 1); + wall_check(FMK_SW, FRWALLSOUTH, 2); + wall_check(FMK_WW, FRWALLWEST, 3); + + _game_fr_tmap = _fdt_wmap; + _fdt_icnt = (_fdt_ttw.wallbits & FRWALLINT); + // set real flip for internal faces now + if (_fdt_icnt) { + flip_setup(4); // 4 is secret internal face code + switch (_fdt_icnt) { +#ifdef USE_OCT + case 6: + _fdt_cur_parm = FROCTNUM; // recompute wacky prm stuff + _fdt_hgts[FDT_PT_FPRM] = me_height_flr(_fdt_mptr) + me_param(_fdt_mptr); + // this cant be done yet + // _fdt_hgts[FDT_PT_CPRM]=MAX_HGT-me_height_ceil(_fdt_mptr)-me_param(_fdt_mptr); + // _fdt_hgts[FDT_PT_PARM]=me_param(_fdt_mptr); + _fr_terr_int_wall(_fdt_ttw.wallbase + 5); // or hmm.. a temp + case 5: + _fr_terr_int_wall(_fdt_ttw.wallbase + 4); // variable and then + case 4: + _fr_terr_int_wall(_fdt_ttw.wallbase + 3); // increment it in + case 3: + _fr_terr_int_wall(_fdt_ttw.wallbase + 2); // each case?? +#endif + case 2: + _fr_terr_int_wall(_fdt_ttw.wallbase + 1); + case 1: + _fr_terr_int_wall(_fdt_ttw.wallbase + 0); + case 0: + break; + } + } +#ifdef USE_OCT // in case it was overloaded by FROCTNUM, not needed if no oct tiles + _fdt_cur_parm = _fdt_fix_parm; +#endif + _game_fr_tmap = me_tmap_flr(_fdt_mptr); + _fr_terr_flr(); + _game_fr_tmap = me_tmap_ceil(_fdt_mptr); + _fr_terr_ceil(); + // now check object cache + + // FIXME HAX HAX HAX why does this segfault? + _fr_parse_obj(); + +#ifdef CLEAR_AS_WE_GO + me_subclip_set(_fdt_mptr, SUBCLIP_OUT_OF_CONE); // sure, deal with it... +#endif + // mprintf("-"); +} + + // when all the leaves have fallen and turned to dust + // will we remain entrenched within our ways + // indifference, the plague that moves throughout this land + +#define FRT_PTS_POLY 1 +#define FRT_PTS_TMAP 2 +#define FRT_PTS_LITE 4 +#define FRT_PTS_CYB 8 + +#define FRT_FLAT 0 +#define FRT_LIT 1 +#define FRT_TMAP 2 +#define FRT_CSPACE 4 +#define FRT_NULL 6 +#define FRT_MAX_F 7 + +static void (*_fr_tabs_int_wall[FRT_MAX_F])(int wall_id) = { + _fr_flat_int_wall, _fr_flat_lit_int_wall, _fr_tmap_int_wall, _fr_tmap_lit_int_wall, + _fr_cspace_wire_int_wall, _fr_cspace_full_int_wall, _fr_null_int_wall}; +static void (*_fr_tabs_ext_wall[FRT_MAX_F])(fix pt_list[4][2]) = { + _fr_flat_ext_wall, _fr_flat_lit_ext_wall, _fr_tmap_ext_wall, _fr_tmap_lit_ext_wall, + _fr_cspace_wire_ext_wall, _fr_cspace_full_ext_wall, _fr_null_ext_wall}; +static void (*_fr_tabs_flr[FRT_MAX_F])(void) = {_fr_flat_flr, _fr_flat_lit_flr, _fr_tmap_flr, + _fr_tmap_lit_flr, _fr_cspace_wire_flr, _fr_cspace_full_flr, + _fr_null_flrceil}; +static void (*_fr_tabs_ceil[FRT_MAX_F])(void) = {_fr_flat_ceil, _fr_flat_lit_ceil, _fr_tmap_ceil, + _fr_tmap_lit_ceil, _fr_cspace_wire_ceil, _fr_cspace_full_ceil, + _fr_null_flrceil}; + +#define CSPACE_WALLS + +void fr_terr_cspace_pick(uchar do_wall) { + int to_do, wall_do; + + if (do_wall) { + wall_do = to_do = FRT_CSPACE + 1; // FRT_FLAT + if (do_wall >= 2) { + if (_fr_curflags & FR_PICKUPM_MASK) + wall_do = FRT_NULL; + else + wall_do--; + } + } else { + if (_fr_curflags & FR_PICKUPM_MASK) + wall_do = to_do = FRT_NULL; + else + wall_do = to_do = FRT_CSPACE; + } + + _fr_terr_int_wall = _fr_tabs_int_wall[wall_do]; + _fr_terr_ext_wall = _fr_tabs_ext_wall[wall_do]; + _fr_terr_ceil = _fr_tabs_ceil[to_do]; + _fr_terr_flr = _fr_tabs_flr[to_do]; +} + +// setup the point revector +void fr_terr_frame_start(void) { + int wall_do, ceil_do, flr_do; + + // cspace + if (_fr_curflags & FR_PICKUPM_MASK) + if (_frp.faces.cyber) + wall_do = ceil_do = flr_do = FRT_NULL; + else + wall_do = ceil_do = flr_do = FRT_FLAT; + else if (_frp.faces.cyber) + wall_do = ceil_do = flr_do = _frp.faces.cyber_full + FRT_CSPACE; + else { // realspace + int lmod = _frp_light_bits_any() ? 1 : 0; + wall_do = lmod + (((_frp.faces.main) && (_frp.faces.wall)) ? FRT_TMAP : 0); + ceil_do = lmod + (((_frp.faces.main) && (_frp.faces.ceiling)) ? FRT_TMAP : 0); + flr_do = lmod + (((_frp.faces.main) && (_frp.faces.floor)) ? FRT_TMAP : 0); + } + + _fr_terr_int_wall = _fr_tabs_int_wall[wall_do]; + _fr_terr_ext_wall = _fr_tabs_ext_wall[wall_do]; + _fr_terr_ceil = _fr_tabs_ceil[ceil_do]; + _fr_terr_flr = _fr_tabs_flr[flr_do]; + + _fr_parse_obj = render_parse_obj; + + _fr_render_walls = _render_3d_walls; + + // setup the tmp points for this frame + g3_alloc_list(FDT_TMPPTCNT, _fdt_tmppts); + + last_csp_fr = 0; // initially no filled mode + _fdt_terr = TRUE; +} + +void fr_terr_frame_end(void) { + void fr_tfunc_grab_start(void); + fr_tfunc_grab_start(); // set up the physics facelet indirections... + _fdt_terr = FALSE; + g3_free_list(FDT_TMPPTCNT, _fdt_tmppts); +} + +#if _fr_defdbg(CURSOR) +// wrapped within the walkman with a halo of distortion +// aural contraceptive aborting pregnant conversation +void fr_set_cursor(int x, int y) { + _fr_cursorx = x; + _fr_cursory = y; +} +#endif + +#define TF_DIRECT + +#ifdef TF_DIRECT +#include "tfdirect.h" +#include "ss_flet.h" +// this is a direct render <-> tfunc connection +// ie. we dont accumulate and then send facelets, we just send as we go +// by calling the tfutil stuff, namely facelet_solve and so on +// thus we never build 3d surfaces, just distances and normals +// hopefully, this will cause things to, gasp, speed up + +//#define AddTmapFlags(fl) if (_game_fr_tmap<10) fl|=SS_BCD_MISC_CLIMB +#define AddTmapFlags(fl) \ + if (textprops[_game_fr_tmap].friction_climb) \ + fl |= SS_BCD_MISC_CLIMB + +uchar wall_pc[] = {SS_BCD_PRIM_NEG_Y | SS_BCD_TYPE_WALL, SS_BCD_PRIM_NEG_X | SS_BCD_TYPE_WALL, + SS_BCD_PRIM_YAXIS | SS_BCD_TYPE_WALL, SS_BCD_PRIM_XAXIS | SS_BCD_TYPE_WALL, + SS_BCD_PRIM_MULTI | SS_BCD_TYPE_WALL}; + +// secret gnosis becomes the order of the day +// we set up primary and transform the center point into our space +// then we just pass it on to facelet_solve, none the worse for wear... +static void _render_tfunc_walls(int which, int cnt) { + // fuck it, ext_walls are always ext, so punt generality, lets go + fix pt[3]; + int pc = wall_pc[which]; + switch (which) // set up localized point set + { // NESW + case 0: + pt[2] = fix_1 - tf_loc_pt[1]; + pt[0] = tf_loc_pt[0]; + break; + case 1: + pt[2] = fix_1 - tf_loc_pt[0]; + pt[0] = fix_1 - tf_loc_pt[1]; + break; + case 2: + pt[2] = tf_loc_pt[1]; + pt[0] = fix_1 - tf_loc_pt[0]; + break; + case 3: + pt[2] = tf_loc_pt[0]; + pt[0] = tf_loc_pt[1]; + break; + } + pt[1] = tf_loc_pt[2]; + AddTmapFlags(pc); + // if (pc&SS_BCD_MISC_CLIMB) mprintf("Set climbable... %x\n",pc); + switch (cnt) { + case 4: + tf_solve_aligned_face(pt, final_wall[3], pc, NULL); + case 3: + tf_solve_aligned_face(pt, final_wall[2], pc, NULL); + case 2: + tf_solve_aligned_face(pt, final_wall[1], pc, NULL); + case 1: + tf_solve_aligned_face(pt, final_wall[0], pc, NULL); + case 0: + break; + } +} + +static void _fr_tfunc_diag_wall(int wall_id) { + fix pt[3], C; + int pfl; + switch (wall_id) { + case 0: // nw - se, normal to upper ne + diag_norms[0] = fix_inv_root2; + diag_norms[1] = fix_inv_root2; + C = tf_loc_pt[0] - tf_loc_pt[1]; + pt[0] = (fix_1 - C) / 2; + pt[2] = tf_loc_pt[1] - pt[0]; + break; + case 1: // sw - ne, normal to lower se + diag_norms[0] = fix_inv_root2; + diag_norms[1] = -fix_inv_root2; + C = tf_loc_pt[0] + tf_loc_pt[1]; + pt[0] = C / 2; + pt[2] = pt[0] - tf_loc_pt[1]; + break; + case 2: // ne - sw, normal to upper nw + diag_norms[0] = -fix_inv_root2; + diag_norms[1] = fix_inv_root2; + C = tf_loc_pt[0] + tf_loc_pt[1]; + pt[0] = fix_1 - (C / 2); + pt[2] = tf_loc_pt[1] - (C / 2); + break; + case 3: // nw - se, normal to lower sw + diag_norms[0] = -fix_inv_root2; + diag_norms[1] = -fix_inv_root2; + C = tf_loc_pt[0] - tf_loc_pt[1]; + pt[0] = (fix_1 - C) / 2; + pt[2] = pt[0] - tf_loc_pt[1]; + break; + } + pt[1] = tf_loc_pt[2]; + pfl = wall_pc[4] | TF_FLG_BOX_FULL; + AddTmapFlags(pfl); + tf_solve_remetriced_face(pt, tf_diag_walls, pfl, diag_norms, fix_inv_root2); +} + +// hack this for now for diagonals +static void _fr_tfunc_int_wall(int wall_id) { + if ((_fdt_mask & FACELET_MASK_I) == 0) + return; + _fr_tfunc_diag_wall(wall_id); +} + +int _fr_tfunc_flr_normal(fix *vec, int *lflg, int fnorm_entry) { + int rv; + if (me_bits_mirror_x(_fdt_mptr) == (MAP_FFLAT << MAP_MIRROR_SHF)) + fnorm_entry = FRFNORM_VFULL; + else + fnorm_entry &= 0xf; + switch (fnorm_entry) { + case FRFNORM_VZERO: + case FRFNORM_VFULL: + *lflg |= SS_BCD_PRIM_ZAXIS | SS_BCD_TYPE_FLOOR; + return 0; + + case FRFNORM_SLPS: + vec[0] = 0; + vec[1] = slope_norm[_fdt_hgts[FDT_PT_PARM]][2]; + rv = 1; + break; + case FRFNORM_SLPW: + vec[0] = slope_norm[_fdt_hgts[FDT_PT_PARM]][2]; + vec[1] = 0; + rv = 2; + break; + case FRFNORM_SLPN: + vec[0] = 0; + vec[1] = slope_norm[_fdt_hgts[FDT_PT_PARM]][0]; + rv = 3; + break; + case FRFNORM_SLPE: + vec[0] = slope_norm[_fdt_hgts[FDT_PT_PARM]][0]; + vec[1] = 0; + rv = 4; + break; + } + vec[2] = slope_norm[_fdt_hgts[FDT_PT_PARM]][1]; + *lflg |= SS_BCD_PRIM_MULTI | SS_BCD_TYPE_FLOOR; + return rv; +} + +int _fr_tfunc_ceil_normal(fix *vec, int *lflg, int fnorm_entry) { + int tmp = me_bits_mirror(_fdt_mptr), rv; + switch (tmp) { + case MAP_CFLAT: + fnorm_entry = FRFNORM_VFULL; + break; + case MAP_MIRROR: + fnorm_entry = fnorm_entry ^ 0x2; // fall through to the mask + case MAP_FFLAT: + case MAP_MATCH: + fnorm_entry &= 0xf; + } + switch (fnorm_entry) { + case FRFNORM_VZERO: + case FRFNORM_VZ_MIR: + case FRFNORM_VF_MIR: + case FRFNORM_VFULL: + *lflg |= SS_BCD_PRIM_NEG_Z | SS_BCD_TYPE_CEIL; + return 0; + + case FRFNORM_SLPS: + vec[0] = 0; + vec[1] = slope_norm[_fdt_hgts[FDT_PT_PARM]][0]; + rv = 1; + break; + case FRFNORM_SLPW: + vec[0] = slope_norm[_fdt_hgts[FDT_PT_PARM]][0]; + vec[1] = 0; + rv = 2; + break; + case FRFNORM_SLPN: + vec[0] = 0; + vec[1] = slope_norm[_fdt_hgts[FDT_PT_PARM]][2]; + rv = 3; + break; + case FRFNORM_SLPE: + vec[0] = slope_norm[_fdt_hgts[FDT_PT_PARM]][2]; + vec[1] = 0; + rv = 4; + break; + } + vec[2] = -slope_norm[_fdt_hgts[FDT_PT_PARM]][1]; + *lflg |= SS_BCD_PRIM_MULTI | SS_BCD_TYPE_CEIL; + return rv; +} + +static fix _tfunc_real_floor[4][2] = {{0, fix_1}, {fix_1, fix_1}, {fix_1, 0}, {0, 0}}; + +static fix _tfunc_nrm[3], _tfunc_rpts[3]; +static int _tfunc_flg; + +int do_fc_trans(fix *xy, fix *sc, fix *vals) { + int rm = fix_div(fix_1, sc[0]); + vals[0] = fix_mul(xy[0], sc[0]) + fix_mul(xy[1], -sc[1]); + vals[1] = fix_mul(xy[0], sc[1]) + fix_mul(xy[1], sc[0]); + // mprintf("dft: from %x %x and %x %x stores %x %x, rm %x\n",xy[0],xy[1],sc[0],sc[1],vals[0],vals[1],rm); + return rm; +} + +void do_floor_element(int nrm_mask) { + int code; + fix sc[2], xy[2]; + + _tfunc_flg = 0; + code = _fr_tfunc_flr_normal(_tfunc_nrm, &_tfunc_flg, nrm_mask & 0xf); + xy[1] = tf_loc_pt[2] - _fr_fhgt_list[_fdt_hgts[FDT_PT_FLR]]; + switch (code) { + case 0: + + *(g3s_vector *)_tfunc_rpts = + *(g3s_vector *)tf_loc_pt; // sub faster version? _memcpy32l(_tfunc_rpts,tf_loc_pt,3); + _tfunc_rpts[2] = xy[1]; + _tfunc_real_floor[0][1] = _tfunc_real_floor[1][1] = fix_1; + tf_solve_aligned_face(_tfunc_rpts, _tfunc_real_floor, _tfunc_flg | TF_FLG_BOX_FULL, NULL); + return; // flat floor + case 1: + xy[0] = tf_loc_pt[1]; + _tfunc_rpts[0] = tf_loc_pt[0]; + sc[1] = _tfunc_nrm[1]; + break; // N up (slpS) slope + case 2: + xy[0] = tf_loc_pt[0]; + _tfunc_rpts[0] = fix_1 - tf_loc_pt[1]; + sc[1] = _tfunc_nrm[0]; + break; // E up slope + case 3: + xy[0] = fix_1 - tf_loc_pt[1]; + _tfunc_rpts[0] = fix_1 - tf_loc_pt[0]; + sc[1] = -_tfunc_nrm[1]; + break; // S up slope + case 4: + xy[0] = fix_1 - tf_loc_pt[0]; + _tfunc_rpts[0] = tf_loc_pt[1]; + sc[1] = -_tfunc_nrm[0]; + break; // W up slope + } + sc[0] = _tfunc_nrm[2]; + _tfunc_real_floor[0][1] = _tfunc_real_floor[1][1] = do_fc_trans(xy, sc, &_tfunc_rpts[1]); + if (nrm_mask <= 0xf) + tf_solve_aligned_face(_tfunc_rpts, _tfunc_real_floor, _tfunc_flg | TF_FLG_BOX_FULL, _tfunc_nrm); +} + +void do_ceil_element(int nrm_mask) { + int code; + fix sc[2], xy[2]; + + _tfunc_flg = 0; + code = _fr_tfunc_ceil_normal(_tfunc_nrm, &_tfunc_flg, nrm_mask & 0xf); + xy[1] = _fr_fhgt_list[_fdt_hgts[FDT_PT_CEIL]] - tf_loc_pt[2]; + switch (code) { + case 0: + _tfunc_rpts[0] = tf_loc_pt[0]; + _tfunc_rpts[1] = fix_1 - tf_loc_pt[1]; + _tfunc_rpts[2] = xy[1]; + _tfunc_real_floor[0][1] = _tfunc_real_floor[1][1] = fix_1; + tf_solve_aligned_face(_tfunc_rpts, _tfunc_real_floor, _tfunc_flg | TF_FLG_BOX_FULL, NULL); + return; // flat ceil + case 1: + xy[0] = fix_1 - tf_loc_pt[1]; + _tfunc_rpts[0] = tf_loc_pt[0]; + sc[1] = -_tfunc_nrm[1]; + break; // N up (slpS) slope + case 2: + xy[0] = fix_1 - tf_loc_pt[0]; + _tfunc_rpts[0] = fix_1 - tf_loc_pt[1]; + sc[1] = -_tfunc_nrm[0]; + break; // E up slope + case 3: + xy[0] = tf_loc_pt[1]; + _tfunc_rpts[0] = fix_1 - tf_loc_pt[0]; + sc[1] = _tfunc_nrm[1]; + break; // S up slope + case 4: + xy[0] = tf_loc_pt[0]; + _tfunc_rpts[0] = tf_loc_pt[1]; + sc[1] = _tfunc_nrm[0]; + break; // W up slope + } + sc[0] = -_tfunc_nrm[2]; + _tfunc_real_floor[0][1] = _tfunc_real_floor[1][1] = do_fc_trans(xy, sc, &_tfunc_rpts[1]); + if (nrm_mask <= 0xf) + tf_solve_aligned_face(_tfunc_rpts, _tfunc_real_floor, _tfunc_flg | TF_FLG_BOX_FULL, _tfunc_nrm); +} + +static void _fr_tfunc_flr(void) { + uchar nrm_mask = fr_fnorm_list[_fdt_tt]; + int fcecnt; + + if ((_fdt_mask & FACELET_MASK_F) == 0) + return; + fcecnt = _fdt_ttf->flags >> FRFLRSHF_2ELEM; // 0 or 1 + // if ((_fdt_tt!=TILE_OPEN)&&(_fdt_tt!=TILE_SOLID)) + // mprintf("BonusF %d (%d)..",nrm_mask,_fdt_tt); + if (fcecnt == 0) + do_floor_element(nrm_mask & 0xf); + else { + int tmp = me_bits_mirror(_fdt_mptr); + uchar icky = (_fdt_tt >= TILE_SLOPECV_NW); + if (tmp == MAP_FFLAT) + do_floor_element(FRFNORM_VFULL); + else if (icky) { + do_floor_element(0xf0 | (nrm_mask & 0xf)); // now make it 3pt... + _tfunc_real_floor[1][0] = 0; + tf_solve_aligned_face(_tfunc_rpts, &_tfunc_real_floor[1], _tfunc_flg | TF_FLG_3PNT_MASK, _tfunc_nrm); + _tfunc_real_floor[1][0] = fix_1; + do_floor_element(0xf0 | (nrm_mask >> 4)); // and so on... + tf_solve_aligned_face(_tfunc_rpts, &_tfunc_real_floor[1], _tfunc_flg | TF_FLG_3PNT_MASK, _tfunc_nrm); + } else { + do_floor_element(nrm_mask & 0xf); + do_floor_element(nrm_mask >> 4); + } + } +} + +static void _fr_tfunc_ceil(void) { + uchar nrm_mask = fr_fnorm_list[_fdt_tt]; + int fcecnt; + + if ((_fdt_mask & FACELET_MASK_C) == 0) + return; + if (_fdt_ttf->flags & FRFLRFLG_NOTOP) + return; + fcecnt = _fdt_ttf->flags >> FRFLRSHF_2ELEM; // 0 or 1 + // if ((_fdt_tt!=TILE_OPEN)&&(_fdt_tt!=TILE_SOLID)) + // mprintf("BonusC %d (%d)..",nrm_mask,_fdt_tt); + if (fcecnt == 0) + do_ceil_element(nrm_mask); + else { + int tmp = me_bits_mirror(_fdt_mptr); + uchar icky = (_fdt_tt < TILE_SLOPECV_NW), hard; + if (tmp == MAP_CFLAT) + hard = 0; + else if (tmp == MAP_MIRROR) + hard = !icky; + else + hard = icky; + if (hard) { // broken, depends on icky + do_ceil_element(0xf0 | (nrm_mask & 0xf)); + tf_solve_aligned_face(_tfunc_rpts, _tfunc_real_floor, _tfunc_flg | TF_FLG_3PNT_MASK, _tfunc_nrm); + do_ceil_element(0xf0 | (nrm_mask >> 4)); + _tfunc_real_floor[2][0] = 0; + tf_solve_aligned_face(_tfunc_rpts, _tfunc_real_floor, _tfunc_flg | TF_FLG_3PNT_MASK, _tfunc_nrm); + _tfunc_real_floor[2][0] = fix_1; + } else { + do_ceil_element(nrm_mask & 0xf); + do_ceil_element(nrm_mask >> 4); + } + } +} + +// check for terrain objects +static void _fr_tfunc_obj(void) { facelet_parse_obj(); } + +void fr_tfunc_grab_start(void) { + _fr_terr_int_wall = _fr_tfunc_int_wall; + _fr_terr_ceil = _fr_tfunc_ceil; + _fr_terr_flr = _fr_tfunc_flr; + + _fr_parse_obj = _fr_tfunc_obj; + + _fr_render_walls = _render_tfunc_walls; +} + +void fr_tfunc_grab_fast(int mask) { + me_subclip_set(_fdt_mptr, SUBCLIP_FULL_TILE); + _fdt_mask = mask; + fr_draw_tile(); + // me_subclip_set(_fdt_mptr,SUBCLIP_OUT_OF_CONE); +} +#endif // TFUNC_SUPPORT + +#ifdef WHOSE_MUMP + +// add to everything.... arrrrgghgh +#ifndef SHIP +#define CheckRendWallBt(btid) ((me_bits_rend4(c_t) & btid) == 0) +#define CheckRendOthBt(btid) ((me_bits_rend3(c_t) & btid) == 0) +#else +#define CheckRendWallBt(btid) (TRUE) +#define CheckRendOthBt(btid) (TRUE) +#endif + +#endif + +//============================================================================== +// Edge-finding routines. +//============================================================================== +uchar edge_get_fandc(MapElem *mp, int c_edge, char *e_list); + +#define EDGE_GET + +#include "fredge.h" +#ifdef EDGE_GET + +uchar edge_get_fandc(MapElem *mp, int c_edge, char *e_list) { + uchar *mmptr, fo, p = me_param(mp), in_fo; + in_fo = face_obstruct[me_tiletype(mp)][c_edge]; + if ((me_tiletype(mp) == TILE_SOLID) || (in_fo == 0xff)) { + e_list[0] = e_list[1] = MAX_HGT; + e_list[2] = e_list[3] = MAX_HGT; + return FALSE; + } else if (p == 0) { + e_list[0] = e_list[1] = me_height_flr(mp); + e_list[2] = e_list[3] = MAX_HGT - me_height_ceil(mp); + } else { + mmptr = (uchar *)mmask_facelet[me_bits_mirror(mp)]; + e_list[0] = e_list[1] = me_height_flr(mp); + fo = (in_fo ^ mmptr[0]) & mmptr[1]; + if (fo & FO_L_PARM) + e_list[0] += p; + if (fo & FO_R_PARM) + e_list[1] += p; + e_list[2] = e_list[3] = MAX_HGT - me_height_ceil(mp); + fo = (in_fo ^ mmptr[2]) & mmptr[3]; + if (fo & FO_L_PARM) + e_list[2] -= p; + if (fo & FO_R_PARM) + e_list[3] -= p; + } + return TRUE; +} + +char edge_vals[3]; +// returns left and right heights for a map edge +// ceil_p is 1 if it is the ceiling you care about +char *map_get_edge(void *omp, int edge, int ceil_p) { + uchar *mmptr, fo, p, in_fo; + MapElem *mp = (MapElem *)omp; + + p = me_param(mp); + in_fo = face_obstruct[me_tiletype(mp)][edge]; + + if (p == 0) { + if (ceil_p) + edge_vals[0] = edge_vals[1] = MAX_HGT - me_height_ceil(mp); + else + edge_vals[0] = edge_vals[1] = me_height_flr(mp); + } else { + mmptr = (uchar *)mmask_facelet[me_bits_mirror(mp)]; + if (ceil_p) { + edge_vals[0] = edge_vals[1] = MAX_HGT - me_height_ceil(mp); + fo = (in_fo ^ mmptr[2]) & mmptr[3]; + p = -p; + } else { + edge_vals[0] = edge_vals[1] = me_height_flr(mp); + fo = (in_fo ^ mmptr[0]) & mmptr[1]; + } + if (fo & FO_L_PARM) + edge_vals[0] += p; + if (fo & FO_R_PARM) + edge_vals[1] += p; + } + return edge_vals; +} + +// 0 and 1 will be ceiling, 2 and 3 will be floor for internal, +4 for other + +// this is a really dumb way to a do this, really +// returns 0 if no edge space, 1 for flat, 2 for mini-step, 3 for step +// 4 for ledge, 5 for cliff +int get_edge_code(void *omp, int edge) { + char edge_list[8], d_list[4], x_diff[4]; + char el, el2, max_df[2], min_dc[2], gap[2]; + MapElem *mp = (MapElem *)omp, *oth_mp = mp + wall_adds[edge]; + + // get the real data, if no internal edge we go home and punt... + if (!edge_get_fandc(mp, edge, &edge_list[0])) { + edge_vals[0] = edge_vals[1] = edge_vals[2] = 0; + return MEDGE_NO_TILE; + } + edge_get_fandc(oth_mp, (edge + 2) & 3, &edge_list[4]); + + for (el = el2 = 0; el < 4; el += 2, el2 += 4) { + x_diff[el + 0] = edge_list[el + 5] - edge_list[el + 0]; // diff from us to it + x_diff[el + 1] = edge_list[el + 4] - edge_list[el + 1]; // fl,fr,cl,cr + d_list[el + 0] = edge_list[el2 + 2] - edge_list[el2 + 0]; // height diffs in square + d_list[el + 1] = edge_list[el2 + 3] - edge_list[el2 + 1]; // ourl,ourr,othl,othr + } + max_df[0] = lg_max(edge_list[5], edge_list[0]); + max_df[1] = lg_max(edge_list[4], edge_list[1]); + min_dc[0] = lg_min(edge_list[7], edge_list[2]); + min_dc[1] = lg_min(edge_list[6], edge_list[3]); + edge_vals[0] = gap[0] = min_dc[0] - max_df[0]; + edge_vals[1] = gap[1] = min_dc[1] - max_df[1]; + edge_vals[2] = (gap[0] + gap[1]) >> 1; // sure, average, not min or max + + if ((gap[0] <= 0) && (gap[1] <= 0)) + return MEDGE_NO_EGRESS; + else if ((x_diff[0] | x_diff[1]) == 0) + return MEDGE_FLAT_CASE; // should scale these based on zshf + else if ((abs(x_diff[0]) > 8) || (abs(x_diff[1]) > 8)) + return MEDGE_CLIFF_THING; + else if ((abs(x_diff[0]) > 4) || (abs(x_diff[1]) > 4)) + return MEDGE_LARGE_STEP; + else if ((abs(x_diff[0]) > 1) || (abs(x_diff[1]) > 1)) + return MEDGE_SMALL_STEP; + return MEDGE_NO_TILE; +} +#endif diff --git a/engine/src/GameSrc/frutil.c b/engine/src/GameSrc/frutil.c new file mode 100644 index 0000000..d4b3085 --- /dev/null +++ b/engine/src/GameSrc/frutil.c @@ -0,0 +1,151 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/frutil.c $ + * $Revision: 1.12 $ + * $Author: dc $ + * $Date: 1994/11/25 16:57:41 $ + * + * utilities for use in the basic indoor terrain rendering of tiles system + */ + +#include + +#include "faketime.h" + +#include "frtypes.h" +#include "frintern.h" +#include "frparams.h" +#include "frflags.h" + +uchar fr_cur_obj_col; +ushort fr_col_to_obj[256]; + +static char fr_str[15]; + +char *fr_get_frame_rate(void) { + fr_str[0] = '\0'; + if (_frp.time.last_frame_cnt > 0) { + if (_frp.time.last_chk_time != 0) { + long num = (*tmd_ticks - _frp.time.last_chk_time); + char mod; + + if (_frp.time.last_frame_cnt > 1) + num /= _frp.time.last_frame_cnt; + num = 28000 / num; + + snprintf(fr_str, sizeof(fr_str), "%ld", num); + + mod = strlen(fr_str); + fr_str[mod + 1] = fr_str[mod]; + fr_str[mod] = fr_str[mod - 1]; + fr_str[mod - 1] = fr_str[mod - 2]; + fr_str[mod - 2] = '.'; + _frp.time.last_frame_len = num / 100; + } + _frp.time.last_frame_cnt = 0; + } + _frp.time.last_chk_time = *tmd_ticks; + + INFO("%s", fr_str); + return fr_str; +} + +// look, vainly i try an reuse code from uw2 +// and, amazingly, it works... wow +#define SEARCH_DIAM 10 +/* uses xwid implicitly, as well as xhgt to determine bounds. note width is really xwid+2 + * checks within search_rad of x,y in the array pointed at by base + * looks for an object... + */ +uchar check_around(uchar *base, int x, int y) { + uchar curval; + int extloop, inloop, clen; + int dvec[2] = {0, 1}; + + for (clen = 1; clen < SEARCH_DIAM; clen++) /* for each radius */ + for (extloop = 0; extloop < 2; extloop++) /* two of each */ + { + for (inloop = 0; inloop < clen; inloop++) { + x += dvec[0]; + y += dvec[1]; + if (((x >= 0) && (x < _fr->draw_canvas.bm.w)) && ((y >= 0) && (y < _fr->draw_canvas.bm.h))) { + curval = *(base + (y * _fr->draw_canvas.bm.row) + x); + if ((curval >= FR_CUR_OBJ_BASE) && (curval < fr_cur_obj_col)) + return curval; + } + } + if (dvec[0] != 0) { + dvec[1] = -dvec[0]; + dvec[0] = 0; + } else { + dvec[0] = dvec[1]; + dvec[1] = 0; + } + } + return 0; +} + +// is transp is set, then the get at is done with transparency on, being able to look through gratings, etc. +// if it is false, then transparency is not used +ushort fr_get_real(fauxrend_context *cur_fr, int x, int y) { + int col, tmpcol; + if ((_fr_glob_flags | _fr->flags) & (FR_NORENDR_MASK | FR_SOLIDFR_MASK)) /* dont really render, call a game thing */ + return 0; + if (x < 0 || x >= cur_fr->draw_canvas.bm.w || + y < 0 || y >= cur_fr->draw_canvas.bm.h) return 0; + col = (int)(*((cur_fr->draw_canvas.bm.bits) + (y * cur_fr->draw_canvas.bm.row) + (x))); + // mprintf("Color %d, obj %d, max c %d at %d + // %d\n",col,(col>=FR_CUR_OBJ_BASE)?fr_col_to_obj[col-FR_CUR_OBJ_BASE]:0,fr_cur_obj_col,x,y); + if ((col >= FR_CUR_OBJ_BASE) && (col < fr_cur_obj_col)) // if we are actually exactly over an object + return (ushort)fr_col_to_obj[col - FR_CUR_OBJ_BASE]; // actual obj_id + // if we found an object nearby (what to do about transparent doors) + if ((tmpcol = check_around(cur_fr->draw_canvas.bm.bits, x, y))) + return (ushort)fr_col_to_obj[tmpcol - FR_CUR_OBJ_BASE]; // actual obj_id + else // its a wall folks, just a wall + return ((ushort)0) - ((ushort)col); // return a tmap as - (tmapid+1), or nothing as 0 +} + +int fr_cspace_idx(void) { + gr_set_fill_parm(1); + return 1; +} + +ushort fr_get_at(frc *fr, int x, int y, uchar transp) { + int (*fr_ptr_idx)(void) = fr_get_idx, of = _fr_glob_flags; + + _fr_top(fr); + _fr_glob_flags |= FR_PICKUPM_MASK; + if (!transp) + _fr_glob_flags |= FR_NOTRANS_MASK; + fr_cur_obj_col = FR_CUR_OBJ_BASE; + if (_frp.faces.cyber) + fr_get_idx = fr_cspace_idx; + else + fr_get_idx = fr_pickup_idx; + fr_rend(fr); + fr_get_idx = fr_ptr_idx; + _fr_glob_flags = of; + return fr_get_real(_fr, x, y); +} + +ushort fr_get_again(frc *fr, int x, int y) { + _fr_top(fr); + return fr_get_real(_fr, x, y); +} diff --git a/engine/src/GameSrc/fullamap.c b/engine/src/GameSrc/fullamap.c new file mode 100644 index 0000000..7dddc7c --- /dev/null +++ b/engine/src/GameSrc/fullamap.c @@ -0,0 +1,109 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/fullamap.c $ + * $Revision: 1.11 $ + * $Author: tjs $ + * $Date: 1994/11/09 11:23:27 $ + * + */ + +#include "tools.h" +#include "game_screen.h" +#include "input.h" + +#include "mainloop.h" +#include "amaploop.h" +#include "lvldata.h" + +extern grs_screen *svga_screen; +extern grs_screen *cit_screen; + +// ------------------- +// INTERNAL PROTOTYPES +// ------------------- +uchar amap_mouse_handler(uiEvent *ev, LGRegion *, intptr_t); +uchar amap_key_handler(uiEvent *ev, LGRegion *r, intptr_t user_data); +errtype amap_init(void); + +uchar amap_mouse_handler(uiEvent *ev, LGRegion *reg, intptr_t v) { + uiMouseData *md = &ev->mouse_data; + if (md->action & (MOUSE_LDOWN | MOUSE_LUP | MOUSE_WHEELUP | MOUSE_WHEELDN)) + return amap_ms_callback(oAMap(MFD_FULLSCR_MAP), ev->pos.x, ev->pos.y, md->action, md->buttons); + return (TRUE); +} + +uchar amap_kb_callback(curAMap *amptr, int code); + +uchar amap_key_handler(uiEvent *ev, LGRegion *r, intptr_t user_data) { + if (amap_kb_callback(oAMap(MFD_FULLSCR_MAP), ev->cooked_key_data.code)) + return FALSE; + return (main_kb_callback(ev, r, user_data)); +} + +// ------------------------------------------------------------- +// amap_init() +// This gets called at the very beginning of time + +uiSlab amap_slab; +LGRegion amap_root_region; + +errtype amap_init(void) { + int id; + LGRect mac_rect = {{0, 0}, {640, 480}}; + + generic_reg_init(TRUE, &amap_root_region, &mac_rect, &amap_slab, amap_key_handler, amap_mouse_handler); + uiInstallRegionHandler(&amap_root_region, UI_EVENT_KBD_POLL | UI_EVENT_MOUSE, amap_scroll_handler, 0, &id); + return (OK); +} + +// ------------------------------------------------------------- +// amap_start() +// This gets called when we actually enter into the amap loop + +void amap_start() { +#ifdef GADGET + _current_root = NULL; /* got rid of pointer type mismatch + * since one was a region and the other a gadget + * someone should probably go and figure it out + */ +#endif + _current_3d_flag = ANIM_UPDATE; + _current_fr_context = NULL; + _current_view = &amap_root_region; + static_change_copy(); + message_info(""); + + HotkeyContext = AMAP_CONTEXT; + uiSetCurrentSlab(&amap_slab); + + gr_set_screen(svga_screen); + fsmap_startup(); + uiShowMouse(NULL); +} + +// ----------------------------------------------- +// amap_exit() +// This gets called when we leave amap mode + +void amap_exit() { + fsmap_free(); + uiHideMouse(NULL); + gr_set_screen(cit_screen); +} diff --git a/engine/src/GameSrc/fullscrn.c b/engine/src/GameSrc/fullscrn.c new file mode 100644 index 0000000..6a4add7 --- /dev/null +++ b/engine/src/GameSrc/fullscrn.c @@ -0,0 +1,458 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/fullscrn.c $ + * $Revision: 1.73 $ + * $Author: dc $ + * $Date: 1994/11/22 20:16:55 $ + */ + + +#include "ShockBitmap.h" +#include "Prefs.h" + +#include "amap.h" +#include "biohelp.h" +#include "fullscrn.h" +#include "leanmetr.h" +#include "cybstrng.h" +#include "frscreen.h" +#include "frflags.h" +#include "frprotox.h" +#include "FrUtils.h" +#include "gameloop.h" +#include "gr2ss.h" +#include "hud.h" +#include "input.h" +#include "invent.h" +#include "mainloop.h" +#include "mfdext.h" +#include "miscqvar.h" +#include "newmfd.h" +#include "objprop.h" +#include "otrip.h" +#include "rendtool.h" +#include "screen.h" +#include "sideicon.h" +#include "status.h" +#include "tools.h" +#include "view360.h" +#include "wares.h" +#include "wrapper.h" + +#include "game_screen.h" // was screen.h? +#include "Shock.h" + +#ifdef NOT_YET // KLC stereo + +#include + +#ifdef STEREO_SUPPORT +#include +#include +#endif + +#endif // NOT_YET + +// ------- +// GLOBALS +// ------- +uchar fullscrn_vitals = TRUE; +uchar fullscrn_icons = TRUE; + +extern uchar inp6d_stereo_active; +extern uchar inp6d_stereo; + +#ifdef SVGA_SUPPORT +grs_screen *svga_screen = NULL; +frc *svga_render_context = NULL; +short svga_mode_data[] = {GRM_320x200x8, GRM_320x400x8, GRM_640x400x8, GRM_640x480x8, GRM_1024x768x8, GRM_320x200x8}; +char mickey_stupid[][2] = {{16, 8}, {16, 4}, {3, 1}, {2, 1}, {3, 1}, {16, 8}}; +short mode_id = 3; // KLC - start off in 640x480 in Mac version old - short mode_id=0; +#endif + +#ifdef GADGET +#include +Gadget *fullroot_gadget; +#endif +uiSlab fullscreen_slab; + +#define CFG_TIME_VAR "time_passes" + +extern void olh_svga_deal(void); +void change_svga_cursors(); + +LGRegion fullroot_region_data, fullview_region_data; +LGRegion *fullroot_region = &fullroot_region_data; // DUH +LGRegion *fullview_region; +LGRegion *inventory_region_full; +LGRegion *pagebutton_region_full; +uchar full_game_3d; +uchar full_visible; + +short base_mouse_xr, base_mouse_yr, base_mouse_thresh; + +errtype fullscreen_init(void) { + extern LGRect fscrn_rect; + + generic_reg_init(TRUE, fullroot_region, NULL, &fullscreen_slab, main_kb_callback, NULL); + + // Full-screen 3d view region + fullview_region = &fullview_region_data; + region_create(fullroot_region, fullview_region, &fscrn_rect, 1, 0, REG_USER_CONTROLLED | AUTODESTROY_FLAG, NULL, + NULL, NULL, NULL); + + install_motion_mouse_handler(fullview_region, NULL); + install_motion_keyboard_handler(fullroot_region); + + wrapper_create_mouse_region(fullview_region); + create_invent_region(fullview_region, &pagebutton_region_full, &inventory_region_full); + init_posture_meters(fullview_region, TRUE); + screen_init_mfd(TRUE); + screen_init_side_icons(fullview_region); + + // mouse_get_rate(&base_mouse_xr, &base_mouse_yr, &base_mouse_thresh); + // base_mouse_xr = 16; + // base_mouse_yr = 8; + // base_mouse_thresh = 100; + base_mouse_xr = 8; + base_mouse_yr = 4; + base_mouse_thresh = 100; + + full_visible = FULL_INVENT_MASK | FULL_L_MFD_MASK | FULL_R_MFD_MASK; + + return (OK); +} + +// Draw all relevant overlays +errtype fullscreen_overlay() { + extern char last_message[128]; + extern uchar game_paused; + + if (!global_fullmap->cyber) { + mfd_draw_button_panel(MFD_RIGHT); + mfd_draw_button_panel(MFD_LEFT); + } + fullscreen_refresh_mfd(MFD_RIGHT); + if (global_fullmap->cyber) + full_visible &= ~FULL_MFD_MASK(MFD_LEFT); + fullscreen_refresh_mfd(MFD_LEFT); + if (!game_paused) + inv_update_fullscreen((full_visible & FULL_INVENT_MASK) != 0); + if (fullscrn_vitals) { + status_vitals_update(TRUE); + if (!global_fullmap->cyber) + update_meters(TRUE); + } + if ((!global_fullmap->cyber) && (fullscrn_icons)) + side_icon_expose_all(); + + // KLC uiSetCursor(); + + return (OK); +} + +// Set all appropriate things to convert us to full screen mode + +void change_svga_cursors() { + ObjID old_obj; + + extern int last_side_icon; + extern int last_invent_cnum; + extern int last_mfd_cnum[NUM_MFDS]; + short temp; + + ss_set_hack_mode(2, &temp); + + // KLC - not needed free_options_cursor(); + make_options_cursor(); + old_obj = object_on_cursor; + if (old_obj != OBJ_NULL) { + pop_cursor_object(); + push_cursor_object(old_obj); + } + free_cursor_bitmaps(); + alloc_cursor_bitmaps(); + reload_motion_cursors(global_fullmap->cyber); + last_side_icon = -1; + last_invent_cnum = -1; + last_mfd_cnum[0] = -1; + last_mfd_cnum[1] = -1; + + biohelp_load_cursor(); + load_misc_cursors(); + + ss_set_hack_mode(0, &temp); +} + +void change_svga_screen_mode() { + extern uchar redraw_paused; + + uchar cur_pal[768]; + uchar *s_table; + short cur_w, cur_h, cur_m; + short mx, my; + uchar mode_change = FALSE; + short temp; + + if (convert_use_mode != mode_id) + mode_change = TRUE; + if (mode_change) { + int retval = -1; + + ui_mouse_get_xy(&mx, &my); + // gr_get_pal(0,256,&cur_pal[0]); + s_table = gr_get_light_tab(); + + uiHideMouse(NULL); + + while (retval == -1) { + /*KLC for stereo support + if (mode_id == 5) + cur_m = i6d_ss->scr_mode; + else + */ + cur_m = svga_mode_data[mode_id]; + retval = gr_set_mode(cur_m, TRUE); + if (retval == -1) { + mode_id = (mode_id + 1) % 5; + } + } + convert_use_mode = mode_id; + cur_w = grd_mode_cap.w; + cur_h = grd_mode_cap.h; + + INFO("Changing screen mode to %i x %i", cur_w, cur_h); + + // CRASHES! + /*if (svga_screen!=NULL) + gr_free_screen(svga_screen);*/ + + ChangeScreenSize(cur_w, cur_h); + + svga_screen = gr_alloc_screen(cur_w, cur_h); + gr_set_screen(svga_screen); + } else { + cur_w = grd_mode_cap.w; + cur_h = grd_mode_cap.h; + } + // calculate new pixel ratio for automap; force 1 for 320x200 + // KLC - we're never 320x200 amap_pixratio_set(svga_mode_data[mode_id]==GRM_320x200x8?FIX_UNIT:0); + // amap_pixratio_set(0); + + amap_pixratio_set(svga_mode_data[mode_id] == GRM_320x200x8 ? FIX_UNIT : 0); + + if (svga_render_context != NULL) { + fr_free_view(svga_render_context); + } + if (full_game_3d) { + svga_render_context = fr_place_view(FR_NEWVIEW, FR_DEFCAM, offscreenDrawSurface->pixels, + FR_DOUBLEB_MASK | FR_WINDOWD_MASK, 0, 0, 0, 0, cur_w, cur_h); + } else { + svga_render_context = fr_place_view( + FR_NEWVIEW, FR_DEFCAM, offscreenDrawSurface->pixels, FR_DOUBLEB_MASK | FR_WINDOWD_MASK | FR_CURVIEW_STRT, 0, + 0, SCONV_X(SCREEN_VIEW_X), SCONV_Y(SCREEN_VIEW_Y), SCONV_X(SCREEN_VIEW_WIDTH), SCONV_Y(SCREEN_VIEW_HEIGHT)); + } + + fr_use_global_detail(svga_render_context); + _current_fr_context = svga_render_context; + if (full_game_3d) + _current_view = fullview_region; + else + _current_view = mainview_region; + _current_3d_flag = DEMOVIEW_UPDATE; + fr_set_view(_current_fr_context); + + // Recompute zoom! + // ss_recompute_zoom(_current_fr_context,old_mode); + + chg_set_flg(DEMOVIEW_UPDATE); + if (mode_change) { + if (mode_id == 0) + game_redrop_rad(0); + else + game_redrop_rad(2 + mode_id); + + ss_mouse_convert(&mx, &my, FALSE); + /*KLC leave out until stereo view is needed + if (mode_id == 5) // hack hack stereo hack + { + switch(i6d_device) + { + case I6D_CTM: + temp_sz.x = 320; + temp_sz.y = 200; + break; + case I6D_VFX1: + temp_sz.x = i6d_ss->scr_w / 2; + temp_sz.y = i6d_ss->scr_h; + break; + } + uiUpdateScreenSize(temp_sz); + } + else + */ + uiUpdateScreenSize(UI_DETECT_SCREEN_SIZE); + // KLC - Can't do this on Mac, can we? mouse_put_xy(mx,my); + // KLC - don't need this. Mac sets this globally. + // mouse_set_rate(mickey_stupid[mode_id][0],mickey_stupid[mode_id][1],2); + // gr_set_pal(0,256,&cur_pal[0]); + gr_set_light_tab(s_table); + uiShowMouse(NULL); + } + if (full_game_3d) { + static_change_copy(); + mfd_change_fullscreen(TRUE); + } + status_bio_update_screenmode(); + ss_set_hack_mode(2, &temp); + inventory_update_screen_mode(); + mfd_update_screen_mode(); + view360_update_screen_mode(); + ss_set_hack_mode(0, &temp); + olh_svga_deal(); + + change_svga_cursors(); + // KLC gamma_dealfunc(QUESTVAR_GET(GAMMACOR_QVAR)); + gamma_dealfunc(gShockPrefs.doGamma); + redraw_paused = TRUE; +} + +void fullscreen_start() { + extern LGRegion *pagebutton_region; + extern LGRegion *inventory_region; + + // Hey, we don't need to hide here because the mouse already gets hidden by fooscreen_exit + // uiHideMouse(NULL); + HotkeyContext = DEMO_CONTEXT; + full_game_3d = TRUE; + uiSetCurrentSlab(&fullscreen_slab); + + inventory_region = inventory_region_full; + pagebutton_region = pagebutton_region_full; +#ifdef GADGET + _current_root = fullroot_gadget; +#endif + +#ifdef STEREO_SUPPORT + if (inp6d_stereo) + mode_id = 5; +#endif + change_svga_screen_mode(); + + inv_change_fullscreen(TRUE); + // mouse_unconstrain(); + player_struct.hardwarez_status[CPTRIP(FULLSCR_HARD_TRIPLE)] |= WARE_ON; + string_message_info(REF_STR_FSMode); + mfd_force_update(); + draw_page_buttons(TRUE); +#ifdef STEREO_SUPPORT + if (inp6d_stereo) { + // uchar cur_pal[768]; + // gr_get_pal(0,256,&cur_pal[0]); + // uiHideMouse(NULL); + // gr_set_mode(i6d_ss->scr_mode,TRUE); + // gr_set_pal(0,256,&cur_pal[0]); + if (i6d_ss->scr_mode == grd_mode) { + i6d_ss->stereo_screen = grd_screen->c; + i6_video(I6VID_SET_MODE, i6d_ss); + if (i6_video(I6VID_STR_SETUP, i6d_ss)) { + Warning(("Stereo setup failed")); + i6_video(I6VID_CLEAR_MODE, i6d_ss); + inp6d_stereo_active = FALSE; + } else + inp6d_stereo_active = TRUE; + } + } +#endif +#ifdef PALFX_FADES +// if (pal_fx_on) palfx_fade_up(FALSE); +#endif + // KLC uiShowMouse(NULL); + + CaptureMouse(true); + SetMotionCursorForMouseXY(); +} + +// Restore all appropriate things to put us back in normal +// screen mode +void fullscreen_exit() { +#ifdef SVGA_SUPPORT + uchar cur_pal[768]; + extern grs_screen *cit_screen; + uchar *s_table; +#endif + +#ifdef STEREO_SUPPORT + if (mode_id == 5) + mode_id = 0; + if (inp6d_stereo_active) { + i6_video(I6VID_CLEAR_MODE, i6d_ss); + inp6d_stereo_active = FALSE; + } +#endif + uiHideMouse(NULL); + +#ifdef SVGA_SUPPORT + if ((_new_mode != GAME_LOOP) && (_new_mode != FULLSCREEN_LOOP)) { + s_table = gr_get_light_tab(); + gr_get_pal(0, 256, &cur_pal[0]); + gr_set_mode(GRM_320x200x8, TRUE); + gr_set_screen(cit_screen); + convert_use_mode = 0; + // KLC change_svga_cursors(); + // KLC status_bio_update_screenmode(); + } +#endif + if (_new_mode == -1) + return; + full_game_3d = FALSE; + mfd_change_fullscreen(FALSE); + inv_change_fullscreen(FALSE); + player_struct.hardwarez_status[CPTRIP(FULLSCR_HARD_TRIPLE)] &= ~WARE_ON; + hud_unset(HUD_MSGLINE); + + /* KLC + #ifdef SVGA_SUPPORT + if ((_new_mode != GAME_LOOP) && (_new_mode != FULLSCREEN_LOOP)) + { + gr_set_pal(0,256,&cur_pal[0]); + gr_set_light_tab(s_table); + } + #endif + */ +} + +// pushes a region down below the view +errtype full_lower_region(LGRegion *r) { + errtype retval; + region_begin_sequence(); + retval = region_move(r, r->r->ul.x, r->r->ul.y, 0); + region_end_sequence(FALSE); + return (retval); +} + +// pulls a region up above the view +errtype full_raise_region(LGRegion *r) { + errtype retval; + region_begin_sequence(); + retval = region_move(r, r->r->ul.x, r->r->ul.y, 2); + region_end_sequence(FALSE); + return (retval); +} diff --git a/engine/src/GameSrc/gameloop.c b/engine/src/GameSrc/gameloop.c new file mode 100644 index 0000000..5bb030d --- /dev/null +++ b/engine/src/GameSrc/gameloop.c @@ -0,0 +1,197 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/gameloop.c $ + * $Revision: 1.32 $ + * $Author: dc $ + * $Date: 1994/11/19 20:35:51 $ + */ + +#include + +#include "Prefs.h" +#include "cyber.h" +#include "leanmetr.h" +#include "mainloop.h" +#include "wares.h" +#include "ai.h" +#include "physics.h" +#include "gamesys.h" +#include "gametime.h" +#include "status.h" +#include "render.h" +#include "musicai.h" +#include "MacTune.h" +#include "newmfd.h" +#include "faketime.h" +#include "invent.h" +#include "damage.h" +#include "effect.h" +#include "fullscrn.h" +#include "tools.h" +#include "olhext.h" +#include "gamescr.h" +#include "gamestrn.h" +#include "cybstrng.h" +#include "colors.h" +#include "gr2ss.h" +#include "game_screen.h" +#include "sndcall.h" + +// ---------- +// GLOBALS +// ---------- +uchar redraw_paused = TRUE; + +// ---------- +// PROTOTYPES +// ---------- +void draw_pause_string(void); + +long pal_frame = 0; + +//------------------------------------------------------------------ +void draw_pause_string(void) { + LGRect r; + short w, h, nw, nh; + + gr_set_fcolor(RED_BASE + 4); + gr_set_font((grs_font *)ResGet(RES_citadelFont)); + gr_string_size(get_string(REF_STR_Pause, NULL, 0), &w, &h); + nw = SCREEN_VIEW_X + (SCREEN_VIEW_WIDTH - w) / 2; + nh = SCREEN_VIEW_Y + (SCREEN_VIEW_HEIGHT - h) / 2; + RECT_FILL(&r, nw, nh, nw + w, nh + h); + gr2ss_override = OVERRIDE_ALL; + uiHideMouse(&r); + ss_string(get_string(REF_STR_Pause, NULL, 0), nw, nh); + uiShowMouse(&r); +} + +//------------------------------------------------------------------ +void game_loop(void) { + extern uchar game_paused; + // temp + // extern char saveArray[16]; + // if (memcmp(0, saveArray, 16)) + // Debugger(); + + // Handle paused game state + if (game_paused) { + if (redraw_paused) { + TRACE("%s: Drawing pause!", __FUNCTION__); + draw_pause_string(); + redraw_paused = FALSE; + } + // KLC - does nothing! loopLine(GL|0x1D,synchronous_update()); + if (music_on) + loopLine(GL|0x1C, mlimbs_do_ai()); + /*if (pal_fx_on) + loopLine(GL|0x1E, palette_advance_all_fx(* (long *) 0x16a)); // TickCount()*/ + } + + // If we're not paused... + + else { + loopLine(GL | 0x10, update_state(time_passes)); // move game time + + if (time_passes) { + TRACE("%s: ai_run", __FUNCTION__); + loopLine(GL | 0x12, ai_run()); + + TRACE("%s: gamesys_run", __FUNCTION__); + loopLine(GL | 0x13, gamesys_run()); + + TRACE("%s: advance_animations", __FUNCTION__); + loopLine(GL | 0x14, advance_animations()); + } + TRACE("%s: wares_update", __FUNCTION__); + loopLine(GL | 0x16, wares_update()); + + TRACE("%s: message_clear_check", __FUNCTION__); + loopLine(GL | 0x1D, message_clear_check()); // This could be done more cleverly with change flags... + + if (localChanges) { + TRACE("%s: render_run", __FUNCTION__); + loopLine(GL | 0x1A, render_run()); + + TRACE("%s: status_vitals_update", __FUNCTION__); + loopLine(GL | 0x17, if (!full_game_3d) status_vitals_update(FALSE)); + /*KLC - no longer needed + if (_change_flag&ANIM_UPDATE) + { + loopLine(GL|0x19, AnimRecur()); + chg_unset_flg(ANIM_UPDATE); + } + */ + + if (full_game_3d && ((_change_flag & INVENTORY_UPDATE) || (_change_flag & MFD_UPDATE))) + _change_flag |= DEMOVIEW_UPDATE; + if (_change_flag & INVENTORY_UPDATE) { + TRACE("%s: INVENTORY_UPDATE", __FUNCTION__); + chg_unset_flg(INVENTORY_UPDATE); + loopLine(GL | 0x1B, inventory_draw()); + } + if (_change_flag & MFD_UPDATE) { + TRACE("%s: MFD_UPDATE", __FUNCTION__); + chg_unset_flg(MFD_UPDATE); + loopLine(GL | 0x18, mfd_update()); + } + + if (_change_flag & DEMOVIEW_UPDATE) { + // KLC - does nothing! + // if (sfx_on || music_on) + // loopLine(GL|0x1D, synchronous_update()); + chg_unset_flg(DEMOVIEW_UPDATE); + } + } + if (!full_game_3d) { + TRACE("%s: update_meters", __FUNCTION__); + loopLine(GL | 0x19, update_meters(FALSE)); + } + if (!full_game_3d && olh_overlay_on) { + TRACE("%s: olh_overlay", __FUNCTION__); + olh_overlay(); + } + + TRACE("%s: physics_run", __FUNCTION__); + loopLine(GL | 0x15, physics_run()); + { + if (!olh_overlay_on && olh_active && !global_fullmap->cyber) { + TRACE("%s: olh_scan_objects", __FUNCTION__); + olh_scan_objects(); + } + } + // KLC - does nothing! loopLine(GL|0x1D,synchronous_update()); + if (sfx_on || music_on) { + TRACE("%s: sound_frame_update", __FUNCTION__); + loopLine(GL | 0x1C, mlimbs_do_ai()); + loopLine(GL | 0x1E, sound_frame_update()); + } + + if (pal_fx_on) { + loopLine(GL | 0x1F, palette_advance_all_fx(*tmd_ticks)); + + gamma_dealfunc(gShockPrefs.doGamma); + } + + TRACE("%s: destroy_destroyed_objects", __FUNCTION__); + loopLine(GL | 0x20, destroy_destroyed_objects()); + loopLine(GL | 0x21, check_cspace_death()); + } +} diff --git a/engine/src/GameSrc/gameobj.c b/engine/src/GameSrc/gameobj.c new file mode 100644 index 0000000..aeb89b5 --- /dev/null +++ b/engine/src/GameSrc/gameobj.c @@ -0,0 +1,1247 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/gameobj.c $ + * $Revision: 1.107 $ + * $Author: dc $ + * $Date: 1994/11/25 16:58:15 $ + */ + +#include +#include // for memset + +#include "effect.h" +#include "gameobj.h" + +#include "fr3d.h" +#include "frcamera.h" +#include "frintern.h" +#include "frtables.h" + +#include "faketime.h" +#include "hud.h" +#include "player.h" +#include "map.h" +#include "citres.h" +#include "objsim.h" +#include "objprop.h" +#include "objuse.h" +#include "objclass.h" +#include "hudobj.h" +#include "otrip.h" +#include "textmaps.h" +#include "gettmaps.h" +#include "objbit.h" +#include "rendtool.h" + +#include "frflags.h" + +#include "ice.h" + +#include "cybmem.h" +#include "rcolors.h" +#include "models.h" +#include "objload.h" + +#include "ai.h" + +#ifdef DOOM_EMULATION_MODE +#include "diffq.h" +#endif + +#ifdef NEW_2d +#include "double.h" +#endif + +#include "OpenGL.h" + +#define VOXEL_PIX_DIST_BASE (fix_make(0, 0x1000)) +#define VOXEL_PIX_DIST_DELTA (fix_make(0, 0x6000)) +// this is obfuscated to put it mildly +#define VOXEL_PIX_SIZE_BASE (fix_make(0, 0x100)) +#define VOXEL_PIX_SIZE_DELTA (fix_make(0, 0xE00)) +#define VOXEL_DEPTH 16 + +#define MAX_VCOLORS NUM_SLOW_VCOLORS + +extern int (*g3_tmap_func)(int n, g3s_phandle *vp, grs_bitmap *bm); + +extern MapElem *_fdt_mptr; + +static int _o_rad; +static g3s_vector _fr_p; +static Obj *_fr_cobj; +static int _sq_lght; + +char curr_clut_table = 0; + +#define height_step fix_make(0, 0x010000 >> SLOPE_SHIFT) + +// prototypes +int munge_val(int val, int range, int delta); +void _fr_draw_parm_cube(grs_bitmap *side_bm, grs_bitmap *oth_bm, int x, int y, int z); +void _fr_draw_poly_cube(int p_color, int x, int y, int z); +void _fr_draw_polyobj(void *model_ptr, uchar use_lighting); +void gen_seed_vec(g3s_vector *gpt_vec, int seed, int scale, int deviant); +void do_xplodamatron(int frame, int severity, int seed, int col1, int col2); +void gen_tetra(g3s_phandle *xplo_pts, fix size, int deviant, int color); +void draw_ice(void); +void draw_ice_wall(void); +void _fr_draw_tmtile(grs_bitmap *draw_bm, int col_val, g3s_phandle *plst, uchar dblface, uchar use_lighting); +void _fr_draw_bitmap(grs_bitmap *draw_bm, int dist, int sc, int anch_x, int anch_y); + +int munge_val(int val, int range, int delta) { + int base = range - delta; + if (val <= delta) + base = 0; + else if (val >= range - delta) + base = range - 2 * delta; + val = (val + base + (rand() % (2 * delta + 1))) & 0xff; + return val; +} + +#define PARM_MAX (0x0f8) +#define PARM_MOD (PARM_MAX << 1) +#define PARM_SHF (10) +//#define PARM_BASE (64) +#define PARM_BASE (16) + +#define LIGHT_3D_OBJS + +#define setup_face(a, b, c, d) \ + cface[0] = cube_pt[a]; cface[0]->uv.u = 0; cface[0]->uv.v = 0; \ + cface[1] = cube_pt[b]; cface[1]->uv.u = 0x100; cface[1]->uv.v = 0; \ + cface[2] = cube_pt[c]; cface[2]->uv.u = 0x100; cface[2]->uv.v = 0x100; \ + cface[3] = cube_pt[d]; cface[3]->uv.u = 0; cface[3]->uv.v = 0x100 + +#define setup_rface(a, b, c, d) \ + cface[0] = cube_pt[a]; cface[0]->uv.u = 0x100; cface[0]->uv.v = 0; \ + cface[1] = cube_pt[b]; cface[1]->uv.u = 0x0; cface[1]->uv.v = 0x000; \ + cface[2] = cube_pt[c]; cface[2]->uv.u = 0; cface[2]->uv.v = 0x100; \ + cface[3] = cube_pt[d]; cface[3]->uv.u = 0x100; cface[3]->uv.v = 0x100 + +//#pragma disable_message(202) +void _fr_draw_parm_cube(grs_bitmap *side_bm, grs_bitmap *oth_bm, int x, int y, int z) { + g3s_phandle cube_pt[8], cface[4]; + g3s_vector cube_vec; + int cur_ft; + + g3_start_object_angles_xyz(&_fr_p, _fr_cobj->loc.p << 8, _fr_cobj->loc.h << 8, _fr_cobj->loc.b << 8, ANGLE_ORDER); + cube_vec.gX = -x; + cube_vec.gY = 0; + cube_vec.gZ = -y; +#ifdef NO_ANTIGRAV_CRATES + cube_vec.gY += _o_rad; +#endif + cube_pt[0] = g3_transform_point(&cube_vec); + cube_pt[0]->p3_flags |= PF_U | PF_V; +#ifdef NO_ANTIGRAV_CRATES + cube_vec.gY -= _o_rad; +#endif + _fdt_pbase = 0; + cube_pt[0]->i = _sq_lght; // _fr_do_light(cube_pt[0],FRPTSZFLR_DN); + x += x; + y += y; + z = -(z + z); // go from radius to real + cube_pt[1] = g3_copy_add_delta_z(cube_pt[0], y); + cube_pt[1]->p3_flags |= PF_U | PF_V; + cube_pt[2] = g3_copy_add_delta_x(cube_pt[1], x); + cube_pt[2]->p3_flags |= PF_U | PF_V; + cube_pt[3] = g3_copy_add_delta_x(cube_pt[0], x); + cube_pt[3]->p3_flags |= PF_U | PF_V; + cube_pt[4] = g3_copy_add_delta_y(cube_pt[0], z); + cube_pt[4]->p3_flags |= PF_U | PF_V; + cube_pt[5] = g3_copy_add_delta_y(cube_pt[1], z); + cube_pt[5]->p3_flags |= PF_U | PF_V; + cube_pt[6] = g3_copy_add_delta_y(cube_pt[2], z); + cube_pt[6]->p3_flags |= PF_U | PF_V; + cube_pt[7] = g3_copy_add_delta_y(cube_pt[3], z); + cube_pt[7]->p3_flags |= PF_U | PF_V; +#ifdef LIGHT_3D_OBJS + cur_ft = gr_get_fill_type(); + if (cur_ft != FILL_SOLID) { + gr_set_fill_type(FILL_CLUT); + gr_set_fill_parm(_fr_clut_list[curr_clut_table] + (cube_pt[0]->i & 0xf00)); + } +#endif + setup_face(0, 3, 2, 1); + (*g3_tmap_func)(4, cface, oth_bm); + setup_rface(7, 4, 5, 6); + (*g3_tmap_func)(4, cface, oth_bm); + setup_face(4, 7, 3, 0); + (*g3_tmap_func)(4, cface, side_bm); + setup_face(7, 6, 2, 3); + (*g3_tmap_func)(4, cface, side_bm); + setup_face(6, 5, 1, 2); + (*g3_tmap_func)(4, cface, side_bm); + setup_face(5, 4, 0, 1); + (*g3_tmap_func)(4, cface, side_bm); + g3_end_object(); +#ifdef LIGHT_3D_OBJS + gr_set_fill_type(cur_ft); +#endif + g3_free_list(8, cube_pt); +} + +#define setup_poly_face(a, b, c, d) \ + cface[0] = cube_pt[a]; \ + cface[1] = cube_pt[b]; \ + cface[2] = cube_pt[c]; \ + cface[3] = cube_pt[d] + +#define setup_poly_rface(a, b, c, d) \ + cface[0] = cube_pt[a]; \ + cface[1] = cube_pt[b]; \ + cface[2] = cube_pt[c]; \ + cface[3] = cube_pt[d]; + +#define fpoly_rend(pc, fc, fpts) \ + if (pc == 0) \ + pc = -0xff; \ + if (pc >= 0) \ + g3_check_and_draw_poly(pc, fc, fpts); \ + else if (pc < -0x80) \ + g3_check_and_draw_tluc_poly(-pc, fc, fpts); \ + else \ + g3_check_and_draw_tluc_spoly(fc, fpts) + +uchar hakx, haky, hakz, haktype = 0xFF; + +// polygon/translucent cubes... +// needs to learn to set i correctly +void _fr_draw_poly_cube(int p_color, int x, int y, int z) { + g3s_phandle cube_pt[8], cface[4]; + g3s_vector cube_vec; + // int cur_ft; + + g3_start_object_angles_xyz(&_fr_p, _fr_cobj->loc.p << 8, _fr_cobj->loc.h << 8, _fr_cobj->loc.b << 8, ANGLE_ORDER); + cube_vec.gX = -x; + cube_vec.gY = 0; + cube_vec.gZ = -y; + cube_pt[0] = g3_transform_point(&cube_vec); + cube_pt[0]->p3_flags |= PF_I; + cube_pt[0]->i = 0x0100; + _fdt_pbase = 0; // cube_pt[0]->i=_sq_lght; // _fr_do_light(cube_pt[0],FRPTSZFLR_DN); + x += x; + y += y; + z = -(z + z); // go from radius to real + if (haktype == 0) + y = (y * (haky + 1)) >> 8; + cube_pt[1] = g3_copy_add_delta_z(cube_pt[0], y); + cube_pt[1]->p3_flags |= PF_I; + cube_pt[1]->i = 0x0200; + cube_pt[2] = g3_copy_add_delta_x(cube_pt[1], x); + cube_pt[2]->p3_flags |= PF_I; + cube_pt[2]->i = 0x0300; + cube_pt[3] = g3_copy_add_delta_x(cube_pt[0], x); + cube_pt[3]->p3_flags |= PF_I; + cube_pt[3]->i = 0x0400; + cube_pt[4] = g3_copy_add_delta_y(cube_pt[0], z); + cube_pt[4]->p3_flags |= PF_I; + cube_pt[4]->i = 0x0500; + cube_pt[5] = g3_copy_add_delta_y(cube_pt[1], z); + cube_pt[5]->p3_flags |= PF_I; + cube_pt[5]->i = 0x0600; + cube_pt[6] = g3_copy_add_delta_y(cube_pt[2], z); + cube_pt[6]->p3_flags |= PF_I; + cube_pt[6]->i = 0x0700; + cube_pt[7] = g3_copy_add_delta_y(cube_pt[3], z); + cube_pt[7]->p3_flags |= PF_I; + cube_pt[7]->i = 0x0800; +#ifdef vvLIGHT_3D_OBJS + cur_ft = gr_get_fill_type(); + if (cur_ft != FILL_SOLID) { + gr_set_fill_type(FILL_CLUT); + gr_set_fill_parm(_fr_clut_list[curr_clut_table] + (cube_pt[0]->i & 0xf00)); + } +#endif + setup_face(0, 3, 2, 1); + fpoly_rend(p_color, 4, cface); + setup_rface(7, 4, 5, 6); + fpoly_rend(p_color, 4, cface); + setup_face(4, 7, 3, 0); + fpoly_rend(p_color, 4, cface); + setup_face(7, 6, 2, 3); + fpoly_rend(p_color, 4, cface); + setup_face(6, 5, 1, 2); + fpoly_rend(p_color, 4, cface); + setup_face(5, 4, 0, 1); + fpoly_rend(p_color, 4, cface); + g3_end_object(); +#ifdef vvLIGHT_3D_OBJS + gr_set_fill_type(cur_ft); +#endif + g3_free_list(8, cube_pt); +} + +// you stand surrounded by dreams brutally crushed +void _fr_draw_polyobj(void *model_ptr, uchar use_lighting) { + int parm_mod = (*tmd_ticks) & PARM_MOD; + int pos_parm = abs(PARM_MAX - parm_mod); // this is dumb + int cur_ft; + // set up clut for lighting in square and all + // should decode 0 and FACE_ somehow... ick +#ifdef LIGHT_3D_OBJS + if (use_lighting) { + cur_ft = gr_get_fill_type(); + if (cur_ft != FILL_SOLID) { + gr_set_fill_type(FILL_CLUT); + gr_set_fill_parm(_fr_clut_list[curr_clut_table] + (_sq_lght & 0xf00)); + } + } +#endif + g3_start_object_angles_xyz(&_fr_p, _fr_cobj->loc.p << 8, _fr_cobj->loc.h << 8, _fr_cobj->loc.b << 8, ANGLE_ORDER); + g3_interpret_object((ubyte *)model_ptr, ((PARM_MAX + PARM_BASE) - pos_parm) << PARM_SHF, PARM_BASE << PARM_SHF); + g3_end_object(); +#ifdef LIGHT_3D_OBJS + if (use_lighting) + gr_set_fill_type(cur_ft); +#endif +} +//#pragma enable_message(202) + +//#pragma disable_message(202) +void gen_seed_vec(g3s_vector *gpt_vec, int seed, int scale, int deviant) { + deviant = (1 << deviant) - 1; + if (seed != 0) { + gpt_vec->gX = ((seed & 0x1f) - 0xf); + seed >>= 5; + gpt_vec->gY = ((seed & 0x1f) - 0xf); + seed >>= 5; + gpt_vec->gZ = ((seed & 0x1f) - 0xf); + } + gpt_vec->gX *= scale; + gpt_vec->gY *= scale; + gpt_vec->gZ *= scale; + gpt_vec->gX += rand() & deviant; + gpt_vec->gY += rand() & deviant; + gpt_vec->gZ += rand() & deviant; +} + +void do_xplodamatron(int frame, int severity, int seed, int col1, int col2) { + g3s_phandle xplo_pts[3]; + g3s_vector xplo_vec; + int seed_add = severity * 4183; + + g3_start_object_angles_xyz(&_fr_p, _fr_cobj->loc.p << 8, _fr_cobj->loc.h << 8, _fr_cobj->loc.b << 8, ANGLE_ORDER); + xplo_vec.gX = xplo_vec.gY = xplo_vec.gZ = 0; + + for (; severity >= 0; severity--, seed += seed_add) { + gen_seed_vec(&xplo_vec, seed, 4 + (frame << 2), 4 + (frame << 3)); + xplo_pts[0] = g3_transform_point(&xplo_vec); + xplo_vec.gX += 0x4000 + (frame << 11); + xplo_vec.gY += 0x6000 + (frame << 12); + xplo_vec.gZ -= 0x3000 + (frame << 11); + xplo_pts[1] = g3_transform_point(&xplo_vec); + xplo_vec.gX -= 0x7000 + (frame << 13); + xplo_vec.gY += 0x5000 + (frame << 10); + xplo_vec.gZ -= 0x2000 + (frame << 12); + xplo_pts[2] = g3_transform_point(&xplo_vec); + g3_check_and_draw_poly(col1 + (rand() & 0xf), 3, xplo_pts); + } + + g3_end_object(); +} + +// should be deviant some day +void gen_tetra(g3s_phandle *xplo_pts, fix size, int ii, int color) { + int i; + g3s_vector xplo_vec; + fix annoying_val = fix_mul(size, fix_make(1, 71 * 65536 / 100)); // 1/2 * 6/root3, in dougs head, = 1.71??? + + xplo_vec.gX = xplo_vec.gZ = 0; + xplo_vec.gY = -2 * size; + xplo_pts[0] = g3_transform_point(&xplo_vec); // top + xplo_pts[1] = g3_copy_add_delta_yz(xplo_pts[0], 3 * size, 2 * size); // north + xplo_pts[2] = g3_copy_add_delta_xz(xplo_pts[1], -annoying_val, -3 * size); // left + xplo_pts[3] = g3_copy_add_delta_x(xplo_pts[2], 2 * annoying_val); // right + + for (i = 0; i < 4; i++) + if (color > 0) { + xplo_pts[i]->rgb = grd_bpal[color]; + xplo_pts[i]->p3_flags |= PF_RGB; + } else { + xplo_pts[i]->rgb = grd_bpal[-color + (rand() & 3)]; + xplo_pts[i]->p3_flags |= PF_RGB; + } +} + +// ok, size should be about 0x1000 for a toggle like thing, perhaps up to 3000 for big big stuff +// deviant should be 5-8, really +// color should come from ice_level, but oh well.... + +void draw_ice(void) { + g3s_phandle xplo_pts[8]; + int size, deviant = (obj_ICE_AGIT(_fr_cobj) >> 6) + 4; + int p, h, b; + + // this is a total hack, should be made real when hp scale is known + size = (_fr_cobj->info.current_hp) * 6; // just scaled us up to try and work better + size += (obj_ICE_AGIT(_fr_cobj)) << 2; // yea, sure, a why not + size <<= obj_ICE_LEVEL(_fr_cobj); + size += 0x400; + if (size > 0x3000) + size = 0x3000; + + p = _fr_cobj->loc.p << 8; + p += (rand() & (1 << (deviant + 1))) - (1 << deviant); + h = _fr_cobj->loc.h << 8; + h += (rand() & (1 << (deviant + 1))) - (1 << deviant); + b = _fr_cobj->loc.b << 8; + b += (rand() & (1 << (deviant + 1))) - (1 << deviant); + + g3_start_object_angles_xyz(&_fr_p, p, h, b, ANGLE_ORDER); + + // should do color based on ice, do separate for outer and inner + gen_tetra(xplo_pts, size, deviant, -BLUE_8_BASE); + gen_tetra(xplo_pts + 4, -(size << 2), deviant, -BLUE_8_BASE - 4); + +#ifndef NOT_REAL + g3_draw_cline(xplo_pts[0], xplo_pts[6]); + g3_draw_cline(xplo_pts[1], xplo_pts[6]); + g3_draw_cline(xplo_pts[3], xplo_pts[6]); + + g3_draw_cline(xplo_pts[0], xplo_pts[7]); + g3_draw_cline(xplo_pts[1], xplo_pts[7]); + g3_draw_cline(xplo_pts[2], xplo_pts[7]); + + g3_draw_cline(xplo_pts[0], xplo_pts[5]); + g3_draw_cline(xplo_pts[2], xplo_pts[5]); + g3_draw_cline(xplo_pts[3], xplo_pts[5]); + + g3_draw_cline(xplo_pts[1], xplo_pts[4]); + g3_draw_cline(xplo_pts[2], xplo_pts[4]); + g3_draw_cline(xplo_pts[3], xplo_pts[4]); +#endif + +#ifdef FAKE + g3_draw_cline(xplo_pts[0], xplo_pts[3]); + g3_draw_cline(xplo_pts[1], xplo_pts[3]); + g3_draw_cline(xplo_pts[2], xplo_pts[3]); + g3_draw_cline(xplo_pts[0], xplo_pts[1]); + g3_draw_cline(xplo_pts[1], xplo_pts[2]); + g3_draw_cline(xplo_pts[2], xplo_pts[0]); + + g3_draw_cline(xplo_pts[4], xplo_pts[5]); + g3_draw_cline(xplo_pts[4], xplo_pts[6]); + g3_draw_cline(xplo_pts[4], xplo_pts[7]); + g3_draw_cline(xplo_pts[5], xplo_pts[6]); + g3_draw_cline(xplo_pts[6], xplo_pts[7]); + g3_draw_cline(xplo_pts[7], xplo_pts[5]); +#endif + + g3_free_list(8, xplo_pts); + g3_end_object(); +} +//#pragma enable_message(202) + +//#pragma disable_message(202) +void draw_ice_wall(void) { int size_x = 0x8000, size_y = 0x8000; } +//#pragma enable_message(202) + +void _fr_draw_tmtile(grs_bitmap *draw_bm, int col_val, g3s_phandle *plst, uchar dblface, uchar use_lighting) { + int t_off_l, t_off_r; + // int face_c, face_f, hgt_f, hgt_c; + int y1 = ((_fr_cobj->loc.y) & 0xff) << 8, x1 = ((_fr_cobj->loc.x) & 0xff) << 8; + + switch (((_fr_cobj->loc.h + 0x20) & 0xff) >> 6) { + case 0: + if (y1 > 0x8000) { + t_off_l = 1; + t_off_r = 2; + } else { + t_off_l = 0; + t_off_r = 3; + } + break; + case 2: + if (y1 > 0x8000) { + t_off_l = 2; + t_off_r = 1; + } else { + t_off_l = 3; + t_off_r = 0; + } + break; + case 1: + if (x1 > 0x8000) { + t_off_l = 2; + t_off_r = 3; + } else { + t_off_l = 1; + t_off_r = 0; + } + break; + case 3: + if (x1 > 0x8000) { + t_off_l = 3; + t_off_r = 2; + } else { + t_off_l = 0; + t_off_r = 1; + } + break; + } + // hgt_c=-p.gY+(fix_yoff<<1); hgt_f=-p.gY; + _fdt_pbase = t_off_l; + _fr_do_light(plst[0], FRPTSZCEIL_DN); + plst[0]->uv.u = 0; + plst[0]->uv.v = 0; + plst[0]->p3_flags = PF_I | PF_V | PF_U; + _fr_do_light(plst[3], FRPTSZFLR_DN); + plst[3]->uv.u = 0; + plst[3]->uv.v = 0x100; + plst[3]->p3_flags = PF_I | PF_V | PF_U; + _fdt_pbase = t_off_r; + _fr_do_light(plst[1], FRPTSZCEIL_DN); + plst[1]->uv.u = 0x100; + plst[1]->uv.v = 0; + plst[1]->p3_flags = PF_I | PF_V | PF_U; + _fr_do_light(plst[2], FRPTSZFLR_DN); + plst[2]->uv.u = 0x100; + plst[2]->uv.v = 0x100; + plst[2]->p3_flags = PF_I | PF_V | PF_U; + if (!use_lighting) { + plst[0]->i = 0; + plst[3]->i = 0; + plst[1]->i = 0; + plst[2]->i = 0; + } + // mprintf("Note light %x %x %x %x\n",plst[0]->i,plst[1]->i,plst[2]->i,plst[3]->i); + if (_fr_curflags & FR_PICKUPM_MASK) + if ((draw_bm == NULL) || (_fr_curflags & FR_NOTRANS_MASK) || ((draw_bm->flags & BMF_TRANS) == 0)) + g3_check_and_draw_poly(gr_get_fill_parm(), 4, plst); + else + (*g3_tmap_func)(4, plst, draw_bm); + else if (col_val != 0xFF) { + int cur_ft = gr_get_fill_type(); + gr_set_fill_type(FILL_CLUT); + gr_set_fill_parm(_fr_clut_list[curr_clut_table] + (_sq_lght & 0xf00)); + fpoly_rend(col_val, 4, plst); + gr_set_fill_type(cur_ft); + } else if (use_opengl()) { + if (draw_bm != NULL) opengl_light_tmap(4, plst, draw_bm); + } else { + if (draw_bm != NULL) g3_light_tmap(4, plst, draw_bm); + } + if (dblface) // draw the bleeding backside, nudge nudge + { + g3s_phandle tmp; + tmp = plst[0]; + plst[0] = plst[1]; + plst[1] = tmp; + tmp = plst[2]; + plst[2] = plst[3]; + plst[3] = tmp; // and if you thought that was obscure + if (_fr_curflags & FR_PICKUPM_MASK) + if ((draw_bm == NULL) || (_fr_curflags & FR_NOTRANS_MASK) || ((draw_bm->flags & BMF_TRANS) == 0)) + g3_check_and_draw_poly(gr_get_fill_parm(), 4, plst); + else + (*g3_tmap_func)(4, plst, draw_bm); + else { + if (col_val != 0xFF) { + int cur_ft = gr_get_fill_type(); + gr_set_fill_type(FILL_CLUT); + gr_set_fill_parm(_fr_clut_list[curr_clut_table] + (_sq_lght & 0xf00)); + fpoly_rend(col_val, 4, plst); + gr_set_fill_type(cur_ft); + } else if (use_opengl()) { + if (draw_bm != NULL) opengl_light_tmap(4, plst, draw_bm); + } else { + if (draw_bm != NULL) g3_light_tmap(4, plst, draw_bm); + } + } + } +} + +// all the qsc code is in the backup of fauxobjd +// so we dont need to carry it around till it works + +// note this always has show_obj's p for p and _fdt_dist for dist.. perhaps shouldnt pass them +//#pragma disable_message(202) +void _fr_draw_bitmap(grs_bitmap *draw_bm, int dist, int sc, int anch_x, int anch_y) { +#ifdef SMOOTH_BITMAPS + grs_canvas tmp_can; + grs_bitmap tmp_bm; + uchar *tmp_ptr; + uchar do_qsc = (dist < fr_qscale_obj); +#endif + g3s_phandle anchor; + grs_vertex **bitmap_verts; + + _fr_p.gY += _o_rad; + anchor = g3_transform_point(&_fr_p); + _fr_p.gY -= _o_rad; + + _fdt_pbase = 0; + anchor->i = _sq_lght; // _fr_do_light(anchor,FRPTSZFLR_DN); + + if (sc) + g3_set_bitmap_scale(fix_make(0, (int)(4096 / 3)), fix_make(0, (int)(4096 / 3))); + if ((anch_x <= 0) && (anch_y <= 0)) + bitmap_verts = g3_light_bitmap(draw_bm, anchor); + else + bitmap_verts = g3_light_anchor_bitmap(draw_bm, anchor, anch_x, anch_y); + if (sc) + g3_set_bitmap_scale(fix_make(0, (int)(2048 / 3)), fix_make(0, (int)(2048 / 3))); + + if ((bitmap_verts != NULL) && IS_HUDOBJ(_fr_cobj - objs)) { +#ifdef SVGA_SUPPORT + fix lx = fix_make(1024, 0), ly = fix_make(768, 0), rx = fix_make(0, 0), ry = fix_make(0, 0); +#else + fix lx = fix_make(320, 0), ly = fix_make(200, 0), rx = fix_make(0, 0), ry = fix_make(0, 0); +#endif + int i; + for (i = 0; i < 4; i++) { + if (bitmap_verts[i]->x < lx) + lx = bitmap_verts[i]->x; + if (bitmap_verts[i]->x > rx) + rx = bitmap_verts[i]->x; + if (bitmap_verts[i]->y < ly) + ly = bitmap_verts[i]->y; + if (bitmap_verts[i]->y > ry) + ry = bitmap_verts[i]->y; + } + SET_HUDOBJ_RECT(_fr_cobj - objs, fix_int(lx), fix_int(ly), fix_int(rx), fix_int(ry)); + } + g3_free_point(anchor); +} + //#pragma enable_message(202) + +#define FAUBJ_BULLET_HACK (NUM_OBJ_RENDER_TYPES) +#define ADD_IT (0) + +// this really shouldnt be here in this way +// Some special bitfields that determine how the data fields +// are parsed for slaving animations +#define INDIRECTED_STUFF_INDICATOR_MASK 0x1000 +#define INDIRECTED_STUFF_DATA_MASK 0xFFF + +#define SECRET_FURNITURE_DEFAULT_O3DREP 0x80 + +short compute_3drep(Obj *cobj, ObjID cobjid, int obj_type) { + short o3drep = -1; + + // fix for screens with data2 of zero wanting to animate, + // and other bigstuffs presumbably wanting to use it as a default. + // only indirect through data2 if it is nonzero OR if we're + // animating. + if ((cobj->obclass == CLASS_BIGSTUFF) && (obj_is_display(ID2TRIP(cobjid))) && + ((objBigstuffs[cobj->specID].data2 != 0) || anim_data_from_id(cobjid, NULL, NULL))) { + int d2 = objBigstuffs[cobj->specID].data2; + if (d2 & INDIRECTED_STUFF_INDICATOR_MASK) { + ObjID newid = d2 & INDIRECTED_STUFF_DATA_MASK; + o3drep = objBigstuffs[objs[newid].specID].data2 + objs[newid].info.current_frame; + } else + o3drep = objBigstuffs[cobj->specID].data2 + cobj->info.current_frame; + } else { + switch (obj_type) { + case FAUBJ_TPOLY: + case FAUBJ_TEXTPOLY: + o3drep = 0; + break; + default: + o3drep = BMAP_NUM_3D(ObjProps[OPNUM(cobjid)].bitmap_3d); + if ((obj_type != FAUBJ_VOX) && (cobj->obclass != CLASS_DOOR) && (cobj->info.current_frame != 255)) { +#ifdef PLAYTEST + if ((cobj->obclass == CLASS_CONTAINER) && + (cobj->info.current_frame > FRAME_NUM_3D(ObjProps[OPNUM(cobjid)].bitmap_3d))) { + Warning(("hey, obj id %x has frame %d, but max is %d!\n", cobjid, cobj->info.current_frame, + FRAME_NUM_3D(ObjProps[OPNUM(cobjid)].bitmap_3d))); + } else +#endif + o3drep += cobj->info.current_frame; + } + break; + } + } + if ((cobj->obclass == CLASS_BIGSTUFF) && (cobj->subclass == BIGSTUFF_SUBCLASS_FURNISHING) && + (objBigstuffs[cobj->specID].data2 == 0)) + o3drep = SECRET_FURNITURE_DEFAULT_O3DREP; + return (o3drep); +} + +#define TRANSLUCENT_INVISOS +//#define TLUC_IN_2D + +// in effect.c also +#define MAX_TELEPORT_FRAME 10 +#define TELEPORT_COLOR 0x1C +// in objsim.c also +#define DIEGO_DEATH_BATTLE_LEVEL 8 + +#define DESTROYED_SCREEN_ANIM_BASE 0x1B + +void show_obj(ObjID cobjid) { + short objtrip; + short o3drep; + int model_num = 0; + extern uchar cam_mode; + uchar *model_ptr; + grs_bitmap *tpdata; +#ifdef TRANSLUCENT_INVISOS +#ifndef TLUC_IN_2D + grs_bitmap tpdata_temp; +#endif +#endif + char scale = 0; + uchar type = 0xFF; + uchar use_cache = FALSE; + Ref ref = 0; + int obj_type, tluc_val = 0xFF, index = 0, loc_h; + uchar light_me = TRUE; + extern cams objmode_cam; + +// check_up(0x220000|cobjid); +// mprintf("cobjid = %x\n",cobjid); +#ifdef DOOM_EMULATION_MODE + if (obj_too_smart(cobjid)) + return; +#endif + objtrip = OPNUM(cobjid); + obj_type = ObjProps[objtrip].render_type; + _fr_cobj = &objs[cobjid]; + o3drep = compute_3drep(_fr_cobj, cobjid, obj_type); + + _fr_p.gX = _fr_cobj->loc.x << 8; + _fr_p.gZ = _fr_cobj->loc.y << 8; + _fr_p.gY = -_fr_cobj->loc.z * height_step >> 3; // down 3, neato magic number technology + + if (_fr_curflags & FR_PICKUPM_MASK) { + gr_set_fill_parm(fr_cur_obj_col); + fr_col_to_obj[(fr_cur_obj_col++) - FR_CUR_OBJ_BASE] = cobjid; + } + + _o_rad = fix_make(ObjProps[objtrip].physics_xr, 0) / 96; // for reanchoring + + // should look at renderer!!!!, not this + + if (global_fullmap->cyber) { + if ((obj_type != FAUBJ_CRIT) && (obj_type != FAUBJ_TEXTPOLY) && (obj_type != FAUBJ_VOX) && + (obj_type != FAUBJ_SPECIAL)) { + // for now, really want a function call here + // uchar sftware_col[5]={0x3B,0x54,0x7D,0x62,0x27}; + uchar sftware_col[5] = {0x3B, 0x4F, 0x76, 0x5A, 0x23}; + int col = 0xC3, dm = 0, move_me = 1, ndm; + extern uchar time_passes; + fix fx, fy, fz; + int sc, v; + // static long ltime=0; + + switch (_fr_cobj->obclass) { + case CLASS_HARDWARE: + col = 0x48 - (objHardwares[_fr_cobj->specID].version << 1); + if (col < 0x45) + col = 0x42; + break; + case CLASS_BIGSTUFF: + sc = objBigstuffs[_fr_cobj->specID].data1; + v = objBigstuffs[_fr_cobj->specID].cosmetic_value; + dm = 0; + // Fall through, good + case CLASS_SOFTWARE: // ultra secret gnosis time + if (_fr_cobj->obclass == CLASS_SOFTWARE) { + sc = _fr_cobj->subclass; + v = objSoftwares[_fr_cobj->specID].version; + dm = objSoftwares[_fr_cobj->specID].data_munge; + } + col = sftware_col[sc] - v; + break; +#ifdef OLD_BIGSTUFF_WAY + case CLASS_BIGSTUFF: + col = sftware_col[objBigstuffs[_fr_cobj->specID].data1] - + (objBigstuffs[_fr_cobj->specID].cosmetic_value << 1); + dm = 0; +#endif + case CLASS_SMALLSTUFF: + if (_fr_cobj->subclass == SMALLSTUFF_SUBCLASS_CYBER) + col = objSmallstuffs[_fr_cobj->specID].cosmetic_value; + break; + case CLASS_ANIMATING: + if (_fr_cobj->subclass == ANIMATING_SUBCLASS_EXPLOSION) // should derive 3 and RED_8_BASE from somewhere + do_xplodamatron(_fr_cobj->info.current_frame, 3, 0x8457, RED_8_BASE, GREEN_8_BASE); + return; + } + if (dm == 0) { + fx = 0x4000; + fy = 0x3000; + fz = 0x2000; + } else { // first set the paramets + fix bsc = 0x3000, csc = (0x0500 * ((dm & 0x7) + 2)); + if (dm & 0x10) + fx = csc; + else + fx = bsc; + if (dm & 0x20) + fy = csc; + else + fy = bsc; + if (dm & 0x40) + fz = csc; + else + fz = bsc; + if ((csc == 0x4500) && (dm & 0x8)) + ndm = 0x7; + else if ((csc == 0x1000) && ((dm & 0x8) == 0)) + ndm = 0x8; + else + ndm = 0; + if (ndm) { + dm = (dm & ~0xf) + ndm; + if (((dm & 0x88) == 0x88) && ((rand() & 0x7) == 0)) + dm = (dm & ~0x70) + (rand() & 0x70); + } + if (dm & 0x8) + dm++; + else + dm--; + // switch (_fr_cobj->class) + // { case CLASS_SOFTWARE: objSoftwares[_fr_cobj->specID].data_munge=dm; break; } + } + _fr_draw_poly_cube(col, fx, fy, fz); + if (move_me && time_passes) { +// I'm just ifdef-ing this out for now, I'll put it back in +// when I have a more coherent plan WRT it. +#ifdef RUBBER_BABY_BUGGY_BUMPERS + _fr_cobj->loc.gZ = munge_val(_fr_cobj->loc.gZ, 256, 4); + _fr_cobj->loc.gX = (_fr_cobj->loc.gX & ~0xff) + munge_val(_fr_cobj->loc.gX & 0xff, 256, 6); + _fr_cobj->loc.gY = (_fr_cobj->loc.gY & ~0xff) + munge_val(_fr_cobj->loc.gY & 0xff, 256, 6); +#endif + _fr_cobj->loc.p += 248 + (((uint)cobjid) % 17); + _fr_cobj->loc.b += 247 + (((uint)cobjid) % 19); + _fr_cobj->loc.h += 245 + (((uint)cobjid) % 23); + } + if (obj_ICE_ICE_BABY(_fr_cobj)) + draw_ice(); + return; + } + } else + + // this is horrible and must be fixed!!! + if (((ObjProps[objtrip].flags & LIGHT_TYPE) <= (1 << LIGHT_TYPE_SHF)) || + (((ObjProps[objtrip].flags & LIGHT_TYPE) == (3 << LIGHT_TYPE_SHF)) && + ((_fr_cobj->info.inst_flags & UNLIT_FLAG) == 0))) { + MapElem *tmp; + int _obj_do_light(int which, fix dist); + fix hack_dist_approx = + fix_fast_pyth_dist(fix_fast_pyth_dist((_fr_p.gX - fr_camera_last[0]), (_fr_p.gZ - fr_camera_last[1])), + (_fr_p.gY + fr_camera_last[2])); + tmp = _fdt_mptr; + _fdt_pbase = 0; + _fdt_mptr = MAP_MAP + ((_fr_cobj->loc.x + 0x80) >> 8) + (((_fr_cobj->loc.y + 0x80) >> 2) & ~0x3F); + _sq_lght = _obj_do_light(FRPTSZFLR_DN, hack_dist_approx); + _fdt_mptr = tmp; + } else + _sq_lght = 0; + + switch (obj_type) { + case FAUBJ_BITMAP: + if ((anchors_3d[o3drep].x > 0) || (anchors_3d[o3drep].y > 0)) + _o_rad = 0; + _fr_draw_bitmap(bitmaps_3d[o3drep], _fdt_dist, ObjProps[objtrip].flags & MY_IM_LARGE, anchors_3d[o3drep].x, + anchors_3d[o3drep].y); + if (global_fullmap->cyber) + if (obj_ICE_ICE_BABY(_fr_cobj)) + draw_ice(); + break; + + case FAUBJ_SPECIAL: + switch (ID2TRIP(cobjid)) { + case MAPNOTE_TRIPLE: { + extern uchar map_notes_on; + g3s_phandle note_pts[4]; + int h = player_struct.game_time & 0x3fff; + + if (((_fr_curflags & FR_HACKCAM_MASK) == 0) && (map_notes_on)) { + int col = hud_colors[hud_color_bank][2]; + _fr_p.gY -= 0x4000; + g3_start_object_angles_xyz(&_fr_p, 0, h << 2, 0, ANGLE_ORDER); + gen_tetra(note_pts, 0x2000, 0, 0); + gr_set_fcolor(col); + g3_draw_line(note_pts[1], note_pts[0]); + g3_draw_line(note_pts[2], note_pts[0]); + g3_draw_line(note_pts[3], note_pts[0]); + g3_draw_line(note_pts[1], note_pts[2]); + g3_draw_line(note_pts[2], note_pts[3]); + g3_draw_line(note_pts[3], note_pts[1]); + g3_free_list(4, note_pts); + g3_end_object(); + _fr_p.gY += 0x6500; + } + } break; + case TRIPBEAM_TRIPLE: + // need a 3d line here... + break; + case FORCE_BRIJ_TRIPLE: + case FORCE_BRIJ2_TRIPLE: + tluc_val = -(int)((uchar)extract_object_special_color(cobjid)); + // mprintf("tluc %d\n",tluc_val); + case BARRICADE_TRIPLE: + // We want to hack tluc_val so that we draw a poly_cube and not a parm_cube + if (ID2TRIP(cobjid) == BARRICADE_TRIPLE) { + tluc_val = objSmallstuffs[_fr_cobj->specID].data2; + if (tluc_val == 0) + tluc_val = me_cybcolor_flr(MAP_GET_XY(OBJ_LOC_BIN_X(_fr_cobj->loc), OBJ_LOC_BIN_Y(_fr_cobj->loc))); + } + case BRIDGE_TRIPLE: + case PILLAR_TRIPLE: + case CATWALK_TRIPLE: + _o_rad = 0; + case SML_CRT_TRIPLE: + case LG_CRT_TRIPLE: + case SECURE_CONTR_TRIPLE: { + extern grs_bitmap tmap_bm[]; + + grs_bitmap *b1, b2; + Ref ref1 = 0, ref2 = 0; + fix fx, fy, fz; + b1 = obj_get_model_data(cobjid, &fx, &fy, &fz, &b2, &ref1, &ref2); + + // 255 is fully extended for hak x thru z + obj_model_hack(cobjid, &hakx, &haky, &hakz, &haktype); + // mprintf("Note for %x, got %d %d %d and %d\n",cobjid,hakx,haky,hakz,haktype); + + if (tluc_val != 0xFF) { + // Note that we do all this here, instead of in draw_poly_cube, since + // most poly cubes, namely cyberspace stuff, don't wanna be lit. + // Maybe this is the wrong way to do it, tho' -- X + int cur_ft; + cur_ft = gr_get_fill_type(); + if (cur_ft != FILL_SOLID) { + gr_set_fill_type(FILL_CLUT); + gr_set_fill_parm(_fr_clut_list[curr_clut_table] + (_sq_lght & 0xf00)); + } + _fr_draw_poly_cube(tluc_val, fx, fy, fz); + gr_set_fill_type(cur_ft); + } else if (b1 != NULL) { + _fr_draw_parm_cube(&b2, b1, fx, fy, fz); + } + if (ref1 != 0) + RefUnlock(ref1); + if (ref2 != 0) + RefUnlock(ref2); + } break; + } + haktype = 0xFF; + break; + + case FAUBJ_MULTIVIEW: + case FAUBJ_CRIT: { + int view; + + static uchar _qtab[4] = {3, 0, 2, 1}; + int xd = _fr_cobj->loc.x - (coor(EYE_X) >> 8); + int yd = _fr_cobj->loc.y - (coor(EYE_Y) >> 8); + int q, l; + + q = _qtab[((xd > 0) ? 2 : 0) + ((yd > 0) ? 1 : 0)]; + // mprintf("Q %d.. v(%d,%d)",q,xd,yd); + xd = abs(xd); + yd = abs(yd); + if (xd >= (yd << 1)) + l = 0; + else if ((xd << 1) <= yd) + l = 2; + else + l = 1; + if (q & 1) + view = (q << 1) + 2 - l; + else + view = (q << 1) + l; + // mprintf(".. yields view %d w/l at %d .. ",view,l); + view = ((_fr_cobj->loc.h - (view << 5) + 0x180) & 0xff) >> 5; + // mprintf(".. w/crit at %d gives %d\n",_fr_cobj->loc.h,view); + if (obj_type == FAUBJ_CRIT) { + LGRect anch; + // extern void ai_critter_seen(ObjID id); + // extern char curr_hack_cam; + // if (curr_hack_cam) + // ai_critter_seen(cobjid); + switch (ID2TRIP(cobjid)) { + case DIEGO_TRIPLE: + if ((get_crit_posture(_fr_cobj->specID) == DEATH_CRITTER_POSTURE) && + (player_struct.level != DIEGO_DEATH_BATTLE_LEVEL)) { + grs_bitmap tele_bm; + uchar line, *srcp, *dstp, *trgp; + tpdata = get_critter_bitmap_fast(cobjid, ID2TRIP(cobjid), get_crit_posture(_fr_cobj->specID), 0, + (ubyte)view, &ref, &anch); + gr_rsd8_convert(tpdata, &tpdata_temp); + tpdata = &tpdata_temp; + LG_memcpy(&tele_bm, tpdata, sizeof(grs_bitmap)); + tele_bm.bits = big_buffer + 32768; + LG_memset(tele_bm.bits, 0, tele_bm.w * tele_bm.h); + line = fix_int(fix_mul_div(fix_make(_fr_cobj->info.current_frame, 0), fix_make(tele_bm.h, 0), + fix_make(MAX_TELEPORT_FRAME, 0))); + for (srcp = tpdata->bits, dstp = tele_bm.bits, trgp = tpdata->bits + line * tele_bm.w; srcp < trgp; + srcp++, dstp++) + if (*srcp != 0) + *dstp = TELEPORT_COLOR + (rand() & 0x3); + trgp = tpdata->bits + tele_bm.h * tele_bm.w; + LG_memcpy(dstp, srcp, trgp - srcp); + + _fr_draw_bitmap(&tele_bm, _fdt_dist, FALSE, anch.ul.x, anch.ul.y); + release_critter_bitmap_fast(ref); + return; + } + break; + } + // bitmask out the lighting stuff + tpdata = get_critter_bitmap_obj_fast(cobjid, view, &ref, &anch); + switch (ID2TRIP(cobjid)) { + case INVISO_CRIT_TRIPLE: +#ifdef TRANSLUCENT_INVISOS +#ifdef TLUC_IN_2D + tpdata->flags |= BMF_TLUC8; +#else + gr_rsd8_convert(tpdata, &tpdata_temp); + tpdata = &tpdata_temp; + tpdata->type = BMT_TLUC8; +#endif +#endif + break; + } + _fr_draw_bitmap(tpdata, _fdt_dist, 0, anch.ul.x, anch.ul.y); + release_critter_bitmap_fast(ref); + } else { + tpdata = bitmaps_3d[o3drep + view]; + _fr_draw_bitmap(tpdata, _fdt_dist, 0, -1, -1); + } + } break; + + case FAUBJ_VOX: { // wait, cant we just keep vvv around, and stuff the two bitmaps, w+h each time, save some here + vxs_vox vvv; + fix damage_factor = fix_div(fix_make(_fr_cobj->info.current_hp, 0), fix_make(ObjProps[objtrip].hit_points, 0)); + fix pdist = VOXEL_PIX_DIST_BASE; + fix psize = VOXEL_PIX_SIZE_BASE + fix_mul(VOXEL_PIX_SIZE_DELTA, damage_factor); + + g3_start_object_angles_xyz(&_fr_p, _fr_cobj->loc.p << 8, _fr_cobj->loc.h << 8, _fr_cobj->loc.b << 8, + ANGLE_ORDER); + vx_init_vox(&vvv, pdist, psize, VOXEL_DEPTH, bitmaps_3d[o3drep], bitmaps_3d[o3drep + 1]); + vx_render(&vvv); + g3_end_object(); + } break; + + case FAUBJ_TL_POLY: + tluc_val = -(int)((uchar)extract_object_special_color(cobjid)); + tpdata = NULL; + case FAUBJ_TEXBITMAP: + case FAUBJ_TPOLY: { + g3s_phandle corn[4]; + g3s_vector ul; + fix fix_xoff, fix_yoff; + + scale = 0; + switch (obj_type) { + case FAUBJ_TPOLY: + switch (ID2TRIP(cobjid)) { + case TMAP_TRIPLE: { + extern uchar all_textures; + tpdata = get_texture_map(objBigstuffs[_fr_cobj->specID].data2, + (all_textures) ? TEXTURE_128_INDEX : TEXTURE_64_INDEX); + scale = -1; + } break; + default: + tpdata = bitmap_from_tpoly_data(o3drep, (ubyte *)&scale, &index, &type, &ref); + if (ref != 0) + use_cache = TRUE; + break; + } + switch (ID2TRIP(cobjid)) { + case SUPERSCREEN_TRIPLE: + if (tpdata != NULL) scale = 7 - tpdata->wlog; + break; + case TMAP_TRIPLE: + case BIGSCREEN_TRIPLE: + if (tpdata != NULL) scale = 6 - tpdata->wlog; + break; + case SCREEN_TRIPLE: + if (tpdata != NULL) scale = 5 - tpdata->wlog; + break; + } + switch (ID2TRIP(cobjid)) { + case SCREEN_TRIPLE: + case SUPERSCREEN_TRIPLE: + case BIGSCREEN_TRIPLE: + if ((!curr_clut_table) && (objBigstuffs[_fr_cobj->specID].data2 != DESTROYED_SCREEN_ANIM_BASE + 3)) + light_me = FALSE; + break; + } + break; + case FAUBJ_TEXBITMAP: + switch (_fr_cobj->obclass) { + case CLASS_BIGSTUFF: + switch (ID2TRIP(cobjid)) { + case WORDS_TRIPLE: + tpdata = get_text_bitmap_obj(cobjid, 0, &scale); // C is for magic cookie, it's good enough for me + ref = 0xFFFFFFFF; + break; + default: + tpdata = get_obj_cache_bitmap(cobjid, &ref); + break; + } + break; + case CLASS_DOOR: + tpdata = get_obj_cache_bitmap(cobjid, &ref); + if ((_fr_cobj->info.current_frame == 0) && (ObjProps[objtrip].flags & RENDER_BLOCK)) + tpdata->flags &= ~BMF_TRANS; +#ifdef HIGHRES_DOORS +#ifdef SVGA_SUPPORT + scale -= 1; +#endif +#endif + break; + default: + tpdata = get_obj_cache_bitmap(cobjid, &ref); + break; + } + if (ref == 0) + tpdata = bitmaps_3d[o3drep]; + else if (ref != 0xFFFFFFFF) + use_cache = TRUE; + break; + } + // scale us + if (tpdata == NULL) // (type==TPOLY_TMAP) + fix_yoff = fix_xoff = fix_make(0, 0x8000); + else // here is where to scale up and all..... + { + fix_xoff = tpdata->w << 9; + fix_yoff = tpdata->h << 9; + } + if (scale > 0) { + fix_xoff <<= scale; + fix_yoff <<= scale; + } else if (scale < 0) { + fix_xoff >>= -scale; + fix_yoff >>= -scale; + } + ul.gX = -fix_xoff; + ul.gY = -fix_yoff; + ul.gZ = 0; // ul.gZ=fix_make(0,0x0080); + + // gruesome hack of destruction!!! + if ((_fr_p.gX & 0xffff) == 0xff00) + _fr_p.gX += 0x100; + if ((_fr_p.gZ & 0xffff) == 0xff00) + _fr_p.gZ += 0x100; + // mprintf("Door at %x %x %x\n",_fr_p.gX,_fr_p.gY,_fr_p.gZ); + + g3_start_object_angles_xyz(&_fr_p, _fr_cobj->loc.p << 8, _fr_cobj->loc.h << 8, _fr_cobj->loc.b << 8, + ANGLE_ORDER); + corn[0] = g3_transform_point(&ul); + corn[1] = g3_copy_add_delta_x(corn[0], fix_xoff << 1); + corn[2] = g3_copy_add_delta_y(corn[1], fix_yoff << 1); + corn[3] = g3_copy_add_delta_y(corn[0], fix_yoff << 1); + _fr_draw_tmtile(tpdata, tluc_val, corn, (_fr_cobj->obclass == CLASS_DOOR), light_me); + if (use_cache) + release_obj_cache_bitmap(ref); + g3_end_object(); + break; + } + + default: + case FAUBJ_TEXTPOLY: + tpdata = bitmap_from_tpoly_data(o3drep, (ubyte *)&scale, &index, &type, &ref); + g3_set_vtext(0, tpdata); + case FAUBJ_ANIMPOLY: + if ((_fr_cobj->obclass == CLASS_SMALLSTUFF) && (_fr_cobj->subclass == SMALLSTUFF_SUBCLASS_CYBER)) + g3_set_vcolor(0, objSmallstuffs[_fr_cobj->specID].cosmetic_value); + case FAUBJ_FLATPOLY: + if (global_fullmap->cyber) { + int foog; + switch (_fr_cobj->obclass) { + case CLASS_CRITTER: + if (_fr_cobj->subclass == CRITTER_SUBCLASS_CYBER) { + for (foog = 0; foog < NUM_VCOLORS; foog++) { + if ((objCritters[_fr_cobj->specID].mood == AI_MOOD_HOSTILE) || + (objCritters[_fr_cobj->specID].mood == AI_MOOD_ATTACKING)) { + uchar c = CyberCritterProps[SCNUM(cobjid)].alt_vcolors[foog], nc; + int hp_state; + int denom = ObjProps[objtrip].hit_points; + if (denom == 0) denom = 1; + hp_state = + 256 * 14 - + ((256 * 14 * _fr_cobj->info.current_hp / denom) & (~0xff)); + if (hp_state > 0xf00) + hp_state = 0xf00; + else if (hp_state < 0) + hp_state = 0; + nc = *((_fr_clut_list[0]) + hp_state + c); + g3_set_vcolor(foog + 1, nc); + } else + g3_set_vcolor(foog + 1, CyberCritterProps[SCNUM(cobjid)].vcolors[foog]); + } + } + break; + case CLASS_SMALLSTUFF: + // Yes, arbitrariness.... + for (foog = 0; foog < NUM_SMALLSTUFF_VCOLORS; foog++) { + g3_set_vcolor(foog + 1, CyberSmallstuffProps[SCNUM(cobjid)].vcolors[foog]); + // Warning(("setting vcolor %d to + // 0x%x!\n",foog+1,CyberSmallstuffProps[SCNUM(cobjid)].vcolors[foog])); + } + // g3_set_vcolor(foog+1,0x33 + objSmallstuffs[_fr_cobj->specID].cosmetic_value + (foog + // << 2)); + break; + case CLASS_PHYSICS: + if (_fr_cobj->subclass == PHYSICS_SUBCLASS_SLOW) { + for (foog = 0; foog < NUM_SLOW_VCOLORS; foog++) { + g3_set_vcolor(foog + 1, SlowPhysicsProps[SCNUM(cobjid)].vcolors[foog]); + } + } + break; + } + } + model_num = ObjProps[objtrip].mfd_id; + if (!model_valid(model_num)) + model_num = 0; + load_model_vtexts(model_num); + model_ptr = (uchar *)get_model_data(model_num); + // go go go + if (ID2TRIP(cobjid) == CAMERA_TRIPLE) { + int mval; + loc_h = _fr_cobj->loc.h; + switch (objBigstuffs[_fr_cobj->specID].data1) { + case 0: + break; + default: + loc_h -= 32; + mval = ((*tmd_ticks) >> 5) & 0x7f; + if (mval > 64) + mval = 128 - mval; + loc_h += mval; + mval = _fr_cobj->loc.h; + _fr_cobj->loc.h = loc_h; + loc_h = mval; + } + } + + _fr_draw_polyobj(model_ptr, !global_fullmap->cyber); + if (ID2TRIP(cobjid) == CAMERA_TRIPLE) + _fr_cobj->loc.h = loc_h; // hack hack hack + if (ref != 0) + release_obj_cache_bitmap(ref); + release_model_data(model_num); + free_model_vtexts(model_num); + if (global_fullmap->cyber) + if (obj_ICE_ICE_BABY(_fr_cobj)) + draw_ice(); + break; + } +} diff --git a/engine/src/GameSrc/gamerend.c b/engine/src/GameSrc/gamerend.c new file mode 100644 index 0000000..de0dfe2 --- /dev/null +++ b/engine/src/GameSrc/gamerend.c @@ -0,0 +1,733 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: u://RCS/gamerend.c $ + * $Revision: 1.77 $ + * $Author: xemu $ + * $Date: 1994/11/25 08:22:19 $ + */ + +#define __GAMEREND_SRC + +#include + +#include "tools.h" + +#include "cyber.h" +#include "gamerend.h" +#include "weapons.h" +#include "colors.h" +#include "rcolors.h" +#include "cybstrng.h" // for resurrect text +#include "gamestrn.h" +#include "gamescr.h" +#include "mainloop.h" +#include "game_screen.h" +#include "player.h" +#include "shodan.h" +#include "bark.h" +#include "cit2d.h" +#include "damage.h" +#include "diffq.h" // for time limit +#include "email.h" +#include "newmfd.h" +#include "fullscrn.h" + +#include "hud.h" +#include "hand.h" +#include "wares.h" +#include "rendfx.h" +#include "hudobj.h" // for beam effect following + +// maybe we can not have this +#include "frtypes.h" // has to be before frprotox so proto gets the context for real +#include "frprotox.h" +#include "frflags.h" +#include "gr2ss.h" + +#include "faketime.h" +#include "hkeyfunc.h" + +#ifdef AUDIOLOGS +#include "audiolog.h" +#endif + +extern uchar tmap_big_buffer[]; + +// prototypes +void do_secret_fx(void); +void gamesys_render_effects(void); +uchar use_ir_hack(void); +void draw_single_static_line(uchar *line_base, int lx, int rx, int c_base); +void draw_line_static(grs_bitmap *stat_dest, int dens1, int color1); + +errtype gamerend_init(void) { + handart_show = 1; + return (OK); +} + +// note these are now 0-255, like all computer percentages should be... +static ubyte fr_sfx_color; +static ubyte dmg_percentage; +static char shield_raisage; + +void set_shield_raisage(uchar going_up) { + if (going_up) + shield_raisage = 1; + else + shield_raisage = -15; + fr_global_mod_flag(FR_SFX_SHIELD, FR_SFX_MASK); +} + +void begin_shodan_conquer_fx(uchar begin) { + if (begin) + fr_global_mod_flag(FR_OVERLAY_SHODAN, FR_OVERLAY_MASK); + else + fr_global_mod_flag(0, FR_OVERLAY_SHODAN); +} + +uchar color_base[] = {BLUE_8_BASE, RED_8_BASE, GREEN_8_BASE}; + +#define MIN_STATIC 10 + +void set_dmg_percentage(int which, ubyte percent) { + if (percent > dmg_percentage) { + fr_sfx_color = color_base[which]; + dmg_percentage = percent; + if (dmg_percentage < MIN_STATIC) + dmg_percentage = MIN_STATIC; + fr_global_mod_flag(FR_SFX_STATIC, FR_SFX_MASK); + } +} + +// ----------------------------------------------- +// beam_effect_update() +// + +extern ObjID beam_effect_id; + +#define PRE_FINAL_DEATH 0x1A +#define DYING_FRAMES 0x20 +#define REBORN_VISION_TICK 0x10 +#define REBORN_FRAMES 0x18 +#define FAKEWIN_FRAMES 0x30 + +#define FAKEWIN_NUM_FONT RES_bigLEDFont +#define FAKEWIN_TEXT_FONT RES_readingFont +#define FAKEWIN_EMAIL_MUNGE 0x01c +#define FAKEWIN_PAPER 0x8 +#define FAKEWIN_STRING_BASE REF_STR_fakewinStrings + +// about a quarter second, really +#define V_CLOCK 0x040 +#define V_MASK (~0x03f) + +#define build_systems_y_coor(i) (5 + (14 * i)) + +static uchar systems_line_colors[] = {RED_8_BASE, RED_8_BASE + 3, ORANGE_8_BASE, ORANGE_8_BASE + 3, GREEN_8_BASE}; + +//#define NUM_SYS_LINES ((sizeof(systems_lines)/sizeof(systems_lines[0]))) +// There is probably a clever way to get this out of the resource, but this is much simpler... +#define NUM_SYS_LINES 5 +#define SYSTEM_BASE REF_STR_ResurrectBase +#define LINE_BUF_SIZE 40 + +#define STATUS_CHI_AMP 8 + +#define CURRENT_VIEW_W (_current_view->r->lr.x - _current_view->r->ul.x) +#define CURRENT_VIEW_H (_current_view->r->lr.y - _current_view->r->ul.y) + +extern uchar flatline_heart; +extern uchar chi_amp; + +ulong secret_sfx_time; +void do_secret_fx(void) { // boy is this a hack.... + static char dot_buf[] = "........"; + static char tmp_buf[] = "99"; + static grs_font *fx_font = NULL; + static long sfx_time = 0; + int c_val = secret_render_fx & VAL_REND_SFX, i, cap; + char line_buf[LINE_BUF_SIZE]; + Ref str; + + if (fx_font == NULL) + fx_font = ResLock(RES_mfdFont); + switch (secret_render_fx & TYPE_REND_SFX) { + case DYING_REND_SFX: // chevron drain, perhaps dim out, view rock at end..... + secret_render_fx++; + flatline_heart = TRUE; + chi_amp = STATUS_CHI_AMP * (DYING_FRAMES - c_val) / DYING_FRAMES; + if (c_val == DYING_FRAMES) { + if (kill_player()) { + secret_render_fx = 0; + player_struct.dead = FALSE; + flatline_heart = FALSE; + chi_amp = STATUS_CHI_AMP; + } else { + secret_render_fx = REBORN_REND_SFX; + fr_global_mod_flag(FR_SOLIDFR_SLDKEEP, FR_SOLIDFR_MASK | FR_SFX_MASK); + fr_solidfr_color = 0xff; + dmg_percentage = 0; + regenerate_player(); + } + } else { + if (c_val >= PRE_FINAL_DEATH) + fr_global_mod_flag(FR_SOLIDFR_STATIC, FR_SOLIDFR_MASK); + dmg_percentage = 20 + (c_val << 2); + fr_global_mod_flag(FR_SFX_STATIC, FR_SFX_MASK); + } + break; + case REBORN_REND_SFX: // add chevron growth, lighting and dimming, starting on back... + gr_set_font(fx_font); + cap = c_val >> 2; + if (cap >= NUM_SYS_LINES) + cap = NUM_SYS_LINES; + for (i = 0; i < cap; i++) { + gr_set_fcolor(systems_line_colors[i]); + ss_string(dot_buf, 30, build_systems_y_coor(i)); + ss_string(get_string(str = (SYSTEM_BASE + i), line_buf, LINE_BUF_SIZE), 56, build_systems_y_coor(i)); + } + if (str == REF_STR_StartHeartString) + flatline_heart = FALSE; + else if (str == REF_STR_StartBrainString) + chi_amp = STATUS_CHI_AMP; + if (c_val < REBORN_VISION_TICK) { + gr_set_fcolor(systems_line_colors[i]); + dot_buf[c_val & 7] = '\0'; + ss_string(dot_buf, 30, build_systems_y_coor(i)); + dot_buf[c_val & 7] = '.'; + } + + if (*tmd_ticks - sfx_time > V_CLOCK) { + secret_render_fx++; + if (c_val == REBORN_FRAMES) { + extern uchar music_on; + secret_render_fx = 0; + + // look - we should say the player is no longer dead! + player_struct.dead = FALSE; + uiFlush(); + // KLC spoof_mouse_event(); + if (music_on) + start_music(); + break; + } else if (c_val == REBORN_VISION_TICK) + fr_global_mod_flag(0, FR_SOLIDFR_MASK); + + sfx_time = (*tmd_ticks) & V_MASK; + } + break; + case TIMELIMIT_REND_SFX: { + grs_font *f; + short w, h; + int stage = (MISSION_3_TICKS - player_struct.game_time) / CIT_CYCLE; + + tmp_buf[0] = tmp_buf[1] = '0'; + f = ResLock(FAKEWIN_NUM_FONT); + gr_set_font(f); + gr_string_size(tmp_buf, &w, &h); + + if (stage < 0 || stage > 99) + stage = 0; + tmp_buf[0] = '0' + stage / 10; + tmp_buf[1] = '0' + stage % 10; + draw_shadowed_string(tmp_buf, (CURRENT_VIEW_W - w) / 2, (CURRENT_VIEW_H - h) / 2, TRUE); + ResUnlock(FAKEWIN_NUM_FONT); + break; + } + case FAKEWIN_REND_SFX: // do stuff + { + char stage = (*tmd_ticks - secret_sfx_time) / CIT_CYCLE; + gr_set_fcolor(RED_BASE + 6); + if (stage < 2) + break; + if (stage < 5) { + res_draw_string(FAKEWIN_TEXT_FONT, FAKEWIN_STRING_BASE, 30, 20); + res_draw_string(FAKEWIN_TEXT_FONT, FAKEWIN_STRING_BASE + 1, 50, 45); + break; + } + if (stage < 18) { + if ((stage > 10) && (c_val == 0)) { + fr_global_mod_flag(FR_SFX_SHAKE, FR_SFX_MASK); + secret_render_fx++; + } + res_draw_string(FAKEWIN_TEXT_FONT, FAKEWIN_STRING_BASE + 2, 50, 30); + tmp_buf[0] = '0' + ((20 - stage) / 10); + tmp_buf[1] = '0' + ((20 - stage) % 10); + res_draw_text(FAKEWIN_NUM_FONT, tmp_buf, 85, 45); + res_draw_string(FAKEWIN_TEXT_FONT, FAKEWIN_STRING_BASE + 3, 95, 80); + break; + } + if (stage < 22) { + if (c_val == 1) { + fr_global_mod_flag(FR_SFX_SHAKE, FR_SFX_MASK); + long_bark(OBJ_NULL, FIRST_SHODAN_MUG + 3, REF_STR_Null, 0); + mfd_change_slot(MFD_LEFT, MFD_INFO_SLOT); + mfd_change_slot(MFD_RIGHT, MFD_INFO_SLOT); +// KLC-duplicate mail add_email_datamunge(FAKEWIN_EMAIL_MUNGE, FALSE); +#ifdef AUDIOLOGS + if (audiolog_setting) + audiolog_play(FAKEWIN_EMAIL_MUNGE); + if (audiolog_setting != 1) +#endif + read_email(RES_paper0, FAKEWIN_PAPER); + fr_global_mod_flag(0, FR_SFX_MASK); + secret_render_fx++; + } + gr_set_fcolor(RED_BASE + 6); + res_draw_string(FAKEWIN_TEXT_FONT, FAKEWIN_STRING_BASE + 4, 75, 50); + break; + } else { + secret_render_fx = 0; + } + } break; + } + if (secret_render_fx == 0) // why must we unset both + { + ResUnlock(RES_mfdFont); + fx_font = NULL; + chg_unset_sta(GL_CHG_2); + chg_unset_flg(GL_CHG_2); + } +} + +extern short mouse_attack_x; +extern short mouse_attack_y; +extern ulong next_fire_time; +extern uchar overload_beam; +extern uchar saveload_static; +extern bool DoubleSize; + +byte beam_offset[NUM_BEAM_GUN] = {-12, -8, -4}; +#define DRAW_BEAM_LINE(c1, c2, c3, c4) \ + { \ + a = mx + (c1); \ + b = my + (c2); \ + ss_point_convert(&a, &b, TRUE); \ + ss_thick_fix_line(fix_make(a, 0), fix_make(b, 0), fix_make(deltax + (c3) + boff + beamx, 0), \ + fix_make(deltay + (c4) + boff + 6, 0)); \ + } + +/* +#define DRAW_BEAM_LINE(c1,c2,c3,c4) { \ + a = mx+(c1);\ + b = my+(c2);\ + if (DoubleSize) \ + gr_fix_line(fix_make(a,0),fix_make(b,0),fix_make(deltax+(c3)+boff,0), fix_make(deltay+22,0));\ + else \ + { \ + ss_point_convert(&a,&b,TRUE);\ + ss_thick_fix_line(fix_make(a,0),fix_make(b,0),fix_make(deltax+(c3)+boff,0),fix_make(deltay+(c4)+boff,0));\ + } \ +} +*/ + +void gamesys_render_effects(void) { + Ref temp; + int deltax, deltay, beamx; + short mx, my; + extern uchar full_game_3d; + + TRACE("%s: gamerend", __FUNCTION__); + + if ((!global_fullmap->cyber) && (!secret_render_fx)) { + ubyte active = player_struct.actives[ACTIVE_WEAPON]; + extern uchar hack_takeover; + + // check to make sure we have an active weapon before drawing handart + if ((player_struct.weapons[active].type != EMPTY_WEAPON_SLOT) && !hack_takeover && !saveload_static) { + // For hand-to-hand weapons, draw them in the center of the screen. + if (player_struct.weapons[active].type == GUN_SUBCLASS_HANDTOHAND) { + mx = (SCREEN_VIEW_WIDTH / 2) + SCREEN_VIEW_X; + my = (SCREEN_VIEW_Y); + ss_point_convert(&mx, &my, TRUE); + } else if (handart_show != 1) // Use mouse position for other weapons + { + mx = mouse_attack_x; + my = mouse_attack_y; + if (!DoubleSize) + ss_point_convert(&mx, &my, TRUE); + else + ui_mouse_get_xy(&mx, &my); + } else // Not showing a weapon + { + ui_mouse_get_xy(&mx, &my); + } + + // Get the weapon art to draw. + temp = get_handart(&deltax, &deltay, &beamx, mx, my); + if (temp != ID_NULL) { + if (handart_show != 1) // are we showing an attack frame? + { + // If this is a beam weapon, we need to draw the beam during attack. + if (player_struct.weapons[active].type == GUN_SUBCLASS_BEAM) { + short base_color = (overload_beam) ? BLUE_BASE : TURQUOISE_BASE; + byte boff = beam_offset[player_struct.weapons[active].subtype]; + + if (beam_effect_id) { + int i; + uchar draw_beam = FALSE; + + for (i = 0; i < current_num_hudobjs; i++) { + struct _hudobj_data *dat = &hudobj_vec[i]; + if ((dat->id == beam_effect_id) && beam_effect_id) { + mx = (dat->xl + dat->xh) / 2; + my = (dat->yl + dat->yh) / 2; + if (DoubleSize) { + mx *= 2; + my *= 2; + } + draw_beam = TRUE; + } + } + if (draw_beam) { + short a, b; + + gr_set_fcolor(base_color + 5); + if (overload_beam) { + DRAW_BEAM_LINE(-2, 0, 17, 12); + gr_set_fcolor(base_color + 1); + } + DRAW_BEAM_LINE(-1, 0, 18, 12); + gr_set_fcolor(base_color + 1); + DRAW_BEAM_LINE(0, 0, 19, 12); + if (!overload_beam) + gr_set_fcolor(base_color + 5); + DRAW_BEAM_LINE(1, 0, 20, 12); + + if (overload_beam) { + gr_set_fcolor(base_color + 5); + DRAW_BEAM_LINE(2, 0, 21, 12); + } + } + } + } + } + if ((handart_show != 1) || ready_to_draw_handart()) { + // draw_hires_resource_bm(temp, SCONV_X(deltax), SCONV_Y(deltay)); + draw_res_bm(temp, deltax, deltay); + notify_draw_handart(); + } + } + } + } + + // Redraw hud displays as appropriate + // HOW ABOUT A FLAG HERE, NOT HARDCODED LOOP NUMBERS + if (!secret_render_fx && _current_loop <= FULLSCREEN_LOOP) { + hud_update(FALSE, _current_fr_context); + } + + if (secret_render_fx) + do_secret_fx(); + + // draw the gamescreen border + if (!full_game_3d) { + // was draw_hires_resource_bm + draw_res_bm(REF_IMG_bm3dBackground1, 0, -2); + draw_res_bm(REF_IMG_bm3dBackground2, 226, -1); + draw_res_bm(REF_IMG_bm3dBackground2, 270, -1); + draw_res_bm(REF_IMG_bm3dBackground3, 522, -2); + draw_res_bm(REF_IMG_bm3dBackground4, 0, 197); + + // whoop whoop whoop! + // hack alert! hack alert! + if (convert_use_mode == 3) { + draw_res_bm(REF_IMG_bm3dBackground5, 27, 257); + draw_res_bm(REF_IMG_bm3dBackground6, 415, 257); + } + } else + fullscreen_overlay(); +} + +uchar use_ir_hack(void) { return (WareActive(player_struct.hardwarez_status[HARDWARE_GOGGLE_INFRARED])); } + +//#pragma aux c_ror_by_5 = "ror eax,5" parm [eax] modify exact [eax]; + +// stolen from RND.LIB, without shift gruesomeness... +#define LC16_MULT 2053 +#define LC16_ADD 13849 + +void draw_single_static_line(uchar *line_base, int lx, int rx, int c_base) { + uchar *cur_pix; + int our_seed = rand(); + for (cur_pix = line_base + lx; lx < rx; lx++, cur_pix++) { +#ifdef SIMPLE_LC_WAY + our_seed = (our_seed * LC16_MULT) + LC16_ADD; + if (our_seed & 0x300) // 3/4 are colored + *cur_pix = c_base + (our_seed & 0x7); + else // 1/4 black + *cur_pix = 0; +#else + if (our_seed & 0x300) { + *cur_pix = c_base + (our_seed & 0x7); + our_seed += (long)cur_pix; + our_seed = ((our_seed >> 5) & 0x07ffffff) | (our_seed << 27); //ror by 5 + } else { + *cur_pix = 0; + our_seed += (our_seed * LC16_MULT) + LC16_ADD; + } +// { *cur_pix=0; our_seed+=rand(); } +#endif + } +} + +#define LAST_INITIAL 32 +// probably have to split it up, and then have a static pass and a translucency pass... +void draw_line_static(grs_bitmap *stat_dest, int dens1, int color1) { + int y, last = 0, lx, rx, cwid = stat_dest->w; + uchar *line_base; + + dens1 >>= 1; + for (line_base = stat_dest->bits, y = 0; y < stat_dest->h; y++, line_base += stat_dest->row) { + if ((last == LAST_INITIAL) || ((rand() & 0xff) < (dens1 + last))) { + lx = (rand() & 0xff) - 0x80; + if (lx > cwid) + lx = cwid - (lx & 0x1f); + if (lx < 0) + lx = 0; + rx = (rand() & 0xff) - 0x80; + if (rx > cwid) + rx = cwid - (rx & 0x1f); + if (rx < 0) + rx = 0; + rx = cwid - rx; + if (rx < lx) { + if (cwid - lx > rx) + lx = 0; + else { + lx = rx; + rx = cwid; + } // gnosis move + } + draw_single_static_line(line_base, lx, rx, color1); + if (last == 0) + last = LAST_INITIAL; + else if (last > 1) + last >>= 1; // decay repeat freq, stop at 1 + } else + last = 0; + } +} + +void draw_full_static( + grs_bitmap *stat_dest, + int c_base) { // note we do this as a for, not a big fill, so it will work with row hacks, full screen, so on.... + uchar *line_base; + int y; + + for (line_base = stat_dest->bits, y = 0; y < stat_dest->h; y++, line_base += stat_dest->row) + draw_single_static_line(line_base, 0, stat_dest->w, c_base); +} + +#define TELEPORT_COLOR 0x1C +#define VHOLD_SHIFT_AMOUNT 7 +short vhold_shift = 0; + +#define FULL_CONVERT_X + +// returns whether to send the bitmap out in the render +int gamesys_draw_func(void *fake_dest_canvas, void *fake_dest_bm, int x, int y, int flags) { + grs_canvas *dest_canvas = (grs_canvas *)fake_dest_canvas; + grs_bitmap *dest_bm = (grs_bitmap *)fake_dest_bm; + uchar *orig_bits; + int orig_h, loop, orig_w; + + TRACE("%s: gamerend", __FUNCTION__); + + if (flags & FR_WINDOWD_MASK) + gamesys_render_effects(); // static gets drawn over window dressing due to this + else + hud_do_objs(x, y, dest_bm->w, dest_bm->h, (flags & FR_DOHFLIP_MASK) != 0); + + if (flags & FR_DOUBLEB_MASK) // looks like a bug to me, eh? + { + switch (flags & FR_SFX_MASK) { + case FR_SFX_VHOLD: + (*fr_mouse_hide)(); + gr_set_canvas(dest_canvas); + + // Save off original state + orig_bits = dest_bm->bits; + orig_h = dest_bm->h; + + // KLC - adjust x and y if in doublesize mode. + if (DoubleSize) { + x *= 2; + y *= 2; + if (y > 0) + y++; // It's one off in slot view. + } + + // Note that all of this contrivance to keep vhold_shift in original + // 320x200 coordinates is to avoid the wacky class of bugs of + // shifting screen mode in the middle of an EMP grenade + { + short vhs = vhold_shift; + if (convert_use_mode) + vhs = SCONV_Y(vhold_shift); + + // Draw top bitmap + dest_bm->bits += dest_bm->row * (orig_h - vhs); + dest_bm->h = vhs; + if (dest_bm->h != 0) + gr_bitmap(dest_bm, x, y); + + // Draw bottom bitmap + dest_bm->bits = orig_bits; + dest_bm->h = orig_h - vhs; + if (dest_bm->h != 0) + gr_bitmap(dest_bm, x, y + vhs); + + // Restore state & increment shift + dest_bm->h = orig_h; + if (convert_use_mode) + vhold_shift += SCONV_Y(VHOLD_SHIFT_AMOUNT); + else + vhold_shift += VHOLD_SHIFT_AMOUNT; + if (convert_use_mode) + vhs = SCONV_Y(vhold_shift); + else + vhs = vhold_shift; + if (vhs > orig_h) + vhold_shift = 0; + } + (*fr_mouse_show)(); + return FALSE; + case FR_SFX_STATIC: + draw_line_static(dest_bm, dmg_percentage, fr_sfx_color); + dmg_percentage >>= 2; + if (dmg_percentage == 0) + fr_global_mod_flag(0, FR_SFX_MASK); + break; + case FR_SFX_TELEPORT: { + uchar *p = dest_bm->bits; + int count = 0; + while (count < (dest_bm->w * dest_bm->h)) { + *p = TELEPORT_COLOR + (rand() & 0x3); + p++; + count++; + } + } break; + case FR_SFX_SHIELD: + orig_h = (dest_bm->h >> 1) - (dest_bm->h >> 5) * abs(shield_raisage); + orig_w = (dest_bm->w >> 1) - (dest_bm->w >> 5) * abs(shield_raisage); + orig_bits = dest_bm->bits; + for (loop = 1; loop < 5; loop++) { + draw_single_static_line(orig_bits + (orig_h + loop) * dest_bm->w, orig_w + loop, + dest_bm->w - orig_w - loop, BLUE_8_BASE); + draw_single_static_line(orig_bits + (dest_bm->h - (orig_h + loop)) * dest_bm->w, orig_w + loop, + dest_bm->w - orig_w - loop, BLUE_8_BASE); + } + if (shield_raisage & 0xf) + shield_raisage++; + else + fr_global_mod_flag(0, FR_SFX_MASK); + break; + } + switch (flags & FR_OVERLAY_MASK) { + case FR_OVERLAY_SHODAN: { + int i; + extern uchar *shodan_bitmask; + extern grs_bitmap shodan_draw_fs; + extern grs_bitmap shodan_draw_normal; + extern char thresh_fail; + uchar *shodan_draw_bits; + grs_bitmap *curr_shodan; + short shodan_level = (player_struct.game_time - time_until_shodan_avatar) >> SHODAN_TIME_SHIFT; + if (shodan_level > MAX_SHODAN_LEVEL) + shodan_level = MAX_SHODAN_LEVEL; + if (full_game_3d) { + curr_shodan = &shodan_draw_fs; + shodan_draw_bits = shodan_draw_fs.bits; + } else { + curr_shodan = &shodan_draw_normal; + shodan_draw_bits = shodan_draw_normal.bits; + } + if ((thresh_fail) || ((rand() & 0x1FF) == 1)) { +#ifdef SVGA_SUPPORT + if (convert_use_mode) { + grs_bitmap temp_bm; + + // Note that we can use this since we know that audiologs aren't playing + // and that we don't want normal texture maps + gr_init_bitmap(&temp_bm, tmap_big_buffer, BMT_FLAT8, BMF_TRANS, curr_shodan->w, curr_shodan->h); + + for (i = 0; i < temp_bm.h * temp_bm.w; i = i + ((thresh_fail) ? 1 : 2)) + *(temp_bm.bits + i) = *(shodan_draw_bits + i); + + // Copy in and scale up the snowy bitmap + ss_bitmap(&temp_bm, 0, 0); + } else +#endif + { + for (i = 0; i < dest_bm->h * dest_bm->w; i = i + ((thresh_fail) ? 1 : 2)) + *(dest_bm->bits + i) = *(shodan_draw_bits + i); + } + } else { +#ifdef SVGA_SUPPORT + if (convert_use_mode) { + grs_bitmap temp_bm; + + // Note that we can use this since we know that audiologs aren't playing + // and that we don't want normal texture maps + gr_init_bitmap(&temp_bm, tmap_big_buffer, BMT_FLAT8, BMF_TRANS, curr_shodan->w, curr_shodan->h); + + for (i = 0; i < temp_bm.h * temp_bm.w; i++) { + if (SHODAN_CONQUER_GET(shodan_bitmask, i)) + *(temp_bm.bits + i) = *(shodan_draw_bits + i); + else + *(temp_bm.bits + i) = 0; + } + // Copy in and scale up the snowy bitmap + ss_bitmap(&temp_bm, 0, 0); + } else +#endif + { + for (i = 0; i < dest_bm->h * dest_bm->w; i++) { + if (SHODAN_CONQUER_GET(shodan_bitmask, i)) + *(dest_bm->bits + i) = *(shodan_draw_bits + i); + } + } + } + } break; + } + } + + return TRUE; // let the renderer do the blit +} + +void gamesys_render_func(void *fake_dest_bitmap, int flags) { + grs_bitmap *dest_bitmap = (grs_bitmap *)fake_dest_bitmap; + grs_canvas temp_canvas; + + gr_make_canvas(dest_bitmap, &temp_canvas); + gr_push_canvas(&temp_canvas); + switch (flags & FR_SOLIDFR_MASK) { + case FR_SOLIDFR_SLDCLR: // if clearing, reset the mask + fr_global_mod_flag(0, FR_SOLIDFR_MASK); + case FR_SOLIDFR_SLDKEEP: // else just clear to the color + gr_clear(fr_solidfr_color); + break; + case FR_SOLIDFR_STATIC: + draw_full_static(dest_bitmap, GRAY_8_BASE); + break; + } + gr_pop_canvas(); +} diff --git a/engine/src/GameSrc/gamesort.c b/engine/src/GameSrc/gamesort.c new file mode 100644 index 0000000..94b4b8b --- /dev/null +++ b/engine/src/GameSrc/gamesort.c @@ -0,0 +1,311 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * GameSort.c + * + * $Source: r:/prj/cit/src/RCS/gamesort.c $ + * $Revision: 1.10 $ + * $Author: dc $ + * $Date: 1994/08/24 06:19:35 $ + * + * Game system object sorting + */ + +#include + +#include "map.h" +#include "objects.h" +#include "objsim.h" +#include "objprop.h" +#include "objclass.h" +#include "otrip.h" +#include "frcamera.h" +#include "gameobj.h" +#include "gamesort.h" + +// want outrageous mprintf's? of course you do! +//#define SORT_SPEW + +// Internal Prototypes +void sort_section(int low, int hi); +void score_objs(int o_num); +int do_part_sort(int ptype, int lo, int hi, int ploc); +void partition_sort(void); + +#define MAX_SORTED_REFS 64 + +#define PRT_NONE 0 +#define PRT_HORIZ 0x20 +#define PRT_VERT 0x40 +#define PRT_BOTH 0x60 +#define PRT_MASK 0x60 + +// this just isnt going to work +#define SCORE_SHIFT 8 +#define SCORE_HALF_ENTRY 0x80 +#define SCORE_REFIDX_MASK 0x1F +#define SCORE_SCORE_MASK 0xFFFFFF00 + +// public, rendtool sets this for camera controls +ObjID no_render_obj = -1; + +static ObjID sq_Refs[MAX_SORTED_REFS]; +static uint score_list[MAX_SORTED_REFS]; // at first holds obj_type8.objtrip24, then objscore24.exp1.partition2.refidx5 + // thus we can sort it w/o losing the refidx, which looks up in sqrefs +static ushort partition_loc[MAX_SORTED_REFS]; // for the partitions + +static uchar partition_type; +static uchar partition_cnt; + +static uint cur_obj_num, draw_last_cnt; +static uint osort_xc, osort_yc, osort_zc; + +// set up data space, +void render_sort_start(void) { + cur_obj_num = 0; + osort_xc = fr_camera_last[0] >> 8; + osort_yc = fr_camera_last[1] >> 8; + osort_zc = fr_camera_last[2] >> (16 - SLOPE_SHIFT - 3); // oh yea +} + +void sort_section(int low, int hi) { + int iloop, oloop; + uint tmp; + + for (oloop = hi - 1; oloop >= low; oloop--) + for (iloop = low; iloop <= oloop; iloop++) + if (score_list[iloop] < score_list[iloop + 1]) { + tmp = score_list[iloop]; + score_list[iloop] = score_list[iloop + 1]; + score_list[iloop + 1] = tmp; + } +} + +void score_objs(int o_num) { + ObjID cobjid; + short objtrip; + Obj *_os_cobj; + extern uchar cam_mode; + int partition = PRT_NONE, obj_type, our_score; + + cobjid = sq_Refs[o_num]; + obj_type = score_list[o_num] >> 24; + objtrip = score_list[o_num] & 0xffffff; + _os_cobj = &objs[cobjid]; + switch (obj_type) { + case FAUBJ_SPECIAL: + switch (ID2TRIP(cobjid)) { + case TRIPBEAM_TRIPLE: + partition = PRT_HORIZ; + break; + case FORCE_BRIJ_TRIPLE: + case BRIDGE_TRIPLE: + case CATWALK_TRIPLE: + partition = PRT_VERT; + break; + } + break; + case FAUBJ_TL_POLY: + case FAUBJ_TEXBITMAP: + case FAUBJ_TPOLY: + if (_os_cobj->obclass == CLASS_DOOR) + if (((_os_cobj->loc.p + 0x20) & 0x7f) < 0x40) + partition = PRT_HORIZ; + else + partition = PRT_VERT; + else + partition = -1; // secret i am a freebie code + break; + } + our_score = + abs((int)osort_xc - (int)_os_cobj->loc.x) + + abs((int)osort_yc - (int)_os_cobj->loc.y) + + abs((int)osort_zc - (int)_os_cobj->loc.z); + our_score <<= 8; // 24 bits of score, should do for now + // if bloodstain, want to | 0x80 here... + if (partition > 0) { + our_score |= partition | o_num; + partition_loc[partition_cnt] = o_num; + partition_cnt++; + partition_type |= partition; + } else if (partition < 0) { + int tmp = our_score | o_num; + if (draw_last_cnt != o_num) { + our_score = score_list[draw_last_cnt]; + if (our_score & PRT_MASK) { + int i = 0; + while (i < partition_cnt) + if (partition_loc[i] == draw_last_cnt) { + partition_loc[i] = o_num; + break; + } + if (i == partition_cnt) + WARN("%s: lost my partition", __FUNCTION__); + } + score_list[draw_last_cnt] = tmp; + } else { + our_score = tmp; + } + draw_last_cnt++; + } else { + our_score |= o_num; + } + score_list[o_num] = our_score; +} + +#define get_part_val(tval, ob, which) \ + switch (which) { \ + case 0: tval = ob->loc.x; break; \ + case 1: tval = ob->loc.y; break; \ + case 2: tval = ob->loc.z; break; \ + } + +int do_part_sort(int ptype, int lo, int hi, int ploc) { + Obj *cur_obj, *part_obj; + int cur_val, part_val, pdir, cam_val, near_f, loidx, hiidx; + int ptptr[2], ptdelta[2], tmpstore = -1, mloc; + + part_obj = &objs[sq_Refs[ploc]]; + pdir = (ptype == PRT_VERT) ? 2 : (part_obj->loc.h & 0x40) ? 0 : 1; + get_part_val(part_val, part_obj, pdir); + cam_val = pdir == 0 ? osort_xc : pdir == 1 ? osort_yc : osort_zc; + near_f = (cam_val < part_val); + + if ((lo <= ploc) && (ploc < hi)) { + tmpstore = score_list[ploc]; + if (ploc < --hi) + score_list[ploc] = score_list[hi]; + } else + WARN("%s: Partition not in list", __FUNCTION__); + + if (near_f) { + ptptr[0] = hi - 1; + ptdelta[0] = -1; + ptptr[1] = lo; + ptdelta[1] = 1; + loidx = 1; + hiidx = 0; + } else { + ptptr[0] = lo; + ptdelta[0] = 1; + ptptr[1] = hi - 1; + ptdelta[1] = -1; + loidx = 0; + hiidx = 1; + } + + // ok, this checks in a massively gruesomely redundant way, rewrite for real in asm + while (ptptr[loidx] <= ptptr[hiidx]) { + cur_obj = &objs[sq_Refs[score_list[ptptr[0]] & SCORE_REFIDX_MASK]]; + get_part_val(cur_val, cur_obj, pdir); + while (cur_val < part_val) { + ptptr[0] += ptdelta[0]; + if (ptptr[loidx] > ptptr[hiidx]) + goto done_psort; + cur_obj = &objs[sq_Refs[score_list[ptptr[0]] & SCORE_REFIDX_MASK]]; + get_part_val(cur_val, cur_obj, pdir); + } + + cur_obj = &objs[sq_Refs[score_list[ptptr[1]] & SCORE_REFIDX_MASK]]; + get_part_val(cur_val, cur_obj, pdir); + while (cur_val > part_val) { + ptptr[1] += ptdelta[1]; + if (ptptr[loidx] > ptptr[hiidx]) + goto done_psort; + cur_obj = &objs[sq_Refs[score_list[ptptr[1]] & SCORE_REFIDX_MASK]]; + get_part_val(cur_val, cur_obj, pdir); + } + + { + int tmpptr = score_list[ptptr[0]]; + score_list[ptptr[0]] = score_list[ptptr[1]]; + score_list[ptptr[1]] = tmpptr; + ptptr[0] += ptdelta[0]; // now move on in + ptptr[1] += ptdelta[1]; + } + } + +done_psort: + + mloc = ptptr[loidx]; + if (tmpstore != -1) { + score_list[hi++] = score_list[mloc]; + score_list[mloc] = tmpstore; + } + + return mloc; +} + +void partition_sort(void) { + if ((partition_cnt > 1) || (partition_type == PRT_BOTH)) + ; // KLC Warning(("Dual partitions\n")); + else { + int mloc; +#ifdef COW_COW + if (cur_obj_num == partition_loc[0]) + sort_section(draw_last_cnt, cur_obj_num - 1); + else if (draw_last_cnt == partition_loc[0]) + sort_section(draw_last_cnt + 1, cur_obj_num); + else +#endif // __FEAR__ the COW COW + { + mloc = do_part_sort(partition_type, draw_last_cnt, cur_obj_num, partition_loc[0]); + sort_section(draw_last_cnt, mloc - 1); + sort_section(mloc + 1, cur_obj_num - 1); + } + } +} + +// go through, do the sort, call show_obj a lot +// for now, just do oscore straight... +void render_sorted_objs(void) { + extern int _fdt_x, _fdt_y; + int i; + partition_cnt = partition_type = draw_last_cnt = 0; + if (cur_obj_num > 1) { + for (i = 0; i < cur_obj_num; i++) + score_objs(i); + if (partition_cnt) + partition_sort(); + else + sort_section(draw_last_cnt, cur_obj_num - 1); + for (i = 0; i < cur_obj_num; i++) + show_obj(sq_Refs[score_list[i] & SCORE_REFIDX_MASK]); + } else if (cur_obj_num == 1) + show_obj(sq_Refs[0]); + cur_obj_num = 0; +} + +void sort_show_obj(ObjID cobjid) { + short objtrip; + int obj_type; + + if (cur_obj_num >= MAX_SORTED_REFS) + return; + if (cobjid == no_render_obj) + return; + objtrip = OPNUM(cobjid); + obj_type = ObjProps[objtrip].render_type; + if (obj_type == FAUBJ_NOOBJ) + return; + + score_list[cur_obj_num] = (obj_type << 24) + objtrip; + sq_Refs[cur_obj_num] = cobjid; + cur_obj_num++; +} diff --git a/engine/src/GameSrc/gamestrn.c b/engine/src/GameSrc/gamestrn.c new file mode 100644 index 0000000..43ed0bc --- /dev/null +++ b/engine/src/GameSrc/gamestrn.c @@ -0,0 +1,119 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/gamestrn.c $ + * $Revision: 1.17 $ + * $Author: xemu $ + * $Date: 1994/09/14 19:35:47 $ + * + */ + +#include + +#include "Shock.h" +#include "gamestrn.h" +#include "criterr.h" +#include "objsim.h" +#include "objprop.h" +#include "cybstrng.h" + +// ------------------- +// FILE NAMES AND VARS +// ------------------- + +#define FILENAME_LEN 14 +#define CONFIG_STRING_RES_FILE "cfgstrn.res" +#define DEFAULT_STRING_RES_FILE "cybstrng.rsrc" +#define STRING_RES_VAR "strings" + +// ------- +// GLOBALS +// ------- + +int string_res_file; // string res filenum + +// --------- +// EXTERNALS +// --------- + +char *language_files[] = {"res/data/cybstrng.res", "res/data/frnstrng.res", "res/data/gerstrng.res"}; +extern char which_lang; + +// Wrapper around RefGet suitable for use by lg_sprintf to get string resources +// for the custom '%S' format specifier. +char *lg_sprintf_string_get(uint32_t ref) +{ + return RefGet(ref); +} + +void init_strings(void) { + // Open the string resource file. + if (which_lang < 0 || which_lang >= sizeof(language_files) / sizeof(*language_files)) + which_lang = 0; + const char *lang_file = language_files[which_lang]; + string_res_file = ResOpenFile(lang_file); + + if (string_res_file < 0) + critical_error(CRITERR_RES | 0); + + //lg_sprintf_install_stringfunc(lg_sprintf_string_get); +} + +char *get_string(int num, char *buf, int bufsize) { + RefTable *table = (RefTable *)ResGet(REFID(num)); + if (!ResInUse(REFID(num)) || !RefIndexValid(table, REFINDEX(num))) { + if (buf != NULL) { + *buf = '\0'; + return buf; + } else + return ""; + } + if (buf != NULL) { + char *s = (char *)RefLock(num); + if (s != NULL) { + strncpy(buf, s, bufsize); + buf[bufsize - 1] = '\0'; + + // printf("Got string %s\n", buf); + } + RefUnlock(num); + return (s == NULL) ? NULL : buf; + } else + return get_temp_string(num); +} + +char *get_temp_string(int num) { return (char *)RefGet(num); } + +char *get_object_short_name(int trip, char *buf, int bufsize) { + return get_string(MKREF(RES_objshortnames, OPTRIP(trip)), buf, bufsize); +} + +char *get_object_long_name(int trip, char *buf, int bufsize) { + return get_string(MKREF(RES_objlongnames, OPTRIP(trip)), buf, bufsize); +} + +void shutdown_strings(void) { ResCloseFile(string_res_file); } + +char *get_texture_name(int abs_texture, char *buf, int bufsiz) { + return get_string(MKREF(RES_texnames, abs_texture), buf, bufsiz); +} + +char *get_texture_use_string(int abs_texture, char *buf, int bufsiz) { + return get_string(MKREF(RES_texuse, abs_texture), buf, bufsiz); +} diff --git a/engine/src/GameSrc/gamesys.c b/engine/src/GameSrc/gamesys.c new file mode 100644 index 0000000..67d5d17 --- /dev/null +++ b/engine/src/GameSrc/gamesys.c @@ -0,0 +1,970 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/gamesys.c $ + * $Revision: 1.128 $ + * $Author: xemu $ + * $Date: 1994/11/28 06:42:41 $ + * + */ + +#include +#include + +#include "ai.h" +#include "cyber.h" +#include "cybstrng.h" +#include "damage.h" +#include "diffq.h" // for time limit +#include "drugs.h" +#include "effect.h" +#include "email.h" +#include "faketime.h" +#include "frflags.h" +#include "frprotox.h" +#include "fullscrn.h" +#include "gameloop.h" +#include "gameobj.h" +#include "gamerend.h" +#include "gamesys.h" +#include "hud.h" +#include "input.h" +#include "lvldata.h" +#include "mainloop.h" +#include "map.h" +#include "mfdfunc.h" +#include "miscqvar.h" +#include "musicai.h" +#include "newmfd.h" +#include "objcrit.h" +#include "objects.h" +#include "objgame.h" +#include "objprop.h" +#include "objsim.h" +#include "objstuff.h" +#include "objuse.h" +#include "otrip.h" +#include "palfx.h" +#include "pathfind.h" +#include "player.h" +#include "physics.h" +#include "rendfx.h" +#include "rendtool.h" +#include "schedule.h" +#include "sfxlist.h" +#include "shodan.h" +#include "tools.h" +#include "tpolys.h" +#include "trigger.h" +#include "visible.h" +#include "wares.h" +#include "weapons.h" +#include "cutsloop.h" + +// ------- +// DEFINES +// ------- +#define SECOND_UPDATE_FREQ APPROX_CIT_CYCLE_HZ +#define HEALTH_RESTORE_PRECISION (APPROX_CIT_CYCLE_SHFT) +#define HEALTH_RESTORE_SHF (6u) +#define HEALTH_RESTORE_UNIT (1u << HEALTH_RESTORE_SHF) // unit time for hp/energy regen +#define HEALTH_RESTORE_MASK (HEALTH_RESTORE_UNIT - 1) +#define sqr(x) ((x) * (x)) +#define MAX_FATIGUE 10000 +#define FATIGUE_WARNING_LEVEL 7000 + +// ------- +// GLOBALS +// ------- +int run_fatigue_rate = 5; +extern short fr_solidfr_time; +extern short fr_sfx_time; +ulong fr_shake_time; +uchar gamesys_on = TRUE; + +// hud vars +short enviro_edrain_rate = 0; +short enviro_absorb_rate = 0; + +LevelData level_gamedata; +Schedule game_seconds_schedule; + +// prototypes +void check_nearby_objects(void); +void fatigue_player(void); +uchar shodan_phase_in(uchar *bitmask, short x, short y, short w, short h, short num, uchar dir); +uchar panel_ref_sanity(ObjID obj); +int apply_rate(int var, int rate, int t0, int t1, int vmin, int vmax); +void do_stuff_every_second(void); +void expose_player_real(short damage, ubyte type, ushort tsecs); + +void game_sched_init(void) { schedule_init(&game_seconds_schedule, GAME_SCHEDULE_SIZE, FALSE); } + +void game_sched_free(void) { + //extern errtype schedule_free(Schedule * s); + schedule_free(&game_seconds_schedule); +} + +short fatigue_accum_rate = 100; + +#define OBJ_CHECK_TICKS CIT_CYCLE +ulong obj_check_time = 0; + +#define MACHINE_LAYER_BASE 25 +#define OBJ_CHECK_RADIUS 7 + +void unshodanizing_callback(ObjID id, intptr_t user_data) { + bool ud = (bool)user_data; + if (ud) { + objBigstuffs[objs[id].specID].data2 = SHODAN_STATIC_MAGIC_COOKIE | (TPOLY_TYPE_CUSTOM_MAT << TPOLY_INDEX_BITS); + objBigstuffs[objs[id].specID].cosmetic_value = 0; + objs[id].info.current_frame = 0; + } else + add_obj_to_animlist(id, FALSE, TRUE, FALSE, 0, 3, TRUE, ANIMCB_REMOVE); +} + +#define STOCHASTIC_SHODAN_MASK 0xF +#define STOCHASTIC_MONSTER_MASK 0xF + +#define STOCHASTIC_SPARKING_MASK 0xFF +#define STOCHASTIC_SPARKING_LEVEL 0x10 +#define SPARKING_RADIUS 5 + +#define STOCHASTIC_FROG_MASK 0xF +#define STOCHASTIC_FROG_LEVEL 0x2 +#define FIRST_GROVE_LEVEL 11 + +#define NEAR_NOISE_RADIUS 5 +#define PERIL_RADIUS 4 + +#define MONSTER_THEME_PATH_LENGTH 12 + +void check_nearby_objects() { + short x, y; + int dist, best_dist; + ObjRefID oref; + int trip; + extern char mlimbs_machine; + extern int mlimbs_monster; + int new_monster; +#ifdef USE_3DREP_FOR_SHODANIZING + short rep; +#endif + ObjID id; + ObjSpecID osid; + LGPoint dest_pt, source_pt; + int pf_id; + extern uchar music_on; + + // Should probably distribute different kinds of checks so that there is not a big hit every OBJ_CHECK_TIME + if (obj_check_time < player_struct.game_time) { + mlimbs_machine = 0; + mlimbs_peril = 0; + best_dist = OBJ_CHECK_RADIUS + 1; + source_pt.x = OBJ_LOC_BIN_X(objs[PLAYER_OBJ].loc); + source_pt.y = OBJ_LOC_BIN_Y(objs[PLAYER_OBJ].loc); + new_monster = -1; + for (x = PLAYER_BIN_X - OBJ_CHECK_RADIUS; x < PLAYER_BIN_X + OBJ_CHECK_RADIUS; x++) { + for (y = PLAYER_BIN_Y - OBJ_CHECK_RADIUS; y < PLAYER_BIN_Y + OBJ_CHECK_RADIUS; y++) { + // Make sure we ain't lookin' off the edge of the map... + if ((x < 0) || (x >= MAP_XSIZE) || (y < 0) || (y >= MAP_YSIZE)) + continue; + + // Hmm, I should probably modularize out the individual kinds of checks.... + oref = me_objref(MAP_GET_XY(x, y)); + while (oref != OBJ_REF_NULL) { + id = objRefs[oref].obj; + dist = long_fast_pyth_dist(x - PLAYER_BIN_X, y - PLAYER_BIN_Y); + if (id != PLAYER_OBJ) { + osid = objs[id].specID; + trip = ID2TRIP(id); + switch (objs[id].obclass) { + case CLASS_CRITTER: + if (music_on) { + if ((new_monster == -1) && (dist < best_dist)) { + // Boy, I wonder how gruesomely slow this is going to be... + dest_pt.x = OBJ_LOC_BIN_X(objs[id].loc); + dest_pt.y = OBJ_LOC_BIN_Y(objs[id].loc); + pf_id = -1; + if (pf_id != -1) { + check_requests(TRUE); + if ((paths[pf_id].num_steps != 0) && + (paths[pf_id].num_steps <= MONSTER_THEME_PATH_LENGTH)) { + delete_path(pf_id); + pf_id = -1; + } + } + // go ahead if we couldn't allocate a path or the path was sufficiently short + if (pf_id == -1) { + switch (ID2TRIP(id)) { + case SERVBOT_TRIPLE: + case REPAIRBOT_TRIPLE: + case REPAIRBOT2_TRIPLE: + case FLIER_TRIPLE: + case AUTOBOMB_TRIPLE: + new_monster = MONSTER_MUSIC_SMALL_ROBOT; + break; + default: + new_monster = objs[id].subclass; + break; + } + best_dist = dist; + } else { + delete_path(pf_id); + pf_id = -1; + } + } + if ((dist < PERIL_RADIUS) && (objCritters[osid].mood != AI_MOOD_FRIENDLY) && + (objCritters[osid].orders != AI_ORDERS_SLEEP)) { + mlimbs_peril = DEFAULT_PERIL_MAX; + } + } + if ((dist < NEAR_NOISE_RADIUS) && (CritterProps[CPTRIP(trip)].near_sound != 255) && + (objCritters[osid].mood != AI_MOOD_ATTACKING) && + (get_crit_posture(osid) != DEATH_CRITTER_POSTURE) && + ((rand() & STOCHASTIC_MONSTER_MASK) == 1)) + play_digi_fx_obj(CritterProps[CPTRIP(trip)].near_sound, 1, objCritters[osid].id); + break; + } + switch (trip) { + case MUSIC_MARK_TRIPLE: + // Hm, should we have this use comparator_check + if (player_struct.level >= FIRST_GROVE_LEVEL) { + if ((rand() & STOCHASTIC_FROG_MASK) < STOCHASTIC_FROG_LEVEL) + play_digi_fx_obj(SFX_GROVE_1, 1, id); + } else + mlimbs_machine = MACHINE_LAYER_BASE + objTraps[objs[id].specID].p1; + break; + case SPARK_CABLE_TRIPLE: + if ((dist < SPARKING_RADIUS) && + ((rand() & STOCHASTIC_SPARKING_MASK) < STOCHASTIC_SPARKING_LEVEL)) + play_digi_fx_obj(SFX_SPARKING_CABLE, 1, id); + break; + case HORZ_KLAXON_TRIPLE: { + // uchar digi_fx_playing(int fx_id, int *handle_ptr); + if (!digi_fx_playing(SFX_KLAXON, NULL)) + play_digi_fx_obj(SFX_KLAXON, 1, id); + } break; + case TV_TRIPLE: + case MONITOR2_TRIPLE: + case SCREEN_TRIPLE: + case BIGSCREEN_TRIPLE: + case SUPERSCREEN_TRIPLE: +#ifdef USE_3DREP_FOR_SHODANIZING + rep = compute_3drep(&(objs[id]), id, ObjProps[OPNUM(id)].render_type); + if ((rep & TPOLY_INDEX_MASK) == SHODAN_STATIC_MAGIC_COOKIE) +#else + if (((objBigstuffs[objs[id].specID].data2 & TPOLY_INDEX_MASK) == + SHODAN_STATIC_MAGIC_COOKIE) && + (((objBigstuffs[objs[id].specID].data2 & TPOLY_TYPE_MASK) >> TPOLY_INDEX_BITS) == + TPOLY_TYPE_CUSTOM_MAT)) +#endif + { + // Chance of shodanizing.... + if ((rand() & STOCHASTIC_SHODAN_MASK) == 1) { + objBigstuffs[objs[id].specID].data2 = FIRST_SHODAN_ANIM; + objBigstuffs[objs[id].specID].cosmetic_value = NUM_SHODAN_FRAMES; + objs[id].info.current_frame = 0; + // animate me: cycle, but don't repeat + add_obj_to_animlist(id, FALSE, FALSE, FALSE, 0, 3, 0, ANIMCB_REMOVE); + } + } + break; + default: + ; + } + } + oref = objRefs[oref].next; + } + } + } + mlimbs_monster = new_monster; + obj_check_time = player_struct.game_time + OBJ_CHECK_TICKS; + } +} + +/* +#define CFG_FATIGUE_VAR "fatigue" +extern ubyte fatigue_threshold; +void reload_fatigue_parms() +{ + int i; + int vec[5]; + i = 5; +// player_struct.fatigue_regen = DEFAULT_FATIGUE_REGEN; + if (config_get_value(CFG_FATIGUE_VAR,CONFIG_INT_TYPE,vec,&i)) + { + switch (i) + { + case 5: + fatigue_accum_rate = vec[4]; + case 4: + player_struct.fatigue_regen_max = vec[3]; + case 3: + player_struct.fatigue_regen_base = vec[2]; + case 2: + fatigue_threshold = vec[1]; + case 1: + run_fatigue_rate = vec[0]; + default: + break; + } + } + +} +*/ + +uchar fatigue_warning; +#define fatigue_val(x) (((x) > SPRINT_CONTROL_THRESHOLD) ? ((int)(x)-SPRINT_CONTROL_THRESHOLD) : 0) +#define FATIGUE_DENOM (CONTROL_MAX_VAL - SPRINT_CONTROL_THRESHOLD) +uchar gamesys_fatigue = TRUE; + +#define SKATE_MOD 8 + +void fatigue_player(void) { + byte *c = player_struct.controls; + int deltat, deltaf; + extern uchar jumpjets_active; + if (gamesys_fatigue && !jumpjets_active && !EDMS_pelvis_is_climbing()) { + deltat = player_struct.deltat; + deltaf = run_fatigue_rate * (fatigue_val(c[CONTROL_YVEL]) + fatigue_val(2 * c[CONTROL_ZVEL]) + + // fatigue_val(c[CONTROL_XVEL])/64 + + // fatigue_val(c[CONTROL_XYROT])/256 + + // fatigue_val(c[CONTROL_XZROT])/256 + + // fatigue_val(c[CONTROL_YZROT])/256 + + 0); + if (player_struct.posture != POSTURE_STAND) + deltaf /= sqr(player_struct.posture + 1); + if (motionware_mode == MOTION_SKATES) + deltaf /= SKATE_MOD; + player_struct.fatigue += deltaf * deltat / FATIGUE_DENOM + player_struct.fatigue_spend * deltat; + if (player_struct.fatigue > MAX_FATIGUE) + player_struct.fatigue = MAX_FATIGUE; + if (player_struct.drug_status[DRUG_STAMINUP] <= 0 && player_struct.fatigue > FATIGUE_WARNING_LEVEL) { + if (!fatigue_warning) { + hud_set(HUD_FATIGUE); + fatigue_warning = TRUE; + } + } else if (fatigue_warning) { + hud_unset(HUD_FATIGUE); + fatigue_warning = FALSE; + } + } +} + +uchar gamesys_render_fx = TRUE; +uchar gamesys_restore_health = TRUE; +uchar gamesys_slow_proj = TRUE; +uchar gamesys_beam_wpns = TRUE; +uchar gamesys_drugs = TRUE; + +ulong next_contin_trig; + +#define NUM_CONTIN_SECONDS 5 +#define CONTIN_INTERVAL CIT_CYCLE *NUM_CONTIN_SECONDS + +short fr_surge_time = 0; +char surg_fx_frame = 0; +short surge_duration = 60; + +#define NUM_SURG_FX_FRAMES 7 +// WH: Unportable, replaced with actual values +// short surge_vals[NUM_SURG_FX_FRAMES] = {-1 << 8, -5 << 8, 0 << 8, 2 << 8, 2 << 8, 1 << 8, 1 << 8}; +short surge_vals[NUM_SURG_FX_FRAMES] = {-256, -1280, 0, 512, 512, 256, 256}; + +#define CONQUER_THRESHOLD 512 +#define UNCONQUER_THRESHOLD 32 +#define MAX_SHODAN_FAILURES 10 +char thresh_fail = 0; +uchar shodan_phase_in(uchar *bitmask, short x, short y, short w, short h, short num, uchar dir) { + int i = 0, nx, ny, val, oval; + while (i < num) { + nx = rand() % w; + ny = rand() % h; + val = x + (y * FULL_VIEW_WIDTH); + val += (nx + (ny * FULL_VIEW_WIDTH)); + oval = val; + + if (dir) { + while (SHODAN_CONQUER_GET(bitmask, val) && (val < SHODAN_BITMASK_SIZE) && (val - oval <= CONQUER_THRESHOLD)) + val++; + if (val < SHODAN_BITMASK_SIZE) + SHODAN_CONQUER_SET(bitmask, val); + if (val - oval > CONQUER_THRESHOLD) { + i = num; + thresh_fail++; + } + } else { + while (!SHODAN_CONQUER_GET(bitmask, val) && (val < SHODAN_BITMASK_SIZE) && + (val - oval <= UNCONQUER_THRESHOLD)) + val++; + if (val < SHODAN_BITMASK_SIZE) + SHODAN_CONQUER_UNSET(bitmask, val); + if (val - oval > UNCONQUER_THRESHOLD) + i = num; + } + i++; + } + if (thresh_fail > MAX_SHODAN_FAILURES) { + return (TRUE); + } + return (FALSE); +} + +#define NUM_SHODAN_REGIONS 4 +short shodan_region_full_x[] = {0, 0, FULL_VIEW_WIDTH * 7 / 8, 0}; +short shodan_region_full_y[] = {0, 0, 0, FULL_VIEW_HEIGHT * 7 / 8}; +short shodan_region_full_width[] = {FULL_VIEW_WIDTH, FULL_VIEW_WIDTH / 8, FULL_VIEW_WIDTH / 8, FULL_VIEW_WIDTH}; +short shodan_region_full_height[] = {FULL_VIEW_HEIGHT / 8, FULL_VIEW_HEIGHT, FULL_VIEW_HEIGHT, FULL_VIEW_HEIGHT / 8}; + +// stolen from trigger.c +#define GAME_OVER_HACK 0x6 + +#define KEY_CODE_ESC 0x1b + +#define DETECT_AUDIOLOG_QVAR_CHANGE + +errtype gamesys_run(void) { + ObjSpecID osi; + uchar dummy; + extern uchar *shodan_bitmask; + +#ifdef AUTOCORRECT_DIFF_TRASH + for (int i = 0; i < 4; i++) { + extern char diff_qvars[4]; + if (player_struct.difficulty[i] != QUESTVAR_GET(diff_qvars[i])) + QUESTVAR_SET(diff_qvars[i], player_struct.difficulty[i]); + } +#endif + + // page_amount = 0; + + if (!gamesys_on) + return (OK); + + if (gamesys_render_fx) { + if (fr_solidfr_time > 0) { + fr_solidfr_time -= player_struct.deltat; + if (fr_solidfr_time <= 0) + fr_global_mod_flag(0, FR_SOLIDFR_MASK); + } + + if (fr_sfx_time > 0) { + fr_sfx_time -= player_struct.deltat; + if (fr_sfx_time <= 0) + + { + fr_global_mod_flag(0, FR_SFX_MASK); + } + } + if (fr_surge_time > 0) { + fr_surge_time -= player_struct.deltat; + if (fr_surge_time <= 0) { + if (surg_fx_frame >= NUM_SURG_FX_FRAMES) + fr_surge_time = 0; + else { + fr_surge_time = surge_duration; + set_global_lighting(surge_vals[surg_fx_frame]); + } + surg_fx_frame++; + } + } + } + + if (shodan_bitmask != NULL) { + if (player_struct.game_time > time_until_shodan_avatar) { + char i; + if (thresh_fail) { + errtype trap_hack_func(int p1, int p2, int p3, int p4); + begin_shodan_conquer_fx(FALSE); + shodan_bitmask = NULL; + trap_hack_func(GAME_OVER_HACK, 0, 0, 0); + palfx_fade_down(); + } else { + for (i = 0; i < NUM_SHODAN_REGIONS; i++) { + shodan_phase_in(shodan_bitmask, shodan_region_full_x[i], shodan_region_full_y[i], + shodan_region_full_width[i], shodan_region_full_height[i], + QUESTVAR_GET(CYBER_DIFF_QVAR) + 1, TRUE); + shodan_phase_in(shodan_bitmask, 0, 0, FULL_VIEW_WIDTH, FULL_VIEW_HEIGHT, + (3 * QUESTVAR_GET(CYBER_DIFF_QVAR)) + 1, TRUE); + } + } + if (thresh_fail) { + // extern errtype mai_player_death(); + mai_player_death(); + time_until_shodan_avatar = player_struct.game_time + (CIT_CYCLE * 8); + } else + time_until_shodan_avatar = player_struct.game_time + SHODAN_INTERVAL; + } + } + + // update fatigue + if (!global_fullmap->cyber) { + + fatigue_player(); + // update drug effects + if (gamesys_drugs) + drugs_update(); + + // cool off all beam weapons + if (gamesys_beam_wpns) + cool_off_beam_weapons(); + } + + do_stuff_every_second(); + + check_nearby_objects(); + + // destroy old slow projectiles + if (gamesys_slow_proj) { + osi = objPhysicss[0].id; + while (osi != OBJ_SPEC_NULL) { + if (objPhysicss[osi].duration < player_struct.game_time) { + ADD_DESTROYED_OBJECT(objPhysicss[osi].id); + } + osi = objPhysicss[osi].next; + } + destroy_destroyed_objects(); + } + + // Run continuous triggers + if (player_struct.game_time > next_contin_trig) { + osi = objTraps[0].id; + while (osi != OBJ_SPEC_NULL) { + if (ID2TRIP(objTraps[osi].id) == CONTIN_TRIG_TRIPLE) + trap_activate(objTraps[osi].id, &dummy); + osi = objTraps[osi].next; + } + next_contin_trig = player_struct.game_time + CONTIN_INTERVAL; + } + + return (OK); +} + +// ---------------------------------------- +// check_hazard_regions() +// +// Checks to see if we're in a bio/radiation zone + +void check_hazard_regions(MapElem *newElem) { + fix hdiff = fix_from_obj_height(PLAYER_OBJ) - fix_from_map_height(me_height_flr(newElem)); + if (me_hazard_rad(newElem) > 0 && hdiff <= fix_make(level_gamedata.hazard.rad_h, 0) / 8) { + short exp = (short)me_hazard_rad(newElem) * (short)level_gamedata.hazard.rad; + exp -= (short)player_struct.hit_points_lost[RADIATION_TYPE - 1]; + if (exp > 0) { + expose_player_real(exp, RADIATION_TYPE, 0); + } + + hud_set(HUD_RADIATION); + if (rand() % 0xFF < 0x80) + play_digi_fx(SFX_RADIATION, 1); + } else + hud_unset(HUD_RADIATION); + + if (!level_gamedata.hazard.zerogbio) { + if (me_hazard_bio(newElem) > 0 && hdiff <= fix_make(level_gamedata.hazard.bio_h, 0) / 8) { + short exp = me_hazard_bio(newElem) * level_gamedata.hazard.bio; + exp -= player_struct.hit_points_lost[BIO_TYPE - 1]; + if (exp > 0) + expose_player_real(exp, BIO_TYPE, 0); + hud_set(HUD_BIOHAZARD); + } else + hud_unset(HUD_BIOHAZARD); + } else { + if (me_hazard_bio(newElem)) + hud_set(HUD_ZEROGRAV); + else + hud_unset(HUD_ZEROGRAV); + } + if ((player_struct.hud_modes & (HUD_RADIATION | HUD_BIOHAZARD | HUD_ENVIROUSE)) == 0) { + enviro_edrain_rate = 0; + } +} + +#define Z_THRESHOLD FIX_UNIT + +uchar panel_ref_sanity(ObjID obj) { + int objtrip = OPNUM(obj), obj_type; + obj_type = ObjProps[objtrip].render_type; + if ((obj_type == FAUBJ_TPOLY) || (obj_type == FAUBJ_TEXBITMAP)) + if (objs[obj].obclass == CLASS_FIXTURE) { + fixang obj_to_p, objh, delt; + fix dy, dx; + + objh = fixang_from_phys_angle(phys_angle_from_obj(objs[obj].loc.h)); + dy = fix_from_obj_coord(objs[PLAYER_OBJ].loc.y) - fix_from_obj_coord(objs[obj].loc.y); + dx = fix_from_obj_coord(objs[PLAYER_OBJ].loc.x) - fix_from_obj_coord(objs[obj].loc.x); + + // x and y swapped here to transform coordinate system + obj_to_p = fix_atan2(dx, dy); + delt = (objh - FIXANG_PI / 2) - obj_to_p; + + // mprintf("Called with %d, locs %d %d and %d %d, got delt %x from %x and %x\n", + // obj,objs[PLAYER_OBJ].loc.x,objs[PLAYER_OBJ].loc.y,objs[obj].loc.x,objs[obj].loc.y,delt,objh,obj_to_p); + + // hmmm? + if (delt > FIXANG_PI) + return FALSE; + } + return TRUE; +} + +// ------------------------------------- +// check_panel_ref +// +// Checks to see if we've walked away from a panel +// in an mfd, and closes the mfd. + +void check_panel_ref(uchar puntme) { + // static short old_x, old_y; + // extern void restore_mfd_slot(int mfd_id); + + ObjID id = player_struct.panel_ref; + + if (id != OBJ_NULL && (id != PLAYER_OBJ || puntme)) { + uchar punt = puntme; + if (objs[id].active) { + punt = punt || !check_object_dist(id, PLAYER_OBJ, MAX_USE_DIST); + punt = punt || !panel_ref_sanity(id); + } + if (punt) { + uchar punt_mfd[NUM_MFDS], punting = 0; + uint8_t mfd_id; + + for (mfd_id = 0; mfd_id < NUM_MFDS; mfd_id++) { + punt_mfd[mfd_id] = mfd_distance_remove(mfd_get_func(mfd_id, MFD_INFO_SLOT)); + punting = punting || punt_mfd[mfd_id]; + } + if (punting) { + mfd_notify_func(MFD_EMPTY_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, TRUE); + } + for (mfd_id = 0; mfd_id < NUM_MFDS; mfd_id++) { + if (punt_mfd[mfd_id] && player_struct.mfd_current_slots[mfd_id] == MFD_INFO_SLOT) { + restore_mfd_slot(mfd_id); + } + } + player_struct.panel_ref = OBJ_NULL; + } + } + // old_x = PLAYER_BIN_X; + // old_y = PLAYER_BIN_Y; +} + +// ================================================= +// do_stuff_every_second() +// +// deals with game system stuff that gets done every nerd second. +// (A nerd second is 256 clock ticks. A nerd minute is 64 nerd seconds) + +// 1 nerd minute = 64*256/(60*280) = 2*256/(15*35) = 512/525 minutes. + +// Rates at which exposure levels degrade by type, in units of +// percent per nerd minute, compounded every nerd second. + +uchar exposure_degrade_rates[] = { + 100, // EXPLOSION_TYPE + 100, // ENERGY_BEAM_TYPE + 100, // MAGNETIC_TYPE + 100, // RADIATION_TYPE + 100, // + 100, // ARMOR_PIERCING_TYPE + 100, // NEEDLE_TYPE + 100, // BIO_TYPE +}; + +// Integrates the function f(t) = num >> denom_shf from t0 to t1, +// dealing with roundoff in a graceful way so that +// integrating over lots of small intervals roughly equals +// the large interval. +#define find_delta(t0, t1, num, denom_shf) ((((t1) * (num)) >> (denom_shf)) - (((t0) * (num)) >> (denom_shf))) + +// computes the change to var by the specified rate as accrued over the +// time from t0 to t1, clamped by [vmin,vmax]. +// returns the new value of var. + +// t0 and t1 are times in nerd seconds, rate is in units per minute. + +int apply_rate(int var, int rate, int t0, int t1, int vmin, int vmax) { + int delta = find_delta(t0, t1, rate, HEALTH_RESTORE_SHF); + int final = lg_min(vmax, lg_max(var + delta, vmin)); + return final; +} + +#define ENERGY_VAR_RATE 50 + +extern ObjID shodan_avatar_id; + +#define MY_FORALLOBJSPECS(pmo, objspec) \ + for (pmo = (objspec[OBJ_SPEC_NULL]).id; pmo != OBJ_SPEC_NULL; pmo = objspec[pmo].next) + +#define REACTOR_BOOM_QB 0x14 // stolen from trigger.c +#define BRIDGE_SEPARATED_QB 0x98 + +#define REALSPACE_HUDS \ + (HUD_RADPOISON | HUD_BIOPOISON | HUD_FATIGUE | HUD_BIOHAZARD | HUD_RADIATION | HUD_ZEROGRAV | HUD_ENVIROUSE) + +void do_stuff_every_second() { + long running_dt = player_struct.game_time - player_struct.last_second_update; + extern int bio_energy_var; + extern int bio_absorb; + extern int rad_absorb; + int last = (player_struct.last_second_update >> HEALTH_RESTORE_PRECISION) & HEALTH_RESTORE_MASK; + int next = (player_struct.game_time >> HEALTH_RESTORE_PRECISION) & HEALTH_RESTORE_MASK; + + if (running_dt > SECOND_UPDATE_FREQ) { + if (global_fullmap->cyber) { + char i; + ObjSpecID osid; + ObjID new_id, shrine_obj = OBJ_NULL; + extern uchar *shodan_bitmask; + + for (i = 0; i < NUM_CS_EFFECTS; i++) + if ((cspace_effect_times[i] != 0) && (cspace_effect_times[i] <= player_struct.game_time)) { + cspace_effect_times[i] = 0; + if (cspace_effect_turnoff[i] != NULL) + cspace_effect_turnoff[i](TRUE, TRUE); + } + + if ((time_until_shodan_avatar != 0) && (player_struct.game_time > time_until_shodan_avatar) && + (shodan_avatar_id == OBJ_NULL) && (shodan_bitmask == NULL)) { + time_until_shodan_avatar = 0; + MY_FORALLOBJSPECS(osid, objSmallstuffs) { + if (ID2TRIP(objSmallstuffs[osid].id) == SHODO_SHRINE_TRIPLE) { + shrine_obj = objSmallstuffs[osid].id; + } + } + if (shrine_obj != OBJ_NULL) { + LGPoint sq; + sq.x = OBJ_LOC_BIN_X(objs[shrine_obj].loc); + sq.y = OBJ_LOC_BIN_Y(objs[shrine_obj].loc); + new_id = object_place(CYBER_SHODAN_TRIPLE, sq); + objCritters[objs[new_id].specID].mood = AI_MOOD_HOSTILE; + shodan_avatar_id = new_id; + } + } + hud_unset(REALSPACE_HUDS); + } else { + if ((QUESTBIT_GET(REACTOR_BOOM_QB)) && (!QUESTBIT_GET(BRIDGE_SEPARATED_QB)) && ((rand() & 0x3F) == 1)) { + play_digi_fx(SFX_RUMBLE, 2); + fr_global_mod_flag(FR_SFX_SHAKE, FR_SFX_MASK); + fr_sfx_time = CIT_CYCLE * 5; // 2 seconds of shake + } + if (next < last) + next += HEALTH_RESTORE_UNIT; + if (player_struct.energy_regen + player_struct.energy_spend != 0) { + int num = player_struct.energy_regen - player_struct.energy_spend; + if (num != 0) { + int finale = apply_rate(player_struct.energy, num, last, next, 0, MAX_ENERGY); + bio_energy_var -= ENERGY_VAR_RATE * (finale - player_struct.energy); + player_struct.energy = (ubyte)finale; + chg_set_flg(VITALS_UPDATE); + } + } + if (player_struct.energy == 0) { + if (!player_struct.energy_out) { + string_message_info(REF_STR_PowerRanOut); + play_digi_fx(SFX_POWER_OUT, 1); + player_struct.energy_out = TRUE; + } + hardware_power_outage(); + gear_power_outage(); + } else + player_struct.energy_out = FALSE; + if (player_struct.hit_points < PLAYER_MAX_HP && player_struct.hit_points_regen != 0) { + + int num = player_struct.hit_points_regen; + player_struct.hit_points = apply_rate(player_struct.hit_points, num, last, next, 0, PLAYER_MAX_HP); + mfd_notify_func(MFD_BIOWARE_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); + } + if (player_struct.hit_points > 0) { + int i; + for (i = 0; i < NUM_DAMAGE_TYPES; i++) + if (player_struct.hit_points_lost[i] > 0) { + int num = player_struct.hit_points_lost[i]; + int degrade = exposure_degrade_rates[i]; + int deltahp = player_struct.hit_points - + apply_rate(player_struct.hit_points, -num, last, next, 0, PLAYER_MAX_HP); + degrade = lg_min(100, find_delta(last, next, degrade, HEALTH_RESTORE_SHF)); + damage_player((ubyte)deltahp, i + 1, NO_SHIELD_ABSORBTION); + player_struct.hit_points_lost[i] = num * (100 - degrade) / 100; + } + if (player_struct.hit_points_lost[RADIATION_TYPE - 1] > 0) + hud_set(HUD_RADPOISON); + else + hud_unset(HUD_RADPOISON); + if (player_struct.hit_points_lost[BIO_TYPE - 1] > 0) + hud_set(HUD_BIOPOISON); + else + hud_unset(HUD_BIOPOISON); + } + update_email_ware(); + } + if (player_struct.fatigue > 0 && player_struct.controls[CONTROL_YVEL] <= SPRINT_CONTROL_THRESHOLD && + !EDMS_pelvis_is_climbing()) { + int newf = player_struct.fatigue - player_struct.fatigue_regen; + player_struct.fatigue_regen += fatigue_accum_rate; + if (player_struct.fatigue_regen > player_struct.fatigue_regen_max) + player_struct.fatigue_regen = player_struct.fatigue_regen_max; + if (newf <= 0) { + newf = 0; + } + if (newf != player_struct.fatigue) { + mfd_notify_func(MFD_BIOWARE_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); + player_struct.fatigue = newf; + } + } else + player_struct.fatigue_regen = player_struct.fatigue_regen_base; + + if (QUESTVAR_GET(MISSION_DIFF_QVAR) == 3) { + int remain = MISSION_3_TICKS - player_struct.game_time; + + if ((remain / CIT_CYCLE) <= 30) + secret_render_fx = TIMELIMIT_REND_SFX; + + if (remain < CIT_CYCLE) { + secret_render_fx = 0; + play_cutscene(ENDGAME_CUTSCENE,TRUE); + + // gDeadPlayerQuit = TRUE; // Pretend the player is dead. + // gPlayingGame = FALSE; // Hop out of the game loop. + } + } + + player_struct.last_second_update = player_struct.game_time; + + // what is this code trying to do? ie. r_a -= (0-r_a)-r_a/4; looks like it can increase or decrease?, end <0, so + // on + if (rad_absorb > 0) + rad_absorb -= rand() % (rad_absorb)-rad_absorb / 4; + else + rad_absorb = 0; + if (bio_absorb > 0) + bio_absorb -= rand() % (bio_absorb)-bio_absorb / 4; + else + bio_absorb = 0; + + check_hazard_regions(MAP_GET_XY(PLAYER_BIN_X, PLAYER_BIN_Y)); + check_panel_ref(FALSE); + } +} + + // --------------------------------------------- + // Expose_player exposes the player to damage of a specified type. + // if tsecs is non-zero, it is the amount of time in which the damage will go away. + + // TSECS IS NO LONGER SUPPORTED, ALL EXPOSURE HAS A BUILT-IN DECAY RATE. + +#define MAX_UBYTE 0xFF + +int enviro_suit_absorb(int damage, int exposure, ubyte dtype); + +void expose_player_real(short damage, ubyte type, ushort tsecs) { + int cval = player_struct.hit_points_lost[type - 1]; + if (damage == 0) + return; + damage = lg_max(-cval, lg_min(damage, MAX_UBYTE - cval)); + if (damage > 0 && (type == BIO_TYPE || type == RADIATION_TYPE)) { + damage = enviro_suit_absorb(damage, cval, type); + } + cval += damage; + player_struct.hit_points_lost[type - 1] = (ubyte)cval; +#ifdef SCHEDULED_DECAY + if (tsecs > 0) { + SchedEvent ev; + SchedExposeData *xd = (SchedExposeData *)&ev.data; + int count = 1; + ev.timestamp = TICKS2TSTAMP(player_struct.game_time + tsecs * CIT_CYCLE); + ev.type = EXPOSE_SCHED_EVENT; + xd->damage = -(damage / count); // plus or minus exposure increment + xd->type = type; + xd->tsecs = tsecs; + xd->count = count; + schedule_event(&game_seconds_schedule, &ev); + } +#endif // SCHEDULED_DECAY +} + +void expose_player(byte damage, ubyte type, ushort tsecs) { expose_player_real(damage, type, tsecs); } + +//------------------------------------------------------- +// enviro_suit_absorb() +// +// returns damage to player after enviro-suit + +#define ENVIRO_ABSORB_DENOM 5 +#define ENVIRO_DRAIN_DENOM 1 +#define ENVIRO_DRAIN_RATE 32 + +// biorhythm vars +int bio_absorb = 0; +int rad_absorb = 0; + +int enviro_suit_absorb(int damage, int exposure, ubyte dtype) { + short drain; + short absorb; + short denom; + short energy; + short old_edrain_rate = enviro_edrain_rate; + ubyte version = player_struct.hardwarez[CPTRIP(ENV_HARD_TRIPLE)]; + + if (dtype == RADIATION_TYPE && version > 0) + version--; + if (version == 0) + return damage; + + // Absorb all but 1/nth of damage + denom = ENVIRO_ABSORB_DENOM + version; + absorb = ((damage + exposure) * (denom - 1)) / denom; + if (absorb == 0) + return damage; + + // Compute drain for that absorption amount. + denom = ENVIRO_DRAIN_DENOM + version + 1; + enviro_edrain_rate = exposure * 60 / (ENVIRO_DRAIN_RATE * denom); + drain = (rand() % 60 + enviro_edrain_rate) / 60; + + // drain energy + energy = drain_energy(drain); + // did we have enough? + if (energy < drain) { + // if not, recompute absorption. + absorb = energy * denom / (denom - 1); + } + enviro_absorb_rate = lg_min(damage, absorb) >> 1; + damage -= lg_min(damage, absorb); + switch (dtype) { + case BIO_TYPE: + bio_absorb = 8 + long_sqrt((int)damage); + break; + case RADIATION_TYPE: + rad_absorb = 8 + long_sqrt((int)damage); + break; + default: + break; + } + if (enviro_absorb_rate > 0) { + if (old_edrain_rate == 0) { + uint32_t time = 5u << APPROX_CIT_CYCLE_SHFT; + hud_set_time(HUD_ENVIROUSE, time); + hud_set_time(HUD_ENERGYUSE, time); + } + } else + hud_unset(HUD_ENVIROUSE); + return (byte)damage; +} diff --git a/engine/src/GameSrc/gametime.c b/engine/src/GameSrc/gametime.c new file mode 100644 index 0000000..3d79a47 --- /dev/null +++ b/engine/src/GameSrc/gametime.c @@ -0,0 +1,107 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/gametime.c $ + * $Revision: 1.16 $ + * $Author: minman $ + * $Date: 1994/09/11 07:42:01 $ + * + */ + +#include + +#include "gametime.h" +#include "faketime.h" +#include "player.h" +#include "schedule.h" +#include "drugs.h" +#include "lvldata.h" +#include "dirac.h" +#include "framer8.h" + +ulong last_real_time = 0; +char reflex_remainder = 0; + +long gNewShockTicks; // whyyyyyy + +#define MAX_DELTAT (CIT_CYCLE / MIN_FRAME_RATE) +#define MIN_DELTAT (CIT_CYCLE / MAX_FRAME_RATE) + +errtype update_state(uchar time_running) { + uchar update = TRUE; + if (time_running) { + ulong deltat; + if (player_struct.drug_status[DRUG_REFLEX] > 0 && !global_fullmap->cyber) { + // So we effectively downshift deltat by 2 to divide by 4 + // we keep the remainder around so that we don't screw up the universe badly + deltat = *tmd_ticks + reflex_remainder - last_real_time; + reflex_remainder = deltat & 0x3; + deltat = deltat >> 2; + } else + deltat = *tmd_ticks - last_real_time; + + if (deltat > MAX_DELTAT) + deltat = MAX_DELTAT; + if (deltat < MIN_DELTAT) { + deltat = 0; + update = FALSE; + } + + // update game time. + player_struct.deltat = deltat; + player_struct.game_time += deltat; + } + if (update) + last_real_time = *tmd_ticks; + + run_schedules(); + + return (OK); +} + +// static ulong time_at_suspend = 0; + +void suspend_game_time(void) { + // if (time_at_suspend == 0) + // time_at_suspend = *tmd_ticks; +} + +void resume_game_time(void) { + // last_real_time += *tmd_ticks - time_at_suspend; + last_real_time = *tmd_ticks; + // time_at_suspend = 0; +} + +#define MAX_PHYSICS_RUNTIME fix_make(10, 0) + +void update_level_gametime(void) { + ulong oldtime = level_gamedata.exit_time; + ulong deltat = player_struct.game_time - oldtime; + run_schedules(); + // run ai's + // KLC ai_time_passes(&deltat); + // KLC ai_freeze_tag(); + // run physics + if (global_fullmap->cyber) { + EDMS_control_Dirac_frame(PLAYER_PHYSICS, 0, 0, 0, 0); + } else { + EDMS_control_pelvis(PLAYER_PHYSICS, 0, 0, 0, 0, 0, 0); + } + EDMS_soliton_vector(lg_min(MAX_PHYSICS_RUNTIME, deltat / CIT_CYCLE)); +} diff --git a/engine/src/GameSrc/gamewrap.c b/engine/src/GameSrc/gamewrap.c new file mode 100644 index 0000000..7b5a7f5 --- /dev/null +++ b/engine/src/GameSrc/gamewrap.c @@ -0,0 +1,529 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/gamewrap.c $ + * $Revision: 1.101 $ + * $Author: xemu $ + * $Date: 1994/11/26 03:36:36 $ + */ + +#include + +#include "Shock.h" + +#include "amap.h" +#include "archiveformat.h" +#include "criterr.h" +#include "cybmem.h" +#include "drugs.h" +#include "dynmem.h" +#include "faketime.h" +#include "frprotox.h" +#include "gamewrap.h" +#include "hud.h" +#include "invent.h" +#include "invpages.h" +#include "leanmetr.h" +#include "mainloop.h" +#include "miscqvar.h" +#include "musicai.h" // to tweak music upon startup +#include "newmfd.h" +#include "objects.h" +#include "objload.h" +#include "objsim.h" +#include "olhext.h" +#include "player.h" +#include "saveload.h" +#include "schedule.h" +#include "setup.h" +#include "shodan.h" +#include "sideicon.h" +#include "status.h" +#include "tools.h" +#include "trigger.h" +#include "wares.h" +#include "wrapper.h" + +#include "otrip.h" + +#include + +#define SCHEDULE_BASE_ID 590 + +extern long old_ticks; +extern char saveload_string[30]; +extern uchar display_saveload_checkpoints; +extern ulong obj_check_time; +extern uchar mlimbs_on; + +// Player struct support for savegames. +// DOS version savegame reserves 32 bytes for puzzle state. +#define PL_MFD_PUZZLE_SIZE 32 +#include "playerlayout.h" +#undef PL_MFD_PUZZLE_SIZE +// Enhanced edition uses 64. +#define PL_MFD_PUZZLE_SIZE 64 +#include "playerlayout.h" +#undef PL_MFD_PUZZLE_SIZE + +const ResLayout *PlayerLayouts[] = { &PlayerLayout_M32, &PlayerLayout_M64 }; +// Decode wrapper for a player layout. Tries to figure out which version saved +// the game from the resource size. +void *decode_player(void *raw, size_t *size, UserDecodeData layout) { + int i; + for (i = 0; i < sizeof PlayerLayouts / sizeof *PlayerLayouts; ++i) { + if (*size == PlayerLayouts[i]->dsize) { + return ResDecode(raw, size, (UserDecodeData)PlayerLayouts[i]); + } + } + ERROR("Could not determine format of saved player!"); + return NULL; +} +// Player format. We always save as enhanced format (64-byte MFD array). +const ResourceFormat PlayerFormat = { + decode_player, ResEncode, (UserDecodeData)&PlayerLayout_M64, NULL }; +#define FORMAT_PLAYER (&PlayerFormat) + +//------------------- +// INTERNAL PROTOTYPES +//------------------- +errtype load_game_schedules(void); +errtype interpret_qvars(void); + +#define OldResIdFromLevel(level) (OLD_SAVE_GAME_ID_BASE + (level * 2) + 2) + +errtype copy_file(char *src_fname, char *dest_fname) { + FILE *fsrc, *fdst; + DEBUG("copy_file: %s to %s", src_fname, dest_fname); + + fsrc = fopen_caseless(src_fname, "rb"); + if (fsrc == NULL) { + return ERR_FOPEN; + } + + fdst = fopen_caseless(dest_fname, "wb"); + if (fdst == NULL) { + return ERR_FOPEN; + } + + int b; + while ((b = fgetc(fsrc)) != EOF) { + fputc(b, fdst); + } + + fclose(fsrc); + fclose(fdst); + + return OK; +} + +void closedown_game(uchar visible) { + // clear any transient hud settings + hud_shutdown_lines(); + drug_closedown(visible); + hardware_closedown(visible); + musicai_clear(); + clear_digi_fx(); + olh_closedown(); + fr_closedown(); + if (visible) + reset_schedules(); +} + +void startup_game(uchar visible) { + drug_startup(visible); + hardware_startup(visible); + if (visible) { + mfd_force_update(); + side_icon_expose_all(); + status_vitals_update(TRUE); + inventory_page = 0; + inv_last_page = INV_BLANK_PAGE; + } +} + +#ifdef NOT_YET + +void check_save_game_wackiness(void) { + // for now, the only thing we have heard of is a bridge in general inventory + // so lets make sure geninv has only geninvable stuff + int i; + ObjID cur_test; + for (i = 0; i < NUM_GENERAL_SLOTS; i++) { + cur_test = player_struct.inventory[i]; +#ifdef USELESS_OBJECT_CHECK + if (cur_test != OBJ_NULL) { + if ((ObjProps[OPNUM(cur_test)].flags & INVENTORY_GENERAL) == 0) + Warning(("You have obj %d a %d as the %d element of geninv, BADNESS\n", cur_test, OPNUM(cur_test), i)); + // else + // Warning(("You have obj %d a %d as the %d element of geninv ok + // %x\n",cur_test,OPNUM(cur_test),i,ObjProps[OPNUM(cur_test)].flags)); + } +#endif + } +} + +#endif // NOT_YET + +errtype save_game(char *fname, char *comment) { + int filenum; + State player_state; + errtype retval; + int idx = SAVE_GAME_ID_BASE; + + // KLC - this does nothing now. check_save_game_wackiness(); + // Why is this done??? closedown_game(FALSE); + + DEBUG("starting save_game"); + + // KLC do it the Mac way i = flush_resource_cache(); + // Size dummy; + // MaxMem(&dummy); DG: I don't think this is needed anymore + + // Open the current game file to save some more resources into it. + // FSMakeFSSpec(gDataVref, gDataDirID, CURRENT_GAME_FNAME, &currSpec); + filenum = ResEditFile(CURRENT_GAME_FNAME, FALSE); + if (filenum < 0) { + ERROR("Couldn't open Current Game"); + return ERR_FOPEN; + } + + // Sakeave comment + ResMake(idx, (void *)comment, strlen(comment) + 1, RTYPE_APP, filenum, RDF_LZW, FORMAT_RAW); + ResWrite(idx); + ResUnmake(idx); + idx++; + + // Save player struct (resource #4001) + player_struct.version_num = PLAYER_VERSION_NUMBER; + player_struct.realspace_loc = objs[player_struct.rep].loc; + EDMS_get_state(objs[PLAYER_OBJ].info.ph, &player_state); + LG_memcpy(player_struct.edms_state, &player_state, sizeof(fix) * 12); + // LZW later ResMake(idx, (void *)&player_struct, sizeof(player_struct), RTYPE_APP, filenum, + // RDF_LZW); + + ResMake(idx, (void *)&player_struct, sizeof(player_struct), RTYPE_APP, filenum, 0, FORMAT_PLAYER); + ResWrite(idx); + ResUnmake(idx); + idx++; + + // HAX HAX HAX Skip the schedule for now! + // Save game schedule (resource #590) + idx = SCHEDULE_BASE_ID; + // LZW later ResMake(idx, (void *)&game_seconds_schedule, sizeof(Schedule), RTYPE_APP, filenum, + // RDF_LZW); + + ResMake(idx, (void *)&game_seconds_schedule, sizeof(Schedule), RTYPE_APP, filenum, 0, FORMAT_SCHEDULE); + ResWrite(idx); + ResUnmake(idx); + idx++; + + // Save game schedule vec info (resource #591) + // LZW later ResMake(idx, (void *)game_seconds_schedule.queue.vec, sizeof(SchedEvent)*GAME_SCHEDULE_SIZE, + // RTYPE_APP, filenum, RDF_LZW); + ResMake(idx, (void *)game_seconds_schedule.queue.vec, sizeof(SchedEvent) * GAME_SCHEDULE_SIZE, RTYPE_APP, filenum, + 0, FORMAT_SCHEDULE_QUEUE); + ResWrite(idx); + ResUnmake(idx); + idx++; + + ResCloseFile(filenum); + + // Save current level + retval = write_level_to_disk(ResIdFromLevel(player_struct.level), TRUE); + if (retval) { + ERROR("Return value from write_level_to_disk is non-zero!"); // + critical_error(CRITERR_FILE | 3); + } + + // Copy current game out to save game slot + if (copy_file(CURRENT_GAME_FNAME, fname) != OK) { + // Put up some alert here. + ERROR("No good copy, dude!"); + // string_message_info(REF_STR_SaveGameFail); + } + // KLC else + // KLC string_message_info(REF_STR_SaveGameSaved); + old_ticks = *tmd_ticks; + // do we have to do this? startup_game(FALSE); + return (OK); +} + +errtype load_game_schedules(void) { + char *oldvec; + int idx = SCHEDULE_BASE_ID; + + oldvec = game_seconds_schedule.queue.vec; + ResExtract(idx++, FORMAT_SCHEDULE, &game_seconds_schedule); + game_seconds_schedule.queue.vec = oldvec; + game_seconds_schedule.queue.comp = compare_events; + ResExtract(idx++, FORMAT_SCHEDULE_QUEUE, oldvec); + return OK; +} + +errtype interpret_qvars(void) { +#ifdef SVGA_SUPPORT + extern short mode_id; +#endif + extern uchar fullscrn_vitals; + extern uchar fullscrn_icons; + extern uchar map_notes_on; + extern ubyte hud_color_bank; + + // KLC - don't do this here - it's a global now. load_da_palette(); + + gamma_dealfunc(QUESTVAR_GET(GAMMACOR_QVAR)); + + // dclick_dealfunc(QUESTVAR_GET(DCLICK_QVAR)); + // joysens_dealfunc(QUESTVAR_GET(JOYSENS_QVAR)); + + recompute_music_level(QUESTVAR_GET(MUSIC_VOLUME_QVAR)); + recompute_digifx_level(QUESTVAR_GET(SFX_VOLUME_QVAR)); +#ifdef AUDIOLOGS + recompute_audiolog_level(QUESTVAR_GET(ALOG_VOLUME_QVAR)); + //audiolog_setting = QUESTVAR_GET(ALOG_OPT_QVAR); //moved to prefs file +#endif + fullscrn_vitals = QUESTVAR_GET(FULLSCRN_VITAL_QVAR); + fullscrn_icons = QUESTVAR_GET(FULLSCRN_ICON_QVAR); + map_notes_on = QUESTVAR_GET(AMAP_NOTES_QVAR); + hud_color_bank = QUESTVAR_GET(HUDCOLOR_QVAR); + + digichan_dealfunc(QUESTVAR_GET(DIGI_CHANNELS_QVAR)); + + // mouse_set_lefty(QUESTVAR_GET(MOUSEHAND_QVAR)); + + language_change(QUESTVAR_GET(LANGUAGE_QVAR)); + + return (OK); +} + +// char saveArray[16]; //¥temp + +errtype load_game(char *fname) { + int filenum; + ObjID old_plr; + uchar bad_save = FALSE; + char orig_lvl; + extern uint dynmem_mask; + + INFO("load_game %s", fname); + + empty_slate(); + + closedown_game(TRUE); + // KLC - don't do this here stop_music(); + + // Copy the save file into the current game + copy_file(fname, CURRENT_GAME_FNAME); + + // Load in player and current level + filenum = ResOpenFile(CURRENT_GAME_FNAME); + old_plr = player_struct.rep; + orig_lvl = player_struct.level; + + ResExtract(SAVE_GAME_ID_BASE + 1, FORMAT_PLAYER, (void *)&player_struct); + + obj_check_time = 0; // KLC - added because it needs to be reset for Mac version. + + // KLC - this is a global pref now. change_detail_level(player_struct.detail_level); + player_struct.rep = old_plr; + player_set_eye_fixang(player_struct.eye_pos); + if (!bad_save) + obj_move_to(PLAYER_OBJ, &(player_struct.realspace_loc), FALSE); + + if (load_game_schedules() != OK) + bad_save = TRUE; + + ResCloseFile(filenum); + + if (orig_lvl == player_struct.level) { + // Warning(("HEY, trying to be clever about loading the game! %d vs %d\n",orig_lvl,player_struct.level)); + dynmem_mask = DYNMEM_PARTIAL; + } + + load_level_from_file(player_struct.level); + obj_load_art(FALSE); // KLC - added here (removed from load_level_data) + // KLC string_message_info(REF_STR_LoadGameLoaded); + dynmem_mask = DYNMEM_ALL; + chg_set_flg(_current_3d_flag); + old_ticks = *tmd_ticks; + interpret_qvars(); + startup_game(FALSE); + + // KLC - do following instead recompute_music_level(QUESTVAR_GET(MUSIC_VOLUME_QVAR)); + if (music_on) { + mlimbs_on = TRUE; + mlimbs_AI_init(); + mai_intro(); // KLC - added here + load_score_for_location(PLAYER_BIN_X, PLAYER_BIN_Y); // KLC - added here + } + + // CC: Should we go back into fullscreen mode? + if (player_struct.hardwarez_status[CPTRIP(FULLSCR_HARD_TRIPLE)]) { + _new_mode = FULLSCREEN_LOOP; + chg_set_flg(GL_CHG_LOOP); + } + + extern uchar muzzle_fire_light; + muzzle_fire_light = FALSE; + if (!(player_struct.hardwarez_status[CPTRIP(LANTERN_HARD_TRIPLE)] & WARE_ON)) + lamp_turnoff(TRUE, FALSE); + else + lamp_turnon(TRUE, FALSE); + + //¥¥ temp + // BlockMove(0, saveArray, 16); + + return (OK); +} + +errtype load_level_from_file(int level_num) { + errtype retval; + + INFO("Loading save %i", level_num); + + retval = load_current_map(ResIdFromLevel(level_num)); + + if (retval == OK) { + player_struct.level = level_num; + + compute_shodometer_value(FALSE); + + // if this is the first time the level is loaded, compute the inital shodan security level + if (player_struct.initial_shodan_vals[player_struct.level] == -1) + player_struct.initial_shodan_vals[player_struct.level] = QUESTVAR_GET(SHODAN_QV); + } + + return (retval); +} + +#ifdef NOT_YET // + +void check_and_update_initial(void) { + extern Datapath savegame_dpath; + char archive_fname[128]; + char dpath_fn[50]; + char *tmp; + extern char real_archive_fn[20]; + if (!DatapathFind(&savegame_dpath, CURRENT_GAME_FNAME, archive_fname)) { + tmp = getenv("CITHOME"); + if (tmp) { + strcpy(dpath_fn, tmp); + strcat(dpath_fn, "\\"); + } else + dpath_fn[0] = '\0'; + strcat(dpath_fn, "data\\"); + strcat(dpath_fn, CURRENT_GAME_FNAME); + + if (!DatapathFind(&DataDirPath, real_archive_fn, archive_fname)) + critical_error(CRITERR_RES | 0x10); + if (copy_file(archive_fname, dpath_fn) != OK) + critical_error(CRITERR_FILE | 0x7); + } +} + +#endif // NOT_YET + +uchar create_initial_game_func(short undefined1, ulong undefined2, void *undefined3) { + int i; + extern int actual_score; + byte plrdiff[4]; + char tmpname[sizeof(player_struct.name)]; + short plr_obj; + + INFO("Starting game"); + DEBUG("Game archive at %s", ARCHIVE_FNAME); + + // Copy archive into local current game file. + + if (copy_file(ARCHIVE_FNAME, CURRENT_GAME_FNAME) != OK) + critical_error(CRITERR_FILE | 7); + + plr_obj = PLAYER_OBJ; + for (i = 0; i < 4; i++) + plrdiff[i] = player_struct.difficulty[i]; + LG_memcpy(tmpname, player_struct.name, sizeof(tmpname)); + + // KLC - don't need this anymore. ResExtract(SAVE_GAME_ID_BASE + 1, (void *)&player_struct); + + init_player(&player_struct); + obj_check_time = 0; // KLC - added here cause it needs to be reset in Mac version + + player_struct.rep = OBJ_NULL; + + load_level_from_file(player_struct.level); + + obj_load_art(FALSE); // KLC - added here (removed from load_level_data) + amap_reset(); + + player_create_initial(); + + LG_memcpy(player_struct.name, tmpname, sizeof(player_struct.name)); + for (i = 0; i < 4; i++) + player_struct.difficulty[i] = plrdiff[i]; + + // KLC - not needed any longer ResCloseFile(filenum); + + // Reset MFDs to be consistent with starting setup + init_newmfd(); + + // No time elapsed, really, honest + old_ticks = *tmd_ticks; + + // Setup some start-game stuff + // Music + current_score = actual_score = last_score = PERIL_SCORE; // KLC - these aren't actually + mlimbs_peril = 1000; // going to do anything. + + if (music_on) { + mlimbs_on = TRUE; + mlimbs_AI_init(); + mai_intro(); // KLC - added here + load_score_for_location(PLAYER_BIN_X, PLAYER_BIN_Y); // KLC - added here + } + + load_dynamic_memory(DYNMEM_ALL); + + // KLC - if not already on, turn on-line help on. + if (!olh_active) + toggle_olh_func(0, 0, 0); + + // Do entry-level triggers for starting level + // Hmm, do we actually want to call this any time we restore + // a saved game or whatever? No, probably not....hmmm..... + + do_level_entry_triggers(); + + // turn on help overlay. + olh_overlay_on = olh_active; + + // Plot timers + + return (FALSE); +} + +errtype write_level_to_disk(int idnum, uchar flush_mem) { + // Eventually, this ought to cleverly determine whether or not to pack + // the save game resource, but for now we will always do so... + + // FSMakeFSSpec(gDataVref, gDataDirID, CURRENT_GAME_FNAME, &currSpec); + + // char* currSpec = "saves/save.dat"; + return (save_current_map(CURRENT_GAME_FNAME, idnum, flush_mem, TRUE)); +} diff --git a/engine/src/GameSrc/gearmfd.c b/engine/src/GameSrc/gearmfd.c new file mode 100644 index 0000000..259f1ad --- /dev/null +++ b/engine/src/GameSrc/gearmfd.c @@ -0,0 +1,164 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/gearmfd.c $ + * $Revision: 1.8 $ + * $Author: xemu $ + * $Date: 1994/10/16 15:53:14 $ + * + */ + +#include "mfdint.h" +#include "mfdext.h" +#include "mfddims.h" +#include "mfdfunc.h" +#include "tools.h" +#include "otrip.h" +#include "objbit.h" +#include "mfdart.h" +#include "objuse.h" +#include "cybstrng.h" +#include "gr2ss.h" + +// ============================================================ +// THE GEAR MFD +// ============================================================ + +// ------- +// DEFINES +// ------- +extern uchar full_game_3d; + +uchar gear_active(ObjID obj); + +// --------------- +// EXPOSE FUNCTION +// --------------- + +/* This gets called whenever the MFD needs to redraw or + undraw. + The control value is a bitmask with the following bits: + MFD_EXPOSE: Update the mfd, if MFD_EXPOSE_FULL is not set, + update incrementally. + MFD_EXPOSE_FULL: Fully redraw the mfd, implies MFD_EXPOSE + + if no bits are set, the mfd is being "unexposed;" its display + being pulled off the screen to make room for a different func. +*/ + +uchar gear_active(ObjID obj) { + switch (ID2TRIP(obj)) { + case TRACBEAM_TRIPLE: + return (objs[obj].info.inst_flags & CLASS_INST_FLAG) != 0; + } + return TRUE; +} + +#define GEAR_BUTTON_H 13 +#define GEAR_BUTTON_Y (MFD_VIEW_HGT - GEAR_BUTTON_H - 1) + +ushort button_bitmaps[] = { + 0, + (ushort)REFINDEX(REF_IMG_Use), + 0, + (ushort)REFINDEX(REF_IMG_Use), + (ushort)REFINDEX(REF_IMG_Active), + (ushort)REFINDEX(REF_IMG_Use), +}; + +void mfd_gear_expose(MFD *mfd, ubyte control) { + int active = player_struct.actives[ACTIVE_GENERAL]; + uchar full = control & MFD_EXPOSE_FULL; + if (control == 0) // MFD is drawing stuff + { + // Do unexpose stuff here. + } + if (control & MFD_EXPOSE) // Time to draw stuff + { + ObjID obj; + // clear update rects + mfd_clear_rects(); + // set up canvas + gr_push_canvas(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + // Clear the canvas by drawing the background bitmap + if (!full_game_3d) + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + + if (active < 0 || active >= NUM_GENERAL_SLOTS) { + mfd_notify_func(MFD_EMPTY_FUNC, MFD_ITEM_SLOT, TRUE, MFD_EMPTY, FALSE); + goto cleanup_et_return; + } + obj = player_struct.inventory[active]; + + if (obj == OBJ_NULL) { + mfd_notify_func(MFD_EMPTY_FUNC, MFD_ITEM_SLOT, TRUE, MFD_EMPTY, FALSE); + goto cleanup_et_return; + } else { + int id = MKREF(RES_mfdSpecial, button_bitmaps[objs[obj].info.type] + (gear_active(obj) ? 0 : 1)); + short wid = res_bm_width(id); + short x = (MFD_VIEW_WID - wid) / 2; + + mfd_item_micro_expose(full, ID2TRIP(obj)); + draw_mfd_item_spew(REF_STR_gearSpew0 + objs[obj].info.type, 1); + draw_res_bm(id, x, GEAR_BUTTON_Y); + mfd_add_rect(x, GEAR_BUTTON_Y, x + wid, MFD_VIEW_HGT); + } + + // on a full expose, make sure to draw everything + + if (full) + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + cleanup_et_return: + // Pop the canvas + gr_pop_canvas(); + // Now that we've popped the canvas, we can send the + // updated mfd to screen + mfd_update_rects(mfd); + } +} + +// ------- +// HANDLER +// ------- + +uchar mfd_gear_handler(MFD *m, uiEvent *e) { + uchar retval = FALSE; + int active = player_struct.actives[ACTIVE_GENERAL]; + LGRect r = {{0, GEAR_BUTTON_Y}, {MFD_VIEW_WID, GEAR_BUTTON_Y + GEAR_BUTTON_H}}; + + if ((e->subtype & (MOUSE_LDOWN | UI_MOUSE_LDOUBLE)) == 0) + return FALSE; + if (active >= 0 && active < NUM_GENERAL_SLOTS) { + ObjID obj = player_struct.inventory[active]; + int id = MKREF(RES_mfdSpecial, button_bitmaps[objs[obj].info.type] + (gear_active(obj) ? 0 : 1)); + short wid = res_bm_width(id); + r.ul.x = (MFD_VIEW_WID - wid) / 2; + r.lr.x = r.ul.x + wid; + RECT_OFFSETTED_RECT(&r, m->rect.ul, &r); + if (RECT_TEST_PT(&r, e->pos)) { + object_use(obj, TRUE, OBJ_NULL); + retval = TRUE; + } + } + return retval; +} diff --git a/engine/src/GameSrc/gr2ss.c b/engine/src/GameSrc/gr2ss.c new file mode 100644 index 0000000..8d82b8d --- /dev/null +++ b/engine/src/GameSrc/gr2ss.c @@ -0,0 +1,579 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#include "cit2d.h" +#include "gr2ss.h" +#include "frtypes.h" +#include "frintern.h" +#include "frprotox.h" +#include "gamescr.h" + + +#ifdef STEREO_SUPPORT +#include +extern uchar inp6d_stereo_active; + +#define S_DELTA 5 +#endif + +#ifdef SVGA_SUPPORT +uchar gr2ss_override = OVERRIDE_NONE; +char convert_type = 0; +char convert_use_mode = 0; + +char mode_count[MAX_CONVERT_TYPES]; +fix convert_x[MAX_CONVERT_TYPES][MAX_USE_MODES]; +fix convert_y[MAX_CONVERT_TYPES][MAX_USE_MODES]; +fix inv_convert_x[MAX_CONVERT_TYPES][MAX_USE_MODES]; +fix inv_convert_y[MAX_CONVERT_TYPES][MAX_USE_MODES]; + +#define SVGA_CONV_NONE 0 +#define SVGA_CONV_NORMAL 1 +#define SVGA_CONV_SCREEN 2 + +// Internal prototypes + +uchar perform_svga_conversion(uchar mask) { + extern uchar full_game_3d; + + if (gr2ss_override & OVERRIDE_FAIL) + return (SVGA_CONV_NONE); + // for now - if ((convert_use_mode == 0) && (!inp6d_stereo_active)) + if (convert_use_mode == 0) + return (SVGA_CONV_NONE); + if (_fr->draw_canvas.bm.bits == grd_canvas->bm.bits) + return (SVGA_CONV_SCREEN); + if (grd_canvas->bm.bits == grd_screen_canvas->bm.bits) + return (SVGA_CONV_SCREEN); + if (gr2ss_override & mask) + return (SVGA_CONV_NORMAL); + if (convert_use_mode == 3) // KLC - the normal mode for Mac Shock. + return (SVGA_CONV_SCREEN); + return (SVGA_CONV_NONE); +} + +// Conversion functions from "abstract" shock 2d functions +// to the actual 2d functions, with appropriate compensations +// for screen mode coordinates and aspect ratios. + +// Note, x and y already converted here! +void ss_scale_string(char *s, short x, short y) { + // needs to scale still! + // know about different fonts instead? That would be better... + grs_font *ttfont = (grs_font *)ResLock(RES_tinyTechFont); + grs_font *mlfont = (grs_font *)ResLock(RES_mediumLEDFont); + grs_font *f = gr_get_font(); + int c = gr_get_fcolor(); + Id use_font = ID_NULL; +#ifdef STEREO_SUPPORT + uchar rv = perform_svga_conversion(OVERRIDE_SCALE); +#endif + + if (convert_use_mode == 0) { + gr_string(s, x, y); + return; + } + + if ((f == ttfont) || (f == mlfont)) { + switch (convert_use_mode) { + case 1: + if (f == ttfont) + use_font = RES_tallTinyTechFont; + break; + case 2: + case 3: + if (f == ttfont) + use_font = RES_doubleTinyTechFont; + else if (f == mlfont) + use_font = RES_doubleMediumLEDFont; + break; + case 4: + if (f == ttfont) + use_font = RES_megaTinyTechFont; + else if (f == mlfont) + use_font = RES_megaMediumLEDFont; + break; + } +#ifdef STEREO_SUPPORT + if ((rv == SVGA_CONV_SCREEN) && inp6d_stereo_active) { + if (use_font == ID_NULL) { + short w, h; + gr_string_size(s, (short *)&w, (short *)&h); + gr_push_canvas(i6d_ss->cf_left); + gr_set_font(f); + gr_set_fcolor(c); + gr_scale_string(s, x + S_DELTA, y, SCONV_X(w) + S_DELTA, SCONV_Y(h)); + gr_pop_canvas(); + gr_push_canvas(i6d_ss->cf_right); + gr_set_font(f); + gr_set_fcolor(c); + gr_scale_string(s, x - S_DELTA, y, SCONV_X(w) - S_DELTA, SCONV_Y(h)); + } else { + gr_push_canvas(i6d_ss->cf_left); + gr_set_font((grs_font *)ResLock(use_font)); + gr_set_fcolor(c); + gr_string(s, x + S_DELTA, y); + gr_pop_canvas(); + gr_push_canvas(i6d_ss->cf_right); + gr_set_font((grs_font *)ResLock(use_font)); + gr_set_fcolor(c); + gr_string(s, x - S_DELTA, y); + } + gr_pop_canvas(); + } else { +#endif + if (use_font == ID_NULL) { + short w, h; + gr_string_size(s, (short *)&w, (short *)&h); + gr_scale_string(s, x, y, SCONV_X(w), SCONV_Y(h)); + } else { + gr_set_font((grs_font *)ResLock(use_font)); + gr_string(s, x, y); + } +#ifdef STEREO_SUPPORT + } +#endif + if (use_font != ID_NULL) + ResUnlock(use_font); + gr_set_font(ttfont); + ResUnlock(RES_tinyTechFont); + ResUnlock(RES_mediumLEDFont); + } else { + // Attempt to scale it + short w, h; + gr_string_size(s, (short *)&w, (short *)&h); + gr_scale_string(s, x, y, SCONV_X(w), SCONV_Y(h)); + } +} + +void ss_string(char *s, short x, short y) { + uchar rv; + if ((rv = perform_svga_conversion(OVERRIDE_SCALE))) { +#ifdef STEREO_SUPPORT + if ((rv == SVGA_CONV_SCREEN) && (inp6d_stereo_active)) { + gr_push_canvas(i6d_ss->cf_left); + if (convert_use_mode) + ss_scale_string(s, SCONV_X(x) + S_DELTA, SCONV_Y(y)); + else + ss_scale_string(s, x + S_DELTA, y); + gr_set_canvas(i6d_ss->cf_right); + if (convert_use_mode) + ss_scale_string(s, SCONV_X(x) - S_DELTA, SCONV_Y(y)); + else + ss_scale_string(s, x - S_DELTA, y); + gr_pop_canvas(); + } else +#endif + ss_scale_string(s, SCONV_X(x), SCONV_Y(y)); + } else { + gr_string(s, x, y); + } +} + +void ss_bitmap(grs_bitmap *bmp, short x, short y) { + uchar rv; + if ((rv = perform_svga_conversion(OVERRIDE_SCALE))) { +#ifdef STEREO_SUPPORT + if ((rv == SVGA_CONV_SCREEN) && (inp6d_stereo_active)) { + gr_push_canvas(i6d_ss->cf_left); + if (convert_use_mode) + gr_scale_bitmap(bmp, SCONV_X(x) + S_DELTA, SCONV_Y(y), SCONV_X(bmp->w) + S_DELTA, SCONV_Y(bmp->h)); + else + gr_bitmap(bmp, x + S_DELTA, y); + gr_set_canvas(i6d_ss->cf_right); + if (convert_use_mode) + gr_scale_bitmap(bmp, SCONV_X(x) - S_DELTA, SCONV_Y(y), SCONV_X(bmp->w) - S_DELTA, SCONV_Y(bmp->h)); + else + gr_bitmap(bmp, x - S_DELTA, y); + gr_pop_canvas(); + } else +#endif + gr_scale_bitmap(bmp, SCONV_X(x), SCONV_Y(y), SCONV_X(bmp->w), SCONV_Y(bmp->h)); + // Warning(("scaling %d x %d to %d x %d\n",bmp->w,bmp->h,SCONV_X(bmp->w),SCONV_Y(bmp->h))); + } else + gr_bitmap(bmp, x, y); +} + +void ss_ubitmap(grs_bitmap *bmp, short x, short y) { + if (perform_svga_conversion(OVERRIDE_SCALE)) + gr_scale_ubitmap(bmp, SCONV_X(x), SCONV_Y(y), SCONV_X(bmp->w), SCONV_Y(bmp->h)); + else + gr_ubitmap(bmp, x, y); +} + +void ss_noscale_bitmap(grs_bitmap *bmp, short x, short y) { + uchar rv; + if ((rv = perform_svga_conversion(OVERRIDE_SCALE))) // ? +#ifdef STEREO_SUPPORT + if ((rv == SVGA_CONV_SCREEN) && (inp6d_stereo_active)) { + gr_push_canvas(i6d_ss->cf_left); + if (convert_use_mode) + gr_bitmap(bmp, SCONV_X(x) + S_DELTA, SCONV_Y(y)); + else + gr_bitmap(bmp, x + S_DELTA, y); + gr_set_canvas(i6d_ss->cf_right); + if (convert_use_mode) + gr_bitmap(bmp, SCONV_X(x) - S_DELTA, SCONV_Y(y)); + else + gr_bitmap(bmp, x - S_DELTA, y); + gr_pop_canvas(); + } else +#endif + gr_bitmap(bmp, SCONV_X(x), SCONV_Y(y)); + else + gr_bitmap(bmp, x, y); +} + +void ss_scale_bitmap(grs_bitmap *bmp, short x, short y, short w, short h) { + if (perform_svga_conversion(OVERRIDE_SCALE)) + gr_scale_bitmap(bmp, SCONV_X(x), SCONV_Y(y), SCONV_X(w), SCONV_Y(h)); + else + gr_scale_bitmap(bmp, x, y, w, h); +} + +void ss_rect(short x1, short y1, short x2, short y2) { + uchar rv; + if ((rv = perform_svga_conversion(OVERRIDE_SCALE))) { +#ifdef STEREO_SUPPORT + if ((rv == SVGA_CONV_SCREEN) && (inp6d_stereo_active)) { + int c = gr_get_fcolor(); + gr_push_canvas(i6d_ss->cf_left); + gr_set_fcolor(c); + if (convert_use_mode) + gr_rect(SCONV_X(x1) + S_DELTA, SCONV_Y(y1), SCONV_X(x2) + S_DELTA, SCONV_Y(y2)); + else + gr_rect(x1 + S_DELTA, y1, x2 + S_DELTA, y2); + gr_set_canvas(i6d_ss->cf_right); + gr_set_fcolor(c); + if (convert_use_mode) + gr_rect(SCONV_X(x1) - S_DELTA, SCONV_Y(y1), SCONV_X(x2) - S_DELTA, SCONV_Y(y2)); + else + gr_rect(x1 - S_DELTA, y1, x2 - S_DELTA, y2); + gr_pop_canvas(); + } else +#endif + gr_rect(SCONV_X(x1), SCONV_Y(y1), SCONV_X(x2), SCONV_Y(y2)); + } else { + gr_rect(x1, y1, x2, y2); + } +} + +void ss_box(short x1, short y1, short x2, short y2) { + uchar rv; + if ((rv = perform_svga_conversion(OVERRIDE_SCALE))) { +#ifdef STEREO_SUPPORT + if ((rv == SVGA_CONV_SCREEN) && (inp6d_stereo_active)) { + int c = gr_get_fcolor(); + gr_push_canvas(i6d_ss->cf_left); + gr_set_fcolor(c); + if (convert_use_mode) + gr_box(SCONV_X(x1) + S_DELTA, SCONV_Y(y1), SCONV_X(x2) + S_DELTA, SCONV_Y(y2)); + else + gr_box(x1 + S_DELTA, y1, x2 + S_DELTA, y2); + gr_set_canvas(i6d_ss->cf_right); + gr_set_fcolor(c); + if (convert_use_mode) + gr_box(SCONV_X(x1) - S_DELTA, SCONV_Y(y1), SCONV_X(x2) - S_DELTA, SCONV_Y(y2)); + else + gr_box(x1 - S_DELTA, y1, x2 - S_DELTA, y2); + gr_pop_canvas(); + } else +#endif + gr_box(RSCONV_X(x1), RSCONV_Y(y1), RSCONV_X(x2), RSCONV_Y(y2)); + } else { + gr_box(x1, y1, x2, y2); + } +} + +void ss_safe_set_cliprect(short x1, short y1, short x2, short y2) { + if (perform_svga_conversion(OVERRIDE_CLIP)) { + // Warning(("setting rect (%d, %d) (%d,%d)!\n",SCONV_X(x1),SCONV_Y(y1),SCONV_X(x2),SCONV_Y(y2))); + safe_set_cliprect(SCONV_X(x1), SCONV_Y(y1), SCONV_X(x2), SCONV_Y(y2)); + } else + safe_set_cliprect(x1, y1, x2, y2); +} + +void ss_cset_cliprect(grs_canvas *pcanv, short x, short y, short w, short h) { + if (perform_svga_conversion(OVERRIDE_CLIP)) { + // Warning(("cset to %d,%d %d, %d!\n",SCONV_X(x), SCONV_Y(y), SCONV_X(w), SCONV_Y(h))); + gr_cset_cliprect(pcanv, SCONV_X(x), SCONV_Y(y), SCONV_X(w), SCONV_Y(h)); + } else + gr_cset_cliprect(pcanv, x, y, w, h); +} + +void ss_int_line(short x1, short y1, short x2, short y2) { + if (perform_svga_conversion(OVERRIDE_SCALE)) + gr_int_line(SCONV_X(x1), SCONV_Y(y1), SCONV_X(x2), SCONV_Y(y2)); + else + gr_int_line(x1, y1, x2, y2); +} + +void ss_thick_int_line(short x1, short y1, short x2, short y2) { + if (perform_svga_conversion(OVERRIDE_SCALE)) { + short min_y, max_y, use_y, min_x, max_x, use_x; + min_y = SCONV_Y(y1); + max_y = SCONV_Y(y1 + 1); + min_x = SCONV_X(x1); + max_x = SCONV_X(x1 + 1); + for (use_y = min_y; use_y < max_y; use_y++) + gr_int_line(SCONV_X(x1), use_y, SCONV_X(x2), SCONV_Y(y2) + use_y - min_y); + for (use_x = min_x; use_x < max_x; use_x++) + gr_int_line(use_x, SCONV_Y(y1), SCONV_X(x2) + use_x - min_x, SCONV_Y(y2)); + } else + gr_int_line(x1, y1, x2, y2); +} + +void ss_int_disk(short x1, short y1, short diam) { + if (perform_svga_conversion(OVERRIDE_SCALE)) + gr_int_disk(SCONV_X(x1), SCONV_Y(y1), SCONV_X(diam) >> 1); + // Hm, should we convert rad? + // Yes, but sadly it's hosed in 320x400 mode, where + // we need to draw an ellipse. This stuff really + // needs to be in the 2D. + else + gr_int_disk(x1, y1, diam >> 1); +} + +void ss_vline(short x1, short y1, short y2) { + if (perform_svga_conversion(OVERRIDE_SCALE)) + gr_vline(SCONV_X(x1), SCONV_Y(y1), SCONV_Y(y2)); + else + gr_vline(x1, y1, y2); +} + +void ss_hline(short x1, short y1, short x2) { + if (perform_svga_conversion(OVERRIDE_SCALE)) + gr_hline(SCONV_X(x1), SCONV_Y(y1), SCONV_X(x2)); + else + gr_hline(x1, y1, x2); +} + +void ss_fix_line(fix x1, fix y1, fix x2, fix y2) { + if (perform_svga_conversion(OVERRIDE_SCALE)) + gr_fix_line(FIXCONV_X(x1), FIXCONV_Y(y1), FIXCONV_X(x2), FIXCONV_Y(y2)); + else + gr_fix_line(x1, y1, x2, y2); +} + +void ss_thick_fix_line(fix x1, fix y1, fix x2, fix y2) { + if (perform_svga_conversion(OVERRIDE_SCALE)) { + fix min_y, max_y, use_y, min_x, max_x, use_x; + min_y = FIXCONV_Y(y1); + max_y = FIXCONV_Y(y1 + FIX_UNIT); + min_x = FIXCONV_X(x1); + max_x = FIXCONV_X(x1 + FIX_UNIT); + for (use_y = min_y; use_y < max_y; use_y = use_y + FIX_UNIT) + gr_fix_line(FIXCONV_X(x1), use_y, FIXCONV_X(x2), FIXCONV_Y(y2) + use_y - min_y); + for (use_x = min_x; use_x < max_x; use_x = use_x + FIX_UNIT) + gr_fix_line(use_x, FIXCONV_Y(y1), FIXCONV_X(x2) + use_x - min_x, FIXCONV_Y(y2)); + } else + gr_fix_line(x1, y1, x2, y2); +} + +void ss_get_bitmap(grs_bitmap *bmp, short x, short y) { + if (perform_svga_conversion(OVERRIDE_GET_BM)) + gr_get_bitmap(bmp, SCONV_X(x), SCONV_Y(y)); + else + gr_get_bitmap(bmp, x, y); +} + +void ss_set_pixel(long color, short x, short y) { + if (perform_svga_conversion(OVERRIDE_SCALE)) + gr_set_pixel(color, SCONV_X(x), SCONV_Y(y)); + else + gr_set_pixel(color, x, y); +} + +void ss_set_thick_pixel(long color, short x, short y) { + if (perform_svga_conversion(OVERRIDE_SCALE)) { + // gr_set_pixel(color, SCONV_X(x), SCONV_Y(y)); + gr_set_fcolor(color); + gr_box(SCONV_X(x), SCONV_Y(y), SCONV_X(x + 1) - 1, SCONV_Y(y + 1) - 1); + } else + gr_set_pixel(color, x, y); +} + +void ss_clut_ubitmap(grs_bitmap *bmp, short x, short y, uchar *cl) { + if (perform_svga_conversion(OVERRIDE_SCALE)) + gr_clut_scale_ubitmap(bmp, SCONV_X(x), SCONV_Y(y), SCONV_X(bmp->w), SCONV_Y(bmp->h), cl); + else + gr_clut_ubitmap(bmp, x, y, cl); +} + +// Registration! + +// Note that the zeroth element of the conversion arrays +// are used to store the base size, since perform_svga_conversion +// already filters out calls with use_mode of 0. +void gr2ss_register_init(char ctype, short init_x, short init_y) { + convert_x[ctype][0] = fix_make(init_x, 0); + convert_y[ctype][0] = fix_make(init_y, 0); +} + +#define WACKY_FIX_COMPENSATION +void gr2ss_register_mode(char conv_mode, short nx, short ny) { + char m; + mode_count[conv_mode]++; + m = mode_count[conv_mode]; + convert_x[conv_mode][m] = fix_div(fix_make(nx, 0), convert_x[conv_mode][0]); + convert_y[conv_mode][m] = fix_div(fix_make(ny, 0), convert_y[conv_mode][0]); + inv_convert_x[conv_mode][m] = fix_div(convert_x[conv_mode][0], fix_make(nx, 0)); + inv_convert_y[conv_mode][m] = fix_div(convert_y[conv_mode][0], fix_make(ny, 0)); + +#ifdef WACKY_FIX_COMPENSATION + // wacky fix point compensation! + if (convert_x[conv_mode][m] & 0xF) + convert_x[conv_mode][m]++; + if (convert_y[conv_mode][m] & 0xF) + convert_y[conv_mode][m]++; + if (inv_convert_x[conv_mode][m] & 0xF) + inv_convert_x[conv_mode][m]++; + if (inv_convert_y[conv_mode][m] & 0xF) + inv_convert_y[conv_mode][m]++; +#endif +} + +void ss_recompute_zoom(frc *which_frc, short oldm) { + fr_mod_cams(which_frc, FR_NOCAM, fix_div(convert_x[convert_type][convert_use_mode], convert_x[convert_type][oldm])); +} + +void ss_point_convert(short *px, short *py, uchar down) { +#ifdef SVGA_SUPPORT + if (convert_use_mode != 0) { + short ox, oy; + ox = *px; + oy = *py; + if (down) { + *px = INV_SCONV_X(*px); + *py = INV_SCONV_Y(*py); + } else { + *px = SCONV_X(*px); + *py = SCONV_Y(*py); + } + // Warning(("%d >> %d %d --> %d %d\n",down,ox,oy,*px,*py)); + } +#endif +} + +short ss_curr_mode_width(void) { return (SCONV_X(convert_x[convert_type][0])); } + +short ss_curr_mode_height(void) { return (SCONV_Y(convert_y[convert_type][0])); } + +// Basically, if you are in the secret hack mode 5 +// then MODE_SCONV_X will act as if you are in mode M +// otherwise it behaves like SCONV_{X,Y} +short MODE_SCONV_X(short cval, short m) { + if ((!m) || (convert_use_mode != 5)) + return (SCONV_X(cval)); + return (fast_fix_mul_int(fix_make(cval, 0), convert_x[convert_type][m])); +} + +short MODE_SCONV_Y(short cval, short m) { + if ((!m) || (convert_use_mode != 5)) + return (SCONV_Y(cval)); + return (fast_fix_mul_int(fix_make(cval, 0), convert_y[convert_type][m])); +} + +// This allows us to override the real mode with some +// fake pretender mode, but only if we are in the magic hack mode 5 +short hack_mode_on; +void ss_set_hack_mode(short new_m, short *tval) { + if ((convert_use_mode != 5) && (!hack_mode_on)) + return; + if (new_m) { + *tval = convert_use_mode; + convert_use_mode = new_m; + hack_mode_on++; + } else { + convert_use_mode = *tval; + hack_mode_on--; + } +} + +#endif + +void ss_mouse_convert(short *px, short *py, uchar down) { + if (convert_use_mode != 0) { +#ifdef STEREO_SUPPORT + if (convert_use_mode == 5) { + switch (i6d_device) { + case I6D_CTM: + return; + break; + case I6D_VFX1: + if (down) + *py = fix_int(fix_mul_div(fix_make(*py, 0), fix_make(200, 0), fix_make(480, 0))); + else + *py = fix_int(fix_mul_div(fix_make(*py, 0), fix_make(480, 0), fix_make(200, 0))); + return; + } + } +#endif + + if (down) { + *px = INV_SCONV_X(*px); + *py = INV_SCONV_Y(*py); + } else { + *px = SCONV_X(*px); + *py = SCONV_Y(*py); + } + } +} + +void ss_mouse_convert_round(short *px, short *py, uchar down) { + short ox, oy; + + if (convert_use_mode != 0) { +#ifdef STEREO_SUPPORT + if (convert_use_mode == 5) { + ss_mouse_convert(px, py, down); + return; + } +#endif + ox = *px; + oy = *py; + if (down) { + *px = fix_int(INV_FIXCONV_X(fix_make(*px, 0x8000))); + *py = fix_int(INV_FIXCONV_Y(fix_make(*py, 0x8000))); + } else { + *px = fix_int(FIXCONV_X(fix_make(*px, 0x8000))); + *py = fix_int(FIXCONV_Y(fix_make(*py, 0x8000))); + } + } +} + +void mouse_unconstrain(void) { + /* for now + #ifdef SVGA_SUPPORT + if (convert_use_mode == 5) + { + switch (i6d_device) + { + case I6D_CTM: + mouse_constrain_xy(0,0,grd_cap->w-1,grd_cap->h-1); + break; + case I6D_VFX1: + mouse_constrain_xy(0,0,i6d_ss->scr_w >> 1, i6d_ss->scr_h); + break; + } + } + else + #endif */ + mouse_constrain_xy(0, 0, grd_cap->w - 1, grd_cap->h - 1); +} diff --git a/engine/src/GameSrc/grenades.c b/engine/src/GameSrc/grenades.c new file mode 100644 index 0000000..6f8e6c0 --- /dev/null +++ b/engine/src/GameSrc/grenades.c @@ -0,0 +1,637 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/grenades.c $ + * $Revision: 1.106 $ + * $Author: xemu $ + * $Date: 1994/11/25 18:18:45 $ + * + */ + +#include "grenades.h" +#include "effect.h" +#include "objwpn.h" +#include "objclass.h" +#include "objprop.h" +#include "gamestrn.h" +#include "cybrnd.h" +#include "damage.h" +#include "combat.h" +#include "colors.h" +#include "objsim.h" +#include "map.h" +#include "faketime.h" +#include "gameobj.h" +#include "player.h" +#include "schedule.h" +#include "musicai.h" +#include "sfxlist.h" +#include "input.h" +#include "tools.h" +#include "otrip.h" +#include "cybstrng.h" +#include "frprotox.h" +#include "frflags.h" +#include "physics.h" +#include "physunit.h" +#include "trigger.h" // for trap_sfx_func() + +//---------------- +// Internal Prototypes +//---------------- +void convert_grenade_to_explosion(ExplosionData *edata, int triple); +ObjID explosion_ray_cast_attack(ObjID gren_id, ObjID target, ObjLoc *gren_loc, fix radius, fix mass, fix speed); +void do_object_explosion(ObjID id); + +ExplosionData game_explosions[GAME_EXPLS] = { + // RADIUS RADIUS CHG DMG DCHG DTYPE KNOCK_MASS OFF PENET + {fix_make(2, 0), fix_make(1, 0), 60, 50, EXPLOSION_FLAG, fix_make(250, 0), 3, 50}, + {fix_make(3, 0), fix_make(1, 0), 100, 80, EXPLOSION_FLAG, fix_make(300, 0), 4, 65}, + {fix_make(8, 0), fix_make(3, 0), 1000, 750, 0x0103, fix_make(1650, 0), 6, 35}, +}; + +// grenade_counter is a unique identifier for grenades, because if one grenade blows up another grenade, +// the dead grenade should have no explosion. this is because the scheduler will check for that unique id, and if +// there is no grenade with the same ObjID AND unique id, then we have to believe that the grenade was +// destroyed. +static ubyte grenade_counter = 1; + +// checks the first two to be equal = which means in_between +#define IN_BETWEEN_NOT_EQUAL(a, b, c) ((((a) <= (b)) && ((b) < (c))) || (((a) >= (b)) && ((b) > (c)))) + +#define GRENADE_TIME_UNIT 10 // how many time-setting units in a second +#define GRENADE_BULLET_MASS fix_make(0, 0x0100) +#define GRENADE_BULLET_SIZE fix_make(0, 0x0500) + +// -------------------------------------------------------- +// convert_grenade_to_explosion() +// + +void convert_grenade_to_explosion(ExplosionData *edata, int triple) { + int ctrip = CPTRIP(triple); + + edata->radius = fix_make(GrenadeProps[ctrip].radius, 0); + edata->radius_change = fix_make(GrenadeProps[ctrip].radius_change, 0); + edata->damage_mod = GrenadeProps[ctrip].damage_modifier; + edata->damage_change = GrenadeProps[ctrip].damage_change; + edata->dtype = GrenadeProps[ctrip].damage_type; + edata->knock_mass = fix_make(GrenadeProps[ctrip].attack_mass, 0) * 30; + edata->offense = GrenadeProps[ctrip].offense_value; + edata->penet = GrenadeProps[ctrip].penetration; +} + +extern ObjID terrain_hit_exclusion; + +// ---------------------------------------------------------------------- +// explosion_ray_cast_attack() +// + +ObjID explosion_ray_cast_attack(ObjID gren_id, ObjID target, ObjLoc *gren_loc, fix radius, fix mass, fix speed) { + Combat_Pt source; + Combat_Pt old_source; + Combat_Pt dest; + ObjID affected_object = OBJ_NULL; + Combat_Pt vector; + fix dist; + ubyte floor_height; + + source.x = fix_from_obj_coord(gren_loc->x); + source.y = fix_from_obj_coord(gren_loc->y); + + floor_height = obj_height_from_fix( + fix_from_map_height(me_height_flr(MAP_GET_XY(OBJ_LOC_BIN_X(*gren_loc), OBJ_LOC_BIN_Y(*gren_loc))))); + + // move the ray cast up a bit if it's too close to ground + source.z = fix_from_obj_height_val(gren_loc->z); + if (floor_height >= (gren_loc->z - 2)) + source.z += fix_from_obj_height_val(4); + + if (objs[target].info.ph != -1) { + State new_state; + get_phys_state(objs[target].info.ph, &new_state, target); + dest.x = new_state.X; + dest.y = new_state.Y; + dest.z = new_state.Z; + } else { + dest.x = fix_from_obj_coord(objs[target].loc.x); + dest.y = fix_from_obj_coord(objs[target].loc.y); + dest.z = fix_from_obj_height_val(objs[target].loc.z); + + // if it's a 3d model - move the target up by a quarter of its radius + // since it's centered in the ground + if (ObjProps[OPNUM(target)].render_type == 1) + dest.z += fix_make(ObjProps[OPNUM(target)].physics_xr, 0) / (PHYSICS_RADIUS_UNIT << 1); + } + + vector.x = dest.x - source.x; + vector.y = dest.y - source.y; + vector.z = dest.z - source.z; + + // normalize vector and convert it + dist = fix_sqrt(fix_mul(vector.x, vector.x) + fix_mul(vector.y, vector.y) + fix_mul(vector.z, vector.z)); + + vector.x = fix_div(vector.x, dist); + vector.y = fix_div(vector.y, dist); + vector.z = fix_div(vector.z, dist); + + old_source = source; + terrain_hit_exclusion = gren_id; + affected_object = ray_cast_vector(gren_id, &source, vector, mass, RAYCAST_ATTACK_SIZE, speed, radius); + + // this worries me - art + dest.x -= (vector.x / 6); + dest.y -= (vector.y / 6); + dest.z -= (vector.z / 6); + + // did we hit air (meaning nothing) + // and because we've checked distance already - we must have hit the object + // + if ((source.x < 0) || (source.y < 0) || (source.z < 0)) { + affected_object = target; + } + // or did we hit after the object???? + else if (!IN_BETWEEN_NOT_EQUAL(old_source.x, source.x, dest.x) && + !IN_BETWEEN_NOT_EQUAL(old_source.y, source.y, dest.y) && + !IN_BETWEEN_NOT_EQUAL(old_source.z, source.z, dest.z)) { + affected_object = target; + } + + return (affected_object); +} + +#define EMP_VHOLD_AMT 380 + +// ---------------------------------------------------------------------- +// do_explosion() +// + +void do_explosion(ObjLoc loc, ObjID exclusion, ubyte special_effect, ExplosionData *edata) { + int x = OBJ_LOC_BIN_X(loc); + int y = OBJ_LOC_BIN_Y(loc); + int cx, cy; + int cradius = fix_int(edata->radius); + fix deltax, deltay, deltaz; + fix radius_squared = fix_mul(edata->radius, edata->radius); + // we're dividing by two - to account later for lack of precision + fix max_damage = fix_make((edata->damage_mod >> 2), 0); + ObjRefID current_ref; + ObjID current_id; + uchar no_effect; + ubyte affect; + int damage; + extern ObjID damage_sound_id; + extern char damage_sound_fx; + + if (special_effect) { + fix dist2player; + deltax = fix_abs(OBJ_LOC_VAL_TO_FIX(objs[PLAYER_OBJ].loc.x - loc.x)); + deltay = fix_abs(OBJ_LOC_VAL_TO_FIX(objs[PLAYER_OBJ].loc.y - loc.y)); + deltaz = fix_from_obj_height_val(objs[PLAYER_OBJ].loc.z) - fix_from_obj_height_val(loc.z); + dist2player = fix_fast_pyth_dist(fix_fast_pyth_dist(deltax, deltay), deltaz); + + do_special_effect_location(exclusion, special_effect, 0xFF, &loc, 0); + + if (dist2player < fix_mul(edata->radius_change, edata->radius_change)) { + if (ID2TRIP(exclusion) == EMP_G_TRIPLE) + trap_sfx_func(0, 0, 4, EMP_VHOLD_AMT); + else { + fr_global_mod_flag(FR_SOLIDFR_SLDCLR, FR_SOLIDFR_MASK); + fr_solidfr_color = GRENADE_COLOR; + } + } + } + + for (cx = x - cradius; cx < (x + cradius); cx++) { + // check dimensions of x + if (cx < 0) + continue; + else if (cx >= MAP_XSIZE) + break; + + for (cy = y - cradius; cy < (y + cradius); cy++) { + // check dimensions of y + if (cy < 0) + continue; + else if (cy >= MAP_XSIZE) + break; + + // get the objrefs for this square + current_ref = MAP_GET_XY(cx, cy)->objRef; + while (current_ref != OBJ_REF_NULL) { + no_effect = FALSE; + affect = 0; + current_id = objRefs[current_ref].obj; + + if (current_id == OBJ_NULL) { + break; + } + + // make sure we're not going to affect ourselves + // also, make sure that the ref points to the object in the same square + // (since objects can have more than one objRef) + + if (current_id == exclusion) { + current_ref = objRefs[current_ref].next; + continue; + } else if ((OBJ_LOC_BIN_X(objs[objRefs[current_ref].obj].loc) != objRefs[current_ref].state.bin.sq.x) || + (OBJ_LOC_BIN_Y(objs[objRefs[current_ref].obj].loc) != objRefs[current_ref].state.bin.sq.y)) { + no_effect = TRUE; + } else { + // is object we're trying to affect already destroyed??? + no_effect = is_obj_destroyed(current_id); + + // make sure that the grenade can affect the object before doing lots of + // heinous computations - also remember the affect value + if (!no_effect) + affect = object_affect(current_id, edata->dtype); + } + + if (exclusion != OBJ_NULL) { + // have a random chance of not chaining explosions + if ((objs[exclusion].obclass != CLASS_GRENADE) && (objs[current_id].obclass != CLASS_CRITTER)) { + if (!RndRange(&grenade_rnd, 0, 1)) + no_effect = TRUE; + } + } + + // only do damage, if no_effect is FALSE, and affect is non-zero + if (!no_effect && affect) { + ObjID affected_object; + ObjLoc hit_loc = loc; + fix dist_squared; + + // get the distance of object to explosion + deltax = fix_abs(OBJ_LOC_VAL_TO_FIX(objs[current_id].loc.x - loc.x)); + deltay = fix_abs(OBJ_LOC_VAL_TO_FIX(objs[current_id].loc.y - loc.y)); + deltaz = fix_from_obj_height_val(objs[current_id].loc.z) - fix_from_obj_height_val(loc.z); + + // get the distance squared and check with radius squared. (saves us a square + // root if object is not within radius). + dist_squared = fix_mul(deltax, deltax) + fix_mul(deltay, deltay) + fix_mul(deltaz, deltaz); + + if (dist_squared < radius_squared) { + // let's do the raycast to see if we can hit the object + affected_object = explosion_ray_cast_attack(exclusion, current_id, &hit_loc, edata->radius, + edata->knock_mass, NO_RAYCAST_KICKBACK_SPEED); + + // let's check the object for valid attack + if ((affected_object != OBJ_NULL) && (affected_object != PLAYER_OBJ)) { + // saves time in dealing with damage! + if (!objs[affected_object].info.current_hp) { + affected_object = OBJ_NULL; + } + } + + if (affected_object == current_id) { + fix ratio; + fix obj_dist; + fix damage_fix; + int percent_damage; + ubyte effect; + ubyte effect_class; + + // divide by 2 to take into account - fix's lack of precision + int dmg = edata->damage_mod >> 2; + int dmgc = edata->damage_change >> 2; + + // let's get the object's distance from explosion! + obj_dist = fix_fast_pyth_dist(fix_fast_pyth_dist(deltax, deltay), deltaz); + + // are we within inner radius??? + if (obj_dist < edata->radius_change) { + ratio = fix_div(obj_dist, edata->radius_change); + damage_fix = max_damage - (ratio * (dmg - dmgc)); + } else { + ratio = + fix_div((obj_dist - edata->radius_change), (edata->radius - edata->radius_change)); + damage_fix = (fix_make(1, 0) - ratio) * dmgc; + } + percent_damage = fix_int(fix_div(damage_fix, max_damage) * 100); + + effect_class = (objs[current_id].obclass == CLASS_CRITTER) + ? CritterProps[CPNUM(current_id)].hit_effect + : NON_CRITTER_EFFECT; + effect = effect_matrix[effect_class][GREN_TYPE][0]; + + // okay let's figure out the damage done by the grenade + // let's check if we're dealing with + // a. combat difficulty - 0 + // b. not the player + // c. toughness not equal to 3 + // if we meet all three of these conditions, damage the object fully + + if (!player_struct.difficulty[COMBAT_DIFF_INDEX] && (current_id != PLAYER_OBJ)) + damage = + (ObjProps[OPNUM(current_id)].toughness != 3) ? objs[current_id].info.current_hp : 0; + else + damage = compute_damage(current_id, edata->dtype, edata->damage_mod, edata->offense, + edata->penet, percent_damage, NULL, NULL, GREN_TYPE); + + if (damage > 0) { + uchar explosion_affected; + ubyte flags; + + explosion_affected = FALSE; + if (objs[current_id].obclass == CLASS_GRENADE) { + ubyte chaining; + int targ_triple; + + // check if this grenade will be affected by blast + targ_triple = MAKETRIP(objs[current_id].obclass, objs[current_id].subclass, + objs[current_id].info.type); + + chaining = GrenadeProps[CPTRIP(targ_triple)].touchiness + (percent_damage / 15) + + RndRange(&grenade_rnd, 0, 6) - 3; + + if (chaining > 7) { + explosion_affected = TRUE; + } + } + + flags = 0; + + // if the grenade went off in the player's hand, the shield does nothing + // and you take double damage cause you are a bobo. + if (current_id == PLAYER_OBJ) { + if (ID2TRIP(exclusion) == GAS_G_TRIPLE) { + damage = (damage * 2) / (2 + player_struct.hardwarez[CPTRIP(ENV_HARD_TRIPLE)]); + } + if ((loc.x == objs[current_id].loc.x) && (loc.y == objs[current_id].loc.y)) { + message_info(get_temp_string( + REF_STR_HoldingGrenade)); //"Holding a live grenade = Double damage\n"); + + // shield does no damage + flags = NO_SHIELD_ABSORBTION; + + // do double damage + damage <<= 1; + } + } + + // need to check that the target is not a grenade that we set off because + // of a chain explosion + if (explosion_affected) { + objGrenades[objs[current_id].specID].unique_id = + 0; // get rid of timer - if grenade was timer + ADD_DESTROYED_OBJECT(current_id); + } else { + uchar do_effect = FALSE; + ubyte destroy = ObjProps[OPNUM(current_id)].destroy_effect; + if ((objs[current_id].info.current_hp <= damage) && DESTROY_OBJ_EFFECT(destroy)) { + objs[current_id].info.current_hp = 0; + do_effect = TRUE; + } else if (damage_object(current_id, damage, edata->dtype, flags)) + do_effect = (EFFECT_VAL(destroy) != 0); + + if (do_effect) { + ObjLoc loc = objs[current_id].loc; + fix deltax = OBJ_LOC_VAL_TO_FIX(objs[PLAYER_OBJ].loc.x - loc.x); + fix deltay = OBJ_LOC_VAL_TO_FIX(objs[PLAYER_OBJ].loc.y - loc.y); + fix dist = fix_fast_pyth_dist(deltax, deltay) << 2; + + if (ObjProps[OPNUM(current_id)].physics_model != 2) + loc.z += obj_height_from_fix( + fix_make(ObjProps[OPNUM(current_id)].physics_xr, 0) / + PHYSICS_RADIUS_UNIT); + + // move explosion towards player + loc.x += obj_coord_from_fix(fix_div(deltax, dist)); + loc.y += obj_coord_from_fix(fix_div(deltay, dist)); + + do_special_effect_location(current_id, destroy, 0xFF, &loc, 0); + } + } + } + } + } + } + + current_ref = objRefs[current_ref].next; + } + } + } + if (damage_sound_fx != -1) { + play_digi_fx_obj(damage_sound_fx, 1, damage_sound_id); + } + damage_sound_fx = -1; +} + +// ----------------------------------------------------------------- +// do_grenade_explosion() +// +// how accurate should we make the explosion location?? +// IMPORTANT: YOU MUST CALL destroy_destroyed_objects OUTSIDE OF PROCEDURE - NOT IN ANY OBJ LOOPS +// after calling do_explosion +// + +void do_grenade_explosion(ObjID id, uchar special_effect) { + ObjID grenade_location_id; + ObjLoc gren_loc; + ExplosionData edata; + int triple; + uchar in_hand = (id == object_on_cursor); + ubyte effect = (special_effect) ? (ObjProps[OPNUM(id)].destroy_effect & 0x7F) : 0; + + // let's get the triple + triple = MAKETRIP(objs[id].obclass, objs[id].subclass, objs[id].info.type); + + // secondly, make sure that we were given a grenade + if (objs[id].obclass != CLASS_GRENADE) { + return; + } + + // is the grenade already a dud???? + if (objGrenades[objs[id].specID].flags & GREN_DUD_FLAG) + return; + + // since we're activating it - set it to be a dud - we'll decide if it explodes later + objGrenades[objs[id].specID].flags |= (GREN_DUD_FLAG); + + grenade_location_id = (in_hand) ? PLAYER_OBJ : id; + // get rid of grenade cursor bitmap + if (in_hand) + pop_cursor_object(); + + gren_loc = objs[grenade_location_id].loc; + + convert_grenade_to_explosion(&edata, triple); + + // Special earthshaker hack + if (ID2TRIP(id) == EARTH_G_TRIPLE) { + extern short fr_sfx_time; + fr_global_mod_flag(FR_SFX_SHAKE, FR_SFX_MASK); + fr_sfx_time = CIT_CYCLE << 1; + } + + // Play sound effect + play_digi_fx_obj(SFX_EXPLOSION_1, 1, grenade_location_id); + + // actually destory the grenade (may want to go away if we want delayed grenade explosions) - minman + // do the actual explosion + do_explosion(gren_loc, id, effect, &edata); + + if (ID2TRIP(id) == EARTH_G_TRIPLE) + play_digi_fx(SFX_RUMBLE, 1); +} + +// -------------------------------------------------------- +// do_object_explosion() +// + +void do_object_explosion(ObjID id) { + ObjLoc loc = objs[id].loc; + ExplosionData edata; + + convert_grenade_to_explosion(&edata, OBJ_G_TRIPLE); + + // Check to see whether or not there is cool special stuff to do when this thing + // gets destroyed. obj_combat_destroy returns whether or not to go ahead and + // continue the destruction process + if (obj_combat_destroy(id)) { + ADD_DESTROYED_OBJECT(id); + } + + // do some explosion here + do_explosion(loc, id, 0, &edata); +} + + // ------------------------------------------------- + // get_grenade_name() + // + +#define NAMEBUFSZ 50 + +char *get_grenade_name(int gtype, char *buf) { + int triple = nth_after_triple(MAKETRIP(CLASS_GRENADE, 0, 0), gtype); + + get_object_short_name(triple, buf, NAMEBUFSZ); + + return buf; +} + +// -------------------------------------------------------- +// activate_grenade() +// + +void activate_grenade(ObjSpecID osid) { + ObjID id; + int triple; + int type; + ubyte deviation; + int tdev; + int time; + ubyte n; + GrenSchedEvent new_event; + + id = objGrenades[osid].id; + + if (id != OBJ_NULL) { + type = objs[id].info.type; + triple = MAKETRIP(objs[id].obclass, objs[id].subclass, type); + objGrenades[osid].flags = GREN_ACTIVE_FLAG; + play_digi_fx(SFX_GRENADE_ARM, 1); + + if (GrenadeProps[CPNUM(id)].flags & GREN_TIMING_TYPE) { + // activate the grenade + + // add deviation for the grenade + + deviation = TimedGrenadeProps[SCTRIP(triple)].timing_deviation; + tdev = RndRange(&grenade_rnd, 0, (deviation * 2)); + n = get_nth_from_triple(triple); + time = (player_struct.grenades_time_setting[n] + tdev - deviation); + + // schedule the grenade explosion + new_event.timestamp = TICKS2TSTAMP(player_struct.game_time + (CIT_CYCLE * time) / GRENADE_TIME_UNIT); + new_event.type = GRENADE_SCHED_EVENT; + new_event.gren_id = id; + new_event.unique_id = grenade_counter; + { errtype err = schedule_event(&global_fullmap->sched[MAP_SCHEDULE_GAMETIME], (SchedEvent *)&new_event); } + objGrenades[osid].timestamp = new_event.timestamp; + } + objGrenades[osid].unique_id = grenade_counter; + + // let's increment the grenade counter + grenade_counter = (grenade_counter == UNIQUE_LIMIT) ? 1 : grenade_counter + 1; + } +} + +uchar activate_grenade_on_cursor(void) { + ObjID oc = object_on_cursor; + + if (oc == OBJ_NULL || objs[oc].obclass != CLASS_GRENADE) + return FALSE; + + if (objGrenades[objs[oc].specID].flags & GREN_ACTIVE_FLAG) + return TRUE; + + // push and pop object cursor to update its live-ness display + pop_cursor_object(); + activate_grenade(objs[oc].specID); + push_cursor_object(oc); + return TRUE; +} + +#define PHYSICS_WAIT 3 + +// -------------------------------------------------------------------------- +// reactivate_mine() +// +// this is so the grenade will contact with the player after the grenade has +// left the player's body. + +void reactivate_mine(ObjID id) { + ObjSpecID osid = objs[id].specID; + GrenSchedEvent new_event; + + if (id != OBJ_NULL) { + // schedule the grenade reactivation + new_event.timestamp = TICKS2TSTAMP(player_struct.game_time + (CIT_CYCLE * PHYSICS_WAIT) / GRENADE_TIME_UNIT); + new_event.type = GRENADE_SCHED_EVENT; + new_event.gren_id = id; + new_event.unique_id = grenade_counter; + { errtype err = schedule_event(&global_fullmap->sched[MAP_SCHEDULE_GAMETIME], (SchedEvent *)&new_event); } + objGrenades[osid].timestamp = new_event.timestamp; + objGrenades[osid].unique_id = grenade_counter; + grenade_counter = (grenade_counter == UNIQUE_LIMIT) ? 1 : grenade_counter + 1; + } +} + +// ------------------------------------------------------ +// grenade_contact() +// +// called when grenade contact something + +void grenade_contact(ObjID id, int undefined) { + if (is_obj_destroyed(id)) { + return; + } + + if ((GrenadeProps[CPNUM(id)].flags & GREN_CONTACT_TYPE) && + (objGrenades[objs[id].specID].flags & GREN_ACTIVE_FLAG)) { + ADD_DESTROYED_OBJECT(id); + } else if ((GrenadeProps[CPNUM(id)].flags & GREN_MINE_TYPE) && + (objGrenades[objs[id].specID].flags & GREN_ACTIVE_FLAG)) { + if (!(objGrenades[objs[id].specID].flags & GREN_MINE_STILL)) { + // loook - don't explode me - i ain't still yet + return; + } + ADD_DESTROYED_OBJECT(id); + } +} diff --git a/engine/src/GameSrc/hand.c b/engine/src/GameSrc/hand.c new file mode 100644 index 0000000..2fd8ca3 --- /dev/null +++ b/engine/src/GameSrc/hand.c @@ -0,0 +1,281 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/hand.c $ + * $Revision: 1.38 $ + * $Author: minman $ + * $Date: 1994/11/17 18:19:18 $ + * + */ + +// Includes +#include "handart.h" +#include "objclass.h" +#include "weapons.h" +#include "player.h" +#include "hand.h" +#include "fullscrn.h" +#include "faketime.h" + +#include "game_screen.h" // Was screen.h? + +typedef struct { + char handart_frame; + char x_offset; + char y_offset; +} handart_frame_info; + +#define HANDART_NUM \ + (get_nth_from_triple(MAKETRIP(CLASS_GUN, player_struct.weapons[player_struct.actives[ACTIVE_WEAPON]].type, \ + player_struct.weapons[player_struct.actives[ACTIVE_WEAPON]].subtype))) + +#define GAMESCR_HANDART_Y 76 +#define FULLSCREEN_HANDART_Y 168 + +#define HANDART_X_BASE 100 +#define HANDART_ID_BASE RES_handArt_0 + +#define FULL_MIDDLE_SCREEN (FULL_VIEW_WIDTH / 2) +#define SCREEN_MIDDLE_SCREEN (SCREEN_VIEW_WIDTH / 2) + +#define MAX_HAND2HAND_FRAMES 5 +#define NUM_FRAMES 2 + +#define PR24_COUNT 5 +#define LASER_EPEE_COUNT 5 + +#define HAND_BOB 3 +#define BOB_MIN 0 +#define BOB_MAX 6 + +#define HAND_BOBX 4 +#define BOBX_MIN 0 +#define BOBX_MAX 8 + +#define BOB_THRESHOLD (fix_make(0, 0x2800)) + +// damn is this ugly - but hey - we can save lots of space!!! + +handart_frame_info hand2hand_info[NUM_HANDTOHAND_GUN][MAX_HAND2HAND_FRAMES] = { + {{0, 10, -2}, {1, 0, 7}, {2, -14, 21}, {3, -26, 26}, {1, 3, 7}}, // pr-24 + {{1, -7, -1}, {0, 10, -20}, {2, -30, 4}, {3, -49, 20}, {1, -16, 19}}, // laser epee +}; + +#define NUM_PROJ_GUN (NUM_PISTOL_GUN + NUM_AUTO_GUN) + +LGPoint pistol_hand_info[NUM_PROJ_GUN][NUM_FRAMES] = { + {{0, 25}, {-4, 19}}, // pistol + {{0, 25}, {-3, 20}}, // dartgun + {{0, 24}, {-2, 18}}, // magnum + {{0, 23}, {-2, 16}}, // assault rifle + {{0, 23}, {-1, 21}}, // riot gun + {{0, 21}, {-15, 11}}, // flechette + {{0, 23}, {-2, 17}}, // skorpion +}; + +#define NUM_ENERGY_GUN (NUM_GUN - NUM_PROJ_GUN) +LGPoint energy_hand_info[NUM_ENERGY_GUN][NUM_FRAMES] = { + {{0, 22}, {-3, 19}}, // magpulse + {{0, 24}, {-4, 16}}, // rail gun + {{0, 0}, {0, 0}}, // filler - hand2hand + {{0, 0}, {0, 0}}, // filler + {{0, 24}, {0, 24}}, // sparq beam + {{0, 24}, {-12, 18}}, // blaster + {{0, 21}, {-10, 15}}, // ion rifle + {{0, 24}, {0, 19}}, // stungun + {{0, 21}, {-16, 10}}, // plasma rifle +}; + +#define BOB_TIME (CIT_CYCLE >> 4) + +ubyte handart_count = 2; +ubyte hand_bobbing = HAND_BOB; +ubyte hand_bobx = HAND_BOBX; +uchar bob_up = TRUE; +uchar bob_left = TRUE; + +// ----------------------------------------- +// get_handart() +// + +Ref get_handart(int *x_offset, int *y_offset, int *beam_x_offset, short mouse_x, short mouse_y) { + int view_base_y; + short screen_height; + short factor; + short hand_x, hand_y; + ubyte frame; + ubyte type; + // byte offset=HAND_BOB; + State new_state; + // RefTable *prt; + + *beam_x_offset = 0; + +#ifdef HANDART_ADJUST + extern ubyte hcount; +#endif + + switch (player_struct.weapons[player_struct.actives[ACTIVE_WEAPON]].type) { + case (GUN_SUBCLASS_PISTOL): + case (GUN_SUBCLASS_AUTO): + frame = handart_show - 1; + hand_x = pistol_hand_info[HANDART_NUM][frame].x; + hand_y = pistol_hand_info[HANDART_NUM][frame].y; + break; + case (GUN_SUBCLASS_HANDTOHAND): + type = player_struct.weapons[player_struct.actives[ACTIVE_WEAPON]].subtype; + frame = hand2hand_info[type][handart_show - 1].handart_frame; +#ifdef HANDART_ADJUST + if (hcount) + frame = hcount - 1; +#endif + hand_x = hand2hand_info[type][handart_show - 1].x_offset; + hand_y = hand2hand_info[type][handart_show - 1].y_offset; + + EDMS_get_state(objs[PLAYER_OBJ].info.ph, &new_state); // look - we have to use get_state to get velocity + hand_x += hand_bobx; + hand_y += hand_bobbing; + + if ((fix_abs(new_state.X_dot) > BOB_THRESHOLD) || (fix_abs(new_state.Y_dot) > BOB_THRESHOLD) || + (fix_abs(new_state.gamma_dot) > BOB_THRESHOLD) || (fix_abs(new_state.beta_dot) > BOB_THRESHOLD)) { + if (player_struct.last_bob + BOB_TIME < player_struct.game_time) { + if (bob_up) { + if (hand_bobbing >= BOB_MAX) { + bob_up = FALSE; + hand_bobbing--; + } else + hand_bobbing++; + } else { + if (hand_bobbing <= BOB_MIN) { + bob_up = TRUE; + hand_bobbing++; + } else + hand_bobbing--; + } + if (bob_left) { + if (hand_bobx <= BOBX_MIN) { + bob_left = FALSE; + hand_bobx++; + } else + hand_bobx--; + } else { + if (hand_bobx >= BOBX_MAX) { + bob_left = TRUE; + hand_bobx--; + } else + hand_bobx++; + } + player_struct.last_bob = player_struct.game_time; + } + } + break; + default: + frame = handart_show - 1; + hand_x = energy_hand_info[HANDART_NUM - NUM_PROJ_GUN][frame].x; + hand_y = energy_hand_info[HANDART_NUM - NUM_PROJ_GUN][frame].y; + break; + } + + if (full_game_3d) { + // old code - do we care if inventory is up???? + // if (full_visible & FULL_INVENT_MASK) + // return(NULL); + + mouse_x -= FULL_VIEW_X; + mouse_y -= FULL_VIEW_Y; + + view_base_y = FULLSCREEN_HANDART_Y; + screen_height = FULL_VIEW_HEIGHT / 3; + + if (mouse_x < 10) + mouse_x = 10; + else if (mouse_x > (FULL_VIEW_WIDTH - 10)) + mouse_x = (FULL_VIEW_WIDTH - 10); + + factor = abs(mouse_x - FULL_MIDDLE_SCREEN) / 2; + factor += ((mouse_y - 40) / 2); + if (factor < 0) + factor = 0; + + *x_offset = + (((mouse_x - FULL_MIDDLE_SCREEN) * factor) / FULL_MIDDLE_SCREEN + FULL_MIDDLE_SCREEN + FULL_VIEW_X - 10) + + hand_x; + *beam_x_offset = -hand_x; + } else { + mouse_x -= SCREEN_VIEW_X; + mouse_y -= SCREEN_VIEW_Y; + view_base_y = GAMESCR_HANDART_Y; + screen_height = SCREEN_VIEW_HEIGHT / 4; + if (mouse_x < 10) + mouse_x = 10; + else if (mouse_x > (SCREEN_VIEW_WIDTH - 10)) + mouse_x = (SCREEN_VIEW_WIDTH - 10); + + if (mouse_y < 0) + mouse_y = 0; + else if (mouse_y > SCREEN_VIEW_HEIGHT) + return (ID_NULL); + else if (mouse_y > (SCREEN_VIEW_HEIGHT - 10)) + view_base_y++; + + factor = abs(mouse_x - SCREEN_MIDDLE_SCREEN) / 3; + factor += (mouse_y - 15); + if (factor < 0) + factor = 0; + + *x_offset = + ((mouse_x - SCREEN_MIDDLE_SCREEN) * factor) / SCREEN_MIDDLE_SCREEN + SCREEN_MIDDLE_SCREEN - 15 + hand_x; + *beam_x_offset = -hand_x; + } + *y_offset = view_base_y + (mouse_y / screen_height) + hand_y; + + reset_handart_count(player_struct.actives[ACTIVE_WEAPON]); + + /* KLC - don't need this check + prt = ResReadRefTable(HANDART_ID_BASE + HANDART_NUM); + if (!(RefIndexValid(prt,frame))) + { + frame = prt->numRefs - 1; + Warning(("ACK PAIN HATE!\n")); + } + ResFreeRefTable(prt); + */ + return (MKREF((HANDART_ID_BASE + HANDART_NUM), frame)); +} + +// -------------------------------------- +// notify_draw_handart() +// + +void notify_draw_handart(void) { + // once a fire frame has been shown, set handart_fire to TRUE + // - this is so we definitely show the fire frame, otherwise it would looooook very goooooofy - minman + + handart_fire = TRUE; +} + +void reset_handart_count(int wpn_num) { + if (player_struct.weapons[wpn_num].type == GUN_SUBCLASS_HANDTOHAND) { + ubyte hit = (handart_count &= 0x80); + handart_count = (player_struct.weapons[wpn_num].subtype == 0) ? PR24_COUNT : LASER_EPEE_COUNT; + if (hit) + handart_count = ((handart_count - 2) | 0x80); + } else + handart_count = 2; +} diff --git a/engine/src/GameSrc/hflip.c b/engine/src/GameSrc/hflip.c new file mode 100644 index 0000000..a949e4f --- /dev/null +++ b/engine/src/GameSrc/hflip.c @@ -0,0 +1,113 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/hflip.c $ + * $Revision: 1.6 $ + * $Author: mahk $ + * $Date: 1994/10/31 22:08:14 $ + */ + +#include "gr2ss.h" +#include "cybmem.h" + +// MLA #define REAL_HFLIP +#ifdef REAL_HFLIP +// asm is good +// tmp should be half a max row +// we copy to half row, then mirrow backwards, then copy half row in +void do_flip_in_place(uchar *bits, uchar *tmp, int w, int h, int row); +#pragma aux do_flip_in_place = \ +/* end of the loop thing, so we need to do it to start out */ \ + "mov eax, ecx" \ +/* edi ptr tmp, esi ptr bits line, eax+ecx width, edx is height */ \ +"per_line:" \ + "shl edx, 16" /* get height back up there */ \ + "mov ebx, esi" \ +/* first copy half row */ \ +"move_left_to_tmp:" \ + "shr ecx, 1" \ + "and ecx, 3" \ + "rep movsb" \ + "mov ecx, eax" \ + "shr ecx, 3" \ + "rep movsd" \ + "dec edi" /* get back to end of tmp stream */ \ +/* now mirror right half back to left */ \ + "mov ecx, eax" \ + "mov esi, ebx" /* get to left of bits */ \ + "add esi, ecx" /* get to right of bits */ \ + "dec esi" /* correct pixel is w-1 */ \ +/* should inline this a bunch, eh? */ \ +"rev_right_to_left_loop:" \ + "mov dl,[esi]" \ + "mov [ebx],dl" \ + "dec esi" \ + "inc ebx" \ + "cmp esi, ebx" \ + "jg rev_right_to_left_loop" \ +/* now take back out of temp */ \ + "jne even_size" \ + "inc ebx" /* if odd, need do nothing to middle pixel */ \ +"even_size:" /* ebx now points at next to fill */ \ + "shr ecx, 1" /* note now edi is source, bx dest */ \ +"rev_temp_to_right_loop:" \ + "mov dl,[edi]" \ + "mov [ebx],dl" \ + "dec edi" \ + "inc ebx" \ + "dec ecx" \ + "jnz rev_temp_to_right_loop" \ + "inc edi" /* edi is left pointing one before start, thus inc */ \ + "mov esi, ebx" /* store final pixel addr back into esi */ \ + "add esi,[esp]" /* esi is pointing one past the end of line */ \ + "mov ecx, eax" \ + "shr edx, 16" \ + "dec edx" \ + "jnz per_line" \ + "add esp, 4" /* get rid of the row_size on the stack */ \ +parm [esi] [edi] [ecx] [edx] modify [eax ebx]; + +#pragma disable_message(202) +// row skip is used implicitly above +void shock_hflip_in_place(grs_bitmap *bm) { + int row_skip = bm->row - bm->w; + uchar tmp[320]; + + do_flip_in_place(bm->bits, tmp, bm->w, bm->h, row_skip); +} +#pragma enable_message(202) + +void _flip_in_place(uchar *bits, uchar *tmp, int w, int h, int row) { do_flip_in_place(bits, tmp, w, h, row - w); } + +#else // !REAL_HFLIP + +void shock_hflip_in_place(grs_bitmap *bm) { + grs_canvas big_canvas; + grs_canvas bm_canvas; + gr_init_canvas(&big_canvas, big_buffer, BMT_FLAT8, bm->w, bm->h); + gr_init_canvas(&bm_canvas, bm->bits, BMT_FLAT8, bm->w, bm->h); + gr_push_canvas(&big_canvas); + gr_hflip_bitmap(bm, 0, 0); + gr_pop_canvas(); + gr_push_canvas(&bm_canvas); + ss_bitmap(&big_canvas.bm, 0, 0); + gr_pop_canvas(); +} + +#endif diff --git a/engine/src/GameSrc/hkeyfunc.c b/engine/src/GameSrc/hkeyfunc.c new file mode 100644 index 0000000..2bcb706 --- /dev/null +++ b/engine/src/GameSrc/hkeyfunc.c @@ -0,0 +1,1467 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/hkeyfunc.c $ + * $Revision: 1.173 $ + * $Author: dc $ + * $Date: 1994/11/18 00:24:50 $ + */ + +#include +#include + +#include "Shock.h" +#include "Prefs.h" + +#include "fullscrn.h" +#include "grenades.h" +#include "invent.h" +#include "loops.h" +#include "mfdint.h" +#include "mfdext.h" +#include "MacTune.h" +#include "musicai.h" +#include "objwpn.h" +#include "saveload.h" +#include "softdef.h" +#include "tools.h" +#include "wares.h" +#include "mouselook.h" +#include "audiolog.h" +#include "Xmi.h" + +//-------------- +// PROTOTYPES +//-------------- +int select_object_by_class(int obclass, int num, ubyte *quantlist); + +int current_palette_mode = TERRAIN_MODE; + +#ifdef NOT_YET // + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define SIGNATURE "giSoink" +#define CFG_HKEY_GO "cyberia" +uchar yes_3d = TRUE; +extern uchar properties_changed; + +#ifdef PLAYTEST +#pragma disable_message(202) +uchar maim_player(ushort keycode, uint32_t context, intptr_t data) { + player_struct.hit_points = 5; + return TRUE; +} +#pragma enable_message(202) + +#pragma disable_message(202) +uchar salt_the_player(ushort keycode, uint32_t context, intptr_t data) { + if (config_get_raw(CFG_HKEY_GO, NULL, 0)) { + player_struct.hit_points = 255; + player_struct.cspace_hp = 255; + memset(player_struct.hit_points_lost, 0, NUM_DAMAGE_TYPES); + player_struct.energy = 255; + player_struct.fatigue = 0; + player_struct.experience = TRUE; + chg_set_flg(VITALS_UPDATE); + } else { + message_info("Winners don't use hotkeys"); + damage_player(25, EXPLOSION_FLAG, 0); + } + return TRUE; +} + +uchar automap_seen(ushort keycode, uint32_t context, intptr_t data) { + ushort x, y; + MapElem *pme; + + if (config_get_raw(CFG_HKEY_GO, NULL, 0)) { + for (x = 0; x < MAP_XSIZE; x++) { + for (y = 0; y < MAP_YSIZE; y++) { + me_bits_seen_set(MAP_GET_XY(x, y)); + } + } + } + return TRUE; +} + +extern errtype give_player_loot(Player *pplr); + +uchar give_player_hotkey(ushort keycode, uint32_t context, intptr_t data) { + if (config_get_raw(CFG_HKEY_GO, NULL, 0)) { + give_player_loot(&player_struct); + chg_set_flg(INVENTORY_UPDATE); + mfd_force_update(); + } + return TRUE; +} + +#pragma enable_message(202) +#endif + +#ifdef PLAYTEST +uchar new_cone_clip = TRUE; + +#pragma disable_message(202) +uchar change_clipper(ushort keycode, uint32_t context, intptr_t data) { + extern errtype render_run(void); + new_cone_clip = !new_cone_clip; + if (new_cone_clip) + mprintf("NEW CONE CLIP\n"); + else + mprintf("OLD CONE CLIP\n"); + render_run(); + return TRUE; +} +#pragma enable_message(202) +#endif + +#pragma disable_message(202) +uchar quit_key_func(ushort keycode, uint32_t context, intptr_t data) { +#ifndef GAMEONLY + extern uchar possible_change; +#endif + +#ifndef GAMEONLY + if ((!possible_change) || (confirm_box("Level changed without save. Really exit?"))) +#endif + { + _new_mode = -1; + chg_set_flg(GL_CHG_LOOP); + } + return TRUE; +} + +extern void loopmode_exit(short), loopmode_enter(short); + +uchar keyhelp_hotkey_func(ushort keycode, uint32_t context, intptr_t data) { + void *keyhelp_txtscrn; + int fake_inp = 0; + extern errtype update_state(uchar time_passes); + + loopmode_exit(_current_loop); + + uiHideMouse(NULL); + uiFlush(); + +#ifdef RES_keyhelp + keyhelp_txtscrn = scrntext_init(RES_smallTechFont, 0x4C, RES_keyhelp); + while (scrntext_advance(keyhelp_txtscrn, fake_inp)) { + + fake_inp = 0; + + // translate keys to escape + if (uiCheckInput()) { + fake_inp = KEY_PGDN; + } + tight_loop(FALSE); + } + + scrntext_free(keyhelp_txtscrn); +#else + Warning(("Ce n'est pas RES_keyhelp\n")); +#endif + + uiShowMouse(NULL); + + update_state(FALSE); + loopmode_enter(_current_loop); + + return TRUE; +} +#endif // NOT_YET + +uchar really_quit_key_func(ushort keycode, uint32_t context, intptr_t data) { + gPlayingGame = false; + return TRUE; +} + +uchar toggle_bool_func(ushort keycode, uint32_t context, intptr_t data) { + bool *tgl = (bool *)data; + *tgl = !*tgl; + return TRUE; +} + +extern bool DoubleSize; + +uchar change_mode_func(ushort keycode, uint32_t context, intptr_t data) { + int newm = (int)data; + + if ((newm == AUTOMAP_LOOP) && ((!player_struct.hardwarez[HARDWARE_AUTOMAP]) || (global_fullmap->cyber))) + return TRUE; + _new_mode = newm; + chg_set_flg(GL_CHG_LOOP); + return TRUE; +} + +#ifdef NOT_YET // + +#ifdef HANDART_ADJUST + +short hdx = 0, hdy = 0; +ubyte hcount = 0; + +uchar move_handart(ushort keycode, uint32_t context, intptr_t data) { + short amt = 1; + ubyte foo = (ubyte)data; + short *dir; + + if (foo & 0x10) { + hdx = hdy = 0; + return TRUE; + } + + if (foo & 0x08) + amt = 10; + foo &= 0x7F; + dir = (foo & 0x02) ? &hdx : &hdy; + + if (foo & 0x01) + (*dir) += amt; + else + (*dir) -= amt; + + return TRUE; +} + +uchar adv_handart(ushort keycode, uint32_t context, intptr_t data) { + hcount = (hcount + 1) % 5; + return TRUE; +} + +#endif // HANDART_ADJUST + +uchar toggle_view_func(ushort keycode, uint32_t context, intptr_t data) { + extern uchar full_game_3d; + return (change_mode_func(keycode, context, (full_game_3d) ? GAME_LOOP : FULLSCREEN_LOOP)); +} + +#endif // NOT_YET + +void start_music(void) { + // if (music_card) + // { + if (MacTuneInit() == 0) { + music_on = TRUE; + mlimbs_on = TRUE; + mlimbs_AI_init(); + load_score_for_location(PLAYER_BIN_X, PLAYER_BIN_Y); + MacTuneStartCurrentTheme(); + } else { + gShockPrefs.soBackMusic = FALSE; + SavePrefs(); + } + // } +} + +void stop_music(void) { + extern uchar mlimbs_on; + + MacTuneShutdown(); + music_on = FALSE; + mlimbs_on = FALSE; + mlimbs_peril = DEFAULT_PERIL_MIN; + mlimbs_monster = NO_MONSTER; +} + +uchar toggle_music_func(ushort keycode, uint32_t context, intptr_t data) { + if (music_on) { + message_info("Music off."); + StopTheMusic(); //do this here, not in stop_music(), to prevent silence when changing levels + stop_music(); + } else { + start_music(); + message_info("Music on."); + } + + gShockPrefs.soBackMusic = music_on; + SavePrefs(); + + return (FALSE); +} + +uchar arm_grenade_hotkey(ushort keycode, uint32_t context, intptr_t data) { + extern uchar show_all_actives; + extern short inv_last_page; + int i, row, act; + + if (!show_all_actives) { + show_all_actives = TRUE; + inv_last_page = -1; + chg_set_flg(INVENTORY_UPDATE); + mfd_force_update(); + return TRUE; + } + if (activate_grenade_on_cursor()) + return TRUE; + act = player_struct.actives[ACTIVE_GRENADE]; + for (i = row = 0; i < act; i++) + if (player_struct.grenades[i]) + row++; + super_drop_func(ACTIVE_GRENADE, row); + return TRUE; +} + +int select_object_by_class(int obclass, int num, ubyte *quantlist) { + extern uchar show_all_actives; + extern short inv_last_page; + int act = player_struct.actives[obclass]; + int newobj = act; + + inv_last_page = -1; + chg_set_flg(INVENTORY_UPDATE); + if (!show_all_actives) { + show_all_actives = TRUE; + return -1; + } + do { + newobj = (newobj + 1) % num; + } while (quantlist[newobj] == 0 && newobj != act); + + player_struct.actives[obclass] = newobj; + return newobj; +} + +uchar select_grenade_hotkey(ushort keycode, uint32_t context, intptr_t data) { + int newobj; + + newobj = select_object_by_class(ACTIVE_GRENADE, NUM_GRENADES, player_struct.grenades); + set_inventory_mfd(MFD_INV_GRENADE, newobj, TRUE); + return TRUE; +} + +uchar select_drug_hotkey(ushort keycode, uint32_t context, intptr_t data) { + int newobj; + + newobj = select_object_by_class(ACTIVE_DRUG, NUM_DRUGS, player_struct.drugs); + set_inventory_mfd(MFD_INV_DRUG, newobj, TRUE); + return TRUE; +} + +uchar use_drug_hotkey(ushort keycode, uint32_t context, intptr_t data) { + extern uchar show_all_actives; + extern short inv_last_page; + int i, row, act; + + if (!show_all_actives) { + show_all_actives = TRUE; + inv_last_page = -1; // to force redraw + chg_set_flg(INVENTORY_UPDATE); + return TRUE; + } + act = player_struct.actives[ACTIVE_DRUG]; + for (i = row = 0; i < act; i++) + if (player_struct.drugs[i]) + row++; + super_use_func(ACTIVE_DRUG, row); + return TRUE; +} + +uchar clear_fullscreen_func(ushort keycode, uint32_t context, intptr_t data) { + extern char last_message[128]; + extern MFD mfd[2]; + + full_lower_region(&mfd[MFD_RIGHT].reg2); + full_lower_region(&mfd[MFD_LEFT].reg2); + full_lower_region(inventory_region_full); + full_visible = 0; + strcpy(last_message, ""); + chg_unset_sta(FULLSCREEN_UPDATE); + return (FALSE); +} + +#ifdef NOT_YET // KLC + +#ifndef GAMEONLY +uchar zoom_func(ushort keycode, uint32_t context, intptr_t data) { + ushort zoom; + + TileMapGetZoom(NULL, &zoom); + if (data == ZOOM_IN) { + zoom++; + } else + zoom = (zoom == 1) ? 1 : zoom - 1; + TileMapSetZoom(NULL, zoom); + return TRUE; +} + +uchar do_popup_textmenu(ushort keycode, uint32_t context, intptr_t g) { + extern errtype textmenu_popup(Gadget * parent); + + textmenu_popup((Gadget *)g); + return TRUE; +} +#endif + +#define MAP_FNAME "map.dat" + +#ifdef PLAYTEST +void edit_load_func(char *fn, uchar source, short level_num) { + char buf[256], *buf2; + errtype retval; + extern Datapath savegame_dpath; + extern char real_archive_fn[20]; + extern void store_objects(char **buf, ObjID *obj_array, char obj_count); + extern void restore_objects(char *buf, ObjID *obj_array, char obj_count); + extern char *get_proj_datadir(char *); + + // if (level_num != 1) + // player_struct.level = 1; + if (!strnicmp(fn, "level", 5)) + player_struct.level = atoi(strncpy(buf, fn + 5, 2)); + store_objects(&buf2, player_struct.inventory, NUM_GENERAL_SLOTS); + switch (source) { + case 0: // local + strcpy(buf, DATADIR); + strcat(buf, fn); + retval = load_current_map(buf, LEVEL_ID_NUM, NULL); + break; + case 1: // currsave + retval = load_level_from_file(level_num); + break; + case 2: // archive + retval = load_current_map(real_archive_fn, ResIdFromLevel(level_num), &savegame_dpath); + break; + case 3: // network + if (get_proj_datadir(buf) != NULL) { + strcat(buf, fn); + retval = load_current_map(buf, LEVEL_ID_NUM, NULL); + } else + retval = ERR_NULL; + break; + case 4: // Old Res + retval = load_current_map(fn, OLD_LEVEL_ID_NUM, &savegame_dpath); + break; + } + restore_objects(buf2, player_struct.inventory, NUM_GENERAL_SLOTS); + switch (retval) { + case ERR_FOPEN: + sprintf(buf, "Error opening %s", fn); + message_box(buf); + break; + case ERR_NOEFFECT: + message_box("bad map version."); + break; + case OK: + compute_shodometer_value(FALSE); + config_set_single_value(CFG_LEVEL_VAR, CONFIG_STRING_TYPE, fn); +#ifndef GAMEONLY + TileMapRedrawPixels(NULL, NULL); + chg_set_flg(EDITVIEW_UPDATE); +#endif + message_info("Load complete"); + break; + } +} +#endif + +#ifdef GADGET +uchar load_level_func(ushort keycode, uint32_t context, intptr_t data) { + char fn[256]; +#ifndef GAMEONLY + extern uchar possible_change; +#endif + extern Gadget *edit_root_gadget; + + fn[0] = '\0'; +#ifndef GAMEONLY + if ((!possible_change) || (confirm_box("Level changed without save! Load anyways?"))) +#endif + level_saveload_box("Load Map", _current_root, 1, fn, edit_load_func, TRUE); + return (TRUE); +} +#endif + +#define BACKUP_FNAME "shockbak.dat" + +#ifdef PLAYTEST +void edit_save_func(char *fn, uchar source, short level_num) { + char buf[64], b2[64]; + extern void reset_schedules(void); +#ifndef GAMEONLY + extern uchar possible_change; +#endif + extern char savegame_dir[50]; + extern Datapath savegame_dpath; + + Spew(DSRC_EDITOR_Restore, ("edit_save_func: fn = %s\n", fn)); + + // Make a backup of previous file, if it exists + if (DatapathFind(&savegame_dpath, fn, buf)) { + strcpy(b2, savegame_dir); + strcat(b2, "\\"); + strcat(b2, BACKUP_FNAME); + copy_file(buf, b2); + } + strcpy(buf, fn); + reset_schedules(); + switch (save_current_map(buf, LEVEL_ID_NUM, TRUE, TRUE)) { + case ERR_FOPEN: + sprintf(buf, "Error opening %s", fn); + message_box(buf); + break; + case OK: + message_info("Save complete."); + config_set_single_value(CFG_LEVEL_VAR, CONFIG_STRING_TYPE, fn); +#ifndef GAMEONLY + possible_change = FALSE; +#endif + break; + } +} +#endif + +#ifdef GADGET +uchar save_level_func(ushort keycode, uint32_t context, intptr_t data) { + if (!saves_allowed) { + message_box("Saves not allowed -- use control panel to change"); + return (FALSE); + } + if ((default_fname == NULL) && !config_get_raw(CFG_LEVEL_VAR, default_fname, 256)) + strcpy(default_fname, MAP_FNAME); + Spew(DSRC_EDITOR_Restore, ("default_fname = %s\n", default_fname)); + level_saveload_box("Save Map", _current_root, 1, default_fname, edit_save_func, FALSE); + return (TRUE); +} +#endif + +#ifndef GAMEONLY +uchar toggle_3d_func(ushort keycode, uint32_t context, intptr_t data) { + TileEditor *te = (TileEditor *)data; + // uchar yes3d = !chg_get_sta(EDITVIEW_UPDATE); + Point newsize; + Point newloc; + int z; + + if (yes_3d) { + newsize.x = TILEMAP_REGION_WIDTH; + newsize.y = TILEMAP_REGION_HEIGHT; + newloc.x = TILEMAP_REGION_X; + newloc.y = TILEMAP_REGION_Y; + z = 0; + chg_set_flg(EDITVIEW_UPDATE); + } else { + newsize.x = VIEW_REGION_WIDTH + TILEMAP_REGION_WIDTH; + newsize.y = TILEMAP_REGION_HEIGHT; + z = 1; + newloc.x = VIEW_REGION_X; + newloc.y = VIEW_REGION_Y; + chg_unset_flg(EDITVIEW_UPDATE); + } + yes_3d = !yes_3d; + region_begin_sequence(); + TileEditorResize(te, newsize); + TileEditorMove(te, newloc, z); + region_end_sequence(TRUE); + return TRUE; +} + +uchar tilemap_mode_func(ushort keycode, uint32_t context, intptr_t data) { + int mode; + extern errtype terrain_palette_popup(void); + extern void bitsmode_palette_popup(void); + extern void cutpaste_palette_popup(void); + extern void cybpal_popup(void); + mode = (int)data; + if (mode != current_palette_mode) { + current_palette_mode = mode; + switch (mode) { + case OBJECT_MODE: + object_palette_popup(); + break; + case TERRAIN_MODE: + terrain_palette_popup(); + break; + case EYEBALL_MODE: + eyeball_palette_popup(); + break; + case CUTPASTE_MODE: + cutpaste_palette_popup(); + break; + case TEXTURING_MODE: + if (global_fullmap->cyber) + cybpal_popup(); + else + texture_palette_popup(); + break; + case BITS_MODE: + bitsmode_palette_popup(); + break; + } + } + return (TRUE); +} + +uchar draw_mode_func(ushort keycode, uint32_t context, intptr_t data) { + TileEditorSetMode(NULL, (int)data); + return (TRUE); +} + +uchar clear_highlight_func(ushort keycode, uint32_t context, intptr_t data) { + TileMapClearHighlights(NULL); + TileMapRedrawPixels(NULL, NULL); + return TRUE; +} +#endif + +#ifndef GAMEONLY +uchar texture_selection_func(ushort keycode, uint32_t context, intptr_t data) { +#ifdef TEXTURE_SELECTION + textpal_create_selector(); +#endif + return (TRUE); +} +#endif + +#ifdef GADGET +uchar lighting_func(ushort keycode, uint32_t context, intptr_t data) { + panel_create_lighting(); + return (TRUE); +} + +uchar inp6d_panel_func(ushort keycode, uint32_t context, intptr_t data) { + extern void panel_create_inp6d(void); + panel_create_inp6d(); + return (TRUE); +} + +uchar render_panel_func(ushort keycode, uint32_t context, intptr_t data) { + panel_create_renderer(); + return (TRUE); +} + +uchar popup_tilemap_func(ushort keycode, uint32_t context, intptr_t data) { return (TRUE); } + +#endif + +#ifdef PLAYTEST +uchar bkpt_me(ushort keycode, uint32_t context, intptr_t data) { // put a break point here, goof + return TRUE; +} +#endif + +#ifdef GADGET +uchar editor_options_func(ushort keycode, uint32_t context, intptr_t data) { + editor_options->parent = _current_root; + gad_menu_popup_at_mouse(editor_options); + return (TRUE); +} + +uchar editor_modes_func(ushort keycode, uint32_t context, intptr_t data) { + editor_modes->parent = _current_root; + gad_menu_popup_at_mouse(editor_modes); + return (TRUE); +} + +uchar misc_menu_func(ushort keycode, uint32_t context, intptr_t data) { + main_misc_menu->parent = _current_root; + main_misc_menu->parent = _current_root; + renderer_misc_menu->parent = _current_root; + gamesys_misc_menu->parent = _current_root; + editor_misc_menu->parent = _current_root; + misc_misc_menu->parent = _current_root; + report_sys_menu->parent = _current_root; + gad_menu_popup_at_mouse(main_misc_menu); + return (TRUE); +} + +uchar control_panel_func(ushort keycode, uint32_t context, intptr_t data) { + panel_create_control(); + return (TRUE); +} +#endif + +#ifndef GAMEONLY +uchar do_find_func(ushort keycode, int32_t context, intptr_t data) { + int hilite_num; + extern errtype generic_tile_eyedropper(TileEditor * te); + extern errtype TerrainPalUpdate(struct _terrainpal * tp); + extern void texture_palette_update(void); + + // hilite_num = 0; + switch (current_palette_mode) { + case OBJECT_MODE: + TileMapFindHighlightNum(NULL, &hilite_num); + object_find_func(hilite_num); + break; + case TERRAIN_MODE: + generic_tile_eyedropper(NULL); + TerrainPalUpdate(NULL); + break; + case TEXTURING_MODE: + generic_tile_eyedropper(NULL); + texture_palette_update(); + break; + + default: + generic_tile_eyedropper(NULL); + break; + } + TileMapRedrawSquares(NULL, NULL); + return (TRUE); +} +#endif + +#ifdef PLAYTEST +#ifndef GAMEONLY +uchar inp6d_kbd = TRUE; +#else +uchar inp6d_kbd = FALSE; +#endif + +uchar stupid_slew_func(ushort keycode, uint32_t context, intptr_t data) { + int dir = (int)data; + int v1, v2; + static int slew_scale = 16; + extern uchar inp6d_kbd; + + if (inp6d_kbd == FALSE) + return TRUE; + + switch (dir) { + case 1: + v1 = EYE_Y; + v2 = slew_scale; + break; + case 2: + v1 = EYE_H; + v2 = -slew_scale; + break; + case 3: + v1 = EYE_Y; + v2 = -slew_scale; + break; + case 4: + v1 = EYE_H; + v2 = slew_scale; + break; + case 5: + v1 = EYE_Z; + v2 = slew_scale; + break; + case 6: + v1 = EYE_Z; + v2 = -slew_scale; + break; + case 7: + v1 = EYE_P; + v2 = -slew_scale; + break; + case 8: + v1 = EYE_P; + v2 = slew_scale; + break; + case 9: + v1 = EYE_B; + v2 = -slew_scale; + break; + case 10: + v1 = EYE_B; + v2 = slew_scale; + break; + case 11: + v1 = EYE_X; + v2 = slew_scale; + break; + case 12: + v1 = EYE_X; + v2 = -slew_scale; + break; + case 13: + v1 = EYE_RESET; + v2 = -slew_scale; + break; + case 14: + if (slew_scale < 256) + slew_scale <<= 1; + return TRUE; + case 15: + if (slew_scale > 1) + slew_scale >>= 1; + return TRUE; + } + fr_camera_slewcam(NULL, v1, v2); + if (_current_loop <= FULLSCREEN_LOOP) + chg_set_flg(DEMOVIEW_UPDATE); +#ifndef GAMEONLY + if (_current_loop == EDIT_LOOP) { + TileMapUpdateCameras(NULL); + chg_set_flg(EDITVIEW_UPDATE); + } +#endif + return (TRUE); +} + +uchar zoom_3d_func(ushort keycode, uint32_t context, intptr_t data) { + uchar zoomin = (bool)data; + + // cant this be current based? + if (zoomin) + fr_mod_cams(_current_fr_context, FR_NOCAM, fix_make(0, 62500)); + else + fr_mod_cams(_current_fr_context, FR_NOCAM, fix_make(1, 3000)); + return (TRUE); +} +#endif + +#ifdef GADGET +uchar menu_close_func(ushort keycode, uint32_t context, intptr_t data) { return (menu_all_popdown()); } +#endif + +#ifdef PLAYTEST +uchar mono_clear_func(ushort keycode, uint32_t context, intptr_t data) { + mono_clear(); + return (FALSE); +} + +uchar mono_toggle_func(ushort keycode, uint32_t context, intptr_t data) { + mono_setmode(MONO_TOG); + message_info("Monochrome Toggled."); + return (FALSE); +} +#endif + +#ifdef GADGET +Gadget *edit_flags_gadget = NULL; +uchar f0, f1, f2; + +uchar edit_flags_close(void *vg, void *ud) { + // Postprocess results into change flags + if (f0) { + chg_set_sta(ML_CHG_BASE << 0); + chg_set_flg(ML_CHG_BASE << 0); + } else { + chg_unset_sta(ML_CHG_BASE << 0); + chg_unset_flg(ML_CHG_BASE << 0); + } + if (f1) { + chg_set_sta(ML_CHG_BASE << 1); + chg_set_flg(ML_CHG_BASE << 1); + } else { + chg_unset_sta(ML_CHG_BASE << 1); + chg_unset_flg(ML_CHG_BASE << 1); + } + if (f2) { + chg_set_sta(ML_CHG_BASE << 2); + chg_set_flg(ML_CHG_BASE << 2); + } else { + chg_unset_sta(ML_CHG_BASE << 2); + chg_unset_flg(ML_CHG_BASE << 2); + } + gadget_destroy(&edit_flags_gadget); + return (FALSE); +} + +uchar edit_flags_func(ushort keycode, uint32_t context, intptr_t data) { + Point pt, ss; + + pt.x = 20; + pt.y = 25; + ss.x = 110; + ss.y = 8; + if (edit_flags_gadget == NULL) { + f0 = ((_change_flag & (ML_CHG_BASE << 0)) != 0); + f1 = ((_change_flag & (ML_CHG_BASE << 1)) != 0); + f2 = ((_change_flag & (ML_CHG_BASE << 2)) != 0); + edit_flags_gadget = gad_qbox_start(_current_root, pt, 10, &EditorStyle, QB_ALIGNMENT, "edit_flags_gadget", ss); + gad_qbox_add("Main Loop Flags", QB_TEXT_SLOT, NULL, QB_RD_ONLY); + gad_qbox_add("Flag 0", QB_BOOL_SLOT, &f0, QB_ARROWS); + gad_qbox_add("Flag 1", QB_BOOL_SLOT, &f1, QB_ARROWS); + gad_qbox_add("Frame Rate", QB_BOOL_SLOT, &f2, QB_ARROWS); + gad_qbox_add("Close", QB_PUSHBUTTON_SLOT, edit_flags_close, QB_NO_OPTION); + gad_qbox_end(); + } + return (FALSE); +} + +uchar music_ai_params_func(ushort keycode, uint32_t context, intptr_t data) { + panel_ai_param_create(); + return (FALSE); +} +#endif + +uchar version_spew_func(ushort keycode, uint32_t context, intptr_t data) { + char tmpstr[] = SIGNATURE; /* for tracking versions */ + char temp[40]; + strcpy(temp, ".... "); + if (start_mem >= BIG_CACHE_THRESHOLD) + temp[0] = 'C'; + if (start_mem > EXTRA_TMAP_THRESHOLD) + temp[1] = 'T'; + if (start_mem > BLEND_THRESHOLD) + temp[2] = 'B'; + if (start_mem > BIG_HACKCAM_THRESHOLD) + temp[3] = 'M'; + strcat(temp, SYSTEM_SHOCK_VERSION); + message_info(temp); + return (FALSE); +} + +#endif // NOT_YET + +char conv_hex(char val); +uchar location_spew_func(ushort, uint32_t, intptr_t); + +char conv_hex(char val) { + char retval = '?'; + if ((val >= 0) && (val <= 9)) + retval = '0' + val; + else if ((val >= 10) && (val <= 15)) + retval = 'a' + (val - 10); + return (retval); +} +/*KLC moved to TOOLS.C +int str_to_hex(char val) +{ + int retval = 0; + if ((val >= '0') && (val <= '9')) + retval = val - '0'; + else if ((val >= 'A') && (val <= 'F')) + retval = 10 + val - 'A'; + else if ((val >= 'a') && (val <= 'f')) + retval = 10 + val - 'a'; + return(retval); +} + +uchar location_spew_func(ushort , uint32_t , intptr_t ) +{ + char goofy_string[32]; + +//#ifdef SVGA_SUPPORT +// sprintf(goofy_string,"00:00.00:%s",get_temp_string(REF_STR_ScreenModeText + convert_use_mode)); +//#else + strcpy(goofy_string,"00:00.00 "); +//#endif + goofy_string[0] = conv_hex( player_struct.level / 16 ); + goofy_string[1] = conv_hex( player_struct.level % 16 ); + if (!time_passes) + goofy_string[2] = '!'; + goofy_string[3] = conv_hex( PLAYER_BIN_X / 16 ); + goofy_string[4] = conv_hex( PLAYER_BIN_X % 16 ); + if (!physics_running) + goofy_string[5] = '*'; + goofy_string[6] = conv_hex( PLAYER_BIN_Y / 16 ); + goofy_string[7] = conv_hex( PLAYER_BIN_Y % 16 ); + + message_info(goofy_string); + return(FALSE); +} +*/ + +uchar toggle_physics_func(ushort keycode, uint32_t context, intptr_t data) { + physics_running = !physics_running; + + extern uchar pacifism_on; + pacifism_on = !physics_running; + + if (physics_running) + message_info("Physics turned on"); + else + message_info("Physics turned off"); + + return (FALSE); +} + +uchar toggle_giveall_func(ushort keycode, uint32_t context, intptr_t data) { + message_info("Kick some ass!"); + + for (int i = 0; i < NUM_HARDWAREZ; i++) + player_struct.hardwarez[i] = 1; + player_struct.hardwarez[HARDWARE_360] = 3; + + //rail gun + player_struct.weapons[0].type = GUN_SUBCLASS_SPECIAL; + player_struct.weapons[0].subtype = 1; + player_struct.weapons[0].ammo = 50; + player_struct.weapons[0].ammo_type = 0; + player_struct.weapons[0].make_info = 0; + + //ion beam + player_struct.weapons[1].type = GUN_SUBCLASS_BEAM; + player_struct.weapons[1].subtype = 2; + player_struct.weapons[1].heat = 0; + player_struct.weapons[1].setting = 40; + player_struct.weapons[1].make_info = 0; + + //riot gun, hollow + player_struct.weapons[2].type = GUN_SUBCLASS_PISTOL; + player_struct.weapons[2].subtype = 4; + player_struct.weapons[2].ammo = 100; + player_struct.weapons[2].ammo_type = 0; + player_struct.weapons[2].make_info = 0; + + //skorpion, slag + player_struct.weapons[3].type = GUN_SUBCLASS_AUTO; + player_struct.weapons[3].subtype = 1; + player_struct.weapons[3].ammo = 150; + player_struct.weapons[3].ammo_type = 0; + player_struct.weapons[3].make_info = 0; + + //magpulse + player_struct.weapons[4].type = GUN_SUBCLASS_SPECIAL; + player_struct.weapons[4].subtype = 0; + player_struct.weapons[4].ammo = 50; + player_struct.weapons[4].ammo_type = 0; + player_struct.weapons[4].make_info = 0; + + //sparq + player_struct.weapons[5].type = GUN_SUBCLASS_BEAM; + player_struct.weapons[5].subtype = 0; + player_struct.weapons[5].heat = 0; + player_struct.weapons[5].setting = 40; + player_struct.weapons[5].make_info = 0; + + //laser rapier + player_struct.weapons[6].type = GUN_SUBCLASS_HANDTOHAND; + player_struct.weapons[6].subtype = 1; + player_struct.weapons[6].heat = 0; + player_struct.weapons[6].setting = 0; + player_struct.weapons[6].make_info = 0; + + player_struct.hit_points = 255; + player_struct.energy = 255; + + // Software stuff + player_struct.softs.misc[SOFTWARE_TURBO] = 5; + player_struct.softs.misc[SOFTWARE_FAKEID] = 5; + player_struct.softs.misc[SOFTWARE_DECOY] = 5; + player_struct.softs.misc[SOFTWARE_RECALL] = 5; + + // So we put games in your game so you can play game while you playing game! + player_struct.softs.misc[SOFTWARE_GAMES] = 255; + + chg_set_flg(INVENTORY_UPDATE); + chg_set_flg(VITALS_UPDATE); + mfd_force_update(); + + return (FALSE); +} + +uchar toggle_up_level_func(ushort keycode, uint32_t context, intptr_t data) { + message_info("Changing level!"); + go_to_different_level((player_struct.level + 1 + 15) % 15); + + return (TRUE); +} + +uchar toggle_down_level_func(ushort keycode, uint32_t context, intptr_t data) { + message_info("Changing level!"); + go_to_different_level((player_struct.level - 1 + 15) % 15); + + return (TRUE); +} + +#ifdef NOT_YET // + +#ifdef PLAYTEST + +#define camera_info message_info +uchar reset_camera_func(ushort keycode, uint32_t context, intptr_t data) { + extern uchar cam_mode; + extern cams objmode_cam, *motion_cam, player_cam; + + if ((uchar *)data) { + if (cam_mode != OBJ_STATIC_CAMERA) { + camera_info("cant toggle"); + return FALSE; + } + if (motion_cam != NULL) { + motion_cam = NULL; + camera_info("back to cam control"); + } else { + motion_cam = fr_camera_getdef(); + camera_info("back to obj control"); + } + } else { + camera_info("camera reset"); + cam_mode = OBJ_PLAYER_CAMERA; + fr_camera_setdef(&player_cam); + } + chg_set_flg(_current_3d_flag); + return (FALSE); +} + +uchar current_camera_func(ushort keycode, uint32_t context, intptr_t data) { + extern cams objmode_cam, *motion_cam; + extern uchar cam_mode; + fix cam_locs[6], *cam_ptr_hack; + + motion_cam = NULL; + // Not sure what to pass for last two params.... + switch ((uchar)data) { + case OBJ_STATIC_CAMERA: + if (cam_mode == OBJ_DYNAMIC_CAMERA) { + camera_info("cant go static"); + return FALSE; + } + motion_cam = fr_camera_getdef(); // note super sneaky fall through hack + camera_info("camera static"); + case OBJ_DYNAMIC_CAMERA: + fr_camera_modtype(&objmode_cam, CAMTYPE_ABS, CAMBIT_OBJ); + cam_ptr_hack = fr_camera_getpos(NULL); + memcpy(cam_locs, cam_ptr_hack, 6 * sizeof(fix)); + fr_camera_update(&objmode_cam, cam_locs, CAM_UPDATE_NONE, NULL); + if (motion_cam == NULL) + camera_info("camera dynamic"); + break; + case OBJ_CURRENT_CAMERA: + camera_info("current obj"); + fr_camera_modtype(&objmode_cam, CAMTYPE_OBJ, CAMBIT_OBJ); + fr_camera_update(&objmode_cam, (void *)current_object, CAM_UPDATE_NONE, NULL); + break; + } + cam_mode = (uchar)data; + fr_camera_setdef(&objmode_cam); + chg_set_flg(_current_3d_flag); + return (FALSE); +} + +uchar mono_log_on = FALSE; + +uchar log_mono_func(ushort keycode, uint32_t context, intptr_t data) { + if (mono_log_on) { + mono_logoff(); + message_info("Mono logging off."); + mono_log_on = FALSE; + } else { + mono_logon("monolog.txt", MONO_LOG_NEW, MONO_LOG_ALLWIN); + message_info("Mono logging on."); + mono_log_on = TRUE; + } + return (FALSE); +} + +uchar clear_transient_lighting_func(ushort keycode, uint32_t context, intptr_t data) { + int x, y; + MapElem *pme; + for (x = 0; x < MAP_XSIZE; x++) { + for (y = 0; y < MAP_YSIZE; y++) { + pme = MAP_GET_XY(x, y); + me_templight_flr_set(pme, 0); + me_templight_ceil_set(pme, 0); + } + } + message_info("Trans. light cleared"); + return (FALSE); +} + +uchar level_entry_trigger_func(ushort keycode, uint32_t context, intptr_t data) { + extern errtype do_level_entry_triggers(); + do_level_entry_triggers(); + message_info("Level entry triggered."); + return (FALSE); +} + +uchar convert_one_level_func(ushort keycode, uint32_t context, intptr_t data) { + extern errtype obj_level_munge(); +#ifdef TEXTURE_CRUNCH_HACK + extern errtype texture_crunch_init(); + + texture_crunch_init(); +#endif + obj_level_munge(); + return (TRUE); +} + + //#define CONVERT_FROM_OLD_RESID + //#define TEXTURE_CRUNCH_HACK + +#define NUM_CONVERT_LEVELS 16 + +uchar convert_all_levels_func(ushort keycode, uint32_t context, intptr_t data) { + int i; + char atoi_buf[10], fn[10], curr_fname[40], new_fname[40]; + errtype retval; + + extern Datapath savegame_dpath; + extern void edit_load_func(char *fn, uchar source, short level_num); + extern void edit_save_func(char *fn, uchar source, short level_num); + extern errtype obj_level_munge(); +#ifdef TEXTURE_CRUNCH_HACK + extern errtype texture_crunch_init(); + + texture_crunch_init(); +#endif + + // save off old level + edit_save_func("templevl.dat", 0, 0); + + // loop through the real levels + for (i = 0; i < NUM_CONVERT_LEVELS; i++) { + retval = OK; + // load level i + strcpy(fn, "level"); + strcat(fn, itoa(i, atoi_buf, 10)); + strcat(fn, ".dat"); + Spew(DSRC_EDITOR_Modify, ("fn = %s\n", fn)); + if (DatapathFind(&savegame_dpath, fn, curr_fname)) { +#ifdef CONVERT_FROM_OLD_RESID + retval = load_current_map(curr_fname, OLD_LEVEL_ID_NUM, &savegame_dpath); +#else + retval = load_current_map(curr_fname, LEVEL_ID_NUM, &savegame_dpath); +#endif + Spew(DSRC_EDITOR_Modify, ("convert_all trying to load %s\n", curr_fname)); + } else + retval = ERR_FOPEN; + + Spew(DSRC_EDITOR_Modify, ("curr_fname = %s\n", curr_fname)); + if (retval != OK) { + strcpy(new_fname, "R:\\prj\\cit\\src\\data\\"); + strcat(new_fname, fn); + retval = load_current_map(new_fname, LEVEL_ID_NUM, NULL); + Spew(DSRC_EDITOR_Modify, ("new_fname = %s\n", new_fname)); + } + + // Generate the report + obj_level_munge(); + Spew(DSRC_EDITOR_Modify, ("convert_all trying to save %s\n", fn)); + save_current_map(fn, LEVEL_ID_NUM, TRUE, TRUE); + } + + // reload original level + edit_load_func("templevl.dat", 0, 0); + + return (FALSE); +} + +#endif + +uchar invulnerable_func(ushort keycode, uint32_t context, intptr_t data) { + if (config_get_raw(CFG_HKEY_GO, NULL, 0)) { + player_invulnerable = !player_invulnerable; + if (player_invulnerable) + message_info("invulnerability on"); + else + message_info("invulnerability off"); + } else { + message_info("Winners don't use hotkeys"); + damage_player(50, EXPLOSION_FLAG, 0); + } + return (FALSE); +} + +uchar pacifist_func(ushort keycode, uint32_t context, intptr_t data) { + extern uchar pacifism_on; + pacifism_on = !pacifism_on; + if (pacifism_on) + message_info("pacifism on"); + else + message_info("pacifism off"); + return (FALSE); +} + +int pause_id; +uchar remove_pause_handler = FALSE; + +uchar pause_callback(uiEvent *, LGRegion *, void *) { return (TRUE); } + +uchar unpause_callback(uiEvent *, LGRegion *, void *) { return (TRUE); } + +#endif // NOT_YET + +uchar pause_game_func(ushort keycode, uint32_t context, intptr_t data) { + extern uchar game_paused, redraw_paused; + + game_paused = !game_paused; + CaptureMouse(!game_paused); + + extern LGCursor globcursor; + if (game_paused) uiPushGlobalCursor(&globcursor); + else uiPopGlobalCursor(); + + if (game_paused) { + redraw_paused = TRUE; + snd_kill_all_samples(); + audiolog_stop(); + return FALSE; + } + + mouse_look_unpause(); + + return TRUE; + /* KLC - not needed for Mac version + game_paused = !game_paused; + if (game_paused) + { + uiPushGlobalCursor(&globcursor); + uiInstallRegionHandler(inventory_region, UI_EVENT_MOUSE_MOVE, pause_callback, NULL, &pause_id); + uiGrabFocus(inventory_region, UI_EVENT_MOUSE_MOVE); + stop_digi_fx(); + redraw_paused=TRUE; + } + else + { + uiRemoveRegionHandler(inventory_region, pause_id); + uiReleaseFocus(inventory_region, UI_EVENT_MOUSE_MOVE); + uiPopGlobalCursor(); + } + */ +} + +/*KLC - not needed for Mac version +uchar unpause_game_func(ushort, uint32_t, intptr_t) +{ + extern uchar game_paused; + extern LGRegion *inventory_region; + + if (game_paused) + { + game_paused = !game_paused; + uiRemoveRegionHandler(inventory_region, pause_id); + uiReleaseFocus(inventory_region, UI_EVENT_MOUSE_MOVE|UI_EVENT_JOY); + uiPopGlobalCursor(); + } + return(FALSE); +} +*/ + +uchar toggle_mouse_look(ushort keycode, uint32_t context, intptr_t data) { + mouse_look_toggle(); + return (TRUE); +} + +//-------------------------------------------------------------------- +// For Mac version. Save the current game. +//-------------------------------------------------------------------- +/* +uchar save_hotkey_func(ushort keycode, uint32_t context, intptr_t data) { + if (global_fullmap->cyber) // Can't save in cyberspace. + { + message_info("Can't save game in cyberspace."); + return TRUE; + } + + if (music_on) // Setup the environment for doing Mac stuff. + MacTuneKillCurrentTheme(); + uiHideMouse(NULL); + SS_ShowCursor(); + + // CopyBits(&gMainWindow->portBits, &gMainOffScreen.bits->portBits, &gActiveArea, &gOffActiveArea, srcCopy, 0L); + + if (gIsNewGame) // Do the save thang. + { + status_bio_end(); + + // Fixme: Save game here! + + status_bio_start(); + } + + uiShowMouse(NULL); + if (music_on) + MacTuneStartCurrentTheme(); + + return TRUE; +} +*/ + +#ifdef NOT_YET // + +//#define CHECK_STATE_N_HOTKEY +#ifdef PLAYTEST +uchar check_state_func(ushort keycode, uint32_t context, intptr_t data) { + int avail_memory(int debug_src); + avail_memory(DSRC_TESTING_Test3); +#ifdef CHECK_STATE_N_HOTKEY + extern void check_state_every_n_seconds(); + check_state_every_n_seconds(); +#endif +#ifdef CORVIN_ZILM_HKEY + extern uchar CorvinZilm; + extern int watchcount; + MemStat pms; + watchcount = 0; + CorvinZilm = TRUE; + MemStats(&pms); +#endif + return (TRUE); +} + +uchar diffdump_game_func(ushort keycode, uint32_t context, intptr_t data) { + char goof[45]; + sprintf(goof, "diff=%d,%d,%d,%d\n", player_struct.difficulty[0], player_struct.difficulty[1], + player_struct.difficulty[2], player_struct.difficulty[3]); + message_info(goof); + return (TRUE); +} + +uchar toggle_difficulty_func(ushort keycode, uint32_t context, intptr_t data) { + ubyte which = (ubyte)data - 1; + + player_struct.difficulty[which]++; + player_struct.difficulty[which] %= 4; + return (TRUE); +} + +uchar toggle_ai_func(ushort keycode, uint32_t context, intptr_t data) { + extern uchar ai_on; + ai_on = !ai_on; + if (ai_on) + message_info("AI state on\n"); + else + message_info("AI state off\n"); + return (TRUE); +} + +uchar toggle_safety_net_func(ushort keycode, uint32_t context, intptr_t data) { + extern uchar safety_net_on; + safety_net_on = !safety_net_on; + if (safety_net_on) + message_info("Safety Net on\n"); + else + message_info("Safety Net off\n"); + return (TRUE); +} +#endif + +#ifdef NEW_RES_LIB_INSTALLED +uchar res_cache_usage_func(ushort keycode, uint32_t context, intptr_t data) { + extern long ResViewCache(uchar only_locks); + ResViewCache((bool)data); + return (TRUE); +} +#endif + +#pragma enable_message(202) + +#endif // NOT_YET diff --git a/engine/src/GameSrc/hud.c b/engine/src/GameSrc/hud.c new file mode 100644 index 0000000..1f860d9 --- /dev/null +++ b/engine/src/GameSrc/hud.c @@ -0,0 +1,648 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/hud.c $ + * $Revision: 1.67 $ + * $Author: mahk $ + * $Date: 1994/11/15 12:27:09 $ + */ + +#define __HUD_SRC + +#include +#include + +#include "cyber.h" +#include "hud.h" +#include "hudobj.h" +#include "player.h" +#include "gr2ss.h" +#include "colors.h" +#include "tools.h" +#include "gamescr.h" +#include "objsim.h" +#include "objgame.h" +#include "gamestrn.h" +#include "frtypes.h" +#include "faketime.h" +#include "fullscrn.h" +#include "olhext.h" +#include "cit2d.h" +#include "damage.h" + +#include "cybstrng.h" +#include "otrip.h" +#include "lvldata.h" +#include "diffq.h" + +#include "game_screen.h" //was screen.h? + +LGRect target_screen_rect; + +// --------------- +// HUD COLOR BANKS +// --------------- +#define RED_CYCLING_COLOR 0x7 + +ubyte hud_color_bank = 0; +ubyte hud_colors[HUD_COLOR_BANKS][HUD_COLORS_PER_BANK] = { + {WHITE, RED_BASE + 3, GREEN_BASE, 0x41, RED_CYCLING_COLOR}, + {0x38, GREEN_BASE, 0x41, WHITE, RED_CYCLING_COLOR}, + {GREEN_BASE, 0x41, WHITE, RED_BASE + 3, RED_CYCLING_COLOR}, +}; + +#ifndef STORE_CLIP +#define STORE_CLIP(a, b, c, d) \ + a = gr_get_clip_l(); \ + b = gr_get_clip_t(); \ + c = gr_get_clip_r(); \ + d = gr_get_clip_b() +#endif // !STORE_CLIP + +#ifndef RESTORE_CLIP +#define RESTORE_CLIP(a, b, c, d) gr_set_cliprect(a, b, c, d) +#endif // !RESTORE_CLIP + +#define HUDBUFSZ 64 + +// --------------------------------------- +// HEY, LETS CREATE A HUD LINE ABSTRACTION + +typedef struct _hudline { + int strid; // String ID to load string from if null. + ubyte color; // what color should it be? + ulong mask; // when should I draw? + char hudvar_id; // hud variable to display + short x; // coords to display at, default ordering if 0 + short y; // ditto + ubyte text; // What text should I have? + ulong time; // how long should I stay around? (0 = forever) +} HudLine; + +#define HUD_STRING_SIZE 48 +#define NUM_HUDLINE_BUFFERS 6 +char hudline_text[NUM_HUDLINE_BUFFERS][HUD_STRING_SIZE + 1]; + +#define X_MARGIN 10 +#define Y_MARGIN 7 +#define Y_STEP 7 +#define HUDLINE_HAS_TEXT(line) ((line)->text != 0) +#define HUDLINE_TEXT(line) (((line)->text) - 1) +#define HUDLINE_SET_TEXT(line, num) ((line)->text = (ubyte)((num) + 1)) +#define HUDLINE_X_CENTER -1 + +HudLine hud_lines[] = { + {REF_STR_GametimeLeft, 0, HUD_GAMETIME, 6}, + {REF_STR_Null, 0, HUD_MSGLINE, 5, HUDLINE_X_CENTER}, + {REF_STR_InfraredOn, 0, HUD_INFRARED, 0}, + {REF_STR_RadiationZone, 0, HUD_RADIATION, 0}, + {REF_STR_BiohazardZone, 0, HUD_BIOHAZARD, 0}, + {REF_STR_Null, 0, HUD_SHODOMETER, 1, X_MARGIN, 90}, + {REF_STR_HighFatigue, 1, HUD_FATIGUE, 0}, + {REF_STR_ShieldAbsorb, 0, HUD_SHIELD, 2, X_MARGIN, 80}, + {REF_STR_AbnormalGravity1, 0, HUD_ZEROGRAV, 3}, + {REF_STR_CyberFakeID, 0, HUD_FAKEID, 0}, + {REF_STR_CyberDecoy, 0, HUD_DECOY, 0}, + {REF_STR_CyberTurbo, 0, HUD_TURBO, 0}, + {REF_STR_CyberTime, 0, HUD_CYBERTIME, 4, X_MARGIN, 90}, + {REF_STR_CyberDanger, 4, HUD_CYBERDANGER, 0}, + {REF_STR_RadPoison, 1, HUD_RADPOISON, 19}, + {REF_STR_BioPoison, 1, HUD_BIOPOISON, 23}, + {REF_STR_Null, 1, HUD_ENVIROUSE, 8}, + // {REF_STR_Null, 1, HUD_ENVIROUSE, 9}, + {REF_STR_EnergyCritical, 1, HUD_BEAMHOT, 0}, + {REF_STR_EnergyUsage, 0, HUD_ENERGYUSE, 7}, +}; + +#define HUD_LINES (sizeof(hud_lines) / sizeof(hud_lines[0])) +#define FULLSCREEN_Y_OFFSET 40 + +#define HUDLINE_BUFFER(i) (hudline_text[HUDLINE_TEXT(&hud_lines[i])]) + +extern bool DoubleSize; + +// -------------- +// PROTOTYPES +// -------------- +uchar hud_color_bank_cycle(ushort keycode, uint32_t context, intptr_t data); +void hud_free_line(int i); +void hud_delete_line(int i); +void compute_hud_var(HudLine *hl); +void hud_update_lines(short x, short *y, short xwid, short ywid); + +// -------------- +// FUNCTIONS +// -------------- +uchar hud_color_bank_cycle(ushort keycode, uint32_t context, intptr_t data) { + hud_color_bank = (hud_color_bank + 1) % HUD_COLOR_BANKS; + return TRUE; +} + +void hud_free_line(int i) { + if (HUDLINE_HAS_TEXT(&hud_lines[i])) { + HUDLINE_BUFFER(i)[0] = '\0'; + hud_lines[i].text = 0; + } +} + +void hud_delete_line(int i) { + hud_free_line(i); + hud_lines[i].mask = 0; +} + +void compute_hud_var(HudLine *hl) { + extern short shield_absorb_perc; + + char *text = hudline_text[HUDLINE_TEXT(hl)]; + int len = strlen(text); + char *s = text + len; + + switch (hl->hudvar_id) { + case 1: + sprintf(text, get_temp_string(REF_STR_ShodanHud), + QUESTVAR_GET(0x10 + player_struct.level) * 100 / + player_struct.initial_shodan_vals[player_struct.level]); + break; + case 2: { + short use_perc = lg_min(shield_absorb_perc, 95); + sprintf(s, "%d %%", use_perc); + } break; + case 3: + sprintf(s, "%d", level_gamedata.hazard.bio * 100 / 4); + len += strlen(s); + s += strlen(s); + get_string(REF_STR_AbnormalGravity2, s, HUD_STRING_SIZE - len); + break; + case 4: + if (time_until_shodan_avatar > player_struct.game_time) + second_format((time_until_shodan_avatar - player_struct.game_time) / CIT_CYCLE, s); + else { + get_string(REF_STR_ShodanNow, s, HUD_STRING_SIZE - len); + gr_set_fcolor(RED_CYCLING_COLOR); // a good cycling color + } + break; + case 5: // message line hud. + { + extern char last_message[]; + strncpy(s, last_message, HUD_STRING_SIZE); + break; + } + case 6: // game time remaining hud + { + int secs = (MISSION_3_TICKS - player_struct.game_time) / CIT_CYCLE; + if (secs < 0) + secs = 0; + second_format(secs, s); + break; + } + case 7: // energy usage hud + { + extern short enviro_edrain_rate; + sprintf(s, "%d", player_struct.energy_spend + enviro_edrain_rate); + len += strlen(s); + s += strlen(s); + get_string(REF_STR_EnergyUnit, s, HUD_STRING_SIZE - len); + break; + } + case 8: // enviro suit absorption + { + extern short enviro_edrain_rate, enviro_absorb_rate; + sprintf(s, get_temp_string(REF_STR_EnviroAbsorb), enviro_absorb_rate); + len += strlen(s); + break; + } + case 9: // enviro suit energy drain + { + extern short enviro_edrain_rate, enviro_absorb_rate; + sprintf(s, get_temp_string(REF_STR_EnviroDrain), enviro_edrain_rate); + len += strlen(s); + break; + } + + case 16: // 16-23 are reserved for damage exposure notices + case 19: + case 23: { + int lvl = player_struct.hit_points_lost[hl->hudvar_id - 16] >> 1; + sprintf(s, "%d", lvl); + len += strlen(s); + s += strlen(s); + get_string(REF_STR_ExposureUnit, s, HUD_STRING_SIZE - len); + } + } +} + +void hud_update_lines(short x, short *y, short unused1, short unused2) { + int i; + short use_x, use_y; + + for (i = 0; i < HUD_LINES; i++) + if (hud_lines[i].mask & player_struct.hud_modes) { + uchar compute_text = FALSE; + if ((hud_lines[i].time != 0) && (hud_lines[i].time < player_struct.game_time)) { + hud_unset(hud_lines[i].mask); + continue; + } + gr_set_fcolor(hud_colors[hud_color_bank][hud_lines[i].color]); + if (!HUDLINE_HAS_TEXT(&hud_lines[i])) { + int j; + if (hud_lines[i].strid == 0) { + hud_delete_line(i); + continue; + } + for (j = 0; j < NUM_HUDLINE_BUFFERS; j++) + if (hudline_text[j][0] == '\0') { + HUDLINE_SET_TEXT(&hud_lines[i], j); + compute_text = TRUE; + break; + } + if (j >= NUM_HUDLINE_BUFFERS) { + WARN("%s: No room for one more hudline", __FUNCTION__); + continue; + } + + } else if (hud_lines[i].hudvar_id != 0) + compute_text = TRUE; + if (compute_text) { + get_string(hud_lines[i].strid, HUDLINE_BUFFER(i), HUD_STRING_SIZE); + compute_hud_var(&hud_lines[i]); + } + strip_newlines(HUDLINE_BUFFER(i)); + if (hud_lines[i].x == 0) + use_x = x; + else if (hud_lines[i].x == HUDLINE_X_CENTER) { + use_x = x + (SCREEN_VIEW_WIDTH - gr_string_width(HUDLINE_BUFFER(i))) / 2; + } else + use_x = hud_lines[i].x; + if (hud_lines[i].y == 0) { + use_y = *y; + *y += Y_STEP; + } else { + extern uchar full_game_3d; + use_y = hud_lines[i].y; + if (full_game_3d) + use_y += FULLSCREEN_Y_OFFSET; + } +#ifdef STEREO_SUPPORT + { + short temp; + if (convert_use_mode == 5) + use_x = 12; + ss_set_hack_mode(2, &temp); +#endif + res_draw_text_shadowed(RES_tinyTechFont, HUDLINE_BUFFER(i), use_x, use_y, TRUE); +#ifdef STEREO_SUPPORT + ss_set_hack_mode(0, &temp); + } +#endif + } else + hud_free_line(i); +} + +// ----------------------------------------- +// HUD COMPASS + +void hud_update_compass(short *y, short xmin, short xwid); + +#define HUD_COMPASS_ARC (80 * 256 / 360) +#define HALF_COMPASS_ARC (40 * 256 / 360) +#define HUD_COMPASS_OCT 32 +#define HUD_COMPASS_STEP 8 +#define COMPASS_TICKSCALE 4 +#define COMPASS_COLOR 2 + +void hud_update_compass(short *y, short xmin, short xwid) { + short ang, betw; + ubyte ver = player_struct.hardwarez[CPTRIP(NAV_HARD_TRIPLE)]; + ubyte pang = objs[player_struct.rep].loc.h - HALF_COMPASS_ARC; + gr_set_fcolor(hud_colors[hud_color_bank][COMPASS_COLOR]); + for (ang = 0; ang <= 255; ang += HUD_COMPASS_STEP) { + ubyte adj = ang - pang; + short x = (int)adj * xwid / HUD_COMPASS_ARC; + short w, h; + if (x >= xwid) + continue; + if (ang % HUD_COMPASS_OCT == 0) // draw an octant string + { + char s[4]; + get_string(REF_STR_DirectionAbbrev + ang / HUD_COMPASS_OCT, s, 4); + gr_string_size(s, &w, &h); + gr_set_fcolor(hud_colors[hud_color_bank][COMPASS_COLOR]); + draw_shadowed_string(s, x - w / 2 + xmin, *y, TRUE); + } else if (ver > 1) { + // draw line of height based one between-ness; + // find lowest set bit in ang + betw = (ang ^ (ang & (ang - 1))) / COMPASS_TICKSCALE; + gr_string_size(get_temp_string(REF_STR_DirectionAbbrev), &w, &h); + // KLC gr_set_fcolor(BLACK); + // KLC ss_vline(x+1+xmin,*y+((h-betw)/2),*y+((h+betw)/2)); + gr_set_fcolor(hud_colors[hud_color_bank][COMPASS_COLOR]); + ss_vline(x + xmin, *y + ((h - betw) / 2) - 1, *y + ((h + betw) / 2) - 1); + } + } + *y += Y_STEP; +} + +//---------------------------------------------- +// critter damage reports +// + +static struct _damage_report { + short damage; + ulong tstamp; + ObjID id; +} hud_critters[4]; + +#define NUM_HUD_CRITTERS (sizeof(hud_critters) / sizeof(struct _damage_report)) + +#define TSTAMP_CUTOFF (CIT_CYCLE / 2) // how old can a damage report get before we nuke it. + +#define TARG_EFF_VERSION 3 + +void hud_report_damage(ObjID target, byte dmglvl); +void draw_target_box(short xl, short yl, short xh, short yh); +void update_damage_report(struct _hudobj_data *dat, uchar reverse); + +void hud_report_damage(ObjID target, byte dmglvl) { + short i, best = 0; + ulong best_tstamp = 0xFFFFFFFF; + ubyte ver = player_struct.hardwarez[CPTRIP(TARG_GOG_TRIPLE)]; + if ((dmglvl != DAMAGE_NONE) && (dmglvl != DAMAGE_INEFFECTIVE) // ineffective flag - minman + && (dmglvl != DAMAGE_STUN) && (dmglvl != DAMAGE_TRANQ) && (ver < TARG_EFF_VERSION)) { + if (ver == 0) + return; + dmglvl = -1; + } + for (i = 0; i < NUM_HUD_CRITTERS; i++) { + if (hud_critters[i].id == target || hud_critters[i].id == OBJ_NULL) { + best = i; + break; + } + if (hud_critters[i].tstamp < best_tstamp) { + best = i; + best_tstamp = hud_critters[i].tstamp; + } + } + hudobj_set_id(target, TRUE); + hud_critters[best].tstamp = player_struct.game_time; + hud_critters[best].id = target; + hud_critters[best].damage = dmglvl; +} + +void draw_target_box(short xl, short yl, short xh, short yh) { + if (DoubleSize) { + xl *= 2; + yl *= 2; + xh *= 2; + yh *= 2; + } + short w = (xh - xl) / 5; + short h = (yh - yl) / 5; + gr_vline(xl, yl, yl + h); + gr_hline(xl, yl, xl + w); + gr_vline(xl, yh, yh - h); + gr_hline(xl, yh, xl + w); + gr_vline(xh, yh, yh - h); + gr_hline(xh, yh, xh - w); + gr_vline(xh, yl, yl + h); + gr_hline(xh, yl, xh - w); +} + +void update_damage_report(struct _hudobj_data *dat, uchar reverse) { + short i; + for (i = 0; i < NUM_HUD_CRITTERS; i++) { + if (dat->id == hud_critters[i].id) { + short w, h; + short x, y; + + char buf[80]; + struct _damage_report *rpt = &hud_critters[i]; + if (player_struct.game_time - rpt->tstamp > TSTAMP_CUTOFF) { + rpt->id = OBJ_NULL; + if (dat->id != player_struct.curr_target) { + hudobj_set_id(dat->id, FALSE); + dat->id = OBJ_NULL; + } + } + if (dat->id != player_struct.curr_target) { + draw_target_box(dat->xl, dat->yl, dat->xh, dat->yh); + } + get_string(REF_STR_TargetDamageBase + rpt->damage, buf, sizeof(buf)); + gr_string_size(buf, &w, &h); + x = (dat->xl + dat->xh - w) / 2; + y = dat->yl - h; + if (reverse) { + /* shock_hflip_in_place is mostly ASM. + extern void shock_hflip_in_place(grs_bitmap* bm); + grs_canvas gc; + grs_font* font = gr_get_font(); + gr_init_canvas(&gc,big_buffer,BMT_FLAT8,w+4,h+4); + gr_push_canvas(&gc); + gr_set_font(font); + gr_clear(0); + draw_shadowed_string(buf,2,2,TRUE); + gr_pop_canvas(); + shock_hflip_in_place(&gc.bm); + gc.bm.flags |= BMF_TRANS; + ss_bitmap(&gc.bm,x-1,y-2); + */ + } else { +#ifdef SVGA_SUPPORT + extern uchar shadow_scale; + uchar old_scale = shadow_scale; + shadow_scale = FALSE; +#endif + if (DoubleSize) { + x *= 2; + y = 2 * y + 1; // Text needed to come down a bit. + } + draw_shadowed_string(buf, x, y, TRUE); +#ifdef SVGA_SUPPORT + shadow_scale = old_scale; +#endif + } + break; + } + } +} + + //------------------------------------------- + // hud_do_objs() + // + // Deals with all hudobjs. + +#define NUM_TARG_FRAMES 5 + +ubyte targ_frame = NUM_TARG_FRAMES; + +#define TARG_COLOR 2 + +void hud_do_objs(short xtop, short ytop, short xwid, short ywid, uchar reverse) { + int i; + // KLC short a,b,c,d; + // KLC STORE_CLIP(a,b,c,d); + PointSetNull(target_screen_rect.ul); + gr_set_fcolor(hud_colors[hud_color_bank][TARG_COLOR]); + if (player_struct.curr_target == OBJ_NULL) + targ_frame = NUM_TARG_FRAMES; + + // KLC safe_set_cliprect(0,0,xwid,ywid); + gr_set_font((grs_font *)ResLock(RES_tinyTechFont)); + for (i = 0; i < current_num_hudobjs; i++) { + struct _hudobj_data *dat = &hudobj_vec[i]; + if (dat->id == OBJ_NULL) + continue; + update_damage_report(dat, reverse); + if (dat->id == OBJ_NULL) + continue; + if (dat->id == player_struct.curr_target) { + short w = (dat->xh - dat->xl) / 5; + short h = (dat->yh - dat->yl) / 5; + draw_target_box(dat->xl - targ_frame * w, dat->yl - targ_frame * h, dat->xh + targ_frame * w, + dat->yh + targ_frame * h); + if (targ_frame > 0) + targ_frame--; + dat->id = OBJ_NULL; + target_screen_rect.ul = MakePoint(dat->xl, dat->yl); + target_screen_rect.lr = MakePoint(dat->xh, dat->yh); + RECT_MOVE(&target_screen_rect, MakePoint(xtop, ytop)); + // Do other targeting stuff here. + } + } + current_num_hudobjs = 0; + if (player_struct.curr_target != OBJ_NULL) + hudobj_set_id(player_struct.curr_target, TRUE); + ResUnlock(RES_tinyTechFont); + // KLC RESTORE_CLIP(a,b,c,d); +} + +// ------------------------------------------ +// hud_update() + +errtype hud_update(uchar redraw_whole, frc *context) { + extern uchar fullscrn_vitals; + extern uchar fullscrn_icons; + fauxrend_context *fc = (fauxrend_context *)context; + short y = Y_MARGIN; + short x = X_MARGIN; + short xwid = fc->xwid; + short a, b, c, d; + STORE_CLIP(a, b, c, d); + safe_set_cliprect(0, 0, fc->xwid, fc->ywid); + gr_set_font((grs_font *)ResLock(RES_tinyTechFont)); + + /* TEMP This is where we display the frame counter, if it is on. + extern Boolean gShowFrameCounter; + if (gShowFrameCounter) + { + static long numFrames = 0; + static long nextTime = 0; + static char msg[64] = "\0\0\0"; + int x, y; + + if (nextTime == 0) + nextTime = *tmd_ticks + 560; // Update every 2 seconds + else if (*tmd_ticks > nextTime) + { + fix_sprint(msg, fix_div(fix_make(numFrames, 0), fix_make(2,0))); + + nextTime = *tmd_ticks + 560; + numFrames = 0; + } + else + numFrames++; + + if (msg[0]) + { + if (full_game_3d) + { + x = 280; + y = 130; + } + else + { + x = 240; + y = 100; + } + gr_set_fcolor(76); + draw_shadowed_string(msg, x, y, TRUE); + } + } + // END TEMP + */ + if (full_game_3d && fullscrn_vitals) { + y = SCREEN_VIEW_Y + Y_MARGIN; + } + if (full_game_3d && fullscrn_icons) { + xwid = SCREEN_VIEW_WIDTH; + x = SCREEN_VIEW_X + X_MARGIN; + } + if ((!global_fullmap->cyber) && (player_struct.hud_modes & HUD_COMPASS)) + hud_update_compass(&y, x, xwid); + hud_update_lines(x, &y, xwid, fc->ywid); + + if (olh_active) + olh_do_hudobjs(fc->xtop, fc->ytop); + hud_do_objs(fc->xtop, fc->ytop, fc->xwid, fc->ywid, FALSE); + + ResUnlock(RES_tinyTechFont); + RESTORE_CLIP(a, b, c, d); + return (OK); +} + +errtype hud_set(ulong hud_modes) { + player_struct.hud_modes |= hud_modes; + + return (OK); +} + +errtype hud_unset(ulong hud_modes) { + int i; + player_struct.hud_modes &= ~hud_modes; + + // Clear any times associated + for (i = 0; i < HUD_LINES; i++) { + if (hud_lines[i].mask & hud_modes) { + hud_lines[i].time = 0; + } + } + return (OK); +} + +errtype hud_set_time(ulong hud_modes, ulong ticks) { + int i; + hud_set(hud_modes); + for (i = 0; i < HUD_LINES; i++) { + if (hud_lines[i].mask & hud_modes) { + hud_lines[i].time = player_struct.game_time + ticks; + } + } + return (OK); +} + +void hud_shutdown_lines(void) { + int i; + for (i = 0; i < HUD_LINES; i++) { + if (hud_lines[i].time > 0) + hud_unset(hud_lines[i].mask); + } +} + +// -------------------------------------------------- +// HUD WARE/MFD +// -------------------------------------------------- diff --git a/engine/src/GameSrc/hudobj.c b/engine/src/GameSrc/hudobj.c new file mode 100644 index 0000000..c12e06e --- /dev/null +++ b/engine/src/GameSrc/hudobj.c @@ -0,0 +1,52 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include "hudobj.h" +#include "objects.h" +#include "objapp.h" + +// ------- +// GLOBALS +// ------- + +ushort hudobj_classes[NUM_CLASSES]; + +struct _hudobj_data hudobj_vec[NUM_HUDOBJS]; + +ubyte current_num_hudobjs = 0; + +// ------------- +// API FUNCTIONS +// ------------- + +void hudobj_set_subclass(ubyte obclass, ubyte subclass, uchar val) { + ushort mask = (subclass == HUDOBJ_ALL_SUBCLASSES) ? 0xFFFF : (1 << subclass); + if (val) + hudobj_classes[obclass] |= mask; + else + hudobj_classes[obclass] &= ~mask; +} + +void hudobj_set_id(short id, uchar val) { + if (id == OBJ_NULL) + return; + if (val) + objs[id].info.inst_flags |= HUDOBJ_INST_FLAG; + else + objs[id].info.inst_flags &= ~HUDOBJ_INST_FLAG; +} diff --git a/engine/src/GameSrc/init.c b/engine/src/GameSrc/init.c new file mode 100644 index 0000000..d820820 --- /dev/null +++ b/engine/src/GameSrc/init.c @@ -0,0 +1,856 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/init.c $ + * $Revision: 1.185 $ + * $Author: xemu $ + * $Date: 1994/11/28 06:38:07 $ + */ + +#include +#include + +#include "Shock.h" +#include "InitMac.h" +#include "ShockBitmap.h" + +#include "criterr.h" +#include "cybmem.h" +#include "cybrnd.h" +#include "drugs.h" +#include "frprotox.h" +#include "gamepal.h" +#include "gamestrn.h" +#include "gamescr.h" +#include "init.h" +#include "input.h" +#include "map.h" +#include "mfdext.h" +#include "musicai.h" +#include "objects.h" +#include "objsim.h" +#include "palfx.h" +#include "physics.h" +#include "player.h" +#include "render.h" +#include "rendtool.h" +#include "sdl_events.h" +#include "sideicon.h" +#include "textmaps.h" +#include "tickcount.h" +#include "tools.h" +#include "gamerend.h" +#include "mainloop.h" +#include "game_screen.h" +#include "shodan.h" +#include "fullscrn.h" +#include "frcamera.h" +#include "dynmem.h" +#include "vitals.h" +#include "view360.h" + +#include "shockolate_version.h" // for system shock version number + +#include "Modding.h" + +/* +#define AIL_SOUND +#include "tminit.h" +#include "mlimbs.h" +#include "fault.h" +#include "dbg.h" +#include "config.h" +#include "memstat.h" +#include "lgprntf.h" + +#include "anim.h" +#include "dpaths.h" +#include "setup.h" +#include "cutscene.h" +#include "bugtrak.h" +#include "btfunc.h" +#include "ai.h" + +// TOTALLY TEMPORARY +#include "textmaps.h" + +#include "obj3d.h" // for 3d base +#include "citmat.h" // for materials base +#include "version.h" // for system shock version number + +#ifdef STARTUP_MEMSTATS +#include "mprintf.h" +#endif + +#include "wsample.h" + +#define CFG_LEVEL_VAR "LEVEL" +#define CFG_DEBUG_VAR "mono_debug" +#define CFG_NOFAULT_VAR "fault_off" +#define CFG_MEMCHECK_VAR "mem_check" +#define CFG_BUGTRAK_VAR "bugtrak" +#define CFG_BUGTRAK_RECORD_VAR "bugtrak_record" +#define CFG_ARCHIVE_VAR "archive" +#define CFG_SELFRUN_VAR "selfrun" +#define CFG_NORUN_VAR "norun" +#define CFG_HEAPCHECK_VAR "heap_checking" +#define CFG_EDMS_SANITY_VAR "edms_sanity" +#define CFG_OPTION_CURSOR_VAR "option_cursor_check" +#define CFG_SERIAL_SECRET "serial_mprint" +*/ +#define ORIGIN_DISPLAY_TIME (60 * 3) +#define LG_DISPLAY_TIME (60 * 3) +#define TITLE_DISPLAY_TIME (60 * 3) +#define MIN_WAIT_TIME (60) + +//void DrawSplashScreen(short id, Boolean fadeIn); +void PreloadGameResources(void); +errtype init_gamesys(); +errtype free_gamesys(void); +errtype init_load_resources(); +errtype init_3d_objects(); +errtype obj_3d_shutdown(); +void init_popups(); +uchar pause_for_input(ulong wait_time); + +errtype init_pal_fx(); +void byebyemessage(void); +/* +errtype init_kb(); +errtype init_debug(); + +extern void load_weapons_data(void); +extern errtype setup_init(void); +extern uchar toggle_heap_check(short keycode, ulong context, void *data); +*/ + +errtype amap_init(void); +// extern long old_ticks; + +/*¥¥ +int global_timer_id; +extern int mlimbs_peril; +*/ +uchar init_done = FALSE; +uchar clear_player_data = TRUE; +uchar objdata_loaded = FALSE; + +/* +extern void (*enter_modes[])(void); + +extern int KeyGetch(void); +extern void start_intro_sound(void); +extern void start_setup_sound(void); +extern void end_intro_sound(void); +extern void end_setup_sound(void); + +extern void init_watchpoints(void); +*/ + +uchar real_archive_fn[64]; +/* +#define SPLASH_RES_FILE "splash.rsrc" +#ifndef EDITOR +#define MIN_SPLASH_TIME 1000 +#else +#define MIN_SPLASH_TIME 0 +#endif +*/ +MemStack temp_memstack; +#define TEMP_STACK_SIZE (16 * 1024) + +uchar pause_for_input(ulong wait_time) { + bool gotInput = false; + + uint32_t wait_until = TickCount() + wait_time; + while (!gotInput && (TickCount() < wait_until)) { + pump_events(); + SDLDraw(); + } + + // return if we got input + return (gotInput); +} + +extern char which_lang; +int mfdart_res_file; +//#ifdef DEMO +// uchar *mfdart_files[] = { "mfdart.rsrc", "mfdart.rsrc", "mfdart.rsrc" }; +//#else +char *mfdart_files[] = {"res/data/mfdart.res", "res/data/mfdfrn.res", "res/data/mfdger.res"}; +//#endif + +/* MLA - don't need these +extern void *CitMalloc(int n); +extern void CitFree(void *p); +*/ + +#define PALETTE_SIZE 768 +uchar ppall[PALETTE_SIZE]; + +//------------------------------------------------- +// Initialize everything! +//------------------------------------------------- +void init_all(void) { + /* + char buf[256]; + char norun[1]; + extern char savegame_dir[50]; + extern Datapath savegame_dpath; */ + ulong pause_time; + int i; + bool speed_splash = FALSE; + /* + + uchar dofault = TRUE; + int dummy_count; + int data[1]; + int cnt; + extern void init_config(int argc,char* argv[]); + extern errtype terrain_palette_popup(void); + extern uchar cam_mode; + */ + + start_mem = slorkatron_memory_check(); + if (start_mem < MINIMUM_GAME_THRESHOLD) + critical_error(CRITERR_MEM | 1); + + // register the bye message + atexit(byebyemessage); + + ResInit(); + // Where are these defined? + // restemp_buffer = ALTERNATE_BUFFER; + // restemp_buffer_size = ALTERNATE_BUFFER_SIZE; + + /* + init_early_dpaths(); + init_config(argc,argv); + if (config_get_raw(CFG_NORUN_VAR,norun,1)) + { + if (norun[0]=='1') + critical_error(CRITERR_EXEC|1); + } + */ + // Spew(DSRC_SYSTEM_Memory, ("initial memory: %d\n",start_mem)); + /* + dofault = !config_get_raw(CFG_NOFAULT_VAR,NULL,0); + DBG(DSRC_SYSTEM_FaultDisable,{ dofault = FALSE;}); + if (dofault) + ex_startup(EXM_ALL); + */ + // KLC - this is done in uiInit() [in UI:EVENT.C] kb_startup(NULL); + // kb_set_state(0x54,KBA_SIGNAL); + + // Use our own buffer for LZW + LzwSetBuffer((void *)big_buffer, BIG_BUFFER_SIZE); + + // use it for rsd unpacking too....this might be fill'd with danger + gr_set_unpack_buf(big_buffer); + + // set up temporary memory stuff + temp_memstack.baseptr = big_buffer + sizeof(big_buffer) - TEMP_STACK_SIZE; + temp_memstack.sz = TEMP_STACK_SIZE; + MemStackInit(&temp_memstack); + TempMemInit(&temp_memstack); + + // initialize random seeds + rnd_init(); + + // initialize strings + init_strings(); + // KLC - not in Mac version + // Initialize the Animation system + // AnimInit(); + + // Initialize low-level keyboard and mouse input. KLC - taken out of uiInit. + mouse_init(grd_cap->w, grd_cap->h); + kb_init(NULL); + + // Initialize map + DEBUG("- Map Startup"); + map_init(); + + DEBUG("- Physics Startup"); + physics_init(); + // KLC - done in InitMac.c. + // atexit(free_all); + + DEBUG("- Load Resources"); + init_load_resources(); + + DEBUG("- 3d Objects Startup"); + init_3d_objects(); + + DEBUG("- Popups Startup"); + init_popups(); + + DEBUG("- Gamesys Startup"); + init_gamesys(); + + // Start up the 3d... + DEBUG("- Renderer Startup"); + fr_startup(); + game_fr_startup(); + + // initialize renderer + DEBUG("- SDL Startup"); + InitSDL(); + + // Initialize the main game screen + DEBUG("- Main game screen Startup"); + region_begin_sequence(); + + DEBUG("- Sound startup"); + snd_startup(); + snd_start_digital(); + music_init(); + digifx_init(); + + // Initialize the palette effects (for fades and color cycling) + DEBUG("- PAL startup"); + palfx_init(); + + // Initialize animation callbacks + { + extern void init_animlist(); + init_animlist(); + } + + // Play the Origin intro movie. + { + // FSSpec fSpec; + // FSMakeFSSpec(gDataVref, gDataDirID, "Origin", &fSpec); + // PlayStartupMovie(&fSpec, 0, 0); + } + + DEBUG("- Screen init"); + screen_init(); + fullscreen_init(); + amap_init(); + init_side_icon_popups(); // KLC - new call. + + DEBUG("- Input init"); + init_input(); // KLC - moved here, after uiInit (in screen_init) + + uiHideMouse(NULL); // KLC - added to hide mouse cursor + + DEBUG("- VR init"); + view360_init(); + // KLC - no longer needed olh_init(); + + // Put up splash screen for US! + DEBUG("- Make splash"); + uiFlush(); + + // DrawSplashScreen(REF_IMG_bmOriginSplash, TRUE); + // SDLDraw(); + + // Set the wait time for our screen + pause_time = TickCount(); + if (!speed_splash) + pause_time += LG_DISPLAY_TIME; + else + pause_time += MIN_WAIT_TIME; + + DEBUG("- Start vitals"); + status_vitals_start(); + + for (i = 0; i < NUM_LOADED_TEXTURES; i++) + loved_textures[i] = i; + + DEBUG("- Gamerenderer startup"); + gamerend_init(); + + DEBUG("- Cameras startup"); + init_hack_cameras(); + + DEBUG("- End Sequence"); + region_end_sequence(FALSE); + + DEBUG("- Lighting startup"); + Init_Lighting(); + + // set default difficulty levels for player + for (i = 0; i < 4; i++) + player_struct.difficulty[i] = 2; + + // KLC - no config stuff for Mac version + // if (!config_get_value(CFG_ARCHIVE_VAR, CONFIG_STRING_TYPE, &real_archive_fn, &dummy_count)) + // BlockMove(ARCHIVE_FNAME, real_archive_fn, 20); + + // KLC init_kb(); + // KLC DbgInstallGetch(KeyGetch); + + // Start out game with high peril, to sound cool... + mlimbs_peril = 95; + + // LG splash screen wait + // pause_for_input(pause_time); + // speed_splash = TRUE; + + init_pal_fx(); + + // Put up title screen + uiFlush(); + + // Preload and lock resources that are used often in the game. + + PreloadGameResources(); + + // Draw something to avoid startup flash + gr_clear(0x00); + SDLDraw(); + + // set the wait time for system shock title screen + + pause_time = TickCount(); + + if (!speed_splash) + pause_time += TITLE_DISPLAY_TIME; + else + pause_time += MIN_WAIT_TIME; + + if ((_current_loop != SETUP_LOOP) && (_current_loop != CUTSCENE_LOOP)) { + //¥¥ for now object_data_load(); + + // gr_clear(0xFF); + // gr_set_pal(0, 256, ppall); + } + + // perhaps shouldnt do this if we are going to go into editor... + // fade down for last time + if (_current_loop != EDIT_LOOP) { + // pause_for_input(TickCount() + 10); + // if (pal_fx_on) + // palfx_fade_down(); + } + + uiFlush(); + init_done = TRUE; +} + +/* +//----------------------------------------------------------- +// Draw a splash screen in its associated color table. +//----------------------------------------------------------- +void DrawSplashScreen(short id, Boolean fadeIn) { + byte pal_id; + uchar savep[768]; + grs_bitmap bits; + // CTabHandle ctab; + extern void finish_pal_effect(byte id); + extern byte palfx_start_fade_up(uchar * new_pal); + + // gr_clear(0xFF); + + // First, clear the screen and load in the color table for this picture. + // gr_clear(0xFF); + ctab = GetCTable(id); // Get the pict's +CLUT if (ctab) + { + BlockMove((**(ctab)).ctTable, (**(gMainColorHand)).ctTable, 256 * sizeof(ColorSpec)); + SetEntries(0, 255, (**(gMainColorHand)).ctTable); + ResetCTSeed(); + DisposCTable(ctab); + +#ifdef DO_FADES + if (fadeIn) // Get it in a form for +palette fade + { + mac_get_pal(0, 256, savep); + gr_set_pal(0, 256, savep); + } +#endif + LoadPictShockBitmap(&gMainOffScreen, id); + +#ifdef DO_FADES + if (fadeIn) + pal_id = palfx_start_fade_up(savep); +#endif + gr_init_bm(&bits, (uchar *)gMainOffScreen.Address, BMT_FLAT8, 0, 640, 480); + gr_bitmap(&bits, 0, 0); + +#ifdef DO_FADES + if (fadeIn) + finish_pal_effect(pal_id); +#endif + } +} +*/ + +void PreloadGameResources(void) { + // Images + ResLock(RES_gamescrGfx); + + // Fonts + ResLock(RES_tinyTechFont); + ResLock(RES_doubleTinyTechFont); + ResLock(RES_citadelFont); + ResLock(RES_mediumLEDFont); + + // Strings + ResLock(RES_objlongnames); + ResLock(RES_traps); + ResLock(RES_words); + ResLock(RES_texnames); + ResLock(RES_texuse); + ResLock(RES_inventory); + ResLock(RES_objshortnames); + ResLock(RES_HUDstrings); + ResLock(RES_lognames); + ResLock(RES_messages); + ResLock(RES_plotware); + ResLock(RES_screenText); + ResLock(RES_cyberspaceText); + ResLock(RES_accessCards); + ResLock(RES_miscellaneous); + ResLock(RES_games); +} + +void object_data_flush(void) { + if (!objdata_loaded) + return; + + free_dynamic_memory(DYNMEM_ALL); + objdata_loaded = FALSE; + obj_shutdown(); +} + +errtype object_data_load(void) { + LGRect bounds; + extern cams objmode_cam; + + // char buf[256]; + // MemStat data; + // extern Datapath savegame_dpath; + + if (objdata_loaded) + return (ERR_NOEFFECT); + + // if(MemStats(&data)) + // { + // Warning(("Heap is bad before starting object_data_load\n")); + // critical_error(CRITERR_MEM|7); + // } + // mprintf("Hey we have %d memory avail before object data load\n", data.free.sizeTot); + + // KLC - Mac cursor showing at this time begin_wait(); + + // Initialize DOS (Doofy Object System) + DEBUG("ObjsInit"); + ObjsInit(); + + obj_init(); + + // initialize player struct + DEBUG("Initialize player"); + if (clear_player_data) + init_player(&player_struct); + clear_player_data = TRUE; + + // Start up some subsystems + DEBUG("init mfd"); + init_newmfd(); + + /* + // strcpy(buf,"DATA\\"); + strcpy(buf,""); + // NOTE: is there any other loop we start in which doesnt overwrite the map + // if not + */ + bounds.ul.x = bounds.ul.y = 0; + bounds.lr.x = global_fullmap->x_size; + bounds.lr.y = global_fullmap->y_size; + + DEBUG("process tilemap"); + rendedit_process_tilemap(global_fullmap, &bounds, TRUE); + + // Make the objmode camera.... + DEBUG("create camera"); + fr_camera_create(&objmode_cam, CAMTYPE_OBJ, player_struct.rep, NULL, NULL); + + DEBUG("load_dynamic_memory"); + objdata_loaded = TRUE; + load_dynamic_memory(DYNMEM_ALL); + + // KLC end_wait(); + return (OK); +} + +#ifdef DUMMY ///¥ + +errtype init_kb() { + // Keyboard frobbing + if (config_get_raw(CHAINING_VAR, NULL, 0)) + kb_set_flags(kb_get_flags() | KBF_CHAIN); + kb_set_state(0x16, KBA_REPEAT); + kb_set_state(0x17, KBA_REPEAT); + kb_set_state(0x18, KBA_REPEAT); + kb_set_state(0x1A, KBA_REPEAT); + kb_set_state(0x1B, KBA_REPEAT); + kb_set_state(0x24, KBA_REPEAT); + kb_set_state(0x25, KBA_REPEAT); + kb_set_state(0x26, KBA_REPEAT); + kb_set_state(0x09, KBA_REPEAT); + kb_set_state(0x33, KBA_REPEAT); + kb_set_state(0x32, KBA_REPEAT); + kb_set_state(0x34, KBA_REPEAT); + return (OK); +} + +#endif // ¥ DUMMY + +errtype load_da_palette(void) { + int pal_file; + + pal_file = ResOpenFile("res/data/gamepal.res"); + if (pal_file < 0) + critical_error(CRITERR_RES | 4); + ResExtract(RES_gamePalette, FORMAT_RAW, ppall); + ResCloseFile(pal_file); + gr_set_pal(0, 256, ppall); + + return (OK); +} + +errtype init_pal_fx() { + int i; + FILE *ipalHdl; + + i = 1; + + // gr_clear(0xFF); + + // Initialize the palette + load_da_palette(); + + // if we arent doing tlucs from a file + gr_alloc_tluc8_spoly_table(16); + + // alloc ipal after the above - since we free ipal earlier + // prevents fragmenting a bit + shock_alloc_ipal(); + // ipalHdl = shock_alloc_ipal(); + + for (i = 0; i < 16; i++) + gr_init_tluc8_spoly_table(i, fix_make(0, 0xe000), fix_make(0, 0x8000), gr_bind_rgb(255, 64, 64), + gr_bind_rgb(127 + (i << 3), 127 + (i << 3), 127 + (i << 3))); + +#ifdef OLD_TLUCS + gr_make_tluc8_table(255, fix_make(0, 0x8000), fix_make(0, 0x8000), gr_bind_rgb(255, 0, 0)); + gr_make_tluc8_table(254, fix_make(0, 0x8000), fix_make(0, 0x8000), gr_bind_rgb(0, 255, 0)); + gr_make_tluc8_table(253, fix_make(0, 0x8000), fix_make(0, 0x8000), gr_bind_rgb(0, 0, 255)); + gr_make_tluc8_table(252, fix_make(0, 0x8000), fix_make(0, 0x8000), gr_bind_rgb(80, 80, 80)); + gr_make_tluc8_table(251, fix_make(0, 0x8000), fix_make(0, 0x8000), gr_bind_rgb(255, 255, 255)); + gr_make_tluc8_table(250, fix_make(0, 0x8000), fix_make(0, 0x8000), gr_bind_rgb(0, 0, 0)); +#else + +#define CIT_FOG_OPAC fix_make(0, 0x3000) +#define CIT_FOG_PURE fix_make(0, 0x6000) + +#define CIT_FORCE_OPAC fix_make(0, 0x5000) +#define CIT_FORCE_PURE fix_make(0, 0x8000) + + gr_make_tluc8_table(249, CIT_FOG_OPAC, CIT_FOG_PURE, gr_bind_rgb(255, 0, 0)); + gr_make_tluc8_table(250, CIT_FOG_OPAC, CIT_FOG_PURE, gr_bind_rgb(0, 255, 0)); + gr_make_tluc8_table(251, CIT_FOG_OPAC, CIT_FOG_PURE, gr_bind_rgb(0, 0, 255)); + gr_make_tluc8_table(248, CIT_FOG_OPAC, CIT_FOG_PURE, gr_bind_rgb(170, 170, 170)); + gr_make_tluc8_table(252, CIT_FOG_OPAC, CIT_FOG_PURE, gr_bind_rgb(240, 240, 240)); + gr_make_tluc8_table(247, CIT_FOG_OPAC, CIT_FOG_PURE, gr_bind_rgb(120, 120, 120)); + + gr_make_tluc8_table(255, CIT_FORCE_OPAC, CIT_FORCE_PURE, gr_bind_rgb(255, 0, 0)); + gr_make_tluc8_table(254, CIT_FORCE_OPAC, CIT_FORCE_PURE, gr_bind_rgb(0, 255, 0)); + gr_make_tluc8_table(253, CIT_FORCE_OPAC, CIT_FORCE_PURE, gr_bind_rgb(0, 0, 255)); +#endif + + { + extern uchar _g3d_enable_blend; + uchar tmppal_lower[32 * 3]; + extern uchar ppall[]; // pointer to main shadow palette + + _g3d_enable_blend = (start_mem >= BLEND_THRESHOLD); + if (_g3d_enable_blend) { + LG_memcpy(tmppal_lower, ppall, 32 * 3); + LG_memset(ppall, 0, 32 * 3); + gr_set_pal(0, 256, ppall); + + gr_init_blend(1); // we want 2 tables, really, basically, and all + + LG_memcpy(ppall, tmppal_lower, 32 * 3); + gr_set_pal(0, 256, ppall); + } + } + + // fclose(ipalHdl); // reclaim the memory, fight the power + grd_ipal = NULL; // hack hack hack + + // Spew(DSRC_EDITOR_Screen, ("Loaded the palette...\n")); + return (OK); +} + +void shock_alloc_ipal() { + + // CC: Make sure we always allocate an ipal first + gr_alloc_ipal(); + + FILE *temp = fopen_caseless("res/data/ipal.dat", "rb"); + if (temp == NULL) { + ERROR("Failed to open ipal.dat"); + return; + } + fread(grd_ipal, 1, 32768, temp); + return; + // return(temp); +} + +errtype init_gamesys() { + // Load data for weapons, drugs, wares + drugs_init(); + init_all_side_icons(); + // KLC wares_init(); doesn't do anything. leave it out. + game_sched_init(); + + return (OK); +} + +errtype free_gamesys(void) { + game_sched_free(); + + return (OK); +} + + // Okay, this should all move to somewhere more real, but I really + // can't put it in the right place until the new 3d regime comes into + // being + +#define MAX_CUSTOMS 30 + +errtype init_3d_objects() { + vx_init(16); + return (OK); +} + +errtype obj_3d_shutdown() { + vx_close(); + return (OK); +} + +errtype init_load_resources() { + // Open the screen resource stuff + if (ResOpenFile("res/data/gamescr.res") < 0) + critical_error(CRITERR_RES | 1); + + // Open the appropriate mfd art file + if ((mfdart_res_file = ResOpenFile("res/data/mfdart.res")) < 0) + critical_error(CRITERR_RES | 2); + + // Open the 3d objects + if (ResOpenFile("res/data/obj3d.res") < 0) + critical_error(CRITERR_RES | 9); + + // Open the Citadel materials file + if (ResOpenFile("res/data/citmat.res") < 0) + critical_error(CRITERR_RES | 9); + + // Open the Digital sound FX file + if (ResOpenFile("res/data/digifx.res") < 0) + critical_error(CRITERR_RES | 9); + + // Go load the additional mod files + LoadModFiles(); + + return (OK); +} + +#ifdef DUMMY // later + +errtype init_debug() { + errtype retval = OK; + return (retval); +} + +errtype init_editor_gadgets() { return (OK); } + +void free_all(void) { + extern void shutdown_config(void); + extern uchar cit_success; + extern void map_free(void); + extern void music_free(void); + extern void free_dpaths(void); + extern view360_shutdown(void); + + _MARK_("free_all"); + + Spew(DSRC_TESTING_Test6, ("shutdown - 1\n")); + tm_close(); + tm_remove_process(global_timer_id); + Spew(DSRC_TESTING_Test6, ("shutdown - 2\n")); + game_fr_shutdown(); + cutscene_free(); + map_free(); + music_free(); + Spew(DSRC_TESTING_Test6, ("shutdown - 3\n")); + player_shutdown(); + Spew(DSRC_TESTING_Test6, ("shutdown - 4\n")); + if (cit_success) + free_dynamic_memory(DYNMEM_ALL); + Spew(DSRC_TESTING_Test6, ("shutdown - 5\n")); + mlimbs_shutdown(); // should shutdown music here too...? + + snd_shutdown(); + Spew(DSRC_TESTING_Test6, ("shutdown - 6\n")); + obj_3d_shutdown(); + Spew(DSRC_TESTING_Test6, ("shutdown - 7\n")); + object_data_flush(); + Spew(DSRC_TESTING_Test6, ("shutdown - 8\n")); + fr_shutdown(); + Spew(DSRC_TESTING_Test6, ("shutdown - 9\n")); + screen_shutdown(); + view360_shutdown(); + status_vitals_end(); + Spew(DSRC_TESTING_Test6, ("shutdown - 10\n")); + shutdown_input(); + Spew(DSRC_TESTING_Test6, ("shutdown - 11\n")); + palette_shutdown(); + // free_dpaths(); + Spew(DSRC_TESTING_Test6, ("shutdown - 12\n")); + shutdown_config(); + Spew(DSRC_TESTING_Test6, ("shutdown - 13\n")); + + Spew(DSRC_TESTING_Test6, ("shutdown - final\n")); + + _MARK_("free_all done"); +} + +#endif // DUMMY + +// when you need those arms around you, you wont find my arms around you +// im going im going im going im gone +void byebyemessage(void) { + extern uchar cit_success; + if (cit_success) +#ifdef DEMO + printf("Thanks for playing the System Shock CD Demo %s.\n", SYSTEM_SHOCK_VERSION); +#else + printf("Thanks for playing System Shock %s.\n", SHOCKOLATE_VERSION); +#endif + else + printf("Our system has been shocked!!!\b But remember to Salt The Fries\n"); +} diff --git a/engine/src/GameSrc/input.c b/engine/src/GameSrc/input.c new file mode 100644 index 0000000..7c0c4b5 --- /dev/null +++ b/engine/src/GameSrc/input.c @@ -0,0 +1,3360 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/input.c $ + * $Revision: 1.293 $ + * $Author: jaemz $ + * $Date: 1994/11/23 00:16:31 $ + */ + +// lets get this somewhere else so we can get it in the manual or something, and not get warnings.. +#ifdef SPACEBALL_SUPPORT +static char sbcopy[] = "Spaceball Interface Copyright 1994 Spaceball Technologies Inc."; +#endif + +#include +#include +#include + +#include "Shock.h" +#include "ShockBitmap.h" +#include "InitMac.h" +#include "Prefs.h" +#include "input.h" +#include "ai.h" +#include "aiflags.h" +#include "citres.h" +#include "colors.h" +#include "cybstrng.h" +#include "doorparm.h" +#include "drugs.h" +#include "emailbit.h" +#include "faketime.h" +#include "frflags.h" // until we do the right thing re: static +#include "FrUtils.h" +#include "fullscrn.h" +#include "gamesys.h" +#include "gamescr.h" +#include "gamestrn.h" +#include "gr2ss.h" +#include "grenades.h" +#include "invent.h" +#include "leanmetr.h" +#include "MacTune.h" +#include "mainloop.h" +#include "movekeys.h" +#include "objbit.h" +#include "objects.h" +#include "objload.h" +#include "objsim.h" +#include "objprop.h" +#include "objuse.h" +#include "otrip.h" +#include "physics.h" +#include "player.h" +#include "rendtool.h" +#include "game_screen.h" +#include "svgacurs.h" +#include "tools.h" +#include "weapons.h" +#include "mouselook.h" + +#ifdef NOT_YET // KLC - for VR headsets + +#include +#include +#include +#ifdef STEREO_SUPPORT +#include +#include +#endif + +#include +#include +#include +#include +#ifdef SVGA_SUPPORT +#include +#endif + +#endif // NOT_YET + +#include "OpenGL.h" + +#define CHECK_FOR_A_PACKET + +#ifdef SVGA_SUPPORT +extern frc *svga_render_context; +#endif + +// ------- +// DEFINES +// ------- + +extern bool DoubleSize; + +#define CFG_DCLICK_RATE_VAR "dclick_time" +#define CFG_OOMPHOMETER "throw_oomph" +#define CFG_INP6D_GO "inp6d" + +ubyte use_distance_mod = 0; +ubyte pickup_distance_mod = 0; +ubyte fatigue_threshold = 5; + +#define MAX_FATIGUE 10000 // this is stolen from gamesys.c +#define FATIGUE_COEFF CIT_CYCLE +#define FATIGUE_THRESHOLD \ + ((player_struct.drug_status[CPTRIP(STAMINA_DRUG_TRIPLE)] == 0) ? (fatigue_threshold * CIT_CYCLE) : MAX_FATIGUE) +#define PLAYER_FATIGUE \ + ((player_struct.fatigue > FATIGUE_THRESHOLD) ? (player_struct.fatigue - FATIGUE_THRESHOLD) / FATIGUE_COEFF : 0) + +#define MOTION_FOOTPLANT_SCANCODE 0x2A + +#define AIM_SCREEN_MARGIN 5 +#define sqr(x) ((x) * (x)) + +extern LGRect target_screen_rect; + +extern uiSlab fullscreen_slab; +extern uiSlab main_slab; + +static ushort mouse_constrain_bits = 0; + +#define FIREKEY_CONSTRAIN_BIT 1 +#define LBUTTON_CONSTRAIN_BIT MOUSE_LDOWN +#define RBUTTON_CONSTRAIN_BIT MOUSE_RDOWN +#define LOCK_CONSTRAIN_BIT 0x8000 + +typedef struct _3d_mouse_stuff { + uchar ldown; + uchar rdown; + int lastsect; + LGPoint lastleft; + LGPoint lastright; + frc *fr; +} view3d_data; + +// ------- +// GLOBALS +// ------- + +Ref motion_cursor_ids[] = { + REF_IMG_bmUpLeftCursor, REF_IMG_bmUpCursor, REF_IMG_bmUpRightCursor, 0, + REF_IMG_bmLeftCursor, REF_IMG_bmDownCursor, REF_IMG_bmRightCursor, 0, + REF_IMG_bmCircLeftCursor, REF_IMG_bmTargetCursor, REF_IMG_bmCircRightCursor, 0, + REF_IMG_bmUpLeftCursor, REF_IMG_bmSprintCursor, REF_IMG_bmUpRightCursor, +}; + +#define NUM_MOTION_CURSORS 15 +#define NUM_CYBER_CURSORS 9 +#define CYBER_CURSOR_BASE REF_IMG_bmCyberUpLeftCursor + +LGCursor motion_cursors[NUM_MOTION_CURSORS]; +grs_bitmap motion_cursor_bitmaps[NUM_MOTION_CURSORS]; + +static uchar posture_keys[NUM_POSTURES] = {'t', 'g', 'b'}; + +int input_cursor_mode = INPUT_NORMAL_CURSOR; +int throw_oomph = 5; + +uchar inp6d_headset = FALSE; +uchar inp6d_stereo = FALSE; +uchar inp6d_doom = FALSE; +uchar inp6d_stereo_active = FALSE; +int inp6d_stereo_div = fix_make(3, 0x4000); // 3.25 inches apart +fix inpJoystickSens = FIX_UNIT; + +// checking for game paused +extern uchar game_paused; + +LGPoint use_cursor_pos; + +#ifdef RCACHE_TEST +extern uchar res_cache_usage_func(ushort keycode, uint32_t context, intptr_t data); +#endif +// extern uchar texture_annihilate_func(ushort keycode, uint32_t context, intptrr_t data); + +// 6d wackiness +uchar inp6d_exists = FALSE; +void inp6d_chk(void); + +#if defined(VFX1_SUPPORT) || defined(CTM_SUPPORT) +#include + +static int tracker_initial_pos[3] = {0, 0, 0}; +uchar recenter_headset(ushort keycode, uint32_t context, intptr_t data); +#endif + +// globals for doubling headset angular values +uchar inp6d_hdouble = FALSE; +uchar inp6d_pdouble = FALSE; +uchar inp6d_bdouble = FALSE; + +// and joysticks, heck, why be efficient +uchar joystick_mouse_emul = FALSE; +uchar joystick_count = 0; +uchar recenter_joystick(ushort keycode, uint32_t context, intptr_t data); + +uchar change_gamma(ushort keycode, uint32_t context, intptr_t data); + +// ------------- +// PROTOTYPES +// ------------- +void handle_keyboard_fatigue(void); +void poll_mouse(void); +uchar eye_hotkey_func(ushort keycode, uint32_t context, intptr_t data); + + +int view3d_mouse_input(LGPoint pos, LGRegion *reg, uchar move, int *lastsect); +void view3d_dclick(LGPoint pos, frc *fr, bool shifted); +void look_at_object(ObjID id); +uchar view3d_mouse_handler(uiEvent *ev, LGRegion *r, intptr_t data); +void view3d_rightbutton_handler(uiEvent *ev, LGRegion *r, view3d_data *data); +uchar view3d_key_handler(uiEvent *ev, LGRegion *r, intptr_t data); +void use_object_in_3d(ObjID obj, bool shifted); + +uchar MacResFunc(ushort keycode, uint32_t context, intptr_t data); +uchar MacSkiplinesFunc(ushort keycode, uint32_t context, intptr_t data); + +//EXTERN FUNCTIONS + + +// ------------- +// INPUT POLLING +// ------------- + +void handle_keyboard_fatigue(void) { + byte cval; + physics_get_one_control(KEYBD_CONTROL_BANK, CONTROL_YVEL, &cval); + if (cval > 0) { + int f = lg_max(CONTROL_MAX_VAL - PLAYER_FATIGUE, SPRINT_CONTROL_THRESHOLD); + if (cval > f) + physics_set_one_control(KEYBD_CONTROL_BANK, CONTROL_YVEL, f); + } + physics_get_one_control(KEYBD_CONTROL_BANK, CONTROL_ZVEL, &cval); + if (cval > 0) { + int f = lg_max(MAX_JUMP_CONTROL - PLAYER_FATIGUE, MAX_JUMP_CONTROL / 2); + if (cval > f) + physics_set_one_control(KEYBD_CONTROL_BANK, CONTROL_ZVEL, f); + } +} + +#ifdef NOT_YET // + + //#define CONSTRAIN_TO_FAUXREND + +#pragma disable_message(202) +void view3d_constrain_mouse(LGRegion *view, int mouse_bit) { + mouse_constrain_bits |= mouse_bit; + if (mouse_constrain_bits != 0) { +#ifdef CONSTRAIN_TO_FAUXREND + { + fauxrend_context *cc = (fauxrend_context *)_current_fr_context; + mouse_constrain_xy(cc->xtop, cc->ytop, cc->xtop + cc->xwid - 1, cc->ytop + cc->ywid - 1); + Warning(("cc->ytop = %d! view->abs_y = %d!\n", cc->ytop, view->abs_y)); + } +#else + ui_mouse_constrain_xy(view->abs_x, view->abs_y, view->abs_x + RectWidth(view->r) - 1, + view->abs_y + RectHeight(view->r) - 1); +#endif + } +} +#pragma enable_message(202) + +void view3d_unconstrain_mouse(int mouse_bit) { + mouse_constrain_bits &= ~mouse_bit; + if (mouse_constrain_bits == 0) { + mouse_unconstrain(); + } +} + +#ifdef PLAYTEST +uchar inp6d_player = TRUE, inp6d_motion = TRUE, inp6d_conform = TRUE; +#endif + +#endif // NOT_YET + +// Sends a motion event to the 3d view. + +uchar view3d_got_event = FALSE; + +void poll_mouse(void) { + if (_current_view != NULL) { + uiEvent ev; + uiMakeMotionEvent(&ev); + ev.type = UI_EVENT_USER_DEFINED; + mouse_constrain_bits |= LOCK_CONSTRAIN_BIT; + uiDispatchEventToRegion(&ev, _current_view); + mouse_constrain_bits &= ~LOCK_CONSTRAIN_BIT; + } +} + +uchar checking_mouse_button_emulation = FALSE; +uchar mouse_button_emulated = FALSE; + +uchar citadel_check_input(void) { + if (uiCheckInput()) + return (TRUE); + + if (checking_mouse_button_emulation) + mouse_button_emulated = FALSE; + + /*KLC - no headsets in Mac version + + if (inp6d_exists) + #ifdef PLAYTEST + if (inp6d_motion) + #endif + { + switch (i6d_device) + { + #ifdef SPACEBALL_SUPPORT + case I6D_SBALL: sball_chk(); break; + #endif + #ifdef CTM_SUPPORT + case I6D_ALLPRO: + case I6D_CTM: ctm_chk(); break; + #endif + case I6D_CYBERMAN: cyberman_chk(); break; + #ifdef VFX1_SUPPORT + case I6D_VFX1: vfx1_chk(); break; + #endif + case I6D_SWIFT: swift_chk(); break; + default: inp6d_chk(); break; + } + } + if (joystick_count) + joystick_chk(); + */ + + // if we're suppose to emulate a mouse button - let's do it! + if (mouse_button_emulated) + return (TRUE); + return (FALSE); +} + +void input_chk(void) { + setup_motion_polling(); + view3d_got_event = FALSE; + uiPoll(); + if (!view3d_got_event) + poll_mouse(); + + // KLC - not needed on MAC kb_flush_bios(); + // KLC - not needed on MAC mouse_set_velocity(0,0); + + /* KLC - comment out for now + if (inp6d_exists) + #ifdef PLAYTEST + if (inp6d_motion) + #endif + { + switch (i6d_device) + { + #ifdef SPACEBALL_SUPPORT + case I6D_SBALL: sball_chk(); break; + #endif + #ifdef CTM_SUPPORT + case I6D_ALLPRO: + case I6D_CTM: ctm_chk(); break; + #endif + case I6D_CYBERMAN: cyberman_chk(); break; + #ifdef VFX1_SUPPORT + case I6D_VFX1: vfx1_chk(); break; + #endif + case I6D_SWIFT: swift_chk(); break; + default: inp6d_chk(); break; + } + } + + if (joystick_count) + joystick_chk(); + */ + process_motion_keys(); + handle_keyboard_fatigue(); +} + +#ifdef NOT_YET // KLC - stuff for VR headsets + +#define Y_CEN 0 +#define MAX_VAL (1 << 15) +#define TRA_TOL (MAX_VAL >> 8) +#define ROT_TOL (MAX_VAL >> 5) +#define Tra_Scale(x) (((x)*CONTROL_MAX_VAL) / (MAX_VAL - TRA_TOL)) +#define Rot_Scale(x) (((x)*CONTROL_MAX_VAL) / (MAX_VAL - ROT_TOL)) + +#ifdef SPACEBALL_SUPPORT + // i really really really want to rewrite all of this cruft + +#define abs(x) ((x) < 0 ? -(x) : (x)) +#define isqr(x) (((x) * (x)) >> 16) +#define icube(x) ((isqr(x) * x) >> 16) +#define sign(x) ((x) < 0 ? -1 : 1) + +int sb_rot_sens = 21; +int sb_tran_sens = 13; +int sb_pitch_sens = 35; +int sb_float_sens = 1; +int sb_jump_thresh = 40; +int sb_jump_sens = 30; +int sb_pitch_thresh = 5; +int sb_crouch_thresh = 60; +int sb_prone_thresh = 90; + +// The number of pitch angles (looking up and down ) allowed +#define NUM_PITCH 10 +int sb_num_pitch = NUM_PITCH; +int sb_pitch_div = 100 / NUM_PITCH; +int sb_pitch_angles = FALSE; + +uchar sb_major_axis = FALSE; + +void FilterSpaceballDataMajorAxis(long *vals) { + int i, ind; + long max; + + max = abs(vals[0]); + ind = 0; + + for (i = 1; i < 6; i++) { + + if (abs(vals[i]) < max) { + vals[i] = 0; + continue; + } + + max = abs(vals[i]); + vals[ind] = 0; + ind = i; + } +} + +void FilterSpaceballDataSimple(long *tx, long *ty, long *tz, long *rx, long *ry, long *rz) { + *rx /= sb_pitch_sens; + *ry /= sb_rot_sens; + *rz /= sb_rot_sens; + *tx /= sb_tran_sens; + *ty /= sb_tran_sens; + *tz /= sb_jump_sens; +} + +void FilterSpaceballDataSquare(long *tx, long *ty, long *tz, long *rx, long *ry, long *rz) { + + *rx = sign(*rx) * isqr(*rx) / sb_pitch_sens; + *ry = icube(*ry) / sb_rot_sens; + *rz = sign(*rz) * isqr(*rz) / sb_rot_sens; + *tx = sign(*tx) * isqr(*tx) / sb_tran_sens; + *ty = sign(*ty) * isqr(*ty) / sb_tran_sens; + *tz = sign(*tz) * isqr(*tz) / sb_jump_sens; +} + +void FilterSpaceballDataFloating(long *tx, long *ty, long *tz, long *rx, long *ry, long *rz) { + long max; + + max = abs(*tx); + + if (abs(*ty) > max) + max = abs(*ty); + + if (abs(*tz) > max) + max = abs(*tz); + + if (abs(*rx) > max) + max = abs(*rx); + + if (abs(*ry) > max) + max = abs(*ry); + + if (abs(*rz) > max) + max = abs(*rz); + + *tx = ((*tx * (0x8000 - (max - abs(*tx)) * sb_float_sens)) >> 16) / sb_tran_sens; + + *ty = ((*ty * (0x8000 - (max - abs(*ty)) * sb_float_sens)) >> 16) / sb_tran_sens; + + *tz = ((*tz * (0x8000 - (max - abs(*tz)) * sb_float_sens)) >> 16) / sb_jump_sens; + + *rx = ((*rx * (0x8000 - (max - abs(*rx)) * sb_float_sens)) >> 16) / sb_pitch_sens; + + *ry = ((*ry * (0x8000 - (max - abs(*ry)) * sb_float_sens)) >> 16) / sb_rot_sens; + + *rz = ((*rz * (0x8000 - (max - abs(*rz)) * sb_float_sens)) >> 16) / sb_rot_sens; +} + + // *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** + // WARNING *** These #defines are also used in panels.c - if your change them here be sure to change them there! + +#define SB_FILTER_ORIG 0 // original SB input by Looking glass +#define SB_FILTER_SIMPLE 1 // Using a simple sensitivity value to map values +#define SB_FILTER_SQUARE 2 // Square the SB data to give emphasis to larger values and de-emphasize low values +#define SB_FILTER_FLOATING 3 // Floating sensitivity depending upon input from SB + +void FilterSpaceballDataOrig(long *tx, long *ty, long *tz, long *rx, long *ry, long *rz) { + + if (abs(*tx) > TRA_TOL) { + + if (*tx > TRA_TOL) + *tx = Tra_Scale(*tx - TRA_TOL); + else + *tx = Tra_Scale(*tx + TRA_TOL); + + } + + else + *tx = 0; + + if (abs(*ty) > TRA_TOL) { + + if (*ty > TRA_TOL) + *ty = Tra_Scale(*ty - TRA_TOL); + else + *ty = Tra_Scale(*ty + TRA_TOL); + + } + + else + *ty = 0; + + if (abs(*tz) > TRA_TOL) { + + if (*tz > TRA_TOL) + *tz = Tra_Scale(*tz - TRA_TOL); + else + *tz = Tra_Scale(*tz + TRA_TOL); + + } else + *tz = 0; + + if (*rx > ROT_TOL) + *rx = Rot_Scale(*rx - ROT_TOL); + else if (*rx < -ROT_TOL) + *rx = Rot_Scale(*rx + ROT_TOL); + else + *rx = 0; + + if (*ry > ROT_TOL) + *ry = Rot_Scale(*ry - ROT_TOL); + else if (*ry < -ROT_TOL) + *ry = Rot_Scale(*ry + ROT_TOL); + else + *ry = 0; + + if (*rz > ROT_TOL) + *rz = Rot_Scale(*rz - ROT_TOL); + else if (*rz < -ROT_TOL) + *rz = Rot_Scale(*rz + ROT_TOL); + else + *rz = 0; +} + +static void (*SbFilterFunc)(long *, long *, long *, long *, long *, long *) = FilterSpaceballDataSquare; + +void SetSbFilterFunc(int val) { + + switch (val) { + + case SB_FILTER_ORIG: + SbFilterFunc = FilterSpaceballDataOrig; + break; + + case SB_FILTER_SIMPLE: + SbFilterFunc = FilterSpaceballDataSimple; + break; + + case SB_FILTER_SQUARE: + SbFilterFunc = FilterSpaceballDataSquare; + break; + + case SB_FILTER_FLOATING: + SbFilterFunc = FilterSpaceballDataFloating; + break; + ; + } +} + +#define JUMP_MULTIPLE 20 + +sball_jump_filter(int jump_val) { + static int old_val = 0; + static int count = 0; + + // mprintf( "Jump value %d ", jump_val ); + + // If the jump value is really a duck value then bag outta here + if (jump_val < 0) + return jump_val; + + /* if ( count > 10 ) { + + count = old_val = 0; + return jump_val; + + } + */ + + // Make the jump value be a multiple of JUMP_MULTIPLE + jump_val = (jump_val / JUMP_MULTIPLE) * JUMP_MULTIPLE; + + // mprintf( "%d\n", jump_val ); + + // If the jump value is in the same bin as the old duck value then + // we assume the is the jump value we want and return the value + if (old_val == jump_val) { + + // mprintf( "Stable value %d\n", jump_val ); + count = old_val = 0; + return jump_val; + } + + // If the new value is in a bin that is less than the old value then + // (the person is letting up on the spaceball) then use the old value + // as the jump value + if (jump_val < old_val) { + int ret; + + // mprintf( "Less value %d %d\n", old_val, jump_val ); + ret = old_val; + count = old_val = 0; + return ret; + } + + old_val = jump_val; + ++count; + + return 0; +} + +#define PITCH_MAX_DELTA 15 + +int sb_pitch_constant = FALSE; + +#define SAMPLE_SIZE 20 + +int FilterPitch(long *pitch) { + long i; + static long ring_buf[SAMPLE_SIZE]; + static long ring_index = 0; + +#if 0 + static long max_val = 0; +#endif + long accum; + + if (abs(*pitch) < sb_pitch_thresh) + *pitch = 0; + + else { + + if (*pitch > 0) + *pitch -= sb_pitch_thresh; + else + *pitch += sb_pitch_thresh; + } + +#if 0 + if ( sb_pitch_constant ) { + + if ( max_val > 0 && *pitch > 0 ) { + + if ( *pitch < max_val ) + *pitch = max_val; + else + max_val = *pitch; + + return FALSE; + } + + + if ( max_val < 0 && *pitch < 0 ) { + + if ( *pitch > max_val ) + *pitch = max_val; + else + max_val = *pitch; + + + return FALSE; + } + + max_val += *pitch; + *pitch = max_val; + +// mprintf( "%d %d\n", max_val, *pitch ); + + return FALSE; + + } +#endif + + ring_buf[ring_index] = *pitch; + + ++ring_index; + if (ring_index == SAMPLE_SIZE) + ring_index = 0; + + accum = 0; + for (i = 0; i < SAMPLE_SIZE; i++) + accum += ring_buf[i]; + + *pitch = accum / SAMPLE_SIZE; + + if (*pitch) + return TRUE; + + return FALSE; +} + +void sball_chk(void) { + i6s_event *inp6d_in; + float foo; + long vals[6]; + + static int count = 0; + static int down_doo_be_doo = FALSE; + static int did_a_jump = FALSE; + static int doing_pitch = FALSE; + + // we'll want to put in code here to check for mouse_button_emulation + // if (checking_mouse_button_emulation && ) + // mouse_button_emulated = TRUE; + + if (game_paused) + return; + + if (did_a_jump) + physics_set_player_controls(INP6D_CONTROL_BANK, 0, 0, 0, 0, 0, 0); + + inp6d_in = i6_poll(); + + if (inp6d_in == NULL) { + if (doing_pitch) { + inp6d_in->x = 0; + inp6d_in->y = 0; + inp6d_in->z = 0; + inp6d_in->rx = 0; + inp6d_in->ry = 0; + inp6d_in->rz = 0; + } else + return; + } + + vals[0] = (float)inp6d_in->x * 0.9; + vals[1] = -inp6d_in->z; + vals[2] = inp6d_in->y; + + foo = -(float)inp6d_in->ry * 1.8; + + if (foo > 32000) + vals[3] = 32000; + else if (foo < -32000) + vals[3] = -32000; + else + vals[3] = foo; + + vals[4] = inp6d_in->rx; + vals[5] = -(float)inp6d_in->rz * 0.9; + + if (sb_major_axis) + FilterSpaceballDataMajorAxis(vals); + + (*SbFilterFunc)(&vals[0], &vals[1], &vals[2], &vals[4], &vals[3], &vals[5]); + +#ifdef PLAYTEST + if (!inp6d_player) { + /* mprintf("Parsed %d %d %d %d %d %d to %d %d %d %d %d %d\n", + inp6d_in->x, inp6d_in->y, inp6d_in->z, inp6d_in->rx, + inp6d_in->ry, inp6d_in->rz, vals[0], vals[1], vals[2], + vals[3], vals[4], vals[5] ); + */ + fr_camera_slewcam(NULL, EYE_X, vals[0] / 5); + fr_camera_slewcam(NULL, EYE_Y, vals[1] / 5); + fr_camera_slewcam(NULL, EYE_Z, vals[2] / 5); + fr_camera_slewcam(NULL, EYE_H, vals[3] / 3); + fr_camera_slewcam(NULL, EYE_P, vals[4] / 3); + fr_camera_slewcam(NULL, EYE_B, vals[5] / 3); + } else +#endif + { + + if (abs(vals[2]) > sb_jump_thresh) { + + if (vals[2] > 0) + vals[2] -= sb_jump_thresh; + else + vals[2] += sb_jump_thresh; + + } else + vals[2] = 0; + + doing_pitch = FilterPitch(&vals[4]); + + if (abs(vals[0]) > CONTROL_MAX_VAL) + vals[0] = sign(vals[0]) * CONTROL_MAX_VAL; + if (abs(vals[1]) > CONTROL_MAX_VAL) + vals[1] = sign(vals[1]) * CONTROL_MAX_VAL; + if (abs(vals[2]) > CONTROL_MAX_VAL) + vals[2] = sign(vals[2]) * CONTROL_MAX_VAL; + if (abs(vals[3]) > CONTROL_MAX_VAL) + vals[3] = sign(vals[3]) * CONTROL_MAX_VAL; + if (abs(vals[4]) > CONTROL_MAX_VAL) + vals[4] = sign(vals[4]) * CONTROL_MAX_VAL; + if (abs(vals[5]) > CONTROL_MAX_VAL) + vals[5] = sign(vals[5]) * CONTROL_MAX_VAL; + + vals[2] = sball_jump_filter(vals[2]); + if (vals[2] > 0) + did_a_jump = TRUE; + + /* mprintf("%d Parsed %d %d %d %d %d %d to %d %d %d %d %d %d\n", count, + inp6d_in->x, inp6d_in->y, inp6d_in->z, inp6d_in->rx, + inp6d_in->ry, inp6d_in->rz, vals[0], vals[1], vals[2], + vals[3], vals[4], vals[5] ); + */ + ++count; + + if (sb_pitch_angles) + vals[4] = (vals[4] / sb_pitch_div) * sb_pitch_div; + + if (vals[2] >= 0) { + + if (down_doo_be_doo) { + player_set_posture(POSTURE_STAND); + down_doo_be_doo = FALSE; + } + + physics_set_player_controls(INP6D_CONTROL_BANK, vals[0], vals[1], vals[2], vals[3], 0, 0); + + } + + else { + + physics_set_player_controls(INP6D_CONTROL_BANK, vals[0], vals[1], 0, vals[3], 0, 0); + + if (abs(vals[2]) < sb_crouch_thresh) { + // mprintf( "Stand\n" ); + player_set_posture(POSTURE_STAND); + down_doo_be_doo = FALSE; + } + + else if (abs(vals[2]) < sb_prone_thresh * 2) { + // mprintf( "Stoop!\n" ); + player_set_posture(POSTURE_STOOP); + down_doo_be_doo = TRUE; + } + + else { + // mprintf( "Prone\n" ); + player_set_posture(POSTURE_PRONE); + down_doo_be_doo = TRUE; + } + } + + player_set_lean(vals[5], 0); + player_set_eye(vals[4]); + } +} +#endif + +#endif // NOT_YET + +uchar main_kb_callback(uiEvent *h, LGRegion *r, intptr_t udata) { + + LGRegion *dummy2; + intptr_t dummy3; + dummy2 = r; + dummy3 = udata; + +#ifdef INPUT_CHAINING + kb_flush_bios(); +#endif // INPUT_CHAINING + + if (h->type == UI_EVENT_KBD_COOKED) + return hotkey_dispatch(h->subtype) == OK; + return FALSE; +} + +uchar posture_hotkey_func(ushort keycode, uint32_t context, intptr_t data) { +#ifndef NO_DUMMIES + uint32_t dummy; + dummy = context + keycode; +#endif + return player_set_posture((unsigned int)data) == OK; +} + +uchar eye_hotkey_func(ushort keycode, uint32_t context, intptr_t data) { + byte eyectl = player_get_eye(); + int r = 1 + (player_struct.drug_status[DRUG_REFLEX] > 0 && !global_fullmap->cyber); + + if (data == 0) { + player_set_eye(0); + return TRUE; + } + for (; r > 0; r--) { + if (data < 0) { + if (eyectl > 0) + eyectl = 0; + else + eyectl = (eyectl - CONTROL_MAX_VAL) / 3; + } else { + if (eyectl < 0) + eyectl = 0; + else + eyectl = (eyectl + CONTROL_MAX_VAL) / 3; + } + } + player_set_eye(eyectl); + return TRUE; +} + +#define EYE_POLLING +#ifndef EYE_POLLING +static ushort eye_up_keys[] = { + KEY_UP | KB_FLAG_SHIFT, + KEY_PAD_UP | KB_FLAG_SHIFT, + 'r', + 'R', +}; + +#define NUM_EYE_UP_KEYS (sizeof(eye_up_keys) / sizeof(ushort)) + +static ushort eye_dn_keys[] = { + KEY_DOWN | KB_FLAG_SHIFT, + KEY_PAD_DOWN | KB_FLAG_SHIFT, + 'v', + 'V', +}; + +#define NUM_EYE_DN_KEYS (sizeof(eye_dn_keys) / sizeof(ushort)) +#endif // !EYE_POLLING + +static ushort eye_lvl_keys[] = { + 'f', + 'F', +}; + +#define NUM_EYE_LVL_KEYS (sizeof(eye_lvl_keys) / sizeof(ushort)) +// ------------------------------------- +// INITIALIZATION +uchar toggle_profile(ushort keycode, uint32_t context, intptr_t data); +#ifdef PLAYTEST +extern uchar automap_seen(ushort keycode, uint32_t context, intptr_t data); +extern uchar maim_player(ushort keycode, uint32_t context, intptr_t data); +extern uchar salt_the_player(ushort keycode, uint32_t context, intptr_t data); +extern uchar give_player_hotkey(ushort keycode, uint32_t context, intptr_t data); +extern uchar change_clipper(ushort keycode, uint32_t context, intptr_t data); +#endif + +#define ckpoint_input(val) Spew(DSRC_TESTING_Test0, ("ii %s @%d\n", val, *tmd_ticks)); + +void reload_motion_cursors(uchar cyber) +{ + extern short cursor_color_offset; + + for (int i = 0; i < NUM_MOTION_CURSORS; i++) + { + grs_bitmap *bm = &motion_cursor_bitmaps[i]; + if (bm->bits != NULL) + { + free(bm->bits); + memset(bm, 0, sizeof(grs_bitmap)); + } + } + + if (!cyber) + { + for (int i = 0; i < NUM_MOTION_CURSORS; i++) + { + grs_bitmap *bm = &motion_cursor_bitmaps[i]; + if (motion_cursor_ids[i] != 0) + load_res_bitmap_cursor(&motion_cursors[i], bm, motion_cursor_ids[i], TRUE); + } + + // slam the cursor color back to it's childhood colors + cursor_color_offset = RED_BASE + 4; + + void SetMotionCursorsColorForActiveWeapon(void); + SetMotionCursorsColorForActiveWeapon(); + } + else + { + for (int i = 0; i < NUM_CYBER_CURSORS; i++) + { + grs_bitmap *bm = &motion_cursor_bitmaps[i]; + load_res_bitmap_cursor(&motion_cursors[i], bm, CYBER_CURSOR_BASE + i, TRUE); + } + } +} + +void free_cursor_bitmaps(void) +{ + //reload_motion_cursors() does everything now +} + +void alloc_cursor_bitmaps(void) +{ + //reload_motion_cursors() does everything now + + //I would just like to point out that this function + //was a good example of what were they thinking? +} + +#include "frtypes.h" +//extern bool gPlayingGame; +extern bool DoubleSize; +extern bool SkipLines; +bool gShowFrameCounter = false; +bool gShowMusicGlobals = false; + +uchar MacQuitFunc(ushort keycode, uint32_t context, intptr_t data) { + return TRUE; +} + +uchar MacResFunc(ushort keycode, uint32_t context, intptr_t data) { + DoubleSize = !DoubleSize; + change_svga_screen_mode(); + + if (DoubleSize) + message_info("Low res."); + else { + message_info("High res."); + SkipLines = FALSE; + } + gShockPrefs.doResolution = (DoubleSize) ? 1 : 0; // KLC - Yeah, got to update this one too + gShockPrefs.doUseQD = SkipLines; // KLC - and this one + SavePrefs(); // KLC - and save the prefs out to disk. + + return TRUE; +} + +uchar MacSkiplinesFunc(ushort keycode, uint32_t context, intptr_t data) { + if (!DoubleSize) // Skip lines only applies in double-size mode. + { + message_info("Skip lines works only in low-res mode."); + return FALSE; + } + SkipLines = !SkipLines; + gShockPrefs.doUseQD = SkipLines; + SavePrefs(); + return TRUE; +} + +uchar MacDetailFunc(ushort keycode, uint32_t context, intptr_t data) { + char msg[32]; + char detailStr[8]; + fauxrend_context *_frc = (fauxrend_context *)svga_render_context; + + if (_frc->detail == 4) // Adjust for that global detail nonsense. + _frc->detail = _fr_global_detail; + + _frc->detail++; // Cycle through the detail levels. + if (_frc->detail >= 4) + _frc->detail = 0; + _fr_global_detail = _frc->detail; // Update the global guy. + + gShockPrefs.doDetail = _frc->detail; // Update and save our prefs. + SavePrefs(); + + switch (_frc->detail) // Show a nice, informative message. + { + case 0: + strcpy(detailStr, "Min"); + break; + case 1: + strcpy(detailStr, "Low"); + break; + case 2: + strcpy(detailStr, "High"); + break; + case 3: + strcpy(detailStr, "Max"); + } + sprintf(msg, "Detail level: %s", detailStr); + message_info(msg); + return TRUE; +} + +/* +// Temporary function. Remove for final build + +uchar temp_FrameCounter_func(ushort keycode, uint32_t context, intptr_t data) +{ + gShowFrameCounter = !gShowFrameCounter; + + if (gShowFrameCounter) + message_info("Frame counter on."); + else + message_info("Frame counter off."); +} + +// end temp functions +*/ + +/* +uchar MacHelpFunc(ushort keycode, uint32_t context, intptr_t data) { + if (music_on) // Setup the environment for doing Mac stuff. + MacTuneKillCurrentTheme(); + uiHideMouse(NULL); + status_bio_end(); + + // CopyBits(&gMainWindow->portBits, &gMainOffScreen.bits->portBits, &gActiveArea, &gOffActiveArea, srcCopy, 0L); + + SS_ShowCursor(); + + // ShowShockHelp(); + + SetPort(gMainWindow); // Update area behind the alert + // BeginUpdate(gMainWindow); + + // CopyBits(&gMainOffScreen.bits->portBits, &gMainWindow->portBits, &gOffActiveArea, &gActiveArea, srcCopy, 0L); + + // EndUpdate(gMainWindow); + + HideCursor(); // go back to Shock. + uiShowMouse(NULL); + status_bio_start(); + if (music_on) + MacTuneStartCurrentTheme(); + + return TRUE; +} +*/ + +uchar toggle_opengl_func(ushort keycode, uint32_t context, intptr_t data) { + toggle_opengl(); + return TRUE; +} + +//most of original init_input() is now either commented out or done elsewhere, so do what's left here +//and comment out original function below +void init_input(void) { + uiDoubleClickDelay = 8; + uiDoubleClickTime = 45; + uiDoubleClicksOn[MOUSE_LBUTTON] = TRUE; // turn on left double clicks + uiAltDoubleClick = TRUE; + + alloc_cursor_bitmaps(); + reload_motion_cursors(FALSE); +} + +/* +void init_input(void) { + extern void init_motion_polling(); + int i = 0; + // KLC int kbdt, joy_type; + int dvec[2]; + + // init keyboard + // KLC for (i = 0; i < 0x80; i++) + // KLC kb_clear_state(i,KBA_REPEAT); + hotkey_init(NUM_HOTKEYS); + + // KLC + // kbdt=dt_keyboard(); + // if (kbdt!=kb_get_country()) + // { + // kb_set_country(kbdt); + // Warning(("Setting kb country to %d\n",kbdt)); + // } + // + init_motion_polling(); + + // init mouse + // KLC mouse_set_timestamp_register((ulong*)tmd_ticks); + dvec[0] = 8; // KLC 30; // default double click deleay; + dvec[1] = 45; // 175; // default double click time + i = 2; + // KLC config_get_value(CFG_DCLICK_RATE_VAR,CONFIG_INT_TYPE,dvec,&i); + uiDoubleClickDelay = dvec[0]; + uiDoubleClickTime = dvec[1]; + uiDoubleClicksOn[MOUSE_LBUTTON] = TRUE; // turn on left double clicks + uiAltDoubleClick = TRUE; + i = 1; + //uiSetMouseMotionPolling(TRUE); + + // Load cursors + + alloc_cursor_bitmaps(); + reload_motion_cursors(FALSE); + + // GAME HOTKEYS + + // MFDs + keyboard_init_mfd(); + + // Game wrapper hotkeys + // these are in all versions, playtest and not + hotkey_add(CONTROL('f'), DEMO_CONTEXT, change_mode_func, FULLSCREEN_LOOP); +#ifdef AUDIOLOGS + hotkey_add(CONTROL('.'), DEMO_CONTEXT, audiolog_cancel_func, 0); +#endif + hotkey_add(CONTROL('s'), DEMO_CONTEXT, save_hotkey_func, 0); + hotkey_add(CONTROL('S'), DEMO_CONTEXT, save_hotkey_func, 0); + hotkey_add(CONTROL('l'), DEMO_CONTEXT, saveload_hotkey_func, TRUE); + hotkey_add(CONTROL('L'), DEMO_CONTEXT, saveload_hotkey_func, TRUE); + + //KLC - not in Mac version + // hotkey_add('?', DEMO_CONTEXT, keyhelp_hotkey_func, 0); + // + // hotkey_add('/',DEMO_CONTEXT,toggle_bool_func,&joystick_mouse_emul); + // + hotkey_add(ALT(KEY_BS), DEMO_CONTEXT, reload_weapon_hotkey, 0); + hotkey_add(CONTROL(KEY_BS), DEMO_CONTEXT, reload_weapon_hotkey, 1); + hotkey_add(ALT('\''), DEMO_CONTEXT, arm_grenade_hotkey, 0); + hotkey_add(CONTROL('\''), DEMO_CONTEXT, select_grenade_hotkey, 0); + hotkey_add(ALT(';'), DEMO_CONTEXT, use_drug_hotkey, 0); + hotkey_add(CONTROL(';'), DEMO_CONTEXT, select_drug_hotkey, 0); + + //#ifndef PLAYTEST + // hotkey_add(DOWN(KEY_PRNTSCRN), EVERY_CONTEXT, gifdump_func, 0); + + hotkey_add(DOWN(KEY_BS), DEMO_CONTEXT, clear_fullscreen_func, 0); + hotkey_add(ALT('h'), DEMO_CONTEXT, hud_color_bank_cycle, 0); + hotkey_add(ALT('H'), DEMO_CONTEXT, hud_color_bank_cycle, 0); + hotkey_add(CONTROL('m'), DEMO_CONTEXT, toggle_music_func, 0); + hotkey_add(CONTROL('M'), DEMO_CONTEXT, toggle_music_func, 0); + // hotkey_add(DOWN(KEY_SPACE),DEMO_CONTEXT,unpause_game_func,TRUE); + + hotkey_add(DOWN('f'), DEMO_CONTEXT, toggle_mouse_look, TRUE); + + hotkey_add(DOWN('p'), DEMO_CONTEXT, pause_game_func, TRUE); + + // Cheats! + hotkey_add(CONTROL('2'), DEMO_CONTEXT, toggle_giveall_func, TRUE); + hotkey_add(CONTROL('3'), DEMO_CONTEXT, toggle_physics_func, TRUE); + hotkey_add(CONTROL('4'), DEMO_CONTEXT, toggle_up_level_func, TRUE); + hotkey_add(CONTROL('5'), DEMO_CONTEXT, toggle_down_level_func, TRUE); + + hotkey_add(DOWN(KEY_ESC), DEMO_CONTEXT, wrapper_options_func, TRUE); + for (i = 0; i < NUM_POSTURES; i++) { + hotkey_add(DOWN(posture_keys[i]), DEMO_CONTEXT, posture_hotkey_func, i); + hotkey_add(DOWN(toupper(posture_keys[i])), DEMO_CONTEXT, posture_hotkey_func, i); + } + hotkey_add(CONTROL('q'), DEMO_CONTEXT, MacQuitFunc, 0); + hotkey_add(CONTROL('1'), DEMO_CONTEXT, MacDetailFunc, 0); + hotkey_add(CONTROL('/'), DEMO_CONTEXT, MacHelpFunc, 0); + hotkey_add(CONTROL('?'), DEMO_CONTEXT, MacHelpFunc, 0); + + // + // hotkey_add(ALT('x'),DEMO_CONTEXT,demo_quit_func,0); + // hotkey_add(ALT('x'),SETUP_CONTEXT,really_quit_key_func,0); + // hotkey_add(ALT('v'),DEMO_CONTEXT,toggle_view_func,0); + // hotkey_add(ALT('V'),DEMO_CONTEXT,toggle_view_func,0); + // hotkey_add(ALT(KEY_F7),DEMO_CONTEXT,version_spew_func,0); + // + // hotkey_add(CONTROL('8'),DEMO_CONTEXT,location_spew_func,0); //testing + // hotkey_add(CONTROL('0'),DEMO_CONTEXT,temp_FrameCounter_func, 0); //testing + + hotkey_add(CONTROL('d'), DEMO_CONTEXT, change_mode_func, GAME_LOOP); + hotkey_add(CONTROL('D'), DEMO_CONTEXT, change_mode_func, FULLSCREEN_LOOP); + hotkey_add(CONTROL('a'), DEMO_CONTEXT, change_mode_func, AUTOMAP_LOOP); + hotkey_add(CONTROL('A'), DEMO_CONTEXT, change_mode_func, AUTOMAP_LOOP); + // + //#else + // hotkey_add(DOWN(KEY_SPACE),DEMO_CONTEXT,unpause_game_func,TRUE); + // hotkey_add(CONTROL('a'), DEMO_CONTEXT, change_mode_func, AUTOMAP_LOOP); + // hotkey_add(CONTROL('A'), DEMO_CONTEXT, change_mode_func, AUTOMAP_LOOP); + // + // hotkey_add_help(DOWN(KEY_BS), DEMO_CONTEXT,clear_fullscreen_func, 0, + // "Clears all overlays from the fullscreen view."); + // hotkey_add_help(DOWN(KEY_PAUSE),DEMO_CONTEXT,pause_game_func,TRUE, "pause the game, gee."); + // hotkey_add_help(DOWN('p'),DEMO_CONTEXT,pause_game_func,TRUE, "pause the game, gee."); + // hotkey_add_help(DOWN(KEY_ESC),DEMO_CONTEXT,wrapper_options_func,TRUE, + // "Opens up the options menu on the main game screen."); + // hotkey_add_help(CONTROL('h'), DEMO_CONTEXT, hud_color_bank_cycle, 0, "cycle hud colors"); + // hotkey_add_help(CONTROL('H'), DEMO_CONTEXT, hud_color_bank_cycle, 0, "cycle hud colors"); + // hotkey_add_help(CONTROL(ALT('~')),EVERY_CONTEXT,toggle_profile,TRUE, "toggle profile"); + // + // for (i = 0; i < NUM_POSTURES; i++) + // { + // hotkey_add_help(DOWN(posture_keys[i]),DEMO_CONTEXT,posture_hotkey_func,i,"change posture"); + // hotkey_add_help(DOWN(toupper(posture_keys[i])),DEMO_CONTEXT,posture_hotkey_func,i,"change posture"); + // } + // hotkey_add_help(ALT('x'),EDIT_CONTEXT|SETUP_CONTEXT,quit_key_func,0,"quit, but ask for confirm."); + // hotkey_add_help(ALT('x'),DEMO_CONTEXT,demo_quit_func,0,"quit, but ask for confirm on options panel."); + // hotkey_add_help(ALT('X'),EVERY_CONTEXT,really_quit_key_func,0, "quit, no questions asked."); + // hotkey_add_help(ALT('v'),EVERY_CONTEXT,toggle_view_func,0, "toggles between game mode and fullscreen mode."); + // hotkey_add_help(ALT('V'),EVERY_CONTEXT,toggle_view_func,0, "toggles between game mode and fullscreen mode."); + // hotkey_add(ALT('d'),EVERY_CONTEXT,mono_config_func,0 + // + // // these are some random hotkeys - debugging + // hotkey_add(CONTROL('q'),EVERY_CONTEXT,maim_player,"maim player"); + // hotkey_add(CONTROL('z'),EVERY_CONTEXT,change_clipper,"maim player"); + // hotkey_add(ALT(KEY_F2),EVERY_CONTEXT,salt_the_player, "Salt the Player"); + // hotkey_add(ALT(KEY_F3),EVERY_CONTEXT,give_player_hotkey, "Give Player Loot"); + // + // // Meta-slewing + // hotkey_add(DOWN('r'), EDIT_CONTEXT, stupid_slew_func, 13); + // hotkey_add(DOWN('>'), DEMO_CONTEXT|EDIT_CONTEXT, stupid_slew_func, 14); + // hotkey_add(DOWN('<'), DEMO_CONTEXT|EDIT_CONTEXT, stupid_slew_func, 15); + // + // // 3d zoomin + // hotkey_add(CONTROL('['), DEMO_CONTEXT|EDIT_CONTEXT, zoom_3d_func, TRUE); + // hotkey_add(CONTROL(']'), DEMO_CONTEXT|EDIT_CONTEXT, zoom_3d_func, FALSE); + // + // ckpoint_input("hotkeys in"); + // + // hotkey_add(DOWN('['),EDIT_CONTEXT,zoom_func,ZOOM_IN); + // hotkey_add(DOWN(']'),EDIT_CONTEXT,zoom_func,ZOOM_OUT); + // hotkey_add(CONTROL('d'),EDIT_CONTEXT,to_demo_mode_func,0); + // + //#endif + // hotkey_add(KEY_F11,DEMO_CONTEXT|EDIT_CONTEXT,change_gamma, 1); + // hotkey_add(KEY_F12,DEMO_CONTEXT|EDIT_CONTEXT,change_gamma,-1); + // + hotkey_add(CONTROL('h'), DEMO_CONTEXT, toggle_olh_func, 0); + hotkey_add(CONTROL('H'), DEMO_CONTEXT, toggle_olh_func, 0); + hotkey_add(ALT('o'), DEMO_CONTEXT, olh_overlay_func, &olh_overlay_on); + hotkey_add(ALT('O'), DEMO_CONTEXT, olh_overlay_func, &olh_overlay_on); + hotkey_add(CONTROL('g'), EVERY_CONTEXT, toggle_opengl_func, 0); + // + // // take these ifdefs out if memory bashing on shippable + //// hotkey_add(ALT(CONTROL(KEY_F4)),EVERY_CONTEXT,texture_annihilate_func,0); + //#ifdef RCACHE_TEST + // hotkey_add(ALT(KEY_F4),EVERY_CONTEXT,res_cache_usage_func,TRUE); + // hotkey_add(CONTROL(KEY_F4),EVERY_CONTEXT,res_cache_usage_func,FALSE); + //#endif + // init_side_icon_hotkeys(); + // + init_invent_hotkeys(); + + //for (i = 0; i < NUM_EYE_LVL_KEYS; i++) + //{ + // hotkey_add(DOWN(eye_lvl_keys[i]),DEMO_CONTEXT,eye_hotkey_func,0); + //} + + // KLC - stuff for VR headsets + // if (config_get_raw(CFG_INP6D_GO,NULL,0)) + // { + // ckpoint_input("inp6d start"); + // + // // hack for these config variables, not sure where else to put them + // inp6d_hdouble = config_get_raw("inp6d_hdouble",NULL,0); + // inp6d_pdouble = config_get_raw("inp6d_pdouble",NULL,0); + // inp6d_bdouble = config_get_raw("inp6d_bdouble",NULL,0); + // + //#if defined(VFX1_SUPPORT)||defined(CTM_SUPPORT)||defined(SPACEBALL_SUPPORT) + // inp6d_exists=(i6_probe()==0 && i6_startup()==0); + // if ((config_get_raw("inp6d_force",NULL,0))) + // inp6d_exists=(i6_force(I6D_VFX1) == 0); + //#else + // inp6d_exists=(i6_probe_small()==0 && i6_startup()==0); + //#endif + //#if defined(VFX1_SUPPORT)||defined(CTM_SUPPORT) + // if ((i6d_device==I6D_VFX1)||(i6d_device==I6D_CTM)||(i6d_device==I6D_ALLPRO)) + // { + // extern uchar fullscrn_vitals, fullscrn_icons; + // i6s_event *inp6d_geth; + // do { + // inp6d_geth=i6_poll(); + // } while (inp6d_geth==NULL); + // + // { + // hotkey_add(ALT('g'),DEMO_CONTEXT|EDIT_CONTEXT,recenter_headset,0); + // inp6d_headset=TRUE; + // tracker_initial_pos[0]= inp6d_geth->ry; + // tracker_initial_pos[1]= inp6d_geth->rx; + // tracker_initial_pos[2]=-inp6d_geth->rz; + // fullscrn_vitals=fullscrn_icons=FALSE; + // if (i6_video(I6VID_STARTUP,NULL)) + // Warning(("Headset video startup failed\n")); + // if ((config_get_raw("inp6d_stereo",NULL,0))&&(i6d_device!=I6D_ALLPRO)) + // { + // int cnt=1, rval[1]; + // if (i6_video(I6VID_STR_START,NULL)) + // Warning(("Headset stereo startup failed\n")); + // else + // { inp6d_stereo=TRUE; inp6d_stereo_active=FALSE; } + // config_get_value("inp6d_stereo",CONFIG_INT_TYPE,rval,&cnt); + // if (cnt>0) inp6d_stereo_div=rval[0]; + // } + // if (config_get_raw("inp6d_doom",NULL,0)) + // inp6d_doom=TRUE; + // } + // } // end of is it a tracker.... + //#endif + // ckpoint_input("inp6d end"); + // } else inp6d_exists=FALSE; + // { + // int cnt=1, rval[1]; + // config_get_value("joystick",CONFIG_INT_TYPE,rval,&cnt); + // if (cnt>0) + // { + // extern ushort wrap_joy_type; + // extern ushort high_joy_flags; + // joy_type=rval[0]; + // wrap_joy_type = joy_type & ~JOY_NO_NGP; + // high_joy_flags = joy_type & JOY_NO_NGP; + // } + // } + // if (joystick_count=joy_init(joy_type)) + // { + //#ifdef PLAYTEST + // mprintf("Got %d joystick pots\n",joystick_count); + //#endif + // hotkey_add(ALT('j'),DEMO_CONTEXT|EDIT_CONTEXT,recenter_joystick,0); + // } + // +} +*/ + +void shutdown_input(void) { + hotkey_shutdown(); + kb_flush_bios(); + + // kb_clear_state(0x1d, 3); + // kb_clear_state(0x9d, 3); + // kb_clear_state(0x38, 3); + // kb_clear_state(0xb8, 3); +} + + // ------------------------ + // 3D VIEW/MOTION INTERFACE + // ------------------------ + + // ------- + // DEFINES + // ------- + +#define VIEW_LSIDE 0 +#define VIEW_HCENTER 1 +#define VIEW_RSIDE 2 + +#define VIEW_TOP 0 +#define VIEW_BOTTOM 4 +#define VIEW_VCENTER 8 +#define VIEW_WAYTOP 12 + +#define CYBER_VIEW_TOP 0 +#define CYBER_VIEW_CENTER 3 +#define CYBER_VIEW_BOTTOM 6 + +#define CENTER_WD_N 1 +#define CENTER_WD_D 8 +#define CYBER_CENTER_WD_D 6 +#define CENTER_HT_N 1 +#define CENTER_HT_D 8 +#define CYBER_CENTER_HT_D 6 + +// ------- +// GLOBALS +// ------- + +short object_on_cursor = 0; +LGCursor object_cursor; + +// ------------------------------------------------------------------------------ +// view3d_rightbutton_handler deals with firing/throwing objects in 3d. + +uchar mouse_jump_ui = TRUE; +uchar fire_slam = FALSE; +uchar left_down_jump = FALSE; + +void reset_input_system(void) { + if (fire_slam) { + if (full_game_3d) + uiPopSlabCursor(&fullscreen_slab); + else + uiPopSlabCursor(&main_slab); + fire_slam = FALSE; + } + mouse_unconstrain(); +} + +#define DROP_REGION_Y(reg) ((reg)->abs_y + 7 * RectHeight((reg)->r) / 8) +uchar weapon_button_up = TRUE; + +// --------- +// INTERNALS +// --------- + +// ------------------------------------------------------------------------------------------- +// CalcMotionCurOffset gets cursor position offset data for +// SetMotionCursorForMouseXY() and view3d_mouse_input() + +void CalcMotionCurOffset(uchar cyber, LGRegion *reg, short *cx, short *cy, short *cw, short *ch, short *x, short *y) +{ + if (DoubleSize) + { + (*x) *= 2; + (*y) *= 2; + } + + if (!cyber) + { + (*cx) = reg->abs_x + RectWidth(reg->r) / 2; + (*cy) = reg->abs_y + 2 * RectHeight(reg->r) / 3; + (*cw) = RectWidth(reg->r) * CENTER_WD_N / CENTER_WD_D; + (*ch) = RectHeight(reg->r) * CENTER_HT_N / CENTER_HT_D; + } + else + { + (*cx) = reg->abs_x + RectWidth(reg->r) / 2; + (*cy) = reg->abs_y + RectHeight(reg->r) / 2; + (*cw) = RectWidth(reg->r) * CENTER_WD_N / CYBER_CENTER_WD_D; + (*ch) = RectHeight(reg->r) * CENTER_HT_N / CYBER_CENTER_HT_D; + } + +#ifdef SVGA_SUPPORT + ss_point_convert(cx, cy, FALSE); + ss_point_convert(cw, ch, FALSE); +#endif + + (*x) -= (*cx); + (*y) -= (*cy); +} + +// ------------------------------------------------------------------------------------------- +// SetMotionCursorForMouseXY sets motion cursor for current mouse x,y position + +// Used to set cursor to weapon color immediately without having to move the mouse + +// called at end of: +// fullscreen_start() fullscrn.c +// screen_start() screen.c + +void SetMotionCursorForMouseXY(void) +{ + if (global_fullmap->cyber) return; + + int cnum; + + LGRegion *reg; + + if (full_game_3d) + reg = fullview_region; + else + reg = mainview_region; + + extern int mlook_enabled; + + if (mlook_enabled) + cnum = VIEW_HCENTER | VIEW_VCENTER; + else + { + short cx, cy, cw, ch, x, y; + + mouse_get_xy(&x, &y); + + CalcMotionCurOffset(FALSE, reg, &cx, &cy, &cw, &ch, &x, &y); + + if (x < -cw) cnum = VIEW_LSIDE; + else if (x > cw) cnum = VIEW_RSIDE; + else cnum = VIEW_HCENTER; + + if (y < -ch) cnum |= VIEW_TOP; + else if (y > ch) cnum |= VIEW_BOTTOM; + else cnum |= VIEW_VCENTER; + } + + LGCursor *c = &motion_cursors[cnum]; + + if (reg == fullview_region) + uiSetGlobalDefaultCursor(c); + else + uiSetRegionDefaultCursor(reg, c); +} + +// ------------------------------------------------------------------------------------------- +// view3d_mouse_input sets/unsets physics controls based on mouse position in 3d + +// return whether any control was applied +int view3d_mouse_input(LGPoint pos, LGRegion *reg, uchar move, + int *lastsect) { // do we really recompute these every frame?? couldnt we have a context or + // something... something, a call to reinit, something + static int dougs_goofy_hack = FALSE; + + int cnum = 0; + byte xvel = 0; + byte yvel = 0; + byte xyrot = 0; + uchar thrust = FALSE; + uchar cyber = global_fullmap->cyber && time_passes; + + short cx, cy, cw, ch, x, y; + + x = pos.x; + y = pos.y; + + CalcMotionCurOffset(cyber, reg, &cx, &cy, &cw, &ch, &x, &y); + + // ok, the idea here is to make sure single left click doesnt move, or at least tells you whats up... + if ((dougs_goofy_hack == FALSE) && move) { + dougs_goofy_hack = TRUE; + move = FALSE; + } else if (!move) + dougs_goofy_hack = FALSE; + + if (x < -cw) { + cnum = VIEW_LSIDE; + if (move) { + xyrot = (x + cw) * 100 / (cx - cw - reg->abs_x); + } + } else if (x > cw) { + cnum = VIEW_RSIDE; + if (move) + xyrot = (x - cw) * 100 / (cx - cw - reg->abs_x); + } else + cnum = VIEW_HCENTER; + + if (cyber) { + if (y < -ch) { + if (move) + yvel = -(-ch - y) * CONTROL_MAX_VAL / (cy - ch - reg->abs_y); + cnum += CYBER_VIEW_TOP; + } else if (y > ch) { + cnum += CYBER_VIEW_BOTTOM; + if (move) { +#ifdef CYBER_ROLL_REGION + if (xyrot == 0) +#endif // CYBER_ROLL_REGION + yvel = -(ch - y) * CONTROL_MAX_VAL / (cy - ch - reg->abs_y); +#ifdef CYBER_ROLL_REGION + else { + xvel = xyrot; + xyrot = 0; + } +#endif // CYBER_ROLL_REGION + } + } else { + if ((thrust = ((cnum == VIEW_HCENTER) && move)) == TRUE) + physics_set_one_control(MOUSE_CONTROL_BANK, CONTROL_ZVEL, MAX_JUMP_CONTROL); + + cnum += CYBER_VIEW_CENTER; + } + } else { + if (y < -ch) { + short ycntl = (-ch - y) * CONTROL_MAX_VAL / (cy - ch - reg->abs_y); + if (move) { + int f = PLAYER_FATIGUE; + if (ycntl + f > CONTROL_MAX_VAL) { // compute new mouse cursor position + int newy; + f = lg_max(CONTROL_MAX_VAL - f, SPRINT_CONTROL_THRESHOLD); + newy = f * (ch + reg->abs_y - cy) / CONTROL_MAX_VAL - ch + cy; + ycntl = (ycntl + f) / 2; + // put the cursor between here and there + if (newy > pos.y) + mouse_put_xy(pos.x, newy); + } + yvel = ycntl; + } + + if (ycntl > SPRINT_CONTROL_THRESHOLD) + cnum |= VIEW_WAYTOP; + else + cnum |= VIEW_TOP; + + } else if (y > ch) { + cnum |= VIEW_BOTTOM; + if (move) { + if (xyrot == 0) + yvel = (ch - y) * CONTROL_MAX_VAL / (cy - ch - reg->abs_y); + else { + xvel = xyrot; + xyrot = 0; + } + } + } else + cnum |= VIEW_VCENTER; + } + + // If mouse look is enabled, just use the centered cursor + extern int mlook_enabled; + if (mlook_enabled) { + cnum = VIEW_HCENTER | VIEW_VCENTER; + + if (cyber) + cnum = VIEW_HCENTER + CYBER_VIEW_CENTER; + } + + if (*lastsect != cnum) { + extern LGRegion *fullview_region; + LGCursor *c = &motion_cursors[cnum]; + // Warning(("hey, cursor num = %d!\n",cnum)); + + // set the cursor to the motion cursor + if (reg == fullview_region) + uiSetGlobalDefaultCursor(c); + else + uiSetRegionDefaultCursor(reg, c); + + *lastsect = cnum; + } + + if (!thrust) + physics_set_player_controls(MOUSE_CONTROL_BANK, xvel, yvel, CONTROL_NO_CHANGE, xyrot, CONTROL_NO_CHANGE, + CONTROL_NO_CHANGE); + + if (dougs_goofy_hack) + return xvel | yvel | xyrot; + return 0; +} + +// Not a directly-installed mouse handler, called from view3d_mouse_handler +void view3d_rightbutton_handler(uiEvent *ev, LGRegion *r, view3d_data *data) { + extern LGCursor fire_cursor; + extern uchar hack_takeover; + LGPoint aimpos = ev->pos; + + if (DoubleSize) // If double sizing, convert the y to 640x480, then + aimpos.y = SCONV_Y(aimpos.y) >> 1; // half it. The x stays as is. + else + ss_point_convert(&(aimpos.x), &(aimpos.y), FALSE); + + // Don't do nuthin if we're in a hack camera + if (hack_takeover) + return; + + if (ev->mouse_data.action & MOUSE_RUP) { + if (!data->rdown) + data->lastright = aimpos; + else + data->rdown = FALSE; + left_down_jump = FALSE; + weapon_button_up = TRUE; + if (fire_slam) { + if (full_game_3d) + uiPopSlabCursor(&fullscreen_slab); + else + uiPopSlabCursor(&main_slab); + fire_slam = FALSE; + } + } + + if (ev->mouse_data.action & MOUSE_RDOWN) { + data->rdown = TRUE; + data->lastright = aimpos; + left_down_jump = data->ldown && !global_fullmap->cyber; + // view3d_constrain_mouse(r,RBUTTON_CONSTRAIN_BIT); + } + + /* + if (mouse_jump_ui && data->ldown && !global_fullmap->cyber) + { + if (ev->action & MOUSE_RDOWN) + { + physics_set_one_control(MOUSE_CONTROL_BANK,CONTROL_ZVEL, MAX_JUMP_CONTROL); + return; + } + } + */ + + switch (input_cursor_mode) { + case INPUT_NORMAL_CURSOR: + if (!global_fullmap->cyber && (player_struct.fire_rate == 0) && + !(ev->mouse_data.action & MOUSE_RDOWN)) + break; + if (left_down_jump) + break; + if (data->rdown) { + // printf("FIRE WEAPON!\n"); + if (fire_player_weapon(&aimpos, r, weapon_button_up) && (ev->mouse_data.action & MOUSE_RDOWN) && !fire_slam) { + if (full_game_3d) + uiPushSlabCursor(&fullscreen_slab, &fire_cursor); + else + uiPushSlabCursor(&main_slab, &fire_cursor); + fire_slam = TRUE; + } + weapon_button_up = FALSE; + } + break; + case INPUT_OBJECT_CURSOR: + if (ev->mouse_data.action & MOUSE_RUP) { + fix vel = throw_oomph * FIX_UNIT; + short dropy = DROP_REGION_Y(r); + short y = aimpos.y; + // if (convert_use_mode != 0) + if (DoubleSize) // If double sizing, convert the y to 640x480, then + dropy = SCONV_Y(dropy) >> 1; // half it. The x stays as is. + else + dropy = SCONV_Y(dropy); + if (y >= dropy && data->lastright.y >= dropy) { + vel = 0; + } + if (player_throw_object(object_on_cursor, aimpos.x, y, data->lastright.x, data->lastright.y, vel)) { + pop_cursor_object(); + uiShowMouse(NULL); // KLC - added to make sure new cursor shows. + } + data->rdown = FALSE; + } + break; + } +} + +// ---------------------------------------------------------------- +// use_object_in_3d deals with double-clicking on an object in the 3d + +uchar check_object_dist(ObjID obj1, ObjID obj2, fix crit) { + uchar retval = FALSE; + fix critrad = ID2radius(obj2); + fix dx = fix_from_obj_coord(objs[obj1].loc.x) - fix_from_obj_coord(objs[obj2].loc.x); + fix dy = fix_from_obj_coord(objs[obj1].loc.y) - fix_from_obj_coord(objs[obj2].loc.y); + fix dz = fix_from_obj_height(obj1) - fix_from_obj_height(obj2); + if (-dz > critrad / 2 && -dz < critrad + FIX_UNIT / 4) { + crit *= 2; + } + retval = fix_fast_pyth_dist(dx, dy) < crit; + if (retval) { + retval = -(critrad * 2 + crit / 2) < dz && dz < crit / 2 + critrad * 2; + } + return retval; +} + +#define TELE_ROD_DIST 16 // 16 feet + +void use_object_in_3d(ObjID obj, bool shifted) { + uchar success = FALSE; + ObjID telerod = OBJ_NULL; + uchar showname = FALSE; + extern ObjID physics_handle_id[MAX_OBJ]; + int mode = USE_MODE(obj); + char buf[80]; + Ref usemode = ID_NULL; + extern short loved_textures[]; + + if (global_fullmap->cyber) { + if (ID2TRIP(obj) != INFONODE_TRIPLE) { + switch (USE_MODE(obj)) { + case USE_USE_MODE: + usemode = REF_STR_PhraseUse; + break; + case PICKUP_USE_MODE: + usemode = REF_STR_PhrasePickUp; + break; + } + // exceptions + switch (objs[obj].obclass) { + case CLASS_BIGSTUFF: + usemode = REF_STR_PhrasePickUp; + break; + case CLASS_CRITTER: + usemode = ID_NULL; + break; + } + if (usemode != ID_NULL) { + sprintf(buf, get_temp_string(REF_STR_CyberspaceUse), get_temp_string(usemode)); + message_info(buf); + } + return; + } + } + + if (input_cursor_mode == INPUT_OBJECT_CURSOR) { + mode = USE_USE_MODE; + if (ID2TRIP(object_on_cursor) == ROD_TRIPLE) { + telerod = object_on_cursor; + object_on_cursor = OBJ_NULL; + use_distance_mod += TELE_ROD_DIST; + } + } + + switch (ID2TRIP(obj)) { + + case TMAP_TRIPLE: + get_texture_use_string(loved_textures[objBigstuffs[objs[obj].specID].data2], buf, 80); + message_info(buf); + return; + case BRIDGE_TRIPLE: { + int dat = ((objBigstuffs[objs[obj].specID].data1) >> 16) & 0xFF; + if (dat & 0x80) { + get_texture_name(loved_textures[dat & (~0x80)], buf, 80); + message_info(buf); + return; + } + break; + } + } + + switch (mode) { + case PICKUP_USE_MODE: { + ObjLocState del_loc_state; + void grenade_contact(ObjID id, int severity); + + if (!check_object_dist(obj, PLAYER_OBJ, MAX_PICKUP_DIST)) { + string_message_info(REF_STR_PickupTooFar); + showname = FALSE; + break; + } + // yank the object out of the map. + del_loc_state.obj = obj; + del_loc_state.loc = objs[obj].loc; + del_loc_state.loc.x = -1; + ObjRefStateBinSetNull(del_loc_state.refs[0].bin); + ObjUpdateLocs(&del_loc_state); + if (objs[obj].info.ph != -1) { + EDMS_kill_object(objs[obj].info.ph); + physics_handle_id[objs[obj].info.ph] = OBJ_NULL; + objs[obj].info.ph = -1; + } + // Put it on the cursor + // showname = TRUE; + push_cursor_object(obj); + + if (objs[obj].obclass == CLASS_GRENADE) + grenade_contact(obj, INT_MAX); + + if (shifted) { + absorb_object_on_cursor(0, 0, 0); //parameters unused + } + else + mouse_look_off(); + + success = TRUE; + } break; + case USE_USE_MODE: + showname = FALSE; + if (objs[obj].obclass != CLASS_CRITTER && ID2TRIP(obj) != MAPNOTE_TRIPLE && + !check_object_dist(obj, PLAYER_OBJ, MAX_USE_DIST)) { + string_message_info(REF_STR_UseTooFar); + break; + } + + extern bool ObjectUseShifted; //see objuse.c + ObjectUseShifted = shifted; + if (!object_use(obj, FALSE, object_on_cursor)) { + if (objs[obj].obclass != CLASS_DOOR) + goto cantuse; + else + showname = TRUE; + } + if (telerod != OBJ_NULL) { + object_on_cursor = telerod; + use_distance_mod -= TELE_ROD_DIST; + } + success = TRUE; + + break; + cantuse: + default: { + char use_str[80], buf2[50]; + sprintf(use_str, get_temp_string(REF_STR_CantUse), get_object_lookname(obj, buf2, 50)); + message_info(use_str); + } break; + } + if (success && !global_fullmap->cyber) { + objs[obj].info.inst_flags |= OLH_INST_FLAG; + } + if (showname) + look_at_object(obj); +} + +//------------------------------------------------------------------------- +// look_at_object prints a descriptive string of the object in the message line + +// these are just cribbed here from email.c... +#define EMAIL_BASE_ID RES_email0 +#define TITLE_IDX 1 +#define SENDER_IDX 2 + +char *get_object_lookname(ObjID id, char use_string[], int sz) { + int ref = -1; + int l; + int usetrip = ID2TRIP(id); + extern short loved_textures[]; + + strcpy(use_string, ""); + + switch (objs[id].obclass) { + case CLASS_FIXTURE: + case CLASS_DOOR: + if (objs[id].info.make_info != 0) + ref = REF_STR_Name0 + objs[id].info.make_info; + break; + case CLASS_GRENADE: + if (objGrenades[objs[id].specID].flags & GREN_ACTIVE_FLAG) { + get_string(REF_STR_WordLiveGrenade, use_string, sz); + l = strlen(use_string); + if (l + 1 < sz) + use_string[l] = ' '; + } + break; + case CLASS_SOFTWARE: + if (objs[id].subclass == SOFTWARE_SUBCLASS_DATA) { + short cont = objSoftwares[objs[id].specID].data_munge; + short num = cont & 0xFF; + if (global_fullmap->cyber) { + ref = REF_STR_DataObj; + break; + } + switch (cont >> 8) { + case LOG_VER: + num += NUM_EMAIL_PROPER; + break; + case DATA_VER: + num += (NUM_EMAIL - NUM_DATA); + break; + } + ref = MKREF(EMAIL_BASE_ID + num, TITLE_IDX); + } + break; + case CLASS_BIGSTUFF: + if (global_fullmap->cyber) { + usetrip = + MAKETRIP(CLASS_SOFTWARE, objBigstuffs[objs[id].specID].data1, objBigstuffs[objs[id].specID].data2); + } else { + switch (ID2TRIP(id)) { + case ICON_TRIPLE: + ref = REF_STR_IconName0 + objs[id].info.current_frame; + break; + case TMAP_TRIPLE: + get_texture_name(loved_textures[objBigstuffs[objs[id].specID].data2], use_string, sz); + return (use_string); + case BRIDGE_TRIPLE: { + int dat = ((objBigstuffs[objs[id].specID].data1) >> 16) & 0xFF; + if (dat & 0x80) { + get_texture_name(loved_textures[dat & (~0x80)], use_string, sz); + return (use_string); + } + break; + } + } + if (ref < 0) { + if (objs[id].info.make_info != 0) + ref = REF_STR_Name0 + objs[id].info.make_info; + } + } + break; + case CLASS_SMALLSTUFF: { + switch (ID2TRIP(id)) { + case PERSCARD_TRIPLE: { + char buf[50]; + int acc, len; + acc = objSmallstuffs[objs[id].specID].data1; +#define PERSONAL_BITS_SHIFT 24 + // get rid of all but personal access bits + get_object_long_name(ID2TRIP(id), use_string, sz); + acc = acc >> PERSONAL_BITS_SHIFT; + ref = PERSONAL_BITS_SHIFT; + if (acc == 0) + return (use_string); + for (; (acc & 1) == 0; acc = acc >> 1) + ref++; + ref = MKREF(RES_accessCards, (ref << 1) + 1); + get_string(ref, buf, sizeof(buf)); + len = strlen(buf); + while (!isspace(buf[len]) && len > 0) + len--; + if (isspace(buf[len])) { + strcat(use_string, "-"); + strcat(use_string, buf + len + 1); + } + return (use_string); + } + case HEAD_TRIPLE: + case HEAD2_TRIPLE: + if (objs[id].info.make_info != 0) + ref = REF_STR_Name0 + objs[id].info.make_info; + break; + } + } break; + case CLASS_HARDWARE: { + sprintf(use_string, "%s v%d", get_object_long_name(ID2TRIP(id), NULL, sz), objHardwares[objs[id].specID].version); + return (use_string); + } + case CLASS_CRITTER: { + char temp[128]; + Ref mod_refid = -1; + if (objCritters[objs[id].specID].orders == AI_ORDERS_SLEEP) + mod_refid = REF_STR_Sleeping; + else if (objCritters[objs[id].specID].flags & AI_FLAG_TRANQ) + mod_refid = REF_STR_Drugged; + else if (objCritters[objs[id].specID].flags & AI_FLAG_CONFUSED) + mod_refid = REF_STR_Stunned; + if (mod_refid != -1) { + get_string(mod_refid, temp, 128); + sprintf(use_string, temp, get_object_long_name(usetrip, NULL, 0)); + return (use_string); + } + } break; + } + // If we haven't set ref or ref is garbage, use the long name. + char *temp = (ref == -1) ? NULL : RefGet(ref); + if (temp == NULL) { + strcat(use_string, get_object_long_name(usetrip, NULL, 0)); + } else { + strncpy(use_string, temp, sz); + use_string[sz-1] = '\0'; + } + return (use_string); +} + +void look_at_object(ObjID id) { + char buf[50]; + get_object_lookname(id, buf, sizeof(buf)); + message_info(buf); +} + +// ------------------------------------------------------------------------ +// view3d_dclick dispatches double clicks based on cursor mode + +// Not a directly-installed mouse handler, called from view3d_mouse_handler +void view3d_dclick(LGPoint pos, frc *fr, bool shifted) { + extern short loved_textures[]; + extern uchar hack_takeover; + short obj_trans, obj; + frc *use_frc; + + extern int _fr_glob_flags; + + if (hack_takeover) + return; + switch (input_cursor_mode) { + case INPUT_NORMAL_CURSOR: + case INPUT_OBJECT_CURSOR: + use_frc = svga_render_context; + obj = fr_get_at_raw(use_frc, pos.x, pos.y, FALSE, FALSE); + if ((obj > 0) && (objs[obj].obclass == CLASS_DOOR)) { + obj_trans = fr_get_at_raw(use_frc, pos.x, pos.y, FALSE, TRUE); + if (obj != obj_trans) { + if (DOOR_REALLY_CLOSED(obj)) { + string_message_info(REF_STR_PickupTooFar); + return; + } else + obj = obj_trans; + } + } else if (obj > 0) { + obj = fr_get_at_raw(use_frc, pos.x, pos.y, FALSE, TRUE); + } + if ((short)obj < 0) { + // Don't display texture look strings in cspace....eventually we should do some cool hack + // for looking through walls, some sort of cspace fr_get_at or something + if (global_fullmap->cyber) + string_message_info(REF_STR_CybWallUse); + else + message_info(get_texture_use_string(loved_textures[~obj], NULL, 0)); + } else if ((short)obj > 0) { + use_cursor_pos = pos; + use_object_in_3d(obj, shifted); + } else { + if (!global_fullmap->cyber) { + if (!(_fr_glob_flags & FR_SOLIDFR_STATIC)) + string_message_info(REF_STR_InkyUse); + } + } + } +} + +// ------------------------------------------------------------------------------- +// view3d_mouse_handler is the actual installed mouse handler, dispatching to the above functions +uchar view3d_mouse_handler(uiEvent *ev, LGRegion *r, intptr_t v) { + static uchar got_focus = FALSE; + uiMouseData *md = &ev->mouse_data; + view3d_data *data = (view3d_data*)v; + uchar retval = TRUE; + LGPoint pt; + LGPoint evp = ev->pos; + extern int _fr_glob_flags; + + pt = evp; + +#ifdef STEREO_SUPPORT + if (convert_use_mode == 5) { + extern uchar inventory_mouse_handler(uiEvent * ev, LGRegion * r, intptr_t data); + extern uchar mfd_view_callback(uiEvent * e, LGRegion * r, intptr_t udata); + switch (i6d_device) { + case I6D_CTM: + if (full_visible & FULL_INVENT_MASK) + return (inventory_mouse_handler(ev, r, v)); + if ((full_visible & FULL_L_MFD_MASK) && (evp.x < ((MFD_VIEW_LFTX + MFD_VIEW_WID) << 1))) + return (mfd_view_callback(ev, r, 0)); + if ((full_visible & FULL_R_MFD_MASK) && (evp.x > ((MFD_VIEW_RGTX + MFD_VIEW_WID) >> 1))) + return (mfd_view_callback(ev, r, 1)); + break; + case I6D_VFX1: + if (full_visible & FULL_INVENT_MASK) + return (inventory_mouse_handler(ev, r, v)); + if ((full_visible & FULL_L_MFD_MASK) && (evp.x < ((MFD_VIEW_LFTX + MFD_VIEW_WID) << 1))) + return (mfd_view_callback(ev, r, 0)); + if ((full_visible & FULL_R_MFD_MASK) && (evp.x > ((MFD_VIEW_RGTX + MFD_VIEW_WID) >> 1))) + return (mfd_view_callback(ev, r, 1)); + break; + } + } +#endif + +#ifdef STEREO_SUPPORT + if (convert_use_mode != 5) +#endif + if (DoubleSize) // If double sizing, convert the y to 640x480, then + evp.y = SCONV_Y(evp.y) >> 1; // half it. The x stays as is. + else + ss_point_convert(&(evp.x), &(evp.y), FALSE); + + view3d_got_event = TRUE; + pt.x += r->r->ul.x - r->abs_x; + pt.y += r->r->ul.y - r->abs_y; + + if (!RECT_TEST_PT(r->r, pt)) { + data->ldown = FALSE; + physics_set_player_controls(MOUSE_CONTROL_BANK, 0, 0, CONTROL_NO_CHANGE, 0, CONTROL_NO_CHANGE, + CONTROL_NO_CHANGE); + return (FALSE); + } + if (md->action & MOUSE_LDOWN) { + data->ldown = TRUE; + data->lastleft = evp; + if (full_game_3d && !got_focus) { + if (uiGrabFocus(r, UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE) == OK) + got_focus = TRUE; + } + chg_set_flg(_current_3d_flag); + // view3d_constrain_mouse(r,LBUTTON_CONSTRAIN_BIT); + } + if (md->action & MOUSE_LUP || !(md->buttons & (1 << MOUSE_LBUTTON))) { + data->ldown = FALSE; + if (full_game_3d && got_focus) { + if (uiReleaseFocus(r, UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE) == OK) + got_focus = FALSE; + } + // view3d_unconstrain_mouse(LBUTTON_CONSTRAIN_BIT); + } + if (md->action & MOUSE_LUP && abs(evp.y - data->lastleft.y) < uiDoubleClickTolerance && + abs(evp.x - data->lastleft.x) < uiDoubleClickTolerance) { + //make shift+leftclick act as double-leftclick with alternate effects + if (md->modifiers & 1) { //shifted click; see sdl_events.c + view3d_dclick(evp, data->fr, TRUE); //TRUE indicates shifted + data->lastleft = MakePoint(-100, -100); + } + else { + ObjID id; + frc *use_frc; + short rabsx, rabsy; + + use_frc = svga_render_context; + rabsx = r->abs_x; + rabsy = r->abs_y; + if (!DoubleSize) + ss_point_convert(&rabsx, &rabsy, FALSE); + + id = fr_get_at(use_frc, evp.x - rabsx, evp.y - rabsy, TRUE); + if ((short)id > 0) { + look_at_object(id); + } else if ((short)id < 0) { + extern short loved_textures[]; + int tnum = loved_textures[~id]; + if (global_fullmap->cyber) + string_message_info(REF_STR_CybWall); + else + message_info(get_texture_name(tnum, NULL, 0)); + } else { + if (!global_fullmap->cyber) { + if (!(_fr_glob_flags & FR_SOLIDFR_STATIC)) + string_message_info(REF_STR_InkyBlack); + } + } + data->lastleft.x = -255; + } + } + if ((md->action & (MOUSE_RDOWN | MOUSE_RUP)) || (md->buttons & (1 << MOUSE_RBUTTON))) + view3d_rightbutton_handler(ev, r, data); + + /* KLC - done in another place now. + else + { + view3d_unconstrain_mouse(RBUTTON_CONSTRAIN_BIT); + if (fire_slam) + { + if (full_game_3d) + uiPopSlabCursor(&fullscreen_slab); + else + uiPopSlabCursor(&main_slab); + fire_slam = FALSE; + } + } + + */ + if ((md->buttons & (1 << MOUSE_RBUTTON)) == 0 || + ((md->buttons & (1 << MOUSE_RBUTTON)) == 0 && global_fullmap->cyber)) + physics_set_one_control(MOUSE_CONTROL_BANK, CONTROL_ZVEL, 0); + + if (md->action & UI_MOUSE_LDOUBLE) { + // Spew(DSRC_USER_I_Motion,("use this, bay-bee!\n")); + view3d_dclick(evp, data->fr, FALSE); + data->lastleft = MakePoint(-100, -100); + } + + if (md->action & (MOUSE_WHEELUP | MOUSE_WHEELDN)) { + cycle_weapons_func(0, 0, md->action & MOUSE_WHEELUP ? -1 : 1); + } + + // data->ldown = TRUE; + + // Do mouse motion. + if (view3d_mouse_input(evp, r, data->ldown, &data->lastsect) != 0) + data->lastleft = MakePoint(-1, -1); // if the player is moving, not a down + + return (retval); +} + +typedef struct _view3d_kdata { + int maxctrl; // max control as affected by fatigue +} view3d_kdata; + +extern int FireKeys[]; //see MacSrc/Prefs.c + +uchar view3d_key_handler(uiEvent *ev, LGRegion *r, intptr_t data) +{ + uiCookedKeyData *kd = &ev->cooked_key_data; + int i, detect = 0, fire_pressed = 0; + + i = 0; + while (FireKeys[i] != 0) + { + if (kd->code == FireKeys[i]) detect = 1; + if (kd->code == (FireKeys[i] | KB_FLAG_DOWN)) {detect = 1; fire_pressed = 1; break;} + i++; + } + if (!detect) return FALSE; + + if (fire_pressed) + { + if (weapon_button_up) // if we haven't fired already + { + LGPoint evp = ev->pos; + ss_point_convert(&(evp.x), &(evp.y), FALSE); + fire_player_weapon(&evp, r, !fire_slam); + fire_slam = TRUE; + weapon_button_up = FALSE; + } + } + else + { + weapon_button_up = TRUE; + fire_slam = FALSE; + } + + return FALSE; +} + +#ifdef NOT_YET // KLC - for VR headsets + +#define CORE6D_MAX_VAL (1 << 15) +#define CORE6D_TRA_TOL (MAX_VAL >> 8) +#define CORE6D_ROT_TOL (MAX_VAL >> 5) +#define Tra_Scale(x) (((x)*CONTROL_MAX_VAL) / (MAX_VAL - TRA_TOL)) +#define Rot_Scale(x) (((x)*CONTROL_MAX_VAL) / (MAX_VAL - ROT_TOL)) + +// total hack lame inp6d function for now.... +void inp6d_chk(void) { + // static inp6d_raw_event last_swift; + i6s_event *inp6d_in; + int xp, yp, zv, h, p, b; + + // we'll want to put in code here to check for mouse_button_emulation + // if (checking_mouse_button_emulation && ) + // mouse_button_emulated = TRUE; + + if (game_paused) + return; + inp6d_in = i6_poll(); + if (inp6d_in == NULL) + return; + xp = inp6d_in->x; + if (abs(xp) > TRA_TOL) + if (xp > TRA_TOL) + xp = Tra_Scale(xp - TRA_TOL); + else + xp = Tra_Scale(xp + TRA_TOL); + else + xp = 0; + yp = -inp6d_in->z; + if (abs(yp) > TRA_TOL) + if (yp > TRA_TOL) + yp = Tra_Scale(yp - TRA_TOL); + else + yp = Tra_Scale(yp + TRA_TOL); + else + yp = 0; + zv = inp6d_in->y; + if (abs(zv) > TRA_TOL) + if (zv > TRA_TOL) + zv = Tra_Scale(zv - TRA_TOL); + else + zv = Tra_Scale(zv + TRA_TOL); + else + zv = 0; + + h = -inp6d_in->ry; + if (h > ROT_TOL) + h = Rot_Scale(h - ROT_TOL); + else if (h < -ROT_TOL) + h = Rot_Scale(h + ROT_TOL); + else + h = 0; + p = inp6d_in->rx; + if (p > ROT_TOL) + p = Rot_Scale(p - ROT_TOL); + else if (p < -ROT_TOL) + p = Rot_Scale(p + ROT_TOL); + else + p = 0; + b = -inp6d_in->rz; + if (b > ROT_TOL) + b = Rot_Scale(b - ROT_TOL); + else if (b < -ROT_TOL) + b = Rot_Scale(b + ROT_TOL); + else + b = 0; + +#ifdef PLAYTEST + if (inp6d_dbg) + mprintf("Parsed %04x %04x %04x %04x %04x %04x to %d %d %d %d %d %d\n", inp6d_in->x, inp6d_in->y, inp6d_in->z, + inp6d_in->rx, inp6d_in->ry, inp6d_in->rz, xp, yp, zv, h, p, b); +#endif + +#ifdef PLAYTEST + if (!inp6d_player) { + fr_camera_slewcam(NULL, EYE_X, xp / 5); + fr_camera_slewcam(NULL, EYE_Y, yp / 5); + fr_camera_slewcam(NULL, EYE_Z, zv / 5); + fr_camera_slewcam(NULL, EYE_H, h / 3); + fr_camera_slewcam(NULL, EYE_P, p / 3); + fr_camera_slewcam(NULL, EYE_B, b / 3); + } else +#endif + { + physics_set_player_controls(INP6D_CONTROL_BANK, xp, yp, zv, h, p, b); + fr_camera_slewcam(NULL, EYE_P, p / 3); // hack horribly for now... yea! + fr_camera_slewcam(NULL, EYE_B, b / 3); + } +} + +#define ANG_P 0 +#define ANG_B 1 +#define ANG_H 2 +#if defined(VFX1_SUPPORT) || defined(CTM_SUPPORT) +short l_angs[3]; + +short *set_abs_head(i6s_event *e) { + if (!inp6d_doom) { + l_angs[ANG_B] = (e->ry) - tracker_initial_pos[0]; + l_angs[ANG_P] = (e->rx) - tracker_initial_pos[1]; + } else + l_angs[ANG_B] = l_angs[ANG_P] = 0; + l_angs[ANG_H] = (-e->rz) - tracker_initial_pos[2]; + if (inp6d_hdouble) + l_angs[ANG_H] += l_angs[ANG_H]; + if (inp6d_pdouble) + l_angs[ANG_P] += l_angs[ANG_P]; + if (inp6d_bdouble) + l_angs[ANG_B] += l_angs[ANG_B]; + return l_angs; +} + +#define BREAK_REGION (CONTROL_MAX_VAL / 4) +#define XTRA_REGION (CONTROL_MAX_VAL - BREAK_REGION) + +// add a double LGRegion fix... +short deparse_angle_region(short angle, short min, short mid, short max) { + int sgn; + if (abs(angle) < min) + return 0; + sgn = angle > 0 ? 1 : -1; + angle = abs(angle); + if (angle < mid) + return sgn * ((angle - min) * BREAK_REGION / (mid - min)); + if (angle < max) + return sgn * (BREAK_REGION + (angle - mid) * XTRA_REGION / (max - mid)); + return sgn * CONTROL_MAX_VAL; +} + +void slam_head(short *angs) { +#ifdef PLAYTEST + static int last_head_h; +#endif + // if (global_fullmap->cyber) + // { // secret head joystick dented with the promise of power + // short h,p,b; + // h=deparse_angle_region(angs[ANG_H],0x0400,0x1400,0x2000); + // p=deparse_angle_region(angs[ANG_P],0x0400,0x1400,0x2000); + // b=deparse_angle_region(angs[ANG_B],0x0300,0x0B00,0x1400); + // physics_set_player_controls(INP6D_CONTROL_BANK, b, -p, 0, h, 0, 0); + // } + // else + { + fr_camera_setone(NULL, EYE_P, angs[ANG_P]); + fr_camera_setone(NULL, EYE_B, angs[ANG_B]); +#ifdef PLAYTEST + if (!inp6d_link) +#endif + fr_camera_setone(NULL, EYE_HEADH, angs[ANG_H]); +#ifdef PLAYTEST + else { + int h_diff = (angs[ANG_H] - last_head_h) / cam_slew_scale[EYE_H]; // HAQ + fr_camera_slewone(NULL, EYE_H, h_diff); + last_head_h = angs[ANG_H]; + } +#endif + } +} +#endif + +#define USE_UPPER_BOUND (CIT_CYCLE / 4) +#define RELOAD_TIME (CIT_CYCLE * 2) + +uchar reload_current_weapon(void); +uchar inp_reloaded = FALSE; +uchar inp_sidestep = FALSE; +int use_but_time = 0; +int weap_time = 0; +void inp_weapon_button(uchar pull) { + int w = player_struct.actives[ACTIVE_WEAPON]; // check if we need to reload + uchar reloaded = FALSE; + + if (object_on_cursor) { + LGPoint pos = MakePoint(_current_view->abs_x + RectWidth(_current_view->r) / 2, + _current_view->abs_y + RectHeight(_current_view->r) / 2); + + ui_mouse_put_xy(pos.x, pos.y); +#ifdef SVGA_SUPPORT + ss_point_convert(&(pos.x), &(pos.y), FALSE); +#endif + if (player_throw_object(object_on_cursor, pos.x, pos.y, pos.x, pos.y, throw_oomph * FIX_UNIT)) + pop_cursor_object(); + return; + } + + // reload if conditions are right + if ((player_struct.weapons[w].type != GUN_SUBCLASS_BEAM) && + (player_struct.weapons[w].type != GUN_SUBCLASS_HANDTOHAND) && + (player_struct.weapons[w].type != GUN_SUBCLASS_BEAMPROJ) && (player_struct.weapons[w].ammo == 0)) { // reload + + if (weap_time == 0) { + weap_time = *tmd_ticks; + reloaded = FALSE; + } else if (!reloaded) + if (*tmd_ticks > weap_time + RELOAD_TIME) { + reload_current_weapon(); + reloaded = TRUE; + } + } else if (weap_time == 0) { + LGPoint pos = + MakePoint(_current_view->abs_x + RectWidth(_current_view->r) / 2, + _current_view->abs_y + RectHeight(_current_view->r) / 2 + (RectHeight(_current_view->r) >> 4)); + + ui_mouse_put_xy(pos.x, pos.y); +#ifdef SVGA_SUPPORT + ss_point_convert(&(pos.x), &(pos.y), FALSE); +#endif + fire_player_weapon(&pos, _current_view, pull); + } +} + +#define inp_weapon_junk() weap_time = 0 + +void inp_use_sidestep_button() { + if (use_but_time) { // if long enough, go to sidestep.... + if (*tmd_ticks > use_but_time + USE_UPPER_BOUND) { + inp_sidestep = TRUE; + use_but_time = 0; + } + } else if (!inp_sidestep) + use_but_time = *tmd_ticks; +} + +// teach this that if you have obj on cursor it knows whether to throw or +// put in your inventory. do this by storing when you pick up w/joystick +// and if mouse moves cancelling but otherwise when joyclicking again with +// an obj already on cursor the put it in inventory + +void inp_use_sidestep_junk() { + if (use_but_time) { + LGPoint pos; + + if (input_cursor_mode == INPUT_OBJECT_CURSOR) { + extern void absorb_object_on_cursor(ushort keycode, uint32_t context, intptr_t data); + absorb_object_on_cursor(0, 0, 0); + } else { + pos = MakePoint(_current_view->abs_x + RectWidth(_current_view->r) / 2, + _current_view->abs_y + RectHeight(_current_view->r) / 2); +#ifdef SVGA_SUPPORT + ss_point_convert(&pos.x, &pos.y, FALSE); +#endif + mouse_put_xy(pos.x, pos.y); + view3d_dclick(pos, NULL, FALSE); + } + use_but_time = 0; + } else + inp_sidestep = FALSE; +} + +#ifdef VFX1_SUPPORT + +#define VFX1_MAX_VAL (1 << 15) +#define VFX1_TRA_TOL (MAX_VAL >> 5) +#define VFX1_Tra_Scale(x) (((x)*CONTROL_MAX_VAL) / (VFX1_MAX_VAL - VFX1_TRA_TOL)) + +// total hack lame guess at a vfx1 function... +void vfx1_chk(void) { + static uchar last_but; + // static LGPoint targ_loc={160,100}; + i6s_event *inp6d_in; + int xp, yp, xp1, xp2, zv; + short *angs; + + if (game_paused) + return; + inp6d_in = i6_poll(); + if (inp6d_in == NULL) + return; + + xp = inp6d_in->x; + + xp += (xp >> 1); // add fifty percent + if (abs(xp) > VFX1_TRA_TOL) + if (xp > VFX1_TRA_TOL) + xp = VFX1_Tra_Scale(xp - VFX1_TRA_TOL); + else + xp = VFX1_Tra_Scale(xp + VFX1_TRA_TOL); + else + xp = 0; + + yp = -inp6d_in->y; // flip it so + is forward motion + yp *= 2; // since the bat, forward 90 degrees is full speed + if (abs(yp) > VFX1_TRA_TOL) + if (yp > VFX1_TRA_TOL) + yp = CONTROL_MAX_VAL / 4 + VFX1_Tra_Scale(yp - VFX1_TRA_TOL); + else + yp = VFX1_Tra_Scale(yp + VFX1_TRA_TOL); + else + yp = 0; + xp1 = xp; + xp2 = 0; + zv = 0; + + angs = set_abs_head(inp6d_in); + + if (inp6d_in->but) { + if (joystick_mouse_emul) { + extern void joystick_emulate_mouse(int x, int y, uchar bstate, uchar last_bstate); + joystick_emulate_mouse(xp, -yp, inp6d_in->but >> 1, last_but >> 1); + xp2 = xp1 = yp = zv = 0; + } else { + if (inp6d_in->but & 1) + inp_weapon_button((last_but & 1) != (inp6d_in->but & 1)); + if (inp6d_in->but & 2) + zv = 100; // jump jump jump + if (inp6d_in->but & 4) + inp_use_sidestep_button(); + } + } + + if (inp_sidestep) { + xp2 = xp1 * 2; + xp1 = 0; + } // switch bat X from heading to sidestep + + if (!joystick_mouse_emul) { + if ((inp6d_in->but & 4) == 0) + inp_use_sidestep_junk(); + + if ((inp6d_in->but & 1) == 0 && (last_but & 1) == 1) { + inp_weapon_junk(); + } + } + +#ifdef PLAYTEST + if (inp6d_dbg) + mprintf("Parsed %04x %04x %04x %04x %04x %04x to %d %d %d %d %d %d\n", inp6d_in->x, inp6d_in->y, inp6d_in->z, + inp6d_in->rx, inp6d_in->ry, inp6d_in->rz, xp, yp, zv, angs[ANG_P], angs[ANG_B], angs[ANG_H]); +#endif + +#ifdef PLAYTEST + if (!inp6d_player) { + fr_camera_slewcam(NULL, EYE_X, xp / 5); + fr_camera_slewcam(NULL, EYE_Y, yp / 5); + // fr_camera_slewcam(NULL,EYE_Z,zv/5); + // fr_camera_setone(NULL,EYE_HEADH,h); + fr_camera_setone(NULL, EYE_H, angs[ANG_H]); + fr_camera_setone(NULL, EYE_P, angs[ANG_P]); + fr_camera_setone(NULL, EYE_B, angs[ANG_B]); + } else +#endif // PLAYTEST + { + // combine the two when in cyberspace + if (global_fullmap->cyber) { + xp1 += deparse_angle_region(angs[ANG_H], 0x0400, 0x1400, 0x2000); + yp -= deparse_angle_region(angs[ANG_P], 0x0400, 0x1400, 0x2000) / 2; + xp2 += deparse_angle_region(angs[ANG_B], 0x0300, 0x0B00, 0x1400); + + if (xp1 > CONTROL_MAX_VAL) + xp1 = CONTROL_MAX_VAL; + else if (xp1 < -CONTROL_MAX_VAL) + xp1 = -CONTROL_MAX_VAL; + + if (yp > CONTROL_MAX_VAL) + yp = CONTROL_MAX_VAL; + else if (yp < -CONTROL_MAX_VAL) + yp = -CONTROL_MAX_VAL; + + if (xp2 > CONTROL_MAX_VAL) + xp2 = CONTROL_MAX_VAL; + else if (xp2 < -CONTROL_MAX_VAL) + xp2 = -CONTROL_MAX_VAL; + + } else { + slam_head(angs); + } + + // b+xp2, -p+yp, 0+zv, h+xp1, 0, 0 + physics_set_player_controls(INP6D_CONTROL_BANK, xp2, yp, zv, xp1, 0, 0); + } + last_but = inp6d_in->but; +} +#endif // VFX1_SUPPORT + +#ifdef CTM_SUPPORT + +// note secret filtering code these days.... +void ctm_chk(void) { + i6s_event *inp6d_in; + short *angs; + + if (game_paused) + return; + inp6d_in = i6_poll(); + if (inp6d_in == NULL) + return; + + angs = set_abs_head(inp6d_in); + +#ifdef PLAYTEST + if (inp6d_dbg) + mprintf("Parsed %04x %04x %04x to %d %d %d\n", inp6d_in->rx, inp6d_in->ry, inp6d_in->rz, angs[ANG_H], + angs[ANG_P], angs[ANG_B]); +#endif // PLAYTEST + +#ifdef PLAYTEST + if (!inp6d_player) { + if (!inp6d_link) + fr_camera_setone(NULL, EYE_HEADH, angs[ANG_H]); + else + fr_camera_setone(NULL, EYE_H, angs[ANG_H]); + fr_camera_setone(NULL, EYE_P, angs[ANG_P]); + fr_camera_setone(NULL, EYE_B, angs[ANG_B]); + } else +#endif // PLAYTEST + { + // combine the two when in cyberspace + if (global_fullmap->cyber) { + short h, p, b; + h = deparse_angle_region(angs[ANG_H], 0x0400, 0x1400, 0x2000); + p = deparse_angle_region(angs[ANG_P], 0x0400, 0x1400, 0x2000) / 2; + b = deparse_angle_region(angs[ANG_B], 0x0300, 0x0B00, 0x1400); + physics_set_player_controls(INP6D_CONTROL_BANK, b, -p, 0, h, 0, 0); + + } else { + slam_head(angs); + } + } +} +#endif // CTM_SUPPORT + +void swift_chk(void) { + i6s_event *inp6d_in; + int our_vals[6], i, tmp; + + // we'll want to put in code here to check for mouse_button_emulation + // if (checking_mouse_button_emulation && ) + // mouse_button_emulated = TRUE; + + if (game_paused) + return; + inp6d_in = i6_poll(); + if (inp6d_in == NULL) + return; + + inp6d_in->y = -inp6d_in->y; // doug cheats, film at 11 + + // translation + for (i = 0; i < 3; i++) { + tmp = inp6d_in->els[i]; + if (abs(tmp) > TRA_TOL) + if (tmp > TRA_TOL) + tmp = Tra_Scale(tmp - TRA_TOL); + else + tmp = Tra_Scale(tmp + TRA_TOL); + else + tmp = 0; + our_vals[i] = tmp; + } + + // rotation + for (i = 3; i < 6; i++) { + tmp = inp6d_in->els[i]; + if (tmp > ROT_TOL) + tmp = Rot_Scale(tmp - ROT_TOL); + else if (tmp < -ROT_TOL) + tmp = Rot_Scale(tmp + ROT_TOL); + else + tmp = 0; + our_vals[i] = tmp; + } // was h = -ry, p = rx, b = -rz + +#ifdef PLAYTEST + if (inp6d_dbg) + mprintf("Parsed %04x %04x %04x %04x %04x %04x to %d %d %d %d %d %d\n", inp6d_in->els[0], inp6d_in->els[1], + inp6d_in->els[2], inp6d_in->els[3], inp6d_in->els[4], inp6d_in->els[5], our_vals[0], our_vals[1], + our_vals[2], our_vals[3], our_vals[4], our_vals[5]); +#endif + +#ifdef PLAYTEST + if (!inp6d_player) { + for (i = 0; i < 3; i++) + fr_camera_slewcam(NULL, i, our_vals[i] / 5); // cheat cheat cheat + fr_camera_slewcam(NULL, EYE_H, our_vals[ANG_H] / 3); + fr_camera_slewcam(NULL, EYE_P, our_vals[ANG_P] / 3); + fr_camera_slewcam(NULL, EYE_B, our_vals[ANG_B] / 3); + } else +#endif + { + physics_set_player_controls(INP6D_CONTROL_BANK, our_vals[0], our_vals[1], our_vals[2], our_vals[3], our_vals[4], + our_vals[5]); + } +} + +// various hacked hotkey functions.... +#pragma disable_message(202) +#if defined(VFX1_SUPPORT) || defined(CTM_SUPPORT) +uchar recenter_headset(ushort keycode, uint32_t context, intptr_t data) { + long start_time = *tmd_ticks; + i6s_event *inp6d_geth; + do { + inp6d_geth = i6_poll(); + } while ((inp6d_geth == NULL) && (*tmd_ticks < start_time + (280 / 4))); + if (inp6d_geth != NULL) { + tracker_initial_pos[0] = inp6d_geth->ry; + tracker_initial_pos[1] = inp6d_geth->rx; + tracker_initial_pos[2] = -inp6d_geth->rz; + // mprintf("Tip %x %x %x\n",tracker_initial_pos[0],tracker_initial_pos[1],tracker_initial_pos[2]); + message_info("Headset ReCentered"); + } // should message_info here with no headset found + return FALSE; +} +#endif + +uchar recenter_joystick(ushort keycode, uint32_t context, intptr_t data) { + joy_center(); + string_message_info(REF_STR_CenterJoyDone); + return FALSE; +} + +uchar change_gamma(ushort keycode, uint32_t context, intptr_t data) { + static fix cit_gamma = fix_make(1, 0); + int dir = (int)data; + if ((dir < 0) && (cit_gamma > fix_make(0, 0x6000))) + cit_gamma -= fix_make(0, 0x0400); + else if ((dir > 0) && (cit_gamma < fix_make(1, 0x6000))) + cit_gamma += fix_make(0, 0x0800); + else + dir = 0; + if (dir != 0) + gr_set_gamma_pal(0, 256, cit_gamma); + // here, should do message_info ResXXX+1-dir, have 3 strings in file, Lowered, Maxed, Raised + // instead, ill do a switch for now... + switch (dir) { + case -1: + message_info("Gamma Lowered"); + break; + case 0: + message_info("Gamma Maxed Out"); + break; + case 1: + message_info("Gamma Raised"); + break; + } + return FALSE; +} +#pragma enable_message(202) + +#define CYBERMAN_MOTION (1 << 1) +#define CYBERMAN_FIRE (1 << 0) // either other button + +#define CYB_MAX_VAL (1 << 15) +#define CYB_TOL (CYB_MAX_VAL >> 14) +#define CYB_Scale(x) (((x)*CONTROL_MAX_VAL) / (CYB_MAX_VAL - CYB_TOL)) + +// total hack lame inp6d function for now.... +void cyberman_chk(void) { + // static inp6d_raw_event last_swift; + i6s_event *inp6d_in; + int xp, yp, zv, h, p, b; + static uchar cyb_mouse_around = TRUE, pchange = FALSE; + static int p_vel = 0, b_vel = 0; // , h_vel=0; + + // we'll want to put in code here to check for mouse_button_emulation + // if (checking_mouse_button_emulation && ) + // mouse_button_emulated = TRUE; + + if (game_paused) + return; + inp6d_in = i6_poll(); + if (inp6d_in == NULL) + return; + + // should wait for a motion prior to flipping over + if ((inp6d_in->but & CYBERMAN_MOTION) == 0) { + if (!cyb_mouse_around) { +#ifdef PLAYTEST + mprintf("CMA punt, zero controls\n"); +#endif + physics_set_player_controls(INP6D_CONTROL_BANK, 0, 0, 0, 0, 0, 0); + uiGlobalEventMask |= (UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE); + uiSetMouseMotionPolling(TRUE); + uiShowMouse(NULL); + cyb_mouse_around = TRUE; + } + return; // should allow mouse in this case, since we really need to do that + } else { + if (cyb_mouse_around) { + uiGlobalEventMask &= ~(UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE); + uiSetMouseMotionPolling(FALSE); + uiHideMouse(NULL); + cyb_mouse_around = FALSE; + } + } + + if (inp6d_in->but & CYBERMAN_FIRE) { + LGPoint pos = + MakePoint(_current_view->abs_x + RectWidth(_current_view->r) / 2, + _current_view->abs_y + RectHeight(_current_view->r) / 2 + (RectHeight(_current_view->r) >> 5)); + +#ifdef SVGA_SUPPORT + ss_point_convert(&(pos.x), &(pos.y), FALSE); +#endif + fire_player_weapon(&pos, _current_view, TRUE); + } + + // fire hack, mouse hack + xp = inp6d_in->x; + if (abs(xp) > CYB_TOL) + if (xp > CYB_TOL) + xp = CYB_Scale(xp - CYB_TOL); + else + xp = CYB_Scale(xp + CYB_TOL); + else + xp = 0; + yp = -inp6d_in->y; + if (abs(yp) > CYB_TOL) + if (yp > CYB_TOL) + yp = CYB_Scale(yp - CYB_TOL); + else + yp = CYB_Scale(yp + CYB_TOL); + else + yp = 0; + zv = inp6d_in->z; + if (abs(zv) > CYB_TOL) + if (zv > CYB_TOL) + zv = CYB_Scale(zv - CYB_TOL); + else + zv = CYB_Scale(zv + CYB_TOL); + else + zv = 0; + + h = -(short)inp6d_in->ry; + if (h > ROT_TOL) + h = Rot_Scale(h - ROT_TOL); + else if (h < -ROT_TOL) + h = Rot_Scale(h + ROT_TOL); + else + h = 0; + p = (short)inp6d_in->rx; + if (p > ROT_TOL) + p = Rot_Scale(p - ROT_TOL); + else if (p < -ROT_TOL) + p = Rot_Scale(p + ROT_TOL); + else + p = 0; + b = -(short)inp6d_in->rz; + if (b > ROT_TOL) + b = Rot_Scale(b - ROT_TOL); + else if (b < -ROT_TOL) + b = Rot_Scale(b + ROT_TOL); + else + b = 0; + +#ifdef PLAYTEST + if (inp6d_dbg) + mprintf("Parsed %04x %04x %04x %04x %04x %04x b%x to %d %d %d %d %d %d\n", inp6d_in->x, inp6d_in->y, + inp6d_in->z, inp6d_in->rx, inp6d_in->ry, inp6d_in->rz, inp6d_in->but, xp, yp, zv, h, p, b); +#endif + +#ifdef PLAYTEST + if (!inp6d_player) { + fr_camera_slewcam(NULL, EYE_X, xp / 5); + fr_camera_slewcam(NULL, EYE_Y, yp / 5); + fr_camera_slewcam(NULL, EYE_Z, zv / 5); + fr_camera_slewcam(NULL, EYE_H, h / 3); + fr_camera_slewcam(NULL, EYE_P, p / 3); + fr_camera_slewcam(NULL, EYE_B, b / 3); + } else +#endif + { + if (abs(xp) < 12) + xp = 0; + if (abs(yp) < 12) + yp = 0; + // if ((abs(xp)>80)||(abs(yp)>80)) + // p=b=0; // how bout that cyberman, eh? + if (b != 0) + xp = yp = zv = p = 0; + if (b > 0) { + if (b_vel > 0) + b_vel += 4; + else + b_vel = 4; + if (b_vel > CONTROL_MAX_VAL) + b_vel = CONTROL_MAX_VAL; + } else if (b < 0) { + if (b_vel < 0) + b_vel -= 4; + else + b_vel = -4; + if (b_vel < -CONTROL_MAX_VAL) + b_vel = -CONTROL_MAX_VAL; + } else + b_vel = 0; + b = b_vel; + + if (p > 0) { + if (p_vel > 0) + p_vel += 6; + else + p_vel = 16; + if (p_vel > CONTROL_MAX_VAL) + p_vel = CONTROL_MAX_VAL; + } else if (p < 0) { + if (p_vel < 0) + p_vel -= 6; + else + p_vel = -16; + if (p_vel < -CONTROL_MAX_VAL) + p_vel = -CONTROL_MAX_VAL; + } else + p_vel = 0; + p = p_vel; + + // if ((abs(b_vel)>80)||(abs(p_vel)>80)) + // zv=0; // no posture changes while zipping along in pitch + if ((b_vel | p_vel) || (abs(xp) > 10) || (abs(yp) > 10)) + zv = 0; // no longer can posture change while pitch or banking + // but should be able to jump........ arrrrghghghhh + + if (zv > 0) { + if (player_struct.posture != POSTURE_STAND) { + if (pchange == FALSE) + player_struct.posture--; + pchange = TRUE; + zv = 0; + } else if (pchange) + zv = 0; // dont jump as you stand + // else + // pchange=TRUE; // treat a jump like a posture change... + } else if (zv < 0) { + if (player_struct.posture != POSTURE_PRONE) + if (pchange == FALSE) + player_struct.posture++; + pchange = TRUE; + zv = 0; + } else + pchange = FALSE; + +#ifdef PLAYTEST + if (inp6d_link) + p = b = 0; // no p or b +#endif + physics_set_player_controls(INP6D_CONTROL_BANK, xp, yp, zv, h, p, b); + } +} + +#define JOY_USE_CONTROL_MAX_VAL (128) +#define JOY_MAX_VAL (128) +#define JOY_TOL (30) + +#define SENSITIVITY_CONTROL +#ifdef SENSITIVITY_CONTROL +#define JOY_Scale(x) fix_mul(inpJoystickSens, (((x)*JOY_USE_CONTROL_MAX_VAL) / (JOY_MAX_VAL - JOY_TOL))) +#else +#define JOY_Scale(x) (((x)*JOY_USE_CONTROL_MAX_VAL) / (JOY_MAX_VAL - JOY_TOL)) +#endif + +void joystick_emulate_mouse(int x, int y, uchar bstate, uchar last_bstate) { + mouse_add_velocity((x)*abs(x) << (MOUSE_VEL_UNIT_SHF - APPROX_CIT_CYCLE_SHFT - 4), + -y * abs(y) << (MOUSE_VEL_UNIT_SHF - APPROX_CIT_CYCLE_SHFT - 4)); + // if any button states are changed, generate + // a low level mouse event. + if ((bstate) != (last_bstate)) { + // sadly, there is no good api to get at this mouse library + // variable, so we will employ gnosis for now. + extern short mouseInstantButts; + mouse_event me; + + ui_mouse_get_xy(&me.x, &me.y); +#ifdef SVGA_SUPPORT + ss_point_convert(&me.x, &me.y, FALSE); +#endif + me.type = 0; + me.buttons = (uchar)mouseInstantButts; + if ((bstate & 1) != (last_bstate & 1)) { + me.type |= (bstate & 1) ? MOUSE_LDOWN : MOUSE_LUP; + if (bstate & 1) + me.buttons |= (1 << MOUSE_LBUTTON); + else + me.buttons &= ~(1 << MOUSE_LBUTTON); + } + if ((bstate & 2) != (last_bstate & 2)) { + me.type |= (bstate & 2) ? MOUSE_RDOWN : MOUSE_RUP; + if (bstate & 2) + me.buttons |= (1 << MOUSE_RBUTTON); + else + me.buttons &= ~(1 << MOUSE_RBUTTON); + } + me.timestamp = *tmd_ticks; + mouse_generate(me); + } +} + +void joystick_chk(void) { + static uchar last_bstate; + int xp, yp, zv = 0, h, p, b = 0; + char pot_vals[4]; + uchar bstate; + + // this has to be fixed... + { + static uchar once = 0; + if (!once) { + once = 1; + joy_center(); + } + } // i wonder if we can punt this??? + + bstate = joy_read_buttons(); + if (checking_mouse_button_emulation && (last_bstate != bstate)) { + mouse_button_emulated = TRUE; + last_bstate = bstate; + } + + if (game_paused) + return; + + joy_read_pots(pot_vals); + + yp = -((int)pot_vals[1]); + // mprintf("have %d from %d...",yp,pot_vals[1]); + if (abs(yp) > JOY_TOL) + if (yp > JOY_TOL) + yp = JOY_Scale(yp - JOY_TOL); + else if (yp < -JOY_TOL) + yp = JOY_Scale(yp + JOY_TOL); + else + yp = 0; + h = pot_vals[0]; + // mprintf("have %d from %d...",h,pot_vals[0]); + if (abs(h) > JOY_TOL) + if (h > JOY_TOL) + h = JOY_Scale(h - JOY_TOL); + else if (h < -JOY_TOL) + h = JOY_Scale(h + JOY_TOL); + else + h = 0; + + if (h > CONTROL_MAX_VAL) + h = CONTROL_MAX_VAL; + else if (h < -CONTROL_MAX_VAL) + h = -CONTROL_MAX_VAL; + if (yp > CONTROL_MAX_VAL) + yp = CONTROL_MAX_VAL; + else if (yp < -CONTROL_MAX_VAL) + yp = -CONTROL_MAX_VAL; + // mprintf("final %d and %d...",h,yp); + + // pitch with throttle here, maybe pedal heading or something + // if (joystick_count==4) + + if (inp_sidestep) // toggle h/sidestep, forward/pitch + { + xp = h; + p = -yp; + h = yp = 0; + } else + p = xp = 0; + // mprintf("net %d %d %d %d\n",xp,yp,h,p); + +#ifdef PLAYTEST + if (inp6d_dbg) { + if (bstate != 0) + mprintf("Yo buttons %2.2x..", bstate); + else + mprintf(" "); + mprintf("cntrl %d %d %d %d from pots %d %d %d %d\n", xp, yp, h, p, pot_vals[0], pot_vals[1], pot_vals[2], + pot_vals[3]); + } +#endif + + if (joystick_mouse_emul) { + joystick_emulate_mouse(h, yp, bstate, last_bstate); + h = yp = xp = p = 0; + } else if (_current_loop <= FULLSCREEN_LOOP) { + if (bstate & 1) + inp_weapon_button((bstate & 1) != (last_bstate & 1)); + // reset on release + else if ((last_bstate & 1) == 1) { + inp_weapon_junk(); + } + if (bstate & 2) + inp_use_sidestep_button(); + else + inp_use_sidestep_junk(); + if (bstate & 4) + zv = MAX_JUMP_CONTROL; + + // if (bstate&8) // ??, who knows + + if (bstate >= 16) // hat behavior + { // coolie... HAT + switch (bstate & JOY_HAT_MASK) { + case JOY_HAT_N: + if (p == 0) + p = -CONTROL_MAX_VAL; + break; + case JOY_HAT_E: + if (b == 0) + b = CONTROL_MAX_VAL; + break; + case JOY_HAT_S: + if (p == 0) + p = CONTROL_MAX_VAL; + break; + case JOY_HAT_W: + if (b == 0) + b = -CONTROL_MAX_VAL; + break; + } + } + } + + if (_current_loop <= FULLSCREEN_LOOP) + physics_set_player_controls(JOYST_CONTROL_BANK, xp, yp, zv, h, p, b); + last_bstate = bstate; +} + +#ifdef PLAYTEST +#include +#pragma disable_message(202) +uchar toggle_profile(ushort keycode, uint32_t context, intptr_t data) { + static uchar UserProf = FALSE; + if (UserProf) + _MARK_("User Off"); + else + _MARK_("User On"); + UserProf = !UserProf; + return TRUE; +} +#pragma enable_message(202) +#endif + +#endif // NOT_YET + +// --------- +// EXTERNALS +// --------- + +void install_motion_mouse_handler(LGRegion *r, frc *fr) { + int cid; + view3d_data *data = (view3d_data *)malloc(sizeof(view3d_data)); + data->ldown = FALSE; + data->rdown = FALSE; + data->lastsect = 0; + data->lastleft.x = 0; + data->lastleft.y = 0; + data->lastright.x = 0; + data->lastright.y = 0; + data->fr = fr; + uiInstallRegionHandler(r, UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE | UI_EVENT_USER_DEFINED, + view3d_mouse_handler, (intptr_t)data, &cid); + + // Yeah, yeah, I know, it's not a mouse handler... + uiInstallRegionHandler(r, UI_EVENT_KBD_COOKED, view3d_key_handler, 0, &cid); + uiSetRegionDefaultCursor(r, NULL); +} + +void install_motion_keyboard_handler(LGRegion *r) { + int cid; + uiInstallRegionHandler(r, UI_EVENT_KBD_POLL, motion_keycheck_handler, 0, &cid); +} + +void pop_cursor_object(void) { + if (input_cursor_mode != INPUT_OBJECT_CURSOR) + return; + object_on_cursor = OBJ_NULL; + uiPopSlabCursor(&fullscreen_slab); + uiPopSlabCursor(&main_slab); + input_cursor_mode = INPUT_NORMAL_CURSOR; +} + +void push_cursor_object(short obj) { + LGPoint hotspot; + grs_bitmap *bmp; +#ifdef CURSOR_BACKUPS + extern LGCursor backup_object_cursor; +#endif + if (objs[obj].obclass == CLASS_GRENADE && objGrenades[objs[obj].specID].flags & GREN_ACTIVE_FLAG) { + push_live_grenade_cursor(obj); + return; + } + uiHideMouse(NULL); + if ((ID2TRIP(obj) == HEAD_TRIPLE) || (ID2TRIP(obj) == HEAD2_TRIPLE)) + bmp = bitmaps_3d[BMAP_NUM_3D(ObjProps[OPNUM(obj)].bitmap_3d) + objs[obj].info.current_frame]; + else + bmp = bitmaps_2d[OPNUM(obj)]; + + if (bmp == NULL) return; + + object_on_cursor = obj; + input_cursor_mode = INPUT_OBJECT_CURSOR; +#ifdef SVGA_SUPPORT + if (convert_use_mode != 0) { + grs_canvas temp_canv; + // Get a new bigger bitmap + gr_init_bm(&svga_cursor_bmp, svga_cursor_bits, BMT_FLAT8, BMF_TRANS, + lg_min(MODE_SCONV_X(bmp->w, 2), SVGA_CURSOR_WIDTH), + lg_min(MODE_SCONV_Y(bmp->h, 2), SVGA_CURSOR_HEIGHT)); + gr_make_canvas(&svga_cursor_bmp, &temp_canv); + + // Draw into it + gr_push_canvas(&temp_canv); + gr_clear(0); + gr_scale_bitmap(bmp, 0, 0, svga_cursor_bmp.w, svga_cursor_bmp.h); + gr_pop_canvas(); + + // use it + bmp = &svga_cursor_bmp; + } +#endif + hotspot.x = bmp->w / 2; + hotspot.y = bmp->h / 2; + uiMakeBitmapCursor(&object_cursor, bmp, hotspot); +#ifdef CURSOR_BACKUPS + uiMakeBitmapCursor(&backup_object_cursor, bmp, hotspot); +#endif + uiPushSlabCursor(&fullscreen_slab, &object_cursor); + uiPushSlabCursor(&main_slab, &object_cursor); + uiShowMouse(NULL); + look_at_object(obj); +} diff --git a/engine/src/GameSrc/invent.c b/engine/src/GameSrc/invent.c new file mode 100644 index 0000000..cf5baa0 --- /dev/null +++ b/engine/src/GameSrc/invent.c @@ -0,0 +1,3422 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/invent.c $ + * $Revision: 1.240 $ + * $Author: dc $ + * $Date: 1994/11/22 15:58:48 $ + * + */ + +// Source code for inventory manipulation / display + +#include + +#include "email.h" +#include "invent.h" +#include "leanmetr.h" +#include "objprop.h" +#include "objwpn.h" +#include "objwarez.h" +#include "objsim.h" +#include "objuse.h" +#include "status.h" +#include "tools.h" +#include "colors.h" +#include "player.h" +#include "weapons.h" +#include "drugs.h" +#include "grenades.h" +#include "wares.h" +#include "cybstrng.h" +#include "gamestrn.h" +#include "sideicon.h" +#include "gameloop.h" +#include "loops.h" +#include "input.h" +#include "mfdext.h" +#include "objbit.h" +#include "fullscrn.h" +#include "cit2d.h" +#include "gr2ss.h" +#include "criterr.h" +#include "view360.h" +#include "objload.h" +#include "invpages.h" +#include "sfxlist.h" +#include "musicai.h" +#include "emailbit.h" +#include "popups.h" + +#include "otrip.h" +#include "gamescr.h" +#include "amap.h" + +#include "game_screen.h" // was screen.h? + +//#include +//#include + +/***************************************/ +/* INVENTORY DISPLAY MODULE */ +/*-------------------------------------*/ +/*--------------------------------------------- +The inventory display is arranged into a number of +/pages/, each of which can contain one or more +/lists/. In general, a list shows "what types of +a particular thing you have," and "how many of +each do you have." There is a list for weapons, +one for grenades, one for drugs, etc. + +The contents of each list is, more or less, defined +by two arrays, and "exists" array and a "quantity" +array. For a particular list, say, the grenades +list, exists[N] is non-zero if you have any +grenades of type N, and quant[N] is the number of +grenades of that type you have.You astutely +observe: "Why are these lists different, when the +quantity of something is always zero if you have +none of it, and thus the quantity array and the +exists array could be the same?" The answer is: +because it's goofy. + +For each list, the inventory display maintains a +state array describing what stuff was actually +DRAWN, so it can redraw incrementally. The +elements of the array are structures of type +/quantity_state/, defined below. + +A list can be divided onto different pages of the +inventory, and each section of a list that +appears on its own page is called a /display/. +The inventory panel figures out what to display +by looking through a huge list of all the +displays in the game. Adding a new display is as +simple as adding a new element to this array. Of +course, that element is a huge wonking structure +describing when and where and how to draw, use, +and select items on the display. + +And now, the code... +----------------------------------------------*/ + +#define KEY_CODE_ESC 0x1b + +// ------------- +// DISPLAY STUFF +// ------------- +// ------- +// DEFINES +// ------- +#define INVENT_CHANGED \ + if (_current_loop <= FULLSCREEN_LOOP) \ + chg_set_flg(INVENTORY_UPDATE); + +// colors & fonts +#define TITLE_COLOR (RED_BASE + 5) +#define ITEM_COLOR (0x5A) +#define SELECTED_ITEM_COLOR (0x4C) +#define BRIGHT_ITEM_COLOR (0xE7) +#define DULL_ITEM_COLOR (0x5F) +// let us no longer pretend we have differenet fonts for everything +#define ITEM_FONT RES_tinyTechFont +#define WEAPONS_FONT ITEM_FONT + +// Screen margins/locations/proportions +#define TOP_MARGIN 2 // was 15 +#define LEFT_MARGIN 4 +#define RIGHT_MARGIN 3 +#define Y_STEP 6 +#define WEAPON_X LEFT_MARGIN +#define AMMO_X 70 +#define GRENADE_LEFT_X (AMMO_X + 4) +#define GRENADE_RIGHT_X (GRENADE_LEFT_X + 30) +#define DRUG_RIGHT_X (INVENTORY_PANEL_WIDTH - RIGHT_MARGIN) +#define DRUG_LEFT_X (DRUG_RIGHT_X - 35) +#define AMMO_LEFT_1 WEAPON_X +#define AMMO_RIGHT_1 (AMMO_LEFT_1 + 40) +#define AMMO_LEFT_2 (AMMO_RIGHT_1 + 4) +#define AMMO_RIGHT_2 (AMMO_LEFT_2 + 40) +#define AMMO_LEFT_3 (AMMO_RIGHT_2 + 4) +#define AMMO_RIGHT_3 (AMMO_LEFT_3 + 40) +#define CENTER_X (INVENTORY_PANEL_WIDTH / 2) +#define RIGHT_X INVENTORY_PANEL_WIDTH +#define ONETHIRD_X (INVENTORY_PANEL_WIDTH / 3) +#define TWOTHIRDS_X (2 * INVENTORY_PANEL_WIDTH / 3) + +// Page button defines +#define FIRST_BTTN_X (3) +#define INVENT_BTTN_Y (2) +#define INVENT_BTTN_HT 3 +#define INVENT_BTTN_WD 18 +#define BUTTON_X_STEP 24 + +// Hey, these colors are stolen from mfd +#define INVENT_BTTN_EMPTY 0xcb +#define INVENT_BTTN_FLASH_ON 0x35 +#define INVENT_BTTN_FLASH_OFF 0xcb +#define INVENT_BTTN_SELECT 0x77 + +typedef enum { + BttnOff = 0, + BttnDummy = 1, + BttnActive = 2, + BttnFlashOff = 3, + BttnFlashOn = 16, + NUM_BUTTON_STATES = BttnFlashOn + 1 +} invent_bttn_state; + +#define FlashOn(state) (((state)&0x1) == 0) +#define Flashing(state) ((state) >= BttnFlashOff && (state) <= BttnFlashOn) + +// mapping from button states to colors +ubyte _bttn_state2color[] = { + INVENT_BTTN_EMPTY, INVENT_BTTN_EMPTY, INVENT_BTTN_SELECT, INVENT_BTTN_FLASH_OFF, INVENT_BTTN_FLASH_ON, +}; + +#define bttn_state2color(state) _bttn_state2color[Flashing(state) ? BttnFlashOff + !(state & 1) : state] + +#define INVENT_BTTN_FLASH_TIME 128 + +#define NUM_PAGE_BUTTONS 6 + +// Page stuff +#define WEAPON_PAGES 1 +#define WEAPONS_PER_PAGE 7 +#define DRUG_PAGES 1 +#define DRUGS_PER_PAGE 7 +#define GRENADE_PAGES 1 +#define GRENADES_PER_PAGE 7 +#define AMMO_PAGES 3 +#define AMMO_PER_PAGE 3 +#define ITEMS_PER_PAGE 7 + +// Misc +#define BUFSZ 50 +#define NULL_ACTIVE -1 // the null active + +// Object adding return codes + +typedef enum { ADD_FAIL, ADD_POP, ADD_SWAP, ADD_REJECT, ADD_NOROOM, ADD_NOEFFECT } AddResult; + +#define IS_POP_RESULT(r) ((r) == ADD_POP || (r) == ADD_NOEFFECT) + +typedef struct _quantity_state { + ushort num; // item number + ubyte exist; // do we have it; + ubyte quant; // quantity + byte pad; +} quantity_state; + +// Get the correct color for an item, given its rank in the list. +typedef uchar (*color_func)(void *dp, int num); + +// Get the string name of an item, given its rank in the +// list. +typedef char *(*name_func)(void *dp, int num, char *buf); + +struct _inventory_display_list; + +// Get the string quantity of an item, given its rank in the +// list,and its actual quantity. +typedef char *(*quant_string_func)(struct _inventory_display_list *dp, int num, int quant, char *buf); + +// Draw a display +typedef void (*draw_func)(struct _inventory_display_list *dp); + +// Select an item given its rank in the list +typedef uchar (*select_func)(struct _inventory_display_list *dp, int itemnum); + +// Use an item given its rank in the list. +typedef uchar (*use_func)(struct _inventory_display_list *dp, int itemnum); + +// Add an object to a list +typedef ubyte (*add_func)(struct _inventory_display_list *dp, int itemnum, ObjID *obj, uchar select); + +// Remove and object from a list +typedef void (*drop_func)(struct _inventory_display_list *dp, int itemnum); + +typedef struct _inventory_display_list { + ushort pgnum; // Page number this display is on + short relnum; // Relative page number for this list. + short left, right; // Left and right pixel edge + short top; // Top pixel margin + ubyte titlecolor; // Title color. Duh. + ubyte listcolor; // color for list items. if greater than 239, + // it's a color func. + ushort first; // first list item to start at. + ushort pgsize; // Number of items in each list page + ushort listlen; // Number of total list items + int titlenum; // String number of title. + int activenum; // index into player_struct.actives + int offset; // offset of quantities into player struct + int mfdtype; // inventory time for set_inventory_mfd + name_func name; // given a type number, give us the name + quant_string_func quant; // Given an item number, give us the quantity string + draw_func draw; // draw this inv_display + select_func select; // select a row + use_func use; // use a row + int add_classes; // Classes of objects that this list represents + add_func add; // function to add an object to this list. + drop_func drop; // remove an object from a row. + int basetrip; // triple of item number zero. + int (*toidx)(int); // convert from a triple to an index (NOT USED) + // state data + uchar dummy; // used to be known_active + quantity_state *lines; // lines of state data +} inv_display; + +int known_actives[NUM_ACTIVES]; + +#define NULL_PAGE 0xFFFF +extern inv_display inv_display_list[]; + +color_func color_func_list[] = {email_color_func}; +#define EMAIL_COLOR_FUNC 240 + +// ------- +// GLOBALS +// ------- + +// The current inventory "page" + +short inventory_page = 0; +uchar show_all_actives = FALSE; + +// The last page we drew +short inv_last_page = INV_BLANK_PAGE; + +LGRegion *inventory_region; +extern LGRegion *inventory_region_game, *inventory_region_full; +LGRegion **all_inventory_regions[] = {&inventory_region_game, &inventory_region_full}; + +#define NUM_INVENT_REGIONS (sizeof(all_inventory_regions) / sizeof(LGRegion **)) + +static struct _weapon_list_state { + ubyte active; + weapon_slot slots[WEAPON_PAGES * WEAPONS_PER_PAGE]; + ubyte ammo_available[WEAPON_PAGES * WEAPONS_PER_PAGE]; +} weapon_list; + +quantity_state generic_lines[48]; + +// page button state +invent_bttn_state page_button_state[NUM_PAGE_BUTTONS] = { + BttnOff, BttnOff, BttnOff, BttnDummy, BttnDummy, BttnOff, +}; +// Last button state that was actually drawn. +invent_bttn_state old_button_state[NUM_PAGE_BUTTONS] = {BttnDummy, BttnDummy, BttnDummy, + BttnDummy, BttnDummy, BttnDummy}; + +LGRegion *pagebutton_region; + +// DRAWING STUFF +grs_bitmap inv_backgnd; +grs_canvas inv_norm_canvas; +grs_canvas inv_fullscrn_canvas; +grs_canvas inv_view360_canvas; +grs_canvas *pinv_canvas = &inv_norm_canvas; + +grs_canvas inv_gamepage_canvas; +grs_canvas inv_fullpage_canvas; +grs_canvas *ppage_canvas = &inv_gamepage_canvas; + +#define inv_canvas (*pinv_canvas) + +#define NUM_PAGE_BTTNS NUM_PAGE_BUTTONS + +#ifdef OLD_BUTTON_CURSORS +LGCursor invent_bttn_cursors[NUM_PAGE_BTTNS]; +grs_bitmap invent_bttn_bitmaps[NUM_PAGE_BTTNS]; +Ref invent_bttn_curs_ids[NUM_PAGE_BTTNS] = { + REF_IMG_bmInventWeapon, REF_IMG_bmInventHardware, REF_IMG_bmInventGeneral, + REF_IMG_bmTargetCursor, REF_IMG_bmInventCombatSoft, REF_IMG_bmInventMiscSoft, +}; +#else +LGCursor invent_bttn_cursor; +grs_bitmap invent_bttn_bitmap; +#endif + +static char *cursor_strings[NUM_PAGE_BUTTONS]; +static char cursor_string_buf[128]; + +#define BUTTON_PANEL_Y (INVENTORY_PANEL_Y + INVENTORY_PANEL_HEIGHT) + +#define INVENT_BUTTON_PANEL_X (-1) +#define INVENT_BUTTON_PANEL_Y (196 - BUTTON_PANEL_Y) + +// --------------------- +// Internal Prototypes +// --------------------- +ubyte add_to_some_page(ObjID obj, uchar select); +void draw_inventory_string(char *s, int x, int y, uchar clear); +void clear_inventory_region(short x1, short y1, short x2, short y2); +void draw_quant_line(char *name, char *quant, long color, uchar active, short left, short right, short y); +void draw_quant_list(inv_display *dp, uchar newpage); +int get_item_at_pixrow(inv_display *dp, int row); +char *weapon_name_func(void *, int num, char *buf); +char *weapon_quant_func(int num, char *buf); +void draw_weapons_list(inv_display *dp); +uchar inventory_select_weapon(inv_display *dp, int w); +uchar weapon_use_func(inv_display *dp, int w); +ubyte weapons_add_func(inv_display *dp, int row, ObjID *objP, uchar select); +void weapon_drop_func(inv_display *dp, int itemnum); +ubyte generic_add_func(inv_display *dp, int row, ObjID *idP, uchar select); +void generic_drop_func(inv_display *dp, int row); +char *null_name_func(inv_display *dp, int n, char *buf); +static char *grenade_name_func(void *vdp, int n, char *buf); +uchar grenade_use_func(inv_display *dp, int row); +ubyte grenade_add_func(inv_display *dp, int row, ObjID *idP, uchar select); +char *drug_name_func(inv_display *dp, int n, char *buf); +uchar drug_use_func(inv_display *dp, int row); +char *ammo_name_func(void *, int n, char *buf); +void hardware_add_specials(int n, int ver); +ubyte ware_add_func(inv_display *dp, int, ObjID *idP, uchar select); +void ware_drop_func(inv_display *dp, int row); +char *null_quant_func(inv_display *, int, int, char *buf); +void draw_general_list(inv_display *dp); +uchar general_use_func(inv_display *dp, int row); +ubyte inv_empty_trash(void); +ubyte add_access_card(inv_display *dp, ObjID *idP, uchar select); +ubyte general_add_func(inv_display *dp, int row, ObjID *idP, uchar select); +void general_drop_func(inv_display *, int row); +uchar inv_select_general(inv_display *dp, int w); +void email_more_draw(inv_display *dp); +uchar email_more_use(inv_display *dp, int); +uchar email_use_func(inv_display *dp, int row); +void email_select_func(inv_display *dp, int row); +ubyte email_add_func(inv_display *, int, ObjID *idP, uchar select); +void email_drop_func(inv_display *, int); +char *log_name_func(void *, int num, char *buf); +uchar log_use_func(inv_display *dp, int row); +void inventory_draw_page(int pgnum); +void draw_page_button_panel(); +uchar do_selection(inv_display *dp, int row); +void add_object_on_cursor(inv_display *dp, int row); +uchar inventory_handle_leftbutton(uiEvent *ev, inv_display *dp, int row); +uchar inventory_handle_rightbutton(uiEvent *ev, LGRegion *reg, inv_display *dp, int row); +uchar inventory_mouse_handler(uiEvent *ev, LGRegion *r, intptr_t); +uchar pagebutton_mouse_handler(uiEvent *ev, LGRegion *r, intptr_t); +uchar invent_hotkey_func(ushort, uint32_t, intptr_t data); +void init_invent_hotkeys(void); +void gen_log_displays(int pgnum); +void absorb_object_on_cursor(ushort keycode, uint32_t context, intptr_t data); +uchar gen_inv_page(int pgnum, int *i, inv_display **dp); +uchar gen_inv_displays(int *i, inv_display **dp); + +// --------------------- +// DISPLAY LIST ROUTINES +// --------------------- + +// -------- +// ROUTINES +// -------- + +void push_inventory_cursors(LGCursor *newcurs) { + int i; + + for (i = 0; i < NUM_INVENT_REGIONS; i++) { + uiPushRegionCursor(*(all_inventory_regions[i]), newcurs); + } +} + +void pop_inventory_cursors(void) { + int i; + + for (i = 0; i < NUM_INVENT_REGIONS; i++) { + uiPopRegionCursor(*(all_inventory_regions[i])); + } +} + +// draw a string in relative coordinates +void draw_inventory_string(char *s, int x, int y, uchar clear) { + short w, h; + short a, b, c, d; + + gr_string_size(s, &w, &h); + if (w <= 0 || h <= 0 || strlen(s) == 0) + return; + STORE_CLIP(a, b, c, d); + // Warning(("draw_string clip %d %d %d %d\n",x-1,y-1,x+w,y+h)); + ss_safe_set_cliprect(x - 1, y - 1, x + w, y + h); + if (!full_game_3d) { + + LGRect r; + r.ul.x = x - 1; + r.ul.y = y - 1; + r.lr.x = x + w; + r.lr.y = y + h; + RECT_MOVE(&r, MakePoint(INVENTORY_PANEL_X, INVENTORY_PANEL_Y)); + uiHideMouse(&r); + if (clear) + ss_bitmap(&inv_backgnd, 0, 0); + // gr_bitmap(&inv_backgnd, 0, 0); + draw_shadowed_string(s, x, y, FALSE); + uiShowMouse(&r); + } else { + long oldcolor = gr_get_fcolor(); + if (clear) { + gr_set_fcolor(0); + ss_rect(x, y, x + w, y + h - 1); + } + gr_set_fcolor(oldcolor); + draw_shadowed_string(s, x, y, TRUE); + } + RESTORE_CLIP(a, b, c, d); +} + +void clear_inventory_region(short x1, short y1, short x2, short y2) { + LGRect r; + short a, b, c, d; + + if (!full_game_3d) { + r.ul.x = x1; + r.ul.y = y1; + r.lr.x = x2; + r.lr.y = y2; + STORE_CLIP(a, b, c, d); + ss_safe_set_cliprect(x1, y1, x2, y2); + RECT_MOVE(&r, MakePoint(INVENTORY_PANEL_X, INVENTORY_PANEL_Y)); + uiHideMouse(&r); + ss_bitmap(&inv_backgnd, 0, 0); + // gr_bitmap(&inv_backgnd, 0, 0); + uiShowMouse(&r); + RESTORE_CLIP(a, b, c, d); + } else { + gr_set_fcolor(0); + ss_rect(x1 - 1, y1, x2 + 1, y2); + } +} + +// Draw a single line of the weapons list +void draw_quant_line(char *name, char *quant, long color, uchar active, short left, short right, short y) { + short ht, wd; + + gr_string_size(name, &wd, &ht); + wd = gr_string_width(quant); + clear_inventory_region(left, y, right, y + ht); + if (active) + gr_set_fcolor(SELECTED_ITEM_COLOR); + else if (color < 256) + gr_set_fcolor(color); + draw_inventory_string(name, left, y, FALSE); + draw_inventory_string(quant, right - wd, y, FALSE); +} + +void draw_quant_list(inv_display *dp, uchar newpage) { + int wtype; + int cnt; + int y, i; + char buf[BUFSZ]; + ubyte *quant = (ubyte *)&player_struct + dp->offset; + ubyte *exist = quant; + int active = (dp->activenum == NULL_ACTIVE) ? -1 : player_struct.actives[dp->activenum]; + int known_active = (dp->activenum == NULL_ACTIVE) ? -1 : known_actives[dp->activenum]; + quantity_state *line = dp->lines + dp->relnum * dp->pgsize; + + // Hey, what if we don't have our active item anymore... + if (active >= 0 && active < dp->listlen && quant[active] == 0) { + player_struct.actives[dp->activenum] = active = MFD_INV_NOTYPE; + set_inventory_mfd(dp->mfdtype, MFD_INV_NOTYPE, TRUE); + } + if (newpage && dp->titlenum != REF_STR_Null) { + gr_set_fcolor(dp->titlecolor); + get_string(dp->titlenum, buf, BUFSZ); + draw_inventory_string(buf, dp->left, dp->top, TRUE); + } + if (dp->first != 0) + wtype = dp->first; + else + for (wtype = 0, cnt = 0; wtype < dp->listlen && cnt < dp->pgsize * dp->relnum; wtype++) { + if (exist[wtype] > 0) + cnt++; + } + for (y = dp->top + Y_STEP, i = 0, cnt = 0; cnt < dp->pgsize; i++, wtype++) { + if (wtype >= dp->listlen) { + if (line->num < dp->listlen) { + clear_inventory_region(dp->left, y, dp->right, y + Y_STEP); + } + y += Y_STEP; + line->num = wtype; + line++; + cnt++; + continue; + } + if (exist[wtype] != 0) { + uchar newactive = active != known_active; + // note the hack for combat softwares + uchar curractive = + ((show_all_actives && dp->pgnum == INV_MAIN_PAGE) || player_struct.current_active == dp->activenum) || + dp->activenum == ACTIVE_COMBAT_SOFT; + uchar changed = newpage || newactive || (line->num != wtype) || (line->quant != quant[wtype]) || + (line->exist != exist[wtype]); + line->num = wtype; + line->quant = quant[wtype]; + line->exist = exist[wtype]; + if (changed) { + char buf[BUFSZ] = ""; + char buf2[BUFSZ] = ""; + uchar is_active = wtype == active && curractive; + uchar col; + + if (dp->name != NULL) + dp->name(dp, wtype, buf); + if (dp->quant != NULL) + dp->quant(dp, wtype, quant[wtype], buf2); + col = dp->listcolor; + if (col > 239) + col = (color_func_list[col - 240])(dp, wtype); + draw_quant_line(buf, buf2, col, is_active, dp->left, dp->right, y); + } + cnt++; + line++; + y += Y_STEP; + } + } +} + +void set_current_active(int activenum) { + int old = player_struct.current_active; + known_actives[old] = -1; + player_struct.current_active = activenum; + known_actives[activenum] = -1; +} + +// Get the item at a particular pixel y +int get_item_at_pixrow(inv_display *dp, int row) { + int linenum = dp->relnum * dp->pgsize; + int ipanel_y; + int y_step; + int r1, r2; + +#ifdef STEREO_SUPPORT + if (convert_use_mode == 5) { + switch (i6d_device) { + case I6D_CTM: + ipanel_y = 1; + y_step = Y_STEP << 2; + break; + case I6D_VFX1: + ipanel_y = INVENTORY_PANEL_Y >> 1; + // ipanel_y = (200 - inv_fullscrn_canvas.bm.h); + // y_step = Y_STEP << 1; + y_step = 10; + break; + default: + break; + } + } else +#endif + { + ipanel_y = INVENTORY_PANEL_Y; + y_step = Y_STEP; + } + r1 = row; + row -= ipanel_y + dp->top + y_step; + if (row < 0) + return -1; + r2 = row; + row /= y_step; + if (row >= dp->pgsize) + return -1; + // Warning(("row = %d = (%d - %d + %d + %d) = %d / %d\n",row,r1,ipanel_y,dp->top,y_step,r2,y_step)); + + return linenum + row; +} + + // -------------------- + // WEAPON DISPLAY FUNCS + // -------------------- + +#define WEAP_CLASSES (1 << CLASS_GUN) +#define WEAP_TRIP MAKETRIP(CLASS_GUN, 0, 0) + +char *weapon_name_func(void *v, int num, char *buf) { + weapon_slot *ws = &player_struct.weapons[num]; + get_weapon_name(ws->type, ws->subtype, buf); + return buf; +} + +char *weapon_quant_func(int num, char *buf) { + int triple, num_ammo_types, ammo_subclass; + ubyte ammo_types[3]; + int i = 0; + weapon_slot *ws = &player_struct.weapons[num]; + uchar energy = is_energy_weapon(ws->type); + + if (is_handtohand_weapon(ws->type)) { + buf[0] = '\0'; + return buf; + } + + if ((!energy) && (ws->type != GUN_SUBCLASS_BEAMPROJ)) { + int ammo = ws->ammo; + if (ws->ammo_type == EMPTY_WEAPON_SLOT) { + ammo = 0; + } + + get_available_ammo_type(ws->type, ws->subtype, &num_ammo_types, ammo_types, &ammo_subclass); + + triple = MAKETRIP(CLASS_AMMO, ammo_subclass, ws->ammo_type); + if (ammo > 0) { + buf[i++] = AMMO_TYPE_LETTER((CPTRIP(triple))); + buf[i++] = ' '; + } + if (ammo == 0 && num_ammo_types > 0) + get_string(REF_STR_AmmoLoad, buf, BUFSZ); + else + sprintf(buf + i, "%d", ammo); + // itoa(ammo,buf+i,10); + } else { + if (ws->heat > OVERHEAT_THRESHOLD) + get_string(REF_STR_GunHot, buf, BUFSZ); + else if (OVERLOAD_VALUE(ws->setting)) + get_string(REF_STR_AmmoOver, buf, BUFSZ); + else if (ws->heat > WARM_THRESHOLD) + get_string(REF_STR_GunWarm, buf, BUFSZ); + else + get_string(REF_STR_GunOK, buf, BUFSZ); + } + return buf; +} + +void draw_weapons_list(inv_display *dp) { + uchar newpage = inv_last_page != inventory_page; + char buf[BUFSZ]; + int i, s; + short y; + gr_set_font(ResLock(WEAPONS_FONT)); + + if (newpage) { + gr_set_fcolor(dp->titlecolor); + get_string(REF_STR_WeaponTitle, buf, BUFSZ); + draw_inventory_string(buf, WEAPON_X, TOP_MARGIN, TRUE); + get_string(REF_STR_AmmoTitle, buf, BUFSZ); + draw_inventory_string(buf, AMMO_X - gr_string_width(buf), TOP_MARGIN, TRUE); + } + s = dp->relnum * WEAPONS_PER_PAGE; + y = TOP_MARGIN + Y_STEP; + for (i = 0; i < WEAPONS_PER_PAGE && s < NUM_WEAPON_SLOTS; i++, s++, y += Y_STEP) { + int num_ammo_types, dummy1; + ubyte dummy2[3]; + uchar avail; + uchar newactive = weapon_list.active != player_struct.actives[ACTIVE_WEAPON]; + uchar changed; + + get_available_ammo_type(weapon_list.slots[s].type, weapon_list.slots[s].subtype, &num_ammo_types, dummy2, + &dummy1); + avail = (num_ammo_types > 0); + changed = newpage || (avail != weapon_list.ammo_available[s]) || + memcmp(&weapon_list.slots[s], &player_struct.weapons[s], sizeof(weapon_slot)) != 0 || + (newactive && s == weapon_list.active) || (newactive && s == player_struct.actives[ACTIVE_WEAPON]); + weapon_list.slots[s] = player_struct.weapons[s]; + weapon_list.ammo_available[s] = avail; + if (!changed) + continue; + if (weapon_list.slots[s].type != EMPTY_WEAPON_SLOT) { + char name[BUFSZ]; + char quant[BUFSZ]; + weapon_name_func(dp, s, name); + weapon_quant_func(s, quant); + draw_quant_line(name, quant, dp->listcolor, s == player_struct.actives[ACTIVE_WEAPON], WEAPON_X, AMMO_X, y); + } else + clear_inventory_region(WEAPON_X, y, AMMO_X, y + Y_STEP); + } + weapon_list.active = player_struct.actives[ACTIVE_WEAPON]; + ResUnlock(WEAPONS_FONT); +} + +uchar inventory_select_weapon(inv_display *dp, int w) { + uchar retval = FALSE; + int aw = player_struct.actives[ACTIVE_WEAPON]; +#ifndef NO_DUMMIES + inv_display *newdisp; + newdisp = dp; +#endif // NO_DUMMIES + if (player_struct.weapons[w].type == EMPTY_WEAPON_SLOT) + goto out; + if (aw != w) { + weapon_slot *ws = &player_struct.weapons[w]; + play_digi_fx(SFX_INVENT_SELECT, 1); + change_selected_weapon(w); + player_struct.last_fire = 0; + player_struct.fire_rate = weapon_fire_rate(ws->type, ws->subtype); + } + player_struct.actives[ACTIVE_WEAPON] = w; + set_inventory_mfd(MFD_INV_WEAPON, w, TRUE); + set_inventory_mfd(MFD_INV_AMMO, 0, TRUE); + mfd_notify_func(NOTIFY_ANY_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, TRUE); + INVENT_CHANGED; + retval = TRUE; +out: + return retval; +} + +uchar weapon_use_func(inv_display *dp, int w) { + if (player_struct.weapons[w].type == EMPTY_WEAPON_SLOT) + return FALSE; + inventory_select_weapon(dp, w); + mfd_change_slot(mfd_grab_func(MFD_WEAPON_FUNC, MFD_WEAPON_SLOT), MFD_WEAPON_SLOT); + return TRUE; +} + +ubyte weapons_add_func(inv_display *dp, int row, ObjID *objP, uchar select) { + ubyte retval = ADD_REJECT; + ObjID obj = *objP; + weapon_slot *ws; + weapon_slot tmp; + ObjSpecID spec; + play_digi_fx(SFX_INVENT_ADD, 1); + row += dp->pgsize * dp->relnum; + if (player_struct.weapons[NUM_WEAPON_SLOTS - 1].type == EMPTY_WEAPON_SLOT) + row = NUM_WEAPON_SLOTS - 1; + if (row < 0 || row >= NUM_WEAPON_SLOTS || player_struct.weapons[row].type == EMPTY_WEAPON_SLOT) { + for (row = 0; row < NUM_WEAPON_SLOTS; row++) + if (player_struct.weapons[row].type == EMPTY_WEAPON_SLOT) + break; + if (row >= NUM_WEAPON_SLOTS) { + string_message_info(REF_STR_InvNoRoom); + return ADD_FAIL; + } + } + ws = &player_struct.weapons[row]; + tmp = *ws; + spec = objs[obj].specID; + + ws->type = objs[obj].subclass; + ws->subtype = objs[obj].info.type; + ws->ammo = objGuns[spec].ammo_count; + ws->ammo_type = objGuns[spec].ammo_type; + if (player_struct.actives[ACTIVE_WEAPON] == row) + set_inventory_mfd(MFD_INV_WEAPON, row, TRUE); + if (tmp.type != EMPTY_WEAPON_SLOT) { + objs[obj].subclass = tmp.type; + objs[obj].info.type = tmp.subtype; + objGuns[spec].ammo_type = tmp.ammo_type; + objGuns[spec].ammo_count = tmp.ammo; + retval = ADD_SWAP; + } else { + obj_destroy(obj); + retval = ADD_POP; + } + if (select) + inventory_select_weapon(dp, row); + + if (player_struct.actives[ACTIVE_WEAPON] == row) { + set_inventory_mfd(MFD_INV_WEAPON, row, TRUE); + mfd_notify_func(MFD_WEAPON_FUNC, MFD_WEAPON_SLOT, TRUE, MFD_ACTIVE, TRUE); + } + // in case ammo mfd + mfd_notify_func(NOTIFY_ANY_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, TRUE); + + void SetMotionCursorsColorForActiveWeapon(void); + SetMotionCursorsColorForActiveWeapon(); + + return retval; +} + +void weapon_drop_func(inv_display *dp, int itemnum) { + weapon_slot *ws; + ObjID obj; + ObjSpecID spec; + int it; + + if (itemnum < 0 || itemnum >= dp->listlen) + return; + ws = &player_struct.weapons[itemnum]; + if (ws->type == EMPTY_WEAPON_SLOT) + return; + obj = obj_create_base(MAKETRIP(CLASS_GUN, ws->type, ws->subtype)); + if (obj == OBJ_NULL) { + return; + } + spec = objs[obj].specID; + objGuns[spec].ammo_type = ws->ammo_type; + objGuns[spec].ammo_count = ws->ammo; + push_cursor_object(obj); + // preserve selected weapon. + if (itemnum < player_struct.actives[ACTIVE_WEAPON]) + player_struct.actives[ACTIVE_WEAPON]--; + for (it = itemnum + 1; it < NUM_WEAPON_SLOTS; it++) { + player_struct.weapons[it - 1] = player_struct.weapons[it]; + } + player_struct.weapons[NUM_WEAPON_SLOTS - 1].type = EMPTY_WEAPON_SLOT; + if (itemnum > 0 && player_struct.weapons[player_struct.actives[ACTIVE_WEAPON]].type == EMPTY_WEAPON_SLOT) + player_struct.actives[ACTIVE_WEAPON]--; + + // in case ammo mfd + mfd_notify_func(NOTIFY_ANY_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, TRUE); + // In case the weapons MFD was looking at that weapon, nix it + if (player_struct.weapons[player_struct.actives[ACTIVE_WEAPON]].type == EMPTY_WEAPON_SLOT) + mfd_notify_func(MFD_EMPTY_FUNC, MFD_WEAPON_SLOT, TRUE, MFD_EMPTY, TRUE); + else + set_inventory_mfd(MFD_INV_WEAPON, player_struct.actives[ACTIVE_WEAPON], TRUE); + INVENT_CHANGED; + + void SetMotionCursorsColorForActiveWeapon(void); + SetMotionCursorsColorForActiveWeapon(); +} + +// ------------- +// GENERIC FUNCS +// ------------- + +static char *generic_name_func(void *vdp, int num, char *buf) { + int trip = nth_after_triple(((inv_display *)vdp)->basetrip, num); + get_object_short_name(trip, buf, BUFSZ); + return buf; +} + +static void generic_draw_list(inv_display *dp) { + uchar newpage = inv_last_page != inventory_page; + gr_set_font(ResLock(ITEM_FONT)); + + draw_quant_list(dp, newpage); + ResUnlock(ITEM_FONT); +} + +ubyte generic_add_func(inv_display *dp, int row, ObjID *idP, uchar select) { + ObjID id = *idP; + int trip = ID2TRIP(id); + int n = OPTRIP(trip) - OPTRIP(dp->basetrip); + int obclass = objs[id].obclass; +#ifndef NO_DUMMIES + int guf; + guf = row; +#endif // NO_DUMMIES + play_digi_fx(SFX_INVENT_ADD, 1); + if (n >= 0 && n < dp->listlen) { + ubyte *quants = (ubyte *)&player_struct + dp->offset; + quants[n]++; + if (dp->activenum != NULL_ACTIVE) { + if (select) { + player_struct.actives[dp->activenum] = n; + play_digi_fx(SFX_INVENT_SELECT, 1); + } + if (select || n == player_struct.actives[dp->activenum]) + set_inventory_mfd(dp->mfdtype, n, TRUE); + } + obj_destroy(id); + // This is a special-case hack for cartridges. + if (obclass == CLASS_AMMO) { + mfd_notify_func(NOTIFY_ANY_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, TRUE); + mfd_notify_func(MFD_WEAPON_FUNC, MFD_WEAPON_SLOT, FALSE, MFD_ACTIVE, TRUE); + INVENT_CHANGED; + } + return ADD_POP; + } + return ADD_REJECT; +} + +void generic_drop_func(inv_display *dp, int row) { + int itemnum = dp->lines[row].num; + ObjID obj; + int triple; + ubyte *quant; + if (itemnum < 0 || itemnum >= dp->listlen) + return; + quant = (ubyte *)&player_struct + dp->offset; + if (quant[itemnum] == 0) + return; + quant[itemnum]--; + triple = nth_after_triple(dp->basetrip, itemnum); + obj = obj_create_base(triple); + if (obj == OBJ_NULL) { + return; + } + push_cursor_object(obj); + if (dp->activenum != NULL_ACTIVE && player_struct.actives[dp->activenum] == itemnum && quant[itemnum] == 0) { + player_struct.actives[dp->activenum] = 0xFF; + set_inventory_mfd(dp->mfdtype, MFD_INV_NOTYPE, FALSE); + } + INVENT_CHANGED; +} + +static char *generic_quant_func(inv_display *dp, int n, int q, char *buf) { +#ifndef NO_DUMMIES + char *dummy; + dummy = n + (char*)dp; +#endif // NO_DUMMIES + // itoa(q,buf,10); + sprintf(buf, "%d", q); + return buf; +} + +char *null_name_func(inv_display *dp, int n, char *buf) { +#ifndef NO_DUMMIES + char *goof; + goof = (char*)dp + n; +#endif // NO_DUMMIES + *buf = '\0'; + return buf; +} + + // ------------- + // GRENADE FUNCS + // ------------- + +#define GREN_CLASSES (1 << CLASS_GRENADE) +#define GREN_TRIP MAKETRIP(CLASS_GRENADE, 0, 0) + +static char *grenade_name_func(void *dp, int n, char *buf) { return get_grenade_name(n, buf); } + +extern uiSlab fullscreen_slab; +extern uiSlab main_slab; + +grs_bitmap grenade_bmap; +#ifdef SVGA_SUPPORT +char grenade_bmap_buffer[8700]; +#else +char grenade_bmap_buffer[700]; +#endif + +void push_live_grenade_cursor(ObjID obj) { + short w, h; + extern LGCursor object_cursor; + char live_string[22]; +#ifdef CURSOR_BACKUPS + extern grs_bitmap backup_object_cursor; + extern uchar *backup[NUM_BACKUP_BITS]; +#endif +#ifdef SVGA_SUPPORT + uchar old_over = gr2ss_override; + short temp; +#endif + grs_canvas cursor_canvas; + LGPoint hotspot; + grs_bitmap *bmap = bitmaps_2d[OPNUM(obj)]; + void *bits = grenade_bmap_buffer; + + grenade_bmap = *bmap; + gr_set_font(ResLock(ITEM_FONT)); + get_string(REF_STR_WordLiveGrenade, live_string, sizeof(live_string)); + gr_string_size(live_string, &w, &h); + w++; + h++; // compensate for shadowing +#ifdef SVGA_SUPPORT + gr2ss_override = OVERRIDE_ALL; + ss_set_hack_mode(2, &temp); + if (convert_use_mode != 0) { + grenade_bmap.w = lg_max(bmap->w, w); + grenade_bmap.h = bmap->h + h; + grenade_bmap.w = SCONV_X(grenade_bmap.w); + grenade_bmap.h = SCONV_Y(grenade_bmap.h); + } else { +#endif + grenade_bmap.w = lg_max(bmap->w, w); + grenade_bmap.h = bmap->h + h; +#ifdef SVGA_SUPPORT + } +#endif + // mprintf("bsize = %d, w * h = %d\n",sizeof(grenade_bmap_buffer), grenade_bmap.w * grenade_bmap.h); + if (sizeof(grenade_bmap_buffer) < grenade_bmap.w * grenade_bmap.h) + critical_error(0x3006); + + gr_init_bitmap(&grenade_bmap, (uchar *)bits, grenade_bmap.type, grenade_bmap.flags, grenade_bmap.w, grenade_bmap.h); + gr_init_canvas(&cursor_canvas, (uchar *)bits, BMT_FLAT8, grenade_bmap.w, grenade_bmap.h); + gr_push_canvas(&cursor_canvas); + gr_set_font(ResGet(ITEM_FONT)); + gr_clear(0); +#ifdef SVGA_SUPPORT + if (convert_use_mode > 0) { + ss_bitmap(bmap, (INV_SCONV_X(grenade_bmap.w) - bmap->w) / 2, 0); + gr_set_fcolor(0x4c); + draw_shadowed_string(live_string, (INV_SCONV_X(grenade_bmap.w) - w) / 2, INV_SCONV_Y(grenade_bmap.h) - h, TRUE); + } else { +#endif + ss_bitmap(bmap, (grenade_bmap.w - bmap->w) / 2, 0); + gr_set_fcolor(0x4c); + draw_shadowed_string(live_string, (grenade_bmap.w - w) / 2 + 1, grenade_bmap.h - h, TRUE); +#ifdef SVGA_SUPPORT + } +#endif + ResUnlock(ITEM_FONT); + gr_pop_canvas(); +#ifdef SVGA_SUPPORT + ss_set_hack_mode(0, &temp); + gr2ss_override = old_over; + if (convert_use_mode != 0) { + hotspot.x = grenade_bmap.w / 2; + hotspot.y = grenade_bmap.h / 2; + } else { +#endif + hotspot.x = grenade_bmap.w / 2; + hotspot.y = grenade_bmap.h / 2; +#ifdef SVGA_SUPPORT + } +#endif + uiHideMouse(NULL); + uiMakeBitmapCursor(&object_cursor, &grenade_bmap, hotspot); + uiPushSlabCursor(&fullscreen_slab, &object_cursor); + uiPushSlabCursor(&main_slab, &object_cursor); + uiShowMouse(NULL); + object_on_cursor = obj; + input_cursor_mode = INPUT_OBJECT_CURSOR; +} + +uchar grenade_use_func(inv_display *dp, int row) { + int itemnum = dp->lines[row].num; + ObjID obj; + int triple; + ubyte *quant; + ObjSpecID spec; + if (itemnum < 0 || itemnum >= dp->listlen) + return (FALSE); + quant = (ubyte *)&player_struct + dp->offset; + if (quant[itemnum] == 0) + return (FALSE); + quant[itemnum]--; + triple = nth_after_triple(dp->basetrip, itemnum); + obj = obj_create_base(triple); + if (obj == OBJ_NULL) { + return (FALSE); + } + if (dp->activenum != NULL_ACTIVE && player_struct.actives[dp->activenum] == itemnum && quant[itemnum] == 0) { + player_struct.actives[dp->activenum] = 0xFF; + set_inventory_mfd(dp->mfdtype, MFD_INV_NOTYPE, FALSE); + } + INVENT_CHANGED; + push_live_grenade_cursor(obj); + spec = objs[obj].specID; + activate_grenade(spec); + return (TRUE); +} + +ubyte grenade_add_func(inv_display *dp, int row, ObjID *idP, uchar select) { + ObjSpecID sid = objs[*idP].specID; + play_digi_fx(SFX_INVENT_ADD, 1); + if (objGrenades[sid].flags & GREN_ACTIVE_FLAG) { + string_message_info(REF_STR_InvLiveGrenade); + return ADD_FAIL; + } + return generic_add_func(dp, row, idP, select); +} + +// ---------- +// DRUG FUNCS +// ---------- +#define DRUG_CLASSES (1 << CLASS_DRUG) +#define DRUG_TRIP MAKETRIP(CLASS_DRUG, 0, 0) + +char *drug_name_func(inv_display *dp, int n, char *buf) { +#ifndef NO_DUMMIES + inv_display *dummy; + dummy = dp; +#endif // NO_DUMMIES + return get_drug_name(n, buf); +} + +uchar drug_use_func(inv_display *dp, int row) { + uchar retval = FALSE; + int n = dp->lines[row].num; + if (n < dp->listlen) { + drug_use(n); + set_inventory_mfd(dp->mfdtype, n, TRUE); + retval = TRUE; + INVENT_CHANGED; + } + return retval; +} + +// ---------- +// AMMO FUNCS +// ---------- +#define AMMO_CLASSES (1 << CLASS_AMMO) +#define AMMO_TRIP MAKETRIP(CLASS_AMMO, 0, 0) + +char *ammo_name_func(void *dp, int n, char *buf) { + int triple; + + buf[0] = AMMO_TYPE_LETTER(n); + buf[1] = ' '; + triple = get_triple_from_class_nth_item(CLASS_AMMO, n); + get_object_short_name(triple, buf + 2, 31); + return buf; +} + +// --------- +// HARDWARES +// --------- +#define HARD_CLASSES (1 << CLASS_HARDWARE) +#define HARD_TRIP MAKETRIP(CLASS_HARDWARE, 0, 0) +#define HARDWARE_PAGES 2 + +static char *ware_name_func(void *vdp, int n, char *buf) { + int type; + switch (((inv_display *)vdp)->activenum) { + case ACTIVE_HARDWARE: + type = WARE_HARD; + break; + case ACTIVE_COMBAT_SOFT: + type = WARE_SOFT_COMBAT; + break; + case ACTIVE_DEFENSE_SOFT: + type = WARE_SOFT_DEFENSE; + break; + case ACTIVE_MISC_SOFT: + type = WARE_SOFT_MISC; + break; + } + get_ware_name(type, n, buf, BUFSZ); + return NULL; +} + +static uchar ware_use_func(inv_display *dp, int row) { + int waretype; + int t = dp->lines[row].num; + if (t >= dp->listlen) + return FALSE; + switch (dp->activenum) { + case ACTIVE_HARDWARE: + waretype = WARE_HARD; + break; + case ACTIVE_COMBAT_SOFT: + waretype = WARE_SOFT_COMBAT; + break; + case ACTIVE_DEFENSE_SOFT: + waretype = WARE_SOFT_DEFENSE; + break; + case ACTIVE_MISC_SOFT: + waretype = WARE_SOFT_MISC; + break; + } + use_ware(waretype, t); + return TRUE; +} + +void hardware_add_specials(int n, int ver) { + extern WARE HardWare[NUM_HARDWAREZ]; + switch (n) { + case HARDWARE_AUTOMAP: + mfd_notify_func(MFD_MAP_FUNC, MFD_MAP_SLOT, TRUE, MFD_ACTIVE, TRUE); + { + int i; + for (i = 0; i < NUM_O_AMAP; i++) + amap_version_set(i, player_struct.hardwarez[HARDWARE_AUTOMAP]); + } + break; + case HARDWARE_TARGET: + mfd_notify_func(MFD_TARGET_FUNC, MFD_TARGET_SLOT, TRUE, MFD_ACTIVE, TRUE); + break; + case HARDWARE_SHIELD: { + int ener = 0; + if (WareActive(player_struct.hardwarez_status[CPTRIP(SHIELD_HARD_TRIPLE)])) + ener = energy_cost(CPTRIP(SHIELD_HARD_TRIPLE)); + SHIELD_SETTING_SET(player_struct.hardwarez_status[n], ver - 1); + if (ener) { + shield_set_absorb(); + ener = energy_cost(CPTRIP(SHIELD_HARD_TRIPLE)) - ener; + set_player_energy_spend(lg_min(MAX_ENERGY, player_struct.energy_spend + ener)); + } + mfd_notify_func(MFD_SHIELD_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + } break; + case HARDWARE_ENVIROSUIT: { + if (_current_loop == GAME_LOOP) + gamescr_bio_func(); + zoom_to_lean_meter(); + } break; + // Flash the email sideicon the first time you pick up the data reader to tell the player he has a log. + // After that, it is never flashed again if a new log is picked up. + case HARDWARE_EMAIL: + player_struct.hardwarez_status[HARDWARE_EMAIL] |= WARE_FLASH; + QUESTBIT_ON(0x12c); + break; + } + if (HardWare[n].sideicon != SI_NONE) { + LGPoint from; + ui_mouse_get_xy(&from.x, &from.y); + zoom_to_side_icon(from, HardWare[n].sideicon); + } +} + +ubyte ware_add_func(inv_display *dp, int nn, ObjID *idP, uchar select) { + ObjID id = *idP; + int trip = ID2TRIP(id); + uchar oneshot; + uchar bigstuff_fake = FALSE; + int n; + extern uchar shameful_obselete_flag; + + if (global_fullmap->cyber && (objs[id].obclass == CLASS_BIGSTUFF)) { + bigstuff_fake = TRUE; + trip = MAKETRIP(CLASS_SOFTWARE, objBigstuffs[objs[id].specID].data1, objBigstuffs[objs[id].specID].data2); + } + n = OPTRIP(trip) - OPTRIP(dp->basetrip); + oneshot = TRIP2CL(trip) == CLASS_SOFTWARE && TRIP2SC(trip) == SOFTWARE_SUBCLASS_ONESHOT; + if (TRIP2CL(trip) == CLASS_SOFTWARE && TRIP2SC(trip) == SOFTWARE_SUBCLASS_DATA) + return ADD_REJECT; + if (n >= 0 && n < dp->listlen) { + ubyte ver; + ubyte *quants = (ubyte *)&player_struct + dp->offset; + if (bigstuff_fake) + ver = objBigstuffs[objs[id].specID].cosmetic_value; + else + ver = (TRIP2CL(trip) == CLASS_HARDWARE) ? objHardwares[objs[id].specID].version + : objSoftwares[objs[id].specID].version; + + if (trip == GAMES_TRIPLE) + quants[n] |= ver; + else if (oneshot) + quants[n]++; + else if (quants[n] >= ver) { + string_message_info(REF_STR_AlreadyHaveOne); + shameful_obselete_flag = TRUE; + return ADD_NOEFFECT; + } else + quants[n] = ver; + + play_digi_fx(SFX_INVENT_WARE, 1); + if (select) { + player_struct.actives[dp->activenum] = n; + } + if (select || n == player_struct.actives[dp->activenum]) + set_inventory_mfd(dp->mfdtype, n, TRUE); + obj_destroy(id); + + // Tell the side icons that things may no longer be what they were + // side_icon_expose_all(); + + // If we picked up an automapper unit, let mfd slot know + if (TRIP2CL(trip) == CLASS_HARDWARE) { + hardware_add_specials(n, ver); + } + return ADD_POP; + } + return ADD_REJECT; +} + +void ware_drop_func(inv_display *dp, int n) { +#ifndef GAMEONLY + int itemnum = dp->lines[row].num; + ObjID obj; + int triple; + uchar oneshot; + ubyte *quant; + extern int nth_after_triple(int, uchar); + if (itemnum < 0 || itemnum >= dp->listlen) + return; + quant = (ubyte *)&player_struct + dp->offset; + if (quant[itemnum] == 0) + return; + triple = nth_after_triple(dp->basetrip, itemnum); + oneshot = TRIP2CL(triple) == CLASS_SOFTWARE && TRIP2SC(triple) == SOFTWARE_SUBCLASS_ONESHOT; + obj = obj_create_base(triple); + if (obj == OBJ_NULL) { + return; + } + if (dp->mfdtype == MFD_INV_HARDWARE) + objHardwares[objs[obj].specID].version = quant[itemnum]; + else + objSoftwares[objs[obj].specID].version = quant[itemnum]; + // If the ware was on, turn it off + if ((dp->mfdtype == MFD_INV_HARDWARE) && (player_struct.hardwarez_status[itemnum] & WARE_ON)) + use_ware(WARE_HARD, itemnum); // actually toggles, not uses + if (oneshot) + quant[itemnum]--; + else + quant[itemnum] = 0; + push_cursor_object(obj); + if (player_struct.actives[dp->activenum] == itemnum) { + player_struct.actives[dp->activenum] = 0xFF; + // Tell the item mfd that what it was looking at may no longer be there + set_inventory_mfd(dp->mfdtype, MFD_INV_NOTYPE, FALSE); + } + INVENT_CHANGED; + + // Tell the side icons that things are no longer what they were + side_icon_expose_all(); + + mfd_notify_func(NOTIFY_ANY_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + + // If we no longer have an automapper, let the mfd know + if (player_struct.hardwarez[HARDWARE_AUTOMAP] == 0) + mfd_notify_func(MFD_EMPTY_FUNC, MFD_MAP_SLOT, TRUE, MFD_EMPTY, TRUE); +#endif // !GAME_ONLY +} + +char *null_quant_func(inv_display *dp, int n, int q, char *buf) { + *buf = '\0'; + return buf; +} + + // ----- + // SOFTS + // ----- + +#define SOFT_PAGES 2 +#define SOFT_CLASSES (1 << CLASS_SOFTWARE) +#define COMSOFT_TRIP MAKETRIP(CLASS_SOFTWARE, SOFTWARE_SUBCLASS_OFFENSE, 0) +#define DEFSOFT_TRIP MAKETRIP(CLASS_SOFTWARE, SOFTWARE_SUBCLASS_DEFENSE, 0) +#define MISCSOFT_TRIP MAKETRIP(CLASS_SOFTWARE, SOFTWARE_SUBCLASS_ONESHOT, 0) + +#define VERSION_PREFIX (get_temp_string(REF_STR_VersionPrefix)[0]) + +// QUANTS ******************************* +static char *soft_quant_func(inv_display *dp, int n, int q, char *buf) { + int l = 1; + buf[0] = VERSION_PREFIX; + if (q < 10) + buf[l++] = q + '0'; + buf[l] = '\0'; + return buf; +} + + // COMPUTRON SUPPORT + +#define CTRON_WD 10 + +#ifdef COMPUTRONS +char *computron_quant_func(inv_display *dp, int n, int q, char *buf) { +#ifdef REALLY_DO_COMPUTRONS + ubyte exists, ctrons; + + switch (dp->mfdtype) { + case MFD_INV_SOFT_COMBAT: + exists = player_struct.softs.combat[n]; + ctrons = player_struct.softs_ctrons.combat[n]; + break; + case MFD_INV_SOFT_DEFENSE: + exists = player_struct.softs.defense[n]; + ctrons = player_struct.softs_ctrons.defense[n]; + break; + case MFD_INV_SOFT_MISC: + exists = player_struct.softs.misc[n]; + ctrons = player_struct.softs_ctrons.misc[n]; + break; + } + if (exists == 0 || ctrons == 0) + *buf = '\0'; + else + itoa(ctrons, buf, 10); +#endif + *buf = '\0'; + return buf; +} +#endif // COMPUTRONS + + // ------------------------------ + // GENERAL INVENTORY -- FUN! FUN! + // ------------------------------ + +#define GENERAL_CLASSES 0xFFFFFF80 +static ObjID general_lines[NUM_GENERAL_SLOTS]; + +#define GARBAGE_COLOR TITLE_COLOR + +void draw_general_list(inv_display *dp) { + uchar newpage = inv_last_page != inventory_page; + char buf[BUFSZ]; + int i, s; + short y; + ubyte active = player_struct.actives[dp->activenum]; + ubyte known_active = known_actives[dp->activenum]; + uchar newactive = known_active != active; + + gr_set_font(ResLock(ITEM_FONT)); + + if (newpage) { + gr_set_fcolor(dp->titlecolor); + get_string(dp->titlenum, buf, BUFSZ); + draw_inventory_string(buf, dp->left, dp->top, TRUE); + } + s = dp->relnum * dp->pgsize; + y = dp->top + Y_STEP; + for (i = 0; i < dp->pgsize && s < dp->listlen; i++, s++, y += Y_STEP) { + uchar curractive = player_struct.current_active == dp->activenum; + uchar changed = newpage || general_lines[s] != player_struct.inventory[s] || newactive; + general_lines[s] = player_struct.inventory[s]; + if (!changed) + continue; + if (general_lines[s] != OBJ_NULL) { + ulong color = dp->listcolor; + char name[BUFSZ]; + get_object_short_name(ID2TRIP(general_lines[s]), name, BUFSZ); + if (!(ObjProps[OPNUM(general_lines[s])].flags & INVENTORY_GENERAL)) + color = GARBAGE_COLOR; + draw_quant_line(name, "", color, s == active && curractive, dp->left, dp->right, y); + } else + clear_inventory_region(dp->left, y, dp->right, y + Y_STEP); + } + ResUnlock(ITEM_FONT); +} + +uchar general_use_func(inv_display *dp, int row) { + ObjID id = player_struct.inventory[row]; + if (id == OBJ_NULL) + return FALSE; + if (ObjProps[OPNUM(id)].flags & OBJECT_USE_NOCURSOR) { + object_use(id, TRUE, object_on_cursor); + // only change mfd if using object did not destroy it. + if (player_struct.current_active == dp->activenum && row <= player_struct.actives[dp->activenum]) + set_inventory_mfd(dp->mfdtype, row, TRUE); + if (player_struct.inventory[row] == id) { + set_inventory_mfd(dp->mfdtype, row, TRUE); + mfd_change_slot(mfd_grab_func(MFD_ITEM_FUNC, MFD_ITEM_SLOT), MFD_ITEM_SLOT); + } + } else if (dp->drop != NULL) + dp->drop(dp, row); + return TRUE; +} + +ubyte inv_empty_trash(void) { + uchar found = FALSE; + ubyte non_trash = 0; + ubyte trash; + ubyte last_trash = NUM_GENERAL_SLOTS; + // find the first trash object + for (trash = 0; trash < NUM_GENERAL_SLOTS; trash++) { + ObjID id = player_struct.inventory[trash]; + if (id != OBJ_NULL && !(ObjProps[OPNUM(id)].flags & INVENTORY_GENERAL)) { + found = TRUE; + break; + } + } + if (!found) + return last_trash; + // find the next non-trash object + for (non_trash = trash; non_trash < NUM_GENERAL_SLOTS; non_trash++) { + ObjID id = player_struct.inventory[non_trash]; + if (id != OBJ_NULL && (ObjProps[OPNUM(id)].flags & INVENTORY_GENERAL)) + break; + } + // iterate through, destroying trash. + for (; trash < NUM_GENERAL_SLOTS; trash++) { + ObjID id = player_struct.inventory[trash]; + uchar is_trash = !(ObjProps[OPNUM(id)].flags & INVENTORY_GENERAL); + if (is_trash) + obj_destroy(id); + if (is_trash || id == OBJ_NULL) { + if (non_trash < NUM_GENERAL_SLOTS) { + player_struct.inventory[trash] = player_struct.inventory[non_trash]; + player_struct.inventory[non_trash] = OBJ_NULL; + for (; non_trash < NUM_GENERAL_SLOTS; non_trash++) { + ObjID id = player_struct.inventory[non_trash]; + if (id != OBJ_NULL && (ObjProps[OPNUM(id)].flags & INVENTORY_GENERAL)) + break; + } + } else { + player_struct.inventory[trash] = OBJ_NULL; + last_trash = lg_min(last_trash, trash); + } + } + } + return last_trash; +} + +ubyte add_access_card(inv_display *dp, ObjID *idP, uchar select) { + ubyte retval = ADD_NOEFFECT; + int i, d1, old_d1, gain; + ObjID cards; + for (i = 0; i < dp->listlen; i++) { + if (ID2TRIP(player_struct.inventory[i]) == GENCARDS_TRIPLE || player_struct.inventory[i] == OBJ_NULL) + break; + } + if (i >= dp->listlen) + i = inv_empty_trash(); + if (i >= dp->listlen) + return ADD_FAIL; + + // Extract the data out of the old card, so that we can copy it + // correctly into the new set of cards. Destroy the old one first + // so that if we are right on the border of number of cards available + // in the universe, we don't die. + d1 = objSmallstuffs[objs[*idP].specID].data1; + obj_destroy(*idP); + + if (player_struct.inventory[i] == OBJ_NULL) { + cards = obj_create_base(GENCARDS_TRIPLE); + if (cards == OBJ_NULL) + return ADD_FAIL; + player_struct.inventory[i] = cards; + retval = ADD_POP; + } else + cards = player_struct.inventory[i]; + old_d1 = objSmallstuffs[objs[cards].specID].data1; + objSmallstuffs[objs[cards].specID].data1 |= d1; + if (select && dp->select != NULL) + dp->select(dp, i); + gain = d1 & (~old_d1); + if (gain == 0) + string_message_info(REF_STR_AccessCardNoGain); + else { + char gainbuf[80], bitname[16]; + int l, bitgot; + + get_string(REF_STR_AccessCardNewGain, gainbuf, 80); + l = strlen(gainbuf); + + bitgot = 0; + while (gain != 0) { + if (gain & 1) { + get_string(MKREF(RES_accessCards, bitgot << 1), bitname, sizeof(bitname)); + if (l + strlen(bitname) + 1 < 80) { + strcat(gainbuf, bitname); + strcat(gainbuf, " "); + l += strlen(bitname) + 1; + } + } + gain = gain >> 1; + bitgot = bitgot + 1; + } + gainbuf[l] = '\0'; + + message_info(gainbuf); + } + return retval; +} + +ubyte general_add_func(inv_display *dp, int row, ObjID *idP, uchar select) { + play_digi_fx(SFX_INVENT_ADD, 1); + if ((objs[*idP].obclass == CLASS_SMALLSTUFF) && + ((objs[*idP].subclass == SMALLSTUFF_SUBCLASS_CARDS) || (ID2TRIP(*idP) == CYBERCARD_TRIPLE))) + return add_access_card(dp, idP, select); + if (player_struct.inventory[NUM_GENERAL_SLOTS - 1] == OBJ_NULL) + row = NUM_GENERAL_SLOTS - 1; + if (row < 0 || row >= dp->listlen || player_struct.inventory[row] == OBJ_NULL) + for (row = 0; row < dp->listlen; row++) + if (player_struct.inventory[row] == OBJ_NULL) + break; + if (row >= dp->listlen) + row = inv_empty_trash(); + if (row >= dp->listlen) + return ADD_NOROOM; + else { + ObjID tmp = player_struct.inventory[row]; + // if we're trying to swap with the "access cards" object, + // use the next object instead. Since there's only one "access cards" + // object, this works. + if (ID2TRIP(tmp) == GENCARDS_TRIPLE) { + row = (row + 1) % dp->listlen; + tmp = player_struct.inventory[row]; + } + player_struct.inventory[row] = *idP; + if (select && dp->select != NULL) + dp->select(dp, row); + if (tmp != OBJ_NULL && ID2TRIP(tmp) != GENCARDS_TRIPLE) { + *idP = tmp; + return ADD_SWAP; + } + return ADD_POP; + } +} + +void remove_general_item(ObjID obj) { + int row; + int i; + + for (row = 0; row < NUM_GENERAL_SLOTS; row++) + if (player_struct.inventory[row] == obj) + break; + if (row >= NUM_GENERAL_SLOTS) + return; + for (i = row + 1; i < NUM_GENERAL_SLOTS; i++) + player_struct.inventory[i - 1] = player_struct.inventory[i]; + player_struct.inventory[NUM_GENERAL_SLOTS - 1] = OBJ_NULL; + + // Turn off any active gear when it leaves our inventory. + switch (ID2TRIP(obj)) { + case TRACBEAM_TRIPLE: + if (objs[obj].info.inst_flags & CLASS_INST_FLAG) + obj_tractor_beam_func(obj, FALSE); + break; + } + if (player_struct.current_active == ACTIVE_GENERAL) + set_inventory_mfd(MFD_INV_GENINV, player_struct.actives[ACTIVE_GENERAL], TRUE); + mfd_notify_func(NOTIFY_ANY_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + // Redraw the panel, or setup thereof + INVENT_CHANGED; +} + +void general_drop_func(inv_display *dp, int row) { + ObjID obj = player_struct.inventory[row]; + if (obj != OBJ_NULL && ID2TRIP(obj) != GENCARDS_TRIPLE) // don't let us drop access cards. + { + // Put on cursor... + push_cursor_object(obj); + remove_general_item(obj); + } +} + +uchar inv_select_general(inv_display *dp, int w) { + uchar retval = FALSE; + if (player_struct.inventory[w] == OBJ_NULL) + goto out; + player_struct.actives[dp->activenum] = w; + set_inventory_mfd(dp->mfdtype, w, TRUE); + INVENT_CHANGED; + retval = TRUE; +out: + return retval; +} + +// ----- +// EMAIL +// ----- + +#define EMAIL_TRIP 0 // MAKETRIP(CLASS_SOFTWARE,SOFTWARE_SUBCLASS_EMAIL,0) + +#define FIRST_DATA (NUM_EMAIL - NUM_DATA) +#define MORE_COLOR SELECTED_ITEM_COLOR + +static uchar email_morebuttons[2]; + +void email_more_draw(inv_display *dp) { + gr_set_font(ResLock(ITEM_FONT)); + + if (dp->relnum % 2 == 1) { + int i; + int count = 0; + for (i = 0; i < NUM_EMAIL_PROPER; i++) + if (player_struct.email[i]) + count++; + if (count > (dp->relnum + 1) * dp->pgsize) { + short y = dp->top + Y_STEP; + char buf[50]; + get_string(REF_STR_EmailMoreRight, buf, sizeof(buf)); + draw_quant_line("", buf, MORE_COLOR, FALSE, dp->left, dp->right, y); + email_morebuttons[1] = TRUE; + } else + email_morebuttons[1] = FALSE; + } else if (dp->relnum != 0) { + short y = dp->top + Y_STEP; + char buf[50]; + get_string(REF_STR_EmailMoreLeft, buf, sizeof(buf)); + draw_quant_line(buf, "", MORE_COLOR, FALSE, dp->left, dp->right, y); + email_morebuttons[0] = TRUE; + } else + email_morebuttons[0] = FALSE; + ResUnlock(ITEM_FONT); +} + +uchar email_more_use(inv_display *dp, int w) { + uchar retval = FALSE; + if (dp->relnum != 0 && email_morebuttons[dp->relnum % 2]) { + int newpage = (dp->relnum % 2 == 0) ? inventory_page - 1 : inventory_page + 1; + inventory_page = newpage; + INVENT_CHANGED; + retval = TRUE; + } + return retval; +} + +#define EMAIL_BASE_ID RES_email0 +#define TITLE_IDX 1 + +uchar email_use_func(inv_display *dp, int row) { + uchar retval = FALSE; + int n = dp->lines[row].num; + + if (n < dp->listlen) { + read_email(EMAIL_BASE_ID, n); + retval = TRUE; + } + return retval; +} + +void email_select_func(inv_display *dp, int row) { + int n = dp->lines[row].num; + if (n < dp->listlen) { + play_digi_fx(SFX_INVENT_SELECT, 1); + select_email(n, TRUE); + } +} + +void add_email_datamunge(short mung, uchar select) { + int n; + uchar flash_email = TRUE; + ubyte ver; + extern short last_email_taken; + + n = mung & 0xFF; + ver = mung >> 8; + switch (ver) { + case EMAIL_VER: + set_email_flags(n); + break; + case LOG_VER: { + int lev; + lev = n / LOGS_PER_LEVEL; + n = NUM_EMAIL_PROPER + n; + if (player_struct.email[n] == 0) + player_struct.logs[lev]++; + flash_email = FALSE; + } break; + case DATA_VER: + flash_email = FALSE; + n = n + NUM_EMAIL - NUM_DATA; + break; + } + if (player_struct.email[n] & EMAIL_GOT) + return; + last_email_taken = ver; + string_message_info(REF_STR_ReceiveEmail + ver); + player_struct.email[n] |= EMAIL_GOT; + if (flash_email) { + player_struct.hardwarez_status[CPTRIP(VIDTEX_HARD_TRIPLE)] |= WARE_FLASH; + QUESTBIT_ON(0x12c); + } + INVENT_CHANGED; + select_email(n, select); +} + +ubyte email_add_func(inv_display *dp, int w, ObjID *idP, uchar select) { + play_digi_fx(SFX_INVENT_ADD, 1); + if (ID2TRIP(*idP) != EMAIL1_TRIPLE && ID2TRIP(*idP) != TEXT1_TRIPLE) + return ADD_REJECT; + add_email_datamunge(SOFTWARE_CONTENTS(objs[*idP].specID), select); + return ADD_POP; +} + +void email_drop_func(inv_display *dp, int n) { + // For now, do nothing. +} + + // ---- + // LOGS + // ---- + + // I'm on your side; we are on the both side. + +#define FIRST_LOG_PAGE 20 + +char *log_name_func(void *v, int num, char *buf) { return get_string(REF_STR_LogName0 + num, buf, BUFSZ); } + +uchar log_use_func(inv_display *dp, int row) { + uchar retval = FALSE; + int n = dp->lines[row].num; + if (n < dp->listlen) { + inventory_draw_new_page(FIRST_LOG_PAGE + n); + retval = TRUE; + } + return retval; +} + +// --------- +// INTERNALS +// --------- + +void inventory_draw_page(int pgnum) { + int i; + inv_display *dpy; + + for (i = 0; gen_inv_page(pgnum, &i, &dpy); i++) { + if (dpy->draw != NULL) + dpy->draw(dpy); + } + for (i = 0; i < NUM_ACTIVES; i++) + known_actives[i] = player_struct.actives[i]; +} + +ubyte add_to_some_page(ObjID obj, uchar select) { + inv_display *dpy; + int i; + for (i = 0; gen_inv_displays(&i, &dpy); i++) { + ubyte pop; + if (global_fullmap->cyber && (objs[obj].obclass == CLASS_BIGSTUFF)) { + if (!(dpy->add_classes & (1 << CLASS_SOFTWARE))) + continue; + } else { + if (!(dpy->add_classes & (1 << objs[obj].obclass))) + continue; + } + if (dpy->add != NULL) + pop = dpy->add(dpy, -1, &obj, select); + if (pop == ADD_FAIL) + return pop; + if (pop == ADD_NOROOM) { + string_message_info(REF_STR_InvNoRoom); + return pop; + } + if (pop != ADD_REJECT) { + if (pop != ADD_NOEFFECT) { + if (dpy->pgnum != inventory_page && dpy->pgnum < NUM_PAGE_BUTTONS) { + page_button_state[dpy->pgnum] = BttnFlashOn; + } + INVENT_CHANGED; + } + return pop; + } + } + string_message_info(REF_STR_InvReject); + return ADD_REJECT; +} + +/*KLC - no longer used +void draw_page_button_panel() +{ + draw_page_buttons(TRUE); +} +*/ + +void draw_page_buttons(uchar full) { + LGRect r, hider; + int i; + short x; + uchar old_over = gr2ss_override; + + gr_push_canvas(ppage_canvas); + + if (full_game_3d) + gr2ss_override = OVERRIDE_NONE; + else + gr2ss_override = OVERRIDE_ALL; + + if (full) { + draw_res_bm(REF_IMG_bmInventoryButtonBackground, INVENT_BUTTON_PANEL_X, INVENT_BUTTON_PANEL_Y); + // draw_hires_resource_bm(REF_IMG_bmInventoryButtonBackground, 0, 0); + } + + r.ul.y = INVENT_BTTN_Y; + r.lr.y = r.ul.y + INVENT_BTTN_HT; + + for (x = FIRST_BTTN_X, i = 0; i < NUM_PAGE_BUTTONS; i++, x += BUTTON_X_STEP) { + invent_bttn_state newstate = page_button_state[i]; + ulong clr; + uchar active = i == inventory_page; + + if (newstate == BttnDummy) + continue; + // Figure out what the button state really is. + if (active) + newstate = BttnActive; + else if (Flashing(newstate)) { + uchar flashon = (player_struct.game_time / INVENT_BTTN_FLASH_TIME) % 2; + if (flashon != FlashOn(newstate)) { + newstate = (invent_bttn_state)((char)newstate - 1); + if (newstate == BttnFlashOff) + newstate = BttnOff; + } + // if (time_passes) // We want to remember that we need to change when time starts again.. + INVENT_CHANGED; + } else + newstate = BttnOff; + + if (!full && newstate == old_button_state[i]) + continue; + + clr = bttn_state2color(newstate); + gr_set_fcolor(clr); + r.ul.x = x; + r.lr.x = x + INVENT_BTTN_WD; + RECT_OFFSETTED_RECT(&r, MakePoint(INVENTORY_PANEL_X, BUTTON_PANEL_Y), &hider); + uiHideMouse(&hider); + ss_rect(r.ul.x, r.ul.y, r.lr.x, r.lr.y); + // gr_rect(SCONV_X(r.ul.x)+2, 2, SCONV_X(r.lr.x)+2, 10); + uiShowMouse(&hider); + page_button_state[i] = old_button_state[i] = newstate; + } + gr_pop_canvas(); + gr2ss_override = old_over; +} + +// --------- +// EXTERNALS +// --------- + +uchar dirty_inv_canvas = FALSE; + +errtype inventory_clear(void) { + gr_push_canvas(&inv_canvas); + if (full_game_3d) + gr_clear(0); + else { + LGRect r; + r.ul.x = INVENTORY_PANEL_X; + r.ul.y = INVENTORY_PANEL_Y; + r.lr.x = INVENTORY_PANEL_X + INVENTORY_PANEL_WIDTH; + r.lr.y = INVENTORY_PANEL_Y + INVENTORY_PANEL_HEIGHT; + if (dirty_inv_canvas) { + FrameDesc *f = RefGet(REF_IMG_bmBlankInventoryPanel); + LG_memcpy(inv_backgnd.bits, f + 1, f->bm.w * f->bm.h); + dirty_inv_canvas = FALSE; + } + uiHideMouse(&r); + ss_safe_set_cliprect(0, 0, INVENTORY_PANEL_WIDTH, INVENTORY_PANEL_HEIGHT); + ss_bitmap(&inv_backgnd, 0, 0); + // gr_bitmap(&inv_backgnd, 0, 0); + uiShowMouse(&r); + } + gr_pop_canvas(); + /* Now, you might ask "Why not just set inv_last_page = INV_BLANK_PAGE all the time?" + And the answer is, well, the wrapper panel saves the inventory page in inv_last_page, + so that things like load game know how to blow the saved page away */ + if (inventory_page == inv_last_page) + inv_last_page = INV_BLANK_PAGE; + return (OK); +} + +errtype inventory_full_redraw() { + int i; + inv_last_page = -1; + for (i = 0; i < NUM_PAGE_BUTTONS; i++) + old_button_state[i] = BttnDummy; + return (inventory_draw()); +} + +errtype inventory_draw(void) { + uchar full = inventory_page != inv_last_page; +#ifdef SVGA_SUPPORT + uchar old_over; + short temp; +#endif + if (inventory_page < 0) + return OK; + gr_push_canvas(&inv_canvas); +#ifdef SVGA_SUPPORT + old_over = gr2ss_override; + // if (full_game_3d) + // gr2ss_override = OVERRIDE_FONT|OVERRIDE_CLIP; + // else + gr2ss_override = OVERRIDE_ALL; +#endif + if (global_fullmap->cyber) + inventory_page = INV_SOFTWARE_PAGE; + if (full) + inventory_clear(); + draw_page_buttons(full_game_3d || full); +#ifdef SVGA_SUPPORT + ss_set_hack_mode(2, &temp); +#endif + inventory_draw_page(inventory_page); +#ifdef SVGA_SUPPORT + ss_set_hack_mode(0, &temp); + gr2ss_override = old_over; +#endif + gr_pop_canvas(); + inv_last_page = inventory_page; + return (OK); +} + +errtype inventory_draw_new_page(int pgnum) { + inv_last_page = -1; + inventory_page = pgnum; + if (full_game_3d) { +#ifdef STEREO_SUPPORT + if (convert_use_mode == 5) + full_visible = FULL_INVENT_MASK; + else +#endif + full_visible |= FULL_INVENT_MASK; + full_raise_region(inventory_region_full); + chg_set_sta(FULLSCREEN_UPDATE); + } + return inventory_draw(); +} + +uchar inventory_add_object(ObjID obj, uchar select) { + ubyte result = add_to_some_page(obj, select); + return (result != ADD_FAIL) && (result != ADD_REJECT) && (result != ADD_NOROOM); +} + +// ------------ +// EVENTHANDLER +// ------------ + +uchar do_selection(inv_display *dp, int row) { + uchar retval = FALSE; + int w = dp->lines[row].num; + if (w >= dp->listlen) + goto out; + if (dp->activenum == NULL_ACTIVE) + goto out; + player_struct.actives[dp->activenum] = w; + set_inventory_mfd(dp->mfdtype, w, TRUE); + INVENT_CHANGED; + retval = TRUE; +out: + return retval; +} + +void add_object_on_cursor(inv_display *dp, int row) { + ObjID obj = object_on_cursor; + ubyte pop = ADD_REJECT; + if (dp != NULL) + pop = (dp->add_classes & (1 << TRIP2CL(ID2TRIP(object_on_cursor)))) ? ADD_POP : ADD_REJECT; + if (pop != ADD_REJECT && dp->add != NULL) + pop = dp->add(dp, row, &obj, FALSE); + if (pop == ADD_NOROOM) { + string_message_info(REF_STR_InvNoRoom); + return; + } + if (pop == ADD_REJECT) + pop = add_to_some_page(obj, FALSE); + if (pop != ADD_REJECT && pop != ADD_FAIL) { + INVENT_CHANGED; + if (IS_POP_RESULT(pop) || pop == ADD_SWAP) + pop_cursor_object(); + if (pop == ADD_SWAP) + push_cursor_object(obj); + uiShowMouse(NULL); // KLC - added to make sure the pointer changes. + } + if (pop == ADD_REJECT) + string_message_info(REF_STR_InvReject); +} + +uchar inventory_handle_leftbutton(uiEvent *ev, inv_display *dp, int row) { + uchar retval = FALSE; +#ifndef NO_DUMMIES + void *dummy; + dummy = ev; +#endif // NO_DUMMIES + switch (input_cursor_mode) { + case INPUT_NORMAL_CURSOR: + if (dp != NULL && row >= 0) { + if (dp->select != NULL) + retval = dp->select(dp, row); + else + retval = do_selection(dp, row); + } + break; + case INPUT_OBJECT_CURSOR: + add_object_on_cursor(dp, row); + retval = TRUE; + break; + } + return retval; +} + +static uchar invpanel_focus = FALSE; + +uchar inventory_handle_rightbutton(uiEvent *ev, LGRegion *reg, inv_display *dp, int row) { + static int lastrow = 0; + static inv_display *lastdp = NULL; + + uchar retval = FALSE; + LGRect r; + uchar grab = FALSE; + + if (input_cursor_mode != INPUT_NORMAL_CURSOR) + return FALSE; + if (ev->subtype & MOUSE_RDOWN && row >= 0 && dp != NULL && !invpanel_focus) { + // let us know if we leave the region + invpanel_focus = TRUE; + uiGrabFocus(reg, UI_EVENT_MOUSE_MOVE); + lastrow = row; + lastdp = dp; + retval = TRUE; + } + // Check to see if we've left the region and release focus. + region_abs_rect(reg, reg->r, &r); + if (!RECT_TEST_PT(&r, ev->pos)) { + grab = TRUE; + row = lastrow; + dp = lastdp; + retval = TRUE; + } + if (ev->subtype & MOUSE_RUP) { + if (row == lastrow && dp == lastdp) + grab = TRUE; + } else if (ev->subtype & MOUSE_MOTION && (row != lastrow || dp != lastdp)) { + row = lastrow; + dp = lastdp; + grab = TRUE; + } + if (grab && dp != NULL && row >= 0) { + uchar cyber = TRIP2CL(dp->basetrip) == CLASS_SOFTWARE; + if (cyber != global_fullmap->cyber) { + int str = cyber ? REF_STR_InvCybFailSoft : REF_STR_InvCybFailHard; + string_message_info(str); + } else if (dp->drop != NULL) { + dp->drop(dp, row); + } + lastrow = -1; + lastdp = NULL; + retval = TRUE; + } + return retval; +} + +#define SEARCH_MARGIN 2 + +uchar inventory_mouse_handler(uiEvent *ev, LGRegion *r, intptr_t data) { + uchar retval = FALSE; + int relx; + inv_display *dp = NULL; + int i; + int row = -1; + extern uchar game_paused; +#ifdef SVGA_SUPPORT + short temp; +#endif + if (game_paused) + return (TRUE); + +#ifdef STEREO_SUPPORT + if (convert_use_mode == 5) { + if (i6d_device == I6D_CTM) + relx = ev->pos.x - 1; + else { + relx = (ev->pos.x - ((320 - inv_fullscrn_canvas.bm.w) / 2)) >> 1; + // Warning(("relx: %d = %d - %d = %d >> 1\n",relx,ev->pos.x,((320-inv_fullscrn_canvas.bm.w)/2), + // (ev->pos.x - ((320-inv_fullscrn_canvas.bm.w)/2)))); + } + } else +#endif + { + relx = ev->pos.x - INVENTORY_PANEL_X; + } + if (invpanel_focus && !(ev->mouse_data.buttons & (1 << MOUSE_RBUTTON))) { + uiReleaseFocus(r, UI_EVENT_MOUSE_MOVE); + invpanel_focus = FALSE; + } + if (full_game_3d && !(full_visible & FULL_INVENT_MASK)) + return FALSE; + if (full_game_3d && !(ev->mouse_data.buttons & (1 << MOUSE_RBUTTON))) { + if (!(ev->mouse_data.action & ~MOUSE_MOTION)) + return FALSE; + if (input_cursor_mode != INPUT_OBJECT_CURSOR) { + uchar found = FALSE; + short rel_y; + short x, y; + short smx, smy; +#ifdef STEREO_SUPPORT + if (convert_use_mode == 5) { + switch (i6d_device) { + case I6D_CTM: + rel_y = ev->pos.y - 1; + break; + case I6D_VFX1: + rel_y = ev->pos.y - (INVENTORY_PANEL_Y >> 1); + // rel_y = ev->pos.y - (200 - inv_fullscrn_canvas.bm.h); + break; + default: + break; + } + } else +#endif + rel_y = ev->pos.y - INVENTORY_PANEL_Y; + gr_push_canvas(&inv_fullscrn_canvas); + smx = SEARCH_MARGIN; + smy = SEARCH_MARGIN; +#ifdef SVGA_SUPPORT + ss_set_hack_mode(2, &temp); + ss_point_convert(&smx, &smy, FALSE); +#endif + for (x = relx - smx; !found && x <= relx + smx; x++) + for (y = rel_y - smy; !found && y <= rel_y + smy; y++) { + short usex, usey; + usex = x; + usey = y; +#ifdef SVGA_SUPPORT + ss_point_convert(&usex, &usey, FALSE); +#endif + if (gr_get_pixel(usex, usey) != 0) // found non-transparent pixel + found = TRUE; + } +#ifdef SVGA_SUPPORT + ss_set_hack_mode(0, &temp); +#endif + gr_pop_canvas(); + if (!found) { + return FALSE; + } + } + } + for (i = 0; gen_inv_page(inventory_page, &i, &dp); i++) { + if (relx < dp->left || relx > dp->right) + continue; + row = get_item_at_pixrow(dp, ev->pos.y); + if (row >= 0) { + break; + } + } + if (input_cursor_mode == INPUT_OBJECT_CURSOR && (ev->mouse_data.action & (MOUSE_LDOWN | MOUSE_RUP | UI_MOUSE_LDOUBLE))) { + add_object_on_cursor(dp, row); + return TRUE; + } + if ((ev->mouse_data.buttons & (1 << MOUSE_RBUTTON)) || (ev->subtype & (MOUSE_RUP | MOUSE_RDOWN))) + if (inventory_handle_rightbutton(ev, r, dp, row)) + retval = TRUE; + // Handle left button + if (ev->subtype & MOUSE_LDOWN) { + if (inventory_handle_leftbutton(ev, dp, row)) + retval = TRUE; + } + // Handle left doubleclick + if (ev->subtype & UI_MOUSE_LDOUBLE) + if (dp != NULL && (row >= 0) && (dp->use != NULL)) { + retval = dp->use(dp, row); + } + return retval; +} + +int last_invent_cnum = -1; // last cursor num set for region +uchar pagebutton_mouse_handler(uiEvent *ev, LGRegion *r, intptr_t data) { + LGPoint pos = ev->pos; + int cnum; + + if (full_game_3d && (ev->mouse_data.buttons & (1 << MOUSE_LBUTTON)) != 0 && + (ev->mouse_data.action & MOUSE_LDOWN) == 0 && + uiLastMouseRegion[MOUSE_LBUTTON] != NULL && uiLastMouseRegion[MOUSE_LBUTTON] != r) { + uiSetRegionDefaultCursor(r, NULL); + return FALSE; + } + + pos.x -= INVENTORY_PANEL_X; + pos.y -= INVENTORY_PANEL_Y; + + cnum = (pos.x - FIRST_BTTN_X) / BUTTON_X_STEP; + if (full_game_3d && global_fullmap->cyber && cnum != INV_SOFTWARE_PAGE) { + last_invent_cnum = cnum; + uiSetRegionDefaultCursor(r, NULL); + return FALSE; + } + + if ((cnum != last_invent_cnum) && (cnum < NUM_PAGE_BTTNS)) { + LGCursor *c = &invent_bttn_cursor; + LGPoint offset = {0, -1}; + + if ((page_button_state[cnum] == BttnDummy) || !popup_cursors) + c = NULL; + last_invent_cnum = cnum; +#ifdef SVGA_SUPPORT + free(invent_bttn_bitmap.bits); + make_popup_cursor(c, &invent_bttn_bitmap, cursor_strings[cnum], POPUP_DOWN, TRUE, offset); +#else + make_popup_cursor(c, &invent_bttn_bitmap, cursor_strings[cnum], POPUP_DOWN, FALSE, offset); +#endif + uiSetRegionDefaultCursor(r, c); + } + + if (input_cursor_mode == INPUT_OBJECT_CURSOR && (ev->mouse_data.action & (MOUSE_LDOWN | MOUSE_RDOWN | UI_MOUSE_LDOUBLE))) { + AddResult pop = (AddResult)add_to_some_page(object_on_cursor, FALSE); + if (IS_POP_RESULT(pop)) + pop_cursor_object(); + return TRUE; + } + + if (page_button_state[cnum] == BttnDummy) + return FALSE; + + if (ev->mouse_data.action & MOUSE_LDOWN) { + int i = cnum; + short x = FIRST_BTTN_X + i * BUTTON_X_STEP; + if (pos.x >= x && pos.x < x + INVENT_BTTN_WD && page_button_state[i] != BttnDummy) { + if (full_game_3d) { + if (i == inventory_page && full_visible & FULL_INVENT_MASK) { + full_visible &= ~FULL_INVENT_MASK; + } else { + gr_push_canvas(pinv_canvas); + gr_clear(0); + gr_pop_canvas(); +#ifdef STEREO_SUPPORT + if (convert_use_mode == 5) + full_visible = FULL_INVENT_MASK; + else +#endif + full_visible |= FULL_INVENT_MASK; + inv_last_page = -1; + full_raise_region(inventory_region_full); + chg_set_sta(FULLSCREEN_UPDATE); + } + } + play_digi_fx(SFX_INVENT_BUTTON, 1); + inventory_page = i; + INVENT_CHANGED; + } + } + return TRUE; +} + +#define MAX_HOTKEY_PAGES 6 +#define EMPTY_PAGE(i) (page_button_state[i] == BttnDummy) + +uchar invent_hotkey_func(ushort keycode, uint32_t context, intptr_t data) { + if (inventory_page < 0) + inventory_page = MAX_HOTKEY_PAGES; + if (inventory_page >= MAX_HOTKEY_PAGES) + inventory_page = -1; + if (data == 0) { + inventory_page--; + if (inventory_page < 0) + inventory_page = MAX_HOTKEY_PAGES - 1; + while (EMPTY_PAGE(inventory_page)) + inventory_page--; + } else { + inventory_page++; + if (inventory_page >= MAX_HOTKEY_PAGES) + inventory_page = 0; + while (EMPTY_PAGE(inventory_page)) + inventory_page++; + } + play_digi_fx(SFX_INVENT_BUTTON, 1); + if (!(full_visible & FULL_INVENT_MASK)) { + gr_push_canvas(pinv_canvas); + gr_clear(0); + gr_pop_canvas(); +#ifdef SVGA_SUPPORT + if (convert_use_mode == 5) + full_visible = FULL_INVENT_MASK; + else +#endif + full_visible |= FULL_INVENT_MASK; + } + INVENT_CHANGED; + return TRUE; +} + +uchar cycle_weapons_func(ushort keycode, uint32_t context, intptr_t data) { + if (global_fullmap->cyber) { + int ac = player_struct.actives[ACTIVE_COMBAT_SOFT]; + int bound1 = (data > 0) ? NUM_COMBAT_SOFTS : -1; + int bound2 = (data > 0) ? 0 : NUM_COMBAT_SOFTS - 1; + int i; + for (i = ac + data; i != bound1; i += data) + if (player_struct.softs.combat[i] != 0) + goto got_soft; + for (i = bound2; i != ac; i += data) + if (player_struct.softs.combat[i] != 0) + goto got_soft; + got_soft: + player_struct.actives[ACTIVE_COMBAT_SOFT] = i; + INVENT_CHANGED; + } else { + int aw = player_struct.actives[ACTIVE_WEAPON]; + + aw += data; + if (aw >= NUM_WEAPON_SLOTS || player_struct.weapons[aw].type == EMPTY_WEAPON_SLOT) + aw = 0; + else if (aw < 0) { + for (aw = NUM_WEAPON_SLOTS - 1; player_struct.weapons[aw].type == EMPTY_WEAPON_SLOT && aw > 0; aw--) + ; + } + inventory_select_weapon(NULL, aw); + } + return TRUE; +} + +#define PAGEUP_KEY KEY_PAD_PGUP | KB_FLAG_DOWN +#define PAGEDN_KEY KEY_PAD_PGDN | KB_FLAG_DOWN + +void init_invent_hotkeys(void) { + /* later + // hotkey_add(PAGEUP_KEY,DEMO_CONTEXT,invent_hotkey_func,0); + hotkey_add(PAGEUP_KEY|KB_FLAG_2ND,DEMO_CONTEXT,invent_hotkey_func,0); + hotkey_add(KB_FLAG_DOWN|KB_FLAG_ALT|'[',DEMO_CONTEXT,invent_hotkey_func,0); + // hotkey_add(PAGEDN_KEY,DEMO_CONTEXT,invent_hotkey_func,1); + hotkey_add(PAGEDN_KEY|KB_FLAG_2ND,DEMO_CONTEXT,invent_hotkey_func,1); + hotkey_add(KB_FLAG_DOWN|KB_FLAG_ALT|']',DEMO_CONTEXT,invent_hotkey_func,1); + */ + hotkey_add(KEY_TAB | KB_FLAG_DOWN, DEMO_CONTEXT, cycle_weapons_func, 1); + hotkey_add(KEY_TAB | KB_FLAG_DOWN | KB_FLAG_SHIFT, DEMO_CONTEXT, cycle_weapons_func, -1); +} + +void invent_language_change(void) { + load_string_array(REF_STR_InvCursor, cursor_strings, cursor_string_buf, sizeof(cursor_string_buf), + NUM_PAGE_BUTTONS); +} + +#define MAX_INV_FULL_WD(x) (fix_int(fix_mul_div(fix_make((x), 0), fix_make(1024, 0), fix_make(320, 0)))) +#define MAX_INV_FULL_HT(y) (fix_int(fix_mul_div(fix_make((y), 0), fix_make(768, 0), fix_make(200, 0)))) + +LGRegion *create_invent_region(LGRegion *root, LGRegion **pbuttons, LGRegion **pinvent) { + static uchar done_init = FALSE; + int id; + LGRect invrect; + LGRegion *invreg = (LGRegion *)malloc(sizeof(LGRegion)); + LGRegion *pagereg = (LGRegion *)malloc(sizeof(LGRegion)); + FrameDesc *f; +#ifdef OLD_BUTTON_CURSORS + LGPoint pt; + int i; +#endif +#ifdef CURSOR_BACKUPS + extern uchar *backup[NUM_BACKUP_BITS]; + extern grs_bitmap backup_invent_bttn_cursors[NUM_PAGE_BTTNS]; +#endif + + // Create the panel region + invrect.ul.x = INVENTORY_PANEL_X; + invrect.ul.y = INVENTORY_PANEL_Y; + invrect.lr.x = invrect.ul.x + INVENTORY_PANEL_WIDTH; + invrect.lr.y = invrect.ul.y + INVENTORY_PANEL_HEIGHT; + region_create(root, invreg, &invrect, 0, 0, REG_USER_CONTROLLED | AUTODESTROY_FLAG, NULL, NULL, NULL, NULL); + uiInstallRegionHandler(invreg, UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE, inventory_mouse_handler, 0, &id); + uiSetRegionDefaultCursor(invreg, NULL); + add_email_handler(invreg); + if (pinvent != NULL) + *pinvent = invreg; + + // Create the pagebutton region + invrect.ul.y = invrect.lr.y; + invrect.lr.y = RectHeight(root->r); + region_create(root, pagereg, &invrect, 0, 0, REG_USER_CONTROLLED | AUTODESTROY_FLAG, NULL, NULL, NULL, NULL); + uiInstallRegionHandler(pagereg, (UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE), pagebutton_mouse_handler, + 0, &id); + uiSetRegionDefaultCursor(pagereg, &globcursor); + + if (pbuttons != NULL) + *pbuttons = pagereg; + + if (!done_init) { + done_init = TRUE; + // Assign different cursors to different buttons in pagebutton region + { + grs_bitmap *bm = &invent_bttn_bitmap; + LGCursor *c = &invent_bttn_cursor; + LGPoint offset = {0, -1}; + + invent_language_change(); + make_popup_cursor(c, bm, cursor_strings[0], POPUP_DOWN, TRUE, offset); + } + + // Pull in the background bitmap + f = RefLock(REF_IMG_bmBlankInventoryPanel); + inv_backgnd = f->bm; + + // This background is going to get used by the 360 ware + // in fullscreen mode, so we need extra bits + inv_backgnd.bits = (uchar *)malloc(MAX_INV_FULL_WD(INV_FULL_WD) * MAX_INV_FULL_HT(grd_cap->h - GAME_MESSAGE_Y)); + LG_memcpy(inv_backgnd.bits, (f + 1), f->bm.w * f->bm.h); + RefUnlock(REF_IMG_bmBlankInventoryPanel); + + // init the canvas + gr_init_sub_canvas(grd_scr_canv, &inv_norm_canvas, INVENTORY_PANEL_X, INVENTORY_PANEL_Y, INVENTORY_PANEL_WIDTH, + INVENTORY_PANEL_HEIGHT); + gr_init_canvas(&inv_fullscrn_canvas, inv_backgnd.bits, BMT_FLAT8, INVENTORY_PANEL_WIDTH, + INVENTORY_PANEL_HEIGHT); + gr_init_canvas(&inv_view360_canvas, inv_backgnd.bits, BMT_FLAT8, INV_FULL_WD, INV_FULL_HT); + gr_init_sub_canvas(grd_scr_canv, &inv_gamepage_canvas, INVENTORY_PANEL_X, BUTTON_PANEL_Y, INVENTORY_PANEL_WIDTH, + grd_cap->h - BUTTON_PANEL_Y); + + uchar *p = (uchar *)malloc(292 * 10); // This canvas holds an off-screen image of the + gr_init_canvas(&inv_fullpage_canvas, p, BMT_FLAT8, 292, 10); // inventory buttons. + gr_push_canvas(&inv_fullpage_canvas); + gr_clear(0); + gr_pop_canvas(); + } + + inventory_update_screen_mode(); + + return invreg; +} + +errtype inventory_update_screen_mode() { + if (convert_use_mode) { + gr_init_sub_canvas(grd_scr_canv, &inv_norm_canvas, SCONV_X(INVENTORY_PANEL_X), SCONV_Y(INVENTORY_PANEL_Y), + SCONV_X(INVENTORY_PANEL_WIDTH), SCONV_Y(INVENTORY_PANEL_HEIGHT)); + if (full_game_3d) { + gr_init_canvas(&inv_fullscrn_canvas, inv_backgnd.bits, BMT_FLAT8, SCONV_X(INVENTORY_PANEL_WIDTH), + SCONV_Y(INVENTORY_PANEL_HEIGHT)); + // gr_init_canvas(&inv_fullscrn_canvas, inv_backgnd.bits, BMT_FLAT8, 290, 120); + gr_init_canvas(&inv_view360_canvas, inv_backgnd.bits, BMT_FLAT8, SCONV_X(INV_FULL_WD), + SCONV_Y(INV_FULL_HT)); + // gr_init_canvas(&inv_view360_canvas, inv_backgnd.bits, BMT_FLAT8, 290, SCONV_Y(INV_FULL_HT)); + } else { + gr_init_sub_canvas(grd_scr_canv, &inv_gamepage_canvas, SCONV_X(INVENTORY_PANEL_X), SCONV_Y(BUTTON_PANEL_Y), + SCONV_X(INVENTORY_PANEL_WIDTH), SCONV_Y(10)); + gr_init_canvas(&inv_view360_canvas, inv_backgnd.bits, BMT_FLAT8, SCONV_X(INV_FULL_WD), + SCONV_Y(INV_FULL_HT)); + } + } else { + gr_init_sub_canvas(grd_scr_canv, &inv_norm_canvas, INVENTORY_PANEL_X, INVENTORY_PANEL_Y, INVENTORY_PANEL_WIDTH, + INVENTORY_PANEL_HEIGHT); + if (full_game_3d) { + gr_init_canvas(&inv_fullscrn_canvas, inv_backgnd.bits, BMT_FLAT8, INVENTORY_PANEL_WIDTH, + INVENTORY_PANEL_HEIGHT); + gr_init_canvas(&inv_view360_canvas, inv_backgnd.bits, BMT_FLAT8, INV_FULL_WD, INV_FULL_HT); + } else { + gr_init_sub_canvas(grd_scr_canv, &inv_gamepage_canvas, INVENTORY_PANEL_X, BUTTON_PANEL_Y, + INVENTORY_PANEL_WIDTH, grd_cap->h - BUTTON_PANEL_Y); + gr_init_canvas(&inv_view360_canvas, inv_backgnd.bits, BMT_FLAT8, INVENTORY_PANEL_WIDTH, + INVENTORY_PANEL_HEIGHT); + } + } + + /*KLC - not used in Mac version + else + { + gr_init_sub_canvas(grd_scr_canv,&inv_norm_canvas,INVENTORY_PANEL_X,INVENTORY_PANEL_Y, + INVENTORY_PANEL_WIDTH,INVENTORY_PANEL_HEIGHT); + if (full_game_3d) + { + gr_init_canvas(&inv_fullscrn_canvas,inv_backgnd.bits, BMT_FLAT8, + INVENTORY_PANEL_WIDTH,INVENTORY_PANEL_HEIGHT); gr_init_canvas(&inv_view360_canvas,inv_backgnd.bits, BMT_FLAT8, + INV_FULL_WD, INV_FULL_HT); + } + else + { + gr_init_sub_canvas(grd_scr_canv,&inv_gamepage_canvas,INVENTORY_PANEL_X,BUTTON_PANEL_Y, + INVENTORY_PANEL_WIDTH,grd_cap->h - BUTTON_PANEL_Y); + } + } + */ + return (OK); +} + +void inv_change_fullscreen(uchar on) { + if (on) { + pinv_canvas = &inv_fullscrn_canvas; + ppage_canvas = &inv_fullpage_canvas; + gr_push_canvas(pinv_canvas); + gr_clear(0); + gr_pop_canvas(); + dirty_inv_canvas = TRUE; + + gr_push_canvas(ppage_canvas); + gr_clear(0); + gr_pop_canvas(); + } else { + int i; + pinv_canvas = &inv_norm_canvas; + ppage_canvas = &inv_gamepage_canvas; + for (i = 0; i < NUM_PAGE_BUTTONS; i++) + old_button_state[i] = BttnOff; + if (inventory_page == INV_EMAILTEXT_PAGE) + inventory_page = INV_MAIN_PAGE; + } + inv_last_page = INV_BLANK_PAGE; + INVENT_CHANGED; +} + +void inv_update_fullscreen(uchar full) { + grs_bitmap *bm; + short a, b, c, d; + STORE_CLIP(a, b, c, d); + if (full) { +#ifdef SVGA_SUPPORT + + if (inv_is_360_view()) { + ss_noscale_bitmap(&inv_view360_canvas.bm, GAME_MESSAGE_X, GAME_MESSAGE_Y); + } else { + inv_fullscrn_canvas.bm.flags |= BMF_TRANS; + // ss_bitmap(&(inv_fullscrn_canvas.bm),INVENTORY_PANEL_X,INVENTORY_PANEL_Y); + /* KLC -- Shouldn't this be ifdef'd out? + if (convert_use_mode == 5) + { + switch (i6d_device) + { + case I6D_CTM: + ss_noscale_bitmap(&(inv_fullscrn_canvas.bm),1,1); + break; + case I6D_VFX1: + // ss_noscale_bitmap(&(inv_fullscrn_canvas.bm),(320-inv_fullscrn_canvas.bm.w)/2,200 - + inv_fullscrn_canvas.bm.h); + ss_noscale_bitmap(&(inv_fullscrn_canvas.bm),(320-inv_fullscrn_canvas.bm.w)/2,INVENTORY_PANEL_Y + >> 1); break; + } + } + else + */ + ss_noscale_bitmap(&(inv_fullscrn_canvas.bm), INVENTORY_PANEL_X, INVENTORY_PANEL_Y); + inv_fullscrn_canvas.bm.flags &= ~BMF_TRANS; + } +#else + if (inv_is_360_view()) { + ss_noscale_bitmap(&inv_view360_canvas.bm, GAME_MESSAGE_X, GAME_MESSAGE_Y); + } else { + inv_fullscrn_canvas.bm.flags |= BMF_TRANS; + // ss_bitmap(&(inv_fullscrn_canvas.bm),INVENTORY_PANEL_X,INVENTORY_PANEL_Y); + ss_noscale_bitmap(&(inv_fullscrn_canvas.bm), INVENTORY_PANEL_X, INVENTORY_PANEL_Y); + inv_fullscrn_canvas.bm.flags &= ~BMF_TRANS; + } +#endif + } + region_set_invisible(inventory_region_full, !full); + bm = &inv_fullpage_canvas.bm; + bm->flags |= BMF_TRANS; + if (global_fullmap->cyber) { + ss_safe_set_cliprect(INVENTORY_PANEL_X + bm->w / 2, BUTTON_PANEL_Y, INVENTORY_PANEL_X + bm->w, + BUTTON_PANEL_Y + bm->h); + } + + if (convert_use_mode == 3) { + // CC - something about this in 640x480 mode does not scale correctly + gr_bitmap(bm, 172, 470); // KLC - was ss_bitmap (with scaling) + } else { + ss_bitmap(bm, INVENTORY_PANEL_X, BUTTON_PANEL_Y); + } + + bm->flags &= BMF_TRANS; + RESTORE_CLIP(a, b, c, d); +} + + // ---------------------- + // THE DISPLAY LIST ARRAY + // ---------------------- + +#define FIELD_OFFSET(fld) (offsetof(Player, fld)) + +inv_display inv_display_list[] = { + // Page 0, weapons, grenades, drugs + // weapons are there own thang, they have slots and stuff... + {0, 0, + WEAPON_X, AMMO_X, TOP_MARGIN, + TITLE_COLOR, ITEM_COLOR, + 0, WEAPONS_PER_PAGE, NUM_WEAPON_SLOTS, + REF_STR_WeaponTitle, + ACTIVE_WEAPON, + 0, + MFD_INV_WEAPON, + NULL, + NULL, + draw_weapons_list, + inventory_select_weapon, + weapon_use_func, + WEAP_CLASSES, + weapons_add_func, + weapon_drop_func, + WEAP_TRIP, + NULL, + 0, + NULL}, + // grenades + {0, 0, + GRENADE_LEFT_X, GRENADE_RIGHT_X, TOP_MARGIN, + TITLE_COLOR, ITEM_COLOR, + 0, GRENADES_PER_PAGE, NUM_GRENADES, + REF_STR_GrenadeTitle, + ACTIVE_GRENADE, + FIELD_OFFSET(grenades), + MFD_INV_GRENADE, + grenade_name_func, + generic_quant_func, + generic_draw_list, + NULL, + grenade_use_func, + GREN_CLASSES, + grenade_add_func, + generic_drop_func, + GREN_TRIP, + NULL, + 0, + generic_lines}, + // drug + {0, 0, + DRUG_LEFT_X, DRUG_RIGHT_X, TOP_MARGIN, + TITLE_COLOR, ITEM_COLOR, + 0, DRUGS_PER_PAGE, NUM_DRUGS, + REF_STR_DrugTitle, + ACTIVE_DRUG, + FIELD_OFFSET(drugs), + MFD_INV_DRUG, + generic_name_func, + generic_quant_func, + generic_draw_list, + NULL, + drug_use_func, + DRUG_CLASSES, + generic_add_func, + generic_drop_func, + DRUG_TRIP, + triple2drug, + 0, + generic_lines + NUM_GRENADES}, + // Page 1, Hardwares. + {1, 0, + LEFT_MARGIN, CENTER_X - RIGHT_MARGIN, TOP_MARGIN, + TITLE_COLOR, ITEM_COLOR, + 0, ITEMS_PER_PAGE, NUM_HARDWAREZ, + REF_STR_HardwareTitle, + ACTIVE_HARDWARE, + FIELD_OFFSET(hardwarez), + MFD_INV_HARDWARE, + ware_name_func, + soft_quant_func, + generic_draw_list, + NULL, + ware_use_func, + HARD_CLASSES, + ware_add_func, + ware_drop_func, + HARD_TRIP, + NULL, + 0, + generic_lines}, + {1, 1, + CENTER_X + LEFT_MARGIN, RIGHT_X - RIGHT_MARGIN, TOP_MARGIN, + TITLE_COLOR, ITEM_COLOR, + 0, ITEMS_PER_PAGE, NUM_HARDWAREZ, + REF_STR_Null, + ACTIVE_HARDWARE, + FIELD_OFFSET(hardwarez), + MFD_INV_HARDWARE, + ware_name_func, + soft_quant_func, + generic_draw_list, + NULL, + ware_use_func, + HARD_CLASSES, + ware_add_func, + ware_drop_func, + HARD_TRIP, + NULL, + 0, + generic_lines}, + // Page 2. General + {2, 0, + LEFT_MARGIN, CENTER_X - RIGHT_MARGIN, TOP_MARGIN, + TITLE_COLOR, ITEM_COLOR, + 0, ITEMS_PER_PAGE, NUM_GENERAL_SLOTS, + REF_STR_GeneralTitle, + ACTIVE_GENERAL, + 0, + MFD_INV_GENINV, + NULL, + NULL, + draw_general_list, + inv_select_general, + general_use_func, + GENERAL_CLASSES, + general_add_func, + general_drop_func, + 0, + NULL, + 0, + (quantity_state *)general_lines}, + {2, 1, + CENTER_X - RIGHT_MARGIN, RIGHT_X - RIGHT_MARGIN, TOP_MARGIN, + TITLE_COLOR, ITEM_COLOR, + 0, ITEMS_PER_PAGE, NUM_GENERAL_SLOTS, + REF_STR_Null, + ACTIVE_GENERAL, + 0, + MFD_INV_GENINV, + NULL, + NULL, + draw_general_list, + inv_select_general, + general_use_func, + GENERAL_CLASSES, + general_add_func, + general_drop_func, + 0, + NULL, + 0, + (quantity_state *)general_lines}, + // Page 2, Softwares. + {5, 0, + LEFT_MARGIN, CENTER_X - RIGHT_MARGIN, TOP_MARGIN, + TITLE_COLOR, ITEM_COLOR, + 0, NUM_COMBAT_SOFTS - 1, NUM_COMBAT_SOFTS - 1, + REF_STR_SoftTitle, + ACTIVE_COMBAT_SOFT, + FIELD_OFFSET(softs.combat), + MFD_INV_SOFT_COMBAT, + ware_name_func, + soft_quant_func, + generic_draw_list, + NULL, + ware_use_func, + SOFT_CLASSES, + ware_add_func, + ware_drop_func, + COMSOFT_TRIP, + NULL, + 0, + generic_lines}, + {5, 0, + LEFT_MARGIN, CENTER_X - RIGHT_MARGIN, TOP_MARGIN + 6 * Y_STEP, + TITLE_COLOR, ITEM_COLOR, + 0, 1, 1, + REF_STR_Null, + ACTIVE_DEFENSE_SOFT, + FIELD_OFFSET(softs.defense), + MFD_INV_SOFT_DEFENSE, + ware_name_func, + soft_quant_func, + generic_draw_list, + NULL, + ware_use_func, + SOFT_CLASSES, + ware_add_func, + ware_drop_func, + DEFSOFT_TRIP, + NULL, + 0, + generic_lines + NUM_COMBAT_SOFTS}, + {5, 0, + CENTER_X + LEFT_MARGIN, RIGHT_X - RIGHT_MARGIN, TOP_MARGIN, + TITLE_COLOR, ITEM_COLOR, + 0, NUM_ONESHOT_SOFTWARE, NUM_ONESHOT_SOFTWARE, + REF_STR_Null, + ACTIVE_MISC_SOFT, + FIELD_OFFSET(softs.misc), + MFD_INV_SOFT_MISC, + ware_name_func, + generic_quant_func, + generic_draw_list, + NULL, + ware_use_func, + SOFT_CLASSES, + ware_add_func, + ware_drop_func, + MISCSOFT_TRIP, + NULL, + 0, + generic_lines + NUM_COMBAT_SOFTS + NUM_DEFENSE_SOFTS}, + {5, 0, + CENTER_X + LEFT_MARGIN, RIGHT_X - RIGHT_MARGIN, TOP_MARGIN + 6 * Y_STEP, + TITLE_COLOR, ITEM_COLOR, + NUM_ONESHOT_SOFTWARE, 1, NUM_MISC_SOFTWARE, + REF_STR_Null, + ACTIVE_MISC_SOFT, + FIELD_OFFSET(softs.misc), + MFD_INV_SOFT_MISC, + ware_name_func, + null_quant_func, + generic_draw_list, + NULL, + ware_use_func, + SOFT_CLASSES, + ware_add_func, + ware_drop_func, + MISCSOFT_TRIP, + NULL, + 0, + generic_lines + NUM_COMBAT_SOFTS + NUM_DEFENSE_SOFTS + NUM_ONESHOT_SOFTWARE}, + // Page 7 main log page + {7, 0, + LEFT_MARGIN, CENTER_X - RIGHT_MARGIN, TOP_MARGIN, + TITLE_COLOR, ITEM_COLOR, + 0, ITEMS_PER_PAGE, NUM_LOG_LEVELS, + REF_STR_LogTitle, + NULL_ACTIVE, + FIELD_OFFSET(logs), + MFD_INV_NULL, + log_name_func, + generic_quant_func, + generic_draw_list, + log_use_func, + log_use_func, + SOFT_CLASSES, + email_add_func, + email_drop_func, + EMAIL_TRIP, + NULL, + 0, + generic_lines}, + {7, 1, + CENTER_X + LEFT_MARGIN, RIGHT_X, TOP_MARGIN, + TITLE_COLOR, ITEM_COLOR, + 0, ITEMS_PER_PAGE, NUM_LOG_LEVELS, + REF_STR_Null, + NULL_ACTIVE, + FIELD_OFFSET(logs), + MFD_INV_NULL, + log_name_func, + generic_quant_func, + generic_draw_list, + log_use_func, + log_use_func, + SOFT_CLASSES, + email_add_func, + email_drop_func, + EMAIL_TRIP, + NULL, + 0, + generic_lines}, +#ifdef NEED_THIRD_LOGLVL_PAGE + {7, 2, + CENTER_X + LEFT_MARGIN, RIGHT_X, TOP_MARGIN - Y_STEP, + TITLE_COLOR, ITEM_COLOR, + 0, ITEMS_PER_PAGE, NUM_LOG_LEVELS - 1, + REF_STR_Null, + NULL_ACTIVE, + FIELD_OFFSET(logs), + MFD_INV_NULL, + log_name_func, + generic_quant_func, + generic_draw_list, + log_use_func, + log_use_func, + SOFT_CLASSES, + email_add_func, + email_drop_func, + EMAIL_TRIP, + NULL, + 0, + generic_lines}, +#endif + // Page 8, Data + {8, 0, + LEFT_MARGIN, ONETHIRD_X - RIGHT_MARGIN, TOP_MARGIN, + TITLE_COLOR, ITEM_COLOR, + FIRST_DATA, ITEMS_PER_PAGE, FIRST_DATA + ITEMS_PER_PAGE, + REF_STR_DataTitle, + NULL_ACTIVE, + FIELD_OFFSET(email), + MFD_INV_NULL, + email_name_func, + null_quant_func, + generic_draw_list, + email_use_func, + email_use_func, + SOFT_CLASSES, + email_add_func, + email_drop_func, + EMAIL_TRIP, + NULL, + 0, + generic_lines}, + {8, 1, + ONETHIRD_X + LEFT_MARGIN, TWOTHIRDS_X - RIGHT_MARGIN, TOP_MARGIN, + TITLE_COLOR, ITEM_COLOR, + FIRST_DATA + ITEMS_PER_PAGE, ITEMS_PER_PAGE + 1, FIRST_DATA + 2 * ITEMS_PER_PAGE + 1, + REF_STR_Null, + NULL_ACTIVE, + FIELD_OFFSET(email), + MFD_INV_NULL, + email_name_func, + null_quant_func, + generic_draw_list, + email_use_func, + email_use_func, + SOFT_CLASSES, + email_add_func, + email_drop_func, + EMAIL_TRIP, + NULL, + 0, + generic_lines}, + {8, 2, + TWOTHIRDS_X + LEFT_MARGIN, RIGHT_X, TOP_MARGIN, + TITLE_COLOR, ITEM_COLOR, + FIRST_DATA + 2 * ITEMS_PER_PAGE + 1, ITEMS_PER_PAGE + 1, NUM_EMAIL, + REF_STR_Null, + NULL_ACTIVE, + FIELD_OFFSET(email), + MFD_INV_NULL, + email_name_func, + null_quant_func, + generic_draw_list, + email_use_func, + email_use_func, + SOFT_CLASSES, + email_add_func, + email_drop_func, + EMAIL_TRIP, + NULL, + 0, + generic_lines}, + // Ammo page, off screen. + {9, 0, + AMMO_LEFT_1, AMMO_RIGHT_1, TOP_MARGIN, + TITLE_COLOR, ITEM_COLOR, + 0, NUM_AMMO_TYPES, NUM_AMMO_TYPES, + REF_STR_PistolCartTitle, + ACTIVE_CART, + FIELD_OFFSET(cartridges), + MFD_INV_AMMO, + ammo_name_func, + generic_quant_func, + generic_draw_list, + NULL, + NULL, + AMMO_CLASSES, + generic_add_func, + generic_drop_func, + AMMO_TRIP, + NULL, + 0, + generic_lines}, + // Pages 50-52 Email + {50, 0, + LEFT_MARGIN, CENTER_X - RIGHT_MARGIN, TOP_MARGIN, + TITLE_COLOR, EMAIL_COLOR_FUNC, + 0, ITEMS_PER_PAGE - 1, NUM_EMAIL_PROPER, + REF_STR_EmailTitle, + NULL_ACTIVE, + FIELD_OFFSET(email), + MFD_INV_NULL, + email_name_func, + null_quant_func, + generic_draw_list, + email_use_func, + email_use_func, + SOFT_CLASSES, + email_add_func, + email_drop_func, + EMAIL_TRIP, + NULL, + 0, + generic_lines}, + {50, 0, + LEFT_MARGIN, CENTER_X - RIGHT_MARGIN, TOP_MARGIN + (ITEMS_PER_PAGE - 1) * Y_STEP, + TITLE_COLOR, EMAIL_COLOR_FUNC, + 0, ITEMS_PER_PAGE - 1, NUM_EMAIL_PROPER, + REF_STR_EmailTitle, + NULL_ACTIVE, + FIELD_OFFSET(email), + MFD_INV_NULL, + NULL, + NULL, + email_more_draw, + email_more_use, + email_more_use, + 0, + NULL, + NULL, + 0, + NULL, + 0, + generic_lines}, + {50, 1, + CENTER_X + LEFT_MARGIN, RIGHT_X - RIGHT_MARGIN, TOP_MARGIN, + TITLE_COLOR, EMAIL_COLOR_FUNC, + 0, ITEMS_PER_PAGE - 1, NUM_EMAIL_PROPER, + REF_STR_Null, + NULL_ACTIVE, + FIELD_OFFSET(email), + MFD_INV_NULL, + email_name_func, + null_quant_func, + generic_draw_list, + email_use_func, + email_use_func, + SOFT_CLASSES, + email_add_func, + email_drop_func, + EMAIL_TRIP, + NULL, + 0, + generic_lines}, + {50, 1, + CENTER_X + LEFT_MARGIN, RIGHT_X - RIGHT_MARGIN, TOP_MARGIN + (ITEMS_PER_PAGE - 1) * Y_STEP, + TITLE_COLOR, EMAIL_COLOR_FUNC, + 0, ITEMS_PER_PAGE - 1, NUM_EMAIL_PROPER, + REF_STR_Null, + NULL_ACTIVE, + FIELD_OFFSET(email), + MFD_INV_NULL, + NULL, + NULL, + email_more_draw, + email_more_use, + email_more_use, + 0, + NULL, + NULL, + EMAIL_TRIP, + NULL, + 0, + generic_lines}, + {51, 2, + LEFT_MARGIN, CENTER_X - RIGHT_MARGIN, TOP_MARGIN, + TITLE_COLOR, EMAIL_COLOR_FUNC, + 0, ITEMS_PER_PAGE - 1, NUM_EMAIL_PROPER, + REF_STR_EmailTitle, + NULL_ACTIVE, + FIELD_OFFSET(email), + MFD_INV_NULL, + email_name_func, + null_quant_func, + generic_draw_list, + email_use_func, + email_use_func, + SOFT_CLASSES, + email_add_func, + email_drop_func, + EMAIL_TRIP, + NULL, + 0, + generic_lines}, + {51, 2, + LEFT_MARGIN, CENTER_X - RIGHT_MARGIN, TOP_MARGIN + (ITEMS_PER_PAGE - 1) * Y_STEP, + TITLE_COLOR, EMAIL_COLOR_FUNC, + 0, ITEMS_PER_PAGE - 1, NUM_EMAIL_PROPER, + REF_STR_Null, + NULL_ACTIVE, + FIELD_OFFSET(email), + MFD_INV_NULL, + NULL, + NULL, + email_more_draw, + email_more_use, + email_more_use, + 0, + NULL, + NULL, + 0, + NULL, + 0, + generic_lines}, + {51, 3, + CENTER_X + LEFT_MARGIN, RIGHT_X - RIGHT_MARGIN, TOP_MARGIN, + TITLE_COLOR, EMAIL_COLOR_FUNC, + 0, ITEMS_PER_PAGE - 1, NUM_EMAIL_PROPER, + REF_STR_Null, + NULL_ACTIVE, + FIELD_OFFSET(email), + MFD_INV_NULL, + email_name_func, + null_quant_func, + generic_draw_list, + email_use_func, + email_use_func, + SOFT_CLASSES, + email_add_func, + email_drop_func, + EMAIL_TRIP, + NULL, + 0, + generic_lines}, + {51, 3, + CENTER_X + LEFT_MARGIN, RIGHT_X - RIGHT_MARGIN, TOP_MARGIN + (ITEMS_PER_PAGE - 1) * Y_STEP, + TITLE_COLOR, EMAIL_COLOR_FUNC, + 0, ITEMS_PER_PAGE - 1, NUM_EMAIL_PROPER, + REF_STR_EmailTitle, + NULL_ACTIVE, + FIELD_OFFSET(email), + MFD_INV_NULL, + NULL, + NULL, + email_more_draw, + email_more_use, + email_more_use, + 0, + NULL, + NULL, + EMAIL_TRIP, + NULL, + 0, + generic_lines}, + {52, 4, + LEFT_MARGIN, CENTER_X - RIGHT_MARGIN, TOP_MARGIN, + TITLE_COLOR, EMAIL_COLOR_FUNC, + 0, ITEMS_PER_PAGE - 1, NUM_EMAIL_PROPER, + REF_STR_EmailTitle, + NULL_ACTIVE, + FIELD_OFFSET(email), + MFD_INV_NULL, + email_name_func, + null_quant_func, + generic_draw_list, + email_use_func, + email_use_func, + SOFT_CLASSES, + email_add_func, + email_drop_func, + EMAIL_TRIP, + NULL, + 0, + generic_lines}, + {52, 4, + LEFT_MARGIN, CENTER_X - RIGHT_MARGIN, TOP_MARGIN + (ITEMS_PER_PAGE - 1) * Y_STEP, + TITLE_COLOR, EMAIL_COLOR_FUNC, + 0, ITEMS_PER_PAGE - 1, NUM_EMAIL_PROPER, + REF_STR_EmailTitle, + NULL_ACTIVE, + FIELD_OFFSET(email), + MFD_INV_NULL, + NULL, + NULL, + email_more_draw, + email_more_use, + email_more_use, + 0, + NULL, + NULL, + 0, + NULL, + 0, + generic_lines}, + {52, 5, + CENTER_X + LEFT_MARGIN, RIGHT_X - RIGHT_MARGIN, TOP_MARGIN, + TITLE_COLOR, EMAIL_COLOR_FUNC, + 0, ITEMS_PER_PAGE - 1, NUM_EMAIL_PROPER, + REF_STR_Null, + NULL_ACTIVE, + FIELD_OFFSET(email), + MFD_INV_NULL, + email_name_func, + null_quant_func, + generic_draw_list, + email_use_func, + email_use_func, + SOFT_CLASSES, + email_add_func, + email_drop_func, + EMAIL_TRIP, + NULL, + 0, + generic_lines}, + {52, 5, + CENTER_X + LEFT_MARGIN, RIGHT_X - RIGHT_MARGIN, TOP_MARGIN + (ITEMS_PER_PAGE - 1) * Y_STEP, + TITLE_COLOR, EMAIL_COLOR_FUNC, + 0, ITEMS_PER_PAGE - 1, NUM_EMAIL_PROPER, + REF_STR_EmailTitle, + NULL_ACTIVE, + FIELD_OFFSET(email), + MFD_INV_NULL, + NULL, + NULL, + email_more_draw, + email_more_use, + email_more_use, + 0, + NULL, + NULL, + EMAIL_TRIP, + NULL, + 0, + generic_lines}, +// Page 20 logs +#define LOG_PAGE(i) \ + {FIRST_LOG_PAGE + i, 0, \ + LEFT_MARGIN, CENTER_X - RIGHT_MARGIN, TOP_MARGIN, \ + TITLE_COLOR, EMAIL_COLOR_FUNC, \ + NUM_EMAIL_PROPER + (i)*LOGS_PER_LEVEL, \ + ITEMS_PER_PAGE, NUM_EMAIL_PROPER + (i)*LOGS_PER_LEVEL + ITEMS_PER_PAGE, \ + REF_STR_LogName0 + i, \ + NULL_ACTIVE, \ + FIELD_OFFSET(email), \ + MFD_INV_NULL, \ + email_name_func, \ + null_quant_func, \ + generic_draw_list, \ + email_use_func, \ + email_use_func, \ + SOFT_CLASSES, \ + email_add_func, \ + email_drop_func, \ + EMAIL_TRIP, \ + NULL, \ + 0, \ + generic_lines}, \ + {FIRST_LOG_PAGE + i, 2, \ + CENTER_X + LEFT_MARGIN, RIGHT_X - RIGHT_MARGIN, TOP_MARGIN - Y_STEP, \ + TITLE_COLOR, EMAIL_COLOR_FUNC, \ + NUM_EMAIL_PROPER + (i)*LOGS_PER_LEVEL + 2 * ITEMS_PER_PAGE + 1, \ + 1, NUM_EMAIL_PROPER + ((i) + 1) * LOGS_PER_LEVEL , \ + REF_STR_Null, \ + NULL_ACTIVE, \ + FIELD_OFFSET(email), \ + MFD_INV_NULL, \ + email_name_func, \ + null_quant_func, \ + generic_draw_list, \ + email_use_func, \ + email_use_func, \ + SOFT_CLASSES, \ + email_add_func, \ + email_drop_func, \ + EMAIL_TRIP, \ + NULL, \ + 0, \ + generic_lines + 2 * ITEMS_PER_PAGE}, \ + {FIRST_LOG_PAGE + i, 1, \ + CENTER_X + LEFT_MARGIN, RIGHT_X - RIGHT_MARGIN, TOP_MARGIN, \ + TITLE_COLOR, EMAIL_COLOR_FUNC, \ + NUM_EMAIL_PROPER + (i)*LOGS_PER_LEVEL + ITEMS_PER_PAGE, ITEMS_PER_PAGE, \ + NUM_EMAIL_PROPER + (i)*LOGS_PER_LEVEL + 2 * ITEMS_PER_PAGE + 1, \ + REF_STR_Null, \ + NULL_ACTIVE, \ + FIELD_OFFSET(email), \ + MFD_INV_NULL, \ + email_name_func, \ + null_quant_func, \ + generic_draw_list, \ + email_use_func, \ + email_use_func, \ + SOFT_CLASSES, \ + email_add_func, \ + email_drop_func, \ + EMAIL_TRIP, \ + NULL, \ + 0, \ + generic_lines} + + // Hey these pages MUST BE LAST. + LOG_PAGE(0), +#ifdef EXPLICIT_LOG_PAGES + LOG_PAGE(1), + LOG_PAGE(2), + LOG_PAGE(3), + LOG_PAGE(4), + LOG_PAGE(5), + LOG_PAGE(6), + LOG_PAGE(7), + LOG_PAGE(8), + LOG_PAGE(9), + LOG_PAGE(10), + LOG_PAGE(11), + LOG_PAGE(12), + LOG_PAGE(13), + LOG_PAGE(14), +#endif // EXPLICIT_LOG_PAGES + +}; + +#define NUM_INV_DISPLAYS (sizeof(inv_display_list) / sizeof(inv_display)) + +#define DUMMY_LOG_INDEX (NUM_INV_DISPLAYS - 3) + +void super_drop_func(int dispnum, int row) { + inv_display *dp = &(inv_display_list[dispnum]); + dp->drop(dp, row); +} + +void super_use_func(int dispnum, int row) { + inv_display *dp = &(inv_display_list[dispnum]); + dp->use(dp, row); +} + +void gen_log_displays(int pgnum) { + inv_display *dp = &inv_display_list[DUMMY_LOG_INDEX]; + pgnum -= FIRST_LOG_PAGE; + if (pgnum >= 0 && pgnum < NUM_LOG_LEVELS) { + dp->pgnum = FIRST_LOG_PAGE + pgnum; + dp->first = NUM_EMAIL_PROPER + pgnum * LOGS_PER_LEVEL; + dp->listlen = NUM_EMAIL_PROPER + ITEMS_PER_PAGE + pgnum * LOGS_PER_LEVEL; + dp->titlenum = MKREF(RES_lognames, pgnum); + dp++; + dp->pgnum = FIRST_LOG_PAGE + pgnum; + dp->first = NUM_EMAIL_PROPER + pgnum * LOGS_PER_LEVEL + 2 * ITEMS_PER_PAGE + 1; + dp->listlen = NUM_EMAIL_PROPER + (pgnum + 1) * LOGS_PER_LEVEL; + dp++; + dp->pgnum = FIRST_LOG_PAGE + pgnum; + dp->first = NUM_EMAIL_PROPER + pgnum * LOGS_PER_LEVEL + ITEMS_PER_PAGE; + dp->listlen = NUM_EMAIL_PROPER + pgnum * LOGS_PER_LEVEL + 2 * ITEMS_PER_PAGE + 1; + } +} + +uchar gen_inv_page(int pgnum, int *i, inv_display **dp) { + if (*i == 0) + gen_log_displays(pgnum); + for (; *i < NUM_INV_DISPLAYS; (*i)++) { + inv_display *idp = &inv_display_list[*i]; + if (idp->pgnum == pgnum) { + *dp = idp; + return TRUE; + } + } + return FALSE; +} + +#define LOG_PAGE_SHF 8 + +uchar gen_inv_displays(int *i, inv_display **dp) { + if (*i == 0) + gen_log_displays(inventory_page); + for (; *i < NUM_INV_DISPLAYS; (*i)++) { + inv_display *idp = &inv_display_list[*i]; + *dp = idp; + return TRUE; + } + return FALSE; +} + +void absorb_object_on_cursor(ushort keycode, uint32_t context, intptr_t data) { + if (object_on_cursor == 0) + return; + + if (inventory_add_object(object_on_cursor, TRUE)) + pop_cursor_object(); +} diff --git a/engine/src/GameSrc/leanmetr.c b/engine/src/GameSrc/leanmetr.c new file mode 100644 index 0000000..d901088 --- /dev/null +++ b/engine/src/GameSrc/leanmetr.c @@ -0,0 +1,593 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/leanmetr.c $ + * $Revision: 1.31 $ + * $Author: mahk $ + * $Date: 1994/11/09 20:45:40 $ + * + */ + +#include "faketime.h" +#include "citres.h" +#include "physics.h" +#include "player.h" +#include "criterr.h" +#include "froslew.h" +#include "objprop.h" +#include "objsim.h" +#include "tools.h" +#include "wares.h" +#include "canvchek.h" + +#include "gamescr.h" +#include "otrip.h" + +#include "gr2ss.h" + +// ------- +// DEFINES +// ------- + + +#define SLOT_EYEMETER_X 141 +#define SLOT_EYEMETER_Y 1 + +#define FULL_EYEMETER_X 10 +#define FULL_EYEMETER_Y 1 + +#define EYEMETER_X() (current_meter_region->abs_x) +#define EYEMETER_Y() (current_meter_region->abs_y) +#define LEANOMETER_XOFF 21 +#define LEANOMETER_YOFF 0 +#define LEANOMETER_X() (EYEMETER_X() + LEANOMETER_XOFF) +#define LEANOMETER_Y() (EYEMETER_Y() + LEANOMETER_YOFF) + +#define EYEMETER_W 19 +#define EYEMETER_H 22 +#define EYE_LEFT_MARGIN 2 +#define DISCRETE_EYE_POSITIONS 3 +#define DISCRETE_MIDDLE_H 6 + +#define MAX_EYE_ANGLE (8 * FIXANG_PI / 18) + +#define LEANOMETER_W 23 +#define LEANOMETER_H EYEMETER_H + +#define NUM_LEAN_BMAPS 9 +#define BMAPS_PER_POSTURE 3 + +extern uchar full_game_3d; + +static ubyte discrete_eye_height[DISCRETE_EYE_POSITIONS] = { + 3, + 10, + 18, +}; +ubyte hires_eye_height[DISCRETE_EYE_POSITIONS] = { + 8, + 26, + 45, +}; +LGPoint shield_offsets[9] = { + {2, 1}, // stand right + {2, 1}, // stand + {2, 1}, // stand left + {2, 1}, // crouch right + {2, 1}, // crouch + {2, 0}, // crouch left + {2, 1}, // prone right + {2, 1}, // prone + {0, 1} // prone left +}; + +void EDMS_lean_o_meter(physics_handle ph, fix *lean, fix *crouch); + +// ------- +// GLOBALS +// ------- +extern uchar gBioInited; + +LGRegion slot_meter_region; +LGRegion fullscrn_meter_region; +LGRegion *current_meter_region = &slot_meter_region; + +#define PICK_METER_REGION(full) ((full) ? &fullscrn_meter_region : &slot_meter_region) + +ushort lean_bmap_res = 0; +ushort shield_bmap_res = 0; + +#define lean_bmap_id(i) (MKREF(lean_bmap_res, i)) +#define eye_bmap_r(i) (get_bitmap_from_ref(REF_IMG_bmEyeIconR + (i))) +#define eye_bmap_l(i) (get_bitmap_from_ref(REF_IMG_bmEyeIconL + (i))) +// KLC - lock instead of get #define lean_bmaps(i) (get_bitmap_from_ref(lean_bmap_id(i))) +#define lean_bmaps(i) (lock_bitmap_from_ref(lean_bmap_id(i))) +#define meter_bkgnd() (get_bitmap_from_ref(full_game_3d ? REF_IMG_bmLeanBkgndTransp : REF_IMG_bmLeanBkgnd)) + +uchar eye_fine_mode = FALSE; + + +// --------- +// INTERNALS +// --------- +#define MAX_LEAN_BASE (RES_leanMeterRad - RES_leanMeterBase) + +void set_base_lean_bmap(uchar shield) { + int v; + ushort baseRes; + + // Set the lean bitmaps resource. + + v = player_struct.hardwarez[CPTRIP(ENV_HARD_TRIPLE)]; // v will be 0-2. + if (v > MAX_LEAN_BASE) + v = MAX_LEAN_BASE; + baseRes = RES_leanMeterBase + v; + + if (baseRes != lean_bmap_res) // If the base lean bitmap has changed: + { + if (lean_bmap_res != 0) // free the old bitmap + { + ResUnlock(lean_bmap_res); + ResDrop(lean_bmap_res); + } + ResLock(baseRes); // Load hi and lock the new bitmap series. + lean_bmap_res = baseRes; // this is our base bitmap now + } + + // Set the shield bitmaps resource. + + if (shield) { + v = SHIELD_SETTING(player_struct.hardwarez_status[CPTRIP(SHIELD_HARD_TRIPLE)]); + if (player_struct.hardwarez[CPTRIP(SHIELD_HARD_TRIPLE)] == SHIELD_VERSIONS) + v = SHIELD_VERSIONS - 1; + baseRes = RES_leanShield1 + v; + + if (baseRes != shield_bmap_res) { + // If the base shield bitmap has changed: + if (shield_bmap_res != 0) { + // free the old bitmap + ResUnlock(shield_bmap_res); + ResDrop(shield_bmap_res); + } + ResLock(baseRes); // Load hi and lock the new bitmap series. + shield_bmap_res = baseRes; // this is our base shield bitmap now + } + } else { + // If shields are now off: + if (shield_bmap_res != 0) { + // If shields were previously on, free up the shield bitmaps. + ResUnlock(shield_bmap_res); + ResDrop(shield_bmap_res); + shield_bmap_res = 0; + } + } +} + +#define LEAN_CONST (fix_2pi / 10) +#define LEAN_TO_LEANX(ln) lg_min(100, lg_max(-100, (100 * (ln) / LEAN_CONST))) + +#define PLAYER_HGT (fix_make(0, 0xbd00)) +#define CROUCH_CONST (2 * PLAYER_HGT / 5) +#define CROUCH_TO_POSTURE(c) lg_min(2, lg_max(0, (2 - lg_max(c, 0) * 2 / CROUCH_CONST))) + +// The posture meter output is filtered by a capacitor, +// the constant below is the reciprocal of the +// filter's RC time constant, in units of 1/sec. +#define POSTURE_FILTER_RATE 20 + +#ifdef BIASED_CAPACITOR +fix compute_filter_weight(ulong deltat) { + int rate = POSTURE_FILTER_RATE; + fix bias = FIX_UNIT * CONTROL_MAX_VAL / (CONTROL_MAX_VAL + 3 * abs(player_struct.controls[CONTROL_YVEL])); + fix weight = fix_make(deltat, 0) * rate >> APPROX_CIT_CYCLE_SHFT; + Spew(DSRC_USER_I_Lean, ("posture filter weight = %q, bias = %q\n", weight, bias)); + weight = fix_mul(weight, bias); + return weight; +} +#else +fix compute_filter_weight(ulong deltat) { + int rate = POSTURE_FILTER_RATE; + fix weight = fix_make(deltat, 0) * rate >> APPROX_CIT_CYCLE_SHFT; + + return weight; +} +#endif + +fix apply_weighted_filter(fix input, fix state, ulong deltat) { + fix weight = compute_filter_weight(deltat); + return fix_div(fix_mul(weight, input) + state, weight + FIX_UNIT); +} + +#define INTENDED_HGT (PLAYER_HGT * (NUM_POSTURES - player_struct.posture - 1) / (NUM_POSTURES - 1)) + +#define STORE_STATE(fval) \ + (player_struct.lean_filter_state = (ushort)((fval) > FIX_UNIT ? FIX_UNIT - 1 : fix_frac(fval))) +#define GET_STATE (fix_make(0, player_struct.lean_filter_state)); + +#define SLAM_DURATION (CIT_CYCLE / 2) + +void slam_posture_meter_state(void) { + STORE_STATE(INTENDED_HGT); + player_struct.posture_slam_state = player_struct.game_time + SLAM_DURATION; +} + +fix velocity_crouch_filter(fix crouch) { + ubyte posture = player_struct.posture + 1; + fix hgt = INTENDED_HGT; + fix vel = posture * FIX_UNIT * + (abs(player_struct.controls[CONTROL_YVEL] + abs(player_struct.controls[CONTROL_ZVEL]))) / + (2 * CONTROL_MAX_VAL); + return fix_div(fix_mul(hgt, vel) + crouch, vel + FIX_UNIT); +} + +void lean_icon(LGPoint *pos, grs_bitmap **icon, int *inum) { + int posture, leanx; + uchar *bp; + + // Determine the posture and lean amount for the player currently. + if (!global_fullmap->cyber) { + fix ln, crouch; + fix state = GET_STATE; + + EDMS_lean_o_meter(PLAYER_PHYSICS, &ln, &crouch); + + crouch = velocity_crouch_filter(crouch); + if (player_struct.game_time > player_struct.posture_slam_state) + state = apply_weighted_filter(crouch, state, player_struct.deltat); + STORE_STATE(state); + posture = CROUCH_TO_POSTURE(state); + leanx = LEAN_TO_LEANX(ln); + } else { + posture = POSTURE_STAND; + leanx = 0; + } + + // Calculate the bitmap resource index based on posture and lean. + *inum = posture * BMAPS_PER_POSTURE + ((100 - leanx) * BMAPS_PER_POSTURE / 201); + + // Get a pointer to the corresponding lean bitmap. + FrameDesc *f = RefGet(MKREF(lean_bmap_res,*inum)); + if (f != NULL) { + f->bm.bits = (uchar *)(f + 1); + *icon = &(f->bm); + } else + DEBUG("%s: No lean resource bitmap!", __FUNCTION__); + + // Determine where to draw the bitmap. + pos->y = 53 - (*icon)->h; + pos->x = (46 - (*icon)->w + 1) * abs(leanx) / 600; + if (leanx < 0) + pos->x = -pos->x; + pos->x += LEANOMETER_X() + (25) / 2 - ((*icon)->w + 1) / 2; + pos->y += LEANOMETER_Y() - 31; +} + +static void undraw_meter_area(LGRect *r) { + short a, b, c, d; + char saveMode; + int x, y; + + STORE_CLIP(a, b, c, d); + ss_safe_set_cliprect(r->ul.x, r->ul.y, r->lr.x, r->lr.y); + x = EYEMETER_X(); + y = EYEMETER_Y(); + + if (is_onscreen()) + uiHideMouse(r); + ss_bitmap(meter_bkgnd(), x, y); + if (is_onscreen()) + uiShowMouse(r); + + RESTORE_CLIP(a, b, c, d); +} + +void player_reset_eye(void) { player_struct.eye_pos = eye_mods[1] = 0; } + +void player_set_eye(byte eyecntl) { + int theta = MAX_EYE_ANGLE * eyecntl / CONTROL_MAX_VAL; + player_struct.eye_pos = theta; + + if (theta < 0) + theta += 2 * FIXANG_PI; + eye_mods[1] = theta; +} + +byte player_get_eye(void) { + int theta = eye_mods[1]; + if (theta > FIXANG_PI) + theta -= 2 * FIXANG_PI; + return (byte)(theta * CONTROL_MAX_VAL / MAX_EYE_ANGLE); +} + +void player_set_eye_fixang(int ang) { + int theta = ang; + if (abs(ang) > MAX_EYE_ANGLE) + theta = (ang < 0) ? -MAX_EYE_ANGLE : MAX_EYE_ANGLE; + player_struct.eye_pos = theta; + if (theta < 0) + theta += 2 * FIXANG_PI; + eye_mods[1] = theta; +} + +int player_get_eye_fixang(void) { + int theta = eye_mods[1]; + if (theta > FIXANG_PI) + theta -= 2 * FIXANG_PI; + return theta; +} + +uchar eye_mouse_handler(uiEvent *ev, LGRegion *r, intptr_t data) { + short x = ev->pos.x - r->abs_x; + short y = ev->pos.y - r->abs_y; + extern uchar hack_takeover; + if (hack_takeover || global_fullmap->cyber) + return FALSE; + if (x < 0 || x >= EYEMETER_W) + return FALSE; + eye_fine_mode = 2 * x > EYEMETER_W; + if (!eye_fine_mode) + y = discrete_eye_height[y * DISCRETE_EYE_POSITIONS / EYEMETER_H]; + if (ev->mouse_data.buttons & (1 << MOUSE_LBUTTON)) { + int theta; + if ((ev->mouse_data.action & MOUSE_LDOWN) == 0 && uiLastMouseRegion[MOUSE_LBUTTON] != r) + return FALSE; + if (eye_fine_mode) + theta = -2 * MAX_EYE_ANGLE * (y) / (EYEMETER_H - 1) + MAX_EYE_ANGLE; + else + theta = -FIXANG_PI / 6 * (y * DISCRETE_EYE_POSITIONS / EYEMETER_H - 1); + // ui_mouse_constrain_xy(me->pos.x,r->abs_y,me->pos.x,r->abs_y+EYEMETER_H-1); + player_set_eye_fixang(theta); + physics_set_relax(CONTROL_YZROT, FALSE); + } + if (ev->mouse_data.buttons == 0) { + // mouse_constrain_xy(0,0,grd_cap->w-1,grd_cap->h-1); + } + return TRUE; +} + +uchar lean_mouse_handler(uiEvent *ev, LGRegion *r, intptr_t data) { + short x = ev->pos.x - r->abs_x - LEANOMETER_XOFF; + short y = ev->pos.y - r->abs_y - LEANOMETER_YOFF; + if (x < 0 || x >= LEANOMETER_W || global_fullmap->cyber) + return FALSE; + if (ev->mouse_data.buttons & (1 << MOUSE_LBUTTON)) { + short posture = y * 3 / LEANOMETER_H; + short xlean = x * 220 / (LEANOMETER_W - 1) - 110; + if ((ev->mouse_data.action & MOUSE_LDOWN) == 0 && uiLastMouseRegion[MOUSE_LBUTTON] != r) + return FALSE; + if (xlean > 10) + xlean -= 10; + else if (xlean < -10) + xlean += 10; + else + xlean = 0; + if (posture != player_struct.posture) + player_set_posture(posture); + player_set_lean(xlean, player_struct.leany); + physics_set_relax(CONTROL_XZROT, FALSE); + // ui_mouse_constrain_xy(LEANOMETER_X(),LEANOMETER_Y()+posture*LEANOMETER_H/3+1,LEANOMETER_X()+LEANOMETER_W-1,LEANOMETER_Y()+(posture+1)*LEANOMETER_H/3-1); + } + if (ev->mouse_data.buttons == 0) { + // mouse_unconstrain(); + } + return TRUE; +} + + // --------- + // EXTERNALS + // --------- + +#define BAD_REGION_CRITERR 3000 + +void init_posture_meters(LGRegion *root, uchar fullscreen) { + LGRegion *reg = PICK_METER_REGION(fullscreen); + int id; + LGRect r = {{0, 0}, {LEANOMETER_W + LEANOMETER_XOFF, EYEMETER_H}}; + errtype err; + + if (fullscreen) { + RECT_MOVE(&r, MakePoint(FULL_EYEMETER_X, FULL_EYEMETER_Y)); + } else { + RECT_MOVE(&r, MakePoint(SLOT_EYEMETER_X, SLOT_EYEMETER_Y)); + } + err = region_create(root, reg, &r, 0, 0, REG_USER_CONTROLLED | AUTODESTROY_FLAG, NULL, NULL, NULL, NULL); + if (err != OK) + critical_error(BAD_REGION_CRITERR); + uiInstallRegionHandler(reg, UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE, eye_mouse_handler, 0, &id); + uiInstallRegionHandler(reg, UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE, lean_mouse_handler, 0, &id); +} + +void update_lean_meter(uchar force) { + static uchar last_shield = FALSE; + static uchar last_shieldstr = 0; + static int last_lean_icon = -1; + static LGPoint last_lean_pos = {-1, -1}; + + LGRect r; + LGPoint pos; + short a, b, c, d; + grs_bitmap *icon; + int inum; + bool shield = WareActive(player_struct.hardwarez_status[CPTRIP(SHIELD_HARD_TRIPLE)]) != 0; + // int shieldstr = SHIELD_SETTING(player_struct.hardwarez_status[CPTRIP(SHIELD_HARD_TRIPLE)]); + int shieldstr; + char saveMode; + bool saveBio; + + // if(player_struct.hardwarez[CPTRIP(SHIELD_HARD_TRIPLE)]==SHIELD_VERSIONS) + // shieldstr=SHIELD_VERSIONS-1; + + current_meter_region = PICK_METER_REGION(full_game_3d); + set_base_lean_bmap(shield); + lean_icon(&pos, &icon, &inum); + + if (shield_bmap_res > 0) + shieldstr = shield_bmap_res - RES_leanShield1; + else + shieldstr = 0; + + // If this is not a force update, and we're drawing the same lean icon in the same position as last + // time, then do nothing. + if (!force && PointsEqual(pos, last_lean_pos) && MKREF(lean_bmap_res, inum) == last_lean_icon + && shield == last_shield && shieldstr == last_shieldstr) + return; + + STORE_CLIP(a, b, c, d); + ss_safe_set_cliprect(LEANOMETER_X(), LEANOMETER_Y(), LEANOMETER_X() + LEANOMETER_W, LEANOMETER_Y() + LEANOMETER_H); + + saveBio = gBioInited; // Turn off biometer while updating the lean meter. + gBioInited = FALSE; + + if (force || last_lean_icon != -1) { + RECT_FILL(&r, LEANOMETER_X(), LEANOMETER_Y(), LEANOMETER_X() + 46, LEANOMETER_Y() + 53); + undraw_meter_area(&r); + } + RECT_FILL(&r, pos.x, pos.y, pos.x + icon->w, pos.y + icon->h); + + // saveMode = convert_use_mode; + // convert_use_mode = 0; + + if (is_onscreen()) + uiHideMouse(&r); + ss_bitmap(icon, r.ul.x, r.ul.y); + + if (shield) { + grs_bitmap *sbm; + + // Get a pointer to the corresponding lean bitmap. + FrameDesc *f = RefGet(MKREF(shield_bmap_res, inum)); + if (f != NULL) { + f->bm.bits = (uint8_t *)(f + 1); + sbm = &(f->bm); + } else + DEBUG("%s: No shield resource bitmap!", __FUNCTION__); + + // Place shield image with offset + ss_bitmap(sbm, r.ul.x - shield_offsets[inum].x, r.ul.y - shield_offsets[inum].y); + } + + gBioInited = saveBio; + + if (is_onscreen()) + uiShowMouse(&r); + // convert_use_mode = saveMode; + + last_lean_pos = pos; + last_lean_icon = MKREF(lean_bmap_res, inum); + last_shield = shield; + last_shieldstr = shieldstr; + RESTORE_CLIP(a, b, c, d); +} + +void draw_eye_bitmap(grs_bitmap *eye_bmap, LGPoint pos, int lasty) { + LGRect r; + char saveMode; + + current_meter_region = PICK_METER_REGION(full_game_3d); + pos.x += EYEMETER_X(); + pos.y += EYEMETER_Y(); + r.ul = pos; + r.lr.x = pos.x + eye_bmap->w; + r.ul.y = lasty; + r.lr.y = lasty + eye_bmap->h + 1; + undraw_meter_area(&r); + r.ul.y = pos.y; + r.lr.y = pos.y + eye_bmap->h; + + // saveMode = convert_use_mode; + // convert_use_mode = 0; + if (is_onscreen()) + uiHideMouse(&r); + ss_bitmap(eye_bmap, r.ul.x, r.ul.y); + if (is_onscreen()) + uiShowMouse(&r); + // convert_use_mode = saveMode; +} + +#define HIRES_EYEMETER_H 53 + +void update_eye_meter(uchar force) { + static short last_y = 0; + static short last_ly = 0; + static uchar last_mode = FALSE; + + short a, b, c, d; + fix pos = eye_mods[1]; + int yang = pos % (2 * FIXANG_PI); + short y; + short lefty; + grs_bitmap *eye_rbmap = eye_bmap_r(!eye_fine_mode); + grs_bitmap *eye_lbmap = eye_bmap_l(eye_fine_mode); + bool saveBio; + + if (yang > FIXANG_PI) + yang -= 2 * FIXANG_PI; + y = -(EYEMETER_H * yang / (2 * MAX_EYE_ANGLE)); + + // Hey, let's take gruesome advantange of the fact that + // booleans are zero or one. + lefty = discrete_eye_height[1 + (yang < 0) - (yang > 0)]; + lefty -= (eye_lbmap->h) / 2; + y += EYEMETER_H / 2 - 2 - (eye_rbmap->h / 2) / 2; + + if (!force && y == last_y && lefty == last_ly && eye_fine_mode == last_mode) + return; + STORE_CLIP(a, b, c, d); + ss_safe_set_cliprect(EYEMETER_X(), EYEMETER_Y(), EYEMETER_X() + EYEMETER_W, EYEMETER_Y() + EYEMETER_H); + + saveBio = gBioInited; // Turn off biometer while updating the eye meter. + gBioInited = FALSE; + + if (force) { + LGRect r = {{0, 0}, {46, 53}}; + RECT_MOVE(&r, MakePoint(EYEMETER_X(), EYEMETER_Y())); + undraw_meter_area(&r); + } + draw_eye_bitmap(eye_lbmap, MakePoint(2, lefty), last_ly); + draw_eye_bitmap(eye_rbmap, MakePoint(19 - eye_rbmap->w, y), last_y); + + gBioInited = saveBio; + + RESTORE_CLIP(a, b, c, d); + last_y = y; + last_ly = lefty; + last_mode = eye_fine_mode; +} + +void update_meters(uchar force) { + current_meter_region = PICK_METER_REGION(full_game_3d); + update_eye_meter(force); + update_lean_meter(force); +} + +void zoom_to_lean_meter(void) { + extern bool DoubleSize; + + LGPoint pos; + LGRect start = {{-5, -5}, {+5, +5}}; + LGRect end = {{0, 0}, {LEANOMETER_W, LEANOMETER_H}}; + + current_meter_region = PICK_METER_REGION(full_game_3d); + RECT_MOVE(&end, MakePoint(LEANOMETER_X(), LEANOMETER_Y())); + mouse_get_xy(&pos.x, &pos.y); + if (!DoubleSize) + ss_point_convert(&(pos.x), &(pos.y), TRUE); + RECT_MOVE(&start, pos); + zoom_rect(&start, &end); +} diff --git a/engine/src/GameSrc/mainloop.c b/engine/src/GameSrc/mainloop.c new file mode 100644 index 0000000..80b3444 --- /dev/null +++ b/engine/src/GameSrc/mainloop.c @@ -0,0 +1,172 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/mainloop.c $ + * $Revision: 1.42 $ + * $Author: xemu $ + * $Date: 1994/11/09 02:09:05 $ + */ + +/* + * Citadel main loops + * + * The idea here is that we have a separate loop for each game mode/setup + * There is a 4/12 bit change flag which is 4 global and 12 local + * The global loop checks OS and input + * Then calls the local loop, which does it's internal inlined functions + * and then processes it's change flags + * The global loop then gets control and does it's own change flags + * If you want to switch modes/loops you call change_loop which both sets + * global change bit 3 as well setting some variables. When the main loop + * reaches the bottom it triggers on change bit 3 and calls the switch code + */ + +#define __MAINLOOP_SRC + +#include + +#include "InitMac.h" +#include "Shock.h" +#include "amaploop.h" +#include "cutsloop.h" +#include "game_screen.h" +#include "fullscrn.h" +#include "loops.h" +#include "fullamap.h" +#include "input.h" +#include "sdl_events.h" +#include "setup.h" +#include "status.h" +#include "tickcount.h" +#include "tools.h" +#include "wrapper.h" + +// how is the game doing, anyway, set to true at end of time +uchar cit_success = FALSE; + +// are we "paused" +uchar game_paused = FALSE; + +frc *_current_fr_context; +short _current_loop = SETUP_LOOP; /* which loop we currently are */ +short _current_3d_flag = DEMOVIEW_UPDATE; +LGRegion *_current_view = NULL; +uint _change_flag = 0; /* change flags for loop */ +uint _static_change = 0; /* current static changes */ +short _new_mode = 0; /* mode to change to, if any */ +short _last_mode = 0; /* last mode, if you want to change back to it */ +uchar time_passes = TRUE; +uchar saves_allowed = FALSE; +uchar physics_running = TRUE; +uchar ai_on = TRUE; +uchar anim_on = TRUE; +uchar player_invulnerable = FALSE; +uchar player_immortal = FALSE; +uchar always_render = FALSE; +uchar pal_fx_on = TRUE; + +// Note that in the shipping version, the edit_loop stuff should never +// get called, but needs to be SOMETHING as a place holder +// void +// (*citadel_loops[])(void)={game_loop,game_loop,game_loop,game_loop,setup_loop,game_loop,cutscene_loop,game_loop,automap_loop}; +// void +// (*enter_modes[])(void)={screen_start,fullscreen_start,screen_start,screen_start,setup_start,screen_start,cutscene_start,fullscreen_start,amap_start}; +// void (*exit_modes[])(void)={screen_exit,fullscreen_exit,screen_exit, +// screen_exit,setup_exit,screen_exit,cutscene_exit,fullscreen_exit,amap_exit}; + +void (*citadel_loops[])(void) = {game_loop, game_loop, game_loop, game_loop, setup_loop, game_loop, cutscene_loop, game_loop, automap_loop}; +void (*enter_modes[])(void) = {screen_start, fullscreen_start, NULL, NULL, setup_start, NULL, cutscene_start, fullscreen_start, amap_start}; +void (*exit_modes[])(void) = {screen_exit, fullscreen_exit, NULL, NULL, setup_exit, NULL, cutscene_exit, fullscreen_exit, amap_exit}; + +void loopmode_switch(short *cmode) { +#ifdef SVGA_SUPPORT + extern uchar wrapper_screenmode_hack; +#endif + + // Actually switch mode + _last_mode = *cmode; + (*exit_modes[_last_mode])(); + *cmode = _new_mode; + _static_change = 0; + if (*cmode >= 0) + (*enter_modes[*cmode])(); + +#ifdef SVGA_SUPPORT + if (wrapper_screenmode_hack) { + wrapper_start(screenmode_screen_init); + } +#endif +} + +void loopmode_exit(short loopmode) { + if (exit_modes[loopmode]) + (*exit_modes[loopmode])(); +} + +void loopmode_enter(short loopmode) { (*enter_modes[loopmode])(); } + +extern void MousePollProc(void); +void mainloop(int argc, char *argv[]) { + while (_current_loop >= 0 && gPlayingGame) { + gShockTicks = TickCount(); + + if (!(_change_flag & (ML_CHG_BASE << 1))) + loopLine(ML | 1, input_chk()); // go get the UI stuff going + + // DG: at the beginning of each frame, get all the events from SDL + pump_events(); + + // Run the loop + (*citadel_loops[_current_loop])(); + + if (globalChanges) // really, only loopmode_switch (the <<3 case) + { // will be in the game + // if (_change_flag&(ML_CHG_BASE<<0)) { loopLine(ML|0x10,loop_debug()); } + if (_change_flag & (ML_CHG_BASE << 3)) { + loopLine(ML | 0x13, loopmode_switch(&_current_loop)); + } + chg_unset_flg(ML_CHG_BASE << 3); + } +#ifdef ALWAYS_SHOW_FR + fr_show_rate(-1); +#endif + // OR in the static change flags... + chg_set_flg(_static_change); + + MousePollProc(); // update the cursor, was 35 times/sec originally + + status_bio_update(); + ZoomDrawProc(FALSE); //draw zoom rectangle if enabled; if not, returns immediately + + SDLDraw(); + + ZoomDrawProc(TRUE); //erase zoom rectangle if enabled; if not, returns immediately + } + + cit_success = TRUE; + // hit them atexit's +} + +errtype static_change_copy() { + if (always_render) + chg_set_sta(_current_3d_flag); + else + chg_unset_sta(_current_3d_flag); + return (OK); +} diff --git a/engine/src/GameSrc/map.c b/engine/src/GameSrc/map.c new file mode 100644 index 0000000..324bb4a --- /dev/null +++ b/engine/src/GameSrc/map.c @@ -0,0 +1,119 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +/* + * $Source: r:/prj/cit/src/RCS/map.c $ + * $Revision: 1.15 $ + * $Author: xemu $ + * $Date: 1994/11/21 06:09:42 $ + * + */ + +#include "map.h" +#include "schedule.h" +#include "statics.h" + +MapElem *global_map; +FullMap *global_fullmap; + +//------------------------ +// Defines +//------------------------ +#define MAP_SCHEDULE_SIZE 128 + +//------------------------ +// Prototypes +//------------------------ +errtype _map_init_elem(FullMap *fmap, int i, int j); + +errtype _map_init_elem(FullMap *fmap, int i, int j) { + MapElem *me = FULLMAP_GET_XY(fmap, i, j); + // clear tiletype + me_tiletype_set(me, 0); + + // clear tmaps[3] + me_tmap_flr_set(me, 0); + me_tmap_wall_set(me, 0); + me_tmap_ceil_set(me, 0); + + // clear flags + me_flags_set(me, 0); + + // clear heights[2] + me_height_flr_set(me, 0); + me_height_ceil_set(me, 0); + + // clear param + me_param_set(me, 0); + + // clear objRef + me_objref_set(me, 0); + + me_templight_flr_set(me, 0); + me_templight_ceil_set(me, 0); + + me_clearsolid_set(me, 0); + me_subclip_set(me, 0xFF); // secret gnosis good + me_rotflr_set(me, 0); + me_rotceil_set(me, 0); + me_hazard_bio_set(me, 0); + me_hazard_rad_set(me, 0); + me_flicker_set(me, 0); + + return (OK); +} + +FullMap *map_create(int xshf, int yshf, int zshf, uchar cyb) { + int i, j; + FullMap *fmap = (FullMap *)malloc(sizeof(FullMap)); + fmap->x_shft = xshf; + fmap->y_shft = yshf; + fmap->z_shft = zshf; + fmap->x_size = 1 << xshf; + fmap->y_size = 1 << yshf; + fmap->cyber = cyb; + fmap->map = (MapElem *)static_map; + for (i = 0; i < fmap->x_size; i++) { + for (j = 0; j < fmap->y_size; j++) { + _map_init_elem(fmap, i, j); + } + } + fmap->x_scale = fmap->y_scale = fmap->z_scale = 0; + for (i = 0; i < NUM_MAP_SCHEDULES; i++) + schedule_init(&fmap->sched[i], MAP_SCHEDULE_SIZE, FALSE); + return fmap; +} + +uchar map_set_default(FullMap *fmap) { + global_fullmap = fmap; + global_map = fmap->map; + return TRUE; +} + +void map_init(void) { + FullMap *ourmap = map_create(DEFAULT_XSHF, DEFAULT_YSHF, DEFAULT_ZSHF, FALSE); + map_set_default(ourmap); +} + +void map_free(void) { + for (int i = 0; i < NUM_MAP_SCHEDULES; i++) + schedule_free(&global_fullmap->sched[i]); + free(fm_map(global_fullmap)); + free(global_fullmap); +} diff --git a/engine/src/GameSrc/mfdfunc.c b/engine/src/GameSrc/mfdfunc.c new file mode 100644 index 0000000..75c2ef8 --- /dev/null +++ b/engine/src/GameSrc/mfdfunc.c @@ -0,0 +1,3159 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/mfdfunc.c $ + * $Revision: 1.237 $ + * $Author: mahk $ + * $Date: 1994/11/23 20:34:20 $ + * + */ + +// Source code for all MFD Expose/Handler function pairs +// This file is for callbacks only, actual infrastructure belongs +// in newmfd.c + +#include + +#include "ammomfd.h" +#include "bark.h" +#include "biohelp.h" +#include "cardmfd.h" +#include "cybermfd.h" +#include "email.h" +#include "fixtrmfd.h" +#include "gearmfd.h" +#include "invent.h" +#include "mfdgump.h" +#include "mfdpanel.h" +#include "newmfd.h" +#include "objprop.h" // temp +#include "plotware.h" +#include "tools.h" +#include "view360.h" +#include "viewhelp.h" +#include "colors.h" +#include "mainloop.h" +#include "gameloop.h" +#include "mfdart.h" +#include "gamescr.h" +#include "objwarez.h" +#include "objsim.h" +#include "gamestrn.h" +#include "cybstrng.h" +#include "mfdint.h" +#include "mfdext.h" +#include "mfddims.h" +#include "player.h" +#include "wares.h" +#include "drugs.h" +#include "weapons.h" +#include "automap.h" +#include "target.h" +#include "criterr.h" +#include "mfdgadg.h" +#include "objclass.h" +#include "otrip.h" +#include "citres.h" +#include "objuse.h" +#include "sfxlist.h" +#include "musicai.h" +#include "fullscrn.h" +#include "objbit.h" +#include "limits.h" +#include "mapflags.h" +#include "input.h" +#include "gr2ss.h" +#include "mfdfunc.h" +#include "mfdgames.h" +#include "shodan.h" + +#define MFD_SHIELD_FUNC 19 + +// ------------ +// Useful Defines +// ------------ + +#define OVERLOAD_BUTTON_Y (MFD_VIEW_HGT - 24) +#define TEMPR_X 47 +#define TEMPR_Y 27 +#define TEMPR_WIDTH 13 +#define TEMPR_HEIGHT 16 +#define TEMPR_DIV 8 +#define SETTING_TEXT 46 +#define ENERGY_TEXT_LEN 40 + +static uchar in_or_out = FALSE; + +#define LNAME_BUFSIZE 128 + +#define GOOD_RED (RED_BASE + 5) +#define ITEM_COLOR (0x5A) +#define SELECTED_ITEM_COLOR (0x4C) +#define UNAVAILABLE_ITEM_COLOR (0x60) +#define X_MARGIN 1 +#define Y_STEP 5 + +#define PUSH_CANVAS(x) gr_push_canvas(x) +#define POP_CANVAS() gr_pop_canvas() + +#define MFD_REGION(m) ((full_game_3d) ? &(m)->reg2 : &(m)->reg) + +// ------- +// Globals +// ------- + +// Forward declaration of array at bottom of file + +extern uchar full_game_3d; + +LGRegion *mfd_regions[NUM_MFDS]; + +// ---------------- +// Local Prototypes +// ---------------- + +void mfd_clear_view(void); +void draw_blank_mfd(void); + +errtype mfd_item_init(MFD_Func *mfd); +void mfd_expose_blank(MFD *m, ubyte control); +void mfd_item_expose(MFD *m, ubyte control); +uchar mfd_item_handler(MFD *m, uiEvent *e); +void mfd_item_micro_hires_expose(uchar full, int triple); + +void mfd_general_inv_expose(MFD *m, ubyte control, ObjID id, uchar full); +uchar mfd_general_inv_handler(MFD *m, uiEvent *ev, int row); + +uchar mfd_lantern_button_handler(MFD *m, LGPoint bttn, uiEvent *ev, void *data); +void mfd_lantern_setting(int setting); +errtype mfd_lanternware_init(MFD_Func *f); +void mfd_lanternware_expose(MFD *mfd, ubyte control); + +void draw_ammo_button(int triple, short x, short y); + +errtype mfd_anim_init(); +void mfd_anim_expose(MFD *m, ubyte control); + +errtype mfd_weapon_init(MFD_Func *mfd); +void mfd_weapon_expose(MFD *m, ubyte control); +uchar mfd_weapon_handler(MFD *m, uiEvent *e); +uchar mfd_weapon_beam_handler(MFD *m, uiEvent *e); +uchar mfd_weapon_projectile_handler(MFD *m, uiEvent *e, weapon_slot *ws); +uchar mfd_weapon_expose_projectile(MFD *m, weapon_slot *ws, ubyte control); +void mfd_weapon_expose_beam(weapon_slot *ws, ubyte id, uchar Redraw); +void mfd_weapon_draw_temp(ubyte temp); +void mfd_weapon_draw_ammo_buttons(int num_ammo_buttons, int ammo_subclass, ubyte *ammo_types, ubyte curr_ammo_type, + int ammo_count); +void mfd_weapon_draw_beam_status_bar(int charge, int setting, uchar does_overload); + + +uchar weapon_mfd_temp; + +void mfd_bioware_expose(MFD *m, ubyte control); + +// ------------ +// USEFUL STUFF +// ------------ + +void mfd_clear_view(void) { + if (full_game_3d) + return; + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); +} + +int mfd_bmap_id(int triple) { + int obclass = TRIP2CL(triple); + int t = CPTRIP(triple); + return MKREF(RES_mfdClass_1 + obclass, t); +} + +void draw_blank_mfd(void) { + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + draw_res_bm(REF_IMG_bmBlankMFD, 0, 0); + // draw_hires_resource_bm(REF_IMG_bmBlankMFD, 0, 0); + draw_res_bm(MKREF(RES_mfdArtOverlays, MFD_ART_TRIOP), 0, 0); +} + +// -------------------- +// FUNCTION INITIALIZER +// -------------------- + +// --------------------------------------------------------------------------- +// mfd_init_funcs() +// +// Here is where you set the global MFD_Func structures to point at +// expose/handler pairs, and also where you set their flags. This is also +// where MFD virtual slots are set to point at their functions. + +void mfd_init_funcs() { + int i; + // Define a couple of MFD functions. + for (i = 0; i < MFD_NUM_FUNCS; i++) + if (mfd_funcs[i].init != NULL) { + errtype err = mfd_funcs[i].init(&mfd_funcs[i]); + if (err != OK) + critical_error(CRITERR_MISC | 1); + } + + // Set slots to point at functions + set_slot_to_func(MFD_WEAPON_SLOT, MFD_WEAPON_FUNC, MFD_ACTIVE); + set_slot_to_func(MFD_ITEM_SLOT, MFD_ITEM_FUNC, MFD_ACTIVE); + set_slot_to_func(MFD_MAP_SLOT, MFD_MAP_FUNC, MFD_ACTIVE); + set_slot_to_func(MFD_INFO_SLOT, MFD_EMPTY_FUNC, MFD_ACTIVE); + set_slot_to_func(MFD_TARGET_SLOT, MFD_TARGET_FUNC, MFD_ACTIVE); + + // set_slot_to_func(MFD_SPECIAL_SLOT, MFD_ANIM_FUNC, MFD_FLASH); + // set_slot_to_func(MFD_SPECIAL_SLOT, MFD_EMPTY_FUNC, MFD_EMPTY); + + // debug_mfd_func_table(); + // debug_mfd_slots_table(); +} + +// =========================================================================== +// +// =========================================================================== + +// -------------------- +// ACTUAL MFD FUNCTIONS +// -------------------- + +// =========================================================================== +// * THE WEAPON MFD * +// =========================================================================== + +// Hey, wow, floats and doubles are UNcool. + +#define mfd_pixels_per_charge_unit (FIX_UNIT * 69 / 100) // How many hor. pixels equals a % of charge? +#define mfd_charge_units_per_pixel (FIX_UNIT * 100 / 69) + +// note _LEFT is 0, _RIGHT is 1 +#define MFDLeftOffs MFD_LEFT +#define MFDRightOffs MFD_RIGHT +#define MFDLastWeapon 0 +#define MFDAmmo 2 +#define MFDLastBeamHeat 4 + +#define MFD_Access(which, lorr) mfd_fdata[MFD_WEAPON_FUNC][(which) + (lorr)] + +#define MFDGetLastLeftWeapon mfd_fdata[MFD_WEAPON_FUNC][0] +#define MFDGetLastRightWeapon mfd_fdata[MFD_WEAPON_FUNC][1] +#define MFDGetLeftAmmo mfd_fdata[MFD_WEAPON_FUNC][2] +#define MFDGetRightAmmo mfd_fdata[MFD_WEAPON_FUNC][3] +#define MFDGetLastLeftBeamHeat mfd_fdata[MFD_WEAPON_FUNC][4] +#define MFDGetLastRightBeamHeat mfd_fdata[MFD_WEAPON_FUNC][5] +#define MFDSetLastLeftWeapon(n) (mfd_fdata[MFD_WEAPON_FUNC][0] = (n)) +#define MFDSetLastRightWeapon(n) (mfd_fdata[MFD_WEAPON_FUNC][1] = (n)) +#define MFDSetLeftAmmo(n) (mfd_fdata[MFD_WEAPON_FUNC][2] = (n)) +#define MFDSetRightAmmo(n) (mfd_fdata[MFD_WEAPON_FUNC][3] = (n)) +#define MFDSetLastLeftBeamHeat(n) (mfd_fdata[MFD_WEAPON_FUNC][4] = (n)) +#define MFDSetLastRightBeamHeat(n) (mfd_fdata[MFD_WEAPON_FUNC][5] = (n)) + +#define MFD_BEAMWPN_STAT_BORDER GREEN_YELLOW_BASE +#define MFD_BEAMWPN_STAT_CHARGE WHITE +#define MFD_BEAMWPN_STAT_MAXCHARGE PURPLE_BASE +#define MFD_BEAMWPN_STAT_DEADSPACE BLACK + +#define WEAPON_ART_Y 7 + +#define AMMO_BUTTON_H 24 +#define AMMO_BUTTON_W 23 +#define AMMO_BUTTON_Y (MFD_VIEW_HGT - AMMO_BUTTON_H) +#define AMMO_STRING_Y (AMMO_BUTTON_Y - 4) +#define AMMO_NAME_Y (MFD_VIEW_HGT - 6) + +#define AMMO_BUTTON_X1 29 +#define AMMO_BUTTON_DX1 0 +#define AMMO_BUTTON_X2 11 +#define AMMO_BUTTON_DX2 31 +#define AMMO_BUTTON_X3 1 +#define AMMO_BUTTON_DX3 24 + +#define MFD_BEAM_RECT_X1 1 +#define MFD_BEAM_RECT_X2 71 +#define MFD_BEAM_RECT_Y1 52 +#define MFD_BEAM_RECT_Y2 56 + +LGRect MfdAmmoRectZone = {{0, AMMO_BUTTON_Y}, {MFD_VIEW_WID, AMMO_BUTTON_Y + AMMO_BUTTON_H}}; +LGRect MfdBeamStatusRect; + +#define NO_CONSTRAIN NUM_MFDS +static ubyte beam_constrain = NO_CONSTRAIN; + +LGCursor slider_cursor; +grs_bitmap slider_cursor_bmap; + +// ---------- WEAPON MFD FUNC --------------- + +// -------------------------------------------------------------------------- +// mfd_weapon_init() +// +// Initializes the MFD weapons function. + +errtype mfd_weapon_init(MFD_Func *mfd) { +#ifndef NO_DUMMIES + void *yum; + yum = mfd; +#endif // NO_DUMMIES + + MFDSetLastLeftWeapon(0); + MFDSetLastRightWeapon(0); + MFDSetLeftAmmo(0xFF); + MFDSetRightAmmo(0xFF); + + MfdBeamStatusRect.ul.x = MFD_BEAM_RECT_X1; + MfdBeamStatusRect.ul.y = MFD_BEAM_RECT_Y1; + MfdBeamStatusRect.lr.x = MFD_BEAM_RECT_X2; + MfdBeamStatusRect.lr.y = MFD_BEAM_RECT_Y2; + + return OK; +} + +// -------------------------------------------------------------------------- +// mfd_weapon_expose() +// +// Draws an overlay of a weapon in the current mfd slot. + +void mfd_weapon_expose(MFD *m, ubyte control) { + weapon_slot *ws; + char buf[50]; + int triple; + uchar punt = player_struct.actives[ACTIVE_WEAPON] == EMPTY_WEAPON_SLOT; + uchar Redraw = FALSE; + uchar RedrawAmmoArea = TRUE; + extern uchar full_game_3d; + + if (control == 0) { + uiCursorStack *cs; + weapon_mfd_temp = FALSE; + + uiGetRegionCursorStack(MFD_REGION(m), &cs); + uiPopCursorEvery(cs, &slider_cursor); + + if (beam_constrain == m->id) { + beam_constrain = NO_CONSTRAIN; + // KLC mouse_unconstrain(); + } + return; + } + + // Get the triple for the current weapon + + if (!punt) + ws = &player_struct.weapons[player_struct.actives[ACTIVE_WEAPON]]; + if (ws->type == EMPTY_WEAPON_SLOT) + punt = TRUE; + if (punt) { + mfd_expose_blank(m, control); + return; + } + + triple = MAKETRIP(CLASS_GUN, ws->type, ws->subtype); + + if (control & MFD_EXPOSE) { + + PUSH_CANVAS(pmfd_canvas); + mfd_clear_rects(); + + if (control & MFD_EXPOSE_FULL) + Redraw = TRUE; + if (MFD_Access(m->id, MFDLastWeapon) != player_struct.actives[ACTIVE_WEAPON]) { + Redraw = TRUE; + MFD_Access(m->id, MFDLastWeapon) = player_struct.actives[ACTIVE_WEAPON]; + MFD_Access(m->id, MFDAmmo) = 0xFF; + weapon_mfd_temp = FALSE; + } + + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + if (!full_game_3d) + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + + // Draw the appropriate weapon art. + // We draw it here because it is effectively "background" + // Hopefully update-rects will take care of us.. + { + int id = mfd_bmap_id(triple); + draw_res_bm(id, (MFD_VIEW_WID - res_bm_width(id)) / 2, WEAPON_ART_Y); + // draw_hires_resource_bm(id, + // (SCONV_X(MFD_VIEW_WID)-res_bm_width(id))/2, SCONV_Y(WEAPON_ART_Y)); // is this right? + } + + // This is all stuff that should be drawn for a full expose of + // a new weapon + if (Redraw) { + short y = 2; + short w, h; + + // Print name of gun in top line of mfd + get_weapon_long_name(ws->type, ws->subtype, buf); + mfd_draw_string(buf, X_MARGIN, y, GOOD_RED, TRUE); + gr_string_size(buf, &w, &h); + y += h; + + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + } + + // Here is where the dynamic info for a given weapon is drawn; ie; data that + // requires a redraw even when the current weapon has not changed + + if ((ws->type == GUN_SUBCLASS_BEAM) || (ws->type == GUN_SUBCLASS_BEAMPROJ)) + mfd_weapon_expose_beam(ws, m->id, Redraw); + else if (ws->type != GUN_SUBCLASS_HANDTOHAND) + RedrawAmmoArea = mfd_weapon_expose_projectile(m, ws, control); + + // Redraw everything + POP_CANVAS(); + mfd_update_rects(m); + } + +} + +// -------------------------------------------------------------------------- +// mfd_weapon_expose_projectile() +// +// Exposes relevant weapons mfd info for a projectile weapon. +// returns whether or not it drew the ammo buttons + +uchar mfd_weapon_expose_projectile(MFD *m, weapon_slot *ws, ubyte control) { + int num_ammo_buttons; + int ammo_subclass; + ubyte ammo_types[3]; + uchar RedrawAmmoFlag = FALSE; + + // Get the ammo data for the current weapon + get_available_ammo_type(ws->type, ws->subtype, &num_ammo_buttons, ammo_types, &ammo_subclass); + + /* + * FIXME: shamaz 20.04.2020 + * + * The following two lines were present in the code provided by + * Nightdive Studios, LLC along with the following comment: + * > ammo_type and setting have same memory location in + * > weapon_slot union. setting can have values greater than + * > num ammo buttons so check for and fix oob + * It's not clear how oob access can happen because .setting and + * .ammo_type fields are used independently (based on a type of + * the weapon, e.g. beam or other range weapon) and never .setting + * value is treated as .ammo_type and vice versa. + * + * On the other hand they cause a problem when you reload your + * weapon with the last clip (of any suitable type). At first, if + * you unload this clip, it get lost (disappears from your + * inventory forever) even if not depleted completely. At second, + * this final clip is replaced with a clip of other type + * (sometimes even inappropriate for this particular weapon). + * + * So I find it better to comment these lines out. Seems to be + * perfectly safe, but some more testing is required. + */ + /* if (ws->ammo_type >= num_ammo_buttons) */ + /* ws->ammo_type = (num_ammo_buttons ? ammo_types[0] : 0); */ + + if ((control & MFD_EXPOSE_FULL) || (ws->ammo == 0)) + RedrawAmmoFlag = TRUE; + else if (MFD_Access(m->id, MFDAmmo) != ws->ammo) { + RedrawAmmoFlag = TRUE; + MFD_Access(m->id, MFDAmmo) = ws->ammo; + } + + // Redraw ammo buttons if neccessary + if (RedrawAmmoFlag) + mfd_weapon_draw_ammo_buttons(num_ammo_buttons, ammo_subclass, ammo_types, ws->ammo_type, ws->ammo); + + return RedrawAmmoFlag; +} + +// --------------------------------------------------------------------------- +// mfd_weapon_draw_ammo_buttons() +// +// Draws the labelled buttons of ammo on a projectile gun's mfd + +#define MAX_CART_COLORS 3 +#define CARTRIDGE_BRACKET 8 // cartridges per color +static uchar cart_colors[MAX_CART_COLORS] = {GOOD_RED, 0x4b, GREEN_BASE + 2}; + +void draw_ammo_button(int triple, short x, short y) { + short h; + int id; + int carts = player_struct.cartridges[CPTRIP(triple)]; + ubyte cnum = lg_min(MAX_CART_COLORS - 1, (carts - 1) / CARTRIDGE_BRACKET); + + // Draw the outline of an ammo box + draw_res_bm(REF_IMG_BullFrame + lg_min(2, lg_max(3 - carts, 0)), x, y); + id = mfd_bmap_id(triple); + h = res_bm_height(id); + draw_res_bm(id, x + 4, y + AMMO_BUTTON_H - 4 - h); + // draw_hires_resource_bm(id, SCONV_X(x+4), SCONV_Y(y+AMMO_BUTTON_H-3)-h); + if (carts > 0) { + int cnt = carts % CARTRIDGE_BRACKET; + if (cnt == 0) + cnt = CARTRIDGE_BRACKET; + gr_set_fcolor(cart_colors[cnum]); + while (cnt-- > 0) { + short cy = y + AMMO_BUTTON_H - 5 - 2 * cnt; + ss_hline(x + AMMO_BUTTON_W - 6, cy, x + AMMO_BUTTON_W - 5); + } + } + if (carts > 0 || player_struct.partial_clip[CPTRIP(triple)] > 0) + mfd_add_rect(x, y, x + AMMO_BUTTON_W, y + AMMO_BUTTON_H); +} + +void mfd_weapon_draw_ammo_buttons(int num_ammo_buttons, int ammo_subclass, ubyte *ammo_types, ubyte curr_ammo_type, + int ammo_count) { + int i, triple; + + // Draw ammo boxes + if (ammo_count == 0) { + for (i = 0; i < num_ammo_buttons; i++) { + short x = (ammo_types[i]) * AMMO_BUTTON_W; + triple = MAKETRIP(CLASS_AMMO, ammo_subclass, ammo_types[i]); + // Get useful ammo information for box label + draw_ammo_button(triple, x, AMMO_BUTTON_Y); + } + if (num_ammo_buttons > 0) + mfd_draw_string(get_temp_string(REF_STR_ClickToLoad), 1, AMMO_STRING_Y, GREEN_YELLOW_BASE, TRUE); + } else { + char buf[4], buf2[50]; + triple = MAKETRIP(CLASS_AMMO, ammo_subclass, curr_ammo_type); + + sprintf(buf, "%d", ammo_count); + gr_set_font(ResGet(RES_mediumLEDFont)); + mfd_string_shadow = MFD_SHADOW_NEVER; + mfd_draw_font_string(buf, MFD_VIEW_WID - gr_string_width(buf) - 2, AMMO_BUTTON_Y + 2, GOOD_RED, + RES_mediumLEDFont, TRUE); + mfd_string_shadow = MFD_SHADOW_FULLSCREEN; // default + + get_object_short_name(triple, buf2, 50); + gr_set_font(ResGet(MFD_FONT)); + mfd_draw_string(buf2, MFD_VIEW_WID - gr_string_width(buf2) - 2, AMMO_NAME_Y, GREEN_YELLOW_BASE, TRUE); + + draw_ammo_button(triple, 0, AMMO_BUTTON_Y); + draw_res_bm(REF_IMG_BullRightArrow, AMMO_BUTTON_W, AMMO_BUTTON_Y); + } + + if ((num_ammo_buttons == 0) && (ammo_count == 0)) { + draw_res_bm(REF_IMG_NoAmmo, (MFD_VIEW_WID - res_bm_width(REF_IMG_NoAmmo)) / 2, AMMO_BUTTON_Y); + } + mfd_add_rect(0, AMMO_STRING_Y, MFD_VIEW_WID, MFD_VIEW_HGT); + return; +} + +#define MFD_BEAM_WARM 0x40 +#define MFD_BEAM_HOT 0x35 +#define MFD_BEAM_READY 0x58 + +ubyte temp_levels[TEMPR_WIDTH] = {1, 2, 2, 3, 3, 4, 5, 6, 7, 9, 11, 13, 16}; + +// -------------------------------------------------------------------------- +// mfd_weapon_draw_temp() +// + +void mfd_weapon_draw_temp(ubyte temp) { + int i; + + gr_set_fcolor(MFD_BEAM_READY); + + for (i = 0; i < TEMPR_WIDTH; i++) { + if (i == 5) + gr_set_fcolor(MFD_BEAM_WARM); + else if (i == 10) + gr_set_fcolor(MFD_BEAM_HOT); + + if (temp >= i * TEMPR_DIV) + ss_vline(TEMPR_X + i * 2, TEMPR_Y + TEMPR_HEIGHT - temp_levels[i], TEMPR_Y + TEMPR_HEIGHT); + } +} + +// -------------------------------------------------------------------------- +// mfd_weapon_draw_beam_status_bar() +// +// Takes the current charge and the maximum charge, and draws the dynamic +// portion of the beam weapon status bar + +void mfd_weapon_draw_beam_status_bar(int amt, int setting, uchar does_overload) { + ubyte setting_x; + + setting = (BEAM_SETTING_VAL(setting) < MIN_ENERGY_USE) ? 1 : (BEAM_SETTING_VAL(setting) - MIN_ENERGY_USE); + if (does_overload) + setting <<= 1; + + setting++; + + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + gr_set_fcolor(MFD_BEAMWPN_STAT_BORDER); // OUTLINE + ss_box(MfdBeamStatusRect.ul.x - 1, MfdBeamStatusRect.ul.y - 1, MfdBeamStatusRect.lr.x + 1, + MfdBeamStatusRect.lr.y + 1); + + setting_x = (ubyte)fix_int(setting * mfd_pixels_per_charge_unit); + setting_x = lg_min(MfdBeamStatusRect.lr.x - MfdBeamStatusRect.ul.x, setting_x); + + // draw the settings bar only if we're not in overload mode + + if (!in_or_out) + draw_raw_resource_bm(REF_IMG_BeamSetting, MfdBeamStatusRect.ul.x + setting_x - 3, MfdBeamStatusRect.ul.y - 1); + + mfd_add_rect(MfdBeamStatusRect.ul.x, MfdBeamStatusRect.ul.y, MfdBeamStatusRect.lr.x, MfdBeamStatusRect.lr.y); + return; +} + +// -------------------------------------------------------------------------- +// mfd_weapon_expose_beam() +// +// Exposes relevant weapons mfd info for a beam weapon. + +void mfd_weapon_expose_beam(weapon_slot *ws, ubyte id, uchar Redraw) { + char buf[ENERGY_TEXT_LEN]; + uchar does_overload = FALSE; + + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + if (id == MFD_LEFT) { + if (MFDGetLastLeftBeamHeat > ws->ammo) + Redraw = TRUE; + MFDSetLastLeftBeamHeat(ws->ammo); + } + if (id == MFD_RIGHT) { + if (MFDGetLastRightBeamHeat > ws->ammo) + Redraw = TRUE; + MFDSetLastRightBeamHeat(ws->ammo); + } + + does_overload = does_weapon_overload(ws->type, ws->subtype); + if (does_overload) { + // if beam weapon is not set to overload then draw overload button, otherwise say "overload enabled" + if (Redraw && OVERLOAD_VALUE(ws->setting)) { + short w = res_bm_width(REF_IMG_BeamOverloadOn); + + draw_raw_resource_bm(REF_IMG_BeamOverloadOn, 1, OVERLOAD_BUTTON_Y); + mfd_add_rect(1, OVERLOAD_BUTTON_Y, 1 + w, MFD_VIEW_HGT); + + get_string(REF_STR_Overload, buf, ENERGY_TEXT_LEN); + mfd_draw_string(buf, 1, SETTING_TEXT, MFD_BEAM_HOT, TRUE); + } else { + short w = res_bm_width(REF_IMG_BeamOverload); + + if (Redraw) { + (ws->ammo < MINIMUM_OVERLOAD) ? draw_raw_resource_bm(REF_IMG_BeamOverload, 1, OVERLOAD_BUTTON_Y) + : draw_raw_resource_bm(REF_IMG_BeamOverloadOff, 1, OVERLOAD_BUTTON_Y); + mfd_add_rect(1, OVERLOAD_BUTTON_Y, 1 + w, MFD_VIEW_HGT); + get_string(REF_STR_EnergySetting, buf, ENERGY_TEXT_LEN); + mfd_draw_string(buf, 1, SETTING_TEXT, MFD_BEAM_READY, TRUE); + } + } + } + + get_string(REF_STR_LowSetting, buf, ENERGY_TEXT_LEN); + mfd_draw_string(buf, 2, MFD_BEAM_RECT_Y1 - 1, MFD_BEAM_READY, TRUE); + get_string(REF_STR_HighSetting, buf, ENERGY_TEXT_LEN); + mfd_draw_string(buf, 57, MFD_BEAM_RECT_Y1 - 1, MFD_BEAM_READY, TRUE); + + draw_raw_resource_bm(REF_IMG_BeamTemperature, TEMPR_X, TEMPR_Y); + + mfd_weapon_draw_temp(ws->ammo); + + // Redraw the beam energy status bar + mfd_weapon_draw_beam_status_bar(ws->ammo, ws->setting, does_overload); + + return; +} + +// --------------------------------------------------------------------------- +// mfd_weapon_handler() +// +// Mostly responsible for figuring out which ammo boxes were clicked on +// to select new ammo, and for manipulating the charge on beam weapons + +uchar mfd_weapon_handler(MFD *m, uiEvent *e) { + weapon_slot *ws; + int triple; + + // Get the triple for the current weapon + ws = &player_struct.weapons[player_struct.actives[ACTIVE_WEAPON]]; + triple = MAKETRIP(CLASS_GUN, ws->type, ws->subtype); + + switch (ws->type) { + case GUN_SUBCLASS_BEAM: // Is beam charge being set? + case GUN_SUBCLASS_BEAMPROJ: // Is beam charge being set? + // if (!(mouse->action & ~MOUSE_MOTION) && !(mouse->buttons & (1 << MOUSE_LBUTTON))) + // return FALSE; + return mfd_weapon_beam_handler(m, e); + break; + case GUN_SUBCLASS_PISTOL: + case GUN_SUBCLASS_AUTO: + case GUN_SUBCLASS_SPECIAL: + if (e->mouse_data.action == MOUSE_MOTION) + return FALSE; + return mfd_weapon_projectile_handler(m, e, ws); + break; + default: + break; + } + return FALSE; +} + +ubyte old_energy_setting = 0xFF; + +// --------------------------------------------------------------------------- +// mfd_weapon_beam_handler() +// +// This is the handler for beam type weapons in the weapons mfd. + +uchar mfd_weapon_beam_handler(MFD *m, uiEvent *e) { + uchar retval = TRUE; + LGRect r; + ubyte setting, setting_x; + weapon_slot *ws = &player_struct.weapons[player_struct.actives[ACTIVE_WEAPON]]; + uchar overld = does_weapon_overload(ws->type, ws->subtype); + +#ifdef CURSOR_BACKUPS + extern grs_bitmap backup_mfd_cursor; + extern uchar *backup[NUM_BACKUP_BITS]; +#endif + + // We're interested in this event iff its a mouse up,down,action event + // in the beam status bar. + if (!(e->mouse_data.action & (MOUSE_LUP | MOUSE_LDOWN | MOUSE_MOTION))) { + return FALSE; + } + + if (overld) { + // okay - here's the overload button code + if (e->mouse_data.action & MOUSE_LDOWN) { + + if (ws->ammo < MINIMUM_OVERLOAD) { + // set up the rect for the overload button + r.ul.x = (MFD_VIEW_WID - res_bm_width(REF_IMG_NoAmmo)) / 2; + r.ul.y = OVERLOAD_BUTTON_Y; + r.lr.x = r.ul.x + res_bm_width(REF_IMG_BeamOverload); + r.lr.y = OVERLOAD_BUTTON_Y + res_bm_height(REF_IMG_BeamOverload); + RECT_OFFSETTED_RECT(&r, m->rect.ul, &r); + + // check if we clicked in the button + if (RECT_TEST_PT(&r, e->pos)) { + // toggle between the two values + chg_set_flg(INVENTORY_UPDATE); + OVERLOAD_VALUE(ws->setting) ? OVERLOAD_RESET(ws->setting) : OVERLOAD_SET(ws->setting); + mfd_force_update(); // make sure it redraws the mfd + return TRUE; + } + } + } + } + + RECT_OFFSETTED_RECT(&MfdBeamStatusRect, m->rect.ul, &r); + + if (!RECT_TEST_PT(&r, e->pos)) { + uiCursorStack *cs; + + uiGetRegionCursorStack(MFD_REGION(m), &cs); + uiPopCursorEvery(cs, &slider_cursor); + mfd_notify_func(MFD_WEAPON_FUNC, MFD_WEAPON_SLOT, FALSE, MFD_ACTIVE, FALSE); + if (!in_or_out) + return retval; + } + if (!in_or_out && (e->mouse_data.buttons == 0)) + return retval; + + // If the left button was pushed, we constrain the mouse to + // the beam status bar, and change the cursor appropriately + if (e->mouse_data.action & MOUSE_LDOWN) { + + in_or_out = TRUE; + + // Constrain the mouse to a 1-pixel y + slider_cursor.hotspot.x = slider_cursor_bmap.w / 2; + slider_cursor.hotspot.y = (slider_cursor_bmap.h / 2) + e->pos.y - (r.ul.y + r.lr.y) / 2; + ui_mouse_constrain_xy(r.ul.x + 1, e->pos.y, r.lr.x - 2, e->pos.y); + beam_constrain = m->id; + // Get our funky mfd-beam-phaser-setting cursor + uiPushRegionCursor(MFD_REGION(m), &slider_cursor); +#ifdef CURSOR_BACKUPS + backup[20] = (uchar *)malloc(f->bm.w * f->bm.h); + LG_memcpy(backup[20], f->bm.bits, f->bm.w * f->bm.h); + gr_init_bm(&backup_mfd_cursor, backup[14], BMT_FLAT8, 0, mfd_cursor.w, mfd_cursor.h); +#endif + retval = TRUE; + } + + // If the left button was released, unconstrain the mouse and reset + // the cursor bitmap. There's no else here for the weird case that + // an up and down event might happen "simultaneously" + if (e->mouse_data.action & MOUSE_LUP) { + + in_or_out = FALSE; + + // Let the mouse run free + // note that we are NOT using the UI here since we're already looking at grd_cap + mouse_constrain_xy(0, 0, grd_cap->w - 1, grd_cap->h - 1); + beam_constrain = NO_CONSTRAIN; + + uiPopRegionCursor(MFD_REGION(m)); + mfd_notify_func(MFD_WEAPON_FUNC, MFD_WEAPON_SLOT, FALSE, MFD_ACTIVE, TRUE); + + retval = TRUE; + } + + // Calculate the max charge setting based on mouse position (making goofy + // exceptions for the endpoints for aesthetics) and set the weapon + // accordingly. + setting_x = e->pos.x - r.ul.x; + if ((e->mouse_data.action & MOUSE_MOTION) && (setting_x == old_energy_setting)) + return retval; + old_energy_setting = setting_x; + + setting = (ubyte)fix_int(setting_x * mfd_charge_units_per_pixel); + + if (overld) + setting >>= 1; + + setting += MIN_ENERGY_USE; + uiSetCursor(); + + set_beam_weapon_max_charge(player_struct.actives[ACTIVE_WEAPON], setting); + // min(MAX_HEAT,setting)); + + return TRUE; +} + +// --------------------------------------------------------------------------- +// mfd_weapon_projectile_handler() +// +// This is the handler for projectile weapons in the weapons mfd. + +uchar mfd_weapon_projectile_handler(MFD *m, uiEvent *e, weapon_slot *ws) { + int ammo_subclass, num_ammo_buttons; + ubyte ammo_types[3]; + LGPoint pos = e->pos; + + pos.x -= m->rect.ul.x; + pos.y -= m->rect.ul.y; + + if (pos.y < AMMO_BUTTON_Y) + return FALSE; + // If we're already loaded, check for double click. + if (ws->ammo > 0) { + uchar retval = FALSE; + if (e->mouse_data.action & UI_MOUSE_LDOUBLE) { + unload_current_weapon(); + MFDSetLeftAmmo(0xFF); + MFDSetRightAmmo(0xFF); + mfd_notify_func(MFD_WEAPON_FUNC, MFD_WEAPON_SLOT, FALSE, MFD_ACTIVE, FALSE); + mfd_notify_func(MFD_ITEM_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + retval = TRUE; + } else if (e->mouse_data.action & MOUSE_LDOWN) { + string_message_info(REF_STR_DClickToUnload); + retval = TRUE; + } + return retval; + } + + // If it wasn't a left-mouse-button down event, throw it away. + if (!(e->mouse_data.action & (MOUSE_LDOWN | UI_MOUSE_LDOUBLE))) + return FALSE; + + // Get the ammo data for the current weapon + get_available_ammo_type(ws->type, ws->subtype, &num_ammo_buttons, ammo_types, &ammo_subclass); + { + int b = pos.x / AMMO_BUTTON_W; + int atype; + for (atype = 0; atype < num_ammo_buttons; atype++) { + // If we have a hit, we let each mfd know independently that + // it needs to redraw its ammo buttons, to save redraw time + if (b == ammo_types[atype] && change_ammo_type(b)) { + if (weapon_mfd_temp) { + int mfd; + for (mfd = 0; mfd < NUM_MFDS; mfd++) { + if (player_struct.mfd_current_slots[mfd] == MFD_WEAPON_SLOT) + restore_mfd_slot(mfd); + } + weapon_mfd_temp = FALSE; + } + MFDSetLeftAmmo(0xFF); + MFDSetRightAmmo(0xFF); + mfd_notify_func(MFD_WEAPON_FUNC, MFD_WEAPON_SLOT, FALSE, MFD_ACTIVE, FALSE); + mfd_notify_func(MFD_ITEM_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + break; + } + } + } + + return TRUE; +} + +void weapon_mfd_for_reload(void) { + uchar target_pri; + uchar take_mfd; + + // Do not take down target mfd in favor of weapon in this case! + target_pri = mfd_funcs[MFD_TARGET_FUNC].priority; + mfd_funcs[MFD_TARGET_FUNC].priority = 1; + take_mfd = mfd_choose_func(MFD_WEAPON_FUNC, MFD_WEAPON_SLOT); + if (player_struct.mfd_current_slots[take_mfd] != MFD_WEAPON_SLOT) { + save_mfd_slot(take_mfd); + weapon_mfd_temp = TRUE; + } + mfd_change_slot(take_mfd, MFD_WEAPON_SLOT); + mfd_funcs[MFD_TARGET_FUNC].priority = target_pri; +} + +// =========================================================================== +// * THE ITEM MFD * +// =========================================================================== + +// OK, the item MFD is pretty heinous, and, in fact, they all +// really want to be their own MFDfuncs, so, let's grant them their wish! + +#define MFDGetLastItemClass(m) mfd_fdata[MFD_ITEM_FUNC][(m)] +#define MFDGetLastItemType(m) mfd_fdata[MFD_ITEM_FUNC][(m) + 2] +#define MFDGetCurrItemClass(m) mfd_fdata[MFD_ITEM_FUNC][(m) + 4] +#define MFDGetCurrItemType(m) mfd_fdata[MFD_ITEM_FUNC][(m) + 6] + +#define MFDSetLastItemClass(m, n) mfd_fdata[MFD_ITEM_FUNC][(m)] = (n) +#define MFDSetLastItemType(m, n) mfd_fdata[MFD_ITEM_FUNC][(m) + 2] = (n) +#define MFDSetCurrItemClass(m, n) mfd_fdata[MFD_ITEM_FUNC][(m) + 4] = (n) +#define MFDSetCurrItemType(m, n) mfd_fdata[MFD_ITEM_FUNC][(m) + 6] = (n) + +LGRect MfdGrenadeBox[2]; + +#define MFD_GRENADE_BOX_X1 27 +#define MFD_GRENADE_BOX_X2 47 +#define MFD_GRENADE_BOX_Y 5 +#define MFD_GRENADE_BOX_W 5 +#define MFD_GRENADE_BOX_H 5 + +#define HARDWARE_BUTTON_H 13 +#define HARDWARE_BUTTON_W 44 +#define HARDWARE_BUTTON_X ((MFD_VIEW_WID - HARDWARE_BUTTON_W) / 2) +#define HARDWARE_BUTTON_Y (MFD_VIEW_HGT - HARDWARE_BUTTON_H - 1) + +#define DRUG_BUTTON_H 13 +#define DRUG_BUTTON_W 32 +#define DRUG_BUTTON_X ((MFD_VIEW_WID - DRUG_BUTTON_W) / 2) +#define DRUG_BUTTON_Y (MFD_VIEW_HGT - 1 - DRUG_BUTTON_H) + +// -------------------------------------------------------------------------- +// mfd_item_init() +// +// Sets some info. + +errtype mfd_item_init(MFD_Func *mfd) { + MFDSetCurrItemClass(0, MFD_INV_NULL); + MFDSetCurrItemClass(1, MFD_INV_NULL); + MFDSetLastItemClass(0, MFD_INV_NULL); + MFDSetLastItemClass(1, MFD_INV_NULL); + + MfdGrenadeBox[0].ul.x = MFD_GRENADE_BOX_X1; + MfdGrenadeBox[0].ul.y = MFD_GRENADE_BOX_Y; + MfdGrenadeBox[0].lr.x = MFD_GRENADE_BOX_X1 + MFD_GRENADE_BOX_W; + MfdGrenadeBox[0].lr.y = MFD_GRENADE_BOX_Y + MFD_GRENADE_BOX_H; + + MfdGrenadeBox[1].ul.x = MFD_GRENADE_BOX_X2; + MfdGrenadeBox[1].ul.y = MFD_GRENADE_BOX_Y; + MfdGrenadeBox[1].lr.x = MFD_GRENADE_BOX_X2 + MFD_GRENADE_BOX_W; + MfdGrenadeBox[1].lr.y = MFD_GRENADE_BOX_Y + MFD_GRENADE_BOX_H; + + return OK; +} + +//-------------------------------------------------------------- +// Like mini-expose, but gets the name for you, and +// conforms to our rect-o-tronic update facility + +void mfd_item_micro_expose(uchar full, int triple) { + if (!full_game_3d) { + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + } + if (full) { + LGPoint siz; + int id; + short y = 2; + char buf[LNAME_BUFSIZE]; + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + get_object_long_name(triple, buf, LNAME_BUFSIZE); + siz = mfd_draw_string(buf, X_MARGIN, y, GREEN_YELLOW_BASE, TRUE); + y += siz.y + 2; + + // Draw the appropriate weapon art + id = mfd_bmap_id(triple); + if (RefIndexValid((RefTable *)ResGet(REFID(id)), REFINDEX(id))) + draw_raw_resource_bm(id, (MFD_VIEW_WID - res_bm_width(id)) / 2, y); + else + ResUnlock(REFID(id)); + } + return; +} + +//-------------------------------------------------------------- +// Called by things that know they have hi-res art to display. + +void mfd_item_micro_hires_expose(uchar full, int triple) { + if (!full_game_3d) { + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + gr_bitmap(&mfd_background, 0, 0); + } + if (full) { + LGPoint siz; + int id; + short y = 2; + char buf[LNAME_BUFSIZE]; + + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + get_object_long_name(triple, buf, LNAME_BUFSIZE); + siz = mfd_draw_string(buf, X_MARGIN, y, GREEN_YELLOW_BASE, TRUE); + y += siz.y + 2; + + id = mfd_bmap_id(triple); + if (RefIndexValid((RefTable *)ResGet(REFID(id)), REFINDEX(id))) + draw_hires_resource_bm(id, (SCONV_X(MFD_VIEW_WID) - res_bm_width(id)) / 2, SCONV_Y(y)); + else + ResUnlock(REFID(id)); + } + return; +} + +// -------------------------------------------------------------------------- +// mfd_item_draw_grenade_setting_boxes() +// +// Draws the little increment/decrement boxes on either side of the +// "time left before grenade blows up" setting on the MFD. + +#ifdef OLD_GRENADE_BUTTONS +void mfd_item_draw_grenade_setting_boxes(MFD *m) { + char buf[2]; + int i; + + for (i = 0; i < 2; i++) { + gr_set_fcolor(WHITE); + ss_rect(MfdGrenadeBox[i].ul.x, MfdGrenadeBox[i].ul.y, MfdGrenadeBox[i].lr.x, MfdGrenadeBox[i].lr.y); + gr_set_fcolor(MFD_BTTN_FLASH); + ss_box(MfdGrenadeBox[i].ul.x, MfdGrenadeBox[i].ul.y, MfdGrenadeBox[i].lr.x, MfdGrenadeBox[i].lr.y); + } + + gr_set_fcolor(BLACK); + sprintf(buf, "-"); + ss_string("-", MfdGrenadeBox[0].ul.x + 1, MfdGrenadeBox[0].ul.y + 1); + sprintf(buf, "+"); + ss_string(buf, MfdGrenadeBox[1].ul.x + 1, MfdGrenadeBox[1].ul.y + 1); + + return; +} +#endif // OLD_GRENADE_BUTTONS + +// -------------------------------------------------------------------------- +// mfd_item_expose() +// +// Draws an overlay of a drug molecule or whatever in the current slot. + +#define NULL_ACTIVE 0xFF +static ubyte cat2active[MFD_INV_CATEGORIES] = { + NULL_ACTIVE, ACTIVE_DRUG, ACTIVE_HARDWARE, ACTIVE_GRENADE, ACTIVE_CART, + ACTIVE_WEAPON, ACTIVE_GENERAL, ACTIVE_COMBAT_SOFT, ACTIVE_DEFENSE_SOFT, ACTIVE_MISC_SOFT, +}; + +#define TRASH_BUTTON REF_IMG_DiscardButton + +#define NULL_REF MKREF(ID_NULL, 0) + +Ref smallstuffSpews[] = {NULL_REF, NULL_REF, NULL_REF, REF_STR_gearSpew0, + NULL_REF, NULL_REF, NULL_REF, REF_STR_plotSpew0}; + +void mfd_general_inv_expose(MFD *mfd, ubyte control, ObjID id, uchar full) { + int triple; + Ref spew = NULL_REF; + + if (id == OBJ_NULL) { + draw_blank_mfd(); + return; + } + triple = ID2TRIP(id); + mfd_item_micro_expose(full, triple); + if (full && !(ObjProps[OPNUM(id)].flags & INVENTORY_GENERAL)) { + short x = (MFD_VIEW_WID - res_bm_width(TRASH_BUTTON)) / 2; + short y = (MFD_VIEW_HGT - res_bm_height(TRASH_BUTTON)) / 2; + draw_raw_resource_bm(TRASH_BUTTON, x, y); + mfd_add_rect(x, y, x + res_bm_width(TRASH_BUTTON), y + res_bm_height(TRASH_BUTTON)); + } + if (objs[id].obclass == CLASS_SMALLSTUFF) + spew = smallstuffSpews[objs[id].subclass]; + if (spew != NULL_REF) + draw_mfd_item_spew(spew + objs[id].info.type, 1); +} + +uchar mfd_general_inv_handler(MFD *m, uiEvent *ev, int row) { + ObjID id = player_struct.inventory[row]; + if (ev->type != UI_EVENT_MOUSE || !(ev->subtype & MOUSE_LDOWN)) + return FALSE; + if (!(ObjProps[OPNUM(id)].flags & INVENTORY_GENERAL)) { + LGPoint pos = MakePoint(ev->pos.x - m->rect.ul.x, ev->pos.y - m->rect.ul.y); + short x = (MFD_VIEW_WID - res_bm_width(TRASH_BUTTON)) / 2; + short y = (MFD_VIEW_HGT - res_bm_height(TRASH_BUTTON)) / 2; + LGRect r; + r.ul = r.lr = MakePoint(x, y); + r.lr.x += res_bm_width(TRASH_BUTTON); + r.lr.y += res_bm_height(TRASH_BUTTON); + if (RECT_TEST_PT(&r, pos)) { + int i; + obj_destroy(id); + for (i = row + 1; i < NUM_GENERAL_SLOTS; i++) + player_struct.inventory[i - 1] = player_struct.inventory[i]; + player_struct.inventory[NUM_GENERAL_SLOTS - 1] = OBJ_NULL; + drain_energy(1); + mfd_notify_func(MFD_EMPTY_FUNC, MFD_ITEM_SLOT, TRUE, MFD_EMPTY, FALSE); + chg_set_flg(INVENTORY_UPDATE); + } + } + return TRUE; +} + +#define STRINGS_PER_WARE (REF_STR_wareSpew1 - REF_STR_wareSpew0) +//#define SPEW_VERT_MARGIN 10 +#define SPEW_VERT_MARGIN 2 + +void draw_mfd_item_spew(Ref id, int n) { + uchar oldwrap = mfd_string_wrap; + short w, h; + short x, y; + char buf[256]; + int i; + + gr_set_font(ResLock(MFD_FONT)); + buf[0] = '\0'; +#ifdef CONCATENATE_ITEMSPEW + for (i = 0; i < n; i++, id++) +#else + i = n - 1; + id += i; +#endif + get_string(id, buf + strlen(buf), sizeof(buf) - strlen(buf)); + gr_string_wrap(buf, MFD_VIEW_WID - 2); + gr_string_size(buf, &w, &h); + x = (MFD_VIEW_WID - w) / 2; + y = (MFD_VIEW_HGT - h - SPEW_VERT_MARGIN) / 2 + SPEW_VERT_MARGIN; + mfd_string_wrap = FALSE; + mfd_full_draw_string(buf, x, y, GREEN_YELLOW_BASE, MFD_FONT, TRUE, TRUE); + mfd_string_wrap = oldwrap; + ResUnlock(MFD_FONT); +} + +void mfd_item_expose(MFD *m, ubyte control) { + ubyte lastclass, currclass, lasttype, currtype = MFD_INV_NOTYPE; + uchar FullRedraw = FALSE; + int triple; + + currclass = MFDGetCurrItemClass(m->id); + lastclass = MFDGetLastItemClass(m->id); + + if (cat2active[currclass] != NULL_ACTIVE) + currtype = player_struct.actives[cat2active[currclass]]; + if (currtype == MFD_INV_NOTYPE) + currclass = MFD_INV_NULL; + lasttype = MFDGetLastItemType(m->id); + MFDSetCurrItemType(m->id, currtype); + + MFDSetLastItemClass(m->id, currclass); + MFDSetLastItemType(m->id, MFDGetCurrItemType(m->id)); + + if (control & MFD_EXPOSE) { + + if ((control & MFD_EXPOSE_FULL) || (currclass != lastclass) || (currtype != lasttype)) + FullRedraw = TRUE; + + // If there is no currently selected member of the current class, + // draw the blank screen thingie + if (MFDGetCurrItemType(m->id) == EMPTY_WEAPON_SLOT) { + mfd_expose_blank(m, control); + return; + } + + PUSH_CANVAS(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + mfd_clear_rects(); + + switch (currclass) { + + case MFD_INV_NULL: + + if (FullRedraw) { + draw_blank_mfd(); + } + break; + + case MFD_INV_DRUG: + + triple = get_triple_from_class_nth_item(CLASS_DRUG, currtype); + + mfd_item_micro_expose(FullRedraw, triple); + // mfd_item_micro_hires_expose(FullRedraw, triple); + if (FullRedraw) + draw_mfd_item_spew(REF_STR_drugSpew0 + currtype, 1); + draw_res_bm(REF_IMG_Apply, DRUG_BUTTON_X, DRUG_BUTTON_Y); + mfd_add_rect(DRUG_BUTTON_X, DRUG_BUTTON_Y, DRUG_BUTTON_X + DRUG_BUTTON_W, MFD_VIEW_HGT); + break; + + case MFD_INV_GRENADE: + + triple = get_triple_from_class_nth_item(CLASS_GRENADE, currtype); + mfd_item_micro_expose(FullRedraw, triple); + // mfd_item_micro_hires_expose(FullRedraw, triple); + break; + + case MFD_INV_HARDWARE: + + // if (currtype == HARDWARE_DATA_READER) { + // ss_safe_set_cliprect(0,0,MFD_VIEW_WID,MFD_VIEW_HGT); + // ss_bitmap(&mfd_background,0,0); + // mfd_draw_string("Video reader",5,10,WHITE,TRUE); + // mfd_draw_string("Text reader",5,15,WHITE,TRUE); + // mfd_draw_string("Email reader",5,20,WHITE,TRUE); + // } + // else + { + int id; + short x = HARDWARE_BUTTON_X, y = HARDWARE_BUTTON_Y; + triple = get_triple_from_class_nth_item(CLASS_HARDWARE, currtype); + + mfd_item_micro_expose(FullRedraw, triple); + // mfd_item_micro_hires_expose(FullRedraw, triple); + if (!is_passive_hardware(currtype)) { + id = (player_struct.hardwarez_status[currtype] & WARE_ON) ? REF_IMG_Active : REF_IMG_Inactive; + draw_res_bm(id, x, y); + mfd_add_rect(x, y, x + HARDWARE_BUTTON_W, MFD_VIEW_HGT); + } + if (FullRedraw) { + uchar version = player_struct.hardwarez[currtype]; + draw_mfd_item_spew(REF_STR_wareSpew0 + STRINGS_PER_WARE * currtype, version); + } + } + + break; + + case MFD_INV_AMMO: + mfd_ammo_expose(control); + break; + + case MFD_INV_SOFT_COMBAT: + case MFD_INV_SOFT_DEFENSE: + triple = get_ware_triple(currclass - MFD_INV_SOFT_COMBAT + WARE_SOFT_COMBAT, currtype); + mfd_item_micro_expose(FullRedraw, triple); + break; + case MFD_INV_SOFT_MISC: + // Monkey see, monkey do, monkey will destroy you. + { + short x = HARDWARE_BUTTON_X, y = HARDWARE_BUTTON_Y; + triple = get_ware_triple(currclass - MFD_INV_SOFT_COMBAT + WARE_SOFT_COMBAT, currtype); + + mfd_item_micro_expose(FullRedraw, triple); + if (is_oneshot_misc_software(currtype)) { + draw_res_bm(REF_IMG_Activate, x, y); + mfd_add_rect(x, y, x + HARDWARE_BUTTON_W, MFD_VIEW_HGT); + } + } + break; + case MFD_INV_GENINV: + mfd_general_inv_expose(m, control, player_struct.inventory[currtype], FullRedraw); + break; + + default: + break; + } + + POP_CANVAS(); + + mfd_update_rects(m); + } +} + +// --------------------------------------------------------------------------- +// mfd_item_handler() +// +// Handle clicks to, for now, the +- grenade set-time boxes. + +uchar mfd_item_handler(MFD *m, uiEvent *e) { + uchar retval = FALSE; + + if (!(e->mouse_data.action & MOUSE_LDOWN)) + return retval; + + switch (MFDGetCurrItemClass(m->id)) { + + case MFD_INV_GRENADE: + +#ifdef OLD_GRENADE_BUTTONS + { + int i; + LGRect r[2]; + int triple; + ubyte min, max; + triple = get_triple_from_class_nth_item(CLASS_GRENADE, MFDGetCurrItemType(m->id)); + if (!(TRIP2SC(triple) == GRENADE_SUBCLASS_TIMED)) + return retval; + + min = TimedGrenadeProps[SCTRIP(triple)].min_time_set; + max = TimedGrenadeProps[SCTRIP(triple)].max_time_set; + + RECT_OFFSETTED_RECT(&MfdGrenadeBox[0], m->rect.ul, &(r[0])); + RECT_OFFSETTED_RECT(&MfdGrenadeBox[1], m->rect.ul, &(r[1])); + + for (i = 0; i < 2; i++) { + + if (RECT_TEST_PT(&r[i], e->pos)) { + + if (i == 0) { + if (player_struct.grenades_time_setting[MFDGetCurrItemType(m->id)] > min) + player_struct.grenades_time_setting[MFDGetCurrItemType(m->id)]--; + } else if (i == 1) { + if (player_struct.grenades_time_setting[MFDGetCurrItemType(m->id)] < max) + player_struct.grenades_time_setting[MFDGetCurrItemType(m->id)]++; + } + + mfd_notify_func(MFD_ITEM_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + } + } + } +#endif // OLD_GRENADE_BUTTONS + break; + case MFD_INV_HARDWARE: { + LGRect r = {{HARDWARE_BUTTON_X, HARDWARE_BUTTON_Y}, + {HARDWARE_BUTTON_X + HARDWARE_BUTTON_W, HARDWARE_BUTTON_Y + HARDWARE_BUTTON_H}}; + RECT_OFFSETTED_RECT(&r, m->rect.ul, &r); + if (RECT_TEST_PT(&r, e->pos)) { + use_ware(WARE_HARD, player_struct.actives[ACTIVE_HARDWARE]); + mfd_notify_func(MFD_ITEM_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + retval = TRUE; + } + } break; + case MFD_INV_SOFT_MISC: { + LGRect r = {{HARDWARE_BUTTON_X, HARDWARE_BUTTON_Y}, + {HARDWARE_BUTTON_X + HARDWARE_BUTTON_W, HARDWARE_BUTTON_Y + HARDWARE_BUTTON_H}}; + RECT_OFFSETTED_RECT(&r, m->rect.ul, &r); + if (RECT_TEST_PT(&r, e->pos)) { + use_ware(WARE_SOFT_MISC, player_struct.actives[ACTIVE_MISC_SOFT]); + mfd_notify_func(MFD_ITEM_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + retval = TRUE; + } + } break; + case MFD_INV_DRUG: { + LGRect r = {{DRUG_BUTTON_X, DRUG_BUTTON_Y}, {DRUG_BUTTON_X + DRUG_BUTTON_W, DRUG_BUTTON_Y + DRUG_BUTTON_H}}; + RECT_OFFSETTED_RECT(&r, m->rect.ul, &r); + // Why is it that for wares, it's use_drug whereas for drugs it's drug_use? Perhaps we'll never know. + if (RECT_TEST_PT(&r, e->pos)) { + drug_use(player_struct.actives[ACTIVE_DRUG]); + mfd_notify_func(MFD_ITEM_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + retval = TRUE; + } + } break; + case MFD_INV_GENINV: + mfd_general_inv_handler(m, e, player_struct.actives[ACTIVE_GENERAL]); + break; + + case MFD_INV_AMMO: + mfd_ammo_handler(m, e); + break; + } + + return retval; +} + +// ---------------------- +// * ITEM MFD FOR LANTERN +// ---------------------- + +#define LANTERN_BARRAY_X 2 +#define LANTERN_BARRAY_WD (MFD_VIEW_WID - 6) +#define LANTERN_BARRAY_Y 45 + +#define LANTERN_LAST_SETTING(mfd) (player_struct.mfd_func_data[MFD_LANTERN_FUNC][mfd]) +#define LANTERN_LAST_VERSION(mfd) (player_struct.mfd_func_data[MFD_LANTERN_FUNC][NUM_MFDS + mfd]) +#define LANTERN_LAST_STATE(mfd) (player_struct.mfd_func_data[MFD_LANTERN_FUNC][2 * NUM_MFDS + mfd]) +#define LANTERN_BARRAY_IDX 0 + +extern uchar muzzle_fire_light; + +int energy_cost(int warenum); + +uchar mfd_lantern_button_handler(MFD *mfd, LGPoint bttn, uiEvent *ev, void *data) { + int n = CPTRIP(LANTERN_HARD_TRIPLE); + int s = player_struct.hardwarez_status[n]; + void mfd_lantern_setting(int setting); + + if (bttn.x >= player_struct.hardwarez[n] || !(ev->subtype & MOUSE_LDOWN)) + return FALSE; // Version too high + if (bttn.x == LAMP_SETTING(s)) { + use_ware(WARE_HARD, n); + return TRUE; + } + mfd_lantern_setting(bttn.x); + return TRUE; +} + +void mfd_lantern_setting(int setting) { + int n = CPTRIP(LANTERN_HARD_TRIPLE); + int s = player_struct.hardwarez_status[n]; + + if (s & WARE_ON) + set_player_energy_spend(player_struct.energy_spend - energy_cost(n)); + LAMP_SETTING_SET(s, setting); + player_struct.hardwarez_status[n] = s; + if (!muzzle_fire_light) { + player_struct.light_value = s; + lamp_set_vals(); + } + if (player_struct.hardwarez_status[n] & WARE_ON) + set_player_energy_spend(lg_min(MAX_ENERGY, (int)player_struct.energy_spend + energy_cost(n))); + mfd_notify_func(MFD_LANTERN_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); +} + +errtype mfd_lanternware_init(MFD_Func *f) { + int cnt = 0; + LGPoint bsize; + LGPoint bdims; + LGRect r; + errtype err; + bsize.x = res_bm_width(REF_IMG_LitLamp0); + bsize.y = res_bm_height(REF_IMG_LitLamp0); + bdims.x = LAMP_VERSIONS; + bdims.y = 1; + r.ul.x = LANTERN_BARRAY_X; + r.ul.y = LANTERN_BARRAY_Y; + r.lr.x = r.ul.x + LANTERN_BARRAY_WD; + r.lr.y = r.ul.y + bsize.y; + err = MFDBttnArrayInit(&f->handlers[cnt++], &r, bdims, bsize, mfd_lantern_button_handler, NULL); + if (err != OK) + return err; + f->handler_count = cnt; + return OK; +} + +void mfd_lanternware_expose(MFD *mfd, ubyte control) { + int n, s, v; + uchar full = control & MFD_EXPOSE_FULL; + if (control == 0) + return; + n = CPTRIP(LANTERN_HARD_TRIPLE); + s = player_struct.hardwarez_status[n]; + v = player_struct.hardwarez[n]; + PUSH_CANVAS(pmfd_canvas); + mfd_clear_rects(); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + if (!full_game_3d) + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + mfd_item_micro_expose(full, LANTERN_HARD_TRIPLE); + // mfd_item_micro_hires_expose(full,LANTERN_HARD_TRIPLE); + if (full) + draw_mfd_item_spew(REF_STR_wareSpew0 + STRINGS_PER_WARE * n, v); + if (full || LAMP_SETTING(s) != LANTERN_LAST_SETTING(mfd->id) || LANTERN_LAST_VERSION(mfd->id) != v || + LANTERN_LAST_STATE(mfd->id) != (s & WARE_ON)) { + int xjump = LANTERN_BARRAY_WD - res_bm_width(REF_IMG_LitLamp0); + int i; + for (i = 0; i < v; i++) { + int lit = i == LAMP_SETTING(s) && (s & WARE_ON); + int id = (lit) ? REF_IMG_LitLamp0 : REF_IMG_UnlitLamp0; + short x = LANTERN_BARRAY_X + i * xjump / (LAMP_VERSIONS - 1); + short y = LANTERN_BARRAY_Y; + draw_raw_resource_bm(id + i, x, LANTERN_BARRAY_Y); + if (i == LAMP_SETTING(s)) { + gr_set_fcolor(GREEN_BASE + 2); + ss_box(x - 1, y - 1, x + res_bm_width(id + i) + 1, y + res_bm_height(id + i) + 1); + } + } + mfd_add_rect(LANTERN_BARRAY_X - 2, LANTERN_BARRAY_Y - 2, LANTERN_BARRAY_X + LANTERN_BARRAY_WD + 2, + MFD_VIEW_HGT); + LANTERN_LAST_SETTING(mfd->id) = LAMP_SETTING(s); + LANTERN_LAST_VERSION(mfd->id) = v; + LANTERN_LAST_STATE(mfd->id) = s & WARE_ON; + } + POP_CANVAS(); + mfd_update_rects(mfd); +} + +// ---------------------- +// * ITEM MFD FOR SHIELD +// ---------------------- + +#define SHIELD_BARRAY_X 2 +#define SHIELD_BARRAY_WD (MFD_VIEW_WID - 4) +#define SHIELD_BARRAY_Y 45 + +#define SHIELD_LAST_STATUS(mfd) (player_struct.mfd_func_data[MFD_SHIELD_FUNC][mfd]) +#define SHIELD_LAST_VERSION(mfd) (player_struct.mfd_func_data[MFD_SHIELD_FUNC][NUM_MFDS + mfd]) +#define SHIELD_BARRAY_IDX 0 + +#define SHIELD_SETTINGS 3 + +void mfd_shield_setting(int setting); +uchar mfd_shield_handler(MFD *m, uiEvent *e); +uchar mfd_shield_button_handler(MFD *m, LGPoint bttn, uiEvent *ev, void *data); +errtype mfd_shield_init(MFD_Func *f); +void mfd_shieldware_expose(MFD *mfd, ubyte control); + +uchar mfd_shield_handler(MFD *m, uiEvent *e) { + uchar retval = FALSE; + LGPoint pos = e->pos; + ubyte n = CPTRIP(SHIELD_HARD_TRIPLE); + ubyte v = player_struct.hardwarez[n]; + if (v != SHIELD_VERSIONS) // are we at max version + return FALSE; + if (!(e->subtype & MOUSE_LDOWN)) + return FALSE; + pos.x -= m->rect.ul.x - SHIELD_BARRAY_X; + pos.y -= m->rect.ul.y - SHIELD_BARRAY_Y; + if (pos.x > 0 && pos.x < SHIELD_BARRAY_WD && pos.y > 0) { + use_ware(WARE_HARD, n); + mfd_shield_setting(v - 1); + retval = TRUE; + } + return retval; +} + +uchar mfd_shield_button_handler(MFD *mfd, LGPoint bttn, uiEvent *ev, void *data) { + int n = CPTRIP(SHIELD_HARD_TRIPLE); + int s = player_struct.hardwarez_status[n]; + + if (bttn.x >= player_struct.hardwarez[n] || !(ev->subtype & MOUSE_LDOWN)) + return FALSE; // Version too high + if (SHIELD_SETTING(s) == bttn.x) { + use_ware(WARE_HARD, n); + return TRUE; + } + mfd_shield_setting(bttn.x); + return TRUE; +} + +void mfd_shield_setting(int setting) { + int n = CPTRIP(SHIELD_HARD_TRIPLE); + int s = player_struct.hardwarez_status[n]; + + if (s & WARE_ON) + set_player_energy_spend(player_struct.energy_spend - energy_cost(n)); + SHIELD_SETTING_SET(s, setting); + player_struct.hardwarez_status[n] = s; + if (s & WARE_ON) { + shield_set_absorb(); + set_player_energy_spend(lg_min(MAX_ENERGY, (int)player_struct.energy_spend + energy_cost(n))); + } + mfd_notify_func(MFD_SHIELD_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); +} + +errtype mfd_shield_init(MFD_Func *f) { + int cnt = 0; + LGPoint bsize; + LGPoint bdims; + LGRect r; + errtype err; + bsize.x = res_bm_width(REF_IMG_LitShield0); + bsize.y = res_bm_height(REF_IMG_LitShield0); + bdims.x = SHIELD_SETTINGS; + bdims.y = 1; + r.ul.x = SHIELD_BARRAY_X; + r.ul.y = SHIELD_BARRAY_Y; + r.lr.x = r.ul.x + SHIELD_BARRAY_WD; + r.lr.y = r.ul.y + bsize.y; + err = MFDBttnArrayInit(&f->handlers[cnt++], &r, bdims, bsize, mfd_shield_button_handler, NULL); + if (err != OK) + return err; + f->handler_count = cnt; + return OK; +} + +void mfd_shieldware_expose(MFD *mfd, ubyte control) { + int n, s, v; + uchar full = control & MFD_EXPOSE_FULL; + if (control == 0) + return; + n = CPTRIP(SHIELD_HARD_TRIPLE); + s = player_struct.hardwarez_status[n]; + v = player_struct.hardwarez[n]; + PUSH_CANVAS(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + mfd_clear_rects(); + if (!full_game_3d) + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + mfd_item_micro_expose(full, SHIELD_HARD_TRIPLE); + // mfd_item_micro_hires_expose(full,SHIELD_HARD_TRIPLE); + if (full) + draw_mfd_item_spew(REF_STR_wareSpew0 + STRINGS_PER_WARE * n, v); + if (full || s != SHIELD_LAST_STATUS(mfd->id) || SHIELD_LAST_VERSION(mfd->id) != v) { + int xjump = SHIELD_BARRAY_WD / SHIELD_SETTINGS; + int i; + if (v >= SHIELD_VERSIONS) { + int id = (s & WARE_ON) ? REF_IMG_LitShieldSuper : REF_IMG_UnlitShieldSuper; + draw_raw_resource_bm(id, SHIELD_BARRAY_X + (SHIELD_BARRAY_WD - res_bm_width(id)) / 2, SHIELD_BARRAY_Y); + mfd_add_rect(SHIELD_BARRAY_X, SHIELD_BARRAY_Y, SHIELD_BARRAY_X + SHIELD_BARRAY_WD, MFD_VIEW_WID); + } else + for (i = 0; i < v; i++) { + int id = (i == SHIELD_SETTING(s) && (s & WARE_ON)) ? REF_IMG_LitShield0 : REF_IMG_UnlitShield0; + grs_bitmap *bm = lock_bitmap_from_ref(id + i); + short x = SHIELD_BARRAY_X + i * xjump; + short y = SHIELD_BARRAY_Y; + mfd_draw_bitmap(bm, x, y); + if (i == SHIELD_SETTING(s)) { + gr_set_fcolor(GREEN_BASE + 2); + ss_box(x - 1, y - 1, x + bm->w + 1, y + bm->h + 1); + } + mfd_add_rect(x - 2, y - 2, x + bm->w + 2, y + bm->h + 2); + RefUnlock(id + i); + } + SHIELD_LAST_STATUS(mfd->id) = s; + SHIELD_LAST_VERSION(mfd->id) = v; + } + POP_CANVAS(); + mfd_update_rects(mfd); +} + +// ---------------------- +// * ITEM MFD FOR MOTION WARE +// ---------------------- + +#define MOTION_BARRAY_WD (3 * MFD_VIEW_WID / 4) +#define MOTION_BARRAY_X ((MFD_VIEW_WID - MOTION_BARRAY_WD) / 2) +#define MOTION_BARRAY_Y 48 + +#define MOTION_LAST_STATUS(mfd) (player_struct.mfd_func_data[MFD_MOTION_FUNC][mfd]) +#define MOTION_LAST_VERSION(mfd) (player_struct.mfd_func_data[MFD_MOTION_FUNC][NUM_MFDS + mfd]) +#define MOTION_BARRAY_IDX 0 + +#define MOTION_SETTING LAMP_SETTING +#define MOTION_SETTING_SET LAMP_SETTING_SET + +#define MOTION_BUTTONS 2 + +uchar mfd_motion_button_handler(MFD *m, LGPoint bttn, uiEvent *ev, void *data); +errtype mfd_motion_init(MFD_Func *f); +void mfd_motionware_expose(MFD *mfd, ubyte control); + +uchar mfd_motion_button_handler(MFD *mfd, LGPoint bttn, uiEvent *ev, void *data) { + int n = CPTRIP(MOTION_HARD_TRIPLE); + int s = player_struct.hardwarez_status[n]; + // if (bttn.x >= player_struct.hardwarez[n] || + if (!(ev->subtype & MOUSE_LDOWN)) + return FALSE; + if (MOTION_SETTING(s) == bttn.x) { + use_ware(WARE_HARD, n); + return TRUE; + } + if (s & WARE_ON) + set_player_energy_spend(player_struct.energy_spend - energy_cost(n)); + MOTION_SETTING_SET(s, bttn.x); + player_struct.hardwarez_status[n] = s; + if (!(s & WARE_ON)) { + // use_ware(WARE_HARD,n); + } else { + motionware_mode = bttn.x + 1; + set_player_energy_spend(lg_min(MAX_ENERGY, (int)player_struct.energy_spend + energy_cost(n))); + } + mfd_notify_func(MFD_MOTION_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + return TRUE; +} + +errtype mfd_motion_init(MFD_Func *f) { + int cnt = 0; + LGPoint bsize; + LGPoint bdims; + LGRect r; + errtype err; + bsize.x = res_bm_width(REF_IMG_LitMotion0); + bsize.y = res_bm_height(REF_IMG_LitMotion0); + bdims.x = MOTION_BUTTONS; + bdims.y = 1; + r.ul.x = MOTION_BARRAY_X; + r.ul.y = MOTION_BARRAY_Y; + r.lr.x = r.ul.x + MOTION_BARRAY_WD; + r.lr.y = r.ul.y + bsize.y; + err = MFDBttnArrayInit(&f->handlers[cnt++], &r, bdims, bsize, mfd_motion_button_handler, NULL); + if (err != OK) + return err; + f->handler_count = cnt; + return OK; +} + +void mfd_motionware_expose(MFD *mfd, ubyte control) { + int n, s, v; + uchar full = control & MFD_EXPOSE_FULL; + if (control == 0) + return; + n = CPTRIP(MOTION_HARD_TRIPLE); + s = player_struct.hardwarez_status[n]; + v = player_struct.hardwarez[n]; + PUSH_CANVAS(pmfd_canvas); + mfd_clear_rects(); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + mfd_item_micro_expose(full, MOTION_HARD_TRIPLE); + // mfd_item_micro_hires_expose(full,MOTION_HARD_TRIPLE); + if (full) + draw_mfd_item_spew(REF_STR_wareSpew0 + STRINGS_PER_WARE * n, v); + if (full || s != MOTION_LAST_STATUS(mfd->id) || MOTION_LAST_VERSION(mfd->id) != v) { + int xjump = MOTION_BARRAY_WD / MOTION_BUTTONS; + int i; + for (i = 0; i < lg_min(v, MOTION_BUTTONS); i++) { + int id = ((i == MOTION_SETTING(s) && (s & WARE_ON))) ? REF_IMG_LitMotion0 : REF_IMG_UnlitMotion0; + grs_bitmap *bm = lock_bitmap_from_ref(id + i); + short x = MOTION_BARRAY_X + i * xjump; + short y = MOTION_BARRAY_Y; + mfd_draw_bitmap(bm, x, y); + if (i == MOTION_SETTING(s)) { + gr_set_fcolor(GREEN_BASE + 2); + ss_box(x - 1, y - 1, x + bm->w + 1, y + bm->h + 1); + } + mfd_add_rect(x - 2, y - 2, x + bm->w + 2, y + bm->h + 2); + RefUnlock(id + i); + } + MOTION_LAST_STATUS(mfd->id) = s; + MOTION_LAST_VERSION(mfd->id) = v; + } + POP_CANVAS(); + mfd_update_rects(mfd); +} + +// ----------------- +// TIMED GRENADE MFD +// ----------------- + +#define MFD_GRENADE_FUNC 10 + +#define GRENADE_TIME_UNIT 10 // fraction of second for each time second unit + +#define GRENADE_SLIDER_X MFD_BEAM_RECT_X1 +#define GRENADE_SLIDER_Y 51 +#define GRENADE_SLIDER_W 70 +#define GRENADE_SLIDER_H 5 + +#define GRENADE_SLIDER_IDX 0 + +#define GRENADE_MOUSE_CONSTRAINED (player_struct.mfd_func_data[MFD_GRENADE_FUNC][7]) + +#define GRENADE_HIRES_CUTOFF 100 + +uchar mfd_grenade_slider_handler(MFD *m, short val, uiEvent *ev, void *data); +uchar mfd_grenade_handler(MFD *m, uiEvent *ev); +errtype mfd_grenade_init(MFD_Func *f); +void mfd_grenade_expose(MFD *mfd, ubyte control); + +uchar mfd_grenade_slider_handler(MFD *m, short val, uiEvent *ev, void *data) { + int n = player_struct.actives[ACTIVE_GRENADE]; + int triple = nth_after_triple(MAKETRIP(CLASS_GRENADE, 0, 0), n); + short min = TimedGrenadeProps[SCTRIP(triple)].min_time_set * GRENADE_TIME_UNIT; + short max = TimedGrenadeProps[SCTRIP(triple)].max_time_set * GRENADE_TIME_UNIT; + short width = GRENADE_SLIDER_W; + short setting; + + if (max > 2 * GRENADE_HIRES_CUTOFF) { + width /= 2; + if (val >= GRENADE_SLIDER_W / 2) { + val -= GRENADE_SLIDER_W / 2; + min = GRENADE_HIRES_CUTOFF; + } else + max = GRENADE_HIRES_CUTOFF; + } + setting = val * (max - min) / width + min; + player_struct.grenades_time_setting[n] = setting; + if (ev->subtype & MOUSE_LDOWN) { + LGRect r = mfd_funcs[MFD_GRENADE_FUNC].handlers[GRENADE_SLIDER_IDX].r; + int my = ev->pos.y; + RECT_OFFSETTED_RECT(&r, m->rect.ul, &r); + slider_cursor.hotspot.x = slider_cursor_bmap.w / 2; + slider_cursor.hotspot.y = (slider_cursor_bmap.h / 2) + my - (r.ul.y + r.lr.y) / 2; + ui_mouse_constrain_xy(r.ul.x, my, r.lr.x - 2, my); + + GRENADE_MOUSE_CONSTRAINED = m->id + 1; + // Get our funky mfd-beam-phaser-setting cursor +#ifdef CURSOR_BACKUPS + backup[20] = (uchar *)malloc(f->bm.w * f->bm.h); + LG_memcpy(backup[20], f->bm.bits, f->bm.w * f->bm.h); + gr_init_bm(&backup_mfd_cursor, backup[14], BMT_FLAT8, 0, mfd_cursor.w, mfd_cursor.h); +#endif + uiPushRegionCursor(MFD_REGION(m), &slider_cursor); + } + if ((ev->mouse_data.buttons & (1 << MOUSE_LBUTTON)) == 0) { + uiCursorStack *cs; + uiGetRegionCursorStack(MFD_REGION(m), &cs); + uiPopCursorEvery(cs, &slider_cursor); + + if (GRENADE_MOUSE_CONSTRAINED) { + mouse_unconstrain(); + GRENADE_MOUSE_CONSTRAINED = 0; + mfd_notify_func(MFD_GRENADE_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, TRUE); + } + } + mfd_notify_func(MFD_GRENADE_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + return TRUE; +} + +uchar mfd_grenade_handler(MFD *m, uiEvent *ev) { + LGRect r = mfd_funcs[MFD_GRENADE_FUNC].handlers[GRENADE_SLIDER_IDX].r; + RECT_MOVE(&r, m->rect.ul); + if (!RECT_TEST_PT(&r, ev->pos)) { + uiCursorStack *cs; + uiGetRegionCursorStack(MFD_REGION(m), &cs); + uiPopCursorEvery(cs, &slider_cursor); + mfd_notify_func(MFD_GRENADE_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + } + return FALSE; +} + +errtype mfd_grenade_init(MFD_Func *f) { + int cnt = 0; + errtype err; + LGRect r = {{GRENADE_SLIDER_X, GRENADE_SLIDER_Y}, + {GRENADE_SLIDER_X + GRENADE_SLIDER_W, GRENADE_SLIDER_Y + GRENADE_SLIDER_H}}; + err = MFDSliderInit(&f->handlers[cnt++], &r, mfd_grenade_slider_handler, NULL); + if (err != OK) + return err; + f->handler_count = cnt; +#ifdef CURSOR_BACKUPS + backup[21] = (uchar *)Malloc(slider_bmap.w * slider_bmap.h); + LG_memcpy(backup[21], slider_bmap.bits, slider_bmap.w * slider_bmap.h); + gr_init_bm(&backup_slider_cursor, backup[21], BMT_FLAT8, 0, slider_bmap.w, slider_bmap.h); +#endif + return OK; +} + +#define LAST_GRENADE(mfd) (player_struct.mfd_func_data[MFD_GRENADE_FUNC][mfd]) +#define LAST_GRENADE_SETTING(mfd) (player_struct.mfd_func_data[MFD_GRENADE_FUNC][mfd + 2]) + +#define GRENADE_SLIDER_BORDER MFD_BEAMWPN_STAT_BORDER +#define GRENADE_SLIDER_SETTING_COLOR MFD_BEAMWPN_STAT_CHARGE + +#define TIME_TEXT_Y (GRENADE_SLIDER_Y - 8) +#define TIME_TEXT_LEN 128 + +void mfd_grenade_expose(MFD *mfd, ubyte control) { + MFD_Func *f = &mfd_funcs[MFD_GRENADE_FUNC]; + int n = player_struct.actives[ACTIVE_GRENADE]; + int triple = nth_after_triple(MAKETRIP(CLASS_GRENADE, 0, 0), n); + uchar full = (control & MFD_EXPOSE_FULL) || (n != LAST_GRENADE(mfd->id)); + short setting = player_struct.grenades_time_setting[n]; + + if (control == 0) { + + uiCursorStack *cs; + uiGetRegionCursorStack(MFD_REGION(mfd), &cs); + uiPopCursorEvery(cs, &slider_cursor); + + if (GRENADE_MOUSE_CONSTRAINED == mfd->id + 1) { + mouse_unconstrain(); + GRENADE_MOUSE_CONSTRAINED = 0; + } + return; + } + mfd_clear_rects(); + PUSH_CANVAS(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + if (!full_game_3d) + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + mfd_item_micro_expose(full, triple); + // mfd_item_micro_hires_expose(full,triple); + LAST_GRENADE(mfd->id) = n; + if (full || LAST_GRENADE_SETTING(mfd->id) != setting) { + char buf[TIME_TEXT_LEN]; + LGRect *r = &f->handlers[GRENADE_SLIDER_IDX].r; + short min = TimedGrenadeProps[SCTRIP(triple)].min_time_set * GRENADE_TIME_UNIT; + short max = TimedGrenadeProps[SCTRIP(triple)].max_time_set * GRENADE_TIME_UNIT; + short width = GRENADE_SLIDER_W; + short base = 0; + short x; + if (max > 2 * GRENADE_HIRES_CUTOFF) { + width /= 2; + if (setting < GRENADE_HIRES_CUTOFF) + max = GRENADE_HIRES_CUTOFF; + else { + base = GRENADE_SLIDER_W / 2; + min = GRENADE_HIRES_CUTOFF; + } + } + x = (setting - min) * width / (max - min) + base; + gr_set_fcolor(GRENADE_SLIDER_BORDER); + ss_box(r->ul.x - 1, r->ul.y - 1, r->lr.x, r->lr.y); + mfd_add_rect(r->ul.x - 1, r->ul.y - 1, r->lr.x, r->lr.y + 1); + gr_set_fcolor(GRENADE_SLIDER_SETTING_COLOR); + if (!GRENADE_MOUSE_CONSTRAINED) + draw_raw_resource_bm(REF_IMG_BeamSetting, r->ul.x + x - 3, r->ul.y - 1); + + + sprintf(buf, "%s %d.%d", get_string(REF_STR_TimeSetting, NULL, TIME_TEXT_LEN), setting / 10, setting % 10); + //numtostring(setting / 10, buf + strlen(buf)); // itoa(setting/10,buf+strlen(buf),10); + //strcat(buf, "."); + //numtostring(setting % 10, buf + strlen(buf)); // itoa(setting%10,buf+strlen(buf),10); + { + LGRect r = {{GRENADE_SLIDER_X, TIME_TEXT_Y}, {MFD_VIEW_WID, GRENADE_SLIDER_Y - 1}}; + mfd_partial_clear(&r); + } + mfd_draw_string(buf, GRENADE_SLIDER_X, TIME_TEXT_Y, GRENADE_SLIDER_BORDER, TRUE); + LAST_GRENADE_SETTING(mfd->id) = setting; + } + POP_CANVAS(); + mfd_update_rects(mfd); +} + +// ------------------ +// * THE BIO WARE MFD +// ------------------ + +#define BIO_TEXT_X 29 + +// --------------------------------------------------------------------------- +// mfd_bioware_expose() +// +// This is the bioware, activated in the info window. It displays stats. +// As of now, it's pretty sketchy. + +#define LAST_HP(mfd) (mfd_fdata[MFD_BIOWARE_FUNC][4 * mfd]) +#define LAST_FATIGUE(mfd) (mfd_fdata[MFD_BIOWARE_FUNC][1 + 4 * mfd]) +#define LAST_DRUGBITS(mfd) (*(ushort *)&mfd_fdata[MFD_BIOWARE_FUNC][2 + 4 * mfd]) + +// this is stolen from gamesys.c +#define MAX_FATIGUE 10000 +#define MAX_HP UCHAR_MAX + +#define BIO_DRUG_UP 1 // experincing normal effects +#define BIO_DRUG_DOWN 2 // experiencing after effects. +#define BIO_DRUG_CLEAN 0 + +#define BITS_PER_DRUG 2 + +void mfd_bioware_expose(MFD *m, ubyte control) { + uchar full = control & MFD_EXPOSE_FULL; + int i, y = 2, triple; + char buf2[12]; + LGRect r; + + r.ul.x = BIO_TEXT_X; + r.ul.y = 20; + r.lr.x = MFD_VIEW_WID; + r.lr.y = MFD_VIEW_HGT; + + // turn off the bioware if there's no exposed mfd. + { + extern WARE HardWare[NUM_HARDWAREZ]; + uchar on = full || control & MFD_EXPOSE; + for (i = 0; i < NUM_MFDS; i++) { + ubyte slot = player_struct.mfd_current_slots[i]; + ubyte func = mfd_get_func(i, slot); + if (func == MFD_BIOWARE_FUNC && (control != 0 || m->id != i)) + on = TRUE; + } + if (on == !(player_struct.hardwarez_status[HARDWARE_BIOWARE] & WARE_ON)) { + use_ware(WARE_HARD, HARDWARE_BIOWARE); + } + } + + if (control & MFD_EXPOSE) { + char *s; + char pct[] = "%"; + short x; + ubyte v = player_struct.hardwarez[HARDWARE_BIOWARE]; + int ref = MKREF(RES_mfdArtOverlays, MFD_ART_HUMAN); + uchar stam = player_struct.drug_status[CPTRIP(STAMINA_DRUG_TRIPLE)] > 0; + + PUSH_CANVAS(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + mfd_clear_rects(); + if (!full_game_3d) + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + gr_set_font(ResLock(MFD_FONT)); + + if (full) { + draw_res_bm(ref, 0, 0); + mfd_add_rect(0, 0, res_bm_width(ref), res_bm_height(ref)); + } +#ifdef BIOWARE_TITLE + // Title + if (full) { + s = get_temp_string(REF_STR_BiowareTitle); + mfd_draw_string(s, BIO_TEXT_X, y, GREEN_YELLOW_BASE, TRUE); + } + y += Y_STEP; +#endif + + // Health + if (full || (LAST_HP(m->id) != player_struct.hit_points)) { + uint hp; + LGRect rest; + s = get_temp_string(REF_STR_BiowareHealth); + x = BIO_TEXT_X + gr_string_width(s); + if (full) + mfd_draw_string(s, BIO_TEXT_X, y, GREEN_YELLOW_BASE, TRUE); + hp = (100 * player_struct.hit_points + (MAX_HP / 2)) / MAX_HP; + if (hp == 0 && player_struct.hit_points > 0) + hp = 1; + sprintf(buf2, "%d", hp); + + // hack to save space in display. Use "I" for "1", knowing + // that "I" is narrower. + string_replace_char(buf2, '1', 'I'); + + mfd_draw_string(buf2, x, y, GREEN_YELLOW_BASE, TRUE); + x += gr_string_width(buf2); + mfd_draw_string(pct, x, y, GREEN_YELLOW_BASE, TRUE); + x += gr_string_width(pct); + rest.ul.x = x; + rest.ul.y = y; + rest.lr.x = MFD_VIEW_WID; + rest.lr.y = y + Y_STEP; + mfd_partial_clear(&rest); + LAST_HP(m->id) = player_struct.hit_points; + } + y += Y_STEP; + + // Fatigue + if (full || stam || (LAST_FATIGUE(m->id) != 100 * (uint)player_struct.fatigue / MAX_FATIGUE)) { + LGRect rest; + ubyte f = 100 * (uint)player_struct.fatigue / MAX_FATIGUE; + s = get_temp_string(REF_STR_BiowareFatigue); + x = BIO_TEXT_X + gr_string_width(s); + if (full) + mfd_draw_string(s, BIO_TEXT_X, y, GREEN_YELLOW_BASE, TRUE); + if (stam) { + strcpy(buf2, "--"); + mfd_draw_string(buf2, x, y, GREEN_YELLOW_BASE, TRUE); + x += gr_string_width(buf2); + } else { + sprintf(buf2, "%d", f); // itoa(f,buf2,10); + + // hack to save space in display. Use "I" for "1", knowing + // that "I" is narrower. + string_replace_char(buf2, '1', 'I'); + mfd_draw_string(buf2, x, y, GREEN_YELLOW_BASE, TRUE); + x += gr_string_width(buf2); + mfd_draw_string(pct, x, y, GREEN_YELLOW_BASE, TRUE); + x += gr_string_width(pct); + } + + rest.ul.x = x; + rest.ul.y = y; + rest.lr.x = MFD_VIEW_WID; + rest.lr.y = y + Y_STEP; + mfd_partial_clear(&rest); + LAST_FATIGUE(m->id) = f; + } + y += Y_STEP; + + if (v > 1) { + ushort drugbits = 0; + for (i = 0; i < NUM_DRUGS; i++) { + if (player_struct.drug_status[i] > 0) + drugbits |= (BIO_DRUG_UP << (i * BITS_PER_DRUG)); + if (player_struct.drug_status[i] < 0) + drugbits |= (BIO_DRUG_DOWN << (i * BITS_PER_DRUG)); + } + + if (full || drugbits != LAST_DRUGBITS(m->id)) { + short savey = y; + LAST_DRUGBITS(m->id) = drugbits; + for (i = 0; i < NUM_DRUGS; i++, drugbits >>= BITS_PER_DRUG) { + ushort drugged = drugbits & ((1 << BITS_PER_DRUG) - 1); + if (drugged != BIO_DRUG_CLEAN) { + ubyte color = (drugged == BIO_DRUG_UP) ? GREEN_BASE + 3 : GOOD_RED; + triple = drug2triple(i); + get_object_short_name(triple, buf2, sizeof(buf2)); + mfd_draw_string(buf2, BIO_TEXT_X, y, color, TRUE); + y += Y_STEP; + } + } + mfd_add_rect(BIO_TEXT_X, savey, MFD_VIEW_WID, savey + Y_STEP * NUM_DRUGS); + } + } + if (full) + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + ResUnlock(MFD_FONT); + + POP_CANVAS(); + mfd_update_rects(m); + } + + return; +} + +// ------------------- +// * THE ANIMATION MFD +// ------------------- + +// --------------------------------------------------------------------------- +// mfd_anim_init() +// +// Open the space station resource file for animation + +errtype mfd_anim_init() { +#ifdef USING_DORKY_BROKEN_ANIM + if (ResOpenFile("space4.res") < 0) + critical_error(CRITERR_RES | 5); +#endif + + return OK; +} + +// --------------------------------------------------------------------------- +// mfd_anim_expose() +// +// Strictly temporary code. Starts or stops the space station animation. + +void mfd_anim_expose(MFD *m, ubyte control) { +#ifndef NO_DUMMIES + MFD *dummy; + ubyte dummy2; + dummy = m; + dummy2 = control; +#endif +#ifdef USING_DORKY_BROKEN_ANIM + static uchar AnimOn[2]; + static ActAnim *anim[2]; + + if (control & MFD_EXPOSE) { + + gr_set_fcolor((long)BLACK); + ss_rect(m->rect.ul.x, m->rect.ul.y, m->rect.lr.x, m->rect.lr.y); + + anim[m->id] = AnimPlayRegion(REF_ANIM_space4, &(m->reg), m->rect.ul, 0); + chg_set_sta(ANIM_UPDATE); + AnimOn[m->id] = TRUE; + } else { + + AnimKill((anim[m->id])); + AnimOn[m->id] = FALSE; + if ((AnimOn[0] == FALSE) && (AnimOn[1] == FALSE)) + chg_unset_sta(ANIM_UPDATE); + } +#endif + return; +} + +// SHODAN!! +// Note this expects all appropriate 2d preparation to already be done!! + +errtype draw_shodan_influence(MFD *mfd, uchar amt); + +errtype draw_shodan_influence(MFD *mfd, uchar amt) { + char *s = get_temp_string(SHODAN_FAILURE_STRING); + + amt = lg_min(NUM_SHODAN_MUGS - 1, amt >> SHODAN_INTERVAL_SHIFT); + draw_raw_res_bm_temp(REF_IMG_EmailMugShotBase + FIRST_SHODAN_MUG + amt, 0, 0); + + gr_set_font(ResLock(MFD_FONT)); + gr_string_wrap(s, MFD_VIEW_WID); + mfd_draw_string(s, 2, 2, SHODAN_COLOR, TRUE); + ResUnlock(MFD_FONT); + gr_font_string_unwrap(s); + + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + return (OK); +} + +// ELEVATOR PANEL MFD +// ------------------ + +// NOMENCLATURE: "level" refers to the internal, unique, game-system +// number for a level. "floor" refers to the in-game floor number for +// a floor. + +#define NUM_ELEV_LVLS 15 +#define NUM_ELEVATOR_BUTTONS 12 +#define ELEV_BTTN_ROWS 4 +#define ELEV_BTTN_COLS ((NUM_ELEVATOR_BUTTONS + ELEV_BTTN_ROWS - 1) / ELEV_BTTN_ROWS) +#define ELEV_BTTNS_X 11 +#define ELEV_BTTNS_WD (MFD_VIEW_WID - 20) +#define ELEV_BTTNS_Y 21 +#define ELEV_BTTNS_HT (MFD_VIEW_HGT - ELEV_BTTNS_Y - 2) +#define ELEV_BTTN_HT 8 +#define ELEV_BTTN_WD 11 +#define ELEV_STATUS_Y 3 +#define ELEV_STATUS_X 50 + +#define ELEV_STATUS_FONT RES_mediumLEDFont +#define ELEV_STATUS_COLOR (GOOD_RED) + +typedef struct _elev_data { + ushort shownlvls; // level shown on the button panel (bitmask) + ushort reachlvls; // level actually reachable (bitmask) + struct _mfd_specific { + ubyte currlev : 4; // current level shown + ubyte selected : 4; // currently selected button + } stat, mfd_last[NUM_MFDS]; +} elev_data_type; + +uchar curr_elev_special = 0; + +errtype mfd_elevator_setlev(MFD *mfd, short lev, elev_data_type *elev_data); +uchar mfd_elevator_button_handler(MFD *mfd, LGPoint bttn, uiEvent *ev, void *data); +errtype mfd_elevator_init(MFD_Func *f); + +errtype mfd_elevator_setlev(MFD *mfd, short lev, elev_data_type *elev_data) { + elev_data->stat.currlev = lev; + mfd_notify_func(MFD_ELEV_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, TRUE); + mfd_update_current_slot(mfd->id, MFD_CHANGEBIT_FULL, 0); + return (OK); +} + +uchar mfd_elevator_button_handler(MFD *mfd, LGPoint bttn, uiEvent *ev, void *data) { + elev_data_type *elev_data = (elev_data_type *)&player_struct.mfd_func_data[MFD_ELEV_FUNC][0]; + int b = bttn.x * ELEV_BTTN_ROWS + bttn.y; + int c; + ubyte l; + ubyte reachl; + ushort bit; + + if (!(ev->mouse_data.action & MOUSE_LDOWN)) + return FALSE; + + // If SHODAN has defeated us, indicate this for our expose func + if (curr_elev_special) { + elev_data->mfd_last[mfd->id].currlev = 0xF; + mfd_notify_func(MFD_ELEV_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, TRUE); + mfd_update_current_slot(mfd->id, MFD_CHANGEBIT_FULL, 0); + return (OK); + } + + for (c = 0, reachl = 0, l = 0, bit = 1; l < NUM_ELEV_LVLS; l++, bit = bit << 1) { + if (elev_data->reachlvls & bit) + reachl++; + if (elev_data->shownlvls & bit) { + c++; + if (c > b) + break; + } + } + if (l >= NUM_ELEV_LVLS) + return TRUE; + elev_data->stat.selected = b; +#ifdef PLAYTEST + mprintf("Pushing button %d\n", b); +#endif + if (!(bit & elev_data->reachlvls)) { + string_message_info(REF_STR_ElevatorNoMove); +#ifdef PLAYTEST + mprintf("Can't get to that level\n"); +#endif + } else { + if (me_bits_music(MAP_GET_XY(PLAYER_BIN_X, PLAYER_BIN_Y)) != ELEVATOR_ZONE) + string_message_info(REF_STR_UseTooFar); + else { + int oldlev; + oldlev = elev_data->stat.currlev; + mfd_elevator_setlev(mfd, l, elev_data); + if (!elevator_use(l, reachl - 1)) + mfd_elevator_setlev(mfd, oldlev, elev_data); + } + } + return TRUE; +} + +#define TEST_ELEVPANEL + +errtype mfd_elevator_init(MFD_Func *f) { + int cnt = 0; + errtype err; + LGPoint bsize = {ELEV_BTTN_WD, ELEV_BTTN_HT}; + LGPoint bdims = {ELEV_BTTN_COLS, ELEV_BTTN_ROWS}; + LGRect r = {{ELEV_BTTNS_X, ELEV_BTTNS_Y}, {ELEV_BTTNS_X + ELEV_BTTNS_WD, ELEV_BTTNS_Y + ELEV_BTTNS_HT}}; + err = MFDBttnArrayInit(&f->handlers[cnt++], &r, bdims, bsize, mfd_elevator_button_handler, NULL); + if (err != OK) + return err; + f->handler_count = cnt; +#ifdef TEST_ELEVPANEL + { + elev_data_type *elev_data = (elev_data_type *)&player_struct.mfd_func_data[MFD_ELEV_FUNC][0]; + elev_data->shownlvls = 0xFFF; + elev_data->reachlvls = 0xF0F; + } +#endif + return OK; +} + +// Does every thing but set the slot.. +void mfd_setup_elevator(ushort levmask, ushort reachmask, ushort curlevel, uchar special) { + elev_data_type *elev_data = (elev_data_type *)&player_struct.mfd_func_data[MFD_ELEV_FUNC][0]; + elev_data->shownlvls = levmask; + elev_data->reachlvls = reachmask; + elev_data->stat.currlev = curlevel; + elev_data->stat.selected = 0xF; + curr_elev_special = special; + mfd_notify_func(MFD_ELEV_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, TRUE); +} + +char *level_to_floor(int lev_num, char *buf) { + int bpos = 0; + char grov_init = toupper(get_temp_string(REF_STR_GroveWord)[0]); + + switch (lev_num) { + case 0: + buf[bpos++] = toupper(get_temp_string(REF_STR_ReactorWord)[0]); + break; + case 11: + buf[bpos++] = grov_init; + buf[bpos++] = '4'; + break; + case 12: + buf[bpos++] = grov_init; + buf[bpos++] = '1'; + break; + case 13: + buf[bpos++] = grov_init; + buf[bpos++] = '2'; + break; + default: + if (lev_num >= 10) { + buf[bpos++] = '0' + ((lev_num / 10) % 10); + buf[bpos++] = '0' + (lev_num % 10); + } else + buf[bpos++] = '0' + lev_num; + break; + } + if (bpos != 0) + buf[bpos] = '\0'; // add trailing stop + return (buf); +} + +#define NUMBER_BUFSZ 4 // don't forget the terminator! + +// if we null your panel_ref, return the value +// before we nulled it. Otherwise, return NULL. +// +ObjID panel_ref_unexpose(int mfdid, int func) { + uchar found = FALSE; + int id = NUM_MFDS; + ObjID pr = player_struct.panel_ref; + + while (mfd_yield_func(func, &id)) + if (id != mfdid) + return OBJ_NULL; + else + found = TRUE; + + if (found) + check_panel_ref(TRUE); + else + player_struct.panel_ref = OBJ_NULL; + return pr; +} + +void mfd_elevator_expose(MFD *mfd, ubyte control) { + elev_data_type *elev_data = (elev_data_type *)&player_struct.mfd_func_data[MFD_ELEV_FUNC][0]; + char buf[NUMBER_BUFSZ]; + uchar full = control & MFD_EXPOSE_FULL; + if (control == 0) { + panel_ref_unexpose(mfd->id, MFD_ELEV_FUNC); + return; + } + mfd_clear_rects(); + PUSH_CANVAS(pmfd_canvas); + if (full) + mfd_clear_view(); + if (curr_elev_special == 0) + elev_data->mfd_last[mfd->id].currlev = 0; + if (elev_data->mfd_last[mfd->id].currlev == 0xF) + draw_shodan_influence(mfd, curr_elev_special); + else { + if (full || elev_data->mfd_last[mfd->id].currlev != elev_data->stat.currlev) { + short w, h; + int lev = elev_data->stat.currlev; + gr_set_font(ResLock(ELEV_STATUS_FONT)); + level_to_floor(lev, buf); + gr_string_size(buf, &w, &h); + mfd_draw_font_string(buf, ELEV_STATUS_X - w, ELEV_STATUS_Y, ELEV_STATUS_COLOR, ELEV_STATUS_FONT, TRUE); + elev_data->mfd_last[mfd->id].currlev = lev; + ResUnlock(ELEV_STATUS_FONT); + } + if (full || elev_data->mfd_last[mfd->id].selected != elev_data->stat.selected) { + int i; + int l; + short w, h; + // LGPoint bstep = { ELEV_BTTNS_WD/ELEV_BTTN_COLS, + // ELEV_BTTNS_HT/ELEV_BTTN_ROWS }; + gr_set_font(ResLock(MFD_FONT)); + for (i = 0, l = 0; i < NUM_ELEVATOR_BUTTONS; i++) { + ubyte clr; + LGPoint bttn; + + bttn.x = ELEV_BTTNS_X + (i / ELEV_BTTN_ROWS) * (ELEV_BTTNS_WD - ELEV_BTTN_WD) / (ELEV_BTTN_COLS - 1); + bttn.y = ELEV_BTTNS_Y + (i % ELEV_BTTN_ROWS) * (ELEV_BTTNS_HT - ELEV_BTTN_HT) / (ELEV_BTTN_ROWS - 1); + + if (i > 0) + l++; + for (; l < NUM_ELEV_LVLS; l++) + if ((1 << l) & elev_data->shownlvls) + break; + if (l >= NUM_ELEV_LVLS) + break; + if (!(elev_data->reachlvls & (1 << l))) + clr = UNAVAILABLE_ITEM_COLOR; + else if (i == elev_data->stat.selected) + clr = SELECTED_ITEM_COLOR; + else + clr = ITEM_COLOR; + gr_set_fcolor((long)clr); + ss_box(bttn.x, bttn.y, bttn.x + ELEV_BTTN_WD, bttn.y + ELEV_BTTN_HT); + gr_set_fcolor((long)ITEM_COLOR + 2); + ss_box(bttn.x - 1, bttn.y - 1, bttn.x + ELEV_BTTN_WD + 1, bttn.y + ELEV_BTTN_HT + 1); + level_to_floor(l, buf); + gr_string_size(buf, &w, &h); + mfd_draw_string(buf, bttn.x + (ELEV_BTTN_WD - w) / 2 + 1, bttn.y + 1, clr, TRUE); + } + elev_data->mfd_last[mfd->id].selected = elev_data->stat.selected; + mfd_add_rect(ELEV_BTTNS_X, ELEV_BTTNS_Y, ELEV_BTTNS_X + ELEV_BTTNS_WD, ELEV_BTTNS_Y + ELEV_BTTNS_HT); + ResUnlock(MFD_FONT); + } + } + POP_CANVAS(); + mfd_update_rects(mfd); +} + +// ------------------ +// KEYPAD MFD +// ------------------ + +#define NUM_KEYPAD_BUTTONS 12 +#define KEYPAD_BTTN_ROWS 4 +#define KEYPAD_BTTN_COLS 3 +#define KEYPAD_X_MARGIN 0 +#define KEYPAD_Y_MARGIN 2 +#define KEYPAD_BTTNS_X 19 +#define KEYPAD_BTTNS_WD (MFD_VIEW_WID - (2 * KEYPAD_BTTNS_X) - 1 - KEYPAD_X_MARGIN) +//#define KEYPAD_BTTNS_WD (MFD_VIEW_WID - (3* KEYPAD_BTTNS_X)) +#define KEYPAD_BTTNS_Y 20 +#define KEYPAD_BTTNS_HT (MFD_VIEW_HGT - KEYPAD_BTTNS_Y - KEYPAD_Y_MARGIN - 1) +#define KEYPAD_BTTN_HT 8 +#define KEYPAD_BTTN_WD 11 +#define KEYPAD_STATUS_Y 3 +#define KEYPAD_STATUS_X 60 + +#define MAX_KEYPAD_DIGITS 3 + +#define KEYPAD_STATUS_FONT RES_mediumLEDFont +#define KEYPAD_STATUS_COLOR (GOOD_RED) + +bool gKeypadOverride = false; // When this is true, don't move the player. + +typedef struct _keypad_data { + uchar curr_digit; + uchar last_digit; + uchar digits[MAX_KEYPAD_DIGITS]; + uchar special; +} keypad_data_type; + +uchar keypad_num(int b); +char *keypad_name(int b, char *buf); +char *mfd_keypad_assemble(keypad_data_type *keypad_data, char *buf); +errtype mfd_keypad_input(MFD *m, char b_num); +uchar keypad_hotkey_func(ushort keycode, uint32_t context, intptr_t data); +uchar mfd_keypad_handler(MFD *m, uiEvent *ev); +uchar mfd_keypad_button_handler(MFD *mfd, LGPoint bttn, uiEvent *ev, void *data); +errtype mfd_keypad_init(MFD_Func *f); +void mfd_keypad_expose(MFD *mfd, ubyte control); + +uchar keypad_num(int b) { +#ifdef KEYPAD_NUM_CASE + uchar retval; + // as Doug points out, this case statement is expressed algorithmically, + // but hey, this is already done, easier to modify and maybe even easier + // to understand. + switch (b) { + case 0: + retval = 1; + break; + case 1: + retval = 4; + break; + case 2: + retval = 7; + break; + case 3: + retval = 10; + break; + case 4: + retval = 2; + break; + case 5: + retval = 5; + break; + case 6: + retval = 8; + break; + case 7: + retval = 0; + break; + case 8: + retval = 3; + break; + case 9: + retval = 6; + break; + case 10: + retval = 9; + break; + case 11: + retval = 11; + break; + } + return (retval); +#endif + static uchar retval[] = {1, 4, 7, 10, 2, 5, 8, 0, 3, 6, 9, 11}; + return (retval[b]); +} + +// note that the b in keypad_name is already converted from keypad_num +char *keypad_name(int b, char *buf) { + switch (b) { + case 10: + strcpy(buf, "-"); + break; + case 11: + strcpy(buf, "C"); + break; + default: + sprintf(buf, "%d", b); + break; + } + return (buf); +} + +char *mfd_keypad_assemble(keypad_data_type *keypad_data, char *buf) { + char tmp[5]; + int i; + strcpy(buf, ""); + for (i = 0; i < keypad_data->curr_digit; i++) { + strcat(buf, keypad_name(keypad_data->digits[i], tmp)); + } + return (buf); +} + +errtype mfd_keypad_input(MFD *mfd, char b_num) { + keypad_data_type *keypad_data = (keypad_data_type *)&player_struct.mfd_func_data[MFD_KEYPAD_FUNC][0]; + + switch (b_num) { + case 10: + if (keypad_data->curr_digit != 0) + keypad_data->curr_digit--; + break; + case 11: + keypad_data->curr_digit = 0; + break; + default: + if (keypad_data->curr_digit == MAX_KEYPAD_DIGITS) + mfd_setup_keypad(keypad_data->special); + keypad_data->digits[keypad_data->curr_digit] = b_num; + keypad_data->curr_digit++; + break; + } + play_digi_fx_obj(SFX_MFD_KEYPAD, 1, player_struct.panel_ref); + if (keypad_data->special == 0 && keypad_data->curr_digit == MAX_KEYPAD_DIGITS) { + keypad_trigger(player_struct.panel_ref, keypad_data->digits); + } + mfd_notify_func(MFD_KEYPAD_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, TRUE); + return (OK); +} + +uchar keypad_hotkey_func(ushort keycode, uint32_t context, intptr_t data) { + extern MFD mfd[]; + uchar digit = kb2ascii(keycode) - '0'; + int m = NUM_MFDS; + while (mfd_yield_func(MFD_KEYPAD_FUNC, &m)) { + mfd_keypad_input(&mfd[m], digit); + return TRUE; + } + return FALSE; +} + +void install_keypad_hotkeys(void) { + int i; + for (i = 0; i < 10; i++) + // KLC hotkey_add(('0'+i)|KB_FLAG_DOWN|KB_FLAG_2ND, DEMO_CONTEXT, keypad_hotkey_func, 0); + hotkey_add(('0' + i) | KB_FLAG_DOWN, DEMO_CONTEXT, keypad_hotkey_func, 0); +} + +uchar mfd_keypad_handler(MFD *m, uiEvent *ev) { + uchar retval = FALSE; + char n; + + if (ev->type != UI_EVENT_KBD_COOKED) + return (FALSE); + if (!(ev->cooked_key_data.code & KB_FLAG_DOWN)) + return (FALSE); + n = (ev->cooked_key_data.code & 0xFF) - '0'; + if ((n < 0) || (n > 9)) + return (FALSE); + mfd_keypad_input(m, n); + return (FALSE); +} + +uchar mfd_keypad_button_handler(MFD *mfd, LGPoint bttn, uiEvent *ev, void *data) { + int b = bttn.x * KEYPAD_BTTN_ROWS + bttn.y; + + // Filter out anything that isn't a left-click down at all + if ((ev->subtype & (MOUSE_LDOWN | UI_MOUSE_LDOUBLE)) == 0) + return (FALSE); + mfd_keypad_input(mfd, keypad_num(b)); + return TRUE; +} + +errtype mfd_keypad_init(MFD_Func *f) { + int cnt = 0; + errtype err; + LGPoint bsize = {KEYPAD_BTTN_WD, KEYPAD_BTTN_HT}; + LGPoint bdims = {KEYPAD_BTTN_COLS, KEYPAD_BTTN_ROWS}; + LGRect r = {{KEYPAD_BTTNS_X, KEYPAD_BTTNS_Y}, {KEYPAD_BTTNS_X + KEYPAD_BTTNS_WD, KEYPAD_BTTNS_Y + KEYPAD_BTTNS_HT}}; + err = MFDBttnArrayInit(&f->handlers[cnt++], &r, bdims, bsize, mfd_keypad_button_handler, NULL); + if (err != OK) + return err; + f->handler_count = cnt; + return OK; +} + +// Does every thing but set the slot.. +void mfd_setup_keypad(char special) { + int i; + keypad_data_type *keypad_data = (keypad_data_type *)&player_struct.mfd_func_data[MFD_KEYPAD_FUNC][0]; + keypad_data->curr_digit = 0; + for (i = 0; i < MAX_KEYPAD_DIGITS; i++) + keypad_data->digits[i] = 0; + keypad_data->special = special; + mfd_notify_func(MFD_KEYPAD_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, TRUE); +} + +void mfd_keypad_expose(MFD *mfd, ubyte control) { + keypad_data_type *keypad_data = (keypad_data_type *)&player_struct.mfd_func_data[MFD_KEYPAD_FUNC][0]; + char buf[NUMBER_BUFSZ]; + uchar full = control & MFD_EXPOSE_FULL; + if (control == 0) { + ObjID pr; + pr = panel_ref_unexpose(mfd->id, MFD_KEYPAD_FUNC); + if (pr) + objs[pr].info.current_frame = 0; + gKeypadOverride = FALSE; + return; + } + mfd_clear_rects(); + PUSH_CANVAS(pmfd_canvas); + if (full) + mfd_clear_view(); + if ((keypad_data->special > 0) && (keypad_data->curr_digit > 0)) + draw_shodan_influence(mfd, keypad_data->special); + else { + if (full || (keypad_data->last_digit != keypad_data->curr_digit)) { + int i; + short w, h; + // LGPoint bstep = { KEYPAD_BTTNS_WD/KEYPAD_BTTN_COLS, + // KEYPAD_BTTNS_HT/KEYPAD_BTTN_ROWS }; + + // Draw cool LED at top of MFD + gr_set_font(ResLock(KEYPAD_STATUS_FONT)); + mfd_keypad_assemble(keypad_data, buf); + gr_string_size(buf, &w, &h); + mfd_draw_font_string(buf, KEYPAD_STATUS_X - w, KEYPAD_STATUS_Y, KEYPAD_STATUS_COLOR, KEYPAD_STATUS_FONT, + TRUE); + keypad_data->last_digit = keypad_data->curr_digit; + ResUnlock(KEYPAD_STATUS_FONT); + + // Draw buttons + gr_set_font(ResLock(MFD_FONT)); + for (i = 0; i < NUM_KEYPAD_BUTTONS; i++) { + ubyte clr; + LGPoint bttn; + + bttn.x = KEYPAD_BTTNS_X + + (i / KEYPAD_BTTN_ROWS) * (KEYPAD_BTTNS_WD - KEYPAD_BTTN_WD) / (KEYPAD_BTTN_COLS - 1); + bttn.y = KEYPAD_BTTNS_Y + + (i % KEYPAD_BTTN_ROWS) * (KEYPAD_BTTNS_HT - KEYPAD_BTTN_HT) / (KEYPAD_BTTN_ROWS - 1); + + if ((keypad_data->curr_digit > 0) && + (keypad_num(i) == keypad_data->digits[keypad_data->curr_digit - 1])) + clr = SELECTED_ITEM_COLOR; + else + clr = ITEM_COLOR; + gr_set_fcolor((long)clr); + ss_box(bttn.x, bttn.y, bttn.x + KEYPAD_BTTN_WD, bttn.y + KEYPAD_BTTN_HT); + gr_set_fcolor(ITEM_COLOR + 2); + ss_box(bttn.x - 1, bttn.y - 1, bttn.x + KEYPAD_BTTN_WD + 1, bttn.y + KEYPAD_BTTN_HT + 1); + keypad_name(keypad_num(i), buf); + gr_string_size(buf, &w, &h); + mfd_draw_string(buf, bttn.x + (KEYPAD_BTTN_WD - w) / 2 + 1, bttn.y + 1, clr, TRUE); + } + mfd_add_rect(KEYPAD_BTTNS_X, KEYPAD_BTTNS_Y, KEYPAD_BTTNS_X + KEYPAD_BTTNS_WD, + KEYPAD_BTTNS_Y + KEYPAD_BTTNS_HT); + ResUnlock(MFD_FONT); + } + } + POP_CANVAS(); + mfd_update_rects(mfd); +} + +/* +// -------------------- +// HUD WARE MFD +// -------------------- + +#define MFD_HUD_FUNC 11 + +#define HUD_SETTING_MASK 0xF8 +#define HUD_SETTING_SHF 3 + +#define HUDWARE_STATUS (player_struct.hardwarez_status[CPTRIP(HUD_GOG_TRIPLE)]) +#define HUDWARE_VERSION (player_struct.hardwarez[CPTRIP(HUD_GOG_TRIPLE)]) + + +ushort hud_ware_bits[] = { HUD_COMPASS, HUD_DETECT_EXP, HUD_GRENADE }; + +#define NUM_HUDWARE_DISPLAYS (sizeof(hud_ware_bits)/sizeof(ushort)) + +#define HUDWARE_DISPLAY_AVAILABLE(dnum) (HUDWARE_VERSION & ( 1 << (dnum))) + +// Note the use of negative logic here.... +#define HUDWARE_DISPLAY_ACTIVE(dnum) (!(HUDWARE_STATUS & (1 << ((dnum) + HUD_SETTING_SHF)))) +#define HUDWARE_DISPLAY_TOGGLE(dnum) (HUDWARE_STATUS ^= (1 << (dnum) + HUD_SETTING_SHF)) + +uchar mfd_hud_button_handler(MFD* m, LGPoint bttn, uiEvent* ev, void* data) +{ + + // Check to see if we actually have the specified display. + if (!HUDWARE_DISPLAY_AVAILABLE(bttn.y)) return FALSE; + // Toggle the display + if (ev->type == UI_EVENT_MOUSE && ev->subtype & (MOUSE_LDOWN|MOUSE_RDOWN)) + HUDWARE_DISPLAY_TOGGLE(bttn.y); + + if (HUDWARE_STATUS & WARE_ON) // update the actual hud. + { + if (HUDWARE_DISPLAY_ACTIVE(bttn.y)) + hud_set(hud_ware_bits[bttn.y]); + else + hud_unset(hud_ware_bits[bttn.y]); + } + mfd_notify_func(MFD_HUD_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + return TRUE; +} + + +#define HUDWARE_BUTTON_X 2 +#define HUDWARE_BUTTON_Y 16 +#define HUDWARE_LINE_SPACING 7 + +#define HUDWARE_LAST_STATUS(mfd) mfd_fdata[MFD_HUD_FUNC][mfd] + +#define HUDWARE_BOX_COLOR ITEM_COLOR +#define HUDWARE_BOX_SIZE HUDWARE_LINE_SPACING + +#ifdef HUDWARE_MFD + +errtype mfd_hud_init(MFD_Func* f) +{ + errtype err; + LGPoint bsize = { MFD_VIEW_WID, HUDWARE_LINE_SPACING}; + LGPoint bdims = { 1, NUM_HUDWARE_DISPLAYS }; + LGRect brect = { { 0, HUDWARE_BUTTON_Y }, + { MFD_VIEW_WID, HUDWARE_BUTTON_Y + NUM_HUDWARE_DISPLAYS*HUDWARE_LINE_SPACING}}; + int cnt = 0; + err = MFDBttnArrayInit(&f->handlers[cnt++],&brect,bdims,bsize,mfd_hud_button_handler,NULL); + if (err != OK) return err; + f->handler_count = cnt; + return OK; +} + +void mfd_hud_expose(MFD* mfd, ubyte control) +{ + uchar full = control & MFD_EXPOSE_FULL; + if (control == 0) return; + + mfd_clear_rects(); + PUSH_CANVAS(pmfd_canvas); + ss_safe_set_cliprect(0,0,MFD_VIEW_WID,MFD_VIEW_HGT); + + // Lay down the "background" + mfd_item_micro_expose(TRUE,HUD_GOG_TRIPLE); + // clear rects so that we don't draw it if we don't have to + if (!full) mfd_clear_rects(); + + if (full || HUDWARE_STATUS != HUDWARE_LAST_STATUS(mfd->id)) + { + int i; + for (i = 0; i < NUM_HUDWARE_DISPLAYS; i++) + if (HUDWARE_DISPLAY_AVAILABLE(i)) + { + short x = HUDWARE_BUTTON_X; + short y = HUDWARE_BUTTON_Y + i * HUDWARE_LINE_SPACING; + short textx; + char* s; + // draw an "x" if active + if (HUDWARE_DISPLAY_ACTIVE(i)) + { + gr_set_fcolor(GOOD_RED); + ss_int_line(x,y,x+HUDWARE_BOX_SIZE-1,y+HUDWARE_BOX_SIZE-1); + ss_int_line(x,y+HUDWARE_BOX_SIZE-1,x+HUDWARE_BOX_SIZE-1,y); + } + gr_set_fcolor(HUDWARE_BOX_COLOR); + ss_box(x,y,x+HUDWARE_BOX_SIZE,y+HUDWARE_BOX_SIZE); + gr_set_font(ResLock(MFD_FONT)); + s = get_temp_string(REF_STR_HudBase+i); + textx = (MFD_VIEW_WID - HUDWARE_BOX_SIZE - gr_string_width(s))/2 + HUDWARE_BOX_SIZE; + mfd_draw_string(s,textx,y+1,gr_get_fcolor(),TRUE); + ResUnlock(MFD_FONT); + mfd_add_rect(x,y,MFD_VIEW_WID,y+HUDWARE_LINE_SPACING); + } + HUDWARE_LAST_STATUS(mfd->id) = HUDWARE_STATUS; + } + POP_CANVAS(); + mfd_update_rects(mfd); +} + +void hudware_update_status(uchar on) +{ + int i; + for (i = 0; i < NUM_HUDWARE_DISPLAYS; i++) + if (HUDWARE_DISPLAY_ACTIVE(i) && on) + hud_set(hud_ware_bits[i]); + else + hud_unset(hud_ware_bits[i]); +} + +#endif // HUDWARE_MFD +*/ + +// ---------------------------------------------------------- +// THE GOOFY SEVERED HEAD MFD +// ---------------------------------------------------------- + +void severed_head_expose(MFD *mfd, ubyte control); + +void severed_head_expose(MFD *mfd, ubyte control) { + uchar full = control & MFD_EXPOSE_FULL; + if (full) { + ubyte headnum = player_struct.actives[ACTIVE_GENERAL]; + ObjID head = player_struct.inventory[headnum]; + int mug; + uint trip = ID2TRIP(head); + + if (head == OBJ_NULL || !(trip == HEAD_TRIPLE || trip == HEAD2_TRIPLE)) + return; + // clear update rects + mfd_clear_rects(); + // set up canvas + gr_push_canvas(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + // Clear the canvas by drawing the background bitmap + if (!full_game_3d) + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + + mug = REF_IMG_EmailMugShotBase + objSmallstuffs[objs[head].specID].data1; + FrameDesc *f = RefLock(mug); + if (f != NULL) { + ss_bitmap(&f->bm, (MFD_VIEW_WID - f->bm.w) / 2, (MFD_VIEW_HGT - f->bm.h) / 2); + RefUnlock(mug); + } else { + WARN("severed_head_expose(): could not load head art ", mug); + } + + // draw the name + mfd_draw_string(get_object_long_name(ID2TRIP(head), NULL, 0), X_MARGIN, 2, GREEN_YELLOW_BASE, TRUE); + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + // Pop the canvas + gr_pop_canvas(); + // Now that we've popped the canvas, we can send the + // updated mfd to screen + mfd_update_rects(mfd); + } +} + +// ------------------- +// * GENERIC BLANK MFD +// ------------------- + +// --------------------------------------------------------------------------- +// mfd_expose_blank() +// +// Draw whatever we're supposed to draw if we're looking at an empty slot. + +void mfd_expose_blank(MFD *m, ubyte control) { + if (full_game_3d) { + full_visible &= ~visible_mask(m->id); + chg_set_sta(FULLSCREEN_UPDATE); + return; + } + if ((control & MFD_EXPOSE) && !full_game_3d) { + + PUSH_CANVAS(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + draw_blank_mfd(); + + POP_CANVAS(); + mfd_update_display(m, 0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + } + + return; +} + +// --------------------------------------------------------------------------- +// CALLS FROM OTHER MODULES TO THE MFD SYSTEM +// --------------------------------------------------------------------------- + +// --------------------------------------------------------------------------- +// set_inventory_mfd() +// +// Called by the inventory whenever a new drug, ware, something is clicked +// on in the inventory panel. + +ulong catbasetrips[MFD_INV_CATEGORIES] = { + 0, + MAKETRIP(CLASS_DRUG, 0, 0), + MAKETRIP(CLASS_HARDWARE, 0, 0), + MAKETRIP(CLASS_GRENADE, 0, 0), + MAKETRIP(CLASS_AMMO, 0, 0), + MAKETRIP(CLASS_GUN, 0, 0), + 0, // general inventory + MAKETRIP(CLASS_SOFTWARE, SOFTWARE_SUBCLASS_OFFENSE, 0), + MAKETRIP(CLASS_SOFTWARE, SOFTWARE_SUBCLASS_DEFENSE, 0), + MAKETRIP(CLASS_SOFTWARE, SOFTWARE_SUBCLASS_ONESHOT, 0), +}; + +// look, another array that should not exist. +ubyte catactives[] = { + 0, + ACTIVE_DRUG, + ACTIVE_HARDWARE, + ACTIVE_GRENADE, + ACTIVE_CART, + ACTIVE_WEAPON, + ACTIVE_GENERAL, + ACTIVE_COMBAT_SOFT, + ACTIVE_DEFENSE_SOFT, + ACTIVE_MISC_SOFT, +}; + +ubyte activecats[] = { + MFD_INV_WEAPON, MFD_INV_GRENADE, MFD_INV_DRUG, MFD_INV_AMMO, MFD_INV_HARDWARE, + MFD_INV_SOFT_COMBAT, MFD_INV_SOFT_DEFENSE, MFD_INV_SOFT_MISC, MFD_INV_GENINV, 0, +}; + + +void set_inventory_mfd(ubyte obclass, ubyte type, uchar grab) { + int i; + ubyte func = MFD_EMPTY_FUNC; + ubyte slot; + MFD_Status stat; + uchar classhit = FALSE; + + switch (obclass) { + + case MFD_INV_WEAPON: + + // The "grab" arg is TRUE in case blanked out by an inventory drop + func = MFD_WEAPON_FUNC; + slot = MFD_WEAPON_SLOT; + stat = MFD_ACTIVE; + if (type == MFD_INV_NOTYPE) { + stat = MFD_EMPTY; + } + break; + + case MFD_INV_NULL: + + if (type == MFD_INV_NOTYPE) + break; + for (i = 0; i < NUM_MFDS; i++) { + MFDSetCurrItemClass(i, obclass); + } + func = MFD_ITEM_FUNC; + slot = MFD_ITEM_SLOT; + stat = MFD_EMPTY; + break; + + default: + for (i = 0; i < NUM_MFDS; i++) { + if (MFDGetCurrItemClass(i) == obclass) + classhit = TRUE; + if (type != MFD_INV_NOTYPE) + MFDSetCurrItemClass(i, obclass); + } + slot = MFD_ITEM_SLOT; + stat = MFD_ACTIVE; + if (type == MFD_INV_NOTYPE) { + if (classhit) { + mfd_notify_func(MFD_EMPTY_FUNC, MFD_ITEM_SLOT, TRUE, MFD_EMPTY, TRUE); + player_struct.actives[catactives[obclass]] = 0; + } + } else if (obclass == MFD_INV_GENINV && player_struct.inventory[type] == OBJ_NULL) { + mfd_notify_func(MFD_EMPTY_FUNC, MFD_ITEM_SLOT, grab, MFD_EMPTY, TRUE); + } else { + ulong opnum = (obclass != MFD_INV_GENINV) ? OPTRIP(catbasetrips[obclass]) + type + : OPNUM(player_struct.inventory[type]); + func = ObjProps[opnum].mfd_id; + if (func == MFD_EMPTY_FUNC) + func = MFD_ITEM_FUNC; + set_current_active(catactives[obclass]); + } + break; + } + if (func != MFD_EMPTY_FUNC) { + mfd_notify_func(func, slot, grab, stat, TRUE); +#ifdef RAISE_ON_SELECT + if (full_game_3d) { + int i; + for (i = 0; i < NUM_MFDS; i++) { + if (player_struct.mfd_current_slots[i] == MFD_ITEM_SLOT) { +#ifdef STEREO_SUPPORT + if (convert_use_mode == 5) { + full_visible = FULL_MFD_MASK(i); + } else +#endif + full_visible |= FULL_MFD_MASK(i); + } + } + } +#endif + } + // THEN we check to see if we need to take over the info mfd + + switch (obclass) { + + case MFD_INV_HARDWARE: + + switch (type) { + + case HARDWARE_BIOWARE: + + if (WareActive(player_struct.hardwarez_status[HARDWARE_BIOWARE])) + mfd_notify_func(MFD_BIOWARE_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, TRUE); + + break; + } + + break; + } + + if (obclass != MFD_INV_NULL && type != MFD_INV_NOTYPE) + player_struct.actives[catactives[obclass]] = type; +} + +void update_item_mfd(void) { + ubyte curr = player_struct.current_active; + if (curr != NULL_ACTIVE) { + ubyte obclass = activecats[curr]; + ubyte type = player_struct.actives[curr]; + ulong opnum = + (obclass != MFD_INV_GENINV) ? OPTRIP(catbasetrips[obclass]) + type : OPNUM(player_struct.inventory[type]); + int func = ObjProps[opnum].mfd_id; + if (func != MFD_EMPTY_FUNC) { + mfd_notify_func(func, MFD_ITEM_FUNC, TRUE, MFD_ACTIVE, TRUE); + } + } +} + +uchar mfd_distance_remove(ubyte slot_func) { + switch (slot_func) { + case MFD_KEYPAD_FUNC: + case MFD_FIXTURE_FUNC: + case MFD_ELEV_FUNC: + case MFD_BARK_FUNC: + case MFD_ACCESSPANEL_FUNC: + case MFD_GUMP_FUNC: + case MFD_GRIDPANEL_FUNC: + return TRUE; + } + return FALSE; +} + +// ------- +// DEFAULT MFD FUNC QUALIFYING FUNCTIONS +uchar mfd_target_qual(void) { return (player_struct.hardwarez[HARDWARE_TARGET] > 0); } + +uchar mfd_automap_qual(void) { return (player_struct.hardwarez[HARDWARE_AUTOMAP] > 0); } + +uchar mfd_weapon_qual(void) { + return (player_struct.weapons[player_struct.actives[ACTIVE_WEAPON]].type != EMPTY_WEAPON_SLOT); +} + +#define PANEL_PRIORITY 37 + +MFD_Func mfd_funcs[MFD_NUM_FUNCS] = { + // MFD_EMPTY_FUNC 0 + {mfd_expose_blank, NULL, NULL, 255, MFD_NOSAVEREST}, + // MFD_ITEM_FUNC 1 + {mfd_item_expose, mfd_item_handler, mfd_item_init, 40}, + // MFD_MAP_FUNC 2 + {mfd_map_expose, mfd_map_handler, mfd_map_init, 20, MFD_INCREMENTAL}, + // MFD_TARGET_FUNC 3 + {mfd_target_expose, mfd_target_handler, NULL, 21}, + // MFD_ANIM_FUNC 4 + {mfd_expose_blank, NULL, NULL, 255}, + // MFD_WEAPON_FUNC 5 + {mfd_weapon_expose, mfd_weapon_handler, mfd_weapon_init, 25}, + // MFD_BIOWARE_FUNC 6 + {mfd_bioware_expose, NULL, NULL, 50, MFD_NOSAVEREST}, + // MFD_LANTERN_FUNC 7 + {mfd_lanternware_expose, NULL, mfd_lanternware_init, 38}, + // MFD_3DVIEW_FUNC 8 + {mfd_view360_expose, NULL, NULL, 25, MFD_NOSAVEREST}, + // MFD_ELEV_FUNC 9 + {mfd_elevator_expose, NULL, mfd_elevator_init, PANEL_PRIORITY, MFD_NOSAVEREST}, + // MFD_GRENADE_FUNC 10 + {mfd_grenade_expose, mfd_grenade_handler, mfd_grenade_init, 32}, + // MFD_HUD_FUNC 11 + { + mfd_expose_blank, + }, + // MFD_FIXTURE_FUNC 12 + {mfd_fixture_expose, mfd_fixture_handler, NULL, 32, MFD_NOSAVEREST}, + // MFD_KEYPAD_FUNC 13 + {mfd_keypad_expose, mfd_keypad_handler, mfd_keypad_init, PANEL_PRIORITY, MFD_NOSAVEREST}, + // MFD_EMAILMUG_FUNC 14 + {mfd_emailmug_expose, mfd_emailmug_handler, NULL, 60, MFD_NOSAVEREST}, + // MFD_EMAILWARE_FUNC 15 + {mfd_emailware_expose, NULL, mfd_emailware_init, 60}, + // MFD_PLOTWARE_FUNC 16 + {mfd_plotware_expose, NULL, mfd_plotware_init, 55}, + // MFD_BARK_FUNC 17 + {mfd_bark_expose, NULL, NULL, 255, MFD_NOSAVEREST}, + // MFD_ACCESSPANEL_FUNC 18 + {mfd_accesspanel_expose, mfd_accesspanel_handler, mfd_accesspanel_init, PANEL_PRIORITY, + MFD_INCREMENTAL | MFD_NOSAVEREST}, + // MFD_SHIELD_FUNC 19 + {mfd_shieldware_expose, mfd_shield_handler, mfd_shield_init, 36}, + // MFD_MOTION_FUNC 20 + {mfd_motionware_expose, NULL, mfd_motion_init, 36}, + // MFD_SEVERED_HEAD_FUNC 21 + {severed_head_expose, NULL, NULL, 250}, + // MFD_TARGETWARE_FUNC 22 + {mfd_targetware_expose, mfd_targetware_handler, NULL, 40}, + // MFD_GUMP_FUNC 23 + {mfd_gump_expose, mfd_gump_handler, NULL, 40, MFD_NOSAVEREST}, + // MFD_CARD_FUNC 24 + {mfd_accesscard_expose, NULL, NULL, 40}, + // MFD_BIOHELP_FUNC 25 + {mfd_biohelp_expose, mfd_biohelp_handler, mfd_biohelp_init, 40}, + // MFD_GRIDPANEL_FUNC 26 + {mfd_gridpanel_expose, mfd_gridpanel_handler, mfd_gridpanel_init, PANEL_PRIORITY, MFD_NOSAVEREST}, + // MFD_GAMES_FUNC 27 + {mfd_games_expose, mfd_games_handler, mfd_games_init, PANEL_PRIORITY, MFD_INCREMENTAL | MFD_NOSAVEREST}, + // MFD_CYBERSPACE_FUNC 28 + {mfd_cspace_expose, NULL, NULL, 40}, + // MFD_VIEWHELP_FUNC 29 + {mfd_viewhelp_expose, NULL, mfd_viewhelp_init, 40}, + // MFD_GEAR_FUNC 30 + {mfd_gear_expose, mfd_gear_handler, NULL, 40}}; diff --git a/engine/src/GameSrc/mfdgadg.c b/engine/src/GameSrc/mfdgadg.c new file mode 100644 index 0000000..d3e67a6 --- /dev/null +++ b/engine/src/GameSrc/mfdgadg.c @@ -0,0 +1,189 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/cit/src/RCS/mfdgadg.c $ + * $Revision: 1.5 $ + * $Author: mahk $ + * $Date: 1994/02/16 09:26:50 $ + * + * $Log: mfdgadg.c $ + * Revision 1.5 1994/02/16 09:26:50 mahk + * Hey, fixed my goofy bugs. + * + * Revision 1.4 1994/02/16 09:19:57 mahk + * Wrote goofy resizing code. + * + * Revision 1.3 1994/01/15 15:12:04 mahk + * Changed buttonarray spacing. + * + * Revision 1.2 1993/10/20 05:41:33 mahk + * Added a slider gadget. + * + * Revision 1.1 1993/09/15 10:54:30 mahk + * Initial revision + * + * + */ + +#include + +#include "gr2ss.h" +#include "mfdgadg.h" + +uchar mfd_buttonarray_handlerproc(MFD *mfd, uiEvent *ev, MFDhandler *h); +uchar mfd_slider_handler(MFD *mfd, uiEvent *ev, MFDhandler *h); + +// ======================= +// BUTTON ARRAYS +// ======================= + +// --------- +// INTERNALS +// --------- + +typedef struct _mfd_bttnarray { + LGPoint bdims; // Diminsions of button array + LGPoint bspace; // pixel spacing between buttons. + LGPoint bsize; // pixel size of buttons. + MFDBttnCallback cb; + void *cbdata; +} MFDBttnArray; + +uchar mfd_buttonarray_handlerproc(MFD *mfd, uiEvent *ev, MFDhandler *h) { + MFDBttnArray *ba = (MFDBttnArray *)(h->data); + LGPoint bttn; + LGPoint pos; + if (ev->type != UI_EVENT_MOUSE || !(ev->subtype & ~MOUSE_MOTION)) + return FALSE; + pos.x = ev->pos.x - mfd->rect.ul.x - h->r.ul.x; + pos.y = ev->pos.y - mfd->rect.ul.y - h->r.ul.y; + // Make sure its on a button, not a space. + if (pos.x % (ba->bspace.x + ba->bsize.x) >= ba->bsize.x || pos.y % (ba->bspace.y + ba->bsize.y) >= ba->bsize.y) + return FALSE; + bttn.x = lg_min(pos.x / (ba->bspace.x + ba->bsize.x), ba->bdims.x - 1); + bttn.y = lg_min(pos.y / (ba->bspace.y + ba->bsize.y), ba->bdims.y - 1); + return ba->cb(mfd, bttn, ev, ba->cbdata); +} + +// --------- +// EXTERNALS +// --------- + +errtype MFDBttnArrayInit(MFDhandler *h, LGRect *r, LGPoint bdims, LGPoint bsize, MFDBttnCallback cb, void *cbdata) { + if (bsize.x < 1 || bsize.y < 1) + return ERR_RANGE; + MFDBttnArray *ba = (MFDBttnArray *)malloc(sizeof(MFDBttnArray)); + if (ba == NULL) + return ERR_NOMEM; + h->r = *r; + h->data = ba; + h->proc = mfd_buttonarray_handlerproc; + ba->bdims = bdims; + ba->bsize = bsize; + if (bdims.x > 1) + ba->bspace.x = (RectWidth(r) - bsize.x) / (bdims.x - 1) - bsize.x; + else + ba->bspace.x = RectWidth(r) - bsize.x; + if (bdims.y > 1) + ba->bspace.y = (RectHeight(r) - bsize.y) / (bdims.y - 1) - bsize.y; + else + ba->bspace.y = RectHeight(r) - bsize.y; + ba->cb = cb; + ba->cbdata = cbdata; + return OK; +} + +errtype MFDBttnArrayShutdown(MFDhandler *h) { + free(h->data); + h->proc = NULL; + return OK; +} + +errtype MFDBttnArrayResize(MFDhandler *h, LGRect *r, LGPoint bdims, LGPoint bsize) { + MFDBttnArray *ba = (MFDBttnArray *)h->data; + if (bsize.x < 1 || bsize.y < 1) + return ERR_RANGE; + h->r = *r; + ba->bdims = bdims; + ba->bsize = bsize; + if (bdims.x > 1) + ba->bspace.x = (RectWidth(r) - bsize.x) / (bdims.x - 1) - bsize.x; + else + ba->bspace.x = RectWidth(r) - bsize.x; + if (bdims.y > 1) + ba->bspace.y = (RectHeight(r) - bsize.y) / (bdims.y - 1) - bsize.y; + else + ba->bspace.y = RectHeight(r) - bsize.y; + return OK; +} + +// ====================== +// SLIDERS +// ====================== + +// --------- +// INTERNALS +// --------- + +typedef struct _mfd_slider { + MFDSliderCallback cb; + void *data; + uchar bttndown; +} MFDSlider; + +uchar mfd_slider_handler(MFD *mfd, uiEvent *ev, MFDhandler *h) { + short x = mfd->rect.ul.x + h->r.ul.x; + short y = mfd->rect.ul.y + h->r.ul.y; + uchar retval = TRUE; + MFDSlider *sl = (MFDSlider *)(h->data); + LGPoint pos = ev->pos; + pos.x -= x; + pos.y -= y; + if (ev->type != UI_EVENT_MOUSE && ev->type != UI_EVENT_MOUSE_MOVE) + return FALSE; + if (ev->mouse_data.action & MOUSE_LDOWN) { + mouse_constrain_xy(x, y, x + RectWidth(&h->r) - 1, y + RectHeight(&h->r) - 1); + sl->bttndown = TRUE; + } + if (sl->bttndown) { + retval = sl->cb(mfd, pos.x, (uiEvent *)ev, sl->data); + } + if (!(ev->mouse_data.buttons & (1 << MOUSE_LBUTTON))) { + sl->bttndown = FALSE; + mouse_unconstrain(); + } + return retval; +} + +// --------- +// EXTERNALS +// --------- + +errtype MFDSliderInit(MFDhandler *h, LGRect *r, MFDSliderCallback cb, void *data) { + MFDSlider *sl = (MFDSlider *)malloc(sizeof(MFDSlider)); + if (sl == NULL) + return ERR_NOMEM; + h->r = *r; + h->data = sl; + h->proc = (MFD_handlerProc)mfd_slider_handler; + sl->cb = cb; + sl->data = data; + sl->bttndown = FALSE; + return OK; +} diff --git a/engine/src/GameSrc/mfdgames.c b/engine/src/GameSrc/mfdgames.c new file mode 100644 index 0000000..3bb97ac --- /dev/null +++ b/engine/src/GameSrc/mfdgames.c @@ -0,0 +1,3416 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/mfdgames.c $ + * $Revision: 1.45 $ + * $Author: buzzard $ + * $Date: 1994/11/22 09:32:36 $ + * + */ + +#include +#include +#include + +#include "faketime.h" +#include "player.h" +#include "newmfd.h" +#include "mfdint.h" +#include "mfddims.h" +#include "mfdpanel.h" +#include "colors.h" +#include "rcolors.h" +#include "tools.h" +#include "wares.h" +#include "objprop.h" +#include "objsim.h" +#include "miscqvar.h" +#include "cit2d.h" +#include "diffq.h" +#include "mfdgames.h" +#include "softdef.h" + +#include "sfxlist.h" +#include "musicai.h" +#include "gamestrn.h" + +#include "cybstrng.h" +#include "otrip.h" +#include "mfdart.h" +#include "gamescr.h" +#include "gr2ss.h" + +#ifdef LOST_TREASURES_OF_MFD_GAMES +#include "minimax.h" +#include "limits.h" +#endif + +// ------- +// Globals +// ------- + +// special storage used by mfdgames and by wiring puzzles +// but note it's not managed correctly across save/restore etc. +// Hey, this used to be 1024! +#define HIDEOUS_GAME_STORAGE 2048 +unsigned char hideous_secret_game_storage[HIDEOUS_GAME_STORAGE + 4]; + +// stuff to check magic cookie in secret game stuff +#define COOKIE (*((ulong *)hideous_secret_game_storage)) + +#define COOK_VAL(a, b, c, d) ((((a)*256 + (b)) * 256 + (c)) * 256 + (d)) +#define GAME_COOK(d) COOK_VAL('G', 'a', 'm', (d)) + +#define NUM_GAMES 8 + +LGRect GamesMenu; +static long score_time = 0; +extern uchar full_game_3d; + +static int games_time_diff; + +void Rect_gr_box(LGRect *r); +void Rect_gr_rect(LGRect *r); + +#define STRING(x) get_temp_string(REF_STR_##x) + +// note everything crammed into chars due to all mfds always active all have memory always architechture +// if they were ints, all would work, and be much happier to boot + +// note that the starting fields of this must match the +// starting fields of Robot Invasion, since they share +// ball and player movement code +typedef struct { + uchar game_mode; + char ball_pos_x, ball_pos_y; + char ball_dir_x, ball_dir_y; + char p_pos, p_spd; + char c_pos, c_spd; + uchar c_score; + uchar p_score; + uchar last_point; + uchar game_won; +} pong_state; + +typedef struct { + uchar game_mode; + ushort save_time; + // current QIX endpoints + char x[2], dx[2], y[2], dy[2]; + uchar color; +} menu_state; + +typedef struct { + uchar game_mode; // current game mode + uchar lane_cnt; // current lane count + uchar diff; // 0-0xf chance of a new car per lane + char player_lane; // current player line + char player_move; // which way we are going + ushort lanes[8]; // two bits for each + uchar game_state; // have we lost + uint frame_cnt; // how many frames at this difficulty + // uchar last_bs; // last button state +} road_state; + +typedef struct { + // shared state with Ping + uchar game_mode; + char ball_pos_x, ball_pos_y; + char ball_dir_x, ball_dir_y; + char p_pos, p_spd; + + // brick state + ushort rows[3]; // BOTS_NUM_ROWS + short hpos; + char hspd; + char vpos; + char balls; +} bots_state; + +#define PONG_X_DIR_MAX 7 + +#define GAME_DATA_SIZE 32 +#define GAME_DATA (&player_struct.mfd_access_puzzles[0]) +#define GAME_DATA_2 (&player_struct.mfd_func_data[MFD_GAMES_FUNC][0]) +#define GAME_MODE (*((uchar *)GAME_DATA)) + +#define GAME_MODE_MENU NUM_GAMES + +//------------ +// PROTOTYPES +//------------ +void games_expose_pong(MFD *m, ubyte control); +void games_expose_null(MFD *m, ubyte control); +void games_expose_menu(MFD *m, ubyte control); +static void games_expose_mcom(MFD *m, ubyte control); +void games_expose_bots(MFD *m, ubyte control); +void games_expose_road(MFD *m, ubyte control); + +void games_init_pong(void *game_state); +static void games_init_mcom(void *game_state); +void games_init_road(void *game_state); +void games_init_null(void *game_state); +void games_init_bots(void *game_state); + +#ifdef LOST_TREASURES_OF_MFD_GAMES +void games_expose_15(MFD *m, ubyte control); +void games_init_15(void *game_state); +uchar games_handle_15(MFD *m, uiEvent *e); + +void games_expose_ttt(MFD *m, ubyte control); +void games_init_ttt(void *game_state); +uchar games_handle_ttt(MFD *m, uiEvent *e); + +void games_expose_wing(MFD *m, ubyte control); +void games_init_wing(void *game_state); +uchar games_handle_wing(MFD *m, uiEvent *e); +#else +#define games_expose_15 games_expose_null +#define games_init_15 games_init_null +#define games_handle_15 games_handle_null + +#define games_expose_ttt games_expose_null +#define games_init_ttt games_init_null +#define games_handle_ttt games_handle_null + +#define games_expose_wing games_expose_null +#define games_init_wing games_init_null +#define games_handle_wing games_handle_null +#endif + +uchar games_handle_pong(MFD *m, uiEvent *e); +uchar games_handle_road(MFD *m, uiEvent *e); +uchar games_handle_menu(MFD *m, uiEvent *ev); +uchar games_handle_null(MFD *m, uiEvent *ev); + +void games_run_pong(pong_state *work_ps); +void games_run_road(road_state *work_ps); +void games_run_bots(bots_state *bs); + +static void mcom_start_level(void); +int tictactoe_evaluator(void *pos); + +void (*game_expose_funcs[])(MFD *m, ubyte control) = {games_expose_pong, games_expose_mcom, games_expose_road, + games_expose_bots, games_expose_15, games_expose_ttt, + games_expose_null, games_expose_wing, games_expose_menu}; + +void (*game_init_funcs[])(void *game_data) = {games_init_pong, games_init_mcom, games_init_road, + games_init_bots, games_init_15, games_init_ttt, + games_init_null, games_init_wing, games_init_null}; + +extern uchar (*game_handler_funcs[])(MFD *m, uiEvent *ev); + +#define NORMAL_DISPLAY 0 +#define SCORE_DISPLAY 1 +#define WIN_DISPLAY 2 + +#define WIN_PAUSE (4 * CIT_CYCLE) +#define SCORE_PAUSE (2 * CIT_CYCLE) + +#define MFD_VIEW_MID (MFD_VIEW_WID / 2) + +// =========================================================================== +// * THE MFD GAMES CODE * +// =========================================================================== +errtype mfd_games_init(MFD_Func *m) { + GamesMenu.ul.x = 1; + GamesMenu.ul.y = 1; + GamesMenu.lr.x = 6; + GamesMenu.lr.y = 6; + player_struct.mfd_func_status[MFD_GAMES_FUNC] |= 1u << 4u; + + return OK; +} + +// --------------------------------------------------------------------------- +// mfd_games_handler() +uchar mfd_games_handler(MFD *m, uiEvent *e) { + int cur_mode = GAME_MODE; + uchar retval = (*game_handler_funcs[cur_mode])(m, e); + LGRect r; + + // detect if you have games + // if (player_struct.hardwarez[HARDWARE_AUTOMAP] == 0) return FALSE; + + if (!(e->mouse_data.action & MOUSE_LDOWN)) + return FALSE; // ignore click releases + + // Did the user click in the "menu" clickbox? + RECT_OFFSETTED_RECT(&GamesMenu, m->rect.ul, &r); + if (RECT_TEST_PT(&r, e->pos)) { + GAME_MODE = GAME_MODE_MENU; + mfd_notify_func(MFD_GAMES_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); + retval = TRUE; + } + return retval; +} + +// --------------------------------------------------------------------------- +// mfd_games_expose() +// + +void mfd_games_expose(MFD *m, ubyte control) { + int cur_mode; + grs_font *fon; + ulong dt = player_struct.deltat; + + if (control & MFD_EXPOSE) { + + gr_push_canvas(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + mfd_clear_rects(); + + fon = ResLock(RES_tinyTechFont); + gr_set_font(fon); + + cur_mode = GAME_MODE; +#ifdef PLAYTEST + // so, like, this code is totally meaningless. + // I'm glad WATCOM bothers to warn about it. + // if anyone fixes it, probably should make + // cur_mode = GAME_MODE_MENU not 0 (ping) + if ((cur_mode < 0) && (cur_mode > NUM_GAMES)) + cur_mode = 0; +#endif + if (COOKIE != GAME_COOK(GAME_MODE)) { + // hey, our secret storage data became invalid... + // umm, so, umm, what to do? Hey, let's just pop + // them back into menu mode for now + cur_mode = GAME_MODE = GAME_MODE_MENU; + COOKIE = GAME_COOK(cur_mode); + // note we rely on the fact that if someone else + // stomps GAME_MODE to GAME_MODE_MENU, this will + // get caught by our cookie test, which will then + // result in us being able to initialize the save_time field + ((menu_state *)GAME_DATA)->save_time = 0; + } + if (m->id == mfd_slot_primary(MFD_INFO_SLOT)) + games_time_diff += dt; + (*game_expose_funcs[cur_mode])(m, control); + + if (GAME_MODE != GAME_MODE_MENU) { + gr_set_fcolor(MFD_BTTN_FLASH); + Rect_gr_rect(&GamesMenu); + gr_set_fcolor(WHITE); + Rect_gr_box(&GamesMenu); + } + + ResUnlock(RES_tinyTechFont); + + gr_pop_canvas(); + mfd_update_rects(m); + } + +} + +#define MENU_GAMELIST_X 5 +#define MENU_GAMELIST_Y 15 +#define MENU_GAMELIST_DY 7 + +#define TIME_TIL_SCREEN_SAVE (60 * 30) // 30 fps, 60 frames + +#define MAX_LINES 12 +int ss_head = 0; +typedef struct { + char x1, y1, x2, y2, c; +} oldLines; +oldLines *old_lines = (oldLines *)(hideous_secret_game_storage + 4); + +static void init_screen_save(menu_state *ms) { + int i; + for (i = 0; i < MAX_LINES; ++i) + old_lines[i].x1 = old_lines[i].y1 = old_lines[i].x2 = old_lines[i].y2 = old_lines[i].c = 0; + + ms->x[0] = MFD_VIEW_WID / 2 - 4; + ms->x[1] = MFD_VIEW_WID / 2 + 4; + ms->y[0] = ms->y[1] = MFD_VIEW_HGT / 2; + ms->dx[0] = 1; + ms->dy[0] = 1; + ms->dx[1] = 2; + ms->dy[1] = -1; + ms->color = 128; +} + +#define draw_shadowed_text(s, x, y) draw_shadowed_string((s), (x), (y), full_game_3d) + +void games_expose_menu(MFD *m, ubyte control) { + char buf[80]; + uint32_t i; + ubyte cur_games = player_struct.softs.misc[SOFTWARE_GAMES]; + menu_state *ms = ((menu_state *)GAME_DATA); + if (!full_game_3d) + draw_res_bm(REF_IMG_bmBlankMFD, 0, 0); + if (ms->save_time < TIME_TIL_SCREEN_SAVE) { + ++ms->save_time; + strcpy(buf, STRING(GamesMenu)); + gr_string_wrap(buf, MFD_VIEW_WID - 8); + gr_set_fcolor(RED_8_BASE + 4); + draw_shadowed_text(buf, 4, 1); + + strcpy(buf, STRING(DontPlay)); + gr_string_wrap(buf, MFD_VIEW_WID - 5); + draw_shadowed_text(buf, 10, MFD_VIEW_HGT - 14); + + gr_set_fcolor(GREEN_8_BASE + 3); + for (i = 0; i < NUM_GAMES; i++) + if (cur_games & (1u << i)) + draw_shadowed_text(STRING(GameName0 + i), MENU_GAMELIST_X + ((i % 2) * ((MFD_VIEW_WID - 10) / 2)), + MENU_GAMELIST_Y + ((i >> 1u) * MENU_GAMELIST_DY)); + } else { + if (ms->save_time == TIME_TIL_SCREEN_SAVE) { + init_screen_save(ms); + ++ms->save_time; + } + // run screen saver + if ((rand() & 0xff) < 20) { + for (i = 0; i < 2; ++i) { + ms->dx[i] = (rand() % 6) - 3; + ms->dy[i] = (rand() % 6) - 3; + if (ms->dx[i] >= 0) + ms->dx[i] += 2; + else + ms->dx[i] -= 1; + if (ms->dy[i] >= 0) + ms->dy[i] += 2; + else + ms->dy[i] -= 1; + } + } + if ((rand() & 0xff) < 80) + ms->color = (rand() & 0x7f) + 32; + for (i = 0; i < 2; ++i) { + ms->x[i] += ms->dx[i]; + ms->y[i] += ms->dy[i]; + if (ms->x[i] < 0 || ms->x[i] >= MFD_VIEW_WID) + ms->x[i] += (ms->dx[i] = -ms->dx[i]); + if (ms->y[i] < 0 || ms->y[i] >= MFD_VIEW_HGT) + ms->y[i] += (ms->dy[i] = -ms->dy[i]); + } + old_lines[ss_head].x1 = ms->x[0]; + old_lines[ss_head].y1 = ms->y[0]; + old_lines[ss_head].x2 = ms->x[1]; + old_lines[ss_head].y2 = ms->y[1]; + old_lines[ss_head].c = ms->color++; + for (i = 0; i < MAX_LINES; ++i) { + gr_set_fcolor(old_lines[i].c); + ss_int_line(old_lines[i].x1, old_lines[i].y1, old_lines[i].x2, old_lines[i].y2); + } + if (++ss_head == MAX_LINES) + ss_head = 0; + } + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); +} + +uchar games_handle_menu(MFD *m, uiEvent *ev) { + uint32_t game; + uint32_t cur_games = player_struct.softs.misc[SOFTWARE_GAMES]; + + if (GAME_MODE == GAME_MODE_MENU) + ((menu_state *)GAME_DATA)->save_time = 0; + + if (!(ev->mouse_data.action & MOUSE_LDOWN)) + return FALSE; // ignore click releases + + if (ev->pos.y - m->rect.ul.y < MENU_GAMELIST_Y) + return FALSE; + game = (((ev->pos.y) - (m->rect.ul.y) - MENU_GAMELIST_Y) / MENU_GAMELIST_DY) * 2; + if ((ev->pos.x) - (m->rect.ul.x) > MENU_GAMELIST_X + (MFD_VIEW_WID - 10) / 2) + game++; + + if (game > NUM_GAMES || ((1u << game) & cur_games) == 0) + return FALSE; + + COOKIE = GAME_COOK(game); + LG_memset(GAME_DATA, 0, GAME_DATA_SIZE); + GAME_MODE = game; + game_init_funcs[GAME_MODE](GAME_DATA); + + string_message_info(REF_STR_GameDescrip0 + game); + + mfd_notify_func(MFD_GAMES_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, TRUE); + return TRUE; +} + +void games_init_null(void *game_state) { } + +void games_expose_null(MFD *m, ubyte control) { + int cur_games = 0xff; + + if (!full_game_3d) + draw_res_bm(REF_IMG_bmBlankMFD, 0, 0); + ss_string(STRING(NotInstalled), 10, 20); + ss_string(STRING(NotInstalled + 1), 15, 35); + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); +} + +uchar games_handle_null(MFD *m, uiEvent *e) { return FALSE; } + + // ----------------- + // mfd pong + +#define PLY_PADDLE_YRAD 2 +#define PLY_PADDLE_XRAD 6 +#define CMP_PADDLE_YRAD 2 +#define CMP_PADDLE_XRAD 6 +#define PONG_BALL_YRAD 2 +#define PONG_BALL_XRAD 3 +#define PONG_BORDER 3 +#define PONG_SWEET_SPOT 2 + +int get_new_x_dir(int paddle_loc, int ball_loc); +int generic_ball_and_paddle(void *game_state); + +// ------------------- +// generic paddle-hits-ball code + +int get_new_x_dir(int paddle_loc, int ball_loc) { + int x_dir = ((ball_loc - paddle_loc) / 2); + if (x_dir < 0) { + if (x_dir >= -PONG_SWEET_SPOT) + x_dir = 0; + else if (x_dir < -PONG_X_DIR_MAX) + x_dir = -PONG_X_DIR_MAX; + else + x_dir++; + } + else if (x_dir > 0) { + if (x_dir <= PONG_SWEET_SPOT) + x_dir = 0; + else if (x_dir > PONG_X_DIR_MAX) + x_dir = PONG_X_DIR_MAX; + else + x_dir--; + } + return x_dir; +} + +// this function returns non-zero +// if the ball went off the bottom past the paddle +int generic_ball_and_paddle(void *game_state) { + pong_state *work_ps = (pong_state *)game_state; + + // now deal with moving the ball, assume it is currently a valid state + work_ps->ball_pos_x += work_ps->ball_dir_x; + work_ps->ball_pos_y += work_ps->ball_dir_y; + work_ps->p_pos += work_ps->p_spd; + if (work_ps->ball_pos_x < PONG_BALL_XRAD) { + work_ps->ball_pos_x = PONG_BALL_XRAD + (PONG_BALL_XRAD - work_ps->ball_pos_x); + work_ps->ball_dir_x = -work_ps->ball_dir_x; + } + if (work_ps->ball_pos_x > MFD_VIEW_WID - 1 - PONG_BALL_XRAD) { + work_ps->ball_pos_x = + MFD_VIEW_WID - 1 - PONG_BALL_XRAD - (PONG_BALL_XRAD - (MFD_VIEW_WID - 1 - work_ps->ball_pos_x)); + work_ps->ball_dir_x = -work_ps->ball_dir_x; + } + if (work_ps->ball_dir_y >= 0) + // lets see whats up with the player + { + if (work_ps->ball_pos_y - PONG_BALL_YRAD > MFD_VIEW_HGT - PONG_BORDER) { + // out the bottom + return 1; + } else if (work_ps->ball_pos_y > MFD_VIEW_HGT - PONG_BORDER - CMP_PADDLE_YRAD * 2 - PONG_BALL_YRAD) { + // was the player there to deflect it + if ((work_ps->ball_pos_x + PONG_BALL_XRAD >= work_ps->p_pos - PLY_PADDLE_XRAD) && + (work_ps->ball_pos_x - PONG_BALL_XRAD <= work_ps->p_pos + PLY_PADDLE_XRAD)) { + // we got it, larry + int nxdir = get_new_x_dir(work_ps->p_pos, work_ps->ball_pos_x); + // hmm, does dirx work yet + work_ps->ball_dir_y = -work_ps->ball_dir_y; + // average of reflection and new angle??? is this good? + // minus looks good if it hits the edge of the paddle + // plus looks good if it hits the middle of the paddle + // hmm... so need average of reflection and new, but + // reflection has different meanings depending where it hit + work_ps->ball_dir_x = (nxdir - work_ps->ball_dir_x) >> 1u; + play_digi_fx(SFX_MFD_SUCCESS, 1); + } + } + } + return 0; +} + +void games_init_pong(void *game_state) { + pong_state *cur_ws = (pong_state *)game_state; + cur_ws->ball_dir_x = cur_ws->ball_dir_y = cur_ws->c_spd = cur_ws->p_spd = cur_ws->p_score = cur_ws->c_score = + cur_ws->game_won = 0; + cur_ws->c_pos = cur_ws->p_pos = MFD_VIEW_MID; // move paddles to middle + score_time = 0; + games_time_diff = 0; +} + +void games_run_pong(pong_state *work_ps) { + int c_des_spd = 0; // desired speed for the computer player + if ((work_ps->ball_dir_x | work_ps->ball_dir_y) == 0) { // create new ball + int serve_speed = (work_ps->c_score + work_ps->p_score) / 8 + 1; + work_ps->ball_pos_y = MFD_VIEW_HGT / 2; + // work_ps->ball_dir_x=(rand()%3)-1; // -1 -> 1 for x + work_ps->ball_dir_x = 0; + work_ps->ball_dir_y = ((rand() % 3) - 1) * serve_speed; + if (work_ps->ball_dir_y == 0) + work_ps->ball_dir_y = -serve_speed; // -2 -> 2, but not 0, for y + if (work_ps->ball_dir_y < 0) + work_ps->ball_pos_x = work_ps->c_pos; + else + work_ps->ball_pos_x = work_ps->p_pos; + score_time = 0; + } + + // the brutally powerful AI, thats right, AI, think about it + if (work_ps->ball_dir_y < 0) // moving towards computer + { + if (work_ps->c_pos < work_ps->ball_pos_x - CMP_PADDLE_XRAD) // we are too far left + c_des_spd = (((work_ps->ball_pos_x - work_ps->c_pos) >> 4u) + 1); + else if (work_ps->c_pos > work_ps->ball_pos_x + CMP_PADDLE_XRAD) + c_des_spd = -(((work_ps->c_pos - work_ps->ball_pos_x) >> 4u) + 1); + } + if (work_ps->c_spd > c_des_spd) + work_ps->c_spd--; + else if (work_ps->c_spd < c_des_spd) + work_ps->c_spd++; + + if (generic_ball_and_paddle(work_ps)) { + // the player missed. + // what a loser + play_digi_fx(SFX_MFD_BUZZ, 1); + work_ps->ball_dir_x = work_ps->ball_dir_y = 0; + work_ps->last_point = 0; + if (++work_ps->c_score == 0x7) + work_ps->game_won = 1; + score_time = player_struct.game_time; + return; + } + + work_ps->c_pos += work_ps->c_spd; + if (work_ps->ball_dir_y < 0) // moving towards computer + { + if (work_ps->ball_pos_y + PONG_BALL_YRAD < PONG_BORDER) { // out the top, point for the player + play_digi_fx(SFX_INVENT_WARE, 1); + work_ps->ball_dir_x = work_ps->ball_dir_y = 0; + work_ps->last_point = 1; + if (++work_ps->p_score == 0x7) + work_ps->game_won = 1; + score_time = player_struct.game_time; + } else if (work_ps->ball_pos_y < + PONG_BORDER + CMP_PADDLE_YRAD * 2 + PONG_BALL_YRAD) { // was the computer there to deflect it + if ((work_ps->ball_pos_x + PONG_BALL_XRAD >= work_ps->c_pos - CMP_PADDLE_XRAD) && + (work_ps->ball_pos_x - PONG_BALL_XRAD <= work_ps->c_pos + CMP_PADDLE_XRAD)) { // we got it, larry + int nxdir = get_new_x_dir(work_ps->c_pos, work_ps->ball_pos_x); + work_ps->ball_dir_y = -work_ps->ball_dir_y; // hmm, does dirx work yet + work_ps->ball_dir_x = + (nxdir - work_ps->ball_dir_x) >> 1u; // average of reflection and new angle??? is this good? + work_ps->ball_dir_x += rand() % 3 - 1; + play_digi_fx(SFX_MFD_SUCCESS, 1); + } + } + } +} + +#define PONG_CYCLE (CIT_CYCLE / 30) +void games_expose_pong(MFD *m, ubyte control) { + pong_state *cur_ps = (pong_state *)GAME_DATA; + int game_use = NORMAL_DISPLAY; + + if (control == 0 && score_time > 0) + score_time = 0; + // is not true + if (cur_ps->game_won) { + if (score_time + WIN_PAUSE > player_struct.game_time) + game_use = WIN_DISPLAY; + else + // { games_init_pong(cur_ps); return;} + { + GAME_MODE = GAME_MODE_MENU; + return; + } + } else if (score_time + SCORE_PAUSE > player_struct.game_time) { + game_use = SCORE_DISPLAY; + games_time_diff = 0; + } else { + uiEvent fake_event; + + for (; games_time_diff >= PONG_CYCLE; games_time_diff -= PONG_CYCLE) { + games_run_pong(cur_ps); + ui_mouse_get_xy(&fake_event.pos.x, &fake_event.pos.y); + games_handle_pong(m, &fake_event); + if (score_time > 0 || cur_ps->game_won) + break; + } + } + if (!full_game_3d) + draw_res_bm(REF_IMG_bmBlankMFD, 0, 0); + + gr_set_fcolor(ORANGE_YELLOW_BASE); + ss_rect(cur_ps->c_pos - CMP_PADDLE_XRAD, PONG_BORDER, + cur_ps->c_pos + CMP_PADDLE_XRAD, PONG_BORDER + CMP_PADDLE_YRAD * 2); + + gr_set_fcolor(ORANGE_YELLOW_BASE); + ss_rect(cur_ps->p_pos - PLY_PADDLE_XRAD, MFD_VIEW_HGT - PONG_BORDER - PLY_PADDLE_YRAD * 2, + cur_ps->p_pos + PLY_PADDLE_XRAD, MFD_VIEW_HGT - PONG_BORDER); + + gr_set_fcolor(GRAY_8_BASE); + ss_rect(cur_ps->ball_pos_x - PONG_BALL_XRAD, cur_ps->ball_pos_y - PONG_BALL_YRAD, + cur_ps->ball_pos_x + PONG_BALL_XRAD, cur_ps->ball_pos_y + PONG_BALL_YRAD); + + if (game_use != NORMAL_DISPLAY) { + char tmp[2] = "V"; + if (cur_ps->last_point) + ss_string(STRING(YouHave), MFD_VIEW_MID - 18, 15); + else + ss_string(STRING(ComputerHas), MFD_VIEW_MID - 25, 15); + if (cur_ps->game_won) + ss_string(STRING(Won), MFD_VIEW_MID - 8, 22); + else + ss_string(STRING(Scored), MFD_VIEW_MID - 16, 22); + ss_string(STRING(You), MFD_VIEW_MID - 25, 35); + tmp[0] = '0' + cur_ps->p_score; + ss_string(tmp, MFD_VIEW_MID + 10, 35); + ss_string(STRING(Computer), MFD_VIEW_MID - 25, 45); + tmp[0] = '0' + cur_ps->c_score; + ss_string(tmp, MFD_VIEW_MID + 10, 45); + } + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + // autoreexpose + mfd_notify_func(MFD_GAMES_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); + +} + +#define PONG_FUDGE 10 + +uchar games_handle_pong(MFD *m, uiEvent *e) { + pong_state *cur_ps = (pong_state *)GAME_DATA; + LGPoint pos = MakePoint(e->pos.x - m->rect.ul.x, e->pos.y - m->rect.ul.y); + ubyte spd = lg_min(3, abs(pos.x - cur_ps->p_pos) / PLY_PADDLE_XRAD + 1); + if (pos.x + PONG_FUDGE < 0 || pos.y + PONG_FUDGE < 0 || pos.x >= RectWidth(&m->rect) + PONG_FUDGE || + pos.y >= RectHeight(&m->rect) + PONG_FUDGE) { + cur_ps->p_spd = 0; + return TRUE; + } + if (cur_ps->p_pos == pos.x) + cur_ps->p_spd = 0; + else if (cur_ps->p_pos > pos.x) + cur_ps->p_spd = -spd; + else + cur_ps->p_spd = spd; + return TRUE; +} + + //---------------------------- + // mfd road game.... + +#define NO_CAR 0u +#define BAD_CAR 1u +#define SMART_CAR 2u +#define CAR_MASK 3u + +#define NORMAL_DISPLAY 0 +#define SCORE_DISPLAY 1 +#define WIN_DISPLAY 2 + +#define CAR_ROW 6u +#define car_hit() (cur_rs->lanes[cur_rs->player_lane] & (CAR_MASK << (CAR_ROW * 2))) + +#define HIT_BAD (BAD_CAR << (CAR_ROW * 2)) +#define HIT_SMART (SMART_CAR << (CAR_ROW * 2)) + +#define FRAMES_PER 100 +#define FRAMES_RAMPDOWN 10 + +#define BASE_DIFF 5 +#define DIFF_PER 4 +#define DIFF_MUL 3 +#define DIFF_MAX 13 +#define LANE_MAX 8 +#define GAME_WON 2 +#define GAME_LOST 4 + +#define LANE_WID 8u +#define LANE_HEIGHT 6u +#define LANE_ROWS 8u +#define ROAD_TOP 3u +#define CAR_TOP 1u +#define CAR_BOT 4u +#define ROAD_BOTTOM ROAD_TOP + (LANE_HEIGHT * LANE_ROWS) + +#define row_top(y) (ROAD_TOP + (LANE_HEIGHT * y)) + +#define BRIGHT_LED (RED_8_BASE) +#define DIM_LED (RED_8_BASE + 5) +#define BRIGHT_PHOSPHOR (GREEN_8_BASE) +#define DIM_PHOSPHOR (GREEN_8_BASE + 5) +#define BRIGHT_CAR (BLUE_8_BASE) +#define DIM_CAR (BLUE_8_BASE + 5) + +#define CAR_IDX 3 +static uchar c_cols[][2] = { + {BRIGHT_LED, DIM_LED}, + {BRIGHT_PHOSPHOR, DIM_PHOSPHOR}, + {45, 61}, + {BRIGHT_CAR, DIM_CAR} +}; + +void games_init_road(void *game_state) { + road_state *cur_rs = (road_state *)GAME_DATA; + games_time_diff = 0; + cur_rs->lane_cnt = 3; + cur_rs->player_lane = 1; + cur_rs->diff = BASE_DIFF; + LG_memset(&cur_rs->player_move, 0, sizeof(road_state) - 4); // clear rest of fields +} + +void games_run_road(road_state *s) { + road_state *cur_rs = (road_state *)GAME_DATA; + int i; + + if (++cur_rs->frame_cnt == FRAMES_PER) // end of level set + { + cur_rs->diff += DIFF_MUL; + if (cur_rs->diff <= DIFF_MAX) + cur_rs->frame_cnt = 0; + } + for (i = 0; i < cur_rs->lane_cnt; i++) // update old cars + cur_rs->lanes[i] <<= 2; + if (cur_rs->frame_cnt > FRAMES_PER) { + if (cur_rs->frame_cnt > FRAMES_PER + FRAMES_RAMPDOWN) { + cur_rs->frame_cnt = 0; + cur_rs->diff = BASE_DIFF; + if (++cur_rs->lane_cnt > LANE_MAX) { + cur_rs->game_state = GAME_WON; + score_time = player_struct.game_time; + } + } + return; + } + for (i = 0; i < cur_rs->lane_cnt; i++) // new cars + if ((rand() % 0x3f) < cur_rs->diff) { + if ((rand() % 0x3f) == 5) + cur_rs->lanes[i] |= SMART_CAR; + else + cur_rs->lanes[i] |= BAD_CAR; + } + cur_rs->player_lane += cur_rs->player_move; + cur_rs->player_move = 0; + if (cur_rs->player_lane < 0) + cur_rs->player_lane = 0; + else if (cur_rs->player_lane >= cur_rs->lane_cnt) + cur_rs->player_lane = cur_rs->lane_cnt - 1; + + if (car_hit() == HIT_SMART) + LG_memset(cur_rs->lanes, 0, 8 * sizeof(ushort)); + else if (car_hit()) // else if (car_hit()==HIT_BAD) + { + play_digi_fx(SFX_DESTROY_CRATE, 1); + cur_rs->game_state = GAME_LOST; + score_time = player_struct.game_time; + } +} + +static void road_vline(int x, int yt, int yb, int c1, int c2) { + gr_set_fcolor(c1); + ss_vline(x, yt, yb); + gr_set_fcolor(c2); + ss_vline(x - 1, yt, yb); + ss_vline(x + 1, yt, yb); +} + +#define ROAD_CYCLE (CIT_CYCLE / 5) +#define uiMakeaDerMotionEventenHausen uiMakeMotionEvent +void games_expose_road(MFD *m, ubyte tac) { + road_state *cur_rs = (road_state *)GAME_DATA; + int game_use = NORMAL_DISPLAY; + + if (tac == 0) + score_time = 0; + // is not true + if (cur_rs->game_state) { + if (score_time + WIN_PAUSE > player_struct.game_time) + game_use = WIN_DISPLAY; + else { + GAME_MODE = GAME_MODE_MENU; + return; + } + } else + for (; games_time_diff >= ROAD_CYCLE; games_time_diff -= ROAD_CYCLE) { + uiEvent fake_event; + uiMakeaDerMotionEventenHausen(&fake_event); + games_run_road(cur_rs); + games_handle_road(m, &fake_event); + if (cur_rs->game_state) + break; + } + + if (!full_game_3d) + draw_res_bm(REF_IMG_bmBlankMFD, 0, 0); + + { // draw the game here, eh? + int ledge, i, j; + ledge = (MFD_VIEW_WID >> 1u) - ((LANE_WID >> 1u) * cur_rs->lane_cnt); + for (i = 0; i < cur_rs->lane_cnt; i++, ledge += LANE_WID) { + int msk = cur_rs->lanes[i]; + road_vline(ledge, ROAD_TOP, ROAD_BOTTOM, c_cols[0][0], c_cols[0][1]); + for (j = 0; j < LANE_ROWS; j++, msk >>= 2u) + if (msk & 0x3u) + road_vline(ledge + (LANE_WID >> 1u), row_top(j) + CAR_TOP, row_top(j) + CAR_BOT, + c_cols[(msk & 0x3u) - 1][0], c_cols[(msk & 0x3u) - 1][1]); + if (i == cur_rs->player_lane) + road_vline(ledge + (LANE_WID >> 1u), row_top(CAR_ROW) + CAR_TOP, row_top(CAR_ROW) + CAR_BOT, + c_cols[CAR_IDX][0], c_cols[CAR_IDX][1]); + } + road_vline(ledge, ROAD_TOP, ROAD_BOTTOM, c_cols[0][0], c_cols[0][1]); + } + + // won lost state + if (game_use != NORMAL_DISPLAY) { + gr_set_fcolor(GRAY_8_BASE); + ss_string(STRING(YouHave), MFD_VIEW_MID - 18, 15); + ss_string((cur_rs->game_state == GAME_WON ? STRING(Won) : STRING(Lost)), MFD_VIEW_MID - 8, 22); + } + + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + // autoreexpose + mfd_notify_func(MFD_GAMES_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); +} + +uchar games_handle_road(MFD *m, uiEvent *e) { + + if (e->type == UI_EVENT_MOUSE) { + road_state *cur_rs = (road_state *)GAME_DATA; + int bt = e->mouse_data.buttons; + if (bt != 0) + if (e->mouse_data.modifiers != 0) + bt++; + + // if (bt!=cur_rs->last_bs) + // { + // cur_rs->last_bs=bt; + // KLC - no need to worry with left/right handed mouse for Mac. + // if (QUESTVAR_GET(MOUSEHAND_QVAR)) + // bt = (((me->buttons)&2)?1:0)+(((me->buttons)&1)?2:0); + if (bt == 1) + cur_rs->player_move = -1; + else if (bt == 2) + cur_rs->player_move = 1; + } + return TRUE; +} + + //---------------------------- + // mfd Robot Invasion ("bots") + // + // cross between breakout and space invaders + +#define BOTS_NUM_ROWS 3u +#define BOTS_NUM_COLUMNS 8u +#define BOTS_MASK ((1u << BOTS_NUM_COLUMNS) - 1) + +#define BOT_WIDTH 8 +#define BOT_HEIGHT 7 +//#define BOT_TOP 10 + +#define INVADER_TYPES 3 + +static int invader[INVADER_TYPES] = {REF_IMG_RAsec1bot, REF_IMG_RAhopper, REF_IMG_RAservbot}; + +static void bots_reset_ball(bots_state *bs) { + bs->ball_pos_y = MFD_VIEW_HGT - PONG_BORDER - 2 * PONG_BALL_YRAD - 2 * PLY_PADDLE_YRAD; + bs->ball_pos_x = bs->p_pos; + bs->ball_dir_x = 0; + bs->ball_dir_y = -1; +} + +static void bots_reset_level(bots_state *bs) { + for (uint8_t i = 0; i < BOTS_NUM_ROWS; ++i) + bs->rows[i] = BOTS_MASK; + bs->hpos = 0; + bs->hspd = 1; + bs->vpos = 10; + bots_reset_ball(bs); +} + +void games_init_bots(void *game_state) { + bots_state *bs = (bots_state *)game_state; + + bots_reset_level(bs); + bs->p_pos = MFD_VIEW_WID / 2; + bs->p_spd = 0; + bs->balls = 3; +} + +#define HPOS (bs->hpos >> 5) +#define BOT_TOP (bs->vpos) + +#define BOT_ON(x, y) \ + ((x) >= 0 && (x) < BOTS_NUM_COLUMNS && (y) >= 0 && (y) < BOTS_NUM_ROWS && (bs->rows[y] & (1 << (x)))) +#define CLEAR_BOT(x, y) (bs->rows[y] &= ~(1 << (x))) + +static int test_bot(bots_state *bs, int x, int y) { + if (BOT_ON(x, y)) { + CLEAR_BOT(x, y); + if (bs->hspd > 0) + ++bs->hspd; + else + --bs->hspd; + return 1; + } + return 0; +} + +#define HPOS_HACK 3 + +void games_run_bots(bots_state *bs) { + int rev, guys; + uint8_t i; + + bs->hpos += bs->hspd; + // test if we've scrolled a bot off the left + // bot #x is at location hpos + x*BOT_WIDTH; + // so if say bot #0 is scrolled off, it's because + // hpos < 0; if bot #1, hpos < -BOT_WIDTH, etc. + // so if y = hpos/-BOT_WIDTH, that's how many bots + // we may have scrolled off. + + guys = bs->rows[0] | bs->rows[1] | bs->rows[2]; + + if (!guys) { + bots_reset_level(bs); + return; + } + + for (i = 2; i >= 0; --i) + if (bs->rows[i]) + break; + if (BOT_TOP + BOT_HEIGHT * (i + 1) > MFD_VIEW_HGT - PONG_BORDER - 2 * PLY_PADDLE_YRAD) + goto loser; + +#ifdef USE_BROKEN_CODE + if (bs->hpos < HPOS_HACK) { + rev = ((unsigned)(-HPOS - HPOS_HACK)) / BOT_WIDTH; + rev = (1 << rev) - 1; // test bottommost bits + if (guys & rev) + bs->hpos += 2 * (bs->hspd = -bs->hspd); + } +#else + if (bs->hpos < 0) { + for (i = 0; i < BOTS_NUM_COLUMNS; ++i) + if (guys & (1u << i)) + break; + if (i * BOT_WIDTH + HPOS - HPOS_HACK < 0) { + bs->hpos += 2 * (bs->hspd = -bs->hspd); + ++bs->vpos; + } + } +#endif + + if (bs->hspd > 0) { +#ifdef USE_BROKEN_CODE + // position of the rightmost bot is BOTS_NUM_COLUMNS * BOT_WIDTH + hpos, + // which scrolls off if > MFD_VIEW_WID + rev = (MFD_VIEW_WID - HPOS) / BOT_WIDTH - BOTS_NUM_COLUMNS; + // rev is now the number of bots we'd've shifted off the right + if (rev > 0) { + if (rev > BOTS_NUM_COLUMNS) + bs->hpos += 2 * (bs->hspd = -bs->hspd); + else { + rev = ~((1 << (BOTS_NUM_COLUMNS - rev)) - 1); + if (guys & rev) + bs->hpos += 2 * (bs->hspd = -bs->hspd); + } + } +#else + // so loop through and see whether any bots are offscreen + for (i = BOTS_NUM_COLUMNS - 1; i >= 0; --i) + if (guys & (1u << i)) + break; + // i is the rightmost bot + if (HPOS + i * BOT_WIDTH + 3 > MFD_VIEW_WID) { + bs->hpos += 2 * (bs->hspd = -bs->hspd); + ++bs->vpos; + } +#endif + } + + if (generic_ball_and_paddle(bs)) { + if (--bs->balls) + bots_reset_ball(bs); + else { + loser: + GAME_MODE = GAME_MODE_MENU; + return; + } + } else { + // handle other bouncy conditions + // switch from upward to downward + int bot_left = (bs->ball_pos_x - PONG_BALL_XRAD - HPOS) / BOT_WIDTH; + int bot_right = (bs->ball_pos_x - PONG_BALL_XRAD - HPOS) / BOT_WIDTH; + int bot_top = (bs->ball_pos_y - BOT_TOP - PONG_BALL_YRAD) / BOT_HEIGHT; + int bot_bot = (bs->ball_pos_y - BOT_TOP + PONG_BALL_YRAD) / BOT_HEIGHT; + + if (bs->ball_dir_x == 0) + bs->ball_dir_x = 1; + rev = 0; + if (bs->ball_dir_y < 0) { + if (bs->ball_pos_y + PONG_BALL_YRAD < PONG_BORDER) + rev = 1; + if (test_bot(bs, bot_left, bot_top) || test_bot(bs, bot_right, bot_top)) + rev = 1; + } + if (bs->ball_dir_y > 0) + if (test_bot(bs, bot_left, bot_bot) || test_bot(bs, bot_right, bot_bot)) + rev = 1; + + if (rev) + bs->ball_pos_y += (bs->ball_dir_y = -bs->ball_dir_y); + + rev = 0; + if (bs->ball_dir_x < 0) + if (test_bot(bs, bot_left, bot_top) || test_bot(bs, bot_left, bot_bot)) + rev = 1; + if (bs->ball_dir_x > 0) + if (test_bot(bs, bot_right, bot_top) || test_bot(bs, bot_right, bot_bot)) + rev = 1; + + if (rev) + bs->ball_pos_x += (bs->ball_dir_x = -bs->ball_dir_x); + } +} + +void games_expose_bots(MFD *m, uchar control) { + bots_state *bs = (bots_state *)GAME_DATA; + uiEvent fake_event; +#ifdef SVGA_SUPPORT + extern char convert_use_mode; +#endif + + for (; games_time_diff >= PONG_CYCLE; games_time_diff -= PONG_CYCLE) { + games_run_bots(bs); + ui_mouse_get_xy(&fake_event.pos.x, &fake_event.pos.y); + games_handle_pong(m, &fake_event); + } + + if (!full_game_3d) + draw_res_bm(REF_IMG_bmBlankMFD, 0, 0); + + for (uint8_t j = 0; j < BOTS_NUM_ROWS; ++j) { + gr_set_fcolor(WHITE); + for (uint8_t i = 0; i < BOTS_NUM_COLUMNS; ++i) { + if (bs->rows[j] & (1u << i)) { +#ifdef SVGA_SUPPORT + draw_res_bm_core(invader[j] + INVADER_TYPES * convert_use_mode, HPOS + BOT_WIDTH * i, + BOT_HEIGHT * j + BOT_TOP - (j == 1), FALSE); +#else + draw_res_bm(invader[j], HPOS + BOT_WIDTH * i, BOT_HEIGHT * j + BOT_TOP - (j == 1)); +#endif + } + } + } + + gr_set_fcolor(ORANGE_YELLOW_BASE); + ss_rect(bs->p_pos - PLY_PADDLE_XRAD, MFD_VIEW_HGT - PONG_BORDER - PLY_PADDLE_YRAD * 2, bs->p_pos + PLY_PADDLE_XRAD, + MFD_VIEW_HGT - PONG_BORDER); + + gr_set_fcolor(GRAY_8_BASE); + ss_rect(bs->ball_pos_x - PONG_BALL_XRAD, bs->ball_pos_y - PONG_BALL_YRAD, bs->ball_pos_x + PONG_BALL_XRAD, + bs->ball_pos_y + PONG_BALL_YRAD); + + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + // autoreexpose + mfd_notify_func(MFD_GAMES_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); + +} +#undef HPOS + +// +//-------------------- +// mfd missile command +// +// saved game state is merely the map of which guys are still alive, +// which round number this is, the number of shots remaining, the number +// of enemies remaining in the wave, and the state of the state machine. +// Thus, when you restore, the state machine state forces us into the +// right mode, and then you still have to fight the right enemies; but +// you can cheat a bit by saving right before you might've lost a guy. +// Not much we can do since save/restore events aren't visible to us. + +typedef struct { + uchar game_mode, state; + + ushort enemies; + ulong score; + + ushort level; // 12 + + uchar guys; // 10 // guys & gun placements + + uchar lmissiles; + uchar rmissiles; // 10 + + uchar quarter; + uchar bob; +} mcom_state; + +// this macro should return a long (well, a 32-bit int) _lvalue_ +// (must be assignable) which is actually maintained even +// when other games are played, i.e. it should be out of quest variables +// and hey, look, it is. + +#define HISCORE_QVAR 0x2D + +#define HISCORE (*((ulong *)&player_struct.questvars[HISCORE_QVAR])) + +#define MAX_EXPLOSIONS 30 +#define MAX_ATTACKERS 24 +#define MAX_MISSILES 16 + +#define EXPLODE_FRAMES 20 +#define FIRE_FRAMES 12 + +// possible states for the state machine; +// we explicitly encode pauses into the numbering scheme +#define MCOM_WAIT_NEW_GAME 1 +#define MCOM_WAIT_FOR_LEVEL (MCOM_WAIT_NEW_GAME + 1) +#define LEVEL_WAIT 30 +#define MCOM_PLAY_GAME (MCOM_WAIT_FOR_LEVEL + LEVEL_WAIT) +#define MCOM_REPORT_MISSILES (MCOM_PLAY_GAME + 1) +#define MISSILE_WAIT 16 +#define MCOM_REPORT_guys (MCOM_REPORT_MISSILES + MISSILE_WAIT) +#define guy_WAIT 16 +#define MCOM_BONUS_guys (MCOM_REPORT_guys + guy_WAIT) +#define BONUS_WAIT 8 +#define MCOM_RELEVEL (MCOM_BONUS_guys + BONUS_WAIT) + +// scoring +#define SCORE_PER_MISSILE_KILLED 10 +#define SCORE_PER_LEFTOVER_SHOT 1 +#define SCORE_PER_GUY_ALIVE 100 + +#define DIEGO_SCORE 5093 + +static long shodan_score[7] = {1307496, 1259431, 1175035, 1143910, 1083477, 1032148, 1027869}; + +#define EXPLODE (hideous_secret_game_storage + 4) + +typedef struct { + uchar x; + uchar y; + uchar frame; +} expStruct; +expStruct *explode = (expStruct *)EXPLODE; + +#define ATTACKER (EXPLODE + sizeof(expStruct) * MAX_EXPLOSIONS) + +typedef struct { + uchar sx; + uchar sy; + int x; // 1 bit sign, 8 bit integer, 8 bit fraction + int y; + int dx; + int dy; +} attackStruct; +attackStruct *attack = (attackStruct *)ATTACKER; + +#define MISSILE (ATTACKER + sizeof(attackStruct) * MAX_ATTACKERS) + +typedef struct { + uchar sx; + uchar sy; + uchar ex; + uchar ey; + uchar frame; +} shotsStruct; +shotsStruct *shots = (shotsStruct *)MISSILE; + +static int num_attackers, num_missiles, num_explosions; +#define GROUND_TOP (4) +#define guy_TOP (GROUND_TOP + 3) + +#define TRAIL_LENGTH 10 + +static unsigned char radius[] = {1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 7, 6, 6, 6, 5, 5, 4, 3, 2, 1}; + +static void make_mcom_explode(int x, int y) { + if (num_explosions < MAX_EXPLOSIONS) { + explode[num_explosions].x = x; + explode[num_explosions].y = y; + explode[num_explosions++].frame = 0; + } +} + +static void make_mcom_shot(int x, int y, int sx) { + if (num_missiles < MAX_MISSILES) { + shots[num_missiles].sx = sx; + shots[num_missiles].sy = GROUND_TOP + 9; + shots[num_missiles].ex = x; + shots[num_missiles].ey = y; + shots[num_missiles++].frame = 0; + play_digi_fx(SFX_GUN_RAILGUN, 1); + } +} + +// if you make this a #define, make sure you cast it to +// an int, otherwise it's unsigned and hoses the dx calculation + +// compute current vertical speed based on level +static int v_speed(void) { return 24 + ((mcom_state *)GAME_DATA)->level * 4; } + +static void make_attacker(int sx, int sy, int dx) { + if (num_attackers < MAX_ATTACKERS && ((mcom_state *)GAME_DATA)->enemies) { + attack[num_attackers].sx = sx; + attack[num_attackers].sy = sy; + attack[num_attackers].x = sx * 256; + attack[num_attackers].y = sy * 256; + attack[num_attackers].dy = -v_speed(); + attack[num_attackers++].dx = (dx - sx) * v_speed() / (sy - guy_TOP); + ((mcom_state *)GAME_DATA)->enemies--; + } +} + +static int guy_loc(int n) { + // 0 1 2 3 4 5 6 7 8 9 + // | ^ ^ | + // edge silo silo edge + + // so first map guy number 0..5 into above numbering scheme + int k = (n / 2) * 3 + (n & 1) + 1; + + // then map 0 to 0 and 9 to MFD_VIEW_WID + return MFD_VIEW_WID * k / 9; +} + +static void make_random_attacker(void) { + // attacker comes from anywhere on top, targetting any guy + make_attacker(rand() % MFD_VIEW_WID, MFD_VIEW_HGT - 1, MFD_VIEW_WID * ((rand() % 8) + 1) / 9); +} + +#define SQRD(x) ((x) * (x)) + +#define BOAT_DEATH_NOISE SFX_SPARKING_CABLE +#ifdef DEMO +#define SWIMMER_DEATH_NOISE SFX_SPARKING_CABLE +#else +#define SWIMMER_DEATH_NOISE SFX_DEATH_9 +#endif + +static void advance_mcom_state(void) { + uint32_t i, j; + uchar silo = FALSE; + mcom_state *ms = (mcom_state *)GAME_DATA; + uchar old_quarter = ms->quarter; + uchar old_bob = ms->bob; + + // this code used to be in the expose func, hopefully this doesn't + // break it + switch (ms->state) { + case MCOM_WAIT_NEW_GAME: + case MCOM_PLAY_GAME: + break; + default: + switch (++ms->state) { + case MCOM_PLAY_GAME: + mcom_start_level(); + break; + case MCOM_RELEVEL: + ms->state = MCOM_WAIT_FOR_LEVEL; + } + } + + // bob the swimmers + ms->quarter = (player_struct.game_time / (CIT_CYCLE / 4)) & 3u; + if (ms->quarter != old_quarter) { + // bob swimmers down + ms->bob |= rand() & ((1u << 6u) - 1); + // bob old swimmers up + ms->bob &= ~old_bob; + } + + // advance explosion animations + for (i = 0; i < num_explosions;) + if (++(explode[i].frame) == EXPLODE_FRAMES) + explode[i] = explode[--num_explosions]; + else + ++i; + + // advance defense missiles steps... conveniently, all simply use + // a frame counter. if missile reaches end frame, blow it up + for (i = 0; i < num_missiles;) + if (++(shots[i].frame) >= FIRE_FRAMES) { + make_mcom_explode(shots[i].ex, shots[i].ey); + shots[i] = shots[--num_missiles]; + } else + ++i; + + // advance an attacking missile. if the missile is within range + // of an explosion, kaboom! otherwise, if it's down at ground level, + // kaboom, and blow something up too! + // otherwise just advance it + for (i = 0; i < num_attackers;) { + // check if we've gotten blown up by an explosion + for (j = 0; j < num_explosions; ++j) + if (SQRD((int)explode[j].x - (int)attack[i].x / 256) + SQRD((int)explode[j].y - (int)attack[i].y / 256) <= + SQRD(radius[explode[j].frame])) + break; + if (j < num_explosions) { + ms->score += SCORE_PER_MISSILE_KILLED; + blowup: + make_mcom_explode(attack[i].x / 256, attack[i].y / 256); + attack[i] = attack[--num_attackers]; + } else { + attack[i].x += attack[i].dx; + attack[i].y += attack[i].dy; + if (attack[i].y <= guy_TOP * 256) { + // blow up the guy that's here! + j = (attack[i].x / 256); + j = (9 * j + MFD_VIEW_WID / 2) / MFD_VIEW_WID - 1; + if (j == 2) { + ms->lmissiles = 0; + silo = TRUE; + } + if (j == 5) { + ms->rmissiles = 0; + silo = TRUE; + } + if (ms->guys & (1u << j)) + play_digi_fx(silo ? BOAT_DEATH_NOISE : SWIMMER_DEATH_NOISE, 1); + else + play_digi_fx(SFX_SPARKING_CABLE, 1); + ms->guys &= ~(1u << j); + goto blowup; + } else + ++i; + } + } +} + +#define ALL_guys 0xffu // binary 11111111 +#define LGUN_FLAG 0x04u // binary 00000100 +#define RGUN_FLAG 0x20u // binary 00100000 +#define JUST_guys (ALL_guys - LGUN_FLAG - RGUN_FLAG) + +static void games_init_mcom(void *game_state) { + mcom_state *ms = (mcom_state *)game_state; + + ms->state = MCOM_WAIT_NEW_GAME; + ms->guys = ALL_guys; + num_attackers = num_missiles = num_explosions = 0; + games_time_diff = 0; +} + +static void mcom_start_game(void) { + mcom_state *ms = (mcom_state *)GAME_DATA; + ms->guys = ALL_guys; + ms->state = MCOM_WAIT_FOR_LEVEL; + ms->lmissiles = ms->rmissiles = 30; + ms->level = 0; + num_attackers = num_missiles = num_explosions = 0; +} + +static void mcom_start_level(void) { + int i; + mcom_state *ms = (mcom_state *)GAME_DATA; + ++ms->level; + ms->enemies = 4 * (ms->level) / 3 + 15; + ms->guys |= (LGUN_FLAG | RGUN_FLAG); + ms->lmissiles = ms->rmissiles = 30; + for (i = 0; i < (4 + (ms->level >> 3)); ++i) + make_random_attacker(); +} + +// hey, hey, we got some user input +static uchar games_handle_mcom(MFD *m, uiEvent *e) { + mcom_state *ms = (mcom_state *)GAME_DATA; + LGPoint pos = MakePoint(e->pos.x - m->rect.ul.x, e->pos.y - m->rect.ul.y); + + if (ms->state == MCOM_WAIT_NEW_GAME) { + if (e->mouse_data.action & MOUSE_LDOWN) + mcom_start_game(); + } else if (ms->state < MCOM_PLAY_GAME || (ms->state == MCOM_PLAY_GAME && (ms->enemies || num_attackers))) { + int left = (e->mouse_data.action & MOUSE_LDOWN) && (e->mouse_data.modifiers == 0); + int right = (e->mouse_data.action & MOUSE_LDOWN) && (e->mouse_data.modifiers != 0); + // KLC - no need to worry about left/right hand mouse for Mac version. + // if (QUESTVAR_GET(MOUSEHAND_QVAR)) + // { + // int temp = left; + // left = right; + // right = temp; + // } + if (left && ms->lmissiles) + make_mcom_shot(pos.x, pos.y, MFD_VIEW_WID / 3), --ms->lmissiles; + if (right && ms->rmissiles) + make_mcom_shot(pos.x, pos.y, MFD_VIEW_WID * 2 / 3), --ms->rmissiles; + } + return TRUE; +} + +// coordinates of the missiles in the boats +static signed char ox[10] = {-3, -1, 1, 3, -2, 0, 2, -1, 1, 0}; +static signed char oy[10] = {1, 1, 1, 1, 3, 3, 3, 5, 5, 7}; + +static void draw_silo(int x, int num) { + // bottom row of silo must have room for 4 missiles, so 9 pixels wide + + draw_res_bm(REF_IMG_Destroyer, x - 4, GROUND_TOP - 2); + + // now plot the missiles waiting to fire + + if (!num) + return; + gr_set_fcolor(ORANGE_8_BASE + 1); + num = num % 10; + if (num == 0) + num = 10; + for (uint32_t i = 0; i < num; ++i) + ss_rect(x + ox[i], GROUND_TOP + oy[i], x + ox[i] + 1, GROUND_TOP + oy[i] + 1); +} + + // static unsigned char guy_disp[] = { 2,6,3,2,5 }; + + //#define gr_int_cline(x0,y0,c0,x1,y1,c1) \ +// gr_fix_cline(fix_make(x0,0),fix_make(y0,0),c0,\ +// fix_make(x1,0),fix_make(y1,0),c1) + // // convert 8-bit rgb values into grs_rgb + //#define make_rgb(r,g,b) (((b) << 24) | ((g) << 13) | ((r) << 2)) + +#define gr_int_cline(x0, y0, c0, x1, y1, c1) ss_int_line(x0, y0, x1, y1) + +// update ten times per second. +#define MCOM_CYCLE (CIT_CYCLE / 35) + +static int hack[] = {0, SCORE_PER_GUY_ALIVE, SCORE_PER_GUY_ALIVE, SCORE_PER_GUY_ALIVE * 2}; +static void games_expose_mcom(MFD *m, ubyte control) { + uint32_t i; + int32_t k; + mcom_state *ms = (mcom_state *)GAME_DATA; + + if (!ms->state) { + ms->state = MCOM_WAIT_NEW_GAME; + ms->guys = ALL_guys; + } + + // water + gr_set_fcolor(BLUE_8_BASE + 6); + ss_rect(0, GROUND_TOP + 1, MFD_VIEW_WID, MFD_VIEW_HGT); + + // sky + gr_set_fcolor(AQUA_8_BASE + 3); + ss_rect(0, 0, MFD_VIEW_WID, GROUND_TOP + 1); + + // print current score behind floating guys + { + char buffer[16]; + sprintf(buffer, "%06ld", ms->score); + gr_set_fcolor(WHITE); + ss_string(buffer, MFD_VIEW_WID - 5 * 6 + 4, 0); + } + + if (ms->guys & LGUN_FLAG) + draw_silo(MFD_VIEW_WID / 3, ms->lmissiles); + if (ms->guys & RGUN_FLAG) + draw_silo(MFD_VIEW_WID * 2 / 3, ms->rmissiles); + + // floating guys + for (i = 0; i < 6; ++i) + if ((1u << "\000\001\003\004\006\007"[i]) & ms->guys) { + k = guy_loc(i); + // now k is the center location of the guy, so now draw the guy + draw_res_bm(REF_IMG_LittleGuy, k - 2, GROUND_TOP - 1 + (((1u << i) & ms->bob) != 0)); + } + + // we've drawn all the background, now draw the foreground stuff + + // draw foreground information behind everything, + // just because it looks cool in Llamatron + // but note we draw it in front of non-moving stuff + + gr_set_fcolor(AQUA_8_BASE + 3); + if (ms->state == MCOM_WAIT_NEW_GAME) { + for (i = 0; i < 8; ++i) { + char buffer[16]; + if (HISCORE > DIEGO_SCORE) { + strncpy(buffer, player_struct.name, 8); + // limited space in hiscore display, so strncpy + strtoupper(buffer); + } + + ss_string(i < 7 ? STRING(ShodanHiScore) : HISCORE <= DIEGO_SCORE ? STRING(DiegoHiScore) : buffer, 4, + i * 5 + 9); + // Note that Shodan has scored 1 digit more than the authors of + // Eel Zapper were expecting, so other people have a leading blank + // of course it's totally unrealistic unless that this would work + // out right unless their score-painting code printed from the + // right, but that's not unreasonable since conversion to decimal + // starts from the right. + sprintf(buffer, i < 7 ? "%07ld" : " %06ld", + i < 7 ? shodan_score[i] : HISCORE < DIEGO_SCORE ? DIEGO_SCORE : HISCORE); + ss_string(buffer, MFD_VIEW_WID - 30, i * 5 + 9); + } + ss_string(STRING(ClickToPlay), MFD_VIEW_MID - 25, MFD_VIEW_HGT - 7); + } else if (ms->state < MCOM_PLAY_GAME) { + char buffer[16]; + sprintf(buffer, STRING(LevelNum), ms->level + 1); + ss_string(buffer, MFD_VIEW_MID - 15, MFD_VIEW_HGT / 2); + } else if (ms->state == MCOM_PLAY_GAME) { + } else { + // ideally this will countup how many you got + char buffer[32]; + int z; + + z = ms->state - MCOM_REPORT_MISSILES; + if (z > MISSILE_WAIT) + z = MISSILE_WAIT; + ss_string(STRING(DepthChargeBonus), 2, MFD_VIEW_HGT / 2 - 8); + sprintf(buffer, "%d", (ms->lmissiles + ms->rmissiles) * SCORE_PER_LEFTOVER_SHOT * z / MISSILE_WAIT); + ss_string(buffer, MFD_VIEW_MID - 5, MFD_VIEW_HGT / 2 - 3); + z = ms->state - MCOM_REPORT_guys; + if (z >= 0) { + if (z > guy_WAIT) + z = guy_WAIT; + ss_string(STRING(GuyBonus), 12, MFD_VIEW_HGT / 2 + 8); + sprintf(buffer, "%d", + (hack[ms->guys & 3u] + hack[(ms->guys >> 3u) & 3u] + hack[(ms->guys >> 6u) & 3u]) * z / guy_WAIT); + ss_string(buffer, MFD_VIEW_MID - 10, MFD_VIEW_HGT / 2 + 13); + } + } + + // draw the incoming missiles + for (i = 0; i < num_attackers; ++i) { + gr_set_fcolor(GREEN_8_BASE + 3); + if (attack[i].y > (MFD_VIEW_HGT - TRAIL_LENGTH << 8)) + ss_fix_line(attack[i].x << 8u, attack[i].y << 8u, attack[i].sx << 16u, attack[i].sy << 16u); + else + ss_fix_line(attack[i].x << 8u, attack[i].y << 8u, + (attack[i].x + (attack[i].x - (attack[i].sx << 8u)) * TRAIL_LENGTH * 256 / (attack[i].y - (attack[i].sy << 8u))) << 8u, + (attack[i].y + (TRAIL_LENGTH << 8u)) << 8u); + + gr_set_fcolor(GRAY_8_BASE); + // an incredibly stupid way to plot a pixel! fix me + ss_hline(attack[i].x / 256, attack[i].y / 256, attack[i].x / 256); + } + + // draw the shots + + gr_set_fcolor(GREEN_BASE + 2); + for (i = 0; i < num_missiles; ++i) + draw_res_bm(REF_IMG_DepthCharge, shots[i].sx + (shots[i].ex - shots[i].sx) * shots[i].frame / FIRE_FRAMES, + shots[i].sy + (shots[i].ey - shots[i].sy) * shots[i].frame / FIRE_FRAMES); + + // draw all the explosions + + for (i = 0; i < num_explosions; ++i) { + gr_set_fcolor(GRAY_8_BASE + 8 - radius[explode[i].frame]); + ss_int_disk(explode[i].x, explode[i].y, radius[explode[i].frame] << 1u); + } + + // advance the state of all the objects + + for (; games_time_diff >= MCOM_CYCLE; games_time_diff -= MCOM_CYCLE) { + advance_mcom_state(); + if (ms->state == MCOM_PLAY_GAME) { + if ((rand() % 1000) < (ms->level + 20)) + make_random_attacker(); + if (!(ms->guys & JUST_guys)) { + ms->state = MCOM_WAIT_NEW_GAME; + if (ms->score > HISCORE) + HISCORE = ms->score; + } + } + } + + // advance the game state if they're done + if (ms->state == MCOM_PLAY_GAME && !ms->enemies && !num_explosions && !num_attackers && !num_missiles) { + // bonuses + ms->score += (ms->lmissiles + ms->rmissiles) * SCORE_PER_LEFTOVER_SHOT; + ms->score += hack[ms->guys & 3u] + hack[(ms->guys >> 3u) & 3u] + hack[(ms->guys >> 6u) & 3u]; + ++ms->state; + } + + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + // autoreexpose + mfd_notify_func(MFD_GAMES_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); +} + +#ifdef LOST_TREASURES_OF_MFD_GAMES +//---------------------------- +//---------------------------- +// mfd 15-sliding-tile puzzle +//---------------------------- +//---------------------------- +#define MFD_PUZZLE_SIZE 4 +#define MFD_PUZZLE_SQ (MFD_PUZZLE_SIZE * MFD_PUZZLE_SIZE) +#define PUZZ15_TILE_SIZE (p15_styles[((puzzle15_state *)GAME_DATA)->style].tsize) +#define PUZZ15_ULX ((MFD_VIEW_WID - (MFD_PUZZLE_SIZE * PUZZ15_TILE_SIZE)) / 2) +#define PUZZ15_ULY ((MFD_VIEW_HGT - (MFD_PUZZLE_SIZE * PUZZ15_TILE_SIZE)) / 2) +#define PUZZ15_CYCLE ((CIT_CYCLE / 2) / PUZZ15_TILE_SIZE) +#define PUZZ15_INIT_SCRAM (4 * PUZZLE_DIFFICULTY + 5) +#define PUZZ15_WID (MFD_PUZZLE_SIZE * PUZZ15_TILE_SIZE) +#define PUZZ15_HGT (MFD_PUZZLE_SIZE * PUZZ15_TILE_SIZE) +#define PUZZ15_AFRAME_CYCLE (CIT_CYCLE) +#define NUM_PUZZ15_STYLES 5 + +typedef struct { + uchar game_mode; + uchar tilenum[MFD_PUZZLE_SQ]; + uchar current_frame; + uchar anim_source; + uchar anim_dir; + uchar style; + uchar scramble; + uchar movedto; + uchar animframe; + uchar pause; +} puzzle15_state; + +typedef struct { + uchar bcolor; + uchar fcolor; + Ref back; + uchar numbers; + uchar tsize; + uchar animating; +} puzz15_style; + +static puzz15_style p15_styles[NUM_PUZZ15_STYLES] = { + {0x60, 0xB0, REF_IMG_EmailMugShotBase + 11, FALSE, 13, FALSE}, + {0x60, 0xB0, REF_IMG_EmailMugShotBase + 23, FALSE, 13, FALSE}, + {0xDE, 0x02, REF_IMG_TriopLogo15, FALSE, 8, FALSE}, + {0xDE, 0x02, REF_IMG_DiegoAnim15, FALSE, 8, TRUE}, + {0x4C, 0x01, 0, TRUE, 13, FALSE}, +}; + +void games_init_15(void *game_state) { + puzzle15_state *state = (puzzle15_state *)game_state; + int i; + + for (i = 0; i < MFD_PUZZLE_SQ; i++) + state->tilenum[i] = i + 1; + state->tilenum[MFD_PUZZLE_SQ - 1] = 0; + state->anim_source = MFD_PUZZLE_SQ; + state->style = rand() % NUM_PUZZ15_STYLES; + state->scramble = PUZZ15_INIT_SCRAM; + games_time_diff = 0; +} + +static uchar puzz15_won() { + puzzle15_state *st = (puzzle15_state *)GAME_DATA; + int i; + + for (i = 0; i < MFD_PUZZLE_SQ - 1; i++) { + if (st->tilenum[i] != (i + 1)) + return (FALSE); + } + return (TRUE); +} + +static void puzz15_xy(int ind, int *x, int *y) { + int r, c; + r = ind / MFD_PUZZLE_SIZE; + c = ind % MFD_PUZZLE_SIZE; + *x = PUZZ15_ULX + (c * PUZZ15_TILE_SIZE); + *y = PUZZ15_ULY + (r * PUZZ15_TILE_SIZE); +} + +static uchar puzz15_move(int x, int y) { + puzzle15_state *st = (puzzle15_state *)GAME_DATA; + int dir = -1, ind; + + ind = x + y * MFD_PUZZLE_SIZE; + + if (x > 0 && st->tilenum[ind - 1] == 0) + dir = 3; + else if (y > 0 && st->tilenum[ind - MFD_PUZZLE_SIZE] == 0) + dir = 0; + else if (x < MFD_PUZZLE_SIZE - 1 && st->tilenum[ind + 1] == 0) + dir = 1; + else if (y < MFD_PUZZLE_SIZE - 1 && st->tilenum[ind + MFD_PUZZLE_SIZE] == 0) + dir = 2; + + if (dir == -1) + return FALSE; + + st->anim_source = y * MFD_PUZZLE_SIZE + x; + st->current_frame = 0; + st->anim_dir = dir; + + return TRUE; +} + +void games_expose_15(MFD *m, ubyte control) { + int i, x, y, t, dx, dy, dt; + short sw, sh; + uchar rex = FALSE; + puzzle15_state *st = (puzzle15_state *)GAME_DATA; + char buf[3]; + int cycle = PUZZ15_CYCLE, aframe; + Ref back; + uchar full, solv, nums = p15_styles[st->style].numbers; + + full = (control & MFD_EXPOSE_FULL); + + if (st->scramble > 0) { + cycle /= 3; + if (st->anim_source == MFD_PUZZLE_SQ) { + do { + x = rand() % MFD_PUZZLE_SIZE; + y = rand() % MFD_PUZZLE_SIZE; + if ((x + y * MFD_PUZZLE_SIZE) != st->movedto) + rex = puzz15_move(x, y); + } while (!rex); + st->scramble--; + } + } + + if (p15_styles[st->style].animating) { + rex = TRUE; + aframe = (player_struct.game_time / PUZZ15_AFRAME_CYCLE) % 4; + if (aframe != st->animframe) { + st->animframe = aframe; + full = TRUE; + } + } + + back = p15_styles[st->style].back; + if (back) + back += st->animframe; + solv = st->pause && back; + if (full) { + if (!full_game_3d) + draw_res_bm(REF_IMG_bmBlankMFD, 0, 0); + gr_set_fcolor(0xBF); + ss_rect(PUZZ15_ULX - 2, PUZZ15_ULY - 2, PUZZ15_ULX + PUZZ15_WID + 2, PUZZ15_ULY + PUZZ15_HGT + 2); + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + } + for (i = 0; i < MFD_PUZZLE_SQ; i++) { + if (st->tilenum[i] == 0 && !(solv)) { + puzz15_xy(i, &x, &y); + gr_set_fcolor(0x1); + ss_rect(x, y, x + PUZZ15_TILE_SIZE, y + PUZZ15_TILE_SIZE); + mfd_add_rect(x, y, x + PUZZ15_TILE_SIZE, y + PUZZ15_TILE_SIZE); + } + } + for (i = 0; i < MFD_PUZZLE_SQ; i++) { + // draw background + puzz15_xy(i, &x, &y); + dx = dy = 0; + t = st->tilenum[i]; + if (t != 0 || (solv)) { + if (i == st->anim_source) { + switch (st->anim_dir) { + case 0: + dy = st->current_frame; + break; + case 1: + dx = -st->current_frame; + break; + case 2: + dy = -st->current_frame; + break; + case 3: + dx = st->current_frame; + break; + } + gr_set_fcolor(0x1); + ss_rect(x, y, x + PUZZ15_TILE_SIZE, y + PUZZ15_TILE_SIZE); + x -= dx; + y -= dy; + } + if (dx || dy || full) { + // draw background + gr_set_fcolor(0x1); + ss_rect(x + dx, y + dy, x + dx + PUZZ15_TILE_SIZE, y + dy + PUZZ15_TILE_SIZE); + mfd_add_rect(x + dx, y + dy, x + dx + PUZZ15_TILE_SIZE, y + dy + PUZZ15_TILE_SIZE); + gr_set_fcolor(p15_styles[st->style].bcolor + ((i + (i / MFD_PUZZLE_SIZE)) & 1)); + ss_rect(x, y, x + PUZZ15_TILE_SIZE, y + PUZZ15_TILE_SIZE); + // draw pretty bitmap + if (back) { + int bx, by, bw, bh; + ss_safe_set_cliprect(x, y, x + PUZZ15_TILE_SIZE, y + PUZZ15_TILE_SIZE); + puzz15_xy((t == 0 ? MFD_PUZZLE_SQ : t) - 1, &bx, &by); + bw = res_bm_width(back); + bh = res_bm_height(back); + draw_res_bm(back, PUZZ15_ULX + x - bx + (PUZZ15_WID - bw) / 2, + PUZZ15_ULY + y - by + (PUZZ15_HGT - bh) / 2); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + } + // draw number + if (nums) { + gr_set_fcolor(p15_styles[st->style].fcolor); + sprintf(buf, "%d", t); + gr_string_size(buf, &sw, &sh); + sw--; // assume blank pixel of kerning. + sh--; // assume pixel descender. + ss_string(buf, x + (PUZZ15_TILE_SIZE - sw) / 2, y + (PUZZ15_TILE_SIZE - sh) / 2); + } + mfd_add_rect(x, y, x + PUZZ15_TILE_SIZE, y + PUZZ15_TILE_SIZE); + } + } + } + + rex = rex || (st->anim_source < MFD_PUZZLE_SQ); + for (; games_time_diff >= cycle; games_time_diff -= cycle) { + if (st->anim_source < MFD_PUZZLE_SQ) { + if (st->current_frame == PUZZ15_TILE_SIZE) { + st->current_frame = 0; + dt = (st->anim_dir & 1u) ? 1 : -MFD_PUZZLE_SIZE; + if (st->anim_dir > 1) + dt = -dt; + st->movedto = st->anim_source + dt; + st->tilenum[st->movedto] = st->tilenum[st->anim_source]; + st->tilenum[st->anim_source] = 0; + st->anim_source = MFD_PUZZLE_SQ; + mfd_notify_func(MFD_GAMES_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, TRUE); + if (puzz15_won()) { + st->pause = TRUE; + return; + } + } else + st->current_frame++; + } + } + + // autoreexpose + if (rex) + mfd_notify_func(MFD_GAMES_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); +} + +uchar games_handle_15(MFD *m, uiEvent *e) { + puzzle15_state *st = (puzzle15_state *)GAME_DATA; + LGPoint pos = MakePoint(e->pos.x - m->rect.ul.x - PUZZ15_ULX, e->pos.y - m->rect.ul.y - PUZZ15_ULY); + + if (st->scramble > 0) + return FALSE; + + if (!(e->mouse_data.action & MOUSE_LDOWN)) + return FALSE; + + if (pos.x < 0 || pos.y < 0) + return TRUE; + + pos.x /= PUZZ15_TILE_SIZE; + pos.y /= PUZZ15_TILE_SIZE; + + if (pos.x >= MFD_PUZZLE_SIZE || pos.y >= MFD_PUZZLE_SIZE) + return TRUE; + if (st->pause) { + st->pause = FALSE; + st->scramble = PUZZ15_INIT_SCRAM; + mfd_notify_func(MFD_GAMES_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); + return TRUE; + } + if (st->anim_source < MFD_PUZZLE_SQ) + return TRUE; + + if (puzz15_move(pos.x, pos.y)) + mfd_notify_func(MFD_GAMES_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); + + return TRUE; +} + +//---------------------------- +//---------------------------- +// mfd Tic-Tac-Toe +//---------------------------- +//---------------------------- + +typedef struct { + uchar owner[9]; +} tictactoe; + +typedef struct { + uchar game_mode; + tictactoe board; + uchar whomoves; + uchar whoplayer; +} ttt_state; + +#define NOBODY 0 +#define X 1 +#define O 2 +#define OTHERPLAYER(w) (X + O - (w)) + +#define TTT_SQ_WID 20 +#define TTT_SQ_HGT 16 + +#define TTT_ULX ((MFD_VIEW_WID - (3 * TTT_SQ_WID)) / 2) +// this leaves a little room at the top for text, which +// we secretly declare is 6 pixels +#define TTT_MESS_HGT 6 +#define TTT_ULY (TTT_MESS_HGT + (MFD_VIEW_HGT - TTT_MESS_HGT - (3 * TTT_SQ_HGT)) / 2) +#define TTT_PUZ_WID (3 * TTT_SQ_WID) +#define TTT_PUZ_HGT (3 * TTT_SQ_HGT) +#define TTT_LRX (TTT_ULX + TTT_PUZ_WID) +#define TTT_LRY (TTT_ULY + TTT_PUZ_HGT) + +static void tictactoe_drawwin(ttt_state *st); +uchar tictactoe_generator(void *pos, int index, bool minimizer_moves); + +// ---------------------- +// TIC-TAC-TOE: +// static evaluator, move generator +// ---------------------- + +static int winnerval(uchar owner) { + if (owner == X) + return INT_MAX; + else if (owner == O) + return INT_MIN; + else + return 0; +} + +static uchar tictactoe_over(tictactoe *st) { + int i, val; + + val = tictactoe_evaluator(st); + if (val == winnerval(X) || val == winnerval(O)) + return TRUE; + + for (i = 0; i < 9; i++) { + if (st->owner[i] == NOBODY) + return FALSE; + } + return TRUE; +} + +static char corners_ttt[] = {0, 2, 6, 8}; + +void games_init_ttt(void *game_state) { + ttt_state *state = (ttt_state *)game_state; + + state->whomoves = X; + state->whoplayer = (rand() & 1) ? X : O; + + fstack_init(hideous_secret_game_storage + sizeof(ttt_state), + sizeof(hideous_secret_game_storage) - sizeof(ttt_state)); + if (state->whoplayer != state->whomoves) { + // fake straight to a corner move + state->board.owner[corners_ttt[rand() & 3]] = state->whomoves; + state->whomoves = state->whoplayer; + } +} + +static char initmove_ttt[] = {0, 1, 4}; + +static int ttt_fullness(tictactoe *st) { + int i, ret = 0; + + for (i = 0; i < 9; i++) { + if (st->owner[i] != NOBODY) + ret++; + } + return ret; +} + +static char move_to_index(char move, tictactoe *st) { + int i; + uchar empty; + + empty = (ttt_fullness(st) == 0); + if (empty) + return initmove_ttt[move]; + + for (i = 0; i < 9 && move >= 0; i++) { + if (st->owner[i] == NOBODY) { + if (move == 0) + return i; + move--; + } + } + return -1; +} + +void games_expose_ttt(MFD *m, ubyte control) { + uchar full; + int val; + static long timeformove = 0, dt, timeout; + char whichmove; + ttt_state *st = (ttt_state *)GAME_DATA; + int loops = 0; + + full = (control & MFD_EXPOSE_FULL); + + if (full) { + int x, y; + uchar over; + uchar owner; + Ref bm; + + if (!full_game_3d) + draw_res_bm(REF_IMG_bmBlankMFD, 0, 0); + gr_set_fcolor(RED_8_BASE + 4); + over = tictactoe_over(&(st->board)); + if (!over) { + // note that we are shamelessly using "bm" to temporarily + // house a string. Sue me. + if (st->whomoves == st->whoplayer) + bm = REF_STR_YourMove; + else + bm = REF_STR_Thinking; + draw_shadowed_text(get_temp_string(bm), (MFD_VIEW_WID - gr_string_width(get_temp_string(bm))) / 2, 1); + } + // Hmm, this used to be gr_uvline.... maybe insufficent to just make it clipped. + ss_vline(TTT_ULX + TTT_SQ_WID, TTT_ULY, TTT_ULY + TTT_PUZ_HGT); + ss_vline(TTT_ULX + 2 * TTT_SQ_WID, TTT_ULY, TTT_ULY + TTT_PUZ_HGT); + ss_int_line(TTT_ULX, TTT_ULY + TTT_SQ_HGT, TTT_ULX + TTT_PUZ_WID, TTT_ULY + TTT_SQ_HGT); + ss_int_line(TTT_ULX, TTT_ULY + 2 * TTT_SQ_HGT, TTT_ULX + TTT_PUZ_WID, TTT_ULY + 2 * TTT_SQ_HGT); + for (y = 0; y < 3; y++) { + for (x = 0; x < 3; x++) { + owner = st->board.owner[x + 3 * y]; + if (owner == NOBODY) + bm = ID_NULL; + else if (owner == st->whoplayer) + bm = REF_IMG_ttt_Player; + else + bm = REF_IMG_ttt_Shodan; + if (bm != ID_NULL) + draw_res_bm(bm, 1 + TTT_ULX + TTT_SQ_WID * x, 1 + TTT_ULY + TTT_SQ_HGT * y); + } + } + if (over) { + gr_set_fcolor(BLUE_8_BASE + 2); + tictactoe_drawwin(st); + } + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + } + if (st->whomoves == st->whoplayer) + return; + if (tictactoe_over(&(st->board))) + return; + + // give us enough time to still achieve 15 fps + dt = (CIT_CYCLE / 15) - player_struct.deltat; + // or give us at least 1/55 second per frame. + if (dt < (CIT_CYCLE / 55)) + dt = CIT_CYCLE / 55; + + timeformove += dt; + + if (timeformove <= 0) + return; + + timeout = *tmd_ticks + timeformove; + + while (*tmd_ticks < timeout) { + minimax_step(); + loops++; + if (minimax_done()) + timeout = *tmd_ticks; + } + timeformove = timeout - *tmd_ticks; + + if (minimax_done()) { + minimax_get_result(&val, &whichmove); + st->board.owner[move_to_index(whichmove, &(st->board))] = OTHERPLAYER(st->whoplayer); + st->whomoves = st->whoplayer; + mfd_notify_func(MFD_GAMES_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, TRUE); + } else + mfd_notify_func(MFD_GAMES_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); +} + +int tictactoe_evaluator(void *pos) { + tictactoe *t = (tictactoe *)pos; + uchar win; + + win = t->owner[0]; + if (win != NOBODY) { + if (t->owner[1] == win && t->owner[2] == win) + return winnerval(win); + if (t->owner[3] == win && t->owner[6] == win) + return winnerval(win); + } + + win = t->owner[8]; + if (win != NOBODY) { + if (t->owner[6] == win && t->owner[7] == win) + return winnerval(win); + if (t->owner[2] == win && t->owner[5] == win) + return winnerval(win); + } + + win = t->owner[4]; + if (win != NOBODY) { + if (t->owner[3] == win && t->owner[5] == win) + return winnerval(win); + if (t->owner[1] == win && t->owner[7] == win) + return winnerval(win); + if (t->owner[0] == win && t->owner[8] == win) + return winnerval(win); + if (t->owner[6] == win && t->owner[2] == win) + return winnerval(win); + } + + return winnerval(NOBODY); +} + +// note that this procedure duplicates a lot of the work done by +// tictactoe_evaluator: we do not consolidate them because we don't +// want to slow down the evaluator. +void tictactoe_drawwin(ttt_state *st) { + uchar win, realwin; + int i; + LGPoint p1, p2; + char buf[80]; + tictactoe *t = &(st->board); + + p1.x = -1; + + for (i = 0; i < 3; i++) { + win = t->owner[i]; + if (t->owner[i + 3] == win && t->owner[i + 6] == win) { + realwin = win; + p1.x = TTT_ULX + (TTT_SQ_WID * i) + (TTT_SQ_WID / 2); + p1.y = TTT_ULY; + p2.x = p1.x; + p2.y = TTT_LRY; + } + } + for (i = 0; i < 9; i += 3) { + win = t->owner[i]; + if (t->owner[i + 1] == win && t->owner[i + 2] == win) { + realwin = win; + p1.x = TTT_ULX; + p1.y = TTT_ULY + (TTT_SQ_HGT * i / 3) + (TTT_SQ_HGT / 2); + p2.x = TTT_LRX; + p2.y = p1.y; + } + } + win = t->owner[0]; + if (t->owner[4] == win && t->owner[8] == win) { + realwin = win; + p1.x = TTT_ULX; + p1.y = TTT_ULY; + p2.x = TTT_LRX; + p2.y = TTT_LRY; + } + win = t->owner[6]; + if (t->owner[4] == win && t->owner[2] == win) { + realwin = win; + p1.x = TTT_ULX; + p1.y = TTT_LRY; + p2.x = TTT_LRX; + p2.y = TTT_ULY; + } + if (p1.x > 0) { + ss_int_line(p1.x, p1.y, p2.x, p2.y); + sprintf(buf, "%s%s", + realwin == st->whoplayer ? (char *)RefGet(REF_STR_YouHave) : (char *)RefGet(REF_STR_ComputerHas), + (char *)RefGet(REF_STR_Won)); + draw_shadowed_text(buf, MFD_VIEW_WID - gr_string_width(buf) - 1, 1); + } +} + +uchar tictactoe_generator(void *pos, int index, bool minimizer_moves) { + tictactoe *t = (tictactoe *)pos; + uchar empty = TRUE; + uchar mover = minimizer_moves ? O : X; + + int realindex = index; + + if (tictactoe_evaluator(pos) != winnerval(NOBODY)) + return FALSE; // already have a winner => no children + +#define NO_SYMMETRIES +#ifdef NO_SYMMETRIES + for (uint8_t i = 0; empty && i < 9; i++) { + if (t->owner[i] != NOBODY) + empty = FALSE; + } + + // don't bother with symmetries of starting moves + if (empty) { + switch (index) { + case 0: + t->owner[0] = mover; + return TRUE; + case 1: + t->owner[1] = mover; + return TRUE; + case 2: + t->owner[4] = mover; + return TRUE; + default: + return FALSE; + } + } +#endif + + for (uint8_t i = 0; i < 9; i++) { + if (t->owner[i] == NOBODY) { + if (index == 0) { + t->owner[i] = mover; + return TRUE; + } + index--; + } + } + return FALSE; +} + +uchar games_handle_ttt(MFD *m, uiEvent *e) { + ttt_state *st = (ttt_state *)GAME_DATA; + LGPoint pos = MakePoint(e->pos.x - m->rect.ul.x - TTT_ULX, e->pos.y - m->rect.ul.y - TTT_ULY); + + if (!(e->mouse_data.action & MOUSE_LDOWN)) + return FALSE; + if (st->whomoves != st->whoplayer) + return TRUE; + if (tictactoe_over(&(st->board))) + return TRUE; + if (pos.x < 0 || pos.y < 0) + return TRUE; + + pos.x /= TTT_SQ_WID; + pos.y /= TTT_SQ_HGT; + + if (pos.x >= TTT_PUZ_WID || pos.y >= TTT_PUZ_HGT) + return TRUE; + + if (st->board.owner[pos.x + 3 * pos.y] != NOBODY) + return TRUE; + + st->board.owner[pos.x + 3 * pos.y] = st->whoplayer; + + if (!tictactoe_over(&(st->board))) { + st->whomoves = OTHERPLAYER(st->whoplayer); + + minimax_setup(&(st->board), sizeof(tictactoe), 9, st->whomoves == O, tictactoe_evaluator, tictactoe_generator, + NULL); + } + + mfd_notify_func(MFD_GAMES_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, TRUE); + + return TRUE; +} + + // + // MFD Wing Commander + // + // use the position of the mouse to steer + // button 1 makes you fire; button 2 lets + // you control thrust and rotate view + // always fire towards middle + + // Object structure... this handles arbitrary object in 3d + + // We'll always track all objects in arbitrary player-centric + // viewspace, a la Star Raiders + +#define WING_QUEST_VAR 0x38 + +#define WING_SFX_GOODGUY_FIRE SFX_GUN_SKORPION +#define WING_SFX_BADGUY_FIRE SFX_GUN_STUNGUN + +#define WING_SFX_HIT_PLAYER SFX_METAL_SPANG +#define WING_SFX_HIT_OTHER SFX_GUN_PIPE_HIT_METAL + +#define WING_SFX_EXPLODE SFX_CPU_EXPLODE + +#define WING_SFX_COMM SFX_MFD_SUCCESS +#define WING_SFX_AUTOPILOT SFX_SHIELD_UP +#define WING_SFX_THEYRE_ATTACKING_US_SIR 113 + +#define WING_HEAR_EXPLODE 512 +#define WING_HEAR_HIT 256 +#define WING_HEAR_FIRE 192 + +typedef struct { + fix x, y, z; // coordinates in 3space + fix dx, dy, dz; // velocity in 3space for objects + int type; // what typa object is it + int damage; // how much damage 'til it blows +} wing_obj; + +typedef struct { + fix x, y, z; + int color; +} wing_star; + +#define MAX_WING_OBJECTS ((HIDEOUS_GAME_STORAGE - 512) / sizeof(wing_obj)) +#define MAX_WING_STARS (512 / sizeof(wing_star)) + +wing_obj *wing = (wing_obj *)(hideous_secret_game_storage + 4); +wing_star *wing_st = (wing_star *)(hideous_secret_game_storage + HIDEOUS_GAME_STORAGE - 512 + 4); + +// so I note that all of these variables should be in the +// player struct, that is in the mfd game state, and I'll +// fix that later. +enum WingmanMode { WINGMAN_FORMATION, WINGMAN_ATTACK }; + +#if 0 + +static int WingmanMode wingman_mode = WINGMAN_FORMATION; +static int num_wing_objects, wing_frame_count; +static int wing_game_mode = WING_BRIEFING, wing_level; +static int wing_message, wing_message_timer; + +#else + +struct wing_data { + uchar game_mode; + uchar wd_wingman_mode; + uchar wd_num_wing_objects; + uchar wd_wing_frame_count; + uchar wd_wing_game_mode; + uchar wd_wing_level; + uchar wd_wing_message; + uchar wd_wing_message_timer; +}; + +#define WING_DATA ((struct wing_data *)GAME_DATA) + +#define wingman_mode (WING_DATA->wd_wingman_mode) +#define num_wing_objects (WING_DATA->wd_num_wing_objects) +#define wing_frame_count (WING_DATA->wd_wing_frame_count) +#define wing_game_mode (WING_DATA->wd_wing_game_mode) +#define wing_level (WING_DATA->wd_wing_level) +#define wing_message (WING_DATA->wd_wing_message) +#define wing_message_timer (WING_DATA->wd_wing_message_timer) + +#endif + +#ifdef PLAYTEST +static int wing_cheat = 0; +#endif + +enum WingTypes { WING_BLUE_HAIR, WING_SHOT, WING_BOOM, WING_WINGMAN, WING_BADGUY, WING_BADGUY2, WING_BADGUY3 }; + +#define WING_GOODGUY_MASK ((1 << WING_BLUE_HAIR) | (1 << WING_WINGMAN)) +#define WING_BADGUY_MASK (0xff << WING_BADGUY) + +enum WingGameMode { WING_PLAY_GAME, WING_BRIEFING, WING_DEBRIEFING, WING_FLYBY, WING_YOUDIED }; + +static int create_wing_object(int type, int dam, fix x, fix y, fix z) { + int i; + if (num_wing_objects < MAX_WING_OBJECTS) { + i = num_wing_objects++; + wing[i].type = type; + wing[i].damage = dam; + wing[i].x = x; + wing[i].y = y; + wing[i].z = z; + } else + i = -1; + return i; +} + +static void wing_delete_all_but(void) { + // delete everything other than wingman + int i = 1; + while (i < num_wing_objects) + if (wing[i].type == WING_WINGMAN) + ++i; + else + wing[i] = wing[--num_wing_objects]; +} + +// All the things that determine units, the scale of things, +// should go in this section for ease of manipulation + +static fix wing_velocity[] = { + fix_make(2, 0), // BLUE_HAIR + fix_make(8, 0), // SHOT + fix_make(0, 0), // BOOM + fix_make(4, 0), // WINGMAN + fix_make(3, 0), // BADGUY + fix_make(2, 0x8000), // BADGUY2 + fix_make(5, 0) // BADGUY3 +}; + +static int wing_damage_amount[] = {20, 1, + 20, // countdown timer for explosion + 20, 8, 12, 16}; + +#define WING_FIRING_RANGE fix_make(200, 0) +#define WING_TRAIL 32 // 32 frames behind other person +#define FORMATION fix_make(48, 0) +#define WING_HIT_DISTANCE fix_make(14, 0) // was 20 + +#define SHOT_TIME 30 +#define SHOT_VALID_TIME 27 +#define WING_ANIM_CYCLE (CIT_CYCLE / 4) +#define WING_CYCLE (CIT_CYCLE / 30) + +#define WING_PHASES 4 + +#define WING_NUM_MISSIONS 13 +#define GHANDI_LEVEL(x) ((x) >= 4 * WING_PHASES && (x) < 5 * WING_PHASES) + +// Wing commander "levels" + +#define W_BAD1 1 +#define W_BAD2 8 +#define W_BAD3 64 + +uchar wing_level_data[] = { + // ? sector + W_BAD1, W_BAD1, 0, W_BAD1, W_BAD1, 0, W_BAD1, 2 * W_BAD1, W_BAD1 * 2, W_BAD1, W_BAD1, W_BAD1 * 4, + + // Scary Sector + 0, 0, W_BAD1 * 2, W_BAD1 + W_BAD2, 0, W_BAD1 * 2, 0, W_BAD1 + W_BAD2 * 2, W_BAD2 * 2, 0, W_BAD1, W_BAD1 * 3, + W_BAD1 * 2, 0, W_BAD1 * 3 + W_BAD2, W_BAD1 * 2 + W_BAD2 * 2, + + // Spilt Milk Sector + W_BAD1 * 2, 0, W_BAD1 * 2 + W_BAD2 * 2, W_BAD1 * 2, 0, W_BAD1 * 2 + W_BAD2, W_BAD2 * 3, 0, W_BAD1 + W_BAD2 * 2, + W_BAD1 * 3, W_BAD1 * 4 + W_BAD2 * 2, W_BAD1 * 3 + W_BAD2 * 4 + W_BAD3 * 3, + + // Tiger sector + 0, W_BAD2 * 4, 0, W_BAD1 * 2 + W_BAD3 * 2, 0, 0, 0, W_BAD1 * 7 + W_BAD2 * 5 + W_BAD3 * 3, W_BAD1 * 4, + W_BAD1 * 3 + W_BAD2 * 2, W_BAD1 * 2 + W_BAD2 * 3, W_BAD1 * 2 + W_BAD2 * 2 + W_BAD3 * 3}; + +uchar wing_wingmen[WING_NUM_MISSIONS] = {1, 1, 1, // ? Sector + 0, 1, 1, 1, // Scary sector + 1, 1, 2, 1, 6, 0}; + +#define LEVEL_WINGMAN_COUNT() wing_wingmen[wing_level / WING_PHASES] + +enum WingMessage { + WING_SILENT = 0, + WING_SAYS_SIGHTED, + WING_SAYS_DIE, + WING_SAYS_ATTACK, + WING_SAYS_FORM, + WING_NO_WINGMAN +}; + +#define WING_MESSAGE_COUNT 30 + +static fix wing_distance(fix a, fix b, fix c) { + fix t; + + a = abs(a); + b = abs(b); + c = abs(c); + t = a > b ? a + b / 2 : b + a / 2; + t = c > t ? c + t / 2 : t + c / 2; + + return t; +} + +static void wing_play_fx(int sound, int obj, int radius) { + if (sound == SFX_NONE) + return; + if (obj != 0) { + if (fix_int(wing_distance(wing[obj].x, wing[obj].y, wing[obj].z)) > radius) + return; + } + play_digi_fx(sound, 1); +} + +static void wing_set_message(int mess) { + wing_message = mess; + wing_message_timer = ((mess == WING_SILENT) ? 0 : WING_MESSAGE_COUNT); + if (mess == WING_SAYS_SIGHTED && wing_level < WING_PHASES) { + wing_play_fx(WING_SFX_THEYRE_ATTACKING_US_SIR, 0, 0); + } +} + +static int wing_find_wingman(int i) { + for (++i; i < num_wing_objects; ++i) + if (wing[i].type == WING_WINGMAN) + break; + return i == num_wing_objects ? 0 : i; +} + +static void wingman_order(void) { + int i = wing_find_wingman(0); + if (!i) + wing_set_message(WING_NO_WINGMAN); + else if (wingman_mode == WINGMAN_FORMATION) { + wingman_mode = WINGMAN_ATTACK; + wing_set_message(WING_SAYS_ATTACK); + } else { + wingman_mode = WINGMAN_FORMATION; + wing_set_message(WING_SAYS_FORM); + } +} + +static int wing_any_enemies(void) { + int i; + for (i = 1; i < num_wing_objects; ++i) + if (wing[i].type >= WING_BADGUY || wing[i].type == WING_BOOM) + return 1; + return 0; +} + +// +// Wing Commander AIs +// +// These blow ping out of the water. You'll see. +// + +// macros to determine when the AI should act +#define wing_let_ai_fire(w) (!(wing_frame_count & 7) && ((w)->type != WING_WINGMAN || !GHANDI_LEVEL(wing_level))) +#define wing_change_ai_facing(w) (!(wing_frame_count & 15) && (rand() & 8)) + +// Manhattan transfer +// (once this used manhattan distance, now it uses "octagon" distance) +#define man_distance(w, a, b, c) wing_distance((w)->x - (a), (w)->y - (b), (w)->z - (c)) +#define man_next_to(w, q, a, b, c) man_distance(w, (q)->x + (a), (q)->y + (b), (q)->z + (c)) +#define man_guy(w, q) man_distance(w, (q)->x, (q)->y, (q)->z) + +// More interesting movement +#define random_vel_adjust() (fix_make(0, (rand() % 512 - 256) << 6)) + +static wing_obj *wing_find_nearest(wing_obj *w, int mask) { + int i; + fix d, e; + wing_obj *z = 0; + + d = 0x7fffffff; + + for (i = 0; i < num_wing_objects; ++i) { + if ((1u << wing[i].type) & mask) { + if (wing[i].type == WING_WINGMAN && GHANDI_LEVEL(wing_level)) + continue; + e = man_guy(w, &wing[i]) + (rand() % fix_make(4, 0)); + if (e < d) { + z = &wing[i]; + d = e; + } + } + } + return z; +} + +// routines for steering + +// we call this with a _valid_ x,y,z velocity for w, that is one +// that's not too fast. This routine then deals with rotation +// issues. +static void wing_try_for_velocity(wing_obj *w, fix x, fix y, fix z) { + // Basically, we only let one of x,y,z change signs at a time. + // We prioritize z, then x, then y, to cause things to go left/right + // more then up/down + + if ((z >= 0 && w->dz < 0) || (z <= 0 && w->dz > 0)) { + if (abs(w->dz) > FIX_UNIT) + z = 0; + w->dz = z; + } else if ((x >= 0 && w->dx < 0) || (x <= 0 && w->x > 0)) { + if (abs(w->dx) > FIX_UNIT) + x = 0; + w->dx = x; + w->dz = z; + if ((y >= 0 && w->dy < 0) || (y <= 0 && w->dy > 0)) { + w->dy = 0; + } else { + if (abs(w->dy) > FIX_UNIT) + y = 0; + w->dy = y; + } + } else { + if (abs(w->dy) > FIX_UNIT) + y = 0; + w->dx = x; + w->dy = y; + w->dz = z; + } + + w->dx += random_vel_adjust(); + w->dy += random_vel_adjust(); + w->dz += random_vel_adjust(); +} + +// convert (dx,dy,dz) to be of length (m) +// Someone tell me why I made this fast and approximate +// (note approximate square root and use of shifts instead +// of divides and multiplies) when it's an MFD game? +static void wing_scale_velocity(fix *dx, fix *dy, fix *dz, fix m) { + fix x = *dx, y = *dy, z = *dz; + fix v; + + // compute approximate velocity + v = wing_distance(x, y, z); + if (v == 0) { + *dx = x; + *dy = y; + *dz = z; + return; + } + + // scale to guy's maximum velocity + while (v < m / 2) + v *= 2, x *= 2, y *= 2, z *= 2; + while (v >= m) + v /= 2, x /= 2, y /= 2, z /= 2; + + if (v < m - m / 4) + v = v + v / 2, x = x + x / 2, y = y + y / 2, z = z + z / 2; + + *dx = x; + *dy = y; + *dz = z; +} + +static void wing_try_to_goto(wing_obj *w, fix x, fix y, fix z) { + // compute effective direction + x -= w->x; + y -= w->y; + z -= w->z; + + wing_scale_velocity(&x, &y, &z, wing_velocity[w->type]); + wing_try_for_velocity(w, x, y, z); +} + +static fix wing_vel, wing_acc; +static fixang wing_a, wing_b, wing_c; + +static void wing_fire_shot(wing_obj *w, int side) { + int i; + fix x, y, z; + // fire out the front of this ship at shot velocity + + if (w->type == WING_BLUE_HAIR) + i = create_wing_object(WING_SHOT, SHOT_TIME, w->x + w->dx, w->y + wing_vel, w->z + w->dz); + else + i = create_wing_object(WING_SHOT, SHOT_TIME, w->x + w->dx, w->y + w->dy, w->z + w->dz); + + if (i == -1) + return; + + if (w->type == WING_BLUE_HAIR) { + x = z = 0; + y = wing_velocity[WING_SHOT]; + } else { + x = w->dx; + y = w->dy; + z = w->dz; + wing_scale_velocity(&x, &y, &z, wing_velocity[WING_SHOT]); + if (x == 0 && y == 0 && z == 0) { + --num_wing_objects; + return; + } + } + + wing[i].x += (wing[i].dx = x) + y * side / 2; + wing[i].y += (wing[i].dy = y) + x * side / 2; + wing[i].z += (wing[i].dz = z) - FIX_UNIT * 4; +} + +static int wing_in_front_of(wing_obj *target, wing_obj *base) { + // if target is in front of base, then line from base to target + // is in same direction as velocity of base + + int x, y, z; + x = fix_int(target->x - base->x); + y = fix_int(target->y - base->y); + z = fix_int(target->z - base->z); + + x = x * fix_int(base->dx * 64); + y = y * fix_int(base->dy * 64); + z = z * fix_int(base->dz * 64); + + return (x + y + z > 0); +} + +static void wing_ai_fire(wing_obj *w, wing_obj *z) { + if (wing_in_front_of(z, w) && !(rand() % 4)) { + wing_play_fx(w->type == WING_WINGMAN ? WING_SFX_GOODGUY_FIRE : WING_SFX_BADGUY_FIRE, w - wing, WING_HEAR_FIRE); + wing_fire_shot(w, -1); + wing_fire_shot(w, 1); + } +} + +static void wing_do_ai(wing_obj *w) { + int mask; + wing_obj *z; + + switch (w->type) { + case WING_BLUE_HAIR: + return; + + case WING_SHOT: + case WING_BOOM: + --w->damage; // countdown until it is dead + break; + + case WING_WINGMAN: + switch (wingman_mode) { + case WINGMAN_FORMATION: + // Formation: + // If near enough to player, turn to face same direction + // else turn towards player's destination + + if (wing_change_ai_facing(w)) { + if (man_next_to(w, &wing[0], -FORMATION / 2, 0, 0) < FORMATION * 2) { + if (wing_vel > wing_velocity[w->type]) + wing_try_for_velocity(w, 0, wing_velocity[w->type], 0); + else + wing_try_for_velocity(w, 0, wing_vel, 0); + } else { + // We should try to lead player somewhat, but + // it depends how close we are. Hmm. + // We'll just go to where he was, and curve in. + wing_try_to_goto(w, -FORMATION / 2, 0, 0); + } + } + if (wing_let_ai_fire(w)) { + z = wing_find_nearest(w, WING_BADGUY_MASK); + if (z && man_guy(w, z) < WING_FIRING_RANGE) + wing_ai_fire(w, z); + } + break; + + case WINGMAN_ATTACK: + mask = WING_BADGUY_MASK; + goto attack_ai; + } + break; + + default: + mask = WING_GOODGUY_MASK; + // fallthrough + + attack_ai: + z = wing_find_nearest(w, mask); + if (z == 0 && w->type == WING_WINGMAN) { + wingman_mode = WINGMAN_FORMATION; + if (!(rand() % 4)) + wing_set_message(WING_SAYS_FORM); + } + if (z && wing_change_ai_facing(w)) { + // if we're not in firing range, just move towards + // otherwise if we're behind him turn to face him + if (man_guy(w, z) < WING_FIRING_RANGE) + // seems better not to do this || !wing_in_front_of(w,z)) + wing_try_to_goto(w, z->x, z->y, z->z); + else + // otherwise try to get behind him + wing_try_to_goto(w, z->x - WING_TRAIL * (z->dx + z->dy / 2), z->y - WING_TRAIL * (z->dy - z->dx / 2), + z->z - WING_TRAIL * z->dz); + } + if (z && man_guy(w, z) < WING_FIRING_RANGE && wing_let_ai_fire(w)) { + wing_ai_fire(w, z); + } + // end of cases of object types + } +} + +static void wing_rotate_vector(fix *v, fix sina, fix cosa, fix sinb, fix cosb, fix sinc, fix cosc) { + fix x, y, z; + + y = fix_mul(cosa, v[1]) + fix_mul(sina, v[2]); + z = fix_mul(cosa, v[2]) - fix_mul(sina, v[1]); + + v[1] = fix_mul(cosb, y) - fix_mul(sinb, v[0]); + x = fix_mul(cosb, v[0]) + fix_mul(sinb, y); + + v[2] = fix_mul(cosc, z) - fix_mul(sinc, x); + v[0] = fix_mul(cosc, x) + fix_mul(sinc, z); +} + +static void wing_move_world(fixang a, fixang b, fixang c, fix v) { + // move the world because player rotated by a & b + // and moved forward by velocity v + + int i; + fix sina, cosa, sinb, cosb, sinc, cosc; + fix_sincos(a, &sina, &cosa); + fix_sincos(b, &sinb, &cosb); + fix_sincos(c, &sinc, &cosc); + for (i = 1; i < num_wing_objects; ++i) { + + if (wing_game_mode == WING_PLAY_GAME) + wing_do_ai(&wing[i]); + + wing_rotate_vector(&wing[i].x, sina, cosa, sinb, cosb, sinc, cosc); + wing_rotate_vector(&wing[i].dx, sina, cosa, sinb, cosb, sinc, cosc); + + wing[i].x += wing[i].dx; + wing[i].y += wing[i].dy - v; // adjust for player's velocity + wing[i].z += wing[i].dz; + } + for (i = 0; i < MAX_WING_STARS; ++i) + wing_rotate_vector(&wing_st[i].x, sina, cosa, sinb, cosb, sinc, cosc); +} + +static void wing_handle_collisions(void) { + int i, j; + // delete any objects which are dead + i = 1; + while (i < num_wing_objects) + if (wing[i].damage <= 0) { + if (wing[i].type == WING_WINGMAN) + wing_set_message(WING_SAYS_DIE); + if (wing[i].type != WING_SHOT && wing[i].type != WING_BOOM) { + // turn guys into explosions + wing[i].type = WING_BOOM; + wing[i].damage = wing_damage_amount[wing[i].type]; + ++i; + wing_play_fx(WING_SFX_EXPLODE, i, WING_HEAR_EXPLODE); + } else + wing[i] = wing[--num_wing_objects]; + } else + ++i; + + for (j = 1; j < num_wing_objects; ++j) { + // check object j against all objects + // we only check for collisions of shots against ships + if (wing[j].type == WING_SHOT && wing[j].damage < SHOT_VALID_TIME) { + for (i = 0; i < num_wing_objects; ++i) { + if (wing[i].type != WING_SHOT && wing[i].type != WING_BOOM) { + // are they in range of each other? + if (man_guy(&wing[i], &wing[j]) < WING_HIT_DISTANCE) { + // do damage to both; shots always die + wing[i].damage -= wing_damage_amount[wing[j].type]; + wing[j].damage = 0; + wing_play_fx((i == 0) ? WING_SFX_HIT_PLAYER : WING_SFX_HIT_OTHER, i, WING_HEAR_HIT); + } + } + } + } + } +} + +static void wing_update_one_time_unit(void) { + for (; games_time_diff >= WING_CYCLE; games_time_diff -= WING_CYCLE) { + wing_vel += wing_acc; + if (wing_vel < 0) + wing_vel = 0; + if (wing_vel > wing_velocity[0]) + wing_vel = wing_velocity[0]; + + ++wing_frame_count; + + wing_move_world(wing_a, wing_b, wing_c, wing_vel); + wing_handle_collisions(); + } + if (wing[0].damage <= 0) + wing_game_mode = WING_YOUDIED; +} + +#define WING_FACE_FORWARD 0 +#define WING_FACE_LEFT 1 +#define WING_FACE_AWAY 2 +#define WING_FACE_RIGHT 3 +#define WING_FACE_UP 4 +#define WING_FACE_DOWN 5 + +static int wing_get_facing(int i) { + int dx = abs(wing[i].dx); + int dy = abs(wing[i].dy); + int dz = abs(wing[i].dz); + + if (dx > dy) + if (dx > dz) + return wing[i].dx > 0 ? WING_FACE_RIGHT : WING_FACE_LEFT; + else + return wing[i].dz > 0 ? WING_FACE_UP : WING_FACE_DOWN; + else if (dy > dz) + return wing[i].dy > 0 ? WING_FACE_AWAY : WING_FACE_FORWARD; + else + return wing[i].dz > 0 ? WING_FACE_UP : WING_FACE_DOWN; +} + +static void scale_res_bm(int ref, int x, int y, int w, int h) { + FrameDesc *f; + + f = RefLock(ref); + if (!f) + return; + + f->bm.bits = (uchar *)(f + 1); + ss_scale_bitmap(&f->bm, x, y, w, h); + + RefUnlock(ref); +} + +#define BRIEF_X 2 +#define BRIEF_Y (MFD_VIEW_HGT - 6) + +#define WING_NUMCHARS 26 + +// the cleverer flow-text-up-from-the-bottom- +// so-that-it-always-fits routine + +static void wing_print_message(char *s, int y) { + char *t, *u, c; + int x; + + u = s + strlen(s) - 1; + while (u - s + 1 > WING_NUMCHARS / 2) { + // u points to the last character which hasn't been plotted + // find last whitespace WING_NUMCHARS or fewer characters + if (u - WING_NUMCHARS < s) + t = s - 1; + else { + t = u - WING_NUMCHARS; + while (*t != ' ') { + if (!*t) + goto ouch; + ++t; + } + } + // print that much of s + c = u[1]; + u[1] = 0; + // wait, first let's check if that's too long + while (gr_string_width(t + 1) > MFD_VIEW_WID - BRIEF_X - 1) { + ++t; + while (*t != ' ') { + if (!*t) + goto ouch; + ++t; + } + } + if (0) { + ouch: + if (u - WING_NUMCHARS < s) + t = s - 1; + else + t = u - WING_NUMCHARS; + } + ss_string(t + 1, BRIEF_X, y); + u[1] = c; + u = t - 1; + y -= 5; + } + if (u >= s) { + c = u[1]; + u[1] = 0; + if (u - s < WING_NUMCHARS / 2) + x = (MFD_VIEW_WID - gr_string_width(s)) / 2 - BRIEF_X; + else + x = 0; + ss_string(s, BRIEF_X + x, y); + u[1] = c; + } +} + +#define WING_VIEW_ANGLE_SCALE_THING fix_make(32, 0) + +static int wing_radius[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 18, 21, 25, 28, 26, 22, 16, 1000}; + +static void wing_render_world(void) { + int i, sx, sy, k, j; + fix sc, q; + wing_obj temp; + + gr_set_fcolor(BLACK + 1); + ss_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + for (i = 0; i < MAX_WING_STARS; ++i) { + if (wing_st[i].y >= fix_make(0, 0x1000)) { + sc = fix_div(WING_VIEW_ANGLE_SCALE_THING, wing_st[i].y); + sx = fix_int(fix_mul(sc, wing_st[i].x)) + MFD_VIEW_MID; + sy = MFD_VIEW_HGT / 2 - fix_int(fix_mul(sc, wing_st[i].z)); + ss_set_pixel(WHITE, sx, sy); + } + } + + // depth sort the objects with an insertion sort + for (i = 2; i < num_wing_objects; ++i) { + temp = wing[i]; + q = temp.y; + j = i - 1; + while (j > 0) { + if (q < wing[j].y) + break; + wing[j + 1] = wing[j]; + --j; + } + wing[j + 1] = temp; + } + + for (i = 1; i < num_wing_objects; ++i) { + if (wing[i].y >= fix_make(0, 0x1000)) { + sc = fix_div(WING_VIEW_ANGLE_SCALE_THING, wing[i].y); + sx = fix_int(fix_mul(sc, wing[i].x)) + MFD_VIEW_MID; + sy = MFD_VIEW_HGT / 2 - fix_int(fix_mul(sc, wing[i].z)); + switch (wing[i].type) { + case WING_WINGMAN: + case WING_BLUE_HAIR: + k = fix_int(sc * 16) + 1; + scale_res_bm(REF_IMG_wing_ship + wing_get_facing(i), sx - k / 2, sy - k / 2, k, k); + break; + case WING_SHOT: + k = fix_int(sc * 1) + 1; + if (k > 1) + scale_res_bm(REF_IMG_DepthCharge, sx, sy, fix_int(sc * 1) + 1, fix_int(sc * 1) + 1); + else + ss_set_pixel(ORANGE_8_BASE, sx, sy); + break; + case WING_BOOM: + k = fix_int(sc * wing_radius[wing[i].damage]) + 1; + gr_set_fcolor(RED_8_BASE + 2 + rand() % 4); + ss_int_disk(sx, sy, k); + break; + + default: + k = fix_int(sc * 16) + 1; + scale_res_bm(REF_IMG_wing_bad1 + 6 * (wing[i].type - WING_BADGUY) + wing_get_facing(i), sx - k / 2, + sy - k / 2, k, k); + } + } + } + + for (i = 1; i < num_wing_objects; ++i) + if (wing[i].type > WING_BOOM) { + sx = fix_rint(wing[i].x / 128) + 10; + sy = fix_rint(wing[i].y / 128) + 10; + if (sx < 0) + sx = 0; + else if (sx > 20) + sx = 20; + if (sy < 0) + sy = 0; + else if (sy > 20) + sy = 20; + ss_set_pixel((wing[i].type == WING_WINGMAN ? GREEN_8_BASE : RED_8_BASE) + 2, sx, MFD_VIEW_HGT - 1 - sy); + } + ss_set_pixel(GREEN_8_BASE + 3, 10, MFD_VIEW_HGT - 1 - 10); + + { + char buffer[16]; + sprintf(buffer, "%03d", wing_vel * 150 / wing_velocity[0]); + gr_set_fcolor(WHITE); + ss_string(buffer, MFD_VIEW_WID - 16, 0); + } + + if (wing_message) { + char *s; + int z = wing_level / WING_PHASES; + switch (wing_message) { + case WING_SAYS_SIGHTED: + s = STRING(WingSighted + z); + break; + case WING_SAYS_DIE: + s = STRING(WingDies + z); + break; + case WING_SAYS_ATTACK: + s = STRING(WingAttack + z); + break; + case WING_SAYS_FORM: + s = STRING(WingForm + z); + break; + case WING_NO_WINGMAN: + s = STRING(NoWing); + break; + } + gr_set_fcolor(GREEN_8_BASE); + wing_print_message(s, MFD_VIEW_HGT - 6); + + if (!--wing_message_timer) + wing_message = 0; + } +} + +// this should actually take as a parameter +static void wing_play_cutscene(char *s) { + int anim; + if (!full_game_3d) + draw_res_bm(REF_IMG_bmBlankMFD, 0, 0); + anim = (player_struct.game_time / WING_ANIM_CYCLE) % 3; + draw_res_bm(REF_IMG_GoofyNed + anim, 20, 2); + + // now display the text + gr_set_fcolor(GRAY_8_BASE + 1); + wing_print_message(s, BRIEF_Y); +} + +static void wing_start_minor_level(void) { + int i, j, wingman = 0, t, n; + wing_game_mode = WING_PLAY_GAME; + + // reset player speed + wing[0].dx = wing[0].dz = 0; + wing[0].dy = wing_vel = wing_velocity[WING_BLUE_HAIR] / 2; + + wing_a = wing_b = wing_c = 0; + wing_acc = 0; + + // delete everything other than wingman + wing_delete_all_but(); + + // rotate the stars + wing_move_world(0, 0x8000, 0, 0); + + i = wing_find_wingman(0); + n = 0; + while (i) { + wing[i].dx = wing[i].dz = 0; + wing[i].dy = wing[0].dy; + wing[i].y = wing[i].z = 0; + wing[i].x = -FORMATION / 3 + n++ * FORMATION / 2; + wingman = 1; + i = wing_find_wingman(i); + } + + // create the enemy + n = wing_level_data[wing_level]; + if (n && wingman) + wing_set_message(WING_SAYS_SIGHTED); + else + wing_set_message(WING_SILENT); + + t = WING_BADGUY; + while (n) { + for (i = 0; i < (n & 7); ++i) { + j = create_wing_object(t, wing_damage_amount[t], fix_make(rand() % 512 - 256, 0), + fix_make(rand() % 64 + 768, 0), fix_make(rand() % 512 - 256, 0)); + if (j != -1) { + wing[j].dx = (rand() % 512 - 256) * 256; + wing[j].dy = (rand() % 512 - 256) * 256; + wing[j].dz = (rand() % 512 - 256) * 256; + } + } + n >>= 3u; + ++t; + } + + wingman_mode = WINGMAN_FORMATION; + games_time_diff = 0; +} + +static void wing_start_major_level(void) { + int j; + + // reset all objects so player has new damage amount + // and so there's always a wingman if there should be + num_wing_objects = 0; + // allocate player object + j = create_wing_object(WING_BLUE_HAIR, wing_damage_amount[WING_BLUE_HAIR], 0, 0, 0); + wing_vel = wing_velocity[WING_BLUE_HAIR] / 2; + wing_acc = 0; + + wing[j].dx = wing[j].dz = 0; + wing[j].dy = wing_vel; // so badguys can try to get behind you + + for (j = 0; j < LEVEL_WINGMAN_COUNT(); ++j) + create_wing_object(WING_WINGMAN, wing_damage_amount[WING_WINGMAN], -FORMATION / 2 - FORMATION, 0, 0); + + wing_start_minor_level(); +} + +static void wing_start_flyby(void) { + int i; + + wing_play_fx(WING_SFX_AUTOPILOT, 0, 0); + wing_delete_all_but(); + wing_move_world(0, 0x8000, 0, 0); + // create a dummy object for the player + + i = create_wing_object(WING_BLUE_HAIR, 1, 0, 0, 0); + + for (i = 1; i < num_wing_objects; ++i) { + wing[i].z = fix_make(6, 0) * (i - 1); + wing[i].y = fix_make(140, 0) - fix_make(8, 0) * i; + wing[i].x = fix_make(1, 0) + fix_make(35, 0) * i; + wing[i].dx = wing[i].dz = 0; + wing[i].dy = -fix_make(4, 0); + } + + wing_frame_count = 0; + wing_game_mode = WING_FLYBY; + games_time_diff = 0; +} + +static void wing_handle_flyby(void) { + wing_a = wing_c = 0; + wing_b = -0x1c0; + wing_vel = 0; + wing_update_one_time_unit(); + wing_render_world(); + + if (wing_frame_count > 64) { + wing_start_minor_level(); + } +} + +static void wing_advance_to_next_level(void) { + if (wing_level % WING_PHASES == WING_PHASES - 1) { + wing_game_mode = WING_DEBRIEFING; + QUESTVAR_SET(WING_QUEST_VAR, wing_level + 1); + } else { + ++wing_level; + wing_start_flyby(); + } +} + +uchar games_handle_wing(MFD *m, uiEvent *e) { + LGPoint pos = MakePoint(e->pos.x - m->rect.ul.x, e->pos.y - m->rect.ul.y); + fix x, y; + + if (wing_game_mode != WING_PLAY_GAME) { + if (!(e->mouse_data.action & MOUSE_LDOWN)) + return FALSE; + if (wing_game_mode == WING_DEBRIEFING) { + wing_game_mode = WING_BRIEFING; + ++wing_level; + return TRUE; + } + if (wing_game_mode == WING_BRIEFING) { + if (wing_level == WING_NUM_MISSIONS * WING_PHASES) { + QUESTVAR_SET(WING_QUEST_VAR, 0); + GAME_MODE = GAME_MODE_MENU; + return TRUE; + } + wing_start_major_level(); + return TRUE; + } + if (wing_game_mode == WING_YOUDIED) { + wing_level = QUESTVAR_GET(WING_QUEST_VAR); + wing_game_mode = WING_BRIEFING; + return TRUE; + } + return FALSE; + } + + x = (pos.x * FIX_UNIT * 2) / MFD_VIEW_WID - FIX_UNIT; + y = (pos.y * FIX_UNIT * 2) / MFD_VIEW_HGT - FIX_UNIT; + + if (abs(x) < fix_make(0, 4096)) + x = 0; + else + x = x + (x > 0 ? -4096 : 4096); + + if (abs(y) < fix_make(0, 4096)) + y = 0; + else + y = y + (y > 0 ? -4096 : 4096); + +#ifdef PLAYTEST + if (wing_cheat && (e->mouse_data.action & MOUSE_RDOWN) && (e->mouse_data.buttons & 3) == 3) { + // right click while left button held + wing_delete_all_but(); + wing_level |= 3; + return TRUE; + } +#endif + + if (e->mouse_data.action & MOUSE_LDOWN) { + if ((e->mouse_data.buttons & 3) == 3) { + // both buttons, assume it's an order + wingman_order(); + wing_play_fx(WING_SFX_COMM, 0, 0); + return TRUE; + } + if (!wing_any_enemies()) { + wing_advance_to_next_level(); + return TRUE; + } + wing_play_fx(WING_SFX_GOODGUY_FIRE, 0, 0); + wing_fire_shot(wing, -1); + wing_fire_shot(wing, 1); + return TRUE; + } + + switch (e->mouse_data.buttons & 3) { + case 0: + case 1: + case 3: // both buttons pushed. Huh. + wing_a = -y / 64; + wing_b = -x / 64; + wing_c = 0; + wing_acc = 0; + break; + + case 2: // do right mouse stuff + wing_acc = -y / 4; + wing_c = x / 64; + wing_a = wing_b = 0; + } + + return TRUE; +} + +void games_init_wing(void *game_state) { + int i; + wing_level = QUESTVAR_GET(WING_QUEST_VAR); + wing_game_mode = WING_BRIEFING; + + for (i = 0; i < MAX_WING_STARS; ++i) { + wing_st[i].x = fix_make(rand() % 512 - 256, 0); + wing_st[i].y = fix_make(rand() % 512 - 256, 0); + wing_st[i].z = fix_make(rand() % 512 - 256, 0); + } +} + +void games_expose_wing(MFD *m, ubyte control) { + switch (wing_game_mode) { + case WING_PLAY_GAME: + wing_update_one_time_unit(); + wing_render_world(); + break; + + case WING_BRIEFING: + wing_play_cutscene(STRING(WingBriefing + wing_level / WING_PHASES)); + break; + + case WING_DEBRIEFING: + wing_play_cutscene(STRING(WingDebriefing + wing_level / WING_PHASES)); + break; + + case WING_YOUDIED: + wing_play_cutscene(STRING(WingYouDied)); + break; + + case WING_FLYBY: + wing_handle_flyby(); + break; + } + + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + // autoreexpose + mfd_notify_func(MFD_GAMES_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); +} + +#endif // LOST_TREASURES + +/* +// this is so lovely, it is a test function, joy +uchar mfd_games_hack_func(short keycode, ulong context, void* data) +{ + mcom_state *cur_state=(mcom_state *)GAME_DATA; + int mfd = mfd_grab_func(MFD_GAMES_FUNC,MFD_INFO_SLOT); + + // give the tester all of the frigging games + player_struct.softs.misc[MISC_SOFTWARE_GAMES] = 0xff; + GAME_MODE = GAME_MODE_MENU; + mfd_notify_func(MFD_GAMES_FUNC,MFD_INFO_SLOT,TRUE,MFD_ACTIVE,TRUE); + mfd_change_slot(mfd,MFD_INFO_SLOT); +#ifdef PLAYTEST + wing_cheat = 1; +#endif + return FALSE; +} +*/ + +void mfd_games_turnon(uchar visible, uchar real_start) { + if (real_start) { + int mfd = mfd_grab_func(MFD_GAMES_FUNC, MFD_INFO_SLOT); + GAME_MODE = GAME_MODE_MENU; + mfd_notify_func(MFD_GAMES_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, TRUE); + mfd_change_slot(mfd, MFD_INFO_SLOT); + player_struct.softs_status.misc[CPTRIP(GAMES_TRIPLE)] &= ~WARE_ON; + } +} + +void mfd_games_turnoff(uchar visible, uchar real_stop) { + // game shutdown code goes here. +} + +uchar (*game_handler_funcs[])(MFD *m, uiEvent *ev) = { + games_handle_pong, + games_handle_mcom, + games_handle_road, + games_handle_pong, // just reuse the pong code for bots + games_handle_15, + games_handle_ttt, + games_handle_null, + games_handle_wing, + games_handle_menu +}; diff --git a/engine/src/GameSrc/mfdgump.c b/engine/src/GameSrc/mfdgump.c new file mode 100644 index 0000000..53c0758 --- /dev/null +++ b/engine/src/GameSrc/mfdgump.c @@ -0,0 +1,283 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/mfdgump.c $ + * $Revision: 1.18 $ + * $Author: dc $ + * $Date: 1994/11/28 06:38:23 $ + * + * + */ + +#include "mfdint.h" +#include "mfdext.h" +#include "mfdfunc.h" +#include "mfddims.h" +#include "tools.h" +#include "gamestrn.h" +#include "objuse.h" +#include "input.h" +#include "objprop.h" +#include "colors.h" +#include "objload.h" +#include "fullscrn.h" +#include "gr2ss.h" + +#include "cybstrng.h" +#include "gamescr.h" + +// ============================================================ +// MFD CONTAINER GUMPS +// ============================================================ + +/* This is the MFD for buttons that zoom into the MFD. */ + +// ------- +// DEFINES +// ------- + +#define MFD_GUMP_FUNC 23 + +#define NUM_CONTENTS 4 +#define Y_STEP 15 +#define FIRST_ITEM_Y 15 +#define LEFT_MARGIN 5 +#define CONTENTS_WID ((MFD_VIEW_WID - 2 * LEFT_MARGIN) / 2) +#define CONTENTS_HGT ((MFD_VIEW_HGT - FIRST_ITEM_Y - 5) / 2) +extern char container_extract(ObjID *pidlist, int d1, int d2); +extern void container_stuff(ObjID *pidlist, int numobjs, int *d1, int *d2); +extern uchar is_container(ObjID id, int **d1, int **d2); + +#define LAST_INPUT_ROW (player_struct.mfd_func_data[MFD_GUMP_FUNC][0]) +#define LAST_DOUBLE (player_struct.mfd_func_data[MFD_GUMP_FUNC][1]) + +// ------- +// GLOBALS +// ------- +ObjID gump_idlist[NUM_CONTENTS]; +uchar gump_num_objs; + + +// --------------- +// EXPOSE FUNCTION +// --------------- + +/* This gets called whenever the MFD needs to redraw or + undraw. + The control value is a bitmask with the following bits: + MFD_EXPOSE: Update the mfd, if MFD_EXPOSE_FULL is not set, + update incrementally. + MFD_EXPOSE_FULL: Fully redraw the mfd, implies MFD_EXPOSE + + if no bits are set, the mfd is being "unexposed;" its display + being pulled off the screen to make room for a different func. +*/ + +void mfd_gump_expose(MFD *mfd, ubyte control) { + uchar full = control & MFD_EXPOSE_FULL; + if (control == 0) // MFD is drawing stuff + { + panel_ref_unexpose(mfd->id, MFD_GUMP_FUNC); + return; + } + if (control & MFD_EXPOSE) // Time to draw stuff + { + ObjID id = player_struct.panel_ref; + uchar i; + + // clear update rects + mfd_clear_rects(); + // set up canvas + gr_push_canvas(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + // Clear the canvas by drawing the background bitmap + if (!full_game_3d) + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + mfd_item_micro_expose(full, ID2TRIP(id)); + + if (full) { + int *d1, *d2; + is_container(id, &d1, &d2); // fill in d1 and d2; + gump_num_objs = container_extract(gump_idlist, *d1, (d2 != NULL) ? *d2 : 0); + for (i = gump_num_objs; i < sizeof(gump_idlist) / sizeof(gump_idlist[0]); i++) + gump_idlist[i] = OBJ_NULL; + LAST_INPUT_ROW = 0xFF; + } + gr_set_font(ResLock(MFD_FONT)); + if (gump_num_objs == 0) { + short x, y; + char *s = get_temp_string(REF_STR_EmptyGump); + gr_string_size(s, &x, &y); + x = (MFD_VIEW_WID - x) / 2; + y = (MFD_VIEW_HGT - y) / 2; + mfd_draw_string(s, x, y, GREEN_YELLOW_BASE, TRUE); + } else + for (i = 0; i < gump_num_objs; i++) { + short x, y; + uchar r = i / 2, c = i % 2; + if (gump_idlist[i] != OBJ_NULL) { + grs_bitmap *bm = bitmaps_2d[OPNUM(gump_idlist[i])]; + x = LEFT_MARGIN + ((c == 0) ? 0 : CONTENTS_WID) + (CONTENTS_WID - bm->w) / 2; + y = FIRST_ITEM_Y + ((r == 0) ? 0 : CONTENTS_HGT) + (CONTENTS_HGT - bm->h) / 2; + ss_bitmap(bm, x, y); + } + // the +1 in the last argument is to get + // mfd_add_rect to union adjacents... + } + mfd_add_rect(LEFT_MARGIN, FIRST_ITEM_Y, LEFT_MARGIN + 2 * CONTENTS_WID, FIRST_ITEM_Y + 2 * CONTENTS_HGT); + ResUnlock(MFD_FONT); + // on a full expose, make sure to draw everything + + if (full) + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + // Pop the canvas + gr_pop_canvas(); + // Now that we've popped the canvas, we can send the + // updated mfd to screen + mfd_update_rects(mfd); + } +} + +void gump_clear(void) { + int i; + + for (i = 0; i < NUM_CONTENTS; i++) + gump_idlist[i] = OBJ_NULL; +} + +uchar gump_pickup(byte row) { + int *d1, *d2; + ObjID cont = player_struct.panel_ref; + + // KLC mouse_unconstrain(); + if (row < 0 || row >= gump_num_objs || gump_idlist[row] == OBJ_NULL) + return FALSE; + push_cursor_object(gump_idlist[row]); + gump_idlist[row] = OBJ_NULL; + if (row == gump_num_objs - 1) + gump_num_objs--; + LAST_INPUT_ROW = 0xFF; + LAST_DOUBLE = FALSE; + // Here's where we update the container object + is_container(cont, &d1, &d2); + container_stuff(gump_idlist, gump_num_objs, d1, d2); + + if (*d1 == 0 && (d2 == NULL || *d2 == 0)) + check_panel_ref(TRUE); // punt empty gump + else + mfd_notify_func(MFD_GUMP_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + return TRUE; +} + +uchar gump_get_useful(bool shifted) { + int row; + uchar useless; + uchar obj_is_useless(ObjID oid); + + for (useless = 0; useless <= 1; useless++) { + for (row = 0; row < gump_num_objs; row++) { + if (gump_idlist[row] && obj_is_useless(gump_idlist[row]) == useless) { + uchar result = gump_pickup(row); + if (result && shifted) + { + extern void absorb_object_on_cursor(ushort keycode, uint32_t context, intptr_t data); //see invent.c + absorb_object_on_cursor(0, 0, 0); //parameters unused + } + return result; + } + } + } + return FALSE; +} + +uchar mfd_gump_handler(MFD *m, uiEvent *e) { + LGPoint pos = e->pos; + byte row; + short x, y; + grs_bitmap *bm; + + pos.x -= m->rect.ul.x; + pos.y -= m->rect.ul.y; + row = (pos.y - FIRST_ITEM_Y) / CONTENTS_HGT; + row = 2 * row + (pos.x - LEFT_MARGIN) / CONTENTS_WID; + +#ifdef RIGHT_BUTTON_GUMP_UI + if (LAST_INPUT_ROW != 0xFF && row != LAST_INPUT_ROW) { + if (e->mouse_data.buttons & (1 << MOUSE_RBUTTON)) { + return gump_pickup(LAST_INPUT_ROW); + } + } +#endif // RIGHT_BUTTON_GUMP_UI + if (row < 0 || row >= gump_num_objs) + return FALSE; + if (LAST_DOUBLE && (e->mouse_data.action & MOUSE_LUP)) { + return gump_pickup(row); + } + if (!(e->mouse_data.action & (MOUSE_LDOWN | UI_MOUSE_LDOUBLE))) + return FALSE; +#ifdef RIGHT_BUTTON_GUMP_UI + if (!(e->mouse_data.action & (MOUSE_LDOWN | MOUSE_RDOWN | UI_MOUSE_LDOUBLE)) && !(e->buttons & (1 << MOUSE_RBUTTON))) + return FALSE; +#endif // RIGHT_BUTTON_GUMP_UI + // Hey, this is a little extra work, but it gets the job done. + bm = bitmaps_2d[OPNUM(gump_idlist[row])]; + x = LEFT_MARGIN + ((row % 2 == 0) ? 0 : CONTENTS_WID) + (CONTENTS_WID - bm->w) / 2; + y = FIRST_ITEM_Y + ((row / 2 == 0) ? 0 : CONTENTS_HGT) + (CONTENTS_HGT - bm->h) / 2; + if (pos.x >= x && pos.x < x + bm->w && pos.y >= y && pos.y < y + bm->h) { + if (e->mouse_data.action == UI_MOUSE_LDOUBLE) { + LAST_DOUBLE = TRUE; + return TRUE; + } + LAST_DOUBLE = FALSE; + if (e->mouse_data.action & MOUSE_LDOWN) { + if (gump_idlist[row] != OBJ_NULL) { + if (e->mouse_data.modifiers & 1) { //shifted click; see sdl_events.c + //try to pickup and absorb object + uchar result = gump_pickup(row); + if (result) { + extern void absorb_object_on_cursor(ushort keycode, uint32_t context, intptr_t data); //see invent.c + absorb_object_on_cursor(0, 0, 0); //parameters unused + } + return result; + } + else { + extern void look_at_object(ObjID); + look_at_object(gump_idlist[row]); + } + } + } +#ifdef RIGHT_BUTTON_GUMP_UI + if (e->action & MOUSE_RDOWN) { + // KLC mouse_constrain_xy(m->rect.ul.x,m->rect.ul.y,m->rect.lr.x-1,m->rect.lr.y-1); + LAST_INPUT_ROW = row; + return TRUE; + } + if (e->action & MOUSE_RUP) + return gump_pickup(row); +#endif // RIGHT_BUTTON_GUMP_UI + } else if (e->mouse_data.buttons & (1 << MOUSE_RBUTTON)) { + return gump_pickup(row); + } + LAST_DOUBLE = FALSE; + // KLC mouse_unconstrain(); + return FALSE; +} diff --git a/engine/src/GameSrc/mfdpanel.c b/engine/src/GameSrc/mfdpanel.c new file mode 100644 index 0000000..b55607d --- /dev/null +++ b/engine/src/GameSrc/mfdpanel.c @@ -0,0 +1,1940 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/mfdpanel.c $ + * $Revision: 1.75 $ + * $Author: dc $ + * $Date: 1994/11/14 03:30:50 $ + */ + +#include +#include +#include + +#include "mfdpanel.h" +#include "mfdint.h" +#include "mfdext.h" +#include "mfddims.h" +#include "mfdgadg.h" +#include "mfdart.h" +#include "tools.h" +#include "cybstrng.h" +#include "gamescr.h" +#include "gamestrn.h" +#include "objbit.h" +#include "citres.h" + +#include "objsim.h" +#include "objgame.h" + +#include "colors.h" +#include "fullscrn.h" + +#include "otrip.h" +#include "gr2ss.h" + +#define WORKING_INC_MFDS +#define DRAW_GRID_PLUSSES + +// ---------- +// PROTOTYPES +// ---------- +extern errtype accesspanel_trigger(ObjID id); +errtype simple_load_res_bitmap_cursor(LGCursor *c, grs_bitmap *bmp, Ref rid); +errtype load_res_bitmap(grs_bitmap *bmp, Ref rid, uchar alloc); + +int wirepos_score(wirePosPuzzle *wppz); +void wirepos_setup_buttons(wirePosPuzzle *wppz); +uchar wirepos_3int_init(wirePosPuzzle *wppz, int a1, int a2, int a3); +void wirepos_3int_update(wirePosPuzzle *wppz); +int wirepos_iswire(wirePosPuzzle *wppz, int wim_code); +int wirepos_rescore_n_check(wirePosPuzzle *wppz); +uchar wirepos_moveto(wirePosPuzzle *wppz, int wim_code); + +uchar mfd_accesspanel_button_handler(MFD *mfd, LGPoint bttn, uiEvent *ev, void *data); +int access_help_string(wirePosPuzzle *wppz); +void mfd_setup_wirepanel(uchar special, ObjID id); +uchar mfd_solve_wirepanel(void); +void mfd_setup_gridpanel(ObjID id); +uchar mfd_solve_gridpanel(void); +void mfd_gridpanel_set_winmove(uchar check); +gpz_state gridpanel_move(LGPoint node, gridFlowPuzzle *gfpz); + +void gpz_set_grid_state(gridFlowPuzzle *gfpz, short row, short col, gpz_state val); +void gpz_uncharge_grid(gridFlowPuzzle *gfpz); +gpz_state gpz_charge_state(gpz_state s); +gpz_state gpz_uncharge_state(gpz_state s); +uchar gpz_is_charged(gpz_state s); +void gpz_toggle_state(gridFlowPuzzle *gfpz, short r, short c); +gpz_state bitarray_get(short siz, short off, uint *base); +void bitarray_set(short siz, short off, ushort val, uint *base); +int gpz_search_depth(gridFlowPuzzle *gfpz); +void gpz_add_gate(gridFlowPuzzle *gfpz, ObjID me); +void gpz_perimeter_to_grid(gridFlowPuzzle *gfpz, ushort per, short *r, short *c); +uchar gpz_state_charged(gridFlowPuzzle *gfpz, short r, short c); +uchar gpz_doneness(gridFlowPuzzle *gfpz); +uchar gpz_propogate_charge_n_check(gridFlowPuzzle *gfpz); +void gpz_setup_buttons(gridFlowPuzzle *gfpz); + +uchar find_winning_move_from(gridFlowPuzzle *gfpz, gridFlowPuzzle *solved, LGPoint *move, int r0, int c0, int dep, + ulong timeout); +uchar find_winning_move(gridFlowPuzzle *gfpz, gridFlowPuzzle *solved, int depth, LGPoint *move, uchar breadth_first, + ulong timeout); +void wacky_int_line(short x1, short y1, short x2, short y2); +char *grid_help_string(gridFlowPuzzle *gfpz, char *buf, int siz); +void id_clut_init(uchar *clut); + +// not so secret, is it? +// the fact that we claim to know the size of this is criminal. +extern uchar hideous_secret_game_storage[2052]; + +#define HSGS hideous_secret_game_storage +#define SHADOW_PANEL_SIG (*((ulong *)HSGS)) +#define OUR_SIGNATURE (((ulong)'G' << 24) | ((ulong)'r' << 16) | ((ulong)'i' << 8) | ((ulong)'d')) +#define SIGN_US() (SHADOW_PANEL_SIG = OUR_SIGNATURE) +#define WE_ARE_SIGNED() ((SHADOW_PANEL_SIG) == OUR_SIGNATURE) + +#define SHADOW_PANEL_ID (*((ObjID *)((&SHADOW_PANEL_SIG) + 1))) +#define SHADOW_PANEL_LEV (*((short *)((&SHADOW_PANEL_ID) + 1))) +#define SHADOW_PANEL_STOR ((uchar *)((&SHADOW_PANEL_LEV) + 1)) +#define MFD_GRID_CLUT ((uchar *)((SHADOW_PANEL_STOR) + sizeof(player_struct.mfd_access_puzzles))) +#define grid_help_clut MFD_GRID_CLUT +#define MFDPANEL_MEMORY_BAG (MFD_GRID_CLUT + 256) +#define MFDPANEL_MEMORY_BAG_SIZE (sizeof(HSGS) - (MFDPANEL_MEMORY_BAG - HSGS)) + +int gpz_base_colors[] = {BLUE_BASE + 10, GREEN_BASE + 7, 0x24, GREEN_YELLOW_BASE + 9, GRAY_BASE + 5}; +int gpz_dk_colors[] = {0x7E, 0x63, 0x27, 0x56, GRAY_BASE + 13}; + +LGCursor gridCursor; +grs_bitmap gridCursorbm; +#ifdef SVGA_SUPPORT +uchar + gridCursorBits[1016]; // This should be enough, maybe... note wacky computation scaling 9x9 to apropos for 1024x768 +#else +uchar gridCursorBits[81]; // This should be enough +#endif + +//#define score_spew(str,v) mprintf(str,v) +//#define score_spew2(str,v,o) mprintf(str,v,o) +#define score_spew(str, v) +#define score_spew2(str, v, o) + +#define WIREPUZ_CODE 0 +#define GRIDPUZ_CODE 1 + +int mfd_slot_primary(int slot) { + int mid; + + for (mid = 0; mid < NUM_MFDS; mid++) { + if (player_struct.mfd_current_slots[mid] == slot) + return mid; + } + return -1; +} + +#define MFD_MARGIN_WID 3 +#define SWITCH_COLOR 2 + +// draws text and/or bitmap centered in current canvas, +// assumed to be an mfd. +static void draw_help_text(char *str, uchar wire, void *puzzle) { + short sw, sh, bw = 0, bh = 0, x, y; + uchar save_w = mfd_string_wrap, bmap = FALSE; + grs_bitmap foot; + uchar bcolor; + + gr_set_font(ResLock(MFD_FONT)); + gr_string_wrap(str, MFD_VIEW_WID - 1 - (2 * MFD_MARGIN_WID)); + gr_string_size(str, &sw, &sh); + // special annointed string gets illustrative bitmap. + if (!wire && !(((gridFlowPuzzle *)puzzle)->gfLayout.have_won)) { + foot.bits = MFDPANEL_MEMORY_BAG; + bmap = (OK == load_res_bitmap(&foot, REF_IMG_GridHelpSwitch, FALSE)); + bw = foot.w; + bh = foot.h; + } + x = (MFD_VIEW_WID - sw) / 2; + y = (MFD_VIEW_HGT - sh - bh) / 2; + mfd_string_wrap = FALSE; + mfd_draw_string(str, x, y, GREEN_YELLOW_BASE + 3, TRUE); + gr_font_string_unwrap(str); + mfd_string_wrap = save_w; + if (bmap) { +#ifdef PUZZ_DIFF_NOHELP + if (PUZZLE_DIFFICULTY < MAX_DIFFICULTY) + bcolor = gpz_base_colors[((gridFlowPuzzle *)puzzle)->control_alg]; + else + bcolor = GPZ_NOALG_COLOR; +#else + bcolor = gpz_base_colors[((gridFlowPuzzle *)puzzle)->gfLayout.control_alg]; +#endif + x = (MFD_VIEW_WID - bw) / 2; + y += sh; + grid_help_clut[SWITCH_COLOR] = bcolor; + ss_clut_ubitmap(&foot, x, y, grid_help_clut); + } +} + +int wirepos_score(wirePosPuzzle *wppz) { + int i, sc = 0, j; + for (i = 0; i < wppz->wirecnt; i++) { + wirePTrg *wpt = &wppz->wires[i]; + if (wppz->scorealg != 0) { + if (memcmp(&wpt->cur, &wpt->targ, sizeof(wirePos)) == 0) { + sc += P_CORRECT; + score_spew("crct %d...", i); + } else if ((wpt->cur.lpos == wpt->targ.lpos) || (wpt->cur.rpos == wpt->targ.rpos)) { + sc += P_POS_OK; + score_spew("pos %d...", i); + } else if (get_delta(wpt->cur) == get_delta(wpt->targ)) { + sc += P_DELTA_OK; + score_spew("dlta %d...", i); + } else + score_spew("fail %d...", i); + } else { + int bsc = 0; + for (j = 0; j < wppz->wirecnt; j++) { + wirePTrg *tpt = &wppz->wires[j]; + if (memcmp(&wpt->cur, &tpt->targ, sizeof(wirePos)) == 0) { + bsc = P_CORRECT; + score_spew2("crct %d %d...", i, j); + } else if ((wpt->cur.lpos == wpt->targ.lpos) || (tpt->cur.rpos == wpt->targ.rpos)) { + if (D_POS_OK > bsc) { + bsc = D_POS_OK; + score_spew2("pos %d %d...", i, j); + } + } else if (get_delta(wpt->cur) == get_delta(tpt->targ)) { + if (D_DELTA_OK > bsc) { + bsc = D_DELTA_OK; + score_spew2("dlta %d %d...", i, j); + } + } else + score_spew2("fail %d %d...", i, j); + } + sc += bsc; + } + } + score_spew("sc %d\n", sc); + sc = (sc * wppz->scale) >> WP_SCALE_SHF; + if (player_struct.drug_status[CPTRIP(GENIUS_DRUG_TRIPLE)] > 0) + sc += sc >> 2; + if (sc > UCHAR_MAX) + sc = UCHAR_MAX; + return wppz->score = sc; +} + +#define pegrand(peg, range) ((peg) ? ((range)-1) : (rand() % (range))) +#define randvar(wppz) pegrand((wppz)->have_won, ((wppz)->scale >> WP_SCALE_SHF) << 1) +#define wirepos_curscore(wpppppz) ((wpppppz)->score + randvar(wpppppz) - ((wpppppz)->scale >> WP_SCALE_SHF)) + +#ifndef GAMEONLY +#define wirepos_spew(wz, csc) \ + mprintf("%d-%d, %d-%d, %d-%d, %d-%d, taps %2.2x %2.2x, wim %x tick %x sc %d cs %d\n", wz->wires[0].cur.lpos, \ + wz->wires[0].cur.rpos, wz->wires[1].cur.lpos, wz->wires[1].cur.rpos, wz->wires[2].cur.lpos, \ + wz->wires[2].cur.rpos, wz->wires[3].cur.lpos, wz->wires[3].cur.rpos, wz->left_tap, wz->right_tap, \ + wz->wire_in_motion, wz->wim_tick, wz->score, csc) +#else +#define wirepos_spew(wz, csc) +#endif + +void wirepos_setup_buttons(wirePosPuzzle *wppz) { + LGPoint bsize = {ACCESSP_BTN_WD, ACCESSP_BTN_HGT}; + LGPoint bdims = {ACCESSP_BTN_COL, ACCESSP_BTN_ROW}; + LGRect r = {{ACCESSP_BTN_X, ACCESSP_BTN_Y}, {ACCESSP_BTN_X + ACCESSP_FULL_WD, ACCESSP_BTN_Y + ACCESSP_FULL_HGT}}; + + bdims.y = wppz->pincnt; + r.lr.y = ACCESSP_BTN_Y + (ACCESSP_BTN_HGT * wppz->pincnt); + MFDBttnArrayResize(&(mfd_funcs[MFD_ACCESSPANEL_FUNC].handlers[0]), &r, bdims, bsize); +} + +// a1 is master data, a2 target pos, a3 current pos +uchar wirepos_3int_init(wirePosPuzzle *wppz, int a1, int a2, int a3) { // restore the puzzle, for now just init it + int i; + wppz->wirecnt = a1 & 0xf; + a1 >>= 4; + if (wppz->wirecnt == 0) + wppz->wirecnt = 4; + wppz->pincnt = a1 & 0xf; + a1 >>= 4; + if (wppz->pincnt == 0) + wppz->pincnt = 6; + wppz->tscore = a1 & 0xff; + a1 >>= 8; + if (wppz->tscore == 0) + wppz->tscore = 128; + wppz->scorealg = a1 & 0xf; + a1 >>= 4; + wppz->have_won = a1 & 0xf; + a1 >>= 4; + wppz->scale = (256 << WP_SCALE_SHF) / (P_CORRECT * wppz->wirecnt); + wppz->left_tap = wppz->right_tap = 0; + // on highest difficulty, always use colored wires, maybe add some + // spurious pins. + if (PUZZLE_DIFFICULTY == MAX_DIFFICULTY) { + int p = wppz->pincnt; + wppz->scorealg = 1; + wppz->pincnt = p + ((player_struct.panel_ref) % (6 - p + 1)); + wppz->tscore = 255; + } + for (i = 0; i < wppz->wirecnt; i++) { + wppz->wires[i].cur.lpos = a3 & 0x7; + wppz->left_tap |= (1 << (a3 & 7)); + a3 >>= 3; + wppz->wires[i].cur.rpos = a3 & 0x7; + wppz->right_tap |= (1 << (a3 & 7)); + a3 >>= 3; + wppz->wires[i].targ.lpos = a2 & 0x7; + a2 >>= 3; + wppz->wires[i].targ.rpos = a2 & 0x7; + a2 >>= 3; + } + wirepos_score(wppz); + wppz->wires_moved = 1; + wppz->last_score = wppz->score; + wppz->wire_in_motion = 0xff; + wppz->wim_tick = wppz->wim_shown = 0; + access_primary_mfd = -1; + return TRUE; +} + +#define WIN_MASK 0x0F00000 + +void wirepos_3int_update(wirePosPuzzle *wppz) { + int p2, p4 = 0, cshf = 0, i; + p2 = objFixtures[objs[wppz->our_id].specID].p2; + if (wppz->have_won) + p2 |= WIN_MASK; + else + p2 &= ~WIN_MASK; + for (i = 0; i < wppz->wirecnt; i++, cshf += 6) { + p4 |= (wppz->wires[i].cur.lpos << (cshf)); + p4 |= (wppz->wires[i].cur.rpos << (cshf + 3)); + } + objFixtures[objs[wppz->our_id].specID].p2 = p2; + objFixtures[objs[wppz->our_id].specID].p4 = p4; +} + +int wirepos_iswire(wirePosPuzzle *wppz, int wim_code) { + int i, owire = wim_code & BTN_MASK; + if (wim_code & LR_MASK) { + for (i = 0; i < wppz->wirecnt; i++) + if (wppz->wires[i].cur.rpos == owire) + return i; + } else { + for (i = 0; i < wppz->wirecnt; i++) + if (wppz->wires[i].cur.lpos == owire) + return i; + } + return -1; +} + +#define ALLOW_FLIP + +uchar wirepos_moveto(wirePosPuzzle *wppz, int wim_code) { + int wim_tap, retv; + + if (wim_code & LR_MASK) + wim_tap = wppz->right_tap; + else + wim_tap = wppz->left_tap; + if ((wppz->wire_in_motion != wim_code) && ((wppz->wire_in_motion & LR_MASK) == (wim_code & LR_MASK)) +#ifndef ALLOW_FLIP + (((1 << (wim_code & BTN_MASK)) & wim_tap) == 0)) +#endif + ) + { + int owire = wppz->wire_in_motion & BTN_MASK, twire = wim_code & BTN_MASK, loc; + loc = wirepos_iswire(wppz, wppz->wire_in_motion); + if (loc == -1) { + retv = FALSE; + } else { +#ifdef ALLOW_FLIP + if (((1 << twire) & wim_tap) != 0) { + int oloc = wirepos_iswire(wppz, wim_code); + if (wim_code & LR_MASK) { + wppz->wires[loc].cur.rpos = twire; + wppz->wires[oloc].cur.rpos = owire; + } else { + wppz->wires[loc].cur.lpos = twire; + wppz->wires[oloc].cur.lpos = owire; + } + } else +#endif + { + wim_tap &= ~(1 << owire); + wim_tap |= (1 << twire); + if (wim_code & LR_MASK) { + wppz->right_tap = wim_tap; + wppz->wires[loc].cur.rpos = twire; + } else { + wppz->left_tap = wim_tap; + wppz->wires[loc].cur.lpos = twire; + } + } + } + retv = TRUE; + } + else + retv = FALSE; + wppz->wire_in_motion = NO_WIRE_IN_MOTION; + return retv; +} + +int wirepos_rescore_n_check(wirePosPuzzle *wppz) { // score and check, if solved, open us + int score = wirepos_curscore(wppz); + if (score > 255) + score = 255; + if ((wppz->have_won == 0) && (score >= wppz->tscore)) { + accesspanel_trigger(player_struct.panel_ref); + wppz->have_won = 1; + wirepos_3int_update(wppz); + return -score; + } + return score; +} + +uchar mfd_accesspanel_button_handler(MFD *mfd, LGPoint bttn, uiEvent *ev, void *data) { + wirePosPuzzle *wppz = (wirePosPuzzle *)&player_struct.mfd_access_puzzles[0]; + int wim_code; + + if (mfd->id != access_primary_mfd) + return TRUE; + + if ((ev->subtype & (MOUSE_LDOWN | UI_MOUSE_LDOUBLE)) == 0) + return TRUE; + + if (wppz->have_won) + return TRUE; + + wppz->wires_moved = 1; + + wim_code = bttn.y + (bttn.x ? LR_MASK : 0); + + // parse input, move appropriately + if (wppz->wire_in_motion != NO_WIRE_IN_MOTION) { // put it down + if (wirepos_moveto(wppz, wim_code)) { + wirepos_score(wppz); + } + } else // pick it up + { + if (wirepos_iswire(wppz, wim_code) != -1) { + wppz->wire_in_motion = wim_code; + } + } + // wirepos_spew(wppz,53); + mfd_notify_func(MFD_ACCESSPANEL_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); + wirepos_3int_update(wppz); + return TRUE; +} + +char mfd_type_accesspanel(ObjID id) { + int p2; + + p2 = objFixtures[objs[id].specID].p2; + p2 = (p2 >> 28) & 0xF; + + if (p2 == WIREPUZ_CODE) + return MFD_ACCESSPANEL_FUNC; + else { + return MFD_GRIDPANEL_FUNC; + } +} + +char mfd_setup_accesspanel(uchar special, ObjID id) { + void mfd_setup_wirepanel(uchar special, ObjID id); + void mfd_setup_gridpanel(ObjID id); + char func; + + func = mfd_type_accesspanel(id); + + if (func == MFD_ACCESSPANEL_FUNC) + mfd_setup_wirepanel(special, id); + else { + mfd_setup_gridpanel(id); + + SIGN_US(); + SHADOW_PANEL_ID = id; + SHADOW_PANEL_LEV = player_struct.level; + memcpy(SHADOW_PANEL_STOR, player_struct.mfd_access_puzzles, sizeof(player_struct.mfd_access_puzzles)); + } + return func; +} + +uchar mfd_solve_accesspanel(ObjID id) { + int p2; + uchar retval; + + p2 = objFixtures[objs[id].specID].p2; + p2 = (p2 >> 28) & 0xF; + + if (p2 == WIREPUZ_CODE) + retval = mfd_solve_wirepanel(); + else { + retval = mfd_solve_gridpanel(); + // okay, grid panels trigger in the expose func, when they + // show you you've won, whereas wire puzzles trigger when they + // score. So, only in this case have we not triggered yet. + + if (retval == EPICK_SOLVED) + accesspanel_trigger(player_struct.panel_ref); + } + return (retval); +} + +// Does every thing but set the slot.. +// Special is how much SHODAN has defeated us by +void mfd_setup_wirepanel(uchar special, ObjID id) { + int p2, p3, p4; + wirePosPuzzle *wppz = (wirePosPuzzle *)&player_struct.mfd_access_puzzles[0]; + p2 = objFixtures[objs[id].specID].p2; + p3 = objFixtures[objs[id].specID].p3; + p4 = objFixtures[objs[id].specID].p4; + if (p3 == 0) + p3 = 0x1c342a50; + if (p4 == 0) + p4 = 0x0cb530a1; + wppz->special = special; + wppz->our_id = id; + wirepos_3int_init(wppz, p2, p3, p4); + mfd_notify_func(MFD_ACCESSPANEL_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, TRUE); +} + +uchar mfd_solve_wirepanel() { + wirePosPuzzle *wppz = (wirePosPuzzle *)&player_struct.mfd_access_puzzles[0]; + int wire, swapper, targ, num_wires; + int score; + + if (wppz->have_won) { + return EPICK_PRESOL; + } + + //keep within array bounds + num_wires = wppz->wirecnt; + if (num_wires > MAX_P_WIRES) num_wires = MAX_P_WIRES; + + for (wire = 0; wire < num_wires; wire++) { +#define MINIMAL_SOLUTION +#ifdef MINIMAL_SOLUTION + // move one wire to target, swapping it with a later wire if + // necessary (we should never need to swap with a previous + // wire, since they are already in their target positions, + // which should never share pins with our target position. + + targ = wppz->wires[wire].targ.lpos; + for (swapper = wire + 1; swapper < num_wires; swapper++) { + if (wppz->wires[swapper].cur.lpos == targ) { + wppz->wires[swapper].cur.lpos = wppz->wires[wire].cur.lpos; + wppz->wires[wire].cur.lpos = targ; + } + } + if (wppz->wires[wire].cur.lpos != targ) + wppz->wires[wire].cur.lpos = targ; + + targ = wppz->wires[wire].targ.rpos; + for (swapper = wire + 1; swapper < num_wires; swapper++) { + if (wppz->wires[swapper].cur.rpos == targ) { + wppz->wires[swapper].cur.rpos = wppz->wires[wire].cur.rpos; + wppz->wires[wire].cur.rpos = targ; + } + } + if (wppz->wires[wire].cur.rpos != targ) + wppz->wires[wire].cur.rpos = targ; + + // rescore, and if target score has been achieved, we are done. + wirepos_score(wppz); + score = wirepos_rescore_n_check(wppz); + if (wppz->have_won) { + wppz->score = score > 0 ? score : -score; + wirepos_3int_update(wppz); + break; + } +#else + wppz->wires[wire].cur = wppz->wires[wire].targ; +#endif + } + +#ifndef MINIMAL_SOLUTION + // rescore, and if target score has been achieved, we are done. + wirepos_score(wppz); + score = wirepos_rescore_n_check(wppz); + if (wppz->have_won) { + wppz->score = score > 0 ? score : -score; + wirepos_3int_update(wppz); + } +#endif + + mfd_notify_func(MFD_ACCESSPANEL_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); + // always return success: if we've gotten to this point without solving + // the puzzle, it should be because of randomness in rescore_n_check + return (EPICK_SOLVED); + // return(wppz->have_won?EPICK_SOLVED:EPICK_FAILED); +} + +static uchar wire_base[MAX_P_WIRES] = {0x35, 0x22, 0x78, 0x4B, 0x5A, 0x41}; +#define ACCESSP_WIRE_DULL 3 + +#define ACCESSP_BLINK_MASK 0x40 + +#define accessp_score_rect_f(f, ls, rs, off) \ + f(ACCESSP_SCORE_X - off + ((ls) >> ACCESSP_SCORE_SHF), ACCESSP_SCORE_YT - off, \ + ACCESSP_SCORE_X + off + ((rs) >> ACCESSP_SCORE_SHF), ACCESSP_SCORE_YB + off) + +#define accessp_score_rect(ls, rs, off) \ + ACCESSP_SCORE_X - off + ((ls) >> ACCESSP_SCORE_SHF), ACCESSP_SCORE_YT - off, \ + ACCESSP_SCORE_X + off + ((rs) >> ACCESSP_SCORE_SHF), ACCESSP_SCORE_YB + off + +#define accessp_score_cmp(sc1, sc2, cmp) ((sc1 >> ACCESSP_SCORE_SHF) cmp(sc2 >> ACCESSP_SCORE_SHF)) + +// if no mfd is on the given slot, return -1 +// else, return the id of the lowest-numbered +// mfd with this slot. +errtype mfd_accesspanel_init(MFD_Func *f) { + int cnt = 0; + errtype err; + LGPoint bsize = {ACCESSP_BTN_WD, ACCESSP_BTN_HGT}; + LGPoint bdims = {ACCESSP_BTN_COL, ACCESSP_BTN_ROW}; + LGRect r = {{ACCESSP_BTN_X, ACCESSP_BTN_Y}, {ACCESSP_BTN_X + ACCESSP_FULL_WD, ACCESSP_BTN_Y + ACCESSP_FULL_HGT}}; + err = MFDBttnArrayInit(&f->handlers[cnt++], &r, bdims, bsize, mfd_accesspanel_button_handler, NULL); + if (err != OK) + return err; + f->handler_count = cnt; +#ifdef WORKING_INC_MFDS + player_struct.mfd_func_status[MFD_ACCESSPANEL_FUNC] |= 1 << 4; +#endif + return OK; +} + +uchar mfd_accesspanel_handler(MFD *m, uiEvent *e) { +#ifdef EPICK_ON_CURSOR_TRY + extern uchar try_use_epick(ObjID panel, ObjID cursor_obj); + + if (ev->type != UI_EVENT_MOUSE || !(ev->subtype & (MOUSE_LDOWN | UI_MOUSE_LDOUBLE))) + return FALSE; + + return (try_use_epick(player_struct.panel_ref, object_on_cursor)); +#else + return (FALSE); +#endif +} + +int access_help_string(wirePosPuzzle *wppz) { + if (wppz->have_won) + return (REF_STR_PanelSolved); + else + return (REF_STR_WirePuzzHelp + (wppz->wire_in_motion != NO_WIRE_IN_MOTION)); +} + +void mfd_accesspanel_expose(MFD *mfd, ubyte control) { + void mfd_clear_view(void); + wirePosPuzzle *wppz = (wirePosPuzzle *)&player_struct.mfd_access_puzzles[0]; + uchar full = control & MFD_EXPOSE_FULL; + if (control & MFD_EXPOSE) // Time to draw stuff + { + int cscore; + uchar primary; + + if ((full && access_primary_mfd < 0) || player_struct.mfd_current_slots[access_primary_mfd] != MFD_INFO_SLOT) { + full = TRUE; + access_primary_mfd = mfd->id; + } + + primary = (mfd->id == access_primary_mfd); + + if (primary) { + if ((cscore = wirepos_rescore_n_check(wppz)) < 0) { + mfd_notify_func(MFD_ACCESSPANEL_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); + cscore = -cscore; + } + cscore = wirepos_curscore(wppz); + if (cscore < 0) + cscore = -cscore; + if (cscore > 255) + cscore = 255; + } + mfd_clear_rects(); + gr_push_canvas(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + if (!primary) { + // draw help string, if different from last string or + // if full expose. + int prev = last_access_help_string; + char buf[80]; + + last_access_help_string = access_help_string(wppz); + if (full || last_access_help_string != prev) { + mfd_clear_view(); + get_string(last_access_help_string, buf, 80); + draw_help_text(buf, TRUE, wppz); + } + gr_pop_canvas(); + mfd_update_rects(mfd); + return; + } + if (full) { + wirepos_setup_buttons(wppz); + mfd_clear_view(); // draw full score (should color based on score), every other line? + gr_set_fcolor(ACCESSP_SCORE_BOR); + accessp_score_rect_f(ss_box, 0, 255, 1); + gr_set_fcolor(ACCESSP_SCORE_COL); + accessp_score_rect_f(ss_rect, 0, cscore, 0); + gr_set_fcolor(ACCESSP_TARG_COL); + ss_vline(ACCESSP_SCORE_X + (wppz->tscore >> ACCESSP_SCORE_SHF) - 1, ACCESSP_SCORE_YT, ACCESSP_SCORE_YB - 1); + } else if (accessp_score_cmp(wppz->last_score, cscore, <)) { // draw more score delta + gr_set_fcolor(ACCESSP_SCORE_COL); + accessp_score_rect_f(ss_rect, wppz->last_score, cscore, 0); + mfd_add_rect(accessp_score_rect(wppz->last_score, cscore, 0)); + if (accessp_score_cmp(wppz->tscore, wppz->last_score, >=) && accessp_score_cmp(cscore, wppz->tscore, >=)) { + gr_set_fcolor(ACCESSP_TARG_COL); + ss_vline(ACCESSP_SCORE_X + (wppz->tscore >> ACCESSP_SCORE_SHF) - 1, ACCESSP_SCORE_YT, + ACCESSP_SCORE_YB - 1); + } + } else if (accessp_score_cmp(wppz->last_score, cscore, >)) { // put back correct part of background + accessp_score_rect_f(ss_safe_set_cliprect, cscore, wppz->last_score, 0); + if (!full_game_3d) + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + mfd_add_rect(accessp_score_rect(cscore, wppz->last_score, 0)); + if (accessp_score_cmp(cscore, wppz->tscore, <=) && accessp_score_cmp(wppz->tscore, wppz->last_score, <=)) { + gr_set_fcolor(ACCESSP_TARG_COL); + ss_vline(ACCESSP_SCORE_X + (wppz->tscore >> ACCESSP_SCORE_SHF) - 1, ACCESSP_SCORE_YT, + ACCESSP_SCORE_YB - 1); + } + } + wppz->last_score = cscore; + if (full || wppz->wires_moved) { // draw wires + int i; + int lry = ACCESSP_BTN_Y + (ACCESSP_BTN_HGT * wppz->pincnt); + wppz->wires_moved = 0; + if (!full) { + ss_safe_set_cliprect(ACCESSP_BTN_X, ACCESSP_BTN_Y, ACCESSP_BTN_X + ACCESSP_FULL_WD, lry); + if (!full_game_3d) + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + mfd_add_rect(ACCESSP_BTN_X, ACCESSP_BTN_Y, ACCESSP_BTN_X + ACCESSP_FULL_WD, lry); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + } + for (i = 0; i < wppz->pincnt; i++) { + gr_set_fcolor(ACCESSP_PIN_COL); + ss_box(ACCESSP_BTN_X + 1, ACCESSP_BTN_Y + (ACCESSP_BTN_HGT * i), ACCESSP_BTN_X + ACCESSP_BTN_WD, + ACCESSP_BTN_Y - 1 + (ACCESSP_BTN_HGT * (i + 1))); + ss_box(ACCESSP_BTN_X + ACCESSP_FULL_WD - ACCESSP_BTN_WD, ACCESSP_BTN_Y + (ACCESSP_BTN_HGT * i), + ACCESSP_BTN_X + ACCESSP_FULL_WD - 1, ACCESSP_BTN_Y - 1 + (ACCESSP_BTN_HGT * (i + 1))); + gr_set_fcolor(ACCESSP_PIN_COL + 6); + ss_box(ACCESSP_BTN_X, ACCESSP_BTN_Y + (ACCESSP_BTN_HGT * i) - 1, ACCESSP_BTN_X + ACCESSP_BTN_WD + 1, + ACCESSP_BTN_Y + (ACCESSP_BTN_HGT * (i + 1))); + ss_box(ACCESSP_BTN_X + ACCESSP_FULL_WD - ACCESSP_BTN_WD - 1, ACCESSP_BTN_Y + (ACCESSP_BTN_HGT * i) - 1, + ACCESSP_BTN_X + ACCESSP_FULL_WD, ACCESSP_BTN_Y + (ACCESSP_BTN_HGT * (i + 1))); + } + gr_set_fcolor(ACCESSP_CHIP_COL); + ss_box(ACCESSP_BTN_X + ACCESSP_BTN_WD, ACCESSP_BTN_Y, ACCESSP_BTN_X + ACCESSP_FULL_WD - ACCESSP_BTN_WD, + lry - 1); + gr_set_fcolor(ACCESSP_CHIP_COL + 7); + ss_box(ACCESSP_BTN_X + ACCESSP_BTN_WD + 1, ACCESSP_BTN_Y + 1, + ACCESSP_BTN_X + ACCESSP_FULL_WD - ACCESSP_BTN_WD - 1, lry - 1 - 1); + for (i = 0; i < wppz->wirecnt; i++) { + int lfti, rghi; + int basecol; + + lfti = wppz->wires[i].cur.lpos; + rghi = wppz->wires[i].cur.rpos; + basecol = wire_base[wppz->scorealg == 0 ? (player_struct.panel_ref & 3) : i]; + gr_set_fcolor(basecol); + ss_thick_int_line(ACCESSP_BTN_X + (ACCESSP_BTN_WD / 2), + ACCESSP_BTN_Y + (ACCESSP_BTN_HGT * lfti) + (ACCESSP_BTN_HGT / 2) - 1, + ACCESSP_BTN_X + ACCESSP_FULL_WD - 1 - (ACCESSP_BTN_WD / 2), + ACCESSP_BTN_Y + (ACCESSP_BTN_HGT * rghi) + (ACCESSP_BTN_HGT / 2) - 1); + gr_set_fcolor(basecol + 3); + ss_thick_int_line(ACCESSP_BTN_X + (ACCESSP_BTN_WD / 2), + ACCESSP_BTN_Y + (ACCESSP_BTN_HGT * lfti) + (ACCESSP_BTN_HGT / 2), + ACCESSP_BTN_X + ACCESSP_FULL_WD - 1 - (ACCESSP_BTN_WD / 2), + ACCESSP_BTN_Y + (ACCESSP_BTN_HGT * rghi) + (ACCESSP_BTN_HGT / 2)); + } + } + if ((wppz->wire_in_motion != NO_WIRE_IN_MOTION) && + (((*tmd_ticks) & ACCESSP_BLINK_MASK) != + wppz->wim_tick)) { // if already shown but not in motion, it is already redrawn, baby + int i = wppz->wire_in_motion & BTN_MASK; + wppz->wim_tick = (*tmd_ticks) & ACCESSP_BLINK_MASK; + if ((wppz->wim_tick == ACCESSP_BLINK_MASK) || (wppz->wire_in_motion == NO_WIRE_IN_MOTION)) { + gr_set_fcolor(0); + wppz->wim_shown = 0; + } else { + gr_set_fcolor(ACCESSP_PIN_COL - 2); + wppz->wim_shown = 1; + } + if (wppz->wire_in_motion & LR_MASK) { + ss_rect(ACCESSP_BTN_X + ACCESSP_FULL_WD - ACCESSP_BTN_WD + 1, ACCESSP_BTN_Y + 1 + (ACCESSP_BTN_HGT * i), + ACCESSP_BTN_X + ACCESSP_FULL_WD - 1 - 1, ACCESSP_BTN_Y - 1 - 1 + (ACCESSP_BTN_HGT * (i + 1))); + mfd_add_rect(ACCESSP_BTN_X + ACCESSP_FULL_WD - ACCESSP_BTN_WD + 1, + ACCESSP_BTN_Y + 1 + (ACCESSP_BTN_HGT * i), ACCESSP_BTN_X + ACCESSP_FULL_WD - 1 - 1, + ACCESSP_BTN_Y - 1 - 1 + (ACCESSP_BTN_HGT * (i + 1))); + } else { + ss_rect(ACCESSP_BTN_X + 1 + 1, ACCESSP_BTN_Y + 1 + (ACCESSP_BTN_HGT * i), + ACCESSP_BTN_X + ACCESSP_BTN_WD - 1, ACCESSP_BTN_Y - 1 - 1 + (ACCESSP_BTN_HGT * (i + 1))); + mfd_add_rect(ACCESSP_BTN_X + 1 + 1, ACCESSP_BTN_Y + 1 + (ACCESSP_BTN_HGT * i), + ACCESSP_BTN_X + ACCESSP_BTN_WD - 1, ACCESSP_BTN_Y - 1 - 1 + (ACCESSP_BTN_HGT * (i + 1))); + } + } + gr_pop_canvas(); + // Now that we've popped the canvas, we can send the + // updated mfd to screen + mfd_update_rects(mfd); +#ifndef WORKING_INC_MFDS + mfd_notify_func(MFD_ACCESSPANEL_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); +#endif + } else { + ObjID obj = panel_ref_unexpose(mfd->id, MFD_ACCESSPANEL_FUNC); + if (obj != OBJ_NULL) { + objs[obj].info.current_frame = 0; + } + + return; + } +} + +// returns the "charged" version of the given state, if any. For states +// which cannot be charged (empty and open) return the given state. +// +gpz_state gpz_charge_state(gpz_state s) { + if (s == GPZ_EMPTY) + return s; + return ((gpz_state)((int)s | 1)); +} + +// returns the "uncharged" version of the given state, if any. Note that +// empty and open are their own uncharged states. +gpz_state gpz_uncharge_state(gpz_state s) { + if (s == GPZ_OPEN) + return s; + return ((gpz_state)((int)s & ~1)); +} + +// returns TRUE iff s is a "charged" state. +uchar gpz_is_charged(gpz_state s) { return (s != GPZ_OPEN && (s & 1)); } + +void gpz_toggle_state(gridFlowPuzzle *gfpz, short r, short c) { + gpz_state s; + + s = gpz_get_grid_state(gfpz, r, c); + s = gpz_uncharge_state(s); + if (s == GPZ_OPEN) + gpz_set_grid_state(gfpz, r, c, GPZ_CLOSED); + else if (s == GPZ_CLOSED) + gpz_set_grid_state(gfpz, r, c, GPZ_OPEN); +} + +// bitarray_get() +// Treating "base" as an array of elements of given size "siz", get +// element number "off". Works for bizzare sizes like 3 bits, packing. +// elements within ints and across int boundaries. +// +gpz_state bitarray_get(short siz, short off, uint *base) { + short loc, shft, ans, over; + + loc = (siz * off) / (sizeof(uint) * 8); + shft = (siz * off) % (sizeof(uint) * 8); + + // get from appropriate offset + ans = base[loc] >> shft; + + // did we cross a word boundary? + over = siz - ((sizeof(uint) * 8) - shft); + if (over > 0) { + ans = ans & ((1 << (siz - over)) - 1); + + ans |= base[loc + 1] << (siz - over); + } + + // mask down to siz bits + ans = ans & ((1 << siz) - 1); + + return (gpz_state)ans; +} + +// bitarray_set() +// Treating "base" as an array of elements of given size "siz", set +// element number "off" to specified value "val." Works for bizzare sizes +// like 3 bits, packing elements within an int and across int boundaries. +// +void bitarray_set(short siz, short off, ushort val, uint *base) { + short loc, shft, over; + + loc = (siz * off) / (sizeof(uint) * 8); + shft = (siz * off) % (sizeof(uint) * 8); + + // restrict to valid range + val &= (1 << siz) - 1; + + // clear destination bits + base[loc] &= ~(((1 << siz) - 1) << shft); + // set new bits + base[loc] |= val << shft; + + // did we cross a word boundary? + over = siz - ((sizeof(uint) * 8) - shft); + if (over > 0) { + base[loc + 1] &= ~((1 << over) - 1); + base[loc + 1] |= val >> (siz - over); + } +} + +errtype mfd_gridpanel_init(MFD_Func *f) { + uchar mfd_gridpanel_button_handler(MFD * mfd, LGPoint bttn, uiEvent * ev, void *data); + int cnt = 0; + errtype err; + LGPoint bsize = {GRIDP_BTN_WD, GRIDP_BTN_HGT}; + LGPoint bdims = {GRIDP_BTN_COL, GRIDP_BTN_ROW}; + LGRect r = {{GRIDP_BTN_X, GRIDP_BTN_Y}, {GRIDP_BTN_X + GRIDP_FULL_WD, GRIDP_BTN_Y + GRIDP_FULL_HGT}}; + err = MFDBttnArrayInit(&f->handlers[cnt++], &r, bdims, bsize, mfd_gridpanel_button_handler, NULL); + if (err != OK) + return err; + f->handler_count = cnt; + gridCursorbm.bits = gridCursorBits; + return OK; +} + +#define GPZ_GOOD_SEARCH_DEPTH 4 +int gpz_search_depth(gridFlowPuzzle *gfpz) { + if (gfpz->gfLayout.control_alg == GPZ_SIMPLE) + return (gfpz->gfLayout.rows * gfpz->gfLayout.cols); + else + return (GPZ_GOOD_SEARCH_DEPTH); +} + +// +// When I lie next to you +// I shiver and shake. +// Tell me you love me, +// I dream I'm awake. +// + +// attempts to add a gate to a grid puzzle in such a way as to keep +// it solvable. Finds candidates for this new gate and tries them +// out with the same solver used by in-game "logic probes". If it can +// solve the puzzle with the new gate, it accepts the new gate. +// +// Times out in this attempt after 1 second. +// +void gpz_add_gate(gridFlowPuzzle *gfpz, ObjID me) { + int r, c, count, rr, cc, n; + gpz_state adj, tmp; + ulong timeout; + ushort seed = (ushort)me; + + // if already opened panel once, don't bother. + // note that this isn't really correct, because there exists + // a panel that you might click on without opening it, because + // it has a comparator. However, we secretly know that the + // puzzle in this case is not one which can get a new gate + // anyway, so this happens to work out. Beware. + + if (objs[me].info.inst_flags & OLH_INST_FLAG) + return; + + timeout = *tmd_ticks + (CIT_CYCLE); + + gpz_uncharge_grid(gfpz); + // find a node which could possibly be charged, and has + // at least three adjacent nodes which could possibly be charged. + // try changing it to a gate and seeing if you can solve the + // puzzle. + for (rr = 0; rr < gfpz->gfLayout.rows; rr++) { + for (cc = 0; cc < gfpz->gfLayout.cols; cc++) { + // note that tight_loop is getting called in the time-consuming + // part of this, find_winning_move, so hopefully we don't have + // to call it here. + + // change our origin from 0,0 so we don't always put + // gates in the upper left. + n = rr + cc * gfpz->gfLayout.rows; + n = (n + seed) % (gfpz->gfLayout.rows * gfpz->gfLayout.cols); + r = n % gfpz->gfLayout.rows; + c = n / gfpz->gfLayout.rows; + if (gpz_get_grid_state(gfpz, r, c) == GPZ_EMPTY) + continue; + // count adjacent chargeable nodes. + { + count = 0; + adj = gpz_get_grid_state(gfpz, r - 1, c); + count += (adj != GPZ_EMPTY); + adj = gpz_get_grid_state(gfpz, r + 1, c); + count += (adj != GPZ_EMPTY); + adj = gpz_get_grid_state(gfpz, r, c - 1); + count += (adj != GPZ_EMPTY); + adj = gpz_get_grid_state(gfpz, r, c + 1); + count += (adj != GPZ_EMPTY); + } + if (count >= 3) { + tmp = gpz_get_grid_state(gfpz, r, c); + gpz_set_grid_state(gfpz, r, c, GPZ_GATE); + if (find_winning_move(gfpz, NULL, gpz_search_depth(gfpz), NULL, FALSE, timeout)) { + // it's solvable. Keep the change. + return; + } else if (*tmd_ticks > timeout) { + gpz_set_grid_state(gfpz, r, c, tmp); + return; + } + gpz_set_grid_state(gfpz, r, c, tmp); + } + } + } + return; +} + +uchar mfd_solve_gridpanel() { + gridFlowPuzzle *gfpz = (gridFlowPuzzle *)&player_struct.mfd_access_puzzles[0]; + gridFlowPuzzle solved; + int search_depth; + uchar found, shadow; + ObjID id = gfpz->gfLayout.our_id; + uchar temp[sizeof(player_struct.mfd_access_puzzles)]; + // for slow machines; don't give probe more than a couple of + // seconds to solve a panel. This time limit is set to 0 to + // mean no time limit. + long logic_probe_timeout = *tmd_ticks + EPICK_TIMEOUT; + + if (gfpz->gfLayout.have_won) + return EPICK_PRESOL; + + shadow = (WE_ARE_SIGNED() && SHADOW_PANEL_ID == id && SHADOW_PANEL_LEV == player_struct.level && + !(objFixtures[objs[id].specID].p2 & 0x10000)); + if (shadow) { + memcpy(temp, player_struct.mfd_access_puzzles, sizeof(player_struct.mfd_access_puzzles)); + memcpy(player_struct.mfd_access_puzzles, SHADOW_PANEL_STOR, sizeof(player_struct.mfd_access_puzzles)); + } + + search_depth = gpz_search_depth(gfpz); + + found = find_winning_move(gfpz, &solved, search_depth, NULL, FALSE, logic_probe_timeout); + if (found && solved.gfLayout.have_won) { + *gfpz = solved; + gpz_4int_update(gfpz); + mfd_notify_func(MFD_GRIDPANEL_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); + } else if (shadow) + memcpy(player_struct.mfd_access_puzzles, temp, sizeof(player_struct.mfd_access_puzzles)); + + return (gfpz->gfLayout.have_won ? EPICK_SOLVED : EPICK_FAILED); +} + +// gets state of grid node at coordinates row,col. If row or col is off +// the grid, then GPZ_EMPTY is returned. +// +#define GPZ_PERIM_TYPEMASK 0x10 +#define GPZ_PERIM_SIDEMASK 0x08 +gpz_state gpz_get_grid_state(gridFlowPuzzle *gfpz, short row, short col) { + void gpz_perimeter_to_grid(gridFlowPuzzle * gfpz, ushort per, short *r, short *c); + short r, c, *q; + + if (row < 0 || col < 0 || row >= (gfpz->gfLayout.rows) || col >= (gfpz->gfLayout.cols)) { + gpz_perimeter_to_grid(gfpz, gfpz->gfLayout.src, &r, &c); + if (gfpz->gfLayout.src & GPZ_PERIM_TYPEMASK) + q = &c; + else + q = &r; + *q += (gfpz->gfLayout.src & GPZ_PERIM_SIDEMASK) ? 1 : -1; + + if (row == r && c == col) + return GPZ_CLOSED_CHARGED; + + gpz_perimeter_to_grid(gfpz, gfpz->gfLayout.dest, &r, &c); + if (gpz_is_charged(gpz_get_grid_state(gfpz, r, c))) { + if (gfpz->gfLayout.dest & GPZ_PERIM_TYPEMASK) + q = &c; + else + q = &r; + *q += (gfpz->gfLayout.dest & GPZ_PERIM_SIDEMASK) ? 1 : -1; + + if (row == r && c == col) + return GPZ_CLOSED_CHARGED; + } + + return (GPZ_EMPTY); + } + return (bitarray_get(GRIDP_STATE_BITS, col + row * (gfpz->gfLayout.cols), gfpz->states)); +} + +// sets state of grid node at coordinates row, col. No effect if row or +// col is off the grid. +void gpz_set_grid_state(gridFlowPuzzle *gfpz, short row, short col, gpz_state val) { + if (row < 0 || col < 0 || row >= (gfpz->gfLayout.rows) || col >= (gfpz->gfLayout.cols)) + return; + bitarray_set(GRIDP_STATE_BITS, col + row * (gfpz->gfLayout.cols), val, gfpz->states); +} + +void gpz_uncharge_grid(gridFlowPuzzle *gfpz) { + int r, c; + gpz_state s; + + for (r = 0; r < gfpz->gfLayout.rows; r++) { + for (c = 0; c < gfpz->gfLayout.cols; c++) { + s = gpz_get_grid_state(gfpz, r, c); + gpz_set_grid_state(gfpz, r, c, gpz_uncharge_state(s)); + } + } +} + +void gpz_perimeter_to_grid(gridFlowPuzzle *gfpz, ushort per, short *r, short *c) { + ushort rr, cc, edge; + + edge = (per >> 3) & 3; + per = per & 7; + + rr = gfpz->gfLayout.rows; + cc = gfpz->gfLayout.cols; + + switch (edge) { + case 0: + *r = 0; + *c = per; + break; + case 1: + *r = rr - 1; + *c = per; + break; + case 2: + *c = 0; + *r = per; + break; + case 3: + *c = cc - 1; + *r = per; + break; + } + return; +} + +uchar gpz_state_charged(gridFlowPuzzle *gfpz, short r, short c) { + return (gpz_is_charged(gpz_get_grid_state(gfpz, r, c))); +} + +#ifdef SHOW_DONENESS +// Find the shortest Manhatten distance from a charged node to the +// destination node. Converts this to a rating in the range 0 to +// 255 by normalizing to the greatest Manhatten distance from the +// destination of any node in the puzzle. + +uchar gpz_doneness(gridFlowPuzzle *gfpz) { + int farthest, doneness, dist; + short r, c, dr, dc; + + doneness = INT_MAX; + farthest = INT_MIN; + + gpz_perimeter_to_grid(gfpz, gfpz->gfLayout.dest, &dr, &dc); + + for (r = 0; r < gfpz->gfLayout.rows; r++) { + for (c = 0; c < gfpz->gfLayout.cols; c++) { + + dist = abs(r - dr) + abs(c - dc); + if (dist > farthest) + farthest = dist; + if (gpz_state_charged(gfpz, r, c) && dist < doneness) + doneness = dist; + } + } + + if (doneness == INT_MAX) + return 0; + + return (UCHAR_MAX * (farthest - doneness) / farthest); +} +#endif + +// recalculates "current" or "charge" flow through grid. Returns TRUE +// iff destination node is charged. +// +uchar gpz_propogate_charge_n_check(gridFlowPuzzle *gfpz) { + uchar flow; + short r, c, src_r, src_c; + gpz_state s; + extern errtype accesspanel_trigger(ObjID id); + + // uncharge whole grid + gpz_uncharge_grid(gfpz); + + // charge source node + gpz_perimeter_to_grid(gfpz, gfpz->gfLayout.src, &r, &c); + + s = gpz_get_grid_state(gfpz, r, c); + if (s == GPZ_CLOSED || s == GPZ_FULL) + gpz_set_grid_state(gfpz, r, c, gpz_charge_state(gpz_get_grid_state(gfpz, r, c))); + + // propogate charges until the propogatin's done. This must terminate + // 'cause we only set "flow" when we charge a node, and eventually we + // will run out of nodes to charge. + do { + flow = FALSE; + + for (r = 0; r < gfpz->gfLayout.rows; r++) { + for (c = 0; c < gfpz->gfLayout.cols; c++) { + int sum; + s = gpz_get_grid_state(gfpz, r, c); + src_r = r; + src_c = c; + switch (s) { + case GPZ_CLOSED: + case GPZ_FULL: + case GPZ_GATE: + sum = (int)gpz_state_charged(gfpz, --src_r, src_c); + sum += (int)gpz_state_charged(gfpz, ++src_r, --src_c); + sum += (int)gpz_state_charged(gfpz, ++src_r, ++src_c); + sum += (int)gpz_state_charged(gfpz, --src_r, ++src_c); + if (s == GPZ_GATE) { + if (sum >= 2) { + gpz_set_grid_state(gfpz, r, c, gpz_charge_state(s)); + flow = TRUE; + } + } else { + if (sum > 0) { + gpz_set_grid_state(gfpz, r, c, gpz_charge_state(s)); + flow = TRUE; + } + } + break; + /* + if(gpz_state_charged(gfpz,--src_r,src_c) + || gpz_state_charged(gfpz,++src_r,--src_c) + || gpz_state_charged(gfpz,++src_r,++src_c) + || gpz_state_charged(gfpz,--src_r,++src_c)) { + gpz_set_grid_state(gfpz,r,c,gpz_charge_state(s)); + flow=TRUE; + } + break; + if(gpz_state_charged(gfpz,--src_r,src_c) + + gpz_state_charged(gfpz,++src_r,--src_c) + + gpz_state_charged(gfpz,++src_r,++src_c) + + gpz_state_charged(gfpz,--src_r,++src_c) >= 2) { + gpz_set_grid_state(gfpz,r,c,gpz_charge_state(s)); + flow=TRUE; + } + break; + */ + // default if state is already charged or cannot be charged. + default: + break; + } + } + } + } while (flow); + + // do we win? + gpz_perimeter_to_grid(gfpz, gfpz->gfLayout.dest, &r, &c); + s = gpz_get_grid_state(gfpz, r, c); + return (gpz_is_charged(s)); +} + +uchar find_winning_move_from(gridFlowPuzzle *gfpz, gridFlowPuzzle *solved, LGPoint *move, int r0, int c0, int dep, + ulong timeout) { + gpz_state gridpanel_move(LGPoint node, gridFlowPuzzle * gfpz); + int r, c, real_c0 = c0; + gpz_state s, pass; + LGPoint try_it; + // this will never put more copies on the stack at a time than our total search depth + // (currently no more than 4) at 32 bytes per copy. + gridFlowPuzzle copy; + + copy = *gfpz; + + // try all moves, working to the right and then down. Since moves are + // commutative and reversible, we need only try moves in this one + // particular order. Try moving open circuits to closed circuits + // first as a heuristic. Also, don't bother with the second pass + // (changing closed circuits to open circuits) if we have the simple + // scoring algorithm. + + for (pass = GPZ_OPEN; pass != GPZ_EMPTY;) { + c0 = real_c0; + for (r = r0; r < gfpz->gfLayout.rows && dep > 0; r++) { + for (c = c0; c < gfpz->gfLayout.cols && dep > 0; c++) { + tight_loop(FALSE); + if (timeout != 0 && *tmd_ticks > timeout) { + return (FALSE); + } + s = gpz_uncharge_state(gpz_get_grid_state(©, r, c)); + if (s == pass) { + try_it.x = c; + try_it.y = r; + gridpanel_move(try_it, ©); + + if (gpz_propogate_charge_n_check(©)) { + if (move) + *move = try_it; + if (solved) { + *solved = copy; + solved->gfLayout.have_won = TRUE; + } + return TRUE; + } + if (gfpz->gfLayout.control_alg == GPZ_SIMPLE) { + dep--; + } else if (dep > 1) { + int new_r0 = r, new_c0 = c + 1; + + if (new_c0 >= gfpz->gfLayout.cols) { + new_c0 = 0; + new_r0++; + } + if (new_r0 < gfpz->gfLayout.rows && + find_winning_move_from(©, solved, move, new_r0, new_c0, dep - 1, timeout)) { + return TRUE; + } else { + // undo this move and try another. + memcpy(copy.states, gfpz->states, sizeof(copy.states)); + } + } else + memcpy(copy.states, gfpz->states, sizeof(copy.states)); + } + } + c0 = 0; + } + if (pass == GPZ_OPEN && gfpz->gfLayout.control_alg != GPZ_SIMPLE) { + // solutions requiring steps of opening closed circuits + // are likely to be inferior. Do not search as deep. + if (dep == 1) + pass = GPZ_EMPTY; + else { + dep--; + pass = GPZ_CLOSED; + } + } else + pass = GPZ_EMPTY; // in other words, stop. + } + return FALSE; +} + +// be very careful with depth here; search time can increase exponentially +// with depth, ya know. +// +uchar find_winning_move(gridFlowPuzzle *gfpz, gridFlowPuzzle *solved, int depth, LGPoint *move, uchar breadth_first, + ulong timeout) { + int d; + + if (breadth_first) { + // brain-damaged breadth-first search by successively deep + // depth first searches. + for (d = 1; d <= depth; d++) + if (find_winning_move_from(gfpz, solved, move, 0, 0, d, timeout)) + return TRUE; + return FALSE; + } else { + return (find_winning_move_from(gfpz, solved, move, 0, 0, depth, timeout)); + } +} + +void gpz_setup_buttons(gridFlowPuzzle *gfpz) { + LGPoint bsize = {GRIDP_BTN_WD, GRIDP_BTN_HGT}; + LGPoint bdims = {GRIDP_BTN_COL, GRIDP_BTN_ROW}; + LGRect rct = {{GRIDP_BTN_X, GRIDP_BTN_Y}, {GRIDP_BTN_X + GRIDP_FULL_WD, GRIDP_BTN_Y + GRIDP_FULL_HGT}}; + + bdims.x = gfpz->gfLayout.cols; + bdims.y = gfpz->gfLayout.rows; + rct.ul.x = (MFD_VIEW_WID - (GRIDP_BTN_WD * gfpz->gfLayout.cols)) / 2 + GRIDP_X_OFFSET; + rct.ul.y = (MFD_VIEW_HGT - (GRIDP_BTN_HGT * gfpz->gfLayout.rows)) / 2 + GRIDP_Y_OFFSET; + rct.lr.x = rct.ul.x + (GRIDP_BTN_WD * gfpz->gfLayout.cols); + rct.lr.y = rct.ul.y + (GRIDP_BTN_HGT * gfpz->gfLayout.rows); + MFDBttnArrayResize(&(mfd_funcs[MFD_GRIDPANEL_FUNC].handlers[0]), &rct, bdims, bsize); +} + +void gpz_4int_init(gridFlowPuzzle *gfpz, uint p1, uint p2, uint p3, uint p4) { + uint state_init[4]; + int r, c, rr, cc, soff, easified = 0; + gpz_state si; + // strip reserved bits from p2 + p2 &= 0xFFFF; + + if (!objs[p2].active || objs[p2].obclass != CLASS_TRAP) { + memset(state_init, 0, sizeof(state_init)); + } else { + ObjTrap *other = &objTraps[objs[p2].specID]; + + state_init[3] = other->p1; + state_init[2] = other->p2; + state_init[1] = other->p3; + state_init[0] = other->p4; + } + + gfpz->gfLayout.have_won = p3 & 0xF; + p3 = p3 >> 4; + gfpz->gfLayout.src = p3 & 0xFF; + p3 = p3 >> 8; + gfpz->gfLayout.dest = p3 & 0xFF; + p3 = p3 >> 8; + gfpz->gfLayout.cols = cc = p3 & 0xF; + p3 = p3 >> 4; + gfpz->gfLayout.rows = rr = p3 & 0xF; + p3 = p3 >> 4; + gfpz->gfLayout.control_alg = p3 & 0xF; + + gfpz->gfLayout.winmove_f = 0; + mfd_gridpanel_set_winmove(FALSE); + +#ifdef GRIDP_AUTO_SOLVE + gfpz->gfLayout.solve_me = 0; +#endif + + soff = 0; + for (r = 0; r < rr; r++) { + for (c = 0; c < cc; c++) { + si = bitarray_get(GRIDP_STATE_BITS, soff++, state_init); + si = gpz_uncharge_state(si); + if (PUZZLE_DIFFICULTY <= 1) { + switch (si) { + case GPZ_GATE: + si = GPZ_FULL; + break; + case GPZ_EMPTY: + if (((player_struct.panel_ref + (easified++)) & 3) == 0) + si = GPZ_OPEN; + break; + default: + break; + } + } + gpz_set_grid_state(gfpz, r, c, si); + } + } + if (PUZZLE_DIFFICULTY == MAX_DIFFICULTY) { + gpz_add_gate(gfpz, player_struct.panel_ref); + } + + grid_primary_mfd = -1; +} + +#define GPZ_WIN_MASK 1 +void gpz_4int_update(gridFlowPuzzle *gfpz) { + uint init_st[4], p2; + int nodecount, node; + gpz_state s; + + nodecount = gfpz->gfLayout.rows * gfpz->gfLayout.cols; + memset(init_st, 0, sizeof(init_st)); + + for (node = 0; node < nodecount; node++) { + s = bitarray_get(GRIDP_STATE_BITS, node, gfpz->states); + s = gpz_uncharge_state(s); + bitarray_set(GRIDP_STATE_BITS, node, s, init_st); + } + + p2 = objFixtures[objs[gfpz->gfLayout.our_id].specID].p2; + p2 = p2 & 0xFFFF; + if (!objs[p2].active || objs[p2].obclass != CLASS_TRAP) { + return; + } else { + ObjTrap *other = &objTraps[objs[p2].specID]; + + other->p1 = init_st[3]; + other->p2 = init_st[2]; + other->p3 = init_st[1]; + other->p4 = init_st[0]; + } + + if (gfpz->gfLayout.have_won) { + uint p3; + + p3 = objFixtures[objs[gfpz->gfLayout.our_id].specID].p3; + p3 = p3 | GPZ_WIN_MASK; + objFixtures[objs[gfpz->gfLayout.our_id].specID].p3 = p3; + } +} + +// executes a move at the given node in the given grid puzzle. +// returns the state of the given node after the move. +// +gpz_state gridpanel_move(LGPoint node, gridFlowPuzzle *gfpz) { + short r, c, sum, dif; + gpz_state s; + + s = gpz_get_grid_state(gfpz, node.y, node.x); + s = gpz_uncharge_state(s); + + if (s == GPZ_OPEN || s == GPZ_CLOSED) { + switch ((gpz_control)gfpz->gfLayout.control_alg) { + case GPZ_SIMPLE: + gpz_toggle_state(gfpz, node.y, node.x); + break; + case GPZ_KING: + for (r = node.y - 1; r <= node.y + 1; r++) { + for (c = node.x - 1; c <= node.x + 1; c++) { + gpz_toggle_state(gfpz, r, c); + } + } + break; + case GPZ_QUEEN: + case GPZ_ROOK: + for (r = 0; r < gfpz->gfLayout.rows; r++) { + gpz_toggle_state(gfpz, r, node.x); + } + for (c = 0; c < gfpz->gfLayout.cols; c++) { + if (c == node.x) + continue; + gpz_toggle_state(gfpz, node.y, c); + } + if (gfpz->gfLayout.control_alg == GPZ_ROOK) + break; // queen falls through to bishop. + case GPZ_BISH: + sum = node.x + node.y; + dif = node.x - node.y; + for (r = 0; r < gfpz->gfLayout.rows; r++) { + gpz_toggle_state(gfpz, r, sum - r); + gpz_toggle_state(gfpz, r, dif + r); + } + // make sure with bishop & queen to toggle center only once. + gpz_set_grid_state(gfpz, node.y, node.x, s); + gpz_toggle_state(gfpz, node.y, node.x); + break; + } + } + + s = gpz_get_grid_state(gfpz, node.y, node.x); + + return (s); +} + +void mfd_gridpanel_set_winmove(uchar check) { + gridFlowPuzzle *gfpz; + + if (check && player_struct.mfd_all_slots[MFD_INFO_SLOT] != MFD_GRIDPANEL_FUNC) + return; + + gfpz = (gridFlowPuzzle *)&player_struct.mfd_access_puzzles[0]; + + if (!gfpz->gfLayout.have_won && player_struct.drug_status[CPTRIP(GENIUS_DRUG_TRIPLE)] > 0) { + uchar gotone; + LGPoint winner; + short diff; + + if (gfpz->gfLayout.control_alg == GPZ_SIMPLE) + diff = 99; + else + diff = (PUZZLE_DIFFICULTY == MAX_DIFFICULTY) ? 2 : 3; + + gotone = find_winning_move(gfpz, NULL, diff, &winner, TRUE, *tmd_ticks + EPICK_TIMEOUT); + + if (gotone) { + gfpz->gfLayout.winmove_c = winner.x; + gfpz->gfLayout.winmove_r = winner.y; + } + gfpz->gfLayout.winmove_f = gotone; + + mfd_notify_func(MFD_GRIDPANEL_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); + } +} + +uchar mfd_gridpanel_button_handler(MFD *mfd, LGPoint bttn, uiEvent *ev, void *data) { + gridFlowPuzzle *gfpz = (gridFlowPuzzle *)&player_struct.mfd_access_puzzles[0]; + gpz_state s; + + if (mfd->id != grid_primary_mfd) + return TRUE; + + if ((ev->subtype & (MOUSE_LDOWN | UI_MOUSE_LDOUBLE)) == 0) + return TRUE; + + if (gfpz->gfLayout.have_won) + return TRUE; + +#ifdef GRIDP_AUTO_SOLVE + if (!gfpz->gfLayout.solve_me) + gfpz->gfLayout.solve_me = TRUE; +#endif + + gfpz->gfLayout.winmove_f = 0; + s = gridpanel_move(bttn, gfpz); + s = gpz_uncharge_state(s); + +#ifdef GRIDP_AUTO_SOLVE + mfd_notify_func(MFD_GRIDPANEL_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); +#endif + + if (s == GPZ_OPEN || s == GPZ_CLOSED) { + gpz_4int_update(gfpz); + mfd_notify_func(MFD_GRIDPANEL_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); + } + + mfd_gridpanel_set_winmove(FALSE); + + return TRUE; +} + +// Does every thing but set the slot.. +void mfd_setup_gridpanel(ObjID id) { + uint p1, p2, p3, p4; + gridFlowPuzzle *gfpz = (gridFlowPuzzle *)&player_struct.mfd_access_puzzles[0]; + p1 = objFixtures[objs[id].specID].p1; + p2 = objFixtures[objs[id].specID].p2; + p3 = objFixtures[objs[id].specID].p3; + p4 = objFixtures[objs[id].specID].p4; + + gfpz->gfLayout.our_id = id; + gpz_4int_init(gfpz, p1, p2, p3, p4); + mfd_notify_func(MFD_GRIDPANEL_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, TRUE); +} + +uchar mfd_gridpanel_handler(MFD *m, uiEvent *ev) { + extern uchar mfd_gridpanel_handler(MFD *, uiEvent *); + uiCursorStack *cs; + gridFlowPuzzle *gfpz = (gridFlowPuzzle *)&player_struct.mfd_access_puzzles[0]; + int rr = gfpz->gfLayout.rows; + int cc = gfpz->gfLayout.cols; + LGRect r; + + r.ul.x = (MFD_VIEW_WID - (GRIDP_BTN_WD * cc)) / 2 + GRIDP_X_OFFSET; + r.ul.y = (MFD_VIEW_HGT - (GRIDP_BTN_HGT * rr)) / 2 + GRIDP_Y_OFFSET; + r.lr.y = r.ul.y + rr * GRIDP_BTN_HGT; + r.lr.x = r.ul.x + cc * GRIDP_BTN_WD; + RECT_MOVE(&r, m->rect.ul); + uiGetRegionCursorStack(&m->reg, &cs); + if (RECT_TEST_PT(&r, ev->pos)) { + uiPushCursorOnce(cs, &gridCursor); + } else { + uiPopCursorEvery(cs, &gridCursor); + } +#ifdef EPICK_ON_CURSOR_TRY + extern uchar try_use_epick(ObjID panel, ObjID cursor_obj); + + if (ev->type != UI_EVENT_MOUSE || !(ev->subtype & (MOUSE_LDOWN | UI_MOUSE_LDOUBLE))) + return FALSE; + + return (try_use_epick(player_struct.panel_ref, object_on_cursor)); +#else + return (FALSE); +#endif +} + +#define GPZ_CHARGE_COLOR (GREEN_YELLOW_BASE + 3) +#define GPZ_BASE_CHARGE_COLOR 0x3 +#define GPZ_RANGE_CHARGE_COLOR 5 +#define GPZ_CHARGE_DK_COLOR 0x56 +#define GPZ_SOURCE_COLOR (GREEN_YELLOW_BASE + 1) +#define GPZ_BOX_COLOR (0xb0 + 9) +#define GPZ_BACK_COLOR (0xb0 + 15) +#define GPZ_WIN_COLOR (RED_BASE + 7) +#define GPZ_NOALG_COLOR 0xA4 + +void wacky_int_line(short x1, short y1, short x2, short y2) { + short delt; + + if (x1 == x2) { + delt = (y2 < y1) ? -1 : 1; + for (; y1 != y2 + delt; y1 += delt) { + ss_set_pixel(GPZ_BASE_CHARGE_COLOR + (256 - x1 - y1) % GPZ_RANGE_CHARGE_COLOR, x1, y1); + } + } else { + delt = (x2 < x1) ? -1 : 1; + for (; x1 != x2 + delt; x1 += delt) { + ss_set_pixel(GPZ_BASE_CHARGE_COLOR + (256 - x1 - y1) % GPZ_RANGE_CHARGE_COLOR, x1, y1); + } + } +} + +char *grid_help_string(gridFlowPuzzle *gfpz, char *buf, int siz) { + int l; + char *ret = buf; + + if (gfpz->gfLayout.have_won) + get_string(REF_STR_PanelSolved, buf, siz); + else { + get_string(REF_STR_GridPuzzHelp, buf, siz); + l = strlen(buf); + buf += l; + siz -= l; + if (player_struct.drug_status[CPTRIP(GENIUS_DRUG_TRIPLE)] > 0) + get_string(REF_STR_GridPuzzSide0 + gfpz->gfLayout.control_alg, buf, siz); + else + get_string(REF_STR_GridPuzzSideMay, buf, siz); + } + return (ret); +} + +// initializes color look-up table to identity function +void id_clut_init(uchar *clut) { + int i; + + for (i = 0; i < 256; i++) { + clut[i] = i; + } +} + +// I remember the feeling +// my hands in your hair + +void mfd_gridpanel_expose(MFD *mfd, ubyte control) { + void mfd_clear_view(void); + int rr, cc, r, c, x, y, ulx, uly, lry; + int bcolor; + short sc, sr, p; + gpz_state s, nearstate; + gridFlowPuzzle *gfpz = (gridFlowPuzzle *)&player_struct.mfd_access_puzzles[0]; + uchar full = control & MFD_EXPOSE_FULL; + +#ifdef SVGA_SUPPORT + // Whatta hack! + if (convert_use_mode) + full = TRUE; +#endif + + rr = gfpz->gfLayout.rows; + cc = gfpz->gfLayout.cols; + + ulx = (MFD_VIEW_WID - (GRIDP_BTN_WD * cc)) / 2 + GRIDP_X_OFFSET; + uly = (MFD_VIEW_HGT - (GRIDP_BTN_HGT * rr)) / 2 + GRIDP_Y_OFFSET; + + if (control == 0) { + uiCursorStack *cs; + uiGetRegionCursorStack(&mfd->reg, &cs); + uiPopCursorEvery(cs, &gridCursor); + } + + if (control & MFD_EXPOSE) // Time to draw stuff + { + short dr, dc; + uchar win, primary, winblink = FALSE; + + if (gfpz->gfLayout.winmove_f) { + mfd_notify_func(MFD_GRIDPANEL_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); + } + + if ((full && grid_primary_mfd < 0) || player_struct.mfd_current_slots[grid_primary_mfd] != MFD_INFO_SLOT) { + full = TRUE; // if we weren't full exposing before, we are now + grid_primary_mfd = mfd->id; + } + + primary = (mfd->id == grid_primary_mfd); + + if (primary) { + + id_clut_init(grid_help_clut); + if (full) + gpz_setup_buttons(gfpz); + win = gpz_propogate_charge_n_check(gfpz); + if ((gfpz->gfLayout.have_won == 0) && win) { + accesspanel_trigger(player_struct.panel_ref); + gfpz->gfLayout.have_won = 1; + gpz_4int_update(gfpz); + } + } + + mfd_clear_rects(); + gr_push_canvas(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + if (!primary) { + char buf[120]; + grid_help_string(gfpz, buf, 120); + mfd_clear_view(); + draw_help_text(buf, FALSE, gfpz); + gr_pop_canvas(); + mfd_update_rects(mfd); + return; + } + +#ifdef PUZZ_DIFF_NOHELP + // set color of wires to indicate control algorithm, unless we're + // on hardest difficulty. + if (PUZZLE_DIFFICULTY < MAX_DIFFICULTY) + bcolor = gpz_base_colors[gfpz->gfLayout.control_alg]; + else + bcolor = GPZ_NOALG_COLOR; +#else + bcolor = gpz_base_colors[gfpz->gfLayout.control_alg]; +#endif + + if (gfpz->gfLayout.have_won) + full = TRUE; + lry = uly + rr * GRIDP_BTN_HGT; + + if (full) { + load_res_bitmap_cursor(&gridCursor, &gridCursorbm, REF_IMG_RookSymbol + gfpz->gfLayout.control_alg, FALSE); + mfd_clear_view(); + } + + ss_safe_set_cliprect(ulx, uly, ulx + cc * GRIDP_BTN_WD, lry + 7); + draw_res_bm(REF_IMG_CircuitBack, ulx, uly); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + gr_set_fcolor(GPZ_BOX_COLOR); + ss_box(ulx - 1, uly - 1, ulx + cc * GRIDP_BTN_WD + 1, lry + 1); + gr_set_fcolor(bcolor); + ss_box(ulx - 2, uly - 2, ulx + cc * GRIDP_BTN_WD + 2, lry + 2); + mfd_add_rect(ulx - 2, uly - 2, ulx + cc * GRIDP_BTN_WD + 2, lry + 2); + + ss_box(ulx - 2, lry + 3, ulx + cc * GRIDP_BTN_WD + 2, lry + 7); + gr_set_fcolor(ACCESSP_SCORE_COL); + ss_rect(ulx - 1, lry + 4, ulx + 1 + gpz_doneness(gfpz) * cc * GRIDP_BTN_WD / UCHAR_MAX, lry + 6); + mfd_add_rect(ulx - 2, lry + 3, ulx + cc * GRIDP_BTN_WD + 2, lry + 7); + + if (full) { + gpz_perimeter_to_grid(gfpz, gfpz->gfLayout.src, &sr, &sc); + p = (gfpz->gfLayout.src >> 3) & 3; + switch (p) { + case 0: + sr = -1; + break; + case 1: + sr = rr; + break; + case 2: + sc = -1; + break; + case 3: + sc = cc; + break; + } + x = sc * GRIDP_BTN_WD + ulx; + y = sr * GRIDP_BTN_HGT + uly; + + gr_set_fcolor(GREEN_YELLOW_BASE + 2); + ss_box(x + 1, y + 1, x + GRIDP_BTN_WD - 1, y + GRIDP_BTN_HGT - 1); + gr_set_fcolor(5); + ss_box(x + 2, y + 2, x + GRIDP_BTN_WD - 2, y + GRIDP_BTN_HGT - 2); + gr_set_fcolor(7); + ss_rect(x + 3, y + 3, x + GRIDP_BTN_WD - 3, y + GRIDP_BTN_HGT - 3); + + gpz_perimeter_to_grid(gfpz, gfpz->gfLayout.dest, &sr, &sc); + p = (gfpz->gfLayout.dest >> 3) & 3; + switch (p) { + case 0: + sr = -1; + break; + case 1: + sr = rr; + break; + case 2: + sc = -1; + break; + case 3: + sc = cc; + break; + } + x = sc * GRIDP_BTN_WD + ulx; + y = sr * GRIDP_BTN_HGT + uly; + gr_set_fcolor(bcolor); + ss_box(x + 1, y + 1, x + GRIDP_BTN_WD - 1, y + GRIDP_BTN_HGT - 1); + if (gfpz->gfLayout.have_won) + gr_set_fcolor(GPZ_CHARGE_COLOR); + else + gr_set_fcolor(GPZ_BACK_COLOR); + ss_rect(x + 2, y + 2, x + GRIDP_BTN_WD - 2, y + GRIDP_BTN_HGT - 2); + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + } + + for (r = 0; r < rr; r++) { + for (c = 0; c < cc; c++) { + x = c * GRIDP_BTN_WD + ulx; + y = r * GRIDP_BTN_HGT + uly; + s = gpz_get_grid_state(gfpz, r, c); + winblink = (!gfpz->gfLayout.have_won && gfpz->gfLayout.winmove_f && gfpz->gfLayout.winmove_c == c && + gfpz->gfLayout.winmove_r == r && (*tmd_ticks) & (1 << 7)); + if (gpz_is_charged(s)) { + short sc, sr; + gpz_perimeter_to_grid(gfpz, gfpz->gfLayout.src, &sr, &sc); + if (sc == c && sr == r) + gr_set_fcolor(GPZ_SOURCE_COLOR); + else + gr_set_fcolor(GPZ_CHARGE_COLOR); + } else + gr_set_fcolor(bcolor); + if (winblink) + gr_set_fcolor(GPZ_WIN_COLOR); + switch (s) { + case GPZ_EMPTY: + break; + case GPZ_FULL_CHARGED: + case GPZ_FULL: + ss_rect(x, y, x + GRIDP_BTN_WD, y + GRIDP_BTN_HGT); + break; + case GPZ_OPEN: + ss_int_line(x + 1, y + 1, x + GRIDP_BTN_WD - 2, y + GRIDP_BTN_HGT - 2); + ss_int_line(x + GRIDP_BTN_WD - 2, y + 1, x + 1, y + GRIDP_BTN_HGT - 2); + if (s == GPZ_OPEN) + break; + case GPZ_CLOSED: + case GPZ_CLOSED_CHARGED: + gr_set_fcolor(winblink ? GPZ_WIN_COLOR : bcolor); + for (dr = -1; dr <= 1; dr++) { + for (dc = -1; dc <= 1; dc++) { + if (dr != 0 && dc != 0) + continue; + nearstate = gpz_get_grid_state(gfpz, r + dr, c + dc); +#ifndef DRAW_GRID_PLUSSES + if (nearstate == GPZ_EMPTY) + continue; +#endif + ss_int_line(x + (GRIDP_BTN_WD / 2), y + (GRIDP_BTN_HGT / 2), + x + (dc + 1) * (GRIDP_BTN_WD / 2), y + (dr + 1) * (GRIDP_BTN_HGT / 2)); + } + } + gr_set_fcolor(winblink ? GPZ_WIN_COLOR : GPZ_CHARGE_COLOR); + for (dr = -1; dr <= 1; dr++) { + for (dc = -1; dc <= 1; dc++) { + if (dr != 0 && dc != 0) + continue; + nearstate = gpz_get_grid_state(gfpz, r + dr, c + dc); + if (gpz_is_charged(nearstate) || (gpz_is_charged(s) && nearstate == GPZ_GATE)) { + wacky_int_line(x + (GRIDP_BTN_WD / 2), y + (GRIDP_BTN_HGT / 2), + x + (dc + 1) * (GRIDP_BTN_WD / 2), y + (dr + 1) * (GRIDP_BTN_HGT / 2)); + } + } + } + break; +#ifdef GPZ_GATES + case GPZ_GATE: + ss_box(x, y, x + GRIDP_BTN_WD, y + GRIDP_BTN_HGT); + gr_set_fcolor(gpz_dk_colors[gfpz->gfLayout.control_alg]); + ss_rect(x + 1, y + 1, x + GRIDP_BTN_WD - 1, y + GRIDP_BTN_HGT - 1); + break; + case GPZ_GATE_CHARGED: + ss_box(x, y, x + GRIDP_BTN_WD, y + GRIDP_BTN_HGT); + gr_set_fcolor(GPZ_CHARGE_DK_COLOR); + ss_rect(x + 1, y + 1, x + GRIDP_BTN_WD - 1, y + GRIDP_BTN_HGT - 1); + break; +#endif + } + } + } + + gr_pop_canvas(); + + } else { + ObjID obj = panel_ref_unexpose(mfd->id, MFD_GRIDPANEL_FUNC); + if (obj != OBJ_NULL) { + objs[obj].info.current_frame = 0; + } + } + // Now that we've popped the canvas, we can send the + // updated mfd to screen + mfd_update_rects(mfd); + +#ifdef GRIDP_AUTO_SOLVE + { + uiEvent ev; + LGPoint bttn; + + if (!gfpz->gfLayout.have_won) { + bttn.x = rand() % cc; + bttn.y = rand() % rr; + + ev.subtype = MOUSE_LDOWN; + + mfd_gridpanel_button_handler(NULL, bttn, &ev, NULL); + } + } +#endif +} diff --git a/engine/src/GameSrc/minimax.c b/engine/src/GameSrc/minimax.c new file mode 100644 index 0000000..a18118c --- /dev/null +++ b/engine/src/GameSrc/minimax.c @@ -0,0 +1,202 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/minimax.c $ + * $Revision: 1.2 $ + * $Author: tjs $ + * $Date: 1994/09/28 17:44:13 $ + * + */ + +#ifdef LOST_TREASURES_OF_MFD_GAMES +#include +#include +#include "minimax.h" + +#define MAX_POSITION_SIZE 32 + +static uchar *FauxStackBase; +static uchar *FauxStackPointer; +static uint FauxStackSize; + +static uint PositionSize; +static uchar (*generate_position)(void *parent, int index, bool minimizer_moves); +static int (*static_evaluator)(void *position); +static uchar (*extend_horizon)(void *position); + +#define StackSpew(x) + +void fstack_push(void *data, uint siz); +void fstack_pop(void *data, uint siz); +void *fstack_create(uint siz); +void fstack_flush(uint siz); + +void fstack_init(uchar *fs, uint siz) { + FauxStackSize = siz; + FauxStackBase = FauxStackPointer = fs; +} + +void fstack_push(void *data, uint siz) { + memcpy(FauxStackPointer, data, siz); + FauxStackPointer += siz; + StackSpew(("pushing %d bytes, top %d\n", siz, FauxStackPointer - FauxStackBase)); +} + +void fstack_pop(void *data, uint siz) { + FauxStackPointer -= siz; + memcpy(data, FauxStackPointer, siz); + StackSpew(("popping %d bytes, top %d\n", siz, FauxStackPointer - FauxStackBase)); +} + +void *fstack_create(uint siz) { + void *retval; + retval = FauxStackPointer; + FauxStackPointer += siz; + StackSpew(("moving top %d bytes, top %d\n", siz, FauxStackPointer - FauxStackBase)); + return ((void *)retval); +} + +void fstack_flush(uint siz) { + FauxStackPointer -= siz; + StackSpew(("flushing top %d bytes, top %d\n", siz, FauxStackPointer - FauxStackBase)); +} + +#define FSTACK_PUSHVAR(v) fstack_push(&v, sizeof(v)) +#define FSTACK_POPVAR(v) fstack_pop(&v, sizeof(v)) + +// ------------ +// MiniMax procedure +// Runs MiniMax evaluation of board position, using user-supplied +// stack for recursion. +// + +void minimax_get_result(int *val, char *which) { + fstack_pop(val, sizeof(int)); + fstack_pop(which, sizeof(char)); +} + +uchar minimax_done(void) { + // done if only value (an int) and move (a char) remain on stack + return (FauxStackPointer <= FauxStackBase + (sizeof(int) + sizeof(char))); +} + +void minimax_setup(void *boardpos, uint pos_siz, char depth, uchar minimize, int (*evaluator)(void *), + uchar (*generate)(void *, int, bool), uchar (*horizon)(void *)) { + char next_child = 0; + + static_evaluator = evaluator; + generate_position = generate; + extend_horizon = horizon; + PositionSize = pos_siz; + + // correspondence of these stack operations to the variables + // used in minimax_step commented below, even the obvious ones. + // + FSTACK_PUSHVAR(next_child); // next_child + fstack_create(sizeof(int)); // bestval + fstack_create(sizeof(char)); // which_child + FSTACK_PUSHVAR(minimize); // minimize + FSTACK_PUSHVAR(depth); // depth + fstack_push(boardpos, pos_siz); // boardpos + fstack_create(sizeof(char) + sizeof(int)); // return values +} + +void minimax_step(void) { + uchar boardpos[MAX_POSITION_SIZE]; + uchar copy[MAX_POSITION_SIZE]; + int value, bestval; + char next_child, which_child; + uchar depth; + uchar minimize; + + FSTACK_POPVAR(value); // get value from previous recursive call + fstack_flush(sizeof(which_child)); // flush which child gave best value + + fstack_pop(boardpos, PositionSize); + FSTACK_POPVAR(depth); + FSTACK_POPVAR(minimize); + FSTACK_POPVAR(which_child); + FSTACK_POPVAR(bestval); + FSTACK_POPVAR(next_child); + + if (depth == 0) { + if (extend_horizon && extend_horizon(boardpos)) { + depth = 1; + } else { + value = static_evaluator(boardpos); + fstack_create(sizeof(which_child)); + FSTACK_PUSHVAR(value); + return; + } + } + + if (next_child <= 0) { + bestval = minimize ? INT_MAX : INT_MIN; + } else { + if ((!minimize) ^ (value < bestval)) { // new value is better than previous + bestval = value; + which_child = next_child - 1; + } + } + +#define PRUNE_WIN +#ifdef PRUNE_WIN + // if have already found a winning move, do not continue + if (bestval == (minimize ? INT_MIN : INT_MAX)) { + FSTACK_PUSHVAR(which_child); + FSTACK_PUSHVAR(bestval); + return; + } +#endif + + memcpy(copy, boardpos, PositionSize); + if (!generate_position(copy, next_child, minimize)) { // no more children to be had + if (next_child == 0) // there were no children at all! + bestval = static_evaluator(boardpos); + FSTACK_PUSHVAR(which_child); + FSTACK_PUSHVAR(bestval); + return; + } + + // before setting up for recursion, preserve our local parameters + next_child++; + FSTACK_PUSHVAR(next_child); + FSTACK_PUSHVAR(bestval); + FSTACK_PUSHVAR(which_child); + fstack_create(sizeof(minimize)); + FSTACK_PUSHVAR(depth); + fstack_create(PositionSize); + + // set up for recursive call + next_child = 0; + FSTACK_PUSHVAR(next_child); + FSTACK_PUSHVAR(bestval); + FSTACK_PUSHVAR(which_child); + minimize = !minimize; + FSTACK_PUSHVAR(minimize); + depth--; + FSTACK_PUSHVAR(depth); + fstack_push(copy, PositionSize); + + // the actual value we push here is never looked at, but we need to + // make space for it. + fstack_create(sizeof(which_child) + sizeof(value)); +} + +#endif // LOST_TREASURES... diff --git a/engine/src/GameSrc/mlimbs.c b/engine/src/GameSrc/mlimbs.c new file mode 100644 index 0000000..9679d16 --- /dev/null +++ b/engine/src/GameSrc/mlimbs.c @@ -0,0 +1,1350 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/mlimbs.c $ + * $Revision: 1.32 $ + * $Author: dc $ + * $Date: 1994/11/23 00:13:15 $ + */ + +#include + +#include "mlimbs.h" + +#define CHANNEL_MAP + +#define LOCK_ALL_CHANNELS + +//uchar mlimbs_on = FALSE; +//char mlimbs_status = 0; // could make this one bitfield of status, on/off, enable/not, so on +int mlimbs_timer_id; // what our timer handle is + +static uchar *mlimbs_theme = NULL; // data about the current theme +volatile int mlimbs_master_slot = -1; +static int mlimbs_cur_theme_id = -1; + +volatile struct mlimbs_piece_info xseq_info[MAX_SEQUENCES]; // Sequence specific information +volatile struct mlimbs_request_info current_request[MLIMBS_MAX_SEQUENCES - 1]; // Request information +volatile struct mlimbs_channel_info channel_info[MLIMBS_MAX_CHANNELS]; // MIDI channel information +volatile struct mlimbs_playing_info userID[MLIMBS_MAX_SEQUENCES - 1]; // Sequence instance specific information +volatile uchar mlimbs_update_requests = FALSE; + +volatile uchar num_XMIDI_sequences = 0; +volatile uchar num_master_measures; +volatile uchar voices_used = 0; +volatile uchar max_voices = 0; + +volatile void (*mlimbs_AI)(void) = NULL; +volatile ulong mlimbs_counter = 0; +volatile long mlimbs_error; +volatile uchar mlimbs_semaphore = FALSE; + +int master_volume = 100; + +char curr_play_list[10]; +char loop_list[10]; +char cpl_num; + +// gruesome hacks to try and get this working for now +int mlimbs_priority[MLIMBS_MAX_SEQUENCES]; + +// convienience psuedo-function defines +#define _uiD_seq(i) ((SEQUENCE *)snd_get_sequence(userID[i].seq_id)) +#define _mlimbs_top \ + if (mlimbs_status == 0) \ + return -1 + +// LONG mlimbs_timbre_callback(MDI_DRIVER *mdi, LONG bank, LONG patch); + +///////////////////////////////////////////////////////////////// +// mlimbs_init (void) +// +// purpose: +// This routine initializes the MLIMBS system. This also +// must be used if you want to switch the SndMIDIDevice that +// MLIMBS uses. This must be called before using mlimbs. +// +// inputs: +// now uses the default global midi device always +int mlimbs_init(void) { + int i; + + // if (!music_card) return -1; + + if (mlimbs_status != 0) + return 1; + for (i = 0; i < 10; i++) { + curr_play_list[i] = -1; + loop_list[i] = -1; + } + cpl_num = 0; + + /* What is max_voices??? + // Determine maximum number of voices + #ifdef AIL_2 + case SOUNDBLASTER: + case SOUNDBLASTERPRO: + case ADLIB: + max_voices = 9; + break; + case SOUNDBLASTERPRO2: + max_voices = 18; + case MT32: + // Switch max_voices to 32 when Roland specific dat files are + // available, since voices usage should be tracked using partials on a Roland. + // max_voices = 32; // Note that this here is actually the # of partials available. + max_voices = 9; // For now, since we're using only SB/ADLIB dat files, set voice + limit to 9. master_volume = 90; // Reduces distortion in Roland MT-32's break; default: max_voices = 9; break; + } + #else + max_voices=18; + #endif + + snd_set_midi_sequences(MLIMBS_MAX_SEQUENCES); + + #ifdef CALLBACK_ON + // Install mlimbs_callback, which will be called by the master sequence twice per loop. + // each one has a different value, one means half done, get ready, the other means do the switch + seq_miditrig=mlimbs_callback; + AIL_register_timbre_callback((MDI_DRIVER *)snd_midi,mlimbs_timbre_callback); + + // seq_finish=mlimbs_seq_done_call; + + // Since we cannot stop and start XMIDI sequences from within a MIDI callback, use + // a timer, running at 100 hz to start and stop XMIDI sequences. The mlimbs_callback + // simply sets a flag, letting the timer know when to update sequence status. + if ((mlimbs_timer_id = tm_add_process(mlimbs_timer_callback, 0, (TMD_FREQ/MLIMBS_TIMER_FREQUENCY))) == -1) + goto die; + #endif + + #ifdef LOCK_ALL_CHANNELS + // Lock all MIDI channels on the current mlimbs_device, and + // initialize the channel_info[] array. + for (i = 0; i < MLIMBS_MAX_CHANNELS; i++) + { + channel_info[i].mchannel = AIL_lock_channel((MDI_DRIVER *)snd_midi); + channel_info[i].usernum = -1; // This indicates what sequence is using this channel. -1 is stale + handle. channel_info[i].status = MLIMBS_STOPPED; // Current status of the channel. + } + #endif + */ + mlimbs_status = 1; + return 1; + +die: + mlimbs_shutdown(); + return -1; +} + +///////////////////////////////////////////////////////////////// +// mlimbs_shutdown +// +// purpose: +// This shuts down the mlimbs system. After calling this, +// call mlimbs_init again to restart it. +// +///////////////////////////////////////////////////////////////// +void mlimbs_shutdown(void) { + if (mlimbs_status == 0) + return; + + /* later, man + mlimbs_purge_theme(); + + #ifdef CALLBACK_ON + seq_miditrig=NULL; + // seq_finish=NULL; + AIL_register_timbre_callback((MDI_DRIVER *)snd_midi,NULL); + tm_remove_process(mlimbs_timer_id); + #endif + + #ifdef LOCK_ALL_CHANNELS + { // Release all the locked MIDI channels, and clear the channel_info[] array + int i; + for (i = 0; i < MLIMBS_MAX_CHANNELS; i++) + { + if (channel_info[i].mchannel >= 0) AIL_release_channel((MDI_DRIVER + *)snd_midi,channel_info[i].mchannel); channel_info[i].mchannel = -1; channel_info[i].usernum = -1; + channel_info[i].status = MLIMBS_STOPPED; + } + } + #endif + */ + mlimbs_status = 0; +} + +#ifdef NOT_YET // + +///////////////////////////////////////////////////////////////// +// int mlimbs_load_theme +// +// purpose: +// Load theme will load an XMIDI file into theme, stopping +// playback and purging the previous XMIDI file from theme. +// It will also allocate state tables if needed and preload all +// timbres used by any sequence in the XMIDI file. +// +// inputs: +// char *xname Filename for the XMIDI theme file. +// char *xinfo Filename for the file to be used in filling out xseq_info[]. +// char *GTL_filename Global Timbre Library filename, for preloading all timbres. +// return: +// 1 if successful +// -1 if unsuccessful +// Also set mlimbs_error to the following values if unsuccessful: +// -2 if it failed to load the XMIDI file +// -3 if it failed to load the data file. +///////////////////////////////////////////////////////////////// +int mlimbs_load_theme(char *xname, char *xinfo, int thmid) { + int i; + FILE *fil; + + // secret_sprint((ss_temp,"try to load themed %s (%d) (%d)\n",xname,thmid,mlimbs_status)); + _mlimbs_top; + mlimbs_purge_theme(); // Purge any previously loaded mlimbs theme + if ((mlimbs_theme = snd_load_raw(xname, NULL)) == NULL) + return -2; + if ((fil = fopen(xinfo, "rb")) == NULL) + return -3; + mlimbs_cur_theme_id = thmid; + fread(&num_XMIDI_sequences, sizeof(uchar), 1, + fil); // Number of sequences in this XMIDI file, NOT COUNTING THE MASTER SEQUENCE + fread(&num_master_measures, sizeof(uchar), 1, + fil); // Number of measures in the master sequence - i.e. # of measures per loop + num_XMIDI_sequences = min(num_XMIDI_sequences, MAX_SEQUENCES); + for (i = 0; i < num_XMIDI_sequences; i++) { + fread(&(xseq_info[i].max_voices), sizeof(uchar), 1, fil); // Maximum # of simultaneous voices + fread(&(xseq_info[i].avg_voices), sizeof(uchar), 1, fil); // Normal # of simultaneous voices + fread(&(xseq_info[i].channel_map), sizeof(ushort), 1, fil); // Bitmap of channel usage + fread(&(xseq_info[i].num_measures), sizeof(uchar), 1, fil); // # of measures + xseq_info[i].num_measures = max( + 1, + xseq_info[i].num_measures); // Don't allow 0 measure chunks (prevents divide by 0 in mlimbs_timer_callback) + fread(&(xseq_info[i].priority), sizeof(int), 1, fil); // Default priority + fread(xseq_info[i].channel_voices, sizeof(uchar), 7, fil); // Now read in channel voice usage + } + fclose(fil); + // secret_sprint((ss_temp,"load themed %s (%d) a-ok\n",xname,thmid)); + return 1; +} + +///////////////////////////////////////////////////////////////// +// int mlimbs_start_theme +// +// purpose: +// This begins playback of the theme by playing the +// master_sequence (sequence 0). NOTE: This doesn't clear +// the current_request array and doesn't reset mlimbs_counter. +// +// inputs: +// +// return: +// 1 if successful +// -1 if not +// Sets mlimbs_error to the following error codes if unsuccessful. +// -1 if no mlimbs device present +// -2 if it failed to load the master track. +// -3 if it failed to start the master track. +// +///////////////////////////////////////////////////////////////// +int mlimbs_start_theme(void) { + if ((mlimbs_status == 0) || (mlimbs_theme == NULL)) + return -1; // no can do + if (mlimbs_master_slot >= 0) + return 1; // already can done + + if ((mlimbs_master_slot = snd_sequence_play(mrefBuild(mlimbs_cur_theme_id, CALLBACK_SEQ_NUM), mlimbs_theme, + CALLBACK_SEQ_NUM, NULL)) == + SND_PERROR) { // better make sure this is looping, eh... + return -2; + } + AIL_set_sequence_loop_count(snd_sequence_ptr_from_id(mlimbs_master_slot), 0); + +#ifdef CALLBACK_ON + // Start the mlimbs timer callback which will now start and stop + // various tracks according to how the current_request structures are set + tm_activate_process(mlimbs_timer_id); +#endif + + // secret_sprint((ss_temp,"start theme %d callback %d\n",mlimbs_cur_theme_id,mlimbs_timer_id)); + + return 1; +} + +// i hate these, thank you +void _mlimbs_clear_req(int i) { + current_request[i].pieceID = -1; + current_request[i].rel_vol = DEFAULT_REL_VOL; + current_request[i].ramp_time = DEFAULT_RAMP_TIME; + current_request[i].ramp = 0; + current_request[i].priority = 0; + current_request[i].loops = -1; + current_request[i].pan = -1; + current_request[i].channel_prioritize = FALSE; + current_request[i].crossfade = 0; +} + +void _mlimbs_clear_uid(int i) { + int j; + userID[i].pieceID = -1; + userID[i].current_channel_map = 0; + userID[i].seq_id = -1; + userID[i].rel_vol = DEFAULT_REL_VOL; + userID[i].channel_prioritize = FALSE; + userID[i].crossfade_status = 11; + for (j = 0; j < 7; j++) + userID[i].sequence_channel_status[j] = SEQUENCE_CHANNEL_UNUSED; +} + +///////////////////////////////////////////////////////////////// +// void mlimbs_stop_theme +// +// purpose: +// This routine stops all mlimbs XMIDI playback. After +// calling this routine, a call to mlimbs_start_theme is +// needed to restart playback. Note that this doesn't +// clear the current_request[] array and doesn't reset the +// mlimbs_counter. +// +// +// inputs: +// +// +///////////////////////////////////////////////////////////////// +void mlimbs_stop_theme(void) { + int i; + + if (mlimbs_status == 0) + return; + + /* Stop all sequences */ +#ifdef CALLBACK_ON + tm_deactivate_process(mlimbs_timer_id); +#endif + + mlimbs_update_requests = FALSE; + + for (i = 0; i < MLIMBS_MAX_SEQUENCES - 1; i++) { + mlimbs_punt_piece(i); + _mlimbs_clear_req(i); + } + mlimbs_master_slot = -1; + voices_used = 0; + + // secret_sprint((ss_temp,"stop theme %d callback %d\n",mlimbs_cur_theme_id,mlimbs_timer_id)); +} + +///////////////////////////////////////////////////////////////// +// void mlimbs_purge_theme +// +// purpose: +// This routine, stops all XMIDI sequence playback, unloads +// the XMIDI sequence from theme, and clears the xseq_info[] +// array. Note that it also reinitializes the current_request[] +// and xseq_info[] arrays. +// +// inputs: +// +// +///////////////////////////////////////////////////////////////// +void mlimbs_purge_theme(void) { + int i; + + mlimbs_update_requests = FALSE; + /* Clear the current_request[] and arrays */ + + for (i = 0; i < MLIMBS_MAX_SEQUENCES - 1; i++) { + _mlimbs_clear_req(i); + _mlimbs_clear_uid(i); + } + + memset(xseq_info, 0, sizeof(xseq_info)); // make sure this is full size + for (i = 0; i < MAX_SEQUENCES; i++) + xseq_info[i].num_measures = 1; + mlimbs_master_slot = -1; + + if (mlimbs_status == 0) + return; + +#ifdef CALLBACK_ON + tm_deactivate_process(mlimbs_timer_id); +#endif + + snd_kill_all_sequences(); + if (mlimbs_theme != NULL) + Free(mlimbs_theme); + mlimbs_theme = NULL; + + mlimbs_update_requests = FALSE; +#ifdef LOCK_ALL_CHANNELS + for (i = 0; i < MLIMBS_MAX_CHANNELS; i++) { + channel_info[i].usernum = -1; + channel_info[i].status = MLIMBS_STOPPED; + } +#endif + + num_XMIDI_sequences = 0; + num_master_measures = 1; + voices_used = 0; + mlimbs_counter = 0; + + // secret_sprint((ss_temp,"purge theme %d callback %d\n",mlimbs_cur_theme_id,mlimbs_timer_id)); +} + +///////////////////////////////////////////////////////////////// +// +// mlimbs_mute_sequence_channel +// +// purpose: This will free a physical channel being used by a chunk's +// sequence channel and will set the sequence channel's status to +// SEQUENCE_CHANNEL_MUTED, or SEQUENCE_CHANNEL_PENDING. This routine is +// used when a chunk must give up a channel to a higher priority chunk, +// or when a chunk must be muted. +// +// inputs: +// int usernum // The sequence instance to give up a channel. +// int x // Which channel to relenquish. +// uchar mute // If TRUE, mute the channel. If FALSE merely, +// // relenquish the channel, and set the sequence channel's +// // status to SEQUENCE_CHANNEL_PENDING +///////////////////////////////////////////////////////////////// + +void mlimbs_mute_sequence_channel(int usernum, int x, uchar mute) { + int val, seq_ch; + int phys_ch; + + if (usernum < 0) + return; + if (userID[usernum].pieceID < 0) + return; + + // secret_sprint((ss_temp,"want to mute %d's %d, %d\n",usernum,x,mute)); + + if (mute == TRUE) { + switch (userID[usernum].sequence_channel_status[x - 10]) { + case SEQUENCE_CHANNEL_PENDING: + userID[usernum].sequence_channel_status[x - 10] = SEQUENCE_CHANNEL_MUTED; + case SEQUENCE_CHANNEL_MUTED: + case SEQUENCE_CHANNEL_UNUSED: + return; + default: + break; + } + } else if (userID[usernum].sequence_channel_status[x - 10] < 0) + return; + + phys_ch = userID[usernum].sequence_channel_status[x - 10]; + + if (channel_info[phys_ch].status == MLIMBS_PLAYING_PIECE) { /* Remap the channel if it is playing in a sequence */ + // secret_sprint((ss_temp,"is playing, remapping it %d from %d\n",phys_ch,x)); + seq_ch = x; + if (seq_ch >= 11) { /* First check if the XMIDI bank select controller was set*/ + /* If it's < 0,this means the controller wasn't initialized. Thus, we need to initialize it + to 0 in order for the stop/resume trick to work */ + val = AIL_controller_value(_uiD_seq(usernum), seq_ch, XMIDI_BANK_SELECT); +#ifdef SND_CHANGE + // hey, this isnt supported in AIL3 + if ((val < 0) || (val > 127)) + AIL_set_controller_value(_uiD_seq(usernum), seq_ch, XMIDI_BANK_SELECT, 0); +#endif + if ((val < 0) || (val > 127)) { + AIL_send_channel_voice_message(NULL, _uiD_seq(usernum), MIDI_CONTROL_CHANGE | (seq_ch - 1), + XMIDI_BANK_SELECT, 0); + } + AIL_stop_sequence(_uiD_seq(usernum)); + AIL_map_sequence_channel(_uiD_seq(usernum), seq_ch, seq_ch); + AIL_resume_sequence(_uiD_seq(usernum)); + channel_info[phys_ch].status = MLIMBS_STOPPED; + } + } + + /* Indicate that this physical channel is free */ + channel_info[phys_ch].usernum = -1; + channel_info[phys_ch].sequence_channel = -1; + voices_used -= xseq_info[userID[usernum].pieceID].channel_voices[x - 10]; + userID[usernum].sequence_channel_status[x - 10] = + (mute == TRUE) ? SEQUENCE_CHANNEL_MUTED : SEQUENCE_CHANNEL_PENDING; + userID[usernum].current_channel_map &= ~(0x0001 << phys_ch); + // secret_sprint((ss_temp,"is now free\n")); +} + +////////////////////////////////////////////////////////////////// +// mlimbs_unmute_sequence_channel +// +// This routine attempts to map a sequence channel of a given +// chunk to a physical channel. It searches for physical channels +// not currently used by other chunks. +// +// NOTE: mlimbs_unmute_sequence_channel will not attempt to free +// channels or voices. +// +////////////////////////////////////////////////////////////////// + +int mlimbs_unmute_sequence_channel(int usernum, int x) { + int i; + + if (usernum < 0) + return 1; + if (userID[usernum].pieceID < 0) + return 1; + + // secret_sprint((ss_temp,"unmuting %d of %d\n",usernum,x)); + + switch (userID[usernum].sequence_channel_status[x - 10]) { + case SEQUENCE_CHANNEL_MUTED: + userID[usernum].sequence_channel_status[x - 10] = SEQUENCE_CHANNEL_PENDING; + case SEQUENCE_CHANNEL_PENDING: + break; + case SEQUENCE_CHANNEL_UNUSED: + default: + return 1; + } + + /* First, make sure there are enough voices */ + if (xseq_info[userID[usernum].pieceID].channel_voices[x - 10] + voices_used > max_voices) + return -1; + + /* Now, look for unused channels */ + for (i = 0; i < MLIMBS_MAX_CHANNELS; i++) { + if (channel_info[i].status == MLIMBS_STOPPED) { + // secret_sprint((ss_temp,"undoing %d (%d)\n",i,channel_info[i].mchannel)); + + AIL_stop_sequence(_uiD_seq(usernum)); + AIL_map_sequence_channel(_uiD_seq(usernum), x, channel_info[i].mchannel); + AIL_resume_sequence(_uiD_seq(usernum)); + + channel_info[i].status = MLIMBS_PLAYING_PIECE; + channel_info[i].usernum = usernum; + channel_info[i].sequence_channel = x; + + voices_used += xseq_info[userID[usernum].pieceID].channel_voices[x - 10]; + + userID[usernum].sequence_channel_status[x - 10] = i; + userID[usernum].current_channel_map |= (0x0001 << i); + return 1; + } + } + // secret_sprint((ss_temp,"we lost, no joy\n")); + return -1; +} + +///////////////////////////////////////////////////////////////// +// mlimbs_channel_prioritize +// +// This routine attempts to acquire free channels and voices +// from lower priority chunks for a given chunk. +// For channels used by lower priority chunks, this routine simply +// frees them. Call mlimbs_assign_channels to give them to a +// chunk. The minimum number of voices required is the number of +// voices the chunk plays on channel 10. If not enough voices +// can be freed, then nothing is punted, and the chunk is not played. +// If channel_prioritize is set to FALSE, then mlimbs_channel_prioritize +// will attempt to acquire all necessary free channels and voices,else +// fail. +// +///////////////////////////////////////////////////////////////// + +int mlimbs_channel_prioritize(int priority, int pieceID, int voices_needed, uchar crossfade, uchar channel_prioritize) { + int i, j; + int channels_needed, num_free_channels; + int seq_punted; + int voices_punted; + int punt_list[MLIMBS_MAX_SEQUENCES]; + int min; + int min_voices_needed; + + if (pieceID >= num_XMIDI_sequences) + return -1; + + channels_needed = num_free_channels = seq_punted = 0; + voices_punted = 0; + + /* the punt_list is a list of userID[] entries */ + for (i = 0; i < MLIMBS_MAX_SEQUENCES; i++) + punt_list[i] = -1; + + /* First, count free physical_channels */ + for (i = 0; i < MLIMBS_MAX_CHANNELS; i++) + if (channel_info[i].usernum == -1) + num_free_channels++; + + /* Count # of channels needed */ + if (crossfade != TRUE || channel_prioritize == FALSE) { + for (i = 11, channels_needed = 0; i < 16; i++) + if ((xseq_info[pieceID].channel_map >> i) & (0x0001)) + channels_needed++; + } else + channels_needed = 0; // If we're crossfading, mlimbs_channel_prioritize + // doesn't need to get channels for this piece, right now. + + // Minimum # of voices needed to play = # of voices on channel 10 since MIDI on + // channel 10 is never remapped to a nonphysical channel + + min_voices_needed = xseq_info[pieceID].channel_voices[0] - (max_voices - voices_used); + + // If we're crossfading, we only needed the minimum # of voices. + if (crossfade == TRUE && channel_prioritize == TRUE) + voices_needed = min_voices_needed; + + if (voices_needed <= 0) + if (channels_needed <= num_free_channels) + return 1; + + /*************************************/ + /* Find all the channels we can punt */ + /* and voices we can free. */ + /*************************************/ + for (i = 0; i < MLIMBS_MAX_SEQUENCES - 1; i++) { + if (userID[i].pieceID >= 0) { + // index mlimbs_priority[] with i+1 because the master track is + // already in slot 0, while userID[0] corresponds to slot 1. + + if (mlimbs_priority[i + 1] > priority) + continue; + + punt_list[seq_punted] = i; + seq_punted++; + + // For channel_prioritized chunks, we can steal individual channels and voices. For non + // channel prioritized chunks, you have to punt the entire piece. + if (userID[i].channel_prioritize == TRUE) { + for (j = 16; j > 10; j--) { + /* If this sequence channel is currently using a physical channel...*/ + if (userID[i].sequence_channel_status[j - 10] >= 0) { + num_free_channels++; + voices_punted += xseq_info[userID[i].pieceID].channel_voices[j - 10]; + } + } + voices_punted += xseq_info[userID[i].pieceID].channel_voices[0]; + } else { +#ifdef USE_MAX_VOICES + voices_punted += xseq_info[userID[i].pieceID].max_voices; +#else + voices_punted += xseq_info[userID[i].pieceID].avg_voices; +#endif + for (j = 0; j < MLIMBS_MAX_CHANNELS; j++) + if ((userID[i].current_channel_map >> j) & (0x0001)) + num_free_channels++; + } + } + } + + // If there aren't enough voices or channels, then + // do not punt anything. + if (channel_prioritize == TRUE) { + if ((voices_punted < min_voices_needed) || (num_free_channels == 0)) + return -1; + } else { + if ((voices_punted < voices_needed) || (num_free_channels < channels_needed)) + return -1; + } + + // Now, punt as many voices and channels that we can + voices_punted = 0; + while ((voices_punted < voices_needed) || (num_free_channels < channels_needed)) { + + // Each time through, find the lowest priority thing in the punt list + min = -1; + for (i = 0; i < seq_punted; i++) { + if (punt_list[i] >= 0) { + if (min < 0) + min = i; + if (mlimbs_priority[punt_list[i] + 1] < mlimbs_priority[punt_list[min] + 1]) + min = i; + } + } + + if (min < 0) { + if (channel_prioritize == TRUE) { + if (voices_punted < min_voices_needed) + return -1; + else + return 1; // Its o.k. not to have all necessary channels + } else + return -1; + } + + // For channel_prioritized chunks, we can steal individual channels and voices. For non + // channel prioritized chunks, you have to punt the entire piece. + if (userID[punt_list[min]].channel_prioritize == TRUE) { + for (j = 16; j > 10; j--) { + if (userID[punt_list[min]].sequence_channel_status[j - 10] >= 0) { + num_free_channels++; + voices_punted += xseq_info[userID[punt_list[min]].pieceID].channel_voices[j - 10]; + mlimbs_mute_sequence_channel(punt_list[min], j, FALSE); + } + if ((voices_punted >= voices_needed) && (num_free_channels >= channels_needed)) + break; + } + + // If punting channels 11-16 wasn't enough, punt entire + // piece, thus freeing channel 10 voices as well + if (j == 10) { + mlimbs_punt_piece(punt_list[min]); + voices_punted += xseq_info[userID[punt_list[min]].pieceID].channel_voices[0]; + } else { + // If we got enough voices and channels, only punt entire + // piece if nothing is playing on channel 10 + for (j = 16; j > 10; j--) { + if (userID[punt_list[min]].sequence_channel_status[j - 10] >= 0) + break; + } + if ((j == 10) && (xseq_info[userID[punt_list[min]].pieceID].channel_voices[0] <= 0)) { + mlimbs_punt_piece(punt_list[min]); + voices_punted += xseq_info[userID[punt_list[min]].pieceID].channel_voices[0]; + } + } + } + + else { + for (j = 0; j < MLIMBS_MAX_CHANNELS; j++) + if ((userID[punt_list[min]].current_channel_map >> j) & (0x0001)) + num_free_channels++; +#ifdef USE_MAX_VOICES + voices_punted += xseq_info[userID[punt_list[min]].pieceID].max_voices; +#else + voices_punted += xseq_info[userID[punt_list[min]].pieceID].avg_voices; +#endif + mlimbs_punt_piece(punt_list[min]); + } + + punt_list[min] = -1; + } + return 1; +} + +///////////////////////////////////////////////////////////////// +// int mlimbs_assign_channels +// +// purpose: +// This routine will mark the userID[].sequence_channel_status[] array +// according to what sequence channels need to use. It then maps as +// many sequence channels as it can to physical channels, in order, +// from 11 to 16, unless the crossfade argument is TRUE. +// +// inputs: +// int usernum +// bool crossfade // If TRUE, all the sequence channels used by +// // the sequence are set to SEQUENCE_CHANNEL_MUTED so crossfade +// // code can unmute channels as the sequence is crossfaded in. +// +///////////////////////////////////////////////////////////////// +int mlimbs_assign_channels(int usernum, uchar crossfade) { + uint j; + ushort c_map; + + if (usernum >= (MLIMBS_MAX_SEQUENCES - 1)) + return -1; + c_map = xseq_info[userID[usernum].pieceID].channel_map; + if (c_map == 0) + return 1; // If no channels are requested from dynamic channel allocation, return. + for (j = 11; j < 17; j++) { + if ((c_map >> (j - 1)) & 0x0001) { // Mark the sequence channel as waiting for a physical channel + // unless the sequence is to be crossfaded in, in which + // case, mute all channels. + if (crossfade == FALSE) { + userID[usernum].sequence_channel_status[j - 10] = SEQUENCE_CHANNEL_PENDING; + if (mlimbs_unmute_sequence_channel(usernum, j) < 0) { + if (userID[usernum].channel_prioritize == FALSE) + return -1; + } + } else + userID[usernum].sequence_channel_status[j - 10] = SEQUENCE_CHANNEL_MUTED; + } else + userID[usernum].sequence_channel_status[j - 10] = SEQUENCE_CHANNEL_UNUSED; + } + return 1; +} + +///////////////////////////////////////////////////////////////// +// int mlimbs_play_piece +// +// purpose: +// This routine will begin playback of a single 'snippet' in +// the theme. +// +// inputs: +// int pieceID Index into the xseq_info array. +// int priority Priority of the request. If < 0, then +// use the default priority (in xseq_info[]). +// int loops Number of times to play the piece +// int rel_vol Relative volume to start the piece at. This will +// be set to 0 for pieces that will ramp up. +// uchar channel_prioritize - if TRUE, then its ok to play the +// chunk even if not enough channels are available, +// If FALSE, then only play chunk if all channels can +// be played. +// uchar crossfade +// +// return: +// -1 if failed +// usernum - an integer index into the userID[] array +///////////////////////////////////////////////////////////////// + +int mlimbs_play_piece(int pieceID, int priority, int loops, int rel_vol, uchar channel_prioritize, uchar crossfade) { + int i, slot, usernum, voices_needed, voices_available; + + if (pieceID >= num_XMIDI_sequences) + return -1; + if (loops == 0) + return 1; + + // secret_sprint((ss_temp,"play piece %d %d %d %d %d %d\n",pieceID, + // priority,loops,rel_vol,channel_prioritize,crossfade)); + + // Check for duplicate piece IDs? + if (cpl_num < 10) { + for (i = 0; i < cpl_num; i++) + if (curr_play_list[i] == pieceID) { + // secret_sprint((ss_temp,"cur play list %d already has %d\n",i,pieceID)); + return 1; + } + } else + Warning(("BADNESS! cpl_num = %d!\n")); + + if (priority < 0) + priority = xseq_info[pieceID].priority; + + voices_needed = 0; + voices_available = max_voices - voices_used; + + // First, free as many voices and channels as possible. + if (channel_prioritize) { // Only need to count # of voices needed if we're not crossfading. since channel 10 + // voices + // are used by mlimbs_channel_prioritize as the # of voices needed if we are crossfading + if (crossfade != TRUE) { + for (i = 0, voices_needed = 0; i < 7; i++) + voices_needed += xseq_info[pieceID].channel_voices[i]; + voices_needed -= voices_available; + } + if (mlimbs_channel_prioritize(priority, pieceID, voices_needed, crossfade, TRUE) < 0) + return -1; + } else { +#ifdef USE_MAX_VOICES + voices_needed = -(voices_available - xseq_info[pieceID].max_voices); +#else + voices_needed = -(voices_available - xseq_info[pieceID].avg_voices); +#endif + if (mlimbs_channel_prioritize(priority, pieceID, voices_needed, crossfade, FALSE) < 0) + return -1; + } + + /***********************************/ + /* Now, attempt to load the piece. */ + /***********************************/ + slot = snd_sequence_play(mrefBuild(mlimbs_cur_theme_id, pieceID + 1), mlimbs_theme, pieceID + 1, NULL); + usernum = slot - 1; + // secret_sprint((ss_temp,"play piece got slot %d for %d\n",slot,pieceID)); + if (slot >= 0) { + userID[usernum].pieceID = pieceID; // What is going on here? + userID[usernum].seq_id = slot; + userID[usernum].channel_prioritize = channel_prioritize; + userID[usernum].crossfade_status = 11; + // Note: tallying of voice usage is now done in mlimbs_assign channels. Only add channel 10 here + voices_used += xseq_info[pieceID].channel_voices[0]; + if (cpl_num < 10) + curr_play_list[cpl_num++] = pieceID; + if (mlimbs_assign_channels(usernum, crossfade) == -1) { /* Try and assign the channels and voices freed above */ + mlimbs_punt_piece(usernum); + return -1; + } + // Now set the initial volume + AIL_set_sequence_volume(_uiD_seq(usernum), (unsigned)rel_vol * master_volume / 100, 0); + return (usernum); // Return entry of userID[] + } else + return -1; +} + +// CHANGE - now resets pieceID as well (inside clearUid) +// we can clearly change it back if we want to + +// void mlimbs_punt_piece +// This routine will stop playback of a single 'snippet' in the theme. +// inputs: usernum Index into the userID array. +void mlimbs_punt_piece(int usernum) { + int i, slot, ch; + + if (userID[usernum].pieceID < 0 || userID[usernum].pieceID >= num_XMIDI_sequences) + return; + + slot = usernum + 1; + + // secret_sprint((ss_temp,"looking to punt slot %d (unum %d)\n",slot,usernum)); + + if (slot >= 0) { + if (cpl_num < 10) { + for (i = 0; i < cpl_num; i++) { + if (curr_play_list[i] == userID[usernum].pieceID) { + if (i != (cpl_num - 1)) { + curr_play_list[i] = curr_play_list[cpl_num - 1]; + loop_list[i] = loop_list[cpl_num - 1]; + } + cpl_num--; + } + } + } + + // secret_sprint((ss_temp,"end seq it is\n",slot,usernum)); + snd_end_sequence(slot); + + for (i = 11; i < 17; i++) { + ch = userID[usernum].sequence_channel_status[i - 10]; + if (ch >= 0) { + voices_used -= xseq_info[userID[usernum].pieceID].channel_voices[i - 10]; + channel_info[ch].usernum = -1; + channel_info[ch].sequence_channel = -1; + if (channel_info[ch].status == MLIMBS_PLAYING_PIECE) + channel_info[ch].status = MLIMBS_STOPPED; + } + userID[usernum].sequence_channel_status[i - 10] = SEQUENCE_CHANNEL_UNUSED; + } + voices_used -= xseq_info[userID[usernum].pieceID].channel_voices[0]; + _mlimbs_clear_uid(usernum); + } +} + +//////////////////////////////////////////////////////////////// +// mlimbs_get_crossfade_status +// +// purpose: +// Given a chunkID, it returns its current crossfade_status. +// Whether it is getting crossfaded in or out depends on +// the crossfade field in the current_request structure. It +// basically searches the array of userID[]'s for a userID that +// is currently playing the given chunk. +// +// inputs: +// int pieceID - id of the chunk to get the status of +// outputs: +// >= 0 - the correct crossfade_status +// 0 - not crossfading) +// 10-16 - next channel to be crossfaded +// +// < 0 - the chunk is not currently playing. +// +/////////////////////////////////////////////////////////////// + +schar mlimbs_get_crossfade_status(int pieceID) { + int i; + + for (i = 0; i < MLIMBS_MAX_SEQUENCES - 1; i++) + if (userID[i].pieceID == pieceID) + return userID[i].crossfade_status; + + return -1; +} + +//////////////////////////////////////////////////////////////// +// mlimbs_callback +// +// purpose: +// This is the routine that gets called by an XMIDI controller +// 119. All the mlimbs_callback does is call the mlimbs_AI, and +// then set mlimbs_update_requests to TRUE, which tells the mlimbs_timer_callback +// to actual execute its code, when next it is called. +// +//////////////////////////////////////////////////////////////// +#pragma disable_message(202) +void cdecl mlimbs_callback(snd_midi_parms *mprm, unsigned trigger_value) { + if (trigger_value) { + mlimbs_update_requests = TRUE; + } else { + if (mlimbs_AI != NULL) // This routine accesses the music AI which should set the structures in + { + mlimbs_AI(); // current_request[] to tell mlimbs which pieces should be playing. + /*KLC + { + extern schar curr_crossfade; + extern int new_theme; + extern uchar decon_count , decon_time ; + extern uchar in_deconst , old_deconst ; + secret_sprint((ss_temp, "note in %d old %d, count %d time %d, cf %d, nt %d\n", + in_deconst, old_deconst, decon_count, decon_time, curr_crossfade, new_theme)); + } + */ + } + } +} +#pragma enable_message(202) + +#ifdef COW +// the key, here, is we need to update all sequences which are done +// ie. clear out their state and such.... +void cdecl mlimbs_seq_done_call(snd_midi_parms *seq) {} +#endif + +////////////////////////////////////////////////////////////////////////////// +// mlimbs_reassign_channels(void) +// +// This routine assigns currently free channels to chunks that are playing, +// and are lacking channels. +// +///////////////////////////////////////////////////////////////////////////// + +void mlimbs_reassign_channels(void) { + int i, j; + int highest; + schar checked[7]; + + for (i = 0; i < 7; i++) + checked[i] = 0; + + highest = -1; + do { + // Check if the current highest priority chunk needs channels + if (highest >= 0) { + for (j = 11; j < 17; j++) { + if (userID[highest].sequence_channel_status[j - 10] == SEQUENCE_CHANNEL_PENDING) + break; + } + if (j == 17) { + checked[highest] = -1; + highest = -1; + } + } + + // If the previous highest priority chunk needs no more channels, or + // a highest priority chunk hasn't been found yet, get the highest + // priority chunk + + if (highest < 0) { + for (i = 0; i < MLIMBS_MAX_SEQUENCES - 1; i++) { + // -1 means that this chunk doens't need more channels + if (checked[i] == -1) + continue; + if (userID[i].pieceID != -1) { + /* Make sure the chunk needs a channel */ + if (checked[i] == 0) { + for (j = 11; j < 17; j++) { + if (userID[i].sequence_channel_status[j - 10] == SEQUENCE_CHANNEL_PENDING) + break; + } + if (j == 17) { + checked[i] = -1; + continue; + } else + checked[i] = 1; // 1 means that this chunk needs channels + } + + if (highest < 0) + highest = i; + else { + if (mlimbs_priority[i + 1] > mlimbs_priority[highest + 1]) + highest = i; + } + } + } + } + + if (highest >= 0) { + if (mlimbs_unmute_sequence_channel(highest, j) < 0) + break; // Ran out of channels or voices to assign, so exit from the loop + } else + break; + } while (1); +} + +#pragma disable_message(202) +LONG cdecl mlimbs_timbre_callback(MDI_DRIVER *mdi, LONG bank, LONG patch) { // dont allow the timbre to load + return 1; +} +#pragma enable_message(202) + +//////////////////////////////////////////////////////////////// +// mlimbs_timer_callback +// +// This timer callback is necessary since starting and stopping +// XMIDI sequences from within an XMIDI callback is not a good +// thing to do. It is currently called at 100 hz. Whenever it +// is called, it first checks whether mlimbs_update_requests has +// been set to TRUE by mlimbs_callback and whether mlimbs_semaphore +// is FALSE. If both the above conditions are met, then mlimbs_timer_callback +// executes the main body of its code, then resets mlimbs_update_requests +// to FALSE and increments the mlimbs_counter. +// +//////////////////////////////////////////////////////////////// +void mlimbs_timer_callback(void) { + int i, j, k, loop, rvol; + int usernum; + + if (mlimbs_update_requests == FALSE) { + return; + } + if (mlimbs_semaphore == TRUE) { + return; + } + + /*KLC + // show current requests + for (i=0; i < MLIMBS_MAX_SEQUENCES -1; i++) + if (current_request[i].pieceID != -1) + { + secret_sprint((ss_temp,"request %d is %d (%d %d %d)\n",i,current_request[i].pieceID, + current_request[i].ramp,current_request[i].crossfade,current_request[i].ramp_time)); + } + */ + + // First punt everything that is not requested. Also crossfade out pieces here. + k = 0; + for (i = 0; i < MLIMBS_MAX_SEQUENCES - 1; i++) { + if (userID[i].pieceID != -1) { + for (j = 0; j < MLIMBS_MAX_SEQUENCES - 1; j++) { + if (current_request[j].pieceID == userID[i].pieceID) { // Begin rampout of this chunk, if desired. + if (current_request[j].ramp < 0) { + if (userID[i].rel_vol != 0) // Check whether this chunk is already being ramped out. + { + userID[i].rel_vol = 0; + AIL_set_sequence_volume(_uiD_seq(i), 0, current_request[j].ramp_time); + // secret_sprint((ss_temp,"start ramp out for %d (req %d)\n",i,j)); + } + } + if (cpl_num < 10) { + for (loop = 0; loop < cpl_num; loop++) { + if (current_request[j].pieceID == curr_play_list[loop]) + loop_list[loop] = current_request[j].loops; + } + } + if (current_request[j].loops > 0) + current_request[j].loops--; + if (current_request[j].loops == 0) { + // secret_sprint((ss_temp,"punt loops over for %d (req %d)\n",i,j)); + mlimbs_punt_piece(i); // i, j, and k, eh? + _mlimbs_clear_req(j); + k++; + } + // Crossfade out pieces here, so that their channels become + // available to pieces to be crossfaded in, later in the callback. + else if (userID[i].channel_prioritize == TRUE) { + if (current_request[j].crossfade < 0) { + if (userID[i].crossfade_status > 16) + userID[i].crossfade_status = 16; + while (userID[i].crossfade_status > 10) { + if (userID[i].sequence_channel_status[userID[i].crossfade_status - 10] >= 0) { + mlimbs_mute_sequence_channel(i, userID[i].crossfade_status, TRUE); + userID[i].crossfade_status--; + break; + } else if (userID[i].sequence_channel_status[userID[i].crossfade_status - 10] == + SEQUENCE_CHANNEL_PENDING) { + userID[i].sequence_channel_status[userID[i].crossfade_status - 10] = + SEQUENCE_CHANNEL_MUTED; + userID[i].crossfade_status--; + break; + } + userID[i].crossfade_status--; + } + // secret_sprint((ss_temp,"crossfade fun for %d (req %d) got + // %d\n",i,j,userID[i].crossfade_status)); + } + } + break; + } + } + if (j == MLIMBS_MAX_SEQUENCES - 1) // If we get here, userID[i] matches none of the current_requests[] + { + // secret_sprint((ss_temp,"gonna punt: no current request for %d (seq %d)\n",userID[i].pieceID,i)); + mlimbs_punt_piece(i); + k++; + } + } + } // Reassign channels to chunks that are missing channels + mlimbs_reassign_channels(); // Play everything on the play list + k = 0; + for (i = 0; i < MLIMBS_MAX_SEQUENCES - 1; i++) { + if (current_request[i].pieceID >= 0) { + if (current_request[i].pieceID >= num_XMIDI_sequences) + continue; + // Don't play empty sequences + if (xseq_info[current_request[i].pieceID].channel_map == 0) + continue; + /* Make sure the piece isn't already playing */ + for (j = 0; j < MLIMBS_MAX_SEQUENCES - 1; + j++) { // If the piece is playing, see if we should fade any channels in. + if (userID[j].pieceID == current_request[i].pieceID) { + if (current_request[i].crossfade > 0) { + if (userID[j].crossfade_status < 11) + userID[j].crossfade_status = 11; + while (userID[j].crossfade_status < 17) { + if (userID[j].sequence_channel_status[userID[j].crossfade_status - 10] == + SEQUENCE_CHANNEL_PENDING) + break; + else if (userID[j].sequence_channel_status[userID[j].crossfade_status - 10] == + SEQUENCE_CHANNEL_MUTED) + userID[j].sequence_channel_status[userID[j].crossfade_status - 10] = + SEQUENCE_CHANNEL_PENDING; + else + userID[j].crossfade_status++; + } + // secret_sprint((ss_temp,"%d already playing [%d req %d] cross + // %d\n",userID[j].pieceID,j,i,userID[j].crossfade_status)); + } + break; + } + } + if (j < MLIMBS_MAX_SEQUENCES - 1) + // secret_sprint((ss_temp,"no dup seq, new, bases %d %d, n_m %d for %d\n", + // mlimbs_counter, num_master_measures, xseq_info[current_request[i].pieceID].num_measures, + // current_request[i].pieceID)); + // I took this out in order to solve the layering timing problem -- Rob F. + // continue; + /* For now, only start playing a piece if the number of measures played is a multiple + of the measure size of the piece. */ + if ((mlimbs_counter * num_master_measures) % xseq_info[current_request[i].pieceID].num_measures == + 0) { // If the chunk is going to be ramped in, set its initial volume to 0. + if (current_request[i].ramp > 0) + rvol = 0; + else + rvol = current_request[i].rel_vol; + usernum = mlimbs_play_piece(current_request[i].pieceID, current_request[i].priority, + current_request[i].loops, rvol, current_request[i].channel_prioritize, + (current_request[i].crossfade > 0)); + // secret_sprint((ss_temp,"brought in %d [%d req %d] crossfade %d got %d (rv + // %d)\n",current_request[i].pieceID,j,i,current_request[i].crossfade,usernum,rvol)); + if (usernum >= 0) { + userID[usernum].rel_vol = current_request[k].rel_vol; + + if (current_request[i].crossfade > 0) + userID[usernum].crossfade_status = + 11; // If we're crossfading in, crossfade starting with channel 11 + else + userID[usernum].crossfade_status = + 16; // If we're not crossfading in, set crossfade_status to 16 + // so that crossfading out will work. + // wait, what if we _are_ crossfading out? + if (current_request[i].ramp > 0) + AIL_set_sequence_volume(_uiD_seq(usernum), + (unsigned)(userID[usernum].rel_vol * master_volume / 100), + current_request[i].ramp_time); + else + AIL_set_sequence_volume(_uiD_seq(usernum), + (unsigned)(userID[usernum].rel_vol * master_volume / 100), 0); + } + } + } + } + mlimbs_update_requests = FALSE; + mlimbs_counter++; // Only update the counter after the pieces playing have been updated +} + +SEQUENCE *_mlimbs_get_a_seq(void) { + extern int snd_find_free_sequence(uchar smp_pri, uchar check_only); + SEQUENCE *S; + int seq_id; + if ((seq_id = snd_find_free_sequence(1000, FALSE)) == SND_PERROR) + return NULL; + S = snd_sequence_ptr_from_id(seq_id); + return S; +} + +extern uchar run_asynch_music_ai; + +// scan through all requested pieces, if not already playing, init_sequence them +void mlimbs_preload_requested_timbres(void) { + char *old; + int i, j, piece; + SEQUENCE *S; + // mprintf("preload requested, stat %d them %x\n",mlimbs_status,mlimbs_theme); + if ((mlimbs_status == 0) || (mlimbs_theme == NULL)) + return; + if ((S = _mlimbs_get_a_seq()) == NULL) + Warning(("No Seq for preload\n")); + old = AIL_register_timbre_callback((MDI_DRIVER *)snd_midi, NULL); + // mprintf("Old is %x, set to null\n",old); + for (i = 0; i < MLIMBS_MAX_SEQUENCES - 1; i++) { + // check if it is already playing before reloading it, eh? + piece = current_request[i].pieceID; + if ((piece >= 0) && (piece < num_XMIDI_sequences)) + if (xseq_info[piece].channel_map != 0) // Don't bother wit empty sequences + { + for (j = 0; j < MLIMBS_MAX_SEQUENCES - 1; j++) + if (userID[j].pieceID == piece) { + // mprintf("but we already have it...\n"); + break; + } + if (j == MLIMBS_MAX_SEQUENCES - 1) { + AIL_init_sequence(S, mlimbs_theme, piece + 1); + // mprintf("Hey bob, requesting %d to load\n",piece); + } + } + } + old = AIL_register_timbre_callback((MDI_DRIVER *)snd_midi, mlimbs_timbre_callback); + // mprintf("Old is %x, set to mtc\n",old); +} + +void mlimbs_preload_full_timbres_and_go_asynch(void) { + int piece; + SEQUENCE *S; + char *old; + // mprintf("preload full go asynch, stat %d them %x\n",mlimbs_status,mlimbs_theme); + if ((mlimbs_status == 0) || (mlimbs_theme == NULL)) + return; + if ((S = _mlimbs_get_a_seq()) == NULL) + Warning(("No Seq for full preload\n")); + old = AIL_register_timbre_callback((MDI_DRIVER *)snd_midi, NULL); + // mprintf("Old is %x, set to null\n",old); + for (piece = 0; piece < num_XMIDI_sequences; piece++) + if (xseq_info[piece].channel_map != 0) + AIL_init_sequence(S, mlimbs_theme, piece + 1); + old = AIL_register_timbre_callback((MDI_DRIVER *)snd_midi, mlimbs_timbre_callback); + // mprintf("Old is %x, set to mtc\n",old); + run_asynch_music_ai = TRUE; +} + +void mlimbs_return_to_synch(void) { + // mprintf("return to synch\n"); + run_asynch_music_ai = FALSE; +} + +/////////////////////////////////////////////////////// +// mlimbs_change_master_volume +// +// master_volume_scheme: +// Here's how the master volume scheme works. mlimbs +// has an overall master_volume which is given as a +// percentage. This is normally set to 100. Each +// sequence that is playing has a relative volume (the +// rel_vol field in the userID structures). Whenever +// the master_volume or a sequence relative volume +// is changed, a call to AIL_set_relative_volume is +// made for each affected sequence. The volume is +// passed to AIL_set_relative_volume as a percentage +// by which all the actual XMIDI sequence volume controller +// values are multiplied. The percentage passed = +// master_volume * userID[].rel_vol. +// Since changing the master_volume itself affects all +// sequences, a call to AIL_set_relative_volume is +// made for all currently playing sequences. +// +// inputs: +// int vol +// +/////////////////////////////////////////////////////// +void mlimbs_change_master_volume(int vol) { + int i; + int percent; + + master_volume = vol; + for (i = 0; i < (MLIMBS_MAX_SEQUENCES - 1); i++) { + percent = userID[i].rel_vol * master_volume / 100; + if (userID[i].seq_id >= 0) { + AIL_set_sequence_volume(_uiD_seq(i), (unsigned)percent, 0); + } + } +} + +#endif // NOT_YET diff --git a/engine/src/GameSrc/mouselook.c b/engine/src/GameSrc/mouselook.c new file mode 100644 index 0000000..d5bb5c5 --- /dev/null +++ b/engine/src/GameSrc/mouselook.c @@ -0,0 +1,123 @@ +/* + +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#include + +#include "leanmetr.h" +#include "mouselook.h" +#include "mouse.h" +#include "player.h" +#include "physics.h" +#include "objsim.h" +#include "Prefs.h" + +float mlook_hsens = 250; +float mlook_vsens = 50; + +int mlook_vel_x, mlook_vel_y; + +extern uchar game_paused; +extern short mouseInstantX, mouseInstantY; +extern int32_t eye_mods[3]; + +extern SDL_Window *window; +extern SDL_Renderer *renderer; + +void middleize_mouse(void); +void get_mouselook_vel(int *vx, int *vy); + +int mlook_enabled = FALSE; + +void mouse_look_physics() { + + if (game_paused || !global_fullmap || !mlook_enabled) + return; + + middleize_mouse(); + + int mvelx, mvely; + get_mouselook_vel(&mvelx, &mvely); + + if (global_fullmap->cyber) { + // see physics_run() in physics.c + mlook_vel_x = -mvelx; + mlook_vel_y = -mvely; + } else { + // player head controls + mvelx *= -mlook_hsens; + mvely *= (gShockPrefs.goInvertMouseY ? mlook_vsens : -mlook_vsens); + + if (mvely != 0) { + // Moving the eye up angle is easy + fix pos = player_struct.eye_pos + mvely; + player_set_eye_fixang(pos); + physics_set_relax(CONTROL_YZROT, FALSE); + } + + if (mvelx != 0) { + EDMS_mouselook(objs[PLAYER_OBJ].info.ph, mvelx); + } + } +} + +bool TriggerRelMouseMode = FALSE; + +void mouse_look_toggle(void) { + mlook_enabled = !mlook_enabled; + + if (mlook_enabled) { + SDL_SetRelativeMouseMode(SDL_TRUE); + + // throw away this first relative mouse reading + int mvelx, mvely; + get_mouselook_vel(&mvelx, &mvely); + } else { + SDL_SetRelativeMouseMode(SDL_FALSE); + + int w, h; + SDL_GetWindowSize(window, &w, &h); + SDL_WarpMouseInWindow(window, w / 2, h / 2); + + TriggerRelMouseMode = TRUE; + } +} + +void mouse_look_off(void) { + if (mlook_enabled) { + mlook_enabled = FALSE; + + SDL_SetRelativeMouseMode(SDL_FALSE); + + int w, h; + SDL_GetWindowSize(window, &w, &h); + SDL_WarpMouseInWindow(window, w / 2, h / 2); + + TriggerRelMouseMode = TRUE; + } +} + +void mouse_look_unpause(void) { + if (mlook_enabled) { + SDL_SetRelativeMouseMode(SDL_TRUE); + + // throw away this first relative mouse reading + int mvelx, mvely; + get_mouselook_vel(&mvelx, &mvely); + } +} diff --git a/engine/src/GameSrc/movekeys.c b/engine/src/GameSrc/movekeys.c new file mode 100644 index 0000000..539ab5e --- /dev/null +++ b/engine/src/GameSrc/movekeys.c @@ -0,0 +1,365 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/movekeys.c $ + * $Revision: 1.25 $ + * $Author: mahk $ + * $Date: 1994/11/22 22:53:07 $ + * + */ + +#include + +#include "input.h" +#include "player.h" +#include "physics.h" +#include "gamesys.h" +#include "movekeys.h" + +#define KEYBD_CONTROL_BANK 1 + +// filled in MacSrc/Prefs.c +MOVE_KEYBIND MoveKeybinds[MAX_MOVE_KEYBINDS + 1]; +MOVE_KEYBIND MoveCyberKeybinds[MAX_MOVE_KEYBINDS + 1]; + +static uchar motion_key_scancodes[256 + 1]; +static byte poll_controls[6]; + +extern bool gKeypadOverride; + +uchar parse_motion_key(ushort code, short *cnum, short *cval); +uchar parse_motion_key_cyber(ushort code, short *cnum, short *cval); +void init_motion_polling(void); + +uchar parse_motion_key(ushort code, short *cnum, short *cval) { + int i = 0, move = -1; + + *cnum = -1; + *cval = 0; + + while (MoveKeybinds[i].code != 255) { + if (code == MoveKeybinds[i].code) { + move = MoveKeybinds[i].move; + break; + } + i++; + } + + switch (move) { + case M_RUNFORWARD: + *cnum = CONTROL_YVEL; + *cval = CONTROL_MAX_VAL; + break; + + case M_FORWARD: + *cnum = CONTROL_YVEL; + *cval = CONTROL_MAX_VAL / 2; + break; + + case M_FASTTURNLEFT: + *cnum = CONTROL_XYROT; + *cval = -CONTROL_MAX_VAL; + break; + + case M_TURNLEFT: + *cnum = CONTROL_XYROT; + *cval = -CONTROL_MAX_VAL / 2; + break; + + case M_FASTTURNRIGHT: + *cnum = CONTROL_XYROT; + *cval = CONTROL_MAX_VAL; + break; + + case M_TURNRIGHT: + *cnum = CONTROL_XYROT; + *cval = CONTROL_MAX_VAL / 2; + break; + + case M_BACK: + *cnum = CONTROL_YVEL; + *cval = -CONTROL_MAX_VAL / 2; + break; + + case M_SLIDELEFT: + *cnum = CONTROL_XVEL; + *cval = -CONTROL_MAX_VAL / 2; + break; + + case M_SLIDERIGHT: + *cnum = CONTROL_XVEL; + *cval = CONTROL_MAX_VAL / 2; + break; + + case M_JUMP: + *cnum = CONTROL_ZVEL; + *cval = MAX_JUMP_CONTROL; + break; + + case M_LEANUP: + *cnum = CONTROL_XZROT; + *cval = 0; + physics_set_relax(*cnum, TRUE); + break; + + case M_LEANLEFT: + *cnum = CONTROL_XZROT; + *cval = -CONTROL_MAX_VAL; + physics_set_relax(*cnum, TRUE); + break; + + case M_LEANRIGHT: + *cnum = CONTROL_XZROT; + *cval = CONTROL_MAX_VAL; + physics_set_relax(*cnum, TRUE); + break; + + case M_LOOKUP: + *cnum = CONTROL_YZROT; + *cval = CONTROL_MAX_VAL; + break; + + case M_LOOKDOWN: + *cnum = CONTROL_YZROT; + *cval = -CONTROL_MAX_VAL; + break; + + case M_RUNLEFT: + *cnum = CONTROL_YVEL; + *cval = CONTROL_MAX_VAL; + if (abs(poll_controls[*cnum]) < abs(*cval)) + poll_controls[*cnum] = *cval; + *cnum = CONTROL_XYROT; + *cval = -CONTROL_MAX_VAL / 2; + break; + + case M_RUNRIGHT: + *cnum = CONTROL_YVEL; + *cval = CONTROL_MAX_VAL; + if (abs(poll_controls[*cnum]) < abs(*cval)) + poll_controls[*cnum] = *cval; + *cnum = CONTROL_XYROT; + *cval = CONTROL_MAX_VAL / 2; + break; + default: + // Unhandled movement. What I gonna do? + ; + } + + return *cnum != -1; +} + +uchar parse_motion_key_cyber(ushort code, short *cnum, short *cval) { + int i = 0, move = -1; + + code &= ~KB_FLAG_2ND; + + *cnum = -1; + *cval = 0; + + while (MoveCyberKeybinds[i].code != 255) { + if (code == MoveCyberKeybinds[i].code) { + move = MoveCyberKeybinds[i].move; + break; + } + i++; + } + + switch (move) { + case M_THRUST: + *cnum = CONTROL_ZVEL; + *cval = MAX_JUMP_CONTROL; + break; + + case M_CLIMB: + *cnum = CONTROL_YVEL; + *cval = -CONTROL_MAX_VAL; + break; + + case M_BANKLEFT: + *cnum = CONTROL_XYROT; + *cval = -CONTROL_MAX_VAL; + break; + + case M_BANKRIGHT: + *cnum = CONTROL_XYROT; + *cval = CONTROL_MAX_VAL; + break; + + case M_DIVE: + *cnum = CONTROL_YVEL; + *cval = CONTROL_MAX_VAL; + break; + + case M_ROLLRIGHT: + *cnum = CONTROL_XVEL; + *cval = -CONTROL_MAX_VAL; + break; + + case M_ROLLLEFT: + *cnum = CONTROL_XVEL; + *cval = CONTROL_MAX_VAL; + break; + + case M_CLIMBLEFT: + *cnum = CONTROL_YVEL; + *cval = -CONTROL_MAX_VAL; + if (abs(poll_controls[*cnum]) < abs(*cval)) + poll_controls[*cnum] = *cval; + *cnum = CONTROL_XYROT; + *cval = -CONTROL_MAX_VAL; + break; + + case M_CLIMBRIGHT: + *cnum = CONTROL_YVEL; + *cval = -CONTROL_MAX_VAL; + if (abs(poll_controls[*cnum]) < abs(*cval)) + poll_controls[*cnum] = *cval; + *cnum = CONTROL_XYROT; + *cval = CONTROL_MAX_VAL; + break; + + case M_DIVERIGHT: + *cnum = CONTROL_YVEL; + *cval = CONTROL_MAX_VAL; + if (abs(poll_controls[*cnum]) < abs(*cval)) + poll_controls[*cnum] = *cval; + *cnum = CONTROL_XYROT; + *cval = CONTROL_MAX_VAL; + break; + + case M_DIVELEFT: + *cnum = CONTROL_YVEL; + *cval = CONTROL_MAX_VAL; + if (abs(poll_controls[*cnum]) < abs(*cval)) + poll_controls[*cnum] = *cval; + *cnum = CONTROL_XYROT; + *cval = -CONTROL_MAX_VAL; + break; + default: + // Unhandled movement. What I gonna do? + ; + } + + return *cnum != -1; +} + +// always poll these codes; see init_motion_polling() below +static int always_motion_poll[] = { + CODE_UP, // up arrow + CODE_DOWN, // down arrow + CODE_LEFT, // left arrow + CODE_RIGHT, // right arrow + CODE_KP_HOME, // keypad home + CODE_KP_UP, // keypad up + CODE_KP_PGUP, // keypad pgup + CODE_KP_LEFT, // keypad left + CODE_KP_5, // keypad 5 + CODE_KP_RIGHT, // keypad right + CODE_KP_END, // keypad end + CODE_KP_DOWN, // keypad down + CODE_KP_PGDN, // keypad pgdn + CODE_KP_ENTER, // keypad enter + CODE_ENTER, // enter + + 255 // signal end of list +}; + +void init_motion_polling(void) { + int i, j = 0, code; + uchar used[256]; + + // keep track of which codes have already been added + memset(used, 0, 256); + + // add move keybinds to list of scancodes to poll + i = 0; + while (MoveKeybinds[i].code != 255) { + code = MoveKeybinds[i].code & 255; + if (!used[code]) { + used[code] = 1; + motion_key_scancodes[j++] = code; + } + i++; + } + + // add move cyber keybinds to list of scancodes to poll + i = 0; + while (MoveCyberKeybinds[i].code != 255) { + code = MoveCyberKeybinds[i].code & 255; + if (!used[code]) { + used[code] = 1; + motion_key_scancodes[j++] = code; + } + i++; + } + + // always poll these codes, so add them if they weren't added already + i = 0; + while (always_motion_poll[i] != 255) { + code = always_motion_poll[i]; + if (!used[code]) { + used[code] = 1; + motion_key_scancodes[j++] = code; + } + i++; + } + + motion_key_scancodes[j] = KBC_NONE; // signal end of list + + uiSetKeyboardPolling(motion_key_scancodes); +} + +void setup_motion_polling(void) { LG_memset(poll_controls, 0, sizeof(poll_controls)); } + +void process_motion_keys(void) { + physics_set_player_controls( + KEYBD_CONTROL_BANK, + poll_controls[CONTROL_XVEL], + poll_controls[CONTROL_YVEL], + poll_controls[CONTROL_ZVEL], + poll_controls[CONTROL_XYROT], + poll_controls[CONTROL_YZROT], + poll_controls[CONTROL_XZROT] + ); +} + +uchar motion_keycheck_handler(uiEvent *ev, LGRegion *r, intptr_t data) { + // KLC - For Mac version, we'll cook our own, since we have the modifier information. + ushort cooked = ev->poll_key_data.scancode | ev->poll_key_data.mods; + + short cnum, cval; + int moveOK = TRUE; + + if (gKeypadOverride) // if a keypad is showing + { + if (ev->poll_key_data.scancode >= 0x52 && ev->poll_key_data.scancode <= 0x5C) // and a keypad number was entered, + moveOK = FALSE; // don't move. + } + + if (moveOK) { + if ((global_fullmap->cyber && parse_motion_key_cyber(cooked, &cnum, &cval)) || + parse_motion_key(cooked, &cnum, &cval)) { + if (abs(poll_controls[cnum]) < abs(cval)) + poll_controls[cnum] = cval; + } + } + + return TRUE; +} diff --git a/engine/src/GameSrc/musicai.c b/engine/src/GameSrc/musicai.c new file mode 100644 index 0000000..a4263c2 --- /dev/null +++ b/engine/src/GameSrc/musicai.c @@ -0,0 +1,770 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/musicai.c $ + * $Revision: 1.124 $ + * $Author: unknown $ + * $Date: 1994/11/26 03:16:18 $ + */ + +#include + +#include "Shock.h" +#include "Prefs.h" + +#include "airupt.h" +#include "musicai.h" +#include "MacTune.h" + +#include "map.h" +#include "mapflags.h" +#include "player.h" +#include "tickcount.h" +#include "tools.h" + +#include "adlmidi.h" +#include "Xmi.h" + +#ifdef AUDIOLOGS +#include "audiolog.h" +#endif + +//#include + +uchar music_card = TRUE, music_on = FALSE; + +uchar track_table[NUM_SCORES][SUPERCHUNKS_PER_SCORE]; +uchar transition_table[NUM_TRANSITIONS]; +uchar layering_table[NUM_LAYERS][MAX_KEYS]; +uchar key_table[NUM_LAYERABLE_SUPERCHUNKS][KEY_BAR_RESOLUTION]; + +char peril_bars = 0; + +int new_theme = 0; +int new_x, new_y; +int old_bore; +short mai_override = 0; +uchar cyber_play = 255; + +int layer_danger = 0; +int layer_success = 0; +int layer_transition = 0; +int transition_count = 0; +char tmode_time = 0; +int actual_score = 0; +uchar decon_count = 0; +uchar decon_time = 8; +uchar in_deconst = FALSE, old_deconst = FALSE; +uchar in_peril = FALSE; +uchar just_started = TRUE; +int score_playing = 0; +short curr_ramp_time, curr_ramp; +char curr_prioritize, curr_crossfade; + +int mlimbs_peril, mlimbs_positive, mlimbs_motion, mlimbs_monster; +ulong mlimbs_combat; +int current_score, current_zone, current_mode, random_flag; +int current_transition, last_score; +int boring_count; +int mlimbs_boredom; +int *output_table; +uchar wait_flag; +int next_mode, ai_cycle; +int cur_digi_channels = 4; + +// extern int digifx_volume_shift(short x, short y, short z, short phi, short theta, short basevol); +// extern int digifx_pan_shift(short x, short y, short z, short phi, short theta); +extern uchar mai_semaphor; + +uchar park_random = 75; +uchar park_playing = 0; +uchar access_random = 45; + +ulong last_damage_sum = 0; +ulong last_vel_time = 0; + +// Damage taken decay & quantity of decay +int danger_hp_level = 10; +int danger_damage_level = 40; +int damage_decay_time = 300; +int damage_decay_amount = 6; +int mai_damage_sum = 0; + +// How long an attack keeps us in combat music mode +int mai_combat_length = 1000; + +uchar bad_digifx = FALSE; + +// KLC - no longer need this Datapath music_dpath; + +#define SMALL_ROBOT_LAYER 3 + +char mlimbs_machine = 0; + +//------------------ +// INTERNAL PROTOTYPES +//------------------ + +errtype musicai_shutdown() { + int i; + for (i = 0; i < MLIMBS_MAX_SEQUENCES - 1; i++) + current_request[i].pieceID = 255; + MacTuneKillCurrentTheme(); + return (OK); +} + +extern uchar run_asynch_music_ai; + +errtype musicai_reset(uchar runai) { + if (runai) // Figure out if there is a theme to start with. + grind_music_ai(); + mlimbs_counter = 0; + return (OK); +} + +void musicai_clear() { + mai_damage_sum = 0; + last_damage_sum = 0; + mlimbs_combat = 0; +} + +void mlimbs_do_ai() { + // extern uchar mlimbs_semaphore; + extern ObjID damage_sound_id; + extern char damage_sound_fx; + + if (!IsPlaying(0)) gReadyToQueue = 1; + + + //repeat shorter tracks while thread 0 is still playing + if (!gReadyToQueue) + { + for (int i = 1; i < MLIMBS_MAX_CHANNELS - 1; i++) + if (current_request[i].pieceID != 255) + if (!IsPlaying(i)) + { + make_request(i, current_request[i].pieceID); + current_request[i].pieceID = 255; //make sure it only plays this time + } + } + + + /* Is this really necessary? It's already called twice in fr_rend(). + #ifdef AUDIOLOGS + audiolog_loop_callback(); + #endif + */ + // Play any queued sound effects, or damage SFX that have yet to get flushed + if (damage_sound_fx != -1) { + play_digi_fx_obj(damage_sound_fx, 1, damage_sound_id); + damage_sound_fx = -1; + } + + if (music_on) { + if (mlimbs_combat != 0) { + if (mlimbs_combat < player_struct.game_time) + mlimbs_combat = 0; + } + + // Set danger layer + layer_danger = 0; + if (mai_damage_sum > danger_damage_level) + layer_danger = 2; + else if (player_struct.hit_points < danger_hp_level) + layer_danger = 1; + + // Decay damage + if ((last_damage_sum + damage_decay_time) < player_struct.game_time) { + mai_damage_sum -= damage_decay_amount; + if (mai_damage_sum < 0) + mai_damage_sum = 0; + last_damage_sum = player_struct.game_time; + } + + if ((score_playing == BRIDGE_ZONE) && in_peril) { + mlimbs_peril = DEFAULT_PERIL_MIN; + mlimbs_combat = 0; + } else { + if ((mlimbs_combat > 0) || in_peril) { + mlimbs_peril = DEFAULT_PERIL_MAX; + } + } + + // KLC - moved here from grind_music_ai, so it can do this check at all times. + if (global_fullmap->cyber) { + MapElem *pme; + int play_me; + + pme = MAP_GET_XY(PLAYER_BIN_X, PLAYER_BIN_Y); // Determine music for this + if (!me_bits_peril(pme)) // location in cyberspace. + play_me = NUM_NODE_THEMES + me_bits_music(pme); + else + play_me = me_bits_music(pme); + if (play_me != cyber_play) // If music needs to be changed, then + { + musicai_shutdown(); // stop playing current tune + make_request(0, play_me); // setup new tune + musicai_reset(FALSE); // reset MLIMBS and + MacTuneStartCurrentTheme(); // start playing the new tune. + } else + make_request(0, play_me); // otherwise just queue up next tune. + cyber_play = play_me; + } + + // This is all pretty temporary right now, but here's what's happening. + // If the gReadyToQueue flag is set, that means the 6-second timer has + // fired. So we call check_asynch_ai() to determine the next tune to play + // then queue it up. + // Does not handle layering. Just one music track! + if (gReadyToQueue) { + extern bool mlimbs_update_requests; + mlimbs_update_requests = TRUE; + + if (!global_fullmap->cyber) + check_asynch_ai(TRUE); + int pid = current_request[0].pieceID; + if (pid != 255) // If there is a theme to play, + { + MacTuneQueueTune(pid); // Queue it up. + mlimbs_counter++; // Tell mlimbs we've queued another tune. + gReadyToQueue = FALSE; + } + } + + // If a tune has finished playing, then another has just started, so prime the + // timer to do the next tune calc. + // if (gTuneDone) { + // MacTunePrimeTimer(); + // gTuneDone = FALSE; + // } + } +} + +#ifdef NOT_YET // + +void mlimbs_do_credits_ai() { + extern uchar mlimbs_semaphore; + if (ai_cycle) { + ai_cycle = 0; + grind_credits_music_ai(); + mlimbs_preload_requested_timbres(); + mlimbs_semaphore = FALSE; + } +} + +#endif // NOT_YET + +errtype mai_attack() { + if (music_on) { + mlimbs_combat = player_struct.game_time + mai_combat_length; + } + return (OK); +} + +errtype mai_intro() { + if (music_on) { + if (transition_table[TRANS_INTRO] != 255) + mai_transition(TRANS_INTRO); + mlimbs_peril = DEFAULT_PERIL_MIN; + mlimbs_combat = 0; + } + return (OK); +} + +errtype mai_monster_nearby(int monster_type) { + if (music_on) { + mlimbs_monster = monster_type; + if (monster_type == NO_MONSTER) { + mlimbs_combat = 0; + mlimbs_peril = DEFAULT_PERIL_MIN; + } + } + return (OK); +} + +errtype mai_monster_defeated() { + if (music_on) { + mlimbs_combat = 0; + } + return (OK); +} + +errtype mai_player_death() { + if (music_on) { + mai_transition(TRANS_DEATH); + mlimbs_peril = DEFAULT_PERIL_MIN; + peril_bars = 0; + layer_danger = 0; + mai_damage_sum = 0; + layer_success = 0; + mlimbs_machine = 0; + mlimbs_monster = 0; + mlimbs_combat = 0; + musicai_shutdown(); + make_request(0, transition_table[TRANS_DEATH]); + musicai_reset(FALSE); + MacTuneStartCurrentTheme(); + } + return (OK); +} + +errtype mlimbs_AI_init(void) { + mlimbs_boredom = 0; + old_bore = 0; + mlimbs_monster = NO_MONSTER; + wait_flag = FALSE; + random_flag = 0; + boring_count = 0; + ai_cycle = 0; + mlimbs_peril = DEFAULT_PERIL_MAX; + current_transition = TRANS_INTRO; + current_mode = TRANSITION_MODE; + tmode_time = 1; // KLC - was 4 + current_score = actual_score = last_score = WALKING_SCORE; + current_zone = HOSPITAL_ZONE; + // mlimbs_AI = &music_ai; + cyber_play = 255; + + return (OK); +} + +errtype mai_transition(int new_trans) { + if ((next_mode == TRANSITION_MODE) || (current_mode == TRANSITION_MODE)) + return (ERR_NOEFFECT); + + if (transition_table[new_trans] < LAYER_BASE) { + current_transition = new_trans; + next_mode = TRANSITION_MODE; + tmode_time = 1; // KLC - was 4 + } else if ((transition_count == 0) && (layering_table[TRANSITION_LAYER_BASE + new_trans][0] != 255)) { + current_transition = new_trans; + // For now, let's not do any layered transitions. + // transition_count = 1; //KLC - was 2 + } + // temp + /* + char msg[30]; + lg_sprintf(msg, "Transitioning:%d, mode:%d, count:%d", new_trans, next_mode, transition_count); + message_info(msg); + */ + return (OK); +} + +int gen_monster(int monster_num) { + if (monster_num < 3) + return (0); + if (monster_num < 6) + return (1); + return (2); +} + +int ext_rp = -1; + +extern struct mlimbs_request_info default_request; + +errtype make_request(int chunk_num, int piece_ID) { + current_request[chunk_num] = default_request; + current_request[chunk_num].pieceID = piece_ID; + + // These get set all around differently and stuff + current_request[chunk_num].crossfade = curr_crossfade; + current_request[chunk_num].ramp_time = curr_ramp_time; + current_request[chunk_num].ramp = curr_ramp; + + DEBUG("make_request %i %i", chunk_num, piece_ID); + + extern int WonGame_ShowStats; + + int i = chunk_num; + int track = 1+piece_ID; + if (i >= 0 && i < NUM_THREADS && track >= 0 && track < NumTracks && !WonGame_ShowStats && !IsPlaying(i)) + { +// extern uchar curr_vol_lev; +// int volume = (int)curr_vol_lev * 127 / 100; //convert from 0-100 to 0-127 + StartTrack(i, track); + } + + return (OK); +} + +/* +errtype load_score_from_cfg(FSSpec *specPtr) +{ + short filenum; + Handle binHdl; + Ptr p; + + filenum = FSpOpenResFile(specPtr, fsRdPerm); + if (filenum == -1) + return (ERR_FOPEN); + binHdl = GetResource('tbin', 128); + if (binHdl == NULL) + return (ERR_FOPEN); + + HLock(binHdl); + p = *binHdl; + BlockMoveData(p, track_table, NUM_SCORES * SUPERCHUNKS_PER_SCORE); + p += NUM_SCORES * SUPERCHUNKS_PER_SCORE; + BlockMoveData(p, transition_table, NUM_TRANSITIONS); + p += NUM_TRANSITIONS; + BlockMoveData(p, layering_table, NUM_LAYERS * MAX_KEYS); + p += NUM_LAYERS * MAX_KEYS; + BlockMoveData(p, key_table, NUM_LAYERABLE_SUPERCHUNKS * KEY_BAR_RESOLUTION); + HUnlock(binHdl); + + CloseResFile(filenum); + return(OK); +} +*/ + +int old_score; + +errtype fade_into_location(int x, int y) { + MapElem *pme; + + new_x = x; + new_y = y; + new_theme = 2; + pme = MAP_GET_XY(new_x, new_y); + score_playing = me_bits_music(pme); + + // For going into/outof elevator and cyberspace, don't do any crossfading. + if ((score_playing == ELEVATOR_ZONE) || (score_playing > CYBERSPACE_SCORE_BASE) || (old_score == ELEVATOR_ZONE) || + (old_score > CYBERSPACE_SCORE_BASE)) { + if (old_score != score_playing) // Don't restart music if going from elevator + { // to elevator (eg, when changing levels). + load_score_for_location(new_x, new_y); + MacTuneStartCurrentTheme(); + new_theme = 0; + } + } else // for now, we're not going to do any cross-fading. Just load the new score. + { + // message_info("Sould be fading into new location."); + load_score_for_location(new_x, new_y); + MacTuneStartCurrentTheme(); + new_theme = 0; + } + return (OK); +} + +/*KLC - don't need +errtype blank_theme_data() +{ + LG_memset(track_table, 255, NUM_SCORES * SUPERCHUNKS_PER_SCORE * sizeof(uchar)); + LG_memset(transition_table, 255, NUM_TRANSITIONS * sizeof(uchar)); + LG_memset(layering_table, 255, NUM_LAYERS * MAX_KEYS * sizeof(uchar)); + LG_memset(key_table, 255, NUM_LAYERABLE_SUPERCHUNKS * KEY_BAR_RESOLUTION * sizeof(uchar)); + return(OK); +} +*/ + +// don't need? uchar voices_4op = FALSE; +// don't need? uchar digi_gain = FALSE; +void load_score_guts(uint8_t score_play) { + int rv; + char base[20]; + + // Get the theme file name. + sprintf(base, "thm%d", score_play); + musicai_shutdown(); + + // rv = MacTuneLoadTheme(&themeSpec, score_playing); + rv = MacTuneLoadTheme(base, score_play); + + if (rv == 0) { + musicai_reset(false); + } + else { + DEBUG("%s: load theme failed!", __FUNCTION__); // handle this a better way. + } +} + +errtype load_score_for_location(int x, int y) { + MapElem *pme; + char sc; + extern char old_bits; + + pme = MAP_GET_XY(x, y); + sc = me_bits_music(pme); + // KLC if ((global_fullmap->cyber) && (sc != 0)) + if (global_fullmap->cyber) + sc = CYBERSPACE_SCORE_BASE; + old_bits = old_score = score_playing = sc; + if (sc == 7) // Randomize boredom for the elevator + mlimbs_boredom = TickCount() % 8; + else + mlimbs_boredom = 0; + load_score_guts(sc); + return (OK); +} + +#ifdef NOT_YET // + +// 16384 +// 8192 +//#define SFX_BUFFER_SIZE 8192 +#define MIDI_TYPE 0 +#define DIGI_TYPE 1 +// #define SPCH_TYPE 2 // perhaps someday, for special CD speech and separate SB digital effects, eh? +#define DEV_TYPES 2 + +#define DEV_CARD 0 +#define DEV_IRQ 1 +#define DEV_DMA 2 +#define DEV_IO 3 +#define DEV_DRQ 4 +#define DEV_PARMS 5 + +// doug gets sneaky, film at 11 +#define MIDI_CARD MIDI_TYPE][DEV_CARD +#define MIDI_IRQ MIDI_TYPE][DEV_IRQ +#define MIDI_DMA MIDI_TYPE][DEV_DMA +#define MIDI_IO MIDI_TYPE][DEV_IO +#define MIDI_DRQ MIDI_TYPE][DEV_DRQ +#define DIGI_CARD DIGI_TYPE][DEV_CARD +#define DIGI_IRQ DIGI_TYPE][DEV_IRQ +#define DIGI_DMA DIGI_TYPE][DEV_DMA +#define DIGI_IO DIGI_TYPE][DEV_IO +#define DIGI_DRQ DIGI_TYPE][DEV_DRQ + +#define SFX_BUFFER_SIZE 8192 +//#define SFX_BUFFER_SIZE 4096 + +static char *dev_suffix[] = {"card", "irq", "dma", "io", "drq"}; +static char *dev_prefix[] = {"midi_", "digi_"}; + +short music_get_config(char *pre, char *suf) { + int tmp_in, dummy_count = 1; + char buf[20]; + strcpy(buf, pre); + strcat(buf, suf); + if (!config_get_value(buf, CONFIG_INT_TYPE, &tmp_in, &dummy_count)) + return -1; + else + return (short)tmp_in; +} + +audio_card *fill_audio_card(audio_card *cinf, short *dinf) { + cinf->type = dinf[DEV_CARD]; + cinf->dname = NULL; + cinf->io = dinf[DEV_IO]; + cinf->irq = dinf[DEV_IRQ]; + cinf->dma_8bit = dinf[DEV_DMA]; + cinf->dma_16bit = -1; // who knows, eh? + return cinf; +} + +#ifdef PLAYTEST +static char def_sound_path[] = "r:\\prj\\cit\\src\\sound"; +#else +static char def_sound_path[] = "sound"; +#endif + +#ifdef SECRET_SUPPORT +FILE *secret_fp = NULL; +char secret_dc_buf[10000]; +volatile char secret_update = FALSE; +void secret_closedown(void) { + if (secret_fp != NULL) + fclose(secret_fp); +} +#endif + +#endif // NOT_YET + +//---------------------------------------------------------------------- +// For Mac version, the vast majority of the config mess just goes away. But we do check for +// the presence of QuickTime Musical Instruments. +//---------------------------------------------------------------------- +errtype music_init() { + /* put in later + int i,j; + uchar gm=FALSE; + short dev_info[DEV_TYPES][DEV_PARMS]; + char s[64],path[64]; + audio_card card_info; + extern uchar curr_sfx_vol; + extern char curr_vol_lev; + + #ifdef SECRET_SUPPORT + if ((secret_fp=fopen("secret.ddb","wt"))!=NULL) + { + secret_dc_buf[0]='\0'; + mono_clear(); + mono_split(MONO_AXIS_Y,4); + mono_setwin(2); + } + atexit(secret_closedown); + #endif + + strcpy(s,def_sound_path); + + dev_info[MIDI_CARD]=music_get_config(dev_prefix[0],dev_suffix[0]); + dev_info[DIGI_CARD]=music_get_config(dev_prefix[1],dev_suffix[0]); + + DatapathClear(&music_dpath); + + // can we make this actually know what is going on? + switch (dev_info[MIDI_CARD]) + { // probably should be in the library, not here... + case GRAVISULTRASTUPID: case MT32: case GENMIDI: case AWE32: case SOUNDSCAPE: case RAP_10: gm=TRUE; break; + } + + // add contents of CFG_SOUNDVAR + if (config_get_raw(CFG_SOUNDVAR,path,64)) + { + // mprintf("hey, path = %s\n",path); + DatapathAdd(&music_dpath, path); + if (gm) + { strcat(path,"\\genmidi"); } + else + { strcat(path,"\\sblaster"); } + // mprintf("now, path = %s\n",path); + DatapathAdd(&music_dpath,path); + } + + // add contents of CFG_CD_SOUNDVAR + if (config_get_raw(CFG_CD_SOUNDVAR,path,64)) + { + // mprintf("hey, path = %s\n",path); + DatapathAdd(&music_dpath, path); + if (gm) + { strcat(path,"\\genmidi"); } + else + { strcat(path,"\\sblaster"); } + // mprintf("now, path = %s\n",path); + DatapathAdd(&music_dpath,path); + } + + #ifdef PLAYTEST + DatapathAdd(&music_dpath,s+15); // and go back and add net/sound if necessary + #else + DatapathAdd(&music_dpath,s); + #endif + + if (gm) + { strcat(s,"\\genmidi"); } + else + { strcat(s,"\\sblaster"); } + + #ifdef PLAYTEST + DatapathAdd(&music_dpath,s+15); // add just sound/devtype + DatapathAdd(&music_dpath,s); + s[21] = '\0'; + DatapathAdd(&music_dpath,s); + #else + DatapathAdd(&music_dpath,s); // add just sound/devtype + #endif + + snd_setup(&music_dpath,"sound/cit"); // really it should go find cit.ad on the datapath, then use its path + + music_card=(dev_info[MIDI_CARD]>0); + sfx_card =(dev_info[DIGI_CARD]>0); + if (!(music_card||sfx_card)) + { + curr_sfx_vol = 0; + curr_vol_lev = 0; + return(ERR_NODEV); + } + + for (i=0; i + if ((dev_info[MIDI_CARD]==GENMIDI)&&(dev_info[DIGI_CARD]==SOUNDBLASTERPRO2)) { + int mod_loc=dev_info[DIGI_IO]; // loc, the io port to send too + if (mod_loc==-1) mod_loc=0x220; // i know much secretness of destruction + outp(mod_loc+4,0x83); // such that def io is 220, which AIL wont + outp(mod_loc+5,0xb); // tell me till later, when we init it + } // which we are not allowed to do yet + } + #endif + + if (music_card) + { + if (snd_start_midi(fill_audio_card(&card_info,dev_info[MIDI_TYPE]))!=SND_OK) + { + Warning(("Device %d not loaded for Midi at %x %x %x + %x\n",dev_info[MIDI_CARD],dev_info[MIDI_IO],dev_info[MIDI_IRQ],dev_info[MIDI_DMA],dev_info[MIDI_DRQ])); music_card = + FALSE; curr_vol_lev = 0; + } + else + { + mlimbs_init(); + } + } + else + curr_vol_lev = 0; + + if (sfx_card) + { + if (snd_start_digital(fill_audio_card(&card_info,dev_info[DIGI_TYPE]))!=SND_OK) + { + Warning(("Device %d not loaded for DigiFx at %x %x %x + %x\n",dev_info[DIGI_CARD],dev_info[DIGI_IO],dev_info[DIGI_IRQ],dev_info[DIGI_DMA],dev_info[DIGI_DRQ])); sfx_card = + FALSE; curr_sfx_vol = 0; + } + else // note this use to allocate double buffer space here + { + snd_set_digital_channels(cur_digi_channels); + // digi_gain = TRUE; // ie look at detail and stuff + } + } + else + curr_sfx_vol = 0; + + if (sfx_card) + { + sfx_on=TRUE; + #ifdef AUDIOLOGS + audiolog_init(); + #endif + } + */ + if (gShockPrefs.soBackMusic) { + // if (music_card) + // { + if (MacTuneInit() == 0) // If no error, go ahead and start up. + { + music_on = mlimbs_on = TRUE; + mlimbs_AI_init(); + } else // else turn off the music globals and prefs + { + gShockPrefs.soBackMusic = FALSE; + SavePrefs(); + music_on = mlimbs_on = FALSE; + } + // } + } else { + music_on = mlimbs_on = FALSE; + } + return (OK); +} + +/* KLC - doesn't do anything +void music_free(void) +{ + DatapathFree(&music_dpath); +} +*/ diff --git a/engine/src/GameSrc/newai.c b/engine/src/GameSrc/newai.c new file mode 100644 index 0000000..23bd940 --- /dev/null +++ b/engine/src/GameSrc/newai.c @@ -0,0 +1,976 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/newai.c $ + * $Revision: 1.43 $ + * $Author: xemu $ + * $Date: 1994/10/24 23:15:00 $ + * + */ + +#include + +#include "ai.h" +#include "cyber.h" +#include "player.h" +#include "pathfind.h" +#include "otrip.h" +#include "objects.h" +#include "objprop.h" +#include "objgame.h" +#include "objcrit.h" +#include "objsim.h" +#include "objbit.h" +#include "faketime.h" +#include "musicai.h" +#include "tilename.h" +#include "ice.h" +#include "tools.h" +#include "trigger.h" +#include "map.h" +#include "mapflags.h" +#include "damage.h" +#include "combat.h" +#include "physics.h" +#include "safeedms.h" +#include "aiflags.h" +#include "doorparm.h" +#include "diffq.h" +#include "visible.h" + +// SOME YUMMY DEFINES! +#define AI_EDMS + +// A bunch of visibility related factors. +// These should probably wind up in a quickbox? +#define BASE_VISIBILITY 178 +#define SHIELD_VISIBLE_FACTOR 52 +#define LIGHT_VISIBLE_FACTOR 75 +#define LANTERN_VISIBLE_FACTOR 80 +#define GUN_VISIBLE_FACTOR 168 +#define UNTRACK_VISIBLE_FACTOR 44 +#define CROUCH_VISIBLE_FACTOR 16 +#define ZDIFF_VISIBLE_FACTOR 1 +#define DIST_VISIBLE_FACTOR 3 +#define BEHIND_VISIBLE_FACTOR 128 + +#define MEDIAN_PERCEPTION 64 + +#define DEFAULT_SPEED fix_make(0xD, 0) + +// Number of ticks where, if we haven't seen the player in that long, +// we forget where he is. +#define AI_ATTENTION_SPAN CIT_CYCLE * 10 + +extern ObjLoc last_known_loc; +ulong time_last_seen; +uchar priority_check; + +short compute_base_visibility(); +errtype run_evil_otto(ObjID id, int dist); +errtype run_cspace_ice(); +errtype ai_spot_player(ObjID id, uchar *raycast_success); +uchar do_physics_stupidity(ObjID id, int big_dist); +void check_attitude_adjustment(ObjID id, ObjSpecID osid, int big_dist, uchar raycast_success); +void load_combat_art(int cp_num); +errtype run_combat_ai(ObjID id, uchar raycast_success); +errtype do_stealth_stuff(ObjID id, short base_vis, uchar *raycast_success, fix dist); +void set_des_heading(ObjID id, ObjSpecID osid, fix targ_x, fix targ_y, fixang *angdiff, fixang *target_ang); +errtype follow_pathfinding(ObjID id, ObjSpecID osid); +LGPoint ai_patrol_func(ObjID id, ObjSpecID osid); +LGPoint ai_highway_func(ObjID id, ObjSpecID osid); +LGPoint ai_roam_func(ObjID id, ObjSpecID osid); +LGPoint ai_none_func(ObjID, ObjSpecID osid); +errtype run_peaceful_ai(ObjID id, int big_dist); + +// Run all the ICEs, deal with their agitation, etc. Boy, this could probably +// be a lot smarter than iterating through all objects, like having the +// agitated objects flag themselves as such when they become agitated, etc. + +#define SHODAN_AVATAR_HOSAGE_DISTANCE 0xA0 +#define AGITATED_ICE_DIST 49 +#define ICE_INTERVAL (CIT_CYCLE >> 1) + +// chance vs 0xFF of firing on a given half-second +uchar ice_fire_chances[] = {0x40, 0x80, 0xD0, 0xF0}; +ulong run_ice_time = 0; + +errtype run_cspace_ice() { + int dx, dy, dist; + // Look for hostile ICEs, which closely resemble creatures + // of course, only do so if we be in cspace + + // I'm agitated. Yeah, agitated. -- Devo + for (ObjID id = (objs[OBJ_NULL]).headused; id != OBJ_NULL; id = objs[id].next) { + // Are we an agitated ice encrusted thing? Is it our stochastic time as determined by Lord Chaos? + if (ICE_ICE_BABY(id) && ICE_AGIT(id) && ((rand() & 0xFF) < ice_fire_chances[ICE_LEVEL(id)])) { + dx = PLAYER_BIN_X - OBJ_LOC_BIN_X(objs[id].loc); + dy = PLAYER_BIN_Y - OBJ_LOC_BIN_Y(objs[id].loc); + dist = dx * dx + dy * dy; + + // Are we close enough to the player to care? + if (dist < AGITATED_ICE_DIST) { + // Lob a slow projectile off at that wacky player. Boy, we're perfectly statically accurate. + ai_fire_special(id, PLAYER_OBJ, CYBERBOLT_TRIPLE, objs[id].loc, objs[PLAYER_OBJ].loc, ICE_LEVEL(id), + SLOW_PROJECTILE_DURATION); + } + } + } + run_ice_time = player_struct.game_time + ICE_INTERVAL; + return (OK); +} + +// Compute and return the player's basic visibility for this frame +short compute_base_visibility() { + MapElem *pme; + short visibility; + + // Update detection variables + if (!cspace_decoy_obj) { + if (time_last_seen > player_struct.game_time + AI_ATTENTION_SPAN) + last_known_loc.x = 255; + + // compute basic visibility + + pme = MAP_GET_XY(PLAYER_BIN_X, PLAYER_BIN_Y); + + // Then we roll against our perception, after figuring in + // tons of factors... + // base visibility + visibility = BASE_VISIBILITY; + + // More visible if shields up (can't check yet) + + // More visible if standing in light + visibility += (LIGHT_VISIBLE_FACTOR / lg_max(1, (lg_max(0, me_light_flr(pme) - me_templight_flr(pme)) + + lg_max(0, me_light_ceil(pme) - me_templight_ceil(pme))) / + 2)); + visibility += LANTERN_VISIBLE_FACTOR * player_struct.light_value; + + // More visible if fired gun recently (this may want to be exponentially decaying + if (player_struct.last_fire) { // make sure we've actually fired a weapon + visibility += + ((GUN_VISIBLE_FACTOR * CIT_CYCLE) / lg_max(player_struct.game_time - player_struct.last_fire, 1)); + } + + // Less visible if critters have lost track of ya + if (last_known_loc.x == 255) + visibility -= UNTRACK_VISIBLE_FACTOR; + + // Some things that don't affect you in cspace + if (!global_fullmap->cyber) { + // Less visible if crouching or crawling + visibility -= CROUCH_VISIBLE_FACTOR * player_struct.posture; + } + } + return (visibility); +} + +// id is evil otto, deal accordingly (moving, hosing, etc.) +errtype run_evil_otto(ObjID id, int dist) { + // Are we close enough to totally hose the player? + if (dist < SHODAN_AVATAR_HOSAGE_DISTANCE) { + damage_player(15, 0x7, NO_SHIELD_ABSORBTION); + // attack_object(PLAYER_OBJ, CritterProps[cp_num].attacks[0].damage_type, + // CritterProps[cp_num].attacks[0].damage_modifier, + // CritterProps[cp_num].attacks[0].offense_value, CritterProps[cp_num].attacks[0].penetration, 0, 100, + // NULL, 0, 0, NULL); + } else { + fix xvec, yvec, fdist; + int dx, dy; + ObjLoc newloc = objs[id].loc; + // Move the AVAMATAR OF SHODAN (Evil Otto) closer to the player + // first, get a normalized vector + dx = PLAYER_BIN_X - OBJ_LOC_BIN_X(objs[id].loc); + dy = PLAYER_BIN_Y - OBJ_LOC_BIN_Y(objs[id].loc); + xvec = fix_from_obj_coord(dx); + yvec = fix_from_obj_coord(dy); + fdist = fix_fast_pyth_dist(xvec, yvec); + xvec = fix_div(xvec, fdist) >> 2; + yvec = fix_div(yvec, fdist) >> 2; + + // then, move otto along it + newloc.x = obj_coord_from_fix(xvec + fix_from_obj_coord(newloc.x)); + newloc.y = obj_coord_from_fix(yvec + fix_from_obj_coord(newloc.y)); + newloc.z = objs[PLAYER_OBJ].loc.z; + // Warning(("AVATAR_SHODAN at 0x%x, 0x%x (dist = 0x%x)\n",newloc.x,newloc.y,dist)); + obj_move_to(id, &newloc, FALSE); + } + return (OK); +} + +// Does appropriate destruction & reconstitution of physics part +// of said critter. +// Returns whether or not we should continue to think +// about this particular creature. +short ignore_distance[] = {6, 10}; + +uchar do_physics_stupidity(ObjID id, int big_dist) { + ObjSpecID osid = objs[id].specID; + int dist = big_dist >> 8; + int use_dist; + fix there_yet; + +#ifdef DISTANCE_AI_KILL + // don't phys-kill anything that is currently pathfinding or in combat mode (it'll leave combat mode + // after a while anyways) + if ((objCritters[osid].path_id != -1) || (objCritters[osid].mood == AI_MOOD_HOSTILE) || + (objCritters[osid].mood == AI_MOOD_ATTACKING)) { + if (!EDMS_frere_jaques(objs[id].info.ph)) { + // Spew(DSRC_PHYSICS_Sleeper, ("obj id %x, ph = %d(0x%x) is PF or attack but + // asleep!\n",id,objs[id].info.ph,objs[id].info.ph)); Spew(DSRC_PHYSICS_Sleeper, ("obj id %x, head = + // %x, spd = %x, urg = %x\n",id,objCritters[osid].des_heading, + // objCritters[osid].des_speed, objCritters[osid].urgency)); + } + return (TRUE); + } + + use_dist = ignore_distance[global_fullmap->cyber]; + + // This really needs to deal with cameras! + if (dist > use_dist) { + // What, we've gone too far away? Well, then set us to zero so EDMS sleeps us nicely + if (CHECK_OBJ_PH(id)) + safe_EDMS_ai_control_robot(objs[id].info.ph, 0, 0, 0, 0, &there_yet, 0); + // else if (!(get_crit_posture(osid) == DEATH_CRITTER_POSTURE)) + // Warning(("hey, trying to sleep id %x without no physics handle (ph = %x)!\n",id,objs[id].info.ph)); + // if (EDMS_frere_jaques(objs[id].info.ph)) + // Spew(DSRC_PHYSICS_Sleeper, ("obj id %x, ph = %d(0x%x) is too far but + // awake!\n",id,objs[id].info.ph,objs[id].info.ph)); + return (FALSE); + } +#endif + if ((!EDMS_frere_jaques(objs[id].info.ph)) && (objCritters[osid].des_speed != 0)) { + // Spew(DSRC_PHYSICS_Sleeper, ("obj id %x, ph = %d(0x%x) is in range but + // asleep!\n",id,objs[id].info.ph,objs[id].info.ph)); Spew(DSRC_PHYSICS_Sleeper, ("obj id %x, head = %x (vs + // %x), spd = %x, urg = %x\n",id,objCritters[osid].des_heading, + // objCritters[osid].des_speed, objCritters[osid].urgency)); + } + return (TRUE); +} + +// If our current pathfind is greater than REPATHFIND_DIST away from reality, +// punt and repathfind +#define REPATHFIND_DIST 0x4 +errtype ai_spot_player(ObjID id, uchar *raycast_success) { + ObjSpecID osid = objs[id].specID; + ObjCritter *pcrit = &objCritters[osid]; + + time_last_seen = player_struct.game_time; + *raycast_success = TRUE; + ai_find_player(id); + + // If not friendly, trigger the combat music + if ((pcrit->mood != AI_MOOD_FRIENDLY) && (!ai_critter_sleeping(osid))) + ai_critter_seen(); + + // Aha! We have seen the player, so if we are of a sort to get ticked off, + // then let's do so. + if (QUESTVAR_GET(COMBAT_DIFF_QVAR) > 0) { + if ((pcrit->mood == AI_MOOD_NEUTRAL) || (pcrit->mood == AI_MOOD_ISOLATION)) { + // punt our old, probably irrelevant pathfind + if (pcrit->path_id != -1) { + delete_path(pcrit->path_id); + pcrit->path_id = -1; + } + } else if (pcrit->path_id != -1) { + // if the difference between our current path's destination and the actual + // location of the player is too great, punt the old one. + if (long_fast_pyth_dist((last_known_loc.x >> 8) - paths[pcrit->path_id].dest.x, + (last_known_loc.y >> 8) - paths[pcrit->path_id].dest.y) > REPATHFIND_DIST) { + pcrit->path_id = -1; + delete_path(pcrit->path_id); + } + } + if ((pcrit->mood == AI_MOOD_NEUTRAL) || (pcrit->mood == AI_MOOD_ISOLATION)) { + if (pcrit->path_id != -1) + delete_path(pcrit->path_id); + pcrit->path_id = -1; + pcrit->mood = AI_MOOD_HOSTILE; + pcrit->flags |= AI_FLAG_CHASING; + // and play our "notice" sound effect + play_digi_fx_obj(CritterProps[CPNUM(id)].notice_sound, 1, id); + } + } + return (OK); +} + +// Do appropriate stuff for critter object id to try and find the +// player +errtype do_stealth_stuff(ObjID id, short base_vis, uchar *raycast_success, fix dist) { + short use_vis; + State plr_state, our_state; + fixang ang_diff, real_ang; + int r; + ObjSpecID osid = objs[id].specID; + ObjCritter *pcrit = &objCritters[osid]; + + if (cspace_decoy_obj) { + time_last_seen = player_struct.game_time; + *raycast_success = TRUE; + last_known_loc = objs[cspace_decoy_obj].loc; + } else { + // Where's Waldo? + // We only get to look when we're getting to run AI on ourselves. + + // If we are "chasing" the player (via our flags) + // Then we don't have to actually spot the player to know his current location + // although we don't count as having "spotted" the player (so that we still time out if we haven't + // found the player in ATTENTION_SPAN amount of time). + // Even if this is true, we do the whole visibility rigamarole so that if we DO see the player, + // then we reset our attention span. + if (pcrit->flags & AI_FLAG_CHASING) + last_known_loc = objs[PLAYER_OBJ].loc; + + // We do all the stealth rolling first, so that we can cut out raycasting whenever possible + + // We should also filter out cases where player is behind der robotenhausen + use_vis = base_vis; + + // Less visible if on far Z to searcher + if (!global_fullmap->cyber) { + use_vis -= ZDIFF_VISIBLE_FACTOR * abs(objs[PLAYER_OBJ].loc.z - objs[id].loc.z); + + // Less visible in far away in normal coords + use_vis -= DIST_VISIBLE_FACTOR * fix_int(dist); + } + + safe_EDMS_get_state(objs[PLAYER_OBJ].info.ph, &plr_state); + // if (!CHECK_OBJ_PH(id)) + // Warning(("Ack! 0x%x ph == %d (osid = 0x%x) sanity = %d!\n",pcrit->id,objs[pcrit->id].info.ph, + // osid,EDMS_sanity_check())); + // else + { + safe_EDMS_get_state(objs[pcrit->id].info.ph, &our_state); + ang_diff = + point_in_view_arc(plr_state.X, plr_state.Y, our_state.X, our_state.Y, + 0x4000 - fixang_from_phys_angle(phys_angle_from_obj(objs[id].loc.h)), &real_ang); + use_vis -= BEHIND_VISIBLE_FACTOR * (ang_diff / 0x8000); + } + + // Now factor in our own perception skill. + use_vis += CritterProps[CPNUM(id)].perception - MEDIAN_PERCEPTION; + + if (use_vis > 0) { + + r = rand() % 255; + if (r < use_vis) { + if (ray_cast_objects(id, PLAYER_OBJ, VISIBLE_MASS, VISIBLE_SIZE, VISIBLE_SPEED, VISIBLE_RANGE) == + PLAYER_OBJ) + ai_spot_player(id, raycast_success); + } + } + } + return (OK); +} + +void set_des_heading(ObjID id, ObjSpecID osid, fix targ_x, fix targ_y, fixang *angdiff, fixang *target_ang) { + State current_state; + +#ifdef AI_EDMS + safe_EDMS_get_state(objs[id].info.ph, ¤t_state); + *angdiff = point_in_view_arc(targ_x, targ_y, current_state.X, current_state.Y, + 0x4000 - fixang_from_phys_angle(phys_angle_from_obj(objs[objCritters[osid].id].loc.h)), + target_ang); + objCritters[osid].des_heading = fixang_to_fixrad(*target_ang); +#else + objCritters[osid].des_heading = 0; +#endif +} + +// Continue along the pathfinding path, grabbing new steps as +// necessary when reaching old steps. +#define MAX_PATH_TRIES 25 + +char dir_table[3][3] = { + { 2, 2, 2 }, + { 3, 0, 1 }, + { 0, 0, 0 }, +}; + +errtype follow_pathfinding(ObjID id, ObjSpecID osid) { + LGPoint sq, csq; + char steps_left, path_id, newdir; + ObjID open_me = OBJ_NULL; + + path_id = objCritters[osid].path_id; + + // If our pathfind request ain't been filled yet, don't do anything + if (paths[path_id].num_steps == -1) { + return (OK); + } + + // See whether or not we've gone further ahead on our path than + // we expected to. Hmm, if this is too slow we could probably keep track + // of our last location and only do this operation if that's changed. + sq.x = OBJ_LOC_BIN_X(objs[id].loc); + sq.y = OBJ_LOC_BIN_Y(objs[id].loc); + if (check_path_cutting(sq, objCritters[osid].path_id)) { + objCritters[osid].pf_x = sq.x; + objCritters[osid].pf_y = sq.y; + } + + if (paths[path_id].num_steps == 0) { + // If our path has been deleted from out from underneath us, stop + // trying to follow it... + delete_path(objCritters[osid].path_id); + objCritters[osid].path_id = -1; + return (OK); + } + + if ((objCritters[osid].pf_x == -1) || (paths[path_id].curr_step == -1) || + ((OBJ_LOC_BIN_X(objs[id].loc) == objCritters[osid].pf_x) && + (OBJ_LOC_BIN_Y(objs[id].loc) == objCritters[osid].pf_y))) { + // We're where we want to be, so lets get the next step! + objCritters[osid].path_tries = 0; + newdir = next_step_on_path(path_id, &sq, &steps_left); + if (steps_left == -1) { + objCritters[osid].path_id = -1; + } else { + // Plug the next step into our local state + objCritters[osid].pf_x = sq.x; + objCritters[osid].pf_y = sq.y; + } + } else { + // Keep on truckin' towards our old location + // char ft1[50],ft2[50],ft3[50]; + fixang angdiff, target_ang; + + if (objCritters[osid].path_tries++ > MAX_PATH_TRIES) { + // Okay, clearly something has rendered our path invalid, + // since we ain't having no success in getting there. Let's punt. + // We could make this keep trying and resubmit a PF request. Should we? + delete_path(objCritters[osid].path_id); + objCritters[osid].path_id = -1; + } + // Is there a door in the way? + // goddamn, this is a stupid way of doing things....argh! + if (!(((ObjProps[OPNUM(id)].flags & CLASS_FLAGS) >> CLASS_FLAGS_SHF) & CRITTER_NODOOR_OBJPROP_FLAG)) { + sq.x = objCritters[osid].pf_x; + sq.y = objCritters[osid].pf_y; + csq.x = OBJ_LOC_BIN_X(objs[id].loc); + csq.y = OBJ_LOC_BIN_Y(objs[id].loc); + if ((abs(csq.x - sq.x) < 2) && (abs(csq.y - sq.y) < 2)) + pf_obj_doors(MAP_GET_XY(csq.x, csq.y), MAP_GET_XY(sq.x, sq.y), + dir_table[sq.y - csq.y + 1][sq.x - csq.x + 1], &open_me); + // Warning(("open_me = %x, dt = %d from (%x,%x) to (%x,%x)!\n",open_me,dir_table[sq.y - csq.y + 1][sq.x + // - csq.x + 1], + // csq.x,csq.y,sq.x,sq.y)); + if ((open_me != OBJ_NULL) && (DOOR_CLOSED(open_me)) && !(door_moving(open_me, FALSE))) { + uchar use_door(ObjID id, uchar in_inv, ObjID cursor_obj); + use_door(open_me, 0x2, OBJ_NULL); + } + } + set_des_heading(id, osid, fix_make(objCritters[osid].pf_x, 0x8000), fix_make(objCritters[osid].pf_y, 0x8000), + &angdiff, &target_ang); + +#ifdef WACKY_SPEED_REDUCTION + if (paths[path_id].num_steps < 2) { + objCritters[osid].des_speed = DEFAULT_SPEED >> 3; + Warning(("speed slowing due to distance!\n")); + } else +#endif + if (objCritters[osid].des_speed == 0) + objCritters[osid].des_speed = DEFAULT_SPEED; + +#ifdef SPEED_QUARTERING + // Quarter speed if we are mostly turning and are going fast + if ((angdiff > 0x2000) && (objCritters[osid].des_speed > MAX_TURNING_SPEED)) { + objCritters[osid].des_speed = objCritters[osid].des_speed >> 2; + } +#endif + } + return (OK); +} + +char ai_ranges; + +// Are we legal to attack right now? If so, slam us into ATTACKING, +// otherwise slam us into hostile. Make appropriate adjustments so that +// we are actively looking for the player in either case. + +// Hey Rocky, watch me pull this constant out of my butt! +#define SHORT_RANGE_Z 0xA0 +void check_attitude_adjustment(ObjID id, ObjSpecID osid, int big_dist, uchar raycast_success) { + char i; + short dist = big_dist >> 8; + int cp_num = CPNUM(id); + uchar care_mask = 0; + + ai_ranges = 0; + + // Frankly, if we have no clue where the player is then + // don't bother trying to find him or anything.... in fact + // we go back to being NEUTRAL, I think. Although we will + // continue on our current pathfinding in hopes of reacquiring + // the player + if (last_known_loc.x == 255) { + objCritters[osid].mood = AI_MOOD_NEUTRAL; + objCritters[osid].flags &= ~AI_FLAG_CHASING; + set_posture(osid, STANDING_CRITTER_POSTURE); + return; + } + + // Check ranges + for (i = 0; i < 2; i++) { + short rng; + rng = CritterProps[cp_num].attacks[i].att_range; + if (dist <= rng) { + // If we are a "short range" attack, then check z before trying + if (rng <= 2) { + if ((abs(objs[id].loc.z - objs[PLAYER_OBJ].loc.z) << SLOPE_SHIFT_U) < SHORT_RANGE_Z) + ai_ranges |= 1 << i; + } else + ai_ranges |= 1 << i; + } + } + + // If we have no chance of doing a given attack, then don't worry about it's range + care_mask = 0; + if (CritterProps[cp_num].alt_perc == 0) + care_mask = 0x1; + else if (CritterProps[cp_num].alt_perc == 0xFF) + care_mask = 0x2; + else + care_mask = 0x3; + + if ((care_mask & ai_ranges) != care_mask) { + // If we aren't in range of both weapons, get closer + objs[id].info.inst_flags |= CLASS_INST_FLAG2; + } + if (ai_ranges & care_mask) { + fixang angdiff, target_ang; + // If we're in range of all wpns, stop trying to get closer + // but do keep trying to face the player. Note that normally the + // "face the player" part is dealt with by the pathfinder, hopefully, + // and so will blast out our des_heading set here. We have to do the work + // anyways here in order to figure out wheher or not the critter is facing + // the player + set_des_heading(id, osid, fix_from_obj_coord(last_known_loc.x), fix_from_obj_coord(last_known_loc.y), &angdiff, + &target_ang); + if (angdiff < 0x2000) { +#ifdef AI_EDMS + if (raycast_success || (ray_cast_objects(id, PLAYER_OBJ, VISIBLE_MASS, VISIBLE_SIZE, VISIBLE_SPEED, + VISIBLE_RANGE) == PLAYER_OBJ)) { + raycast_success = TRUE; + objCritters[osid].mood = AI_MOOD_ATTACKING; +#ifdef ANNOYING_COMBAT_SPEW + Spew(DSRC_AI_Combat, ("id %x Spotted player, attacking!\n")); +#endif + } else +#endif + { + objCritters[osid].mood = AI_MOOD_HOSTILE; + set_posture_movesafe(osid, STANDING_CRITTER_POSTURE); +#ifdef ANNOYING_COMBAT_SPEW + Spew(DSRC_AI_Combat, ("id %x failed raycast!\n")); +#endif + } + } else { + objCritters[osid].mood = AI_MOOD_HOSTILE; + set_posture_movesafe(osid, STANDING_CRITTER_POSTURE); +#ifdef ANNOYING_COMBAT_SPEW + Spew(DSRC_AI_Combat, ("id %x failed angcheck, angdiff = %x\n", angdiff)); +#endif + } + } +} + +void load_combat_art(int cp_num) { + extern Id posture_bases[]; + char p; + if (ResPtr(posture_bases[ATTACKING_CRITTER_POSTURE] + cp_num) == NULL) { + // Suspend time during loading of combat art + ulong old_ticks = *tmd_ticks; + extern ulong last_real_time; + for (p = ATTACKING_CRITTER_POSTURE; p <= ATTACKING2_CRITTER_POSTURE; p++) { + if (p != KNOCKBACK_CRITTER_POSTURE) { + ResLock(posture_bases[p] + cp_num); + ResUnlock(posture_bases[p] + cp_num); + } + } + last_real_time += *tmd_ticks - old_ticks; + } +} + +// copied in ai.c +#define DEFAULT_URGENCY fix_make(0x30, 0) + +// Run the AI for a combat-worthy critter, either looking actively for +// a nearby player, or actually shooting at such +// This really needs to have gnosis of: +// -- beelining for player when close enough & appropriate +// -- sidestepping intelligently, using cover and such (we sidestep very stupidly now) +errtype run_combat_ai(ObjID id, uchar raycast_success) { + ObjSpecID osid = objs[id].specID; + ObjCritter *pcrit = &objCritters[osid]; + LGPoint dest, source; + int cp_num; + + // Sidestep stupidly + // pcrit->sidestep = fix_make(rand()%200 - 100, 0); + + cp_num = CPNUM(id); + if (pcrit->mood == AI_MOOD_ATTACKING) { + // Don't bother if we're already trying to attack + if ((get_crit_posture(osid) != ATTACKING_CRITTER_POSTURE) && + (get_crit_posture(osid) != ATTACKING2_CRITTER_POSTURE)) { + if (ai_ranges) { + if (pcrit->attack_count < player_struct.game_time) { + char posture; + extern uchar music_on; + + load_combat_art(cp_num); + if (!(ai_ranges & 0x1)) + posture = ATTACKING2_CRITTER_POSTURE; + else if (!(ai_ranges & 0x2)) + posture = ATTACKING_CRITTER_POSTURE; + else + posture = (rand() % 255 < CritterProps[cp_num].alt_perc) ? ATTACKING2_CRITTER_POSTURE + : ATTACKING_CRITTER_POSTURE; + set_posture(osid, posture); + if (music_on) + mai_attack(); + // Set how long before we get to attack again! + pcrit->attack_count = (posture == ATTACKING_CRITTER_POSTURE) + ? player_struct.game_time + CritterProps[cp_num].attacks[0].speed + : player_struct.game_time + CritterProps[cp_num].attacks[1].speed; + } + } else + // If we are out of range, stop being all attack-like in one's posturing + set_posture(osid, STANDING_CRITTER_POSTURE); + } + } + + if (raycast_success) { + // jeepers! that's the player! + fixang diffang, targang; + + if (pcrit->orders == AI_ORDERS_NOMOVE) + pcrit->sidestep = 0; + else + pcrit->sidestep = fix_make(rand() % 400 - 200, 0); + + delete_path(pcrit->path_id); + pcrit->path_id = -1; + set_des_heading(id, osid, fix_make(OBJ_LOC_BIN_X(objs[PLAYER_OBJ].loc), 0x8000), + fix_make(OBJ_LOC_BIN_Y(objs[PLAYER_OBJ].loc), 0x8000), &diffang, &targang); + + // If we're out of range or keep missing, run hell-bent towards the player + // otherwise take careful potshots + if ((ai_ranges) && (!(objs[id].info.inst_flags & CLASS_INST_FLAG2)) && (QUESTVAR_GET(COMBAT_DIFF_QVAR) < 3) && + (ID2TRIP(id) != AUTOBOMB_TRIPLE)) { + objCritters[osid].des_speed = DEFAULT_SPEED >> 3; + objCritters[osid].urgency = DEFAULT_URGENCY >> 1; + } else { + objCritters[osid].des_speed = DEFAULT_SPEED; + objCritters[osid].urgency = DEFAULT_URGENCY << 1; + } + } else { + objCritters[osid].sidestep = 0; + objCritters[osid].des_speed = DEFAULT_SPEED; + if (pcrit->path_id != -1) { + follow_pathfinding(id, osid); + } else { + // Do our damnedest to get closer to the player + // For now this is a pathfind, it should gain knowledge of beelining soon + if ((objs[id].info.inst_flags & CLASS_INST_FLAG2) || (pcrit->mood == AI_MOOD_HOSTILE)) { + source.x = OBJ_LOC_BIN_X(objs[id].loc); + source.y = OBJ_LOC_BIN_Y(objs[id].loc); + dest.x = OBJ_LOC_BIN_X(objs[PLAYER_OBJ].loc); + dest.y = OBJ_LOC_BIN_Y(objs[PLAYER_OBJ].loc); + pcrit->path_id = request_pathfind(source, dest, objs[PLAYER_OBJ].loc.z, objs[id].loc.z, TRUE); + // mprintf("combat id %x pathfind request = 0x%x!\n",id,pcrit->path_id); + + // if the path we want don't exist, stochastically go back to being neutral + check_requests(TRUE); + if ((paths[pcrit->path_id].num_steps == 0) && ((rand() & 0xFF) < 0x30)) { + objCritters[osid].mood = AI_MOOD_NEUTRAL; + objCritters[osid].flags &= ~AI_FLAG_CHASING; + set_posture(osid, STANDING_CRITTER_POSTURE); + } + } + } + } + return (OK); +} + +LGPoint ai_patrol_func(ObjID oid, ObjSpecID osid) { + char temp_x, temp_y; + ObjCritter *pcrit = &objCritters[osid]; + LGPoint dest; + + // Swap destination + temp_x = pcrit->dest_x; + temp_y = pcrit->dest_y; + dest.x = pcrit->dest_x = pcrit->x1; + dest.y = pcrit->dest_y = pcrit->y1; + pcrit->x1 = temp_x; + pcrit->y1 = temp_y; + return (dest); +} + +LGPoint ai_highway_func(ObjID oid, ObjSpecID osid) { + LGPoint dest = {-1, -1}; + ObjID curr_id; + int param, interface_param; + + curr_id = objCritters[osid].loot2; + if (objs[curr_id].obclass != CLASS_TRAP) { + return (dest); + } + switch (objCritters[osid].x1) { + case 0: + param = objTraps[objs[curr_id].specID].p1; + break; + case 1: + param = objTraps[objs[curr_id].specID].p2; + break; + case 2: + param = objTraps[objs[curr_id].specID].p3; + break; + } + + // Secret usage in highway functions + if ((objTraps[objs[curr_id].specID].p4 & 0xFFFF) != 0) { + interface_param = objTraps[objs[curr_id].specID].p4 >> 16; + switch (objTraps[objs[curr_id].specID].p4 & 0xFFFF) { + case 1: + do_multi_stuff(interface_param); + break; + } + } + if ((param != OBJ_NULL) && (objs[param].active) && (objs[param].obclass == CLASS_TRAP)) { + dest.x = OBJ_LOC_BIN_X(objs[param].loc); + dest.y = OBJ_LOC_BIN_Y(objs[param].loc); + objCritters[osid].dest_x = dest.x; + objCritters[osid].dest_y = dest.y; + objCritters[osid].loot2 = param; + } + return (dest); +} + +#define DEFAULT_BROWNIAN_DIST 5 +#define FIND_ROAM_TRIES 25 + +LGPoint ai_roam_func(ObjID id, ObjSpecID osid) { + LGPoint dest, source; + char tries = 0; + uchar okay; + short bd; + + source.x = OBJ_LOC_BIN_X(objs[id].loc); + source.y = OBJ_LOC_BIN_Y(objs[id].loc); + + // reset number of tries + objCritters[osid].x1 = 0; + + bd = objCritters[osid].y1 ? objCritters[osid].y1 : DEFAULT_BROWNIAN_DIST; + okay = FALSE; + while (!okay && (tries < FIND_ROAM_TRIES)) { + dest.x = source.x + rand() % bd - (bd / 2); + dest.y = source.y + rand() % bd - (bd / 2); + if (me_tiletype(MAP_GET_XY(dest.x, dest.y)) == TILE_OPEN) + okay = TRUE; + tries++; + } + return (dest); +} + +LGPoint ai_none_func(ObjID oid, ObjSpecID osid) { + LGPoint goof = {-1, -1}; + objCritters[osid].urgency = 0; + objCritters[osid].sidestep = 0; + objCritters[osid].des_speed = 0; + return (goof); +} + +// Run the AI for a non-combat critter, carrying out SHODANs will in +// some other way +LGPoint (*ai_order_funcs[])(ObjSpecID, ObjID) = {ai_none_func, ai_roam_func, ai_none_func, + ai_patrol_func, ai_highway_func, ai_none_func}; + +#define MAX_ROAM_PATH 32 +#define MAX_ROAM_TRIES 126 + +#define TRANQ_RAND_MASK 0xFFF +#define TRANQ_RAND_LEVEL 5 +#define CONFUSE_RAND_MASK 0xFFF +#define CONFUSE_RAND_LEVEL 5 + +errtype run_peaceful_ai(ObjID id, int big_dist) { + ObjSpecID osid = objs[id].specID; + LGPoint source, dest; + uchar do_path = TRUE; + + objCritters[osid].sidestep = 0; + if (objCritters[osid].path_id != -1) { + // Follow our pathfinding... + + // If we have too far to go, and we aren't very specific about + // what it is we are doing, try again + if ((objCritters[osid].orders == AI_ORDERS_ROAM) || (objCritters[osid].flags & AI_FLAG_CONFUSED)) { + objCritters[osid].x1++; + + // if confused we are more impatient to find new locations + if (objCritters[osid].flags & AI_FLAG_CONFUSED) + objCritters[osid].x1 += 3; + + // stop this particular wandering if the path we just chose is too long or if we've been at it for too long. + if (((objCritters[osid].x1 == 0) && (path_length(objCritters[osid].path_id) > MAX_ROAM_PATH)) || + (objCritters[osid].x1 > MAX_ROAM_TRIES)) { + // Spew(DSRC_AI_Pathfind, ("punting roam path!\n")); + delete_path(objCritters[osid].path_id); + objCritters[osid].path_id = -1; + do_path = FALSE; + } + } + if (do_path) + follow_pathfinding(id, osid); + } else { + // Hmm, we should make a new pathfinding request + // But punt out if we are too far away from the player + if (do_physics_stupidity(id, big_dist)) { + source.x = OBJ_LOC_BIN_X(objs[id].loc); + source.y = OBJ_LOC_BIN_Y(objs[id].loc); + if (ai_order_funcs[objCritters[osid].orders] != NULL) { + dest = ai_order_funcs[objCritters[osid].orders](id, osid); + if (dest.x != -1) { + objCritters[osid].path_id = request_pathfind(source, dest, 0, objs[id].loc.z, FALSE); + // mprintf("peaceful id %x pathfind request = 0x%x!\n",id,objCritters[osid].path_id); + // Spew(DSRC_AI_Path, ("peace(%x): id %x getting new path (%d), from %x,%x to + // %x,%x\n", + // big_dist,id,objCritters[osid].path_id, + // PT_UNWRAP(source),PT_UNWRAP(dest))); + } + } + } + // else + // Spew(DSRC_AI_Combat, ("combat skipping id %x due to distance %x\n",id,big_dist)); + } + if ((objCritters[osid].flags & AI_FLAG_CONFUSED) && ((rand() & CONFUSE_RAND_MASK) < CONFUSE_RAND_LEVEL)) { + set_crit_posture(osid, MOVING_CRITTER_POSTURE); + objCritters[osid].flags &= ~AI_FLAG_CONFUSED; + } + return (OK); +} + +#define COMBAT_FRAMES 2 +#define DEFAULT_FRAMES 13 + +errtype ai_run() { + ObjSpecID osid; + ObjID id; + short visibility; + uchar raycast_success; + int dist; + char mood; + extern ObjID shodan_avatar_id; +#ifdef PLAYTEST + short crit_count = 0; +#endif + +#ifndef GAMEONLY + // Punt out if no physics or no ai + if (!physics_running) + return (OK); +#endif + + check_requests(FALSE); + + // Check ICE agitation + if ((global_fullmap->cyber) && (run_ice_time < player_struct.game_time)) + run_cspace_ice(); + + visibility = compute_base_visibility(); + + // Cycle through all the critters + priority_check = FALSE; + osid = objCritters[0].id; + while (osid != OBJ_SPEC_NULL) { + if ((!CHECK_OBJ_PH(objCritters[osid].id)) && (objCritters[osid].id != shodan_avatar_id)) { + goto ai_loop_end; + } + if ((objCritters[osid].orders == AI_ORDERS_SLEEP) || (get_crit_posture(osid) == DEATH_CRITTER_POSTURE)) + goto ai_loop_end; + else if (objCritters[osid].flags & AI_FLAG_TRANQ) { + if ((rand() & TRANQ_RAND_MASK) < TRANQ_RAND_LEVEL) { + objCritters[osid].flags &= ~AI_FLAG_TRANQ; + if (CritterProps[CPNUM(objCritters[osid].id)].flags & AI_FLAG_FLYING) + apply_gravity_to_one_object(objCritters[osid].id, 0); + } + goto ai_loop_end; + } + + // Do some book-keeping + id = objCritters[osid].id; + if (id == PLAYER_OBJ) + goto ai_loop_end; + raycast_success = FALSE; + + dist = long_fast_pyth_dist(objs[id].loc.x - objs[PLAYER_OBJ].loc.x, objs[id].loc.y - objs[PLAYER_OBJ].loc.y); + if (global_fullmap->cyber && (id == shodan_avatar_id)) { + run_evil_otto(id, dist); + goto ai_loop_end; + } + if (!do_physics_stupidity(id, dist)) { + goto ai_loop_end; + } + + objCritters[osid].wait_frames--; + + // Tell EDMS what to do with us. +#ifdef AI_EDMS + apply_EDMS_controls(osid); +#endif + + // If it is our turn to get a bigger share of the + // computron pie, then let's crank. + if (objCritters[osid].wait_frames <= 0) { + do_stealth_stuff(id, visibility, &raycast_success, dist); + mood = objCritters[osid].mood; + if (((mood == AI_MOOD_HOSTILE) || (mood == AI_MOOD_ATTACKING)) && + !(objCritters[osid].flags & AI_FLAG_CONFUSED)) { + check_attitude_adjustment(id, osid, dist, raycast_success); + run_combat_ai(id, raycast_success); + objCritters[osid].wait_frames = COMBAT_FRAMES; + } else { + // if (objCritters[osid].flags & AI_FLAG_CONFUSED) + // Spew(DSRC_AI_Hacks, ("critter %x, is confused! flags = + // %x\n",id,objCritters[osid].flags)); + run_peaceful_ai(id, dist); +#ifdef USE_DIST_OVERRIDE_FOR_DEFAULT_FRAMES + objCritters[osid].wait_frames = min(DEFAULT_FRAMES, dist >> 8); +// if ((dist >> 8) < DEFAULT_FRAMES) +// Spew(DSRC_AI_AI, ("using %d frames for id %x instead of %d\n",dist >> 8, id, DEFAULT_FRAMES)); +#else + objCritters[osid].wait_frames = DEFAULT_FRAMES; +#endif + } + // a bit o' random deviation... + objCritters[osid].wait_frames += (*tmd_ticks & 0x2); + } + + ai_loop_end: + osid = objCritters[osid].next; + } + if (priority_check) + check_requests(TRUE); + return (OK); +} diff --git a/engine/src/GameSrc/newmfd.c b/engine/src/GameSrc/newmfd.c new file mode 100644 index 0000000..ba49015 --- /dev/null +++ b/engine/src/GameSrc/newmfd.c @@ -0,0 +1,1650 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +// NEWMFD.C + +/* + * $Source: r:/prj/cit/src/RCS/newmfd.c $ + * $Revision: 1.153 $ + * $Author: xemu $ + * $Date: 1994/11/21 22:03:39 $ + * + */ + +// Source code for controlling the multi-function displays (MFDs) +// All MFD infrastructure belongs here, all expose/handler callbacks +// belong in mfdfunc.c + +#include +#include + +#include "game_screen.h" // for the root region +#include "fullscrn.h" +#include "invent.h" +#include "mfdint.h" +#include "mfdext.h" +#include "mfdfunc.h" +#include "mfddims.h" +#include "input.h" +#include "player.h" +#include "tools.h" +#include "mainloop.h" +#include "gameloop.h" +#include "gamescr.h" +#include "musicai.h" // for digital FX +#include "sfxlist.h" // same +#include "citres.h" +#include "weapons.h" +#include "cit2d.h" +#include "popups.h" +#include "statics.h" +#include "gr2ss.h" + +#include "cybstrng.h" + +// ----------------------- +// Player_Struct Accessors +// ----------------------- + +#define mfd_empty_func(mfd_num) player_struct.mfd_empty_funcs[(mfd_num)] +#define mfd_index(mfd_num) player_struct.mfd_current_slots[(mfd_num)] +#define set_mfd_to_slot(mfd_id, vs, as) player_struct.mfd_virtual_slots[(mfd_id)][(vs)] = (as) +#define set_default_to_func(mfd_num, fnum) player_struct.mfd_empty_funcs[(mfd_num)] = (fnum) +#define mfd_get_active_func(mfd_id) mfd_get_func((mfd_id), (mfd_index((mfd_id)))) + +// ------- +// Globals +// ------- + +#define MFD_NUM_BTTNS MFD_NUM_VIRTUAL_SLOTS + +MFD mfd[2]; // Our actual MFD's +uchar Flash = TRUE; // State of blinking buttons +LGCursor mfd_bttn_cursors[NUM_MFDS]; +grs_bitmap mfd_bttn_bitmaps[NUM_MFDS]; + +grs_canvas _offscreen_mfd, _fullscreen_mfd; + +#define mfdL mfd[MFD_LEFT] +#define mfdR mfd[MFD_RIGHT] + +grs_bitmap mfd_background; +grs_canvas *pmfd_canvas; + +// ----------- +// Prototypes +// ----------- +void mfd_set_slot(ubyte mfd_id, ubyte newSlot, uchar OnOff); +void mfd_draw_all_buttons(ubyte mfd_id); +errtype mfd_clear_all(); + +void mfd_clear_func(ubyte func_id); + +uchar mfd_object_cursor_handler(uiEvent *ev, LGRegion *, int which_mfd); + +void mfd_draw_button(ubyte mfd_id, ubyte b); +void mfd_select_button(int which_panel, int which_button); + +void mfd_default_mru(uchar func); +void set_mfd_from_defaults(int mfd_id, uchar func, uchar slot); + +// KLC dbg_mfd_state used to be here. + +// ------------------ +// INITIALIZERS +// ------------------ + +// --------------------------------------------------------------------------- +// init_newmfd() +// +// Initialize the MFD system (called from init_all() in init.c) + +void init_newmfd() { + ubyte i; + + // Set the default MFD function + set_default_to_func(MFD_LEFT, MFD_EMPTY_FUNC); + set_default_to_func(MFD_RIGHT, MFD_EMPTY_FUNC); + + // Now set actual MFD slots to point at virtual slots + for (i = 0; i < NUM_MFDS; i++) + mfd[i].id = i; + + player_struct.mfd_current_slots[MFD_LEFT] = MFD_WEAPON_SLOT; + player_struct.mfd_current_slots[MFD_RIGHT] = MFD_ITEM_SLOT; + + for (i = 0; i < MFD_NUM_VIRTUAL_SLOTS; i++) { + set_mfd_to_slot(MFD_LEFT, i, i); + set_mfd_to_slot(MFD_RIGHT, i, i); + } + + for (i = 0; i < MFD_NUM_FUNCS; i++) + if (mfd_funcs[i].flags & MFD_INCREMENTAL) + player_struct.mfd_func_status[i] |= 1 << 4; + + chg_set_flg(MFD_UPDATE); + + return; +} + +// --------------------------------------------------------------------------- +// init_newmfd_button_cursors() +// +// Initialize the twelve goofy cursors, each of which hovers over an MFD button, +// as spec'd in last nights warren/artist/programmers meeting (SPAZ 8/5) + +static char *cursor_strings[MFD_NUM_BTTNS]; +static char cursor_strbuf[128]; + +void mfd_language_change(void) { + load_string_array(REF_STR_MFDCursor, cursor_strings, cursor_strbuf, sizeof(cursor_strbuf), MFD_NUM_BTTNS); +} + +void init_newmfd_button_cursors() { + int i; + mfd_language_change(); + for (i = 0; i < NUM_MFDS; i++) { + LGCursor *c = &mfd_bttn_cursors[i]; + grs_bitmap *bm = &mfd_bttn_bitmaps[i]; + LGPoint offset = {0, 0}; + make_popup_cursor(c, bm, cursor_strings[i], i, TRUE, offset); + } +} + +// --------------------------------------------------------------------------- +// screen_init_mfd_draw() +// +// Basically, just draw the friggin' buttons and set mfd's to their +// first slot. (called from screen_start() in screen.c) + +void screen_init_mfd_draw() { + mfd_set_slot(MFD_LEFT, mfd_index(MFD_LEFT), TRUE); + mfd_set_slot(MFD_RIGHT, mfd_index(MFD_RIGHT), TRUE); + + mfd_draw_all_buttons(MFD_LEFT); + mfd_draw_all_buttons(MFD_RIGHT); + + return; +} + +#ifdef SVGA_SUPPORT +#define MAX_WD(x) (fix_int(fix_mul_div(fix_make((x), 0), fix_make(1024, 0), fix_make(320, 0)))) +#define MAX_HT(y) (fix_int(fix_mul_div(fix_make((y), 0), fix_make(768, 0), fix_make(200, 0)))) +#endif + +// --------------------------------------------------------------------------- +// screen_init_mfd(); +// +// Declare the appropriate regions for the MFD's and their button panels. +// (called from screen_start() in screen.c) + +void screen_init_mfd(uchar fullscrn) { + static uchar done_init = FALSE; + FrameDesc *f; + int id; + int lval, rval; + + lval = MFD_LEFT; // Screen callbacks need to know their + rval = MFD_RIGHT; // left from their right, thusly + + // Set up the Rect structures for MFD screen-space + + // Left View Window + mfdL.rect.ul.x = MFD_VIEW_LFTX; + mfdL.rect.ul.y = MFD_VIEW_Y; + mfdL.rect.lr.x = MFD_VIEW_LFTX + MFD_VIEW_WID; + mfdL.rect.lr.y = MFD_VIEW_Y + MFD_VIEW_HGT; + + // Right View Window + mfdR.rect.ul.x = MFD_VIEW_RGTX; + mfdR.rect.ul.y = MFD_VIEW_Y; + mfdR.rect.lr.x = MFD_VIEW_RGTX + MFD_VIEW_WID; + mfdR.rect.lr.y = MFD_VIEW_Y + MFD_VIEW_HGT; + + // Left Button Panel + mfdL.bttn.rect.ul.x = MFD_BTTN_LFTX; + mfdL.bttn.rect.ul.y = MFD_BTTN_Y; + mfdL.bttn.rect.lr.x = MFD_BTTN_LFTX + MFD_BTTN_WID; + mfdL.bttn.rect.lr.y = MFD_BTTN_Y + MFD_BTTN_HGT; + + // Right Button Panel + mfdR.bttn.rect.ul.x = MFD_BTTN_RGTX; + mfdR.bttn.rect.ul.y = MFD_BTTN_Y; + mfdR.bttn.rect.lr.x = MFD_BTTN_RGTX + MFD_BTTN_WID; + mfdR.bttn.rect.lr.y = MFD_BTTN_Y + MFD_BTTN_HGT; + + // Now, actually create the four regions, and add handlers + if (!fullscrn) { + macro_region_create(root_region, &(mfdL.reg), &(mfdL.rect)); + macro_region_create(root_region, &(mfdR.reg), &(mfdR.rect)); + macro_region_create(root_region, &(mfdL.bttn.reg), &(mfdL.bttn.rect)); + macro_region_create(root_region, &(mfdR.bttn.reg), &(mfdR.bttn.rect)); + + uiInstallRegionHandler(&(mfdL.reg), (UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE), mfd_view_callback, MFD_LEFT, + &id); + uiInstallRegionHandler(&(mfdR.reg), (UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE), mfd_view_callback, + MFD_RIGHT, &id); + + uiInstallRegionHandler(&(mfdL.bttn.reg), (UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE), mfd_button_callback, + MFD_LEFT, &id); + uiInstallRegionHandler(&(mfdR.bttn.reg), (UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE), mfd_button_callback, + MFD_RIGHT, &id); + } else { + uiCursorStack *cs; + macro_region_create(fullview_region, &(mfdL.reg2), &(mfdL.rect)); + uiGetRegionCursorStack(&(mfdL.reg), &cs); + uiSetRegionCursorStack(&(mfdL.reg2), cs); + macro_region_create(fullview_region, &(mfdR.reg2), &(mfdR.rect)); + uiGetRegionCursorStack(&(mfdR.reg), &cs); + uiSetRegionCursorStack(&(mfdR.reg2), cs); + region_create(fullview_region, &(mfdL.bttn.reg2), &(mfdL.bttn.rect), 2, 0, REG_USER_CONTROLLED, NULL, NULL, + NULL, NULL); + uiGetRegionCursorStack(&(mfdL.bttn.reg), &cs); + uiSetRegionCursorStack(&(mfdL.bttn.reg2), cs); + region_create(fullview_region, &(mfdR.bttn.reg2), &(mfdR.bttn.rect), 2, 0, REG_USER_CONTROLLED, NULL, NULL, + NULL, NULL); + uiGetRegionCursorStack(&(mfdR.bttn.reg), &cs); + uiSetRegionCursorStack(&(mfdR.bttn.reg2), cs); + + uiInstallRegionHandler(&(mfdL.reg2), (UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE), mfd_view_callback_full, + MFD_LEFT, &id); + uiInstallRegionHandler(&(mfdR.reg2), (UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE), mfd_view_callback_full, + MFD_RIGHT, &id); + + uiInstallRegionHandler(&(mfdL.bttn.reg2), (UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE), mfd_button_callback, + MFD_LEFT, &id); + uiInstallRegionHandler(&(mfdR.bttn.reg2), (UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE), mfd_button_callback, + MFD_RIGHT, &id); + } + + if (!done_init) { + done_init = TRUE; + + // CC: These bytes get made on the fly now + mfd_canvas_bits = (uchar *)malloc(MAX_WD(MFD_VIEW_WID) * MAX_HT(MFD_VIEW_HGT)); + + // Pull in the background bitmap + f = RefLock(REF_IMG_bmBlankMFD); + mfd_background = f->bm; + mfd_background.bits = (uchar *)malloc(MAX_WD(MFD_VIEW_WID) * MAX_HT(MFD_VIEW_HGT)); + + LG_memcpy(mfd_background.bits, (f + 1), f->bm.w * f->bm.h); + RefUnlock(REF_IMG_bmBlankMFD); + + gr_init_canvas(&_offscreen_mfd, mfd_canvas_bits, BMT_FLAT8, MFD_VIEW_WID, MFD_VIEW_HGT); + gr_init_canvas(&_fullscreen_mfd, mfd_background.bits, BMT_FLAT8, MFD_VIEW_WID, MFD_VIEW_HGT); + pmfd_canvas = &_offscreen_mfd; + init_newmfd_button_cursors(); + mfd_init_funcs(); + } + return; +} + +#ifdef SVGA_SUPPORT +errtype mfd_update_screen_mode() { + if (convert_use_mode == 0) { + gr_init_canvas(&_offscreen_mfd, mfd_canvas_bits, BMT_FLAT8, MFD_VIEW_WID, MFD_VIEW_HGT); + gr_init_canvas(&_fullscreen_mfd, mfd_background.bits, BMT_FLAT8, MFD_VIEW_WID, MFD_VIEW_HGT); + } else { + + int new_width = SCONV_X(MFD_VIEW_WID); + int new_height = SCONV_Y(MFD_VIEW_HGT); + + // CC: Resize the MFD bytes to fit the new mode + free(mfd_background.bits); + free(mfd_canvas_bits); + + mfd_background.bits = (uchar *)malloc(new_width * new_height); + mfd_canvas_bits = (uchar *)malloc(new_width * new_height); + + // Copy the background bytes + grs_bitmap *bm = lock_bitmap_from_ref(REF_IMG_bmBlankMFD); + LG_memcpy(mfd_background.bits, bm->bits, bm->w * bm->h); + RefUnlock(REF_IMG_bmBlankMFD); + + gr_init_canvas(&_offscreen_mfd, mfd_canvas_bits, BMT_FLAT8, new_width, new_height); + gr_init_canvas(&_fullscreen_mfd, mfd_background.bits, BMT_FLAT8, new_width, new_height); + } + return (OK); +} + +errtype mfd_clear_all() { + if (full_game_3d) { + gr_push_canvas(&_offscreen_mfd); + gr_clear(0); + gr_pop_canvas(); + gr_push_canvas(&_fullscreen_mfd); + gr_clear(0); + gr_pop_canvas(); + } + return (OK); +} +#endif + +// --------------------------------------------- +// mfd_change_fullscreen(uchar on); +// +// set up and clean up for fullscreen mode. + +void mfd_change_fullscreen(uchar on) { + if (on) { + gr_push_canvas(&_fullscreen_mfd); + gr_clear(0); + gr_pop_canvas(); + gr_push_canvas(&_offscreen_mfd); + gr_clear(0); + gr_pop_canvas(); + } else { + // we use the mfd background for the canvas, so + // put the background bitmap back + grs_bitmap *bm = lock_bitmap_from_ref(REF_IMG_bmBlankMFD); + RefUnlock(REF_IMG_bmBlankMFD); + LG_memcpy(_fullscreen_mfd.bm.bits, bm->bits, bm->w * bm->h); + } +} + +// --------------------------------------------------------------------------- +// keyboard_init_mfd() +// +// Tell the function keys that they're supposed to map to our button panels. +// (Called from init_input() in input.c) + +void keyboard_init_mfd() { + /* KLC leave out F-keys and char codes. + + hotkey_add(KEY_F1, DEMO_CONTEXT,mfd_button_callback_kb,0); + hotkey_add(KEY_F2, DEMO_CONTEXT,mfd_button_callback_kb,1); + hotkey_add(KEY_F3, DEMO_CONTEXT,mfd_button_callback_kb,2); + hotkey_add(KEY_F4, DEMO_CONTEXT,mfd_button_callback_kb,3); + hotkey_add(KEY_F5, DEMO_CONTEXT,mfd_button_callback_kb,4); + hotkey_add(KEY_F6, DEMO_CONTEXT,mfd_button_callback_kb,5); + hotkey_add(KEY_F7, DEMO_CONTEXT,mfd_button_callback_kb,6); + hotkey_add(KEY_F8, DEMO_CONTEXT,mfd_button_callback_kb,7); + hotkey_add(KEY_F9, DEMO_CONTEXT,mfd_button_callback_kb,8); + hotkey_add(KEY_F10,DEMO_CONTEXT,mfd_button_callback_kb,9); + */ + install_keypad_hotkeys(); +} + +// -------------- +// FROBBERS +// -------------- + +// --------------------------------------------------------------------------- +// set_slot_to_func() +// +// Sets a slot to point to a given function struct, and sets status too. + +void set_slot_to_func(ubyte snum, ubyte fnum, MFD_Status stat) { + + player_struct.mfd_all_slots[snum] = fnum; + + if ((player_struct.mfd_slot_status[snum] == MFD_FLASH) && (stat == MFD_ACTIVE)) + ; + else + player_struct.mfd_slot_status[snum] = stat; + + return; +} + +// --------------------------------------------------------------------------- +// mfd_clear_func() +// +// Set a functions last update to current game time, clear its CHANGEBIT +// field if set. + +void mfd_clear_func(ubyte func_id) { + mfd_funcs[func_id].last = player_struct.game_time; + + player_struct.mfd_func_status[func_id] &= ~MFD_CHANGEBIT; + player_struct.mfd_func_status[func_id] &= ~MFD_CHANGEBIT_FULL; + return; +} + +#define MFD_STEREO_HACK_MODE 6 + +// --------------------------------------------------------------------------- +// mfd_notify_func() +// +// Let a function know its been changed and needs re-exposure. +// Also check a specified slot to see if that function is there: if +// is not, we might grab it and put it there. We also set the slot's +// status as demanded. + +//#define MFD_STEREO_HACK_MODE ((i6d_device == I6D_CTM) ? 6 : 7) +void mfd_notify_func(ubyte fnum, ubyte snum, uchar Grab, MFD_Status stat, uchar Full) { + ubyte i, j; + int oldf = player_struct.mfd_all_slots[snum]; + byte mfd_but[NUM_MFDS]; + + if (fnum == NOTIFY_ANY_FUNC) + fnum = oldf; + + for (i = 0; i < NUM_MFDS; i++) + mfd_but[i] = -1; + + if ((oldf != fnum) && (Grab)) { + player_struct.mfd_all_slots[snum] = fnum; + Full = TRUE; + } + + player_struct.mfd_func_status[fnum] |= MFD_CHANGEBIT; + if (Full) { + void mfd_default_mru(uchar func); +#ifdef SVGA_SUPPORT + uchar old_over = gr2ss_override; + short temp; + gr2ss_override = OVERRIDE_ALL; + ss_set_hack_mode(MFD_STEREO_HACK_MODE, &temp); +#endif + + for (i = 0; i < NUM_MFDS; i++) { + if (oldf != fnum && player_struct.mfd_current_slots[i] == snum) { + mfd_funcs[oldf].expose(&(mfd[i]), 0); + } + } +#ifdef SVGA_SUPPORT + ss_set_hack_mode(0, &temp); + gr2ss_override = old_over; +#endif + player_struct.mfd_func_status[fnum] |= MFD_CHANGEBIT_FULL; + mfd_default_mru(fnum); + } + + if (player_struct.mfd_all_slots[snum] == fnum) { + set_slot_to_func(snum, fnum, stat); + + // Find which buttons we have to redraw because of the change + for (i = 0; i < NUM_MFDS; i++) { + for (j = 0; j < MFD_NUM_VIRTUAL_SLOTS; j++) { + if (player_struct.mfd_virtual_slots[i][j] == snum) { + mfd_but[i] = j; + if (player_struct.mfd_current_slots[i] == j) { + player_struct.mfd_slot_status[snum] = stat; + if (stat == MFD_FLASH) + player_struct.mfd_slot_status[snum] = MFD_ACTIVE; + } + } + } + } + + // Now redraw them + if (_current_loop <= FULLSCREEN_LOOP && !global_fullmap->cyber) + for (i = 0; i < NUM_MFDS; i++) + if (mfd_but[i] != -1) + mfd_draw_button(i, mfd_but[i]); + } + + chg_set_flg(MFD_UPDATE); + +} + +// --------------------------------------------------------------------------- +// mfd_get_func() +// +// Returns an MFD's slot's function. + +ubyte mfd_get_func(ubyte mfd_id, ubyte s) { + ubyte slot_num; + + slot_num = player_struct.mfd_virtual_slots[mfd_id][s]; + + if (player_struct.mfd_slot_status[slot_num] == MFD_EMPTY) + return player_struct.mfd_empty_funcs[mfd_id]; + else + return player_struct.mfd_all_slots[slot_num]; +} + +// --------------------------------------------------------------------------- +// mfd_set_slot() +// +// Sets the mfd to a given slot without caring about turning off what was +// previously there, or any change-related state. Permits usage from both +// initializer and slot-changer. + +void mfd_set_slot(ubyte mfd_id, ubyte newSlot, uchar OnOff) { + MFD_Func *f; + ubyte old_index; + ubyte f_id; + ubyte new_slot; + ubyte old_slot; + + old_index = player_struct.mfd_current_slots[mfd_id]; + old_slot = player_struct.mfd_virtual_slots[mfd_id][old_index]; + new_slot = player_struct.mfd_virtual_slots[mfd_id][newSlot]; + + if (!OnOff && ((player_struct.mfd_slot_status[old_slot] != MFD_EMPTY) || + (player_struct.mfd_slot_status[new_slot] != MFD_EMPTY))) { + uchar old_over = gr2ss_override; + short temp; + gr2ss_override = OVERRIDE_ALL; + ss_set_hack_mode(MFD_STEREO_HACK_MODE, &temp); + f_id = mfd_get_func(mfd_id, newSlot); + f = &(mfd_funcs[f_id]); + f->expose(&(mfd[mfd_id]), 0); + ss_set_hack_mode(0, &temp); + gr2ss_override = old_over; + } + + if (player_struct.mfd_slot_status[new_slot] == MFD_FLASH) { + player_struct.mfd_slot_status[new_slot] = MFD_ACTIVE; + + // We have to tell other panel about this button change! + if (global_fullmap->cyber) { + if (mfd_id == MFD_LEFT) + mfd_draw_button(MFD_RIGHT, newSlot); + else + mfd_draw_button(MFD_LEFT, newSlot); + } + } + + if (OnOff) { + player_struct.mfd_current_slots[mfd_id] = newSlot; + mfd_force_update_single(mfd_id); + if (full_game_3d) { + if (!(full_visible & visible_mask(mfd_id))) { + if (mfd_id == MFD_LEFT) + gr_push_canvas(&_offscreen_mfd); + else + gr_push_canvas(&_fullscreen_mfd); + gr_clear(0); + gr_pop_canvas(); + } +#ifdef STEREO_SUPPORT + if (convert_use_mode == 5) + full_visible = visible_mask(mfd_id); + else +#endif + { + full_visible |= visible_mask(mfd_id); + } + full_raise_region(&mfd[mfd_id].reg2); + chg_set_sta(FULLSCREEN_UPDATE); + } + } + + return; +} + +// --------------------------------------------------------------------------- +// mfd_change_slot() +// +// Shifts an mfd over to a new slot. + +void mfd_change_slot(ubyte mfd_id, ubyte new_slot) { + ubyte old; + + if (global_fullmap->cyber && (new_slot != MFD_INFO_SLOT || mfd_id != MFD_RIGHT)) + return; // no slots in c-space + old = player_struct.mfd_current_slots[mfd_id]; + + if (new_slot == old && !full_game_3d) + return; + + // Tell old slot it needs to stop drawing whatever it was in that mfd + if (new_slot != old) + mfd_set_slot(mfd_id, old, FALSE); + + // Set to new slot and draw its graphics if neccessary + mfd_set_slot(mfd_id, new_slot, TRUE); + + // Update the buttons + if (!global_fullmap->cyber) { + mfd_draw_button(mfd_id, old); + mfd_draw_button(mfd_id, new_slot); + } + + return; +} + +// --------------------------------------------------------------------------- +// mfd_grab() +// +// Picks an MFD to grab (i.e. the MFD whose information is +// lowest priority. Returns the mfd id. + +int mfd_grab(void) { + int i; + ubyte min = 0; + int id; + for (i = 0; i < NUM_MFDS; i++) { + ubyte slot = player_struct.mfd_current_slots[i]; + ubyte func = mfd_get_func(i, slot); + ubyte p = mfd_funcs[func].priority; + if (p > min) { + min = p; + id = i; + } + } + return id; +} + +// --------------------------------------------------------------------------- +// mfd_grab_func() +// +// Like mfd_grab(), except specifies a func number. If any mfd is already +// set to that func, returns that mfd id instead of the lowest prority. +// Otherwise, if there is already an mfd on the given slot, returns that +// mfd. If neither of these conditions holds, returns the mfd with the +// lowest priority. If other mfds are on the same slot as the one we +// are grabbing for a new func, try to restore to them. + +// mfd_choose_func() does the same thing as mfd_grab_func, but does not +// assume we necessarily actually want to grab the mfd it returns, and +// therefore does not restore to other mfds on the same slot. +// +int mfd_choose_func(int my_func, int my_slot) { + int i; + ubyte min = 0; + ubyte slot, func, p; + int lowid, retval, sameslotid = -1; + + for (i = 0; i < NUM_MFDS; i++) { + slot = player_struct.mfd_current_slots[i]; + func = mfd_get_func(i, slot); + p = mfd_funcs[func].priority; + + if (func == my_func) + return i; + if (slot == my_slot) + sameslotid = i; + if (p > min) { + min = p; + lowid = i; + } + } + if (sameslotid != -1) + retval = sameslotid; + else + retval = lowid; + + return retval; +} + +int mfd_grab_func(int my_func, int my_slot) { + ubyte mfd, slot; + int i; + + mfd = mfd_choose_func(my_func, my_slot); + slot = player_struct.mfd_current_slots[mfd]; + + // if more than one mfd is on the slot we're grabbing, try + // restoring to the other slots. + for (i = 0; i < NUM_MFDS; i++) { + if (i != mfd && player_struct.mfd_current_slots[i] == slot) { + restore_mfd_slot(i); + break; + } + } + return mfd; +} + +// ----------------------------------------------------------------------- +// mfd_yield_func() +// +// If no mfd has func as its current function, returns FALSE. Otherwise, +// if *mfd_id is NUM_MFDS, sets mfd_id to the lowest mfd id which has func as +// its function and returns TRUE. Otherwise, sets mfd_id to the lowest +// such id which is greater than the one provided and returns TRUE, or +// returns FALSE if there is no such greater mfd. Thus acts as an +// iterator on mfd's with the given func. +// Why, you may ask? 'Cause it's pretty much exactly as easy as a +// function which just finds out if some mfd has this current function, +// which is what I need, and it's loads more generally useful. Har har. + +uchar mfd_yield_func(int func, int *mfd_id) { + int id; + + for (id = (*mfd_id != NUM_MFDS) ? (*mfd_id) + 1 : 0; id < NUM_MFDS; id++) { + if (mfd_get_active_func(id) == func) { + *mfd_id = id; + return TRUE; + } + } + return FALSE; +} + +// ----------------------------------------------------------- +// mfd_zoom_rect(Rect* start, int mfd) +// +// Zooms a rect from the specified starting point to the +// the indicated rect. + +void mfd_zoom_rect(LGRect *start, int mfdnum) { + DEBUG("Zooming mfd %i", mfdnum); + LGRect r1, r2; + play_digi_fx(SFX_ZOOM_BOX, 1); + r1 = *start; + r2 = mfd[mfdnum].rect; + zoom_rect(&r1, &r2); +} + +// ------------------------ +// CALLBACK FUNCTIONS +// ------------------------ + +// ------------------------------------------------------------------ +// mfd_object_cursor_handler() gets called for events in the MFD +// region with an object on the cursor. + +uchar object_button_down = FALSE; + +uchar mfd_object_cursor_handler(uiEvent *ev, LGRegion *reg, int which_mfd) { + uchar retval = FALSE; + int trip, mid; + int new_slot = -1; + ObjID obj = object_on_cursor; + if (ev->type != UI_EVENT_MOUSE) + return TRUE; + if (ev->subtype & (MOUSE_RDOWN | MOUSE_LDOWN)) { + object_button_down = TRUE; + retval = TRUE; + } + if ((ev->subtype & (MOUSE_LUP | MOUSE_RUP)) && object_button_down) { + extern uchar gump_num_objs; + uchar is_gump = mfd_get_active_func(which_mfd) == MFD_GUMP_FUNC && gump_num_objs != 0; + + object_button_down = FALSE; + retval = TRUE; + if (inventory_add_object(object_on_cursor, !is_gump)) { + if (!is_gump) { + switch (objs[obj].obclass) { + case CLASS_GUN: + new_slot = MFD_WEAPON_SLOT; + break; + case CLASS_AMMO: + trip = current_weapon_trip(); + if (trip != -1 && gun_takes_ammo(trip, ID2TRIP(obj)) && + player_struct.mfd_current_slots[which_mfd] == MFD_WEAPON_SLOT) + ; // do nothing + else + new_slot = MFD_ITEM_SLOT; + break; + + case CLASS_SOFTWARE: + if (objs[obj].subclass == SOFTWARE_SUBCLASS_DATA) { + new_slot = MFD_INFO_SLOT; + break; + } + default: + new_slot = MFD_ITEM_SLOT; + } + for (mid = 0; mid < NUM_MFDS; mid++) { + if (mid != which_mfd && mfd_index(mid) == new_slot) + restore_mfd_slot(mid); + } + mfd_change_slot(which_mfd, new_slot); + mfd_force_update_single(which_mfd); + } + pop_cursor_object(); + } + } + return retval; +} + + // --------------------------------------------------------------------------- + // mfd_view_callback() + // + // The callback for the MFD view windows. Triggered by mouseclicks inside + // the regions. + +#define SEARCH_MARGIN 2 + +uchar mfd_scan_opacity(int mfd_id, LGPoint epos) { + uchar retval = FALSE; + LGPoint pos = epos; + short x, y; + grs_canvas *cv = ((int)mfd_id == MFD_RIGHT) ? &_fullscreen_mfd : &_offscreen_mfd; + + pos.x -= mfd[mfd_id].reg.abs_x; + pos.y -= mfd[mfd_id].reg.abs_y; + gr_push_canvas(cv); + for (x = pos.x - SEARCH_MARGIN; x <= pos.x + SEARCH_MARGIN; x++) + for (y = pos.y - SEARCH_MARGIN; y <= pos.y + SEARCH_MARGIN; y++) + if (gr_get_pixel(x, y) != 0) + retval = TRUE; + gr_pop_canvas(); + return retval; +} + +uchar mfd_view_callback_full(uiEvent *e, LGRegion *r, intptr_t udata) { + uchar retval = FALSE; + uchar mask; + if (udata == MFD_RIGHT) + mask = FULL_R_MFD_MASK; + else + mask = FULL_L_MFD_MASK; + if (full_visible & mask) { + retval = mfd_view_callback(e, r, udata); + if (!retval) { + retval = mfd_scan_opacity(udata, e->pos); + } + } + return retval; +} + +uchar mfd_view_callback(uiEvent *e, LGRegion *r, intptr_t udata) { + int i; + int which_mfd; + MFD *m; + ubyte func_id; + MFD_Func *f; + + LGRegion dummy; // dummy + dummy = *r; // dummy + + which_mfd = (int)udata; + + // We should pass on info to the appropriate slot's current handler + if (which_mfd == MFD_LEFT) + m = &mfdL; + else + m = &mfdR; + + if (input_cursor_mode == INPUT_OBJECT_CURSOR) + return mfd_object_cursor_handler(e, r, which_mfd); + else + object_button_down = FALSE; + func_id = mfd_get_active_func(which_mfd); + f = &(mfd_funcs[func_id]); + if (f->simp && f->simp(m, e)) + return TRUE; + for (i = 0; i < f->handler_count; i++) { + LGPoint pos = e->pos; +#ifdef STEREO_SUPPORT + if (convert_use_mode == 5) { + pos.y -= m->rect.ul.y; + switch (i6d_device) { + case I6D_CTM: + if (which_mfd == 0) + pos.x -= m->rect.ul.x; + else + pos.x -= (m->rect.ul.x << 1); + break; + case I6D_VFX1: + Warning(("original pos.x = %d, m->rect.ul.x = %d!\n", pos.x, m->rect.ul.x)); + pos.x -= (m->rect.ul.x); + break; + } + } else { +#endif + pos.x -= m->rect.ul.x; + pos.y -= m->rect.ul.y; +#ifdef STEREO_SUPPORT + } +#endif + if (RECT_TEST_PT(&f->handlers[i].r, pos)) + if (f->handlers[i].proc(m, e, &f->handlers[i])) + return TRUE; + } + + return FALSE; +} + +// --------------------------------------------------------------------------- +// mfd_button_callback() +// +// The callback for the MFD button panels. Triggered by mouseclicks inside +// the button panels. +int last_mfd_cnum[NUM_MFDS] = {-1, -1}; +uchar mfd_button_callback(uiEvent *e, LGRegion *r, intptr_t udata) { + int cnum, which_panel, which_button; + div_t result; + +#ifndef NO_DUMMIES + LGRegion dummy; + dummy = *r; +#endif + + if (global_fullmap->cyber) { + uiSetRegionDefaultCursor(r, NULL); + return FALSE; + } else { + which_panel = (int)udata; + + // Divide mouseclick height to discover which button we meant + result = div((e->pos.y - MFD_BTTN_Y), MFD_BTTN_SZ + MFD_BTTN_BLNK); + which_button = result.quot; + + cnum = which_button; + + if (player_struct.mfd_slot_status[which_button] == MFD_UNAVAIL || !popup_cursors) { + if ((cnum != last_mfd_cnum[which_panel])) { + last_mfd_cnum[which_panel] = cnum; + uiSetRegionDefaultCursor(r, &globcursor); + } + } + if (player_struct.mfd_slot_status[which_button] != MFD_UNAVAIL) { + if ((cnum != last_mfd_cnum[which_panel]) && popup_cursors) { + LGPoint offset = {0, 0}; + last_mfd_cnum[which_panel] = cnum; + free(mfd_bttn_bitmaps[which_panel].bits); + make_popup_cursor(&mfd_bttn_cursors[which_panel], &mfd_bttn_bitmaps[which_panel], cursor_strings[cnum], + which_panel, TRUE, offset); + uiSetRegionDefaultCursor(r, &mfd_bttn_cursors[which_panel]); + } + + if (!(e->mouse_data.action & (MOUSE_LDOWN | UI_MOUSE_LDOUBLE))) + return TRUE; // ignore all but left clickdowns + + // If things are ok, select button + if ((result.rem < MFD_BTTN_SZ) && (which_button < MFD_NUM_VIRTUAL_SLOTS)) + mfd_select_button(which_panel, which_button); + } + } + + return TRUE; +} + +// --------------------------------------------------------------------------- +// mfd_button_callback_kb() +// +// The callback for the MFD button panels, as triggered by function keys + +uchar mfd_button_callback_kb(ushort keycode, uint32_t context, intptr_t data) { + int which_panel, which_button; + + if (!global_fullmap->cyber) { + + DECODE_MFD_SELECTION(which_panel, which_button, data); + + mfd_select_button(which_panel, which_button % MFD_NUM_VIRTUAL_SLOTS); + } + + return TRUE; +} + +// --------------------------------------------------------------------------- +// mfd_select_button() +// +// A more specific version of mfd_button_callback(), where we've figured +// out which button exactly has been hit, whether because we came here +// straight from a function key or through the mouse callback parser. +// The passed argument is the equivalent of the function key number, even +// if we got it from the mouse handler. + +void mfd_select_button(int which_panel, int which_button) { + // Play sound effect + ubyte old = player_struct.mfd_current_slots[which_panel]; + int hnd = play_digi_fx(SFX_MFD_BUTTON, 1); + + if (hnd >= 0) { + snd_digi_parms *ssp; + ssp = snd_sample_parms(hnd); + + // Woo hoo, hardcode city + if (which_panel == MFD_LEFT) + ssp->pan = 30; + else + ssp->pan = 97; + } + + if (full_game_3d && which_button == old && (full_visible & visible_mask(which_panel))) { + full_visible &= ~visible_mask(which_panel); + } else + mfd_change_slot((ubyte)which_panel, (ubyte)which_button); +} + +// --------------------------------------------------------------------------- +// mfd_update() +// +// This is what gets called from the main loop each frame. + +void mfd_update() { + static uchar LastFlash = FALSE; + ubyte steps_cache[NUM_MFDS] = {0, 0}; + + int i, j; + ubyte slots[NUM_MFDS]; + ubyte status_cache[NUM_MFDS]; + + Flash = (bool)((player_struct.game_time / MFD_BTTN_FLASH_TIME) % 2); + + if (!global_fullmap->cyber) + for (i = 0; i < NUM_MFDS; i++) { + for (j = 0; j < MFD_NUM_VIRTUAL_SLOTS; j++) { + slots[i] = player_struct.mfd_virtual_slots[i][j]; + if (player_struct.mfd_slot_status[slots[i]] == MFD_FLASH) { + chg_set_flg(MFD_UPDATE); + if (LastFlash != Flash) + mfd_draw_button(i, j); + } + } + } + if (LastFlash != Flash) + LastFlash = Flash; + + // Is it time to update appropriate mfd's? + // Check only current slots, and look at flag to see + // if they need constant update + +#ifndef BAD_BITS_BUG_FIXED + _fullscreen_mfd.bm.bits = mfd_background.bits; + _offscreen_mfd.bm.bits = mfd_canvas_bits; +#endif // BAD_BITS_BUG_FIXED + + // This code totally depends on our item func implementation. + i = NUM_MFDS; + if (mfd_yield_func(MFD_ITEM_FUNC, &i)) { + update_item_mfd(); + } + + // Build the status cache. + for (i = 0; i < NUM_MFDS; i++) { + ubyte f_id = mfd_get_active_func(i); + MFD_Func *f = &(mfd_funcs[f_id]); + status_cache[i] = (player_struct.mfd_func_status[f_id]); + if (f->flags & MFD_INCREMENTAL) { + long deltat = (player_struct.game_time - f->last) >> 4; + ubyte increment = (player_struct.mfd_func_status[f_id] >> 4); + ubyte num_steps = (increment > 0) ? lg_max(0, deltat / increment) : 0; + steps_cache[i] = num_steps; + chg_set_flg(MFD_UPDATE); + } + } + + // Now update the stati that need it. + for (i = 0; i < NUM_MFDS; i++) { + if ((status_cache[i] & (MFD_CHANGEBIT | MFD_CHANGEBIT_FULL)) || steps_cache[i] > 0) { + ubyte f_id = mfd_get_active_func(i); + mfd_clear_func(f_id); + mfd_update_current_slot(i, status_cache[i], steps_cache[i]); + } + } + return; +} + +// --------------------------------------------------------------------------- +// mfd_update_current_slot() +// +// See if we need to update anything in the current slot being +// viewed in an MFD. Returns TRUE if it updated a function. + +uchar mfd_update_current_slot(ubyte mfd_id, ubyte status, ubyte num_steps) { + MFD_Func *f; + ubyte f_id; + ubyte control; + MFD *m; + + f_id = mfd_get_active_func(mfd_id); + f = &(mfd_funcs[f_id]); + m = &(mfd[mfd_id]); + if (player_struct.panel_ref == OBJ_NULL && mfd_distance_remove(f_id)) { + check_panel_ref(TRUE); + } + + // If the change bit is set, or if the function is incremental + // and enough time has gone by, then we need to expose + + { +#ifdef SVGA_SUPPORT + uchar old_over = gr2ss_override; + short temp; + gr2ss_override = OVERRIDE_ALL; + ss_set_hack_mode(MFD_STEREO_HACK_MODE, &temp); +#endif + control = (num_steps << 4) | MFD_EXPOSE; + if (full_game_3d || (status & MFD_CHANGEBIT_FULL)) + control |= MFD_EXPOSE_FULL; + + // Okay, if we are in full screen mode, secretly switch the fine canvas + // usually served in this restaurant with our own Folger's brand canvas + + pmfd_canvas = (full_game_3d && mfd_id == MFD_RIGHT) ? &_fullscreen_mfd : &_offscreen_mfd; + if (full_game_3d && (control & MFD_EXPOSE_FULL)) { + gr_push_canvas(pmfd_canvas); + gr_clear(0); + gr_pop_canvas(); + } + + f->expose(m, control); // pass # steps + flags to +#ifdef SVGA_SUPPORT + ss_set_hack_mode(0, &temp); + gr2ss_override = old_over; +#endif + + return TRUE; + } +} + +// --------------------------------------------------------------------------- +// mfd_force_update() +// +// Forces a redraw of both the button panels and mfd slots + +void mfd_force_update() { + ubyte i; + for (i = 0; i < NUM_MFDS; i++) { + mfd_force_update_single(i); + } +} + +// --------------------------------------------------------------------------- +// mfd_force_update() +// +// Forces a redraw of one of the button panels and mfd slots + +void mfd_force_update_single(int which_mfd) { + ubyte f_id, s_id; + MFD_Status stat; + + if (_current_loop <= FULLSCREEN_LOOP) + mfd_draw_all_buttons(which_mfd); + + f_id = mfd_get_active_func(which_mfd); + s_id = player_struct.mfd_virtual_slots[which_mfd][mfd_index(which_mfd)]; + stat = player_struct.mfd_slot_status[s_id]; + + mfd_notify_func(f_id, s_id, FALSE, stat, TRUE); + + return; +} + +//-------------------------------------------------------- +// fullscreen_refresh_mfd() +// +// re-blits a single mfd. + +void fullscreen_refresh_mfd(ubyte mfd_id) { + ushort a, b, c, d; + LGRect r; + MFD *m = &mfd[mfd_id]; + uchar visible = (full_visible & visible_mask(mfd_id)) != 0; +#ifdef SVGA_SUPPORT + uchar old_over = gr2ss_override; +#endif + if (visible) { + pmfd_canvas = (mfd_id == MFD_RIGHT) ? &_fullscreen_mfd : &_offscreen_mfd; + + r.ul = MakePoint(0, 0); + r.lr = MakePoint(MFD_VIEW_WID, MFD_VIEW_HGT); + RECT_MOVE(&r, m->rect.ul); + STORE_CLIP(a, b, c, d); +#ifdef SVGA_SUPPORT + gr2ss_override = OVERRIDE_ALL; +#endif +#ifdef STEREO_SUPPORT + if (convert_use_mode == 5) { + pmfd_canvas->bm.flags |= BMF_TRANS; + if (mfd_id == 0) { + ss_safe_set_cliprect(r.ul.x, 0, r.lr.x << 1, r.lr.y); + if (i6d_device == I6D_CTM) + ss_noscale_bitmap(&(pmfd_canvas->bm), m->rect.ul.x, -5); + else + ss_noscale_bitmap(&(pmfd_canvas->bm), m->rect.ul.x, m->rect.ul.y); + } else { + ss_safe_set_cliprect(r.ul.x >> 1, 0, r.lr.x, r.lr.y); + if (i6d_device == I6D_CTM) + ss_noscale_bitmap(&(pmfd_canvas->bm), m->rect.ul.x >> 1, -5); + else + ss_noscale_bitmap(&(pmfd_canvas->bm), m->rect.ul.x >> 1, m->rect.ul.y); + } + pmfd_canvas->bm.flags &= ~BMF_TRANS; + } else { +#endif + ss_safe_set_cliprect(r.ul.x, r.ul.y, r.lr.x, r.lr.y); + pmfd_canvas->bm.flags |= BMF_TRANS; + ss_noscale_bitmap(&(pmfd_canvas->bm), m->rect.ul.x, m->rect.ul.y); + pmfd_canvas->bm.flags &= ~BMF_TRANS; +#ifdef STEREO_SUPPORT + } +#endif +#ifdef SVGA_SUPPORT + gr2ss_override = old_over; +#endif + RESTORE_CLIP(a, b, c, d); + } + region_set_invisible(&m->reg2, !visible); +} + +// --------------------------- +// MFD INTERNAL GRAPHICS +// --------------------------- + +// --------------------------------------------------------------------------- +// mfd_draw_button() +// +// Draws a button in a given color code depending on its status. + +uchar cyber_button_back_door = FALSE; + +void mfd_draw_button(ubyte mfd_id, ubyte b) { + MFD *m; + LGRect r; + ubyte slot; +#ifdef SVGA_SUPPORT + uchar old_over = gr2ss_override; + gr2ss_override = OVERRIDE_ALL; +#endif + + if (global_fullmap->cyber && full_game_3d) + return; + + m = &(mfd[mfd_id]); + + r.ul.x = m->bttn.rect.ul.x + 1; + r.ul.y = m->bttn.rect.ul.y + 1; + r.ul.y += (b * (MFD_BTTN_SZ + MFD_BTTN_BLNK)); + + r.lr.x = r.ul.x + MFD_BTTN_WID - 1; + r.lr.y = r.ul.y + MFD_BTTN_SZ; + + slot = player_struct.mfd_virtual_slots[mfd_id][b]; + + if (player_struct.mfd_slot_status[slot] == MFD_EMPTY) + gr_set_fcolor(MFD_BTTN_EMPTY); + else if (player_struct.mfd_slot_status[slot] == MFD_ACTIVE) + gr_set_fcolor(MFD_BTTN_ACTIVE); + else if (player_struct.mfd_slot_status[slot] == MFD_UNAVAIL) + gr_set_fcolor(MFD_BTTN_UNAVAIL); + else if (player_struct.mfd_slot_status[slot] == MFD_FLASH) { + if (Flash) + gr_set_fcolor((long)MFD_BTTN_FLASH); // Is blink on or off? + else + gr_set_fcolor((long)MFD_BTTN_EMPTY); // Draw appropriately + } + + if (mfd_index(m->id) == b) + gr_set_fcolor((long)MFD_BTTN_SELECT); // current + + uiHideMouse(&r); + ss_rect(r.ul.x, r.ul.y, r.lr.x - 2, r.lr.y - 2); +/*{ + short bx = (mfd_id == 0) ? 3 : 629; + short by = 333 + (b*26); + gr_rect(bx, by, bx+6, by+17); +}*/ +#ifdef SVGA_SUPPORT + gr2ss_override = old_over; +#endif + uiShowMouse(&r); + + return; +} + + // --------------------------------------------------------------------------- + // mfd_draw_button_panel() + // + // Draws an MFD's panel of buttons, and their associated background art as well + +#define MFD_PANEL_Y 326 +#define MFD_LEFT_PANEL_X 1 +#define MFD_RIGHT_PANEL_X 627 + +void mfd_draw_button_panel(ubyte mfd_id) { + int x[2] = {MFD_LEFT_PANEL_X, MFD_RIGHT_PANEL_X}; + + draw_res_bm(REF_IMG_bmMFDButtonBackground, x[mfd_id], MFD_PANEL_Y); + mfd_draw_all_buttons(mfd_id); + return; +} + +// --------------------------------------------------------------------------- +// mfd_draw_all_buttons() +// +// Draws an MFD's panel of buttons. + +void mfd_draw_all_buttons(ubyte mfd_id) { + ubyte i; + + for (i = 0; i < MFD_NUM_VIRTUAL_SLOTS; i++) + mfd_draw_button(mfd_id, i); + + return; +} + +// --------------------------------------------------------------------------- +// mfd_draw_string() +// +// Draws a string to the mfd canvas, at a relative x, y location. It is +// the calling expose functions responsibility to recopy from the canvas +// to the screen. Returns a point describing the pixel dimensions of the string + +uchar mfd_string_wrap = TRUE; +ubyte mfd_string_shadow = MFD_SHADOW_FULLSCREEN; + +LGPoint mfd_full_draw_string(char *s, short x, short y, long c, int font, uchar DrawString, uchar transp) { + LGPoint siz; + short w, h; + ushort sc1, sc2, sc3, sc4; + short border = 0; + grs_font *thefont = ResLock(font); + + x = lg_min(lg_max(x, 0), MFD_VIEW_WID - 1); + y = lg_min(lg_max(y, 0), MFD_VIEW_HGT - 1); + STORE_CLIP(sc1, sc2, sc3, sc4); + if ((full_game_3d && mfd_string_shadow == MFD_SHADOW_FULLSCREEN) || + mfd_string_shadow == MFD_SHADOW_ALWAYS) + border = 1; + + gr_set_font(thefont); + if (mfd_string_wrap) + gr_string_wrap(s, MFD_VIEW_WID - x - 1); + gr_string_size(s, &w, &h); + w = lg_min(w, MFD_VIEW_WID - x - border); + h = lg_min(h, MFD_VIEW_HGT - y - border); + siz.x = w; + siz.y = h; + if (w <= 0 || h <= 0) + goto out; + ss_safe_set_cliprect(lg_max(x - border, 0), lg_max(y - border, 0), x + w + border, y + h + border); + if (!full_game_3d && !transp) + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background,0,0); + if (DrawString) { + gr_set_fcolor(c); + draw_shadowed_string(s, x, y, border > 0); + } + mfd_add_rect(x - border, y - border, x + w + border, y + h + border); +out: + if (mfd_string_wrap) + gr_font_string_unwrap(s); + ResUnlock(font); + RESTORE_CLIP(sc1, sc2, sc3, sc4); + + return siz; +} + +LGPoint mfd_draw_font_string(char *s, short x, short y, long c, int font, uchar DrawString) { + // Hey, this used to always specify non-transparent strings,but that just plain + // seemed wrong, so I switched it.... -- Xemu + return mfd_full_draw_string(s, x, y, c, font, DrawString, TRUE); +} + +LGPoint mfd_draw_string(char *s, short x, short y, long c, uchar DrawString) { + return mfd_draw_font_string(s, x, y, c, RES_tinyTechFont, DrawString); +} + +// ---------------------------------------------------------------------- +// mfd_draw_bitmap() draws a bitmap and adds its rect to the +// update list. + +void mfd_draw_bitmap(grs_bitmap *bmp, short x, short y) { + ss_bitmap(bmp, x, y); + mfd_add_rect(x, y, x + bmp->w, y + bmp->h); +} + +// -------------------------------------------------------------------------- +// mfd_partial_clear() +// +// Clears a portion of an mfd canvas + +void mfd_partial_clear(LGRect *r) { + if (!full_game_3d) { + ss_safe_set_cliprect(r->ul.x, r->ul.y, r->lr.x, r->lr.y); + mfd_add_rect(r->ul.x, r->ul.y, r->lr.x, r->lr.y); + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + } + + return; +} + + // ------------------------------------------------------------------- + // UPDATE RECT STUFF + // + // Here we are collecting a list refresh rectangles which will be + // updated from the off-screen mfd canvas + +#define NUM_MFD_RECTS 16 +LGRect mfd_update_list[NUM_MFD_RECTS]; +int mfd_num_updates = 0; + +// ------------------------------------------------------------------- +// +// mfd_clear_rects(), clears the update list to empty + +void mfd_clear_rects(void) { mfd_num_updates = 0; } + +// ------------------------------------------------------------------- +// +// mfd_add_rect() adds a rect to the rect list. + +errtype mfd_add_rect(short x, short y, short x1, short y1) { + int i; + LGRect r; + short tmp; + // check for invalid rect + if (x > x1) { + tmp = x; + x = x1; + x1 = tmp; + } + if (y > y1) { + tmp = y; + y = y1; + y1 = tmp; + } + r.ul.x = x; + r.ul.y = y; + r.lr.x = x1; + r.lr.y = y1; + for (i = 0; i < mfd_num_updates; i++) + if (RECT_TEST_SECT(&mfd_update_list[i], &r)) { + // If we intersect with some existing rect, union it in to r and + // Delete it from the list + RECT_UNION(&mfd_update_list[i], &r, &r); + if (i != mfd_num_updates - 1) + mfd_update_list[i] = mfd_update_list[mfd_num_updates - 1]; + mfd_num_updates--; + i = 0; // We might now intersect a previous one. + } + if (mfd_num_updates >= NUM_MFD_RECTS) + return ERR_DOVERFLOW; + mfd_update_list[mfd_num_updates++] = r; + return OK; +} + +// ------------------------------------------------------------------- +// +// mfd_update_rects() Updates all rects in the update list, then clears +// the update list. + +void mfd_update_rects(MFD *m) { + int i; + for (i = 0; i < mfd_num_updates; i++) { + LGRect *r = &mfd_update_list[i]; + // Filter out degenerate rects! + if ((r->lr.x <= r->ul.x) || (r->lr.y <= r->ul.y)) + continue; + mfd_update_display(m, r->ul.x, r->ul.y, r->lr.x, r->lr.y); + } + mfd_num_updates = 0; +} + +// -------------------------------------------------------------------------- +// mfd_update_display() +// +// Updates a portion of the view window from canvas. + +void mfd_update_display(MFD *m, short x0, short y0, short x1, short y1) { + ushort a, b, c, d; + uchar old_over = gr2ss_override; + + if (!full_game_3d) { + LGRect r; + + if ((x0 > x1) || (y0 > y1)) + return; + + r.ul.x = x0; + r.ul.y = y0; + r.lr.x = x1; + r.lr.y = y1; + + RECT_OFFSETTED_RECT(&r, m->rect.ul, &r); + if (!RECT_TEST_SECT(&r, &m->rect)) + return; + RectSect(&r, &m->rect, &r); + + gr_push_canvas(grd_screen_canvas); + uiHideMouse(&r); + STORE_CLIP(a, b, c, d); + gr2ss_override = OVERRIDE_ALL; + ss_safe_set_cliprect(r.ul.x, r.ul.y, r.lr.x, r.lr.y); + ss_noscale_bitmap(&(pmfd_canvas->bm), m->rect.ul.x, m->rect.ul.y); + + gr2ss_override = old_over; + uiShowMouse(&r); + RESTORE_CLIP(a, b, c, d); + gr_pop_canvas(); + } + + return; +} + +// *********************************************** +// **** SAVE/RESTORE and DEFAULT MFD MANAGER ***** +// *********************************************** + +// for saving and restoring mfd settings around "panel" mfd's, we +// use our secret 6th slot. + +typedef uchar (*mfd_def_qual)(void); + +typedef struct { + uchar func; + uchar slot; + mfd_def_qual qual; +} mfd_default; + +mfd_default default_mfds[] = {{MFD_MAP_FUNC, MFD_MAP_SLOT, &mfd_automap_qual}, + {MFD_WEAPON_FUNC, MFD_WEAPON_SLOT, &mfd_weapon_qual}, + {MFD_TARGET_FUNC, MFD_TARGET_SLOT, &mfd_target_qual}}; + +#define NUM_MFD_DEFAULTS (sizeof(default_mfds) / sizeof(mfd_default)) + +void mfd_default_mru(uchar func) { + int i, pos = -1; + mfd_default tmp; + + for (i = 0; i < NUM_MFD_DEFAULTS; i++) { + if (default_mfds[i].func == func) { + pos = i; + tmp = default_mfds[i]; + break; + } + } + if (pos < 0) + return; // func not in default list + + for (i = pos; i > 0; i--) + default_mfds[i] = default_mfds[i - 1]; + default_mfds[0] = tmp; +} + +void save_mfd_slot(int mfd_id) { + uchar func, mask; + int slot; + + if (global_fullmap->cyber) + return; + if (player_struct.mfd_save_slot[mfd_id] < 0) { + slot = player_struct.mfd_current_slots[mfd_id]; + + func = mfd_get_func(mfd_id, slot); + if (!(mfd_funcs[func].flags & MFD_NOSAVEREST)) { + player_struct.mfd_save_slot[mfd_id] = slot; + set_slot_to_func(MFD_SPECIAL_SLOT + mfd_id, func, MFD_ACTIVE); + if (full_game_3d) { + mask = visible_mask(mfd_id); + player_struct.mfd_save_vis &= ~mask; + player_struct.mfd_save_vis |= (mask & full_visible); + } + } + } +} + +// sets mfd to given slot and func, unless the func passed in is MFD_EMPTY_FUNC +// or some MFD already has that func. In that case, set to some other slot +// from a list of hopefully useful defaults. +// note that if you pass in MFD_EMPTY_FUNC, a new setting is always selected, +// so the slot argument is ignored. + +void set_mfd_from_defaults(int mfd_id, uchar func, uchar slot) { + uchar def, mid; + uchar check; + + def = 0; + do { + check = FALSE; + for (mid = 0; mid < NUM_MFDS; mid++) { + if (func == MFD_EMPTY_FUNC || func == mfd_get_func(mid, player_struct.mfd_current_slots[mid])) { + // don't restore func that we already have on some mfd. + if (default_mfds[def].qual()) { + func = default_mfds[def].func; + slot = default_mfds[def].slot; + } + def++; + check = TRUE; + break; + } + } + } while (check && def < NUM_MFD_DEFAULTS); + + if (func == MFD_EMPTY_FUNC) + slot = (global_fullmap->cyber) ? MFD_INFO_SLOT : MFD_ITEM_SLOT; // failure case + mfd_notify_func(func, slot, TRUE, MFD_ACTIVE, TRUE); + if (!full_game_3d || (full_visible & FULL_MFD_MASK(mfd_id))) + mfd_change_slot(mfd_id, slot); +} + +// scans throught the mfd's looking for mfd's that are set to the given slot/func. +// once it have found max such mfd's, starts setting any subsequent mfd's +// with that func to defaults as above. + +void cap_mfds_with_func(uchar func, uchar max) { + int mid; + + for (mid = 0; mid < NUM_MFDS; mid++) { + if (mfd_get_func(mid, player_struct.mfd_current_slots[mid]) == func) { + if (max == 0) + restore_mfd_slot(mid); + else + max--; + } + } +} + +void restore_mfd_slot(int mfd_id) { + uchar func, slot; + if (global_fullmap->cyber) + return; + if (full_game_3d && !(visible_mask(mfd_id) & full_visible)) + return; + if (player_struct.mfd_save_slot[mfd_id] < 0) { + func = MFD_EMPTY_FUNC; + slot = MFD_INFO_SLOT; + } else { + func = player_struct.mfd_all_slots[MFD_SPECIAL_SLOT + mfd_id]; + slot = player_struct.mfd_save_slot[mfd_id]; + } + + set_mfd_from_defaults(mfd_id, func, slot); + player_struct.mfd_save_slot[mfd_id] = -1; + full_visible &= ~(visible_mask(mfd_id)); +#ifdef STEREO_SUPPORT + if (convert_use_mode == 5) + full_visible = (player_struct.mfd_save_vis & visible_mask(mfd_id)); + else +#endif + { + full_visible |= (player_struct.mfd_save_vis & visible_mask(mfd_id)); + } +} diff --git a/engine/src/GameSrc/objapp.c b/engine/src/GameSrc/objapp.c new file mode 100644 index 0000000..39a71e6 --- /dev/null +++ b/engine/src/GameSrc/objapp.c @@ -0,0 +1,172 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* +** $Header: n:/project/cit/src/RCS/objapp.c 1.17 1994/05/14 03:30:03 xemu Exp $ +* +*/ + +#include "objects.h" +#include "objwpn.h" +#include "objwarez.h" +#include "objstuff.h" +#include "objgame.h" +#include "objcrit.h" +#include "objprop.h" +#include "map.h" + +////////////////////////////// APPLICATION-SPECIFIC DATA +// +// Here we define the arrays for all the application-specific +// classes defined in objapp.h. Follow this example, and there +// won't be any trouble. +// +// ## INSERT NEW CLASS HERE + +/*const*/ ObjSpecHeader objSpecHeaders[NUM_CLASSES] = { + {NUM_OBJECTS_GUN, sizeof(ObjGun), (char *)&objGuns}, + {NUM_OBJECTS_AMMO, sizeof(ObjAmmo), (char *)&objAmmos}, + {NUM_OBJECTS_PHYSICS, sizeof(ObjPhysics), (char *)&objPhysicss}, + {NUM_OBJECTS_GRENADE, sizeof(ObjGrenade), (char *)&objGrenades}, + {NUM_OBJECTS_DRUG, sizeof(ObjDrug), (char *)&objDrugs}, + {NUM_OBJECTS_HARDWARE, sizeof(ObjHardware), (char *)&objHardwares}, + {NUM_OBJECTS_SOFTWARE, sizeof(ObjSoftware), (char *)&objSoftwares}, + {NUM_OBJECTS_BIGSTUFF, sizeof(ObjBigstuff), (char *)&objBigstuffs}, + {NUM_OBJECTS_SMALLSTUFF, sizeof(ObjSmallstuff), (char *)&objSmallstuffs}, + {NUM_OBJECTS_FIXTURE, sizeof(ObjFixture), (char *)&objFixtures}, + {NUM_OBJECTS_DOOR, sizeof(ObjDoor), (char *)&objDoors}, + {NUM_OBJECTS_ANIMATING, sizeof(ObjAnimating), (char *)&objAnimatings}, + {NUM_OBJECTS_TRAP, sizeof(ObjTrap), (char *)&objTraps}, + {NUM_OBJECTS_CONTAINER, sizeof(ObjContainer), (char *)&objContainers}, + {NUM_OBJECTS_CRITTER, sizeof(ObjCritter), (char *)&objCritters}, +}; + +// const ObjSpecHeader ObjPropHeader = {NUM_OBJECT, sizeof(ObjProp), &ObjProps} + +const ObjSpecHeader ClassPropHeaders[NUM_CLASSES] = { + {NUM_GUN, sizeof(GunProp), (char *)&GunProps}, + {NUM_AMMO, sizeof(AmmoProp), (char *)&AmmoProps}, + {NUM_PHYSICS, sizeof(PhysicsProp), (char *)&PhysicsProps}, + {NUM_GRENADE, sizeof(GrenadeProp), (char *)&GrenadeProps}, + {NUM_DRUG, sizeof(DrugProp), (char *)&DrugProps}, + {NUM_HARDWARE, sizeof(HardwareProp), (char *)&HardwareProps}, + {NUM_SOFTWARE, sizeof(SoftwareProp), (char *)&SoftwareProps}, + {NUM_BIGSTUFF, sizeof(BigstuffProp), (char *)&BigstuffProps}, + {NUM_SMALLSTUFF, sizeof(SmallstuffProp), (char *)&SmallstuffProps}, + {NUM_FIXTURE, sizeof(FixtureProp), (char *)&FixtureProps}, + {NUM_DOOR, sizeof(DoorProp), (char *)&DoorProps}, + {NUM_CONTAINER, sizeof(ContainerProp), (char *)&ContainerProps}, + {NUM_CRITTER, sizeof(CritterProp), (char *)&CritterProps}, +}; + +const ObjSpecHeader SubclassPropHeaders[NUM_SUBCLASSES] = { + {NUM_PISTOL_GUN, sizeof(PistolGunProp), (char *)&PistolGunProps}, + {NUM_AUTO_GUN, sizeof(AutoGunProp), (char *)&AutoGunProps}, + {NUM_SPECIAL_GUN, sizeof(SpecialGunProp), (char *)&SpecialGunProps}, + {NUM_HANDTOHAND_GUN, sizeof(HandtohandGunProp), (char *)&HandtohandGunProps}, + {NUM_BEAM_GUN, sizeof(BeamGunProp), (char *)&BeamGunProps}, + {NUM_BEAMPROJ_GUN, sizeof(BeamprojGunProp), (char *)&BeamprojGunProps}, + {NUM_PISTOL_AMMO, sizeof(PistolAmmoProp), (char *)&PistolAmmoProps}, + {NUM_NEEDLE_AMMO, sizeof(NeedleAmmoProp), (char *)&NeedleAmmoProps}, + {NUM_MAGNUM_AMMO, sizeof(MagnumAmmoProp), (char *)&MagnumAmmoProps}, + {NUM_RIFLE_AMMO, sizeof(RifleAmmoProp), (char *)&RifleAmmoProps}, + {NUM_FLECHETTE_AMMO, sizeof(FlechetteAmmoProp), (char *)&FlechetteAmmoProps}, + {NUM_AUTO_AMMO, sizeof(AutoAmmoProp), (char *)&AutoAmmoProps}, + {NUM_PROJ_AMMO, sizeof(ProjAmmoProp), (char *)&ProjAmmoProps}, + {NUM_TRACER_PHYSICS, sizeof(TracerPhysicsProp), (char *)&TracerPhysicsProps}, + {NUM_SLOW_PHYSICS, sizeof(SlowPhysicsProp), (char *)&SlowPhysicsProps}, + {NUM_CAMERA_PHYSICS, sizeof(CameraPhysicsProp), (char *)&CameraPhysicsProps}, + {NUM_DIRECT_GRENADE, sizeof(DirectGrenadeProp), (char *)&DirectGrenadeProps}, + {NUM_TIMED_GRENADE, sizeof(TimedGrenadeProp), (char *)&TimedGrenadeProps}, + {NUM_STATS_DRUG, sizeof(StatsDrugProp), (char *)&StatsDrugProps}, + {NUM_GOGGLE_HARDWARE, sizeof(GoggleHardwareProp), (char *)&GoggleHardwareProps}, + {NUM_HARDWARE_HARDWARE, sizeof(HardwareHardwareProp), (char *)&HardwareHardwareProps}, + {NUM_OFFENSE_SOFTWARE, sizeof(OffenseSoftwareProp), (char *)&OffenseSoftwareProps}, + {NUM_DEFENSE_SOFTWARE, sizeof(DefenseSoftwareProp), (char *)&DefenseSoftwareProps}, + {NUM_ONESHOT_SOFTWARE, sizeof(OneshotSoftwareProp), (char *)&OneshotSoftwareProps}, + {NUM_MISC_SOFTWARE, sizeof(MiscSoftwareProp), (char *)&MiscSoftwareProps}, + {NUM_DATA_SOFTWARE, sizeof(DataSoftwareProp), (char *)&DataSoftwareProps}, + {NUM_ELECTRONIC_BIGSTUFF, sizeof(ElectronicBigstuffProp), (char *)&ElectronicBigstuffProps}, + {NUM_FURNISHING_BIGSTUFF, sizeof(FurnishingBigstuffProp), (char *)&FurnishingBigstuffProps}, + {NUM_ONTHEWALL_BIGSTUFF, sizeof(OnthewallBigstuffProp), (char *)&OnthewallBigstuffProps}, + {NUM_LIGHT_BIGSTUFF, sizeof(LightBigstuffProp), (char *)&LightBigstuffProps}, + {NUM_LABGEAR_BIGSTUFF, sizeof(LabgearBigstuffProp), (char *)&LabgearBigstuffProps}, + {NUM_TECHNO_BIGSTUFF, sizeof(TechnoBigstuffProp), (char *)&TechnoBigstuffProps}, + {NUM_DECOR_BIGSTUFF, sizeof(DecorBigstuffProp), (char *)&DecorBigstuffProps}, + {NUM_TERRAIN_BIGSTUFF, sizeof(TerrainBigstuffProp), (char *)&TerrainBigstuffProps}, + {NUM_USELESS_SMALLSTUFF, sizeof(UselessSmallstuffProp), (char *)&UselessSmallstuffProps}, + {NUM_BROKEN_SMALLSTUFF, sizeof(BrokenSmallstuffProp), (char *)&BrokenSmallstuffProps}, + {NUM_CORPSELIKE_SMALLSTUFF, sizeof(CorpselikeSmallstuffProp), (char *)&CorpselikeSmallstuffProps}, + {NUM_GEAR_SMALLSTUFF, sizeof(GearSmallstuffProp), (char *)&GearSmallstuffProps}, + {NUM_CARDS_SMALLSTUFF, sizeof(CardsSmallstuffProp), (char *)&CardsSmallstuffProps}, + {NUM_CYBER_SMALLSTUFF, sizeof(CyberSmallstuffProp), (char *)&CyberSmallstuffProps}, + {NUM_ONTHEWALL_SMALLSTUFF, sizeof(OnthewallSmallstuffProp), (char *)&OnthewallSmallstuffProps}, + {NUM_PLOT_SMALLSTUFF, sizeof(PlotSmallstuffProp), (char *)&PlotSmallstuffProps}, + {NUM_CONTROL_FIXTURE, sizeof(ControlFixtureProp), (char *)&ControlFixtureProps}, + {NUM_RECEPTACLE_FIXTURE, sizeof(ReceptacleFixtureProp), (char *)&ReceptacleFixtureProps}, + {NUM_TERMINAL_FIXTURE, sizeof(TerminalFixtureProp), (char *)&TerminalFixtureProps}, + {NUM_PANEL_FIXTURE, sizeof(PanelFixtureProp), (char *)&PanelFixtureProps}, + {NUM_VENDING_FIXTURE, sizeof(VendingFixtureProp), (char *)&VendingFixtureProps}, + {NUM_CYBER_FIXTURE, sizeof(CyberFixtureProp), (char *)&CyberFixtureProps}, + {NUM_NORMAL_DOOR, sizeof(NormalDoorProp), (char *)&NormalDoorProps}, + {NUM_DOORWAYS_DOOR, sizeof(DoorwaysDoorProp), (char *)&DoorwaysDoorProps}, + {NUM_FORCE_DOOR, sizeof(ForceDoorProp), (char *)&ForceDoorProps}, + {NUM_ELEVATOR_DOOR, sizeof(ElevatorDoorProp), (char *)&ElevatorDoorProps}, + {NUM_SPECIAL_DOOR, sizeof(SpecialDoorProp), (char *)&SpecialDoorProps}, + {NUM_OBJECT_ANIMATING, sizeof(ObjectsAnimatingProp), (char *)&ObjectsAnimatingProps}, + {NUM_TRANSITORY_ANIMATING, sizeof(TransitoryAnimatingProp), (char *)&TransitoryAnimatingProps}, + {NUM_EXPLOSION_ANIMATING, sizeof(ExplosionAnimatingProp), (char *)&ExplosionAnimatingProps}, + {NUM_TRIGGER_TRAP, sizeof(TriggerTrapProp), (char *)&TriggerTrapProps}, + {NUM_FEEDBACKS_TRAP, sizeof(FeedbacksTrapProp), (char *)&FeedbacksTrapProps}, + {NUM_SECRET_TRAP, sizeof(SecretTrapProp), (char *)&SecretTrapProps}, + {NUM_ACTUAL_CONTAINER, sizeof(ActualContainerProp), (char *)&ActualContainerProps}, + {NUM_WASTE_CONTAINER, sizeof(WasteContainerProp), (char *)&WasteContainerProps}, + {NUM_LIQUID_CONTAINER, sizeof(LiquidContainerProp), (char *)&LiquidContainerProps}, + {NUM_MUTANT_CORPSE_CONTAINER, sizeof(MutantCorpseContainerProp), (char *)&MutantCorpseContainerProps}, + {NUM_ROBOT_CORPSE_CONTAINER, sizeof(RobotCorpseContainerProp), (char *)&RobotCorpseContainerProps}, + {NUM_CYBORG_CORPSE_CONTAINER, sizeof(CyborgCorpseContainerProp), (char *)&CyborgCorpseContainerProps}, + {NUM_OTHER_CORPSE_CONTAINER, sizeof(OtherCorpseContainerProp), (char *)&OtherCorpseContainerProps}, + {NUM_MUTANT_CRITTER, sizeof(MutantCritterProp), (char *)&MutantCritterProps}, + {NUM_ROBOT_CRITTER, sizeof(RobotCritterProp), (char *)&RobotCritterProps}, + {NUM_CYBORG_CRITTER, sizeof(CyborgCritterProp), (char *)&CyborgCritterProps}, + {NUM_CYBER_CRITTER, sizeof(CyberCritterProp), (char *)&CyberCritterProps}, + {NUM_ROBOBABE_CRITTER, sizeof(RobobabeCritterProp), (char *)&RobobabeCritterProps}, +}; + +////////////////////////////// APPLICATION-SPECIFIC FUNCTIONS + +static int map_x, map_y; + +void ObjInfoInit(ObjInfo *info) { + info->type = 0; + info->ph = -1; +} + +void ObjRefStateBinIteratorInit(void) { map_x = map_y = 0; } + +uchar ObjRefStateBinIterator(ObjRefStateBin *bin) { + if (map_y == MAP_YSIZE) + return FALSE; + bin->sq.x = map_x; + bin->sq.y = map_y; + if (++map_x == MAP_XSIZE) { + map_x = 0; + map_y++; + } + return TRUE; +} diff --git a/engine/src/GameSrc/objects.c b/engine/src/GameSrc/objects.c new file mode 100644 index 0000000..f149f11 --- /dev/null +++ b/engine/src/GameSrc/objects.c @@ -0,0 +1,1523 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* +** $Header: r:/prj/cit/src/RCS/objects.c 1.13 1994/08/31 16:27:36 tjs Exp $ +* +*/ + +//////////////////////////////////////////////////////////// +// +// READ ME FIRST! +// +////////////////////////////// +// +// So then, let us determine some determinology for our terms, here +// +// There are really three places a thing can be: +// WORLD: in the world and being used +// FLOATING: existent but not connected to the real world in any way +// UNUSED: not used at all, ready to be reclaimed +// +// Any function modifying the world in some way will probably contain +// one of the following words: +// +// Grab: get an unused thing UNUSED -> FLOATING +// Free: return a thing to the unused pile FLOATING -> UNUSED +// +// Add: add to reality FLOATING -> WORLD +// Rem: remove from reality WORLD -> FLOATING +// +// Make: spring fully formed into the world UNUSED -> WORLD +// Del: terminate with extreme prejudice WORLD -> UNUSED +// +// For things for which there is no real FLOATING state, Del and Make are used. +// Basically, whenever you have Rem'ed something, assume that it is +// still floating and that you must do something more to it. +// +// Object system functions tend to have names beginning with "Obj." +// If one main argument is being passed in, and it would otherwise +// be unclear what type of argument it is, that type is then put in the name. +// Then follow any additional nouns and verbs describing what the function does. +// All Objs, ObjRefs, ObjSpecs, etc. are referred to by their ID's. +// Thus, ObjRefLinkDel deletes a link from an ObjRef to an Obj and is passed an ObjRefID. +// +////////////////////////////// + +#include + +#include "objects.h" +#include "map.h" + +/* +#define DBG_Check(x) DBG(DSRC_OBJECTS_Check,x) +#define SpewCheck(x) Spew(DSRC_OBJECTS_Check,x) +#define DBG_Anal(x) DBG(DSRC_OBJECTS_Anal,x) +#define SpewAnal(x) Spew(DSRC_OBJECTS_Anal,x) +#define DBG_Report(x) DBG(DSRC_OBJECTS_Report,x) +#define SpewReport(x) Spew(DSRC_OBJECTS_Report,x) +#define DBG_Hash(x) DBG(DSRC_OBJECTS_Hash,x) +#define SpewHash(x) Spew(DSRC_OBJECTS_Hash,x) +*/ + +// See objects.h and objapp.h for a description of global variables + +#ifdef SUPPORT_VERSION_26_OBJS +old_Obj old_objs[NUM_OBJECTS]; +#endif + +#ifdef THESE_WERENT_IN_STATIC_S_BUT_THEY_ARE_SO_THESE_ARENT_REALLY_HERE +Obj objs[NUM_OBJECTS]; +ObjRef objRefs[NUM_REF_OBJECTS]; +uchar objsDealt[NUM_OBJECTS / 8]; +#endif + +ObjLocState objLocStates[MAX_OBJS_CHANGING]; +uchar numObjLocStates; + +#ifdef HASH_OBJECTS + +ObjHashElem objHashTable[OBJ_HASH_ENTRIES]; + +uchar ObjDeleteHashElem(ObjRefStateBin bin); + +#ifndef USE_FUNCTION_FOR_HASH_GET +ObjHashElemID HASHENTRY; +#endif + +#endif // HASH_OBJECTS + +static void ObjRefRem(ObjRefID ref); +static uchar ObjLinkMake(ObjRefID ref, ObjID obj); +static ObjID ObjRefLinkDel(ObjRefID ref); +static uchar ObjRefAdd(ObjRefID ref, ObjRefState refstate); +static uchar ObjDelRefs(ObjID ID); +static ObjID ObjGrab(void); +static uchar ObjFree(ObjID ref); +static ObjRefID ObjRefGrab(void); +static ObjID ObjRefFree(ObjRefID ref, uchar cleanup); +static ObjSpecID ObjSpecGrab(ObjClass obclass); +static uchar ObjSpecFree(ObjClass obclass, ObjSpecID id); +static uchar ObjAndSpecFree(ObjID obj); + +//////////////////////////////////////////////////////////// +// +// PUBLIC FUNCTIONS +// +//////////////////////////////////////////////////////////// + +////////////////////////////// +// +// Initializes all object stuff +// +// This entire function should be made faster, as it is currently quite stupid. +// +void ObjsInit(void) { + int i; + short c; + ObjSpecHeader *head; + ObjSpec *os; + + // SpewReport (("ObjsInit ()\n")); + + // clear out everything + LG_memset((void *)objs, 0, sizeof(Obj) * NUM_OBJECTS); + LG_memset((void *)objRefs, 0, sizeof(ObjRef) * NUM_REF_OBJECTS); + for (c = CLASS_FIRST; c < NUM_CLASSES; c++) { + head = &objSpecHeaders[c]; + LG_memset((void *)head->data, 0, head->size * head->struct_size); + } + + // set up the free chains for objects + for (i = 0; i < NUM_OBJECTS - 1; i++) + objs[i].next = i + 1; + objs[NUM_OBJECTS - 1].next = 0; + objs[0].headused = 0; + + // set up the free chains for references + for (i = 0; i < NUM_REF_OBJECTS - 1; i++) + objRefs[i].next = i + 1; + objRefs[NUM_REF_OBJECTS - 1].next = 0; + + // set up the free chains for class-specific info + for (c = CLASS_FIRST; c < NUM_CLASSES; c++) { + head = &objSpecHeaders[c]; + ((ObjSpec *)(head->data))->bits.id = 0; // really head of used chain + for (i = 0; i < head->size - 1; i++) { + os = (ObjSpec *)(head->data + i * head->struct_size); + os->next = i + 1; + } + ((ObjSpec *)(head->data + (head->size - 1) * head->struct_size))->next = 0; + } + +#ifdef HASH_OBJECTS + // set up the free chains for the hash table + LG_memset((void *)objHashTable, 0, sizeof(ObjHashElem) * OBJ_HASH_ENTRIES); + for (i = 0; i < OBJ_HASH_HEAD_ENTRIES_START - 1; i++) + objHashTable[i].next = i + 1; + objHashTable[OBJ_HASH_HEAD_ENTRIES_START - 1].next = 0; +#endif + + // temp + // if (!ObjSysOkay()) + // DebugString("Obj Sys bad after ObjsInit"); +} + +////////////////////////////// +// +// Finds a free Obj and a free ObjSpec of the appropriate class, and +// links them together. Fills up the given fields. Returns whether it +// succeeded. +// +uchar ObjAndSpecGrab(ObjClass obclass, ObjID *id, ObjSpecID *specid) { + ObjSpecHeader *head; + + // SpewReport (("ObjAndSpecGrab (class %d)\n", obclass)); + + if ((*id = ObjGrab()) == OBJ_NULL) + return FALSE; + if ((*specid = ObjSpecGrab(obclass)) == OBJ_SPEC_NULL) { + ObjFree(*id); + return FALSE; + } + objs[*id].obclass = obclass; + objs[*id].specID = *specid; + head = &objSpecHeaders[obclass]; + ((ObjSpec *)(head->data + *specid * head->struct_size))->bits.id = *id; + + // temp + // if (!ObjSysOkay()) + // DebugString("Obj Sys bad after ObjAndSpecGrab"); + + return TRUE; +} + +////////////////////////////// +// +// Sets the given fields of an object appropriately, and makes it active. +// Currently always returns TRUE. +// +uchar ObjPlace(ObjID id, ObjLoc *loc) { + // DBG_Report ({ + // char str[80]; + // ObjLocSprint (str, *loc); + // SpewReport (("ObjPlace (%s)\n", str)); + //}) + ObjLocCopy(*loc, objs[id].loc); + + // temp + // if (!ObjSysOkay()) + // DebugString("Obj Sys bad after ObjPlace"); + + return TRUE; +} + +////////////////////////////// +// +// Removes the specified ObjRef from the map, +// destroys its reference to its Obj, +// and then destroys it. +// +// Quite a vicious little function really. +// +ObjID ObjRefDel(ObjRefID ref) { + uchar tmp; + + // SpewReport (("ObjRefDel (ref %d)\n", ref)); + + ObjRefRem(ref); + tmp = ObjRefFree(ref, TRUE); + + // temp + // if (!ObjSysOkay()) + // DebugString("Obj Sys bad after ObjRefDel"); + + return tmp; +} + +////////////////////////////// +// +// Gets a free ObjRef, assigns it to the given Obj, +// and puts it in the world in the given place. +// +// Returns the ObjRefID used, or OBJ_REF_NULL if it +// couldn't do it for any reason. +// +ObjRefID ObjRefMake(ObjID obj, ObjRefState refstate) { + ObjRefID ref; + uchar ok; + + // DBG_Report ({ + // char str[80]; + // ObjRefStateSprint (str, refstate); + // SpewReport (("ObjRefMake (obj %d %s)\n", obj, str)); + //}) + if ((ref = ObjRefGrab()) == OBJ_REF_NULL) + return OBJ_REF_NULL; + + ok = ObjLinkMake(ref, obj); + + // DBG_Check({ + // if (!ok) + // { + // Warning (("Could not make link from ObjRef %d to Obj %d\n", ref, obj)); + // ObjRefFree (ref, TRUE); + // return OBJ_REF_NULL; + // } + //}) + + ok = ObjRefAdd(ref, refstate); + + // DBG_Check({ + // if (!ok) + // { + // char str[80]; + // ObjRefStateSprint (str, refstate); + // Warning (("Could not add ObjRef %d to %s\n", ref, str)); + // ObjRefFree (ref, TRUE); + // return OBJ_REF_NULL; + // } + //}) + + // temp + // if (!ObjSysOkay()) + // DebugString("Obj Sys bad after ObjRefMake"); + + return ref; +} + +////////////////////////////// +// +// Deletes the given obj and all its references. +// Returns whether success was achieved in this quest. +// +uchar ObjDel(ObjID obj) { + uchar ok; + + // SpewReport (("ObjDel (obj %d)\n", obj)); + + ok = ObjDelRefs(obj); + + // DBG_Check ({ + // if (!ok) + // { + // Warning (("Could not delete refs to Obj %d\n", obj)); + // return FALSE; + // } + //}) + + ok = ObjAndSpecFree(obj); + + // DBG_Check ({ + // if (!ok) + // { + // Warning (("Could not free obj & spec for Obj %d\n", obj)); + // return FALSE; + // } + //}) + + // temp + // if (!ObjSysOkay()) + // DebugString("Obj Sys bad after ObjDel"); + + return TRUE; +} + +////////////////////////////// +// +// Updates the location of a moving object. +// Returns whether it was successful in doing so. +// +// The given ObjLocState contains information about the new location of the +// object. We know the old location of the object by following ref pointers +// around. This function updates all the ObjRefs's referring to that object +// correctly. +// +uchar ObjUpdateLocs(ObjLocState *olsp) { + ObjID obj; // this object + ObjRefID ref; // each reference of it + ObjRefID firstref; // first ref that refers to it + ObjRefState *newrefs; // squares the object will soon be in + ObjRefState in[MAX_REFS_PER_OBJ]; // places moved into + ObjRefID out[MAX_REFS_PER_OBJ]; // old references of the object + int incount = 0; // number of places moved into + int outcount = 0; // number of places moved out of + int newcount; // # of new square being moved into + ObjRefState *stCur; // current place being checked + int i; // loopy loopy + + // SpewReport (("ObjUpdateLocs (obj %d)\n", olsp->obj)); + // DBG_Report ({ + // char str[80]; + // newrefs = olsp->refs; + // while (!ObjRefStateBinCheckNull(newrefs->bin)) + // { + // ObjRefStateSprint (str, *newrefs); + // SpewReport (("[%s] ", str)); + // newrefs++; + // } + // SpewReport (("\n")); + // }) + + // Get some data first + obj = olsp->obj; + newrefs = olsp->refs; + ObjLocCopy(olsp->loc, objs[obj].loc); + + // Figure out where we are now + outcount = 0; + firstref = ref = objs[obj].ref; + + if (firstref != OBJ_REF_NULL) { + // We use a do-while so that we don't fail the loop condition + // at the very beginning of the loop. + do { + out[outcount++] = ref; + ref = objRefs[ref].nextref; + } while (ref != firstref); + } + + // Now, for each square we will be in, + // + // - if the bin is in out[], then we are staying in it; + // update its info field and then delete it from out[] + // + // - if the bin is not in out[], then we are moving into it; + // add it to in[] + // + // At the end of the following loop, all the bins we are moving out of + // will be in out[], and all the bins we are moving into will be in in[]. + + incount = 0; + for (newcount = 0; !ObjRefStateBinCheckNull(newrefs[newcount].bin); newcount++) { + stCur = &newrefs[newcount]; + + // Check all bins in out[] for a match + for (i = 0; i < outcount; i++) { + if (ObjRefStateBinEqual(objRefs[out[i]].state.bin, stCur->bin)) { +#ifndef NO_OBJ_REF_STATE_INFO + objRefs[out[i]].state.info = stCur->info; // update info +#endif + out[i] = out[--outcount]; // delete this bin from out + goto found_bin_in_out; // go back to the outer loop + } + } + + // this bin was not in out[], so we add it to in[]. + in[incount++] = *stCur; + + found_bin_in_out:; + } + + // Now we must delete the references in out[] and add the ones in in[]. + // Note that we might be able to speed up the following code if we had to + // by, instead of freeing RefID's and then grabbing them again, reusing + // them somehow. It is unclear how often both out[] and in[] will have + // stuff in them; if this case occurs often it would probably be worth + // speeding up. + + for (i = 0; i < outcount; i++) + ObjRefDel(out[i]); + + for (i = 0; i < incount; i++) + if (!ObjRefMake(obj, in[i])) + return FALSE; + + // temp + // if (!ObjSysOkay()) + // DebugString("Obj Sys bad after ObjUpdateLocs"); + + return TRUE; +} + +#ifdef HASH_OBJECTS +////////////////////////////// +// +// Return a free hash table element, or 0 if none. +// +ObjHashElemID ObjGrabHashEntry(void) { + ObjHashElemID elem = objHashTable[0].next; + + if (elem == 0) + return elem; + objHashTable[0].next = objHashTable[elem].next; + return elem; +} + +////////////////////////////// +// +// Free up the given hash table element. +// +void ObjFreeHashEntry(ObjHashElemID elem) { + objHashTable[elem].ref = 0; // make sure nobody thinks + // there's something here + objHashTable[elem].next = objHashTable[0].next; + objHashTable[0].next = elem; +} + +////////////////////////////// +// +// Returns the ID of a hash entry pointing to the contents of the given bin. +// If create is true, then return an entry even if once doesn't exist now. +// It will then have a ref field of NULL, signifying that it was just +// created. You must immediately set the ref's StateBin correctly. +// +#ifdef USE_FUNCTION_FOR_HASH_GET +ObjHashElemID ObjGetHashElem(ObjRefStateBin bin, uchar create) { + ObjHashElemID entry; + ObjHashElemID firstentry, nextentry; + + entry = OBJ_HASH_FUNC(bin); + if (objHashTable[entry].ref == OBJ_REF_NULL) { + // Nothing at all at this hash location + if (!create) + return 0; + else + return entry; + } + + // Check if the first element is correct + if (ObjRefStateBinEqual(objRefs[objHashTable[entry].ref].state.bin, bin)) + return entry; + + // Go through the list looking for the right object chain + firstentry = entry; + while (objHashTable[entry].next != 0) { + nextentry = objHashTable[entry].next; + if (ObjRefStateBinEqual(objRefs[objHashTable[nextentry].ref].state.bin, bin)) { + ObjRefID tmpref; + + // Move nextentry to the top + tmpref = objHashTable[firstentry].ref; + objHashTable[firstentry].ref = objHashTable[nextentry].ref; + objHashTable[nextentry].ref = tmpref; + return firstentry; + } + entry = nextentry; + } + + // Couldn't find it + if (!create) + return 0; + + // Make a new one + if ((nextentry = ObjGrabHashEntry()) == 0) + return 0; + objHashTable[entry].next = nextentry; + objHashTable[nextentry].ref = objHashTable[firstentry].ref; + objHashTable[nextentry].next = 0; + objHashTable[firstentry].ref = OBJ_REF_NULL; + return firstentry; +} +#else +////////////////////////////// +// +// This is just the special case of the ObjGetHashElem() function, which is now +// called from the macro version of ObjGetHashElem() when it sees that it needs +// it. At this point, the entry is used, but not by us, so we go down the +// chain looking for the right entry. +// +ObjHashElemID ObjGetHashElemFromChain(ObjRefStateBin bin, uchar create, ObjHashElemID firstentry) { + ObjHashElemID entry = firstentry; + ObjHashElemID nextentry; + + // Go through the list looking for the right object chain + while (objHashTable[entry].next != 0) { + nextentry = objHashTable[entry].next; + if (ObjRefStateBinEqual(objRefs[objHashTable[nextentry].ref].state.bin, bin)) { + ObjRefID tmpref; + + // Move nextentry to the top + tmpref = objHashTable[firstentry].ref; + objHashTable[firstentry].ref = objHashTable[nextentry].ref; + objHashTable[nextentry].ref = tmpref; + return firstentry; + } + entry = nextentry; + } + + // Couldn't find it + if (!create) + return 0; + + // Make a new one + if ((nextentry = ObjGrabHashEntry()) == 0) + return 0; + objHashTable[entry].next = nextentry; + objHashTable[nextentry].ref = objHashTable[firstentry].ref; + objHashTable[nextentry].next = 0; + objHashTable[firstentry].ref = OBJ_REF_NULL; + return firstentry; +} +#endif + +////////////////////////////// +// +// Deletes the entry in the hash table corresponding to the ref +// chain at the given bin. Returns FALSE if there was nothing to delete. +// +uchar ObjDeleteHashElem(ObjRefStateBin bin) { + ObjHashElemID firstentry = OBJ_HASH_FUNC(bin); + ObjHashElemID entry, nextentry; + + if (objHashTable[firstentry].ref == OBJ_REF_NULL) + return FALSE; + + // This should be true if we always move to the front of the list like above + if (ObjRefStateBinEqual(objRefs[objHashTable[firstentry].ref].state.bin, bin)) { + if ((nextentry = objHashTable[firstentry].next) == 0) { + // This was the only one + objHashTable[firstentry].ref = OBJ_REF_NULL; + return TRUE; + } + objHashTable[firstentry].ref = objHashTable[nextentry].ref; + objHashTable[firstentry].next = objHashTable[nextentry].next; + ObjFreeHashEntry(nextentry); + return TRUE; + } + + entry = firstentry; + nextentry = objHashTable[entry].next; + while (nextentry != 0) { + if (ObjRefStateBinEqual(objRefs[objHashTable[nextentry].ref].state.bin, bin)) { + objHashTable[entry].next = objHashTable[nextentry].next; + ObjFreeHashEntry(nextentry); + return TRUE; + } + entry = nextentry; + nextentry = objHashTable[entry].next; + } + return FALSE; +} + +static int hash_i; + +void ObjHashIteratorInit(void) { hash_i = 0; } + +uchar ObjHashIterator(ObjRefID *ref) { + while (hash_i < OBJ_HASH_ENTRIES && objHashTable[hash_i].ref == 0) + hash_i++; + if (hash_i == OBJ_HASH_ENTRIES) + return FALSE; + *ref = objHashTable[hash_i++].ref; + return TRUE; +} + +#define MAX_CHAIN_LENGTH 10 // let's hope it gets no higher +int numlengths[MAX_CHAIN_LENGTH + 1]; +////////////////////////////// +// +// +// +ObjHashStats(void) { + int i; + + for (i = 0; i <= MAX_CHAIN_LENGTH; i++) + numlengths[i] = 0; + for (i = OBJ_HASH_HEAD_ENTRIES_START; i < OBJ_HASH_ENTRIES; i++) { + int j = i, length = 0; + if (objHashTable[i].ref != 0) { + while (j != 0) + length++, j = objHashTable[j].next; + } + if (length > MAX_CHAIN_LENGTH) + length = MAX_CHAIN_LENGTH; + numlengths[length]++; + } + for (i = 0; i <= MAX_CHAIN_LENGTH; i++) { + if (numlengths[i] > 0) + SpewHash(("Object hash chains of length %d: %d\n", i, numlengths[i])); + } +} + +#endif // HASH_OBJECTS + + //////////////////////////////////////////////////////////// + // + // DEBUGGING FUNCTIONS + // + //////////////////////////////////////////////////////////// + +#define OBJ_NO_STATE 0 +#define OBJ_FREE 1 +#define OBJ_USED 2 +#define OBJ_IN_MAP 4 + +////////////////////////////// +// +// Check whether the object system is consistent. +// +uchar ObjSysOkay(void) { + char usedObj[NUM_OBJECTS]; + char usedRef[NUM_REF_OBJECTS]; + ObjID cur; + ObjSpecHeader *head; + int i, j; + ObjRefStateBin refbin; + ObjRefID ref; + + // 1. Every Obj is in either the free chain or the used chain, + // and does not appear twice in any chain. + + LG_memset(usedObj, 0, NUM_OBJECTS); + + // SpewAnal (("Free Objs: ")); + // DBG_Anal ({RangeInit ();}) + + cur = objs[OBJ_NULL].next; + while (cur) { + // DBG_Anal ({RangeAdd (cur);}) + if (cur < 0 || cur >= NUM_OBJECTS) { + // DBG_Anal ({RangeFlush ();}) SpewAnal (("%s\n", range_str)); + DEBUG("%s: Invalid ID in free chain", __FUNCTION__); // cur + return FALSE; + } + if (objs[cur].active) { + // DBG_Anal ({RangeFlush ();}) SpewAnal (("%s\n", range_str)); + DEBUG("%s: Active Obj in free chain", __FUNCTION__); // objs[cur] + return FALSE; + } + if (usedObj[cur] == OBJ_FREE) { + // DBG_Anal ({RangeFlush ();}) SpewAnal (("%s\n", range_str)); + DEBUG("%s: Obj is free more than once", __FUNCTION__); // usedObj[cur] + return FALSE; + } + usedObj[cur] = OBJ_FREE; + cur = objs[cur].next; + } + // DBG_Anal ({RangeFlush ();}) SpewAnal (("%s\n", range_str)); + + // SpewAnal (("Used Objs: ")); + // DBG_Anal ({RangeInit ();}) + + cur = objs[OBJ_NULL].ref; + while (cur) { + // DBG_Anal ({RangeAdd (cur);}) + if (cur < 0 || cur >= NUM_OBJECTS) { + // DBG_Anal ({RangeFlush ();}) SpewAnal (("%s\n", range_str)); + DEBUG("%s: Invalid ID in Obj used chain", __FUNCTION__); // cur + return FALSE; + } + if (usedObj[cur] == OBJ_FREE) { + // DBG_Anal ({RangeFlush ();}) SpewAnal (("%s\n", range_str)); + DEBUG("%s: Obj is free and used chain", __FUNCTION__); // cur + return FALSE; + } + if (usedObj[cur] == OBJ_USED) { + // DBG_Anal ({RangeFlush ();}) SpewAnal (("%s\n", range_str)); + DEBUG("%s: Obj is used twice", __FUNCTION__); // cur + return FALSE; + } + usedObj[cur] = OBJ_USED; + cur = objs[cur].next; + } + // DBG_Anal ({RangeFlush ();}) SpewAnal (("%s\n", range_str)); + + for (cur = 1; cur < NUM_OBJECTS; cur++) { + if (usedObj[cur] == 0) { + DEBUG("%s: Obj is neither free nor used", __FUNCTION__); // cur + return FALSE; + } + } + + // 3. No ObjRef occurs twice in the free chain. + + LG_memset(usedRef, 0, NUM_REF_OBJECTS); + + cur = objRefs[OBJ_REF_NULL].next; + + // SpewAnal (("Free ObjRefs: ")); + // DBG_Anal ({RangeInit ();}) + + while (cur) { + // DBG_Anal ({RangeAdd (cur);}) + if (cur < 0 || cur >= NUM_REF_OBJECTS) { + // DBG_Anal ({RangeFlush ();}) SpewAnal (("%s\n", range_str)); + DEBUG("%s: Invalid ID in ObjRef free chain", __FUNCTION__); // cur + return FALSE; + } + if (usedRef[cur] == OBJ_FREE) { + // DBG_Anal ({RangeFlush ();}) SpewAnal (("%s\n", range_str)); + DEBUG("%s: ObjRef is free more than once", __FUNCTION__); // cur + return FALSE; + } + usedRef[cur] = OBJ_FREE; + cur = objRefs[cur].next; + } + // DBG_Anal ({RangeFlush ();}) SpewAnal (("%s\n", range_str)); + + // 2. Every ObjSpec is in either the free chain or the used chain, and does + // not appear twice in any chain. + + for (i = CLASS_FIRST; i < NUM_CLASSES; i++) { + LG_memset(usedObj, 0, NUM_OBJECTS); + head = &objSpecHeaders[i]; + cur = ((ObjSpec *)head->data)->next; + while (cur) { + if (cur < 0 || cur >= head->size) { + DEBUG("%s: Invalid ID (cur) in Class (i) free chain", __FUNCTION__); + return FALSE; + } + if (usedObj[cur] == OBJ_FREE) { + DEBUG("%s: Class (i) ObjSpec (cur) is free more than once", __FUNCTION__); + return FALSE; + } + usedObj[cur] = OBJ_FREE; + cur = ((ObjSpec *)(head->data + cur * head->struct_size))->next; + } + cur = ((ObjSpec *)head->data)->bits.id; + while (cur) { + if (cur < 0 || cur >= head->size) { + DEBUG("%s: Invalid ID (cur) in Class (i) used chain", __FUNCTION__); + return FALSE; + } + if (usedObj[cur] == OBJ_FREE) { + DEBUG("%s: Class (i) ObjSpec (cur) is free and used", __FUNCTION__); + return FALSE; + } + if (usedObj[cur] == OBJ_USED) { + DEBUG("%s: Class (i) ObjSpec (cur) is used twice", __FUNCTION__); + return FALSE; + } + usedObj[cur] = OBJ_USED; + cur = ((ObjSpec *)(head->data + cur * head->struct_size))->next; + } + for (cur = 1; cur < head->size; cur++) { + if (usedObj[cur] == 0) { + DEBUG("%s: Class (i) ObjSpec (cur) is neither free nor used", __FUNCTION__); + return FALSE; + } + } + } + + // 4. All active ObjRefs occur exactly once in the map. + // 5. All active ObjRefs point to the map element in which they occur. + // 6. All active ObjRefs point to active Objs. + +#ifdef HASH_OBJECTS + ObjHashIteratorInit(); +#else + ObjRefStateBinIteratorInit(); +#endif + +#ifdef HASH_OBJECTS + while (ObjHashIterator(&ref)) +#else + while (ObjRefStateBinIterator(&refbin)) +#endif + { +#ifdef HASH_OBJECTS + refbin = objRefs[ref].state.bin; +#else + ref = ObjRefHead(refbin); +#endif + + // DBG_Anal ({ + // if (ref != OBJ_REF_NULL) + // { + // ObjRefStateBinSprint (str, refbin); + // SpewAnal (("Contents of [%s]: ", str)); + // } + // }) + + while (ref != OBJ_REF_NULL) { +#ifndef NO_OBJ_REF_STATE_INFO +// DBG_Anal ({ +// ObjRefStateInfoSprint (str, objRefs[ref].state.info); +// SpewAnal ((" ObjRef %3d [%s] -> Obj %3d\n", ref, str, objRefs[ref].obj)); +// }) +#else +// DBG_Anal ({ +// SpewAnal ((" ObjRef %3d -> Obj %3d\n", ref, objRefs[ref].obj)); +// }) +#endif + + if (usedRef[ref] & OBJ_FREE) { + ObjRefStateBinSprint(str, refbin); + DEBUG("%s: ObjRef (ref) is free and in map bin (str)", __FUNCTION__); + return FALSE; + } + + if (usedRef[ref] & OBJ_IN_MAP) { + ObjRefStateBinSprint(str, refbin); + DEBUG("%s: ObjRef (ref) exists in two bins (one is str)", __FUNCTION__); + return FALSE; + } + + if (!ObjRefStateBinEqual(objRefs[ref].state.bin, refbin)) { + ObjRefStateBinSprint(str, refbin); + ObjRefStateBinSprint(str2, objRefs[ref].state.bin); + DEBUG("%s: ObjRef (ref) thinks it is in (str2) but is in (str)", __FUNCTION__); + return FALSE; + } + + if (objRefs[ref].obj == OBJ_NULL) { + ObjRefStateBinSprint(str, refbin); + DEBUG("%s: ObjRef (ref) in (str) points to null Obj", __FUNCTION__); + return FALSE; + } + + usedRef[ref] |= OBJ_IN_MAP; + + ref = objRefs[ref].next; + } + } + + // 7. All ObjRefs referring to a single Obj reside in distinct map elements. + // 8. The links between an Obj and its ObjSpec are valid. + // 9. The links between an Obj and its ObjRefs are valid. + + cur = objs[OBJ_NULL].headused; + while (cur != OBJ_NULL) { + ObjRefStateBin refbins[MAX_REFS_PER_OBJ]; + ObjRefID firstref; + ObjRefID ref; + int i = 0; + + if (!objs[cur].active) + goto done_checking_obj; + + ref = firstref = objs[cur].ref; + if (ref == OBJ_REF_NULL) + goto done_checking_refs; + + do { + if (i == MAX_REFS_PER_OBJ) { + DEBUG("%s: Too many ObjRefs refer to Obj (cur)", __FUNCTION__); + return FALSE; + } + + if (objRefs[ref].obj != cur) { + DEBUG("%s: Obj (cur) points to ObjRef (ref) but not vice versa", __FUNCTION__); + return FALSE; + } + + for (j = 0; j < i; j++) + if (ObjRefStateBinEqual(refbins[j], objRefs[ref].state.bin)) + return FALSE; + + refbins[i++] = objRefs[ref].state.bin; + ref = objRefs[ref].nextref; + } while (ref != firstref); + + done_checking_refs: + + if (objs[cur].specID != OBJ_SPEC_NULL) { + char *data; + ObjSpecHeader *head; + + if (objs[cur].obclass < 0 || objs[cur].obclass >= NUM_CLASSES) { + DEBUG("%s: Obj (cur) has invalid obclass (objs[cur].obclass)", __FUNCTION__); + return FALSE; + } + + head = &objSpecHeaders[objs[cur].obclass]; + data = head->data; + + if (((ObjSpec *)(data + head->struct_size * objs[cur].specID))->bits.id != cur || + ((ObjSpec *)(data + head->struct_size * objs[cur].specID))->bits.tile == TRUE) { + DEBUG("%s: Obj (cur) obclass-specific data does not point back to it", __FUNCTION__); + return FALSE; + } + } + + done_checking_obj: + + cur = objs[cur].next; + } + +#ifdef HASH_OBJECTS + DBG_Hash({ ObjHashStats(); }) +#endif + + return TRUE; +} + +//////////////////////////////////////////////////////////// +// +// STATIC FUNCTIONS +// +// Some of these may want to become public at some point. +// However, before making one of these functions public, first +// make sure that there isn't already a public interface that you +// can use instead. +// +//////////////////////////////////////////////////////////// + +////////////////////////////// +// +// Returns a ObjID that is not currently being used +// or OBJ_NULL If there are none +// +// The caller is responsible for setting the fieds of the Obj, including active. +// This function does clear the ref field to ensure we don't follow bad pointers around. +// +static ObjID ObjGrab(void) { + ObjID obj; // the next free ObjID + + // SpewReport (("ObjGrab ()\n")); + + if (objs[OBJ_NULL].next == OBJ_NULL) // all gone + { + // Warning (("No room in ObjGrab\n")); + return OBJ_NULL; + } + + // remove the head of the free chain and return it + obj = objs[OBJ_NULL].next; + objs[OBJ_NULL].next = objs[obj].next; + + // and put obj at the head of the used chain + objs[obj].next = objs[OBJ_NULL].headused; + objs[objs[OBJ_NULL].headused].prev = obj; + objs[obj].prev = OBJ_NULL; + objs[OBJ_NULL].headused = obj; + + objs[obj].ref = OBJ_REF_NULL; + + ObjInfoInit(&objs[obj].info); + + return obj; +} + +////////////////////////////// +// +// Frees up the given object +// Returns FALSE (and refuses to free the object) +// if there are objRefs referring to the object +// +static uchar ObjFree(ObjID obj) { + // SpewReport (("ObjFree (obj %d)\n", obj)); + + // DBG_Check ({ + // // Check if something still depends on this to be valid + // if (objs[obj].active && objs[obj].ref != OBJ_REF_NULL) + // { + // Warning (("Tried to free obj %d which was depended on\n", obj)); + // return FALSE; + // } + //}) + + objs[obj].active = FALSE; + + // take obj out of the used chain + if (objs[obj].prev == OBJ_NULL) + objs[OBJ_NULL].headused = objs[obj].next; + else + objs[objs[obj].prev].next = objs[obj].next; + + objs[objs[obj].next].prev = objs[obj].prev; + // don't need to clear objs[obj].next since we are resetting it immediately + + // and put it back at the head of the free chain + objs[obj].next = objs[OBJ_NULL].next; + objs[OBJ_NULL].next = obj; + + return TRUE; +} + +////////////////////////////// +// +// Returns an ObjRefID that is not currently being used +// or OBJ_REF_NULL if there are none +// +static ObjRefID ObjRefGrab(void) { + ObjRefID thisobj; // the next free ObjRefID + + // SpewReport (("ObjRefGrab ()\n")); + + if (objRefs[OBJ_REF_NULL].next == OBJ_REF_NULL) { + // Warning (("No room in ObjRefGrab\n")); + return OBJ_REF_NULL; + } + + // remove the head of the free chain and return it + thisobj = objRefs[OBJ_REF_NULL].next; + objRefs[OBJ_REF_NULL].next = objRefs[thisobj].next; + + objRefs[thisobj].obj = OBJ_NULL; + objRefs[thisobj].next = OBJ_REF_NULL; + objRefs[thisobj].nextref = OBJ_REF_NULL; + + return thisobj; +} + +////////////////////////////// +// +// Frees up the space used by the ObjRef referred to by ref. +// +// If cleanup is TRUE, also deletes the reference of the ObjRef to the Obj. +// If this is the last reference to an Obj, returns that Obj's ID. +// +static ObjID ObjRefFree(ObjRefID ref, uchar cleanup) { + ObjID obj; // the object ref refers to + + // SpewReport (("ObjRefFree (ref %d cleanup %d)\n", ref, cleanup)); + + if (cleanup) + obj = ObjRefLinkDel(ref); // remove the link + else + obj = OBJ_REF_NULL; + objRefs[ref].next = objRefs[OBJ_NULL].next; + objRefs[OBJ_REF_NULL].next = ref; + + return obj; +} + +////////////////////////////// +// +// Returns an ObjSpecID of the specified obclass that is not currently being +// used, or OBJ_SPEC_NULL if there are none available +// +static ObjSpecID ObjSpecGrab(ObjClass obclass) { + char *data; + ObjSpecHeader *head; + ObjSpecID thisid; + ObjSpec *spec0, *thisspec; + + // SpewReport (("ObjSpecGrab (obclass %d)\n", obclass)); + + // DBG_Check ({ + // if (obclass >= NUM_CLASSES) + // { + // Warning (("Invalid obclass %d in ObjSpecGrab\n", obclass)); + // return OBJ_SPEC_NULL; + // } + //}) + + head = &objSpecHeaders[obclass]; + data = head->data; + spec0 = (ObjSpec *)data; + + if (spec0->next == OBJ_SPEC_NULL) + return OBJ_SPEC_NULL; + + // remove the head of the free chain and return it + thisid = spec0->next; + thisspec = (ObjSpec *)(data + head->struct_size * thisid); + spec0->next = thisspec->next; + + // and put this at the head of the used chain + thisspec->next = spec0->headused; + ((ObjSpec *)(data + head->struct_size * spec0->headused))->prev = thisid; + thisspec->prev = OBJ_SPEC_NULL; + spec0->headused = thisid; + + return thisid; +} + +#ifdef COMPRESS_OBJSPECS +ObjSpecID HeaderObjSpecGrab(ObjClass obclass, ObjSpecHeader *head) { + char *data; + ObjSpecID thisid; + ObjSpec *spec0, *thisspec; + + SpewReport(("ObjSpecGrab (obclass %d)\n", obclass)); + + DBG_Check({ + if (obclass >= NUM_CLASSES) { + Warning(("Invalid obclass %d in ObjSpecGrab\n", obclass)); + return OBJ_SPEC_NULL; + } + }) + + data = head->data; + spec0 = (ObjSpec *)data; + + if (spec0->next == OBJ_SPEC_NULL) + return OBJ_SPEC_NULL; + + // remove the head of the free chain and return it + thisid = spec0->next; + thisspec = (ObjSpec *)(data + head->struct_size * thisid); + spec0->next = thisspec->next; + + // and put this at the head of the used chain + thisspec->next = spec0->headused; + ((ObjSpec *)(data + head->struct_size * spec0->headused))->prev = thisid; + thisspec->prev = OBJ_SPEC_NULL; + spec0->headused = thisid; + + return thisid; +} +#endif + +////////////////////////////// +// +// Frees up the space used by the ObjSpec in the specified class +// referred to by id. +// +static uchar ObjSpecFree(ObjClass obclass, ObjSpecID id) { + char *data; + ObjSpecHeader *head; + ObjSpec *spec0, *thisspec; + + // SpewReport (("ObjSpecFree (obclass %d, specid %d)\n", obclass, id)); + + // DBG_Check ({ + // if (obclass >= NUM_CLASSES) + // { + // Warning (("Invalid obclass %d in ObjSpecFree\n", obclass)); + // return FALSE; + // } + //}) + + head = &objSpecHeaders[obclass]; + data = head->data; + spec0 = (ObjSpec *)&data[0]; + thisspec = (ObjSpec *)(data + head->struct_size * id); + + // take this out of the used chain + if (thisspec->prev == OBJ_SPEC_NULL) + spec0->headused = thisspec->next; + else + ((ObjSpec *)(data + head->struct_size * thisspec->prev))->next = thisspec->next; + + ((ObjSpec *)(data + head->struct_size * thisspec->next))->prev = thisspec->prev; + // don't need to clear thisspec->next since we are resetting it immediately + + // and put it back at the head of the free chain + thisspec->next = spec0->headfree; + spec0->headfree = id; + + return TRUE; +} + +#ifdef COMPRESS_OBJSPECS +uchar HeaderObjSpecFree(ObjClass obclass, ObjSpecID id, ObjSpecHeader *head) { + char *data; + ObjSpec *spec0, *thisspec; + + SpewReport(("ObjSpecFree (obclass %d, specid %d)\n", obclass, id)); + + DBG_Check({ + if (obclass >= NUM_CLASSES) { + Warning(("Invalid obclass %d in ObjSpecFree\n", obclass)); + return FALSE; + } + }) + + data = head->data; + spec0 = (ObjSpec *)&data[0]; + thisspec = (ObjSpec *)(data + head->struct_size * id); + + // take this out of the used chain + if (thisspec->prev == OBJ_SPEC_NULL) + spec0->headused = thisspec->next; + else + ((ObjSpec *)(data + head->struct_size * thisspec->prev))->next = thisspec->next; + + ((ObjSpec *)(data + head->struct_size * thisspec->next))->prev = thisspec->prev; + // don't need to clear thisspec->next since we are resetting it immediately + + // and put it back at the head of the free chain + thisspec->next = spec0->headfree; + spec0->headfree = id; + + return TRUE; +} +#endif + +#ifdef COMPRESS_OBJSPECS +uchar HeaderObjSpecCopy(ObjClass cls, ObjSpecID old, ObjSpecID new, ObjSpecHeader *head) { + char *data; + ObjSpec *spec0; + int size; + + SpewReport(("ObjSpecCopy (obclass %d)\n", cls)); + + DBG_Check({ + if (cls >= NUM_CLASSES) { + Warning(("Invalid obclass %d in ObjSpecCopy\n", cls)); + return FALSE; + } + }) + + data = head->data; + spec0 = (ObjSpec *)data; + + // the ObjSpec (generic) part of the new spec is already set; we + // need to copy the rest + + if ((size = head->struct_size - sizeof(ObjSpec)) > 0) { + LG_memcpy(data + head->struct_size * new + sizeof(ObjSpec), data + head->struct_size * old + sizeof(ObjSpec), + size); + } + return TRUE; +} +#endif + +////////////////////////////// +// +// Removes the given ObjRef from the object list in a map bin. +// +static void ObjRefRem(ObjRefID ref) { + ObjRefID *ptr; // what we must change to splice ref out +#ifdef HASH_OBJECTS + ObjHashElemID hash_entry; +#endif + + // SpewReport (("ObjRefRem (ref %d)\n", ref)); + +#ifdef HASH_OBJECTS + if ((hash_entry = ObjGetHashElem(objRefs[ref].state.bin, FALSE)) == 0) { + Warning(("Tried to remove ref %d not in hash table in ObjRefRem\n", ref)); + return; + } + + if (objHashTable[hash_entry].ref == ref) { + if (objRefs[ref].next == OBJ_REF_NULL) { + // This was the only one + if (!ObjDeleteHashElem(objRefs[ref].state.bin)) { + Warning(("Couldn't delete refchain %d from hash table in ObjRefRem\n", ref)); + return; + } + } else { + objHashTable[hash_entry].ref = objRefs[ref].next; + } + objRefs[ref].next = OBJ_REF_NULL; // we are no longer in a chain + ObjRefStateBinSetNull(objRefs[ref].state.bin); // we are no longer in the world + return; + } + + ptr = &objRefs[objHashTable[hash_entry].ref].next; // next field of head of ref chain +#else + ptr = &ObjRefHead(objRefs[ref].state.bin); +#endif + + while (*ptr != ref) + ptr = &(objRefs[*ptr].next); + + // ptr is now the address of an ObjRefID equal to ref. + // It could be the address of the obj field of a MapPoint + // or the address of the next field of an ObjRef + // Got it? + + *ptr = objRefs[ref].next; // we have spliced ref out + objRefs[ref].next = OBJ_REF_NULL; // we are no longer in a chain + ObjRefStateBinSetNull(objRefs[ref].state.bin); // we are no longer in the world +} + +////////////////////////////// +// +// Makes ref be a reference to obj +// Returns whether we could do it +// +static uchar ObjLinkMake(ObjRefID ref, ObjID obj) { + // SpewReport (("ObjLinkMake (ref %d, obj %d)\n", ref, obj)); + + // check that obj is a real object + // DBG_Check ({ + // if (objs[obj].active == FALSE) + // { + // Warning (("Obj %d is inactive in ObjLinkMake\n", obj)); + // return FALSE; + // } + //}) + + if (objs[obj].ref == OBJ_REF_NULL) { + // we are the first ObjRef to refer to obj + objs[obj].ref = ref; + objRefs[ref].nextref = ref; + } else { + // there are other references already + objRefs[ref].nextref = objRefs[objs[obj].ref].nextref; + objRefs[objs[obj].ref].nextref = ref; + } + + // add the reference + objRefs[ref].obj = obj; + return TRUE; +} + +////////////////////////////// +// +// Deletes the references of ref to its obj +// If this was the last reference to that obj, +// returns its ID, otherwise returns OBJ_NULL +// +static ObjID ObjRefLinkDel(ObjRefID ref) { + ObjID obj; + ObjRefID curref; + + // SpewReport (("ObjRefLinkDel (ref %d)\n", ref)); + + obj = objRefs[ref].obj; + + // DBG_Check ({ + // // make sure we're actually referring to something + // if (obj == OBJ_NULL) + // { + // Warning (("ref %d refers to nothing in ObjRefLinkDel\n", ref)); + // return OBJ_NULL; + // } + //}) + + if (objRefs[ref].nextref == ref) // last one + { + objs[obj].ref = OBJ_REF_NULL; + return obj; + } else { + // run around the circular list until we reach ourselves + // and splice ourselves out + curref = ref; + while (objRefs[curref].nextref != ref) + curref = objRefs[curref].nextref; + objRefs[curref].nextref = objRefs[ref].nextref; + objs[obj].ref = curref; // easier than checking objs[obj].ref + } + + // delete the reference + objRefs[ref].obj = OBJ_NULL; + return OBJ_NULL; +} + +////////////////////////////// +// +// Takes an ObjRef and adds it to the real world at the given place. +// This ObjRef must already refer to a valid Obj. +// Returns whether everything worked okay. +// +static uchar ObjRefAdd(ObjRefID ref, ObjRefState refstate) { + ObjID obj; + ObjRefID *refhead; +#ifdef HASH_OBJECTS + ObjHashElemID hash_entry; +#endif + + // DBG_Report ({ + // char str[80]; + // ObjRefStateSprint (str, refstate); + // SpewReport (("ObjRefAdd (ref %d, %s)\n", ref, str)); + // }) + + obj = objRefs[ref].obj; + + // DBG_Check ({ + // // make sure we point to a valid object + // if (obj == OBJ_NULL || objs[obj].active == FALSE) + // { + // Warning (("Ref %d points to invalid Obj %d in ObjRefAdd\n", ref, obj)); + // return FALSE; + // } + //}) + +#ifdef HASH_OBJECTS + if ((hash_entry = ObjGetHashElem(refstate.bin, TRUE)) == 0) { + char str[80]; + ObjRefStateBinSprint(str, refstate.bin); + Warning(("Could not create hash entry at %s in ObjRefAdd\n", str)); + return FALSE; + } + refhead = &objHashTable[hash_entry].ref; +#else + refhead = &ObjRefHead(refstate.bin); +#endif + + objRefs[ref].next = *refhead; + *refhead = ref; + + ObjRefStateBinCopy(refstate.bin, objRefs[ref].state.bin); +#ifndef NO_OBJ_REF_STATE_INFO + ObjRefStateInfoCopy(refstate.info, objRefs[ref].state.info); +#endif + + return TRUE; +} + +////////////////////////////// +// +// Deletes all references to a given object. +// Returns whether the object really exists or not. +// +// NOTE: This is currently much slower than it could be because +// we delete references one at a time and clean up each time. +// +static uchar ObjDelRefs(ObjID obj) { + ObjRefID ref; + ObjRefID nextref = OBJ_REF_NULL; + + // SpewReport (("ObjDelRefs (obj %d)\n", obj)); + + // DBG_Check ({ + // if (objs[obj].active == FALSE) + // { + // Warning (("Obj %d inactive in ObjDelRefs\n", obj)); + // return FALSE; + // } + //}) + + ref = objs[obj].ref; + if (ref == OBJ_REF_NULL) + return TRUE; + + while (TRUE) { + nextref = objRefs[ref].nextref; + ObjRefRem(ref); + // objRefs[ref].obj = OBJ_NULL; + ObjRefFree(ref, TRUE); + if (ref == nextref) + break; + ref = nextref; + } + + objs[obj].ref = OBJ_REF_NULL; + return TRUE; +} + +////////////////////////////// +// +// Frees up the given object and its class-specific data. +// Returns FALSE (and refuses to free the object) +// if there are ObjRefs referring to the object. +// +static uchar ObjAndSpecFree(ObjID obj) { + ObjClass obclass; + ObjSpecID specID; + uchar ok; + + // SpewReport (("ObjAndSpecFree (obj %d)\n", obj)); + + // The only place that things should be able to go wrong is in ObjFree, + // so we do that first + + obclass = objs[obj].obclass; + specID = objs[obj].specID; + + ok = ObjFree(obj); + + // DBG_Check ({ + // if (!ok) + // { + // Warning (("ObjFree (%d) failed in ObjAndSpecFree\n",obj)); + // return FALSE; + // } + //}) + + ok = ObjSpecFree(obclass, specID); + + // DBG_Check ({ + // if (!ok) + // { + // Warning (("ObjSpecFree (%d, %d) failed in ObjAndSpecFree\n", obclass, specID)); + // return FALSE; + // } + //}) + + return TRUE; +} diff --git a/engine/src/GameSrc/objload.c b/engine/src/GameSrc/objload.c new file mode 100644 index 0000000..e872da1 --- /dev/null +++ b/engine/src/GameSrc/objload.c @@ -0,0 +1,280 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include + +#include "Shock.h" + +#include "criterr.h" +#include "objects.h" +#include "objload.h" +#include "player.h" +#include "objart.h" +#include "citres.h" +#include "treasure.h" +#include "otrip.h" +#include "gameobj.h" +#include "statics.h" +#include "cybmem.h" + +#define OBJECT_ART_BASE RES_bmObjectIcons + +errtype voxel_convert(grs_bitmap *bmp); +void load_treasure_table(uchar *loadme, char cp); +void compute_complex_loadage(uchar *loadme); +grs_bitmap *get_objbitmap_from_pool(int i, uchar t); + +// Transform the bitmap from a greyscale drawing to an actual 0-16 depth map +// For now we simply do this by subtracting 208 +errtype voxel_convert(grs_bitmap *bmp) { + int x, y; + for (x = 0; x < bmp->w; x++) + for (y = 0; y < bmp->h; y++) + if (bmp->bits[(y * bmp->w) + x] != 0) + bmp->bits[(y * bmp->w) + x] -= 208; + return (OK); +} + +void load_treasure_table(uchar *loadme, char cp) { + int j, t0, t1; + for (j = 0; j < NUM_TREASURE_SLOTS; j++) { + t0 = treasure_table[cp][j][0]; + t1 = treasure_table[cp][j][1]; + if (t0 == 0) + break; + if (t1 == NOTHING_TRIPLE) + continue; + ObjLoadMeSet(OPTRIP(t1)); + } +} + +#define FIRST_CORPSE_TTYPE 11 + +void compute_complex_loadage(uchar *loadme) { + int i, tr, objtrip; + weapon_slot wpnslot; + + // Figure out what to load + ObjLoadMeClearAll(); + + // If it's on the level, load it, with some specialized hacks + for (ObjID id = (objs[OBJ_NULL]).headused; id != OBJ_NULL; id = objs[id].next) { + objtrip = ID2TRIP(id); + switch (objtrip) { + case ANTENNA_PAN_TRIPLE: + ObjLoadMeSet(OPTRIP(PLAS_ANTENNA_TRIPLE)); + ObjLoadMeSet(OPTRIP(DEST_ANTENNA_TRIPLE)); + break; + case FAUX_X_TRIPLE: + ObjLoadMeSet(OPTRIP(ISOTOPE_X_TRIPLE)); + break; + case ISOTOPE_X_TRIPLE: + ObjLoadMeSet(OPTRIP(FAUX_X_TRIPLE)); + break; + } + ObjLoadMeSet(OPNUM(id)); + } + + // Also load art for things player can carry that aren't + // in the object system but are in the player_struct + for (i = 0; i < NUM_WEAPON_SLOTS; i++) { + wpnslot = player_struct.weapons[i]; + if (wpnslot.type != EMPTY_WEAPON_SLOT) + ObjLoadMeSet(OPTRIP(MAKETRIP(CLASS_GUN, wpnslot.type, wpnslot.subtype))); + } + tr = OPTRIP(MAKETRIP(CLASS_HARDWARE, 0, 0)); + for (i = 0; i < NUM_HARDWAREZ; i++) + if (player_struct.hardwarez[i]) + ObjLoadMeSet(tr + i); + tr = OPTRIP(MAKETRIP(CLASS_AMMO, 0, 0)); + for (i = 0; i < NUM_AMMO_TYPES; i++) + if (player_struct.cartridges[i] || player_struct.partial_clip[i]) + ObjLoadMeSet(tr + i); + tr = OPTRIP(MAKETRIP(CLASS_DRUG, 0, 0)); + for (i = 0; i < NUM_DRUGZ; i++) + if (player_struct.drugs[i]) + ObjLoadMeSet(tr + i); + tr = OPTRIP(MAKETRIP(CLASS_GRENADE, 0, 0)); + for (i = 0; i < NUM_GRENADEZ; i++) + if (player_struct.grenades[i]) + ObjLoadMeSet(tr + i); + + // Load art for things that can get generated over the course of events + // such as explosions and slow projectiles and loot + tr = OPTRIP(MAKETRIP(CLASS_ANIMATING, ANIMATING_SUBCLASS_TRANSITORY, 0)); + for (i = 0; i < NUM_TRANSITORY_ANIMATING + NUM_EXPLOSION_ANIMATING; i++) + ObjLoadMeSet(tr + i); + tr = OPTRIP(MAKETRIP(CLASS_PHYSICS, PHYSICS_SUBCLASS_SLOW, 0)); + for (i = 0; i < NUM_SLOW_PHYSICS; i++) + ObjLoadMeSet(tr + i); + for (i = 0; i < NUM_TREASURE_TYPES; i++) + load_treasure_table(loadme, i); + + // Load corpses for any creatures on the level as well as their loot + tr = OPTRIP(MAKETRIP(CLASS_CRITTER, 0, 0)); + for (i = tr; i < tr + NUM_CRITTER; i++) { + // We have loaded that creature's "art", so load in it's corpse too + if ((ObjLoadMeCheck(i)) && (CritterProps[i - tr].corpse)) + ObjLoadMeSet(OPTRIP(CritterProps[i - tr].corpse)); + } +} + +// Wow, this is stupid, but is really wanted for ease +// of integration +// t is 0 for 2d bitmaps +// 1 for 3d bitmaps +grs_bitmap *get_objbitmap_from_pool(int i, uchar t) { + // Warning(("objbitmap_from_pool (%d, %d, %d vs %d)\n",i,t,(t*NUM_OBJECT) + i,OBJ_BITMAP_POOL_SIZE)); + if (((t * NUM_OBJECT) + i) > OBJ_BITMAP_POOL_SIZE) { + return (NULL); + } + return (&obj_bitmap_pool[(t * NUM_OBJECT) + i]); +} + +// Scan through all objects on the level, load all pertinent art +// and punt all irrelevant art +// NEEDED: empty bitmap support & defaulting? +// bitmap zero support +ulong objart_loadsize = 0; + +#define APPROX_REF_TAB_SIZE 7000 + +static uchar bitmap_zero_loaded = FALSE; + +errtype obj_load_art(uchar flush_all) { + uchar loadme[NUM_OBJECT_BIT_LEN]; + LGRect dummy_anchor; + short objart_count = 0, count_3d = 0; + int objfnum; + short i, f; + uchar ref_buffer_used = TRUE; + + if (flush_all) + ObjLoadMeClearAll(); + else + compute_complex_loadage((uchar *)loadme); + + // If low memory, see if what we are currently trying to do is any + // different at all than current loadage. If yes, flush all first. + // If no, punt out now, duh. + if ((!flush_all) && (start_mem < BIG_CACHE_THRESHOLD)) { + uchar different = FALSE; + for (i = 0; (i < NUM_OBJECT) && !different; i++) { + if ((ObjLoadMeCheck(i) && (bitmaps_2d[i] == NULL)) || (!ObjLoadMeCheck(i) && (bitmaps_2d[i] != NULL))) { + different = TRUE; + } + } + if (different) + obj_load_art(TRUE); + else + return (OK); + } + + // Open the damn file + if (!flush_all) { + objfnum = ResOpenFile("res/data/objart.res"); + if (objfnum < 0) + critical_error(CRITERR_RES | 5); + // Read out the infamous bitmap zero + if (!bitmap_zero_loaded) { + DEBUG("Read Bitmap Zero"); + bitmaps_3d[count_3d] = get_objbitmap_from_pool(count_3d, 1); // KLC - added here. + load_bitmap_from_res(bitmaps_3d[count_3d], OBJECT_ART_BASE, objart_count++, TRUE, &dummy_anchor); + bitmap_zero_loaded = TRUE; + objart_loadsize += bitmaps_3d[count_3d]->w * bitmaps_3d[count_3d]->h; + anchors_3d[count_3d] = dummy_anchor.ul; + } else + objart_count++; + } + count_3d++; + + // Iterate through all the art, picking out what we like + // and skipping over what we dont (also freeing any memory that + // we grabbed for earlier levels that is now irrelevant). + // Make sure to skip over things already loaded, etc. + for (i = 0; i < NUM_OBJECT; i++) { + if (ObjLoadMeCheck(i)) { + // mprintf("load bitmaps_2d[%d] = %x count_3d = %x ObjProps[%d].bitmap_3d = + // %x\n",i,bitmaps_2d[i],count_3d,i, + // ObjProps[i].bitmap_3d); + // If we're not in memory, load us + if (bitmaps_2d[i] == NULL) { + bitmaps_2d[i] = get_objbitmap_from_pool(i, 0); + load_bitmap_from_res(bitmaps_2d[i], OBJECT_ART_BASE, objart_count++, TRUE, &dummy_anchor); + objart_loadsize += bitmaps_2d[i]->w * bitmaps_2d[i]->h; + ObjProps[i].bitmap_3d = ObjProps[i].bitmap_3d | count_3d; + for (f = 0; f < (FRAME_NUM_3D(ObjProps[i].bitmap_3d) + 1); f++) { + uchar not_using_2d = FALSE; + + if ((f == 0) && (empty_bitmap(bitmaps_2d[i]))) { + objart_loadsize -= bitmaps_2d[i]->w * bitmaps_2d[i]->h; + free(bitmaps_2d[i]->bits); + not_using_2d = TRUE; + } + + bitmaps_3d[count_3d] = get_objbitmap_from_pool(count_3d, 1); + load_bitmap_from_res(bitmaps_3d[count_3d], OBJECT_ART_BASE, objart_count++, TRUE, + &dummy_anchor); + objart_loadsize += bitmaps_3d[count_3d]->w * bitmaps_3d[count_3d]->h; + anchors_3d[count_3d] = dummy_anchor.ul; + if (not_using_2d) + bitmaps_2d[i] = bitmaps_3d[count_3d]; + + if ((f > 0) && (ObjProps[i].render_type == FAUBJ_VOX)) + voxel_convert(bitmaps_3d[count_3d]); + count_3d++; + } + objart_count++; + } else // skip over us since we are already loaded + { + objart_count += 3 + FRAME_NUM_3D(ObjProps[i].bitmap_3d); // 2d + 3d + editor icon + frames + count_3d += FRAME_NUM_3D(ObjProps[i].bitmap_3d) + 1; + } + } else { + // We are not wanted, flush us if loaded + if (bitmaps_2d[i] != NULL) { + if (bitmaps_2d[i] != bitmaps_3d[count_3d]) { + objart_loadsize -= bitmaps_2d[i]->w * bitmaps_2d[i]->h; + free(bitmaps_2d[i]->bits); + } + bitmaps_2d[i] = NULL; + objart_count++; + for (f = 0; f < (FRAME_NUM_3D(ObjProps[i].bitmap_3d) + 1); f++) { + objart_loadsize -= bitmaps_3d[count_3d]->w * bitmaps_3d[count_3d]->h; + free(bitmaps_3d[count_3d]->bits); + count_3d++; + objart_count++; + } + objart_count++; + } else { + objart_count += FRAME_NUM_3D(ObjProps[i].bitmap_3d) + 3; // 2d + editicon + 3d + number of frames + count_3d += FRAME_NUM_3D(ObjProps[i].bitmap_3d) + 1; + } + } + } + + // Closedown + if (!flush_all) { + ResCloseFile(objfnum); + } + + // mprintf("count_3d = %x(%d) NUM_OBJECT=%x(%d), EXTRA_FRAMES=%x(%d)\n",count_3d,count_3d, + // NUM_OBJECT,NUM_OBJECT,EXTRA_FRAMES,EXTRA_FRAMES); + // mprintf("OBJART_LOADSIZE = %d\n",objart_loadsize); + return (OK); +} diff --git a/engine/src/GameSrc/objprop.c b/engine/src/GameSrc/objprop.c new file mode 100644 index 0000000..023d5d7 --- /dev/null +++ b/engine/src/GameSrc/objprop.c @@ -0,0 +1,290 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/cit/src/RCS/objprop.c $ + * $Revision: 1.23 $ + * $Author: tjs $ + * $Date: 1994/02/26 19:38:26 $ + * + * $Log: objprop.c $ + * Revision 1.23 1994/02/26 19:38:26 tjs + * Got disgusted with giant case statement in num_types, replaced it + * with a giant table. Executable shrinks by 4K. + * + * Revision 1.22 1994/02/03 17:01:11 minman + * new ammo\gun regime' + * + * Revision 1.21 1994/01/14 15:21:47 xemu + * smallstuff plot + * + * Revision 1.20 1994/01/09 03:33:16 xemu + * load frames from art + * + * Revision 1.19 1993/12/14 15:10:48 xemu + * better frame warning + * + * Revision 1.18 1993/12/08 22:01:15 xemu + * more spew, added objart3 + * + * Revision 1.17 1993/11/22 19:47:31 xemu + * sanity checker + * + * Revision 1.16 1993/10/08 00:56:49 xemu + * The Object Millienia is HERE + * + * Revision 1.15 1993/10/01 23:53:38 xemu + * new object regime + * + * Revision 1.14 1993/09/02 23:02:33 xemu + * angle! + * + * Revision 1.13 1993/08/19 20:19:57 jojak + * revamped object hierarchy + * + * Revision 1.12 1993/08/17 21:52:51 minman + * fixed some bugs with assuming subclasses always 16 + * also added get_nth_from triple + * + * Revision 1.11 1993/08/10 15:52:34 xemu + * removed trap subclass + * + * Revision 1.10 1993/08/06 16:03:58 minman + * changed software subclasse + * + * Revision 1.9 1993/08/05 19:16:24 mahk + * Added nth_after_triple + * + * Revision 1.8 1993/08/05 14:02:27 minman + * changed to new object properties order + * + * Revision 1.7 1993/08/04 02:08:23 minman + * squished the grenade/drug list to 7 types a piece + * + * Revision 1.6 1993/08/03 23:15:06 minman + * added transitory animating subclass + * + * Revision 1.5 1993/08/02 20:39:25 spaz + * Fixed get_triple_From_class_nth_item() + * + * Revision 1.4 1993/08/01 23:24:03 spaz + * get_triple_from_class_nth_item() func + * + * Revision 1.3 1993/07/31 20:49:29 minman + * changed class/subclass hierarchy - no more shotguns + * + * Revision 1.2 1993/07/26 00:49:22 minman + * added comments + * + * Revision 1.1 1993/07/25 23:16:00 minman + * Initial revision + * + * + */ + +#include "objclass.h" +#include "objprop.h" + +// ---------------------------------------------- +// num_types() +// +// ## INSERT NEW CLASS HERE +// ## INSERT NEW SUBCLASS HERE + +#define MAX_SUBCLASSES 8 + +// Curse us for having both NUM_OBJECT_ANIMATING and NUM_OBJECTS_ANIMATING. +// Note that NUM_OBJECT_ANIMATING is the only constant in this list that +// breaks our naming convention, being as it is the number of types in the +// subclass ANIMATING_SUBCLASS_OBJECTS, not ANIMATING_SUBCLASS_OBJECT. + +static uchar numtypes_array[NUM_CLASSES][MAX_SUBCLASSES] = { + {NUM_PISTOL_GUN, NUM_AUTO_GUN, NUM_SPECIAL_GUN, NUM_HANDTOHAND_GUN, + NUM_BEAM_GUN, NUM_BEAMPROJ_GUN}, + {NUM_PISTOL_AMMO, NUM_NEEDLE_AMMO, NUM_MAGNUM_AMMO, NUM_RIFLE_AMMO, + NUM_FLECHETTE_AMMO, NUM_AUTO_AMMO, NUM_PROJ_AMMO}, + {NUM_TRACER_PHYSICS, NUM_SLOW_PHYSICS, NUM_CAMERA_PHYSICS}, + {NUM_DIRECT_GRENADE, NUM_TIMED_GRENADE}, + {NUM_STATS_DRUG}, + {NUM_GOGGLE_HARDWARE, NUM_HARDWARE_HARDWARE}, + {NUM_OFFENSE_SOFTWARE, NUM_DEFENSE_SOFTWARE, NUM_ONESHOT_SOFTWARE, + NUM_MISC_SOFTWARE, NUM_DATA_SOFTWARE}, + {NUM_ELECTRONIC_BIGSTUFF, NUM_FURNISHING_BIGSTUFF, NUM_ONTHEWALL_BIGSTUFF, + NUM_LIGHT_BIGSTUFF, NUM_LABGEAR_BIGSTUFF, NUM_TECHNO_BIGSTUFF, + NUM_DECOR_BIGSTUFF, NUM_TERRAIN_BIGSTUFF}, + {NUM_USELESS_SMALLSTUFF, NUM_BROKEN_SMALLSTUFF, NUM_CORPSELIKE_SMALLSTUFF, + NUM_GEAR_SMALLSTUFF, NUM_CARDS_SMALLSTUFF, NUM_CYBER_SMALLSTUFF, + NUM_ONTHEWALL_SMALLSTUFF, NUM_PLOT_SMALLSTUFF}, + {NUM_CONTROL_FIXTURE, NUM_RECEPTACLE_FIXTURE, NUM_TERMINAL_FIXTURE, + NUM_PANEL_FIXTURE, NUM_VENDING_FIXTURE, NUM_CYBER_FIXTURE}, + {NUM_NORMAL_DOOR, NUM_DOORWAYS_DOOR, NUM_FORCE_DOOR, NUM_ELEVATOR_DOOR, + NUM_SPECIAL_DOOR}, + {NUM_OBJECT_ANIMATING, NUM_TRANSITORY_ANIMATING, NUM_EXPLOSION_ANIMATING}, + {NUM_TRIGGER_TRAP, NUM_FEEDBACKS_TRAP, NUM_SECRET_TRAP}, + {NUM_ACTUAL_CONTAINER, NUM_WASTE_CONTAINER, NUM_LIQUID_CONTAINER, + NUM_MUTANT_CORPSE_CONTAINER, NUM_ROBOT_CORPSE_CONTAINER, + NUM_CYBORG_CORPSE_CONTAINER, NUM_OTHER_CORPSE_CONTAINER}, + {NUM_MUTANT_CRITTER, NUM_ROBOT_CRITTER, NUM_CYBORG_CRITTER, + NUM_CYBER_CRITTER, NUM_ROBOBABE_CRITTER} +}; + +short num_types(uchar obclass, uchar subclass) { + if (obclass >= NUM_CLASSES || subclass >= num_subclasses[obclass]) { + // Warning(("Class and subclass given isn't a valid pair. Class - %d Subclass - %d.\n", obclass, + // subclass)); + return (0); + } + + return (numtypes_array[obclass][subclass]); +} + +// --------------------------------------------------------------------------- +// get_triple_from_class_nth_item() +// +// If you specify the nth overall item of a class, this function will +// return the corresponding triple. Returns -1 if the nth item of +// the class did not exist. + +int get_triple_from_class_nth_item(uchar obclass, uchar n) { + ubyte i, subclass_types, total_types; + int triple; + + total_types = 0; + + // Cycle through all subclasses for this obclass + for (i = 0; i < num_subclasses[obclass]; i++) { + + // Skip the entire next subclass + subclass_types = num_types(obclass, i); + total_types += subclass_types; + + // Aha! We've found or gone past the desired triple + if (total_types > n) { + + triple = MAKETRIP(obclass, i, n - (total_types - subclass_types)); + return triple; + } + } + + // We've gone through all subclasses, and still haven't found what we're looking + // for. Ah well... + // Warning(("Invalid obclass and n given to get_triple_from_class_nth_item")); + // Warning(("Class - %d N - %d\n", obclass, n)); + return -1; +} + +// --------------------------------------------------------------------------- +// nth_after_triple() +// +// Returns the nth valid object triple after base + +int nth_after_triple(int base, uchar n) { + ubyte i, subclass_types, total_types; + int triple; + ubyte obclass; + + total_types = 0; + + // Cycle through all 16 members of the specified class + for (obclass = TRIP2CL(base); obclass < NUM_CLASSES; obclass++) + for (i = TRIP2SC(base); i < num_subclasses[obclass]; i++) { + + // Skip the entire next subclass + subclass_types = num_types(obclass, i); + total_types += subclass_types; + + // Aha! We've found or gone past the desired triple + if (total_types > n) { + triple = MAKETRIP(obclass, i, n - (total_types - subclass_types)); + return triple; + } + } + + // We've gone through all 16, and still haven't found what we're looking + // for. Ah well... + + return -1; +} + +// ------------------------------------------------------------------ +// get_nth_from_triple() +// + +int get_nth_from_triple(int triple) { + ubyte obclass, subclass; + int n, j; + + obclass = TRIP2CL(triple); + subclass = TRIP2SC(triple); + + if (obclass > NUM_CLASSES) + return (0); + if (subclass > num_subclasses[obclass]) + return (0); + + for (n = 0, j = 0; j < subclass; j++) + n += num_types(obclass, j); + + n += TRIP2TY(triple); + + return (n); +} + +/* Don't need this for Mac version!! + +// ------------------------------------------------------------------ +// sanity_check_obj_props() +// +// Performs a basic sanity check on the object properties. Useful for making +// sure that data hasn't been corrupted, either by bad load-time data or memory +// trashes. +// +// Initially, this will probably be pretty bonehead and straightforward, but hopefully +// it will get more stoked as time goes by and people decide to add stuff to it. + +#define MAX_SIZE 253 +#define MAX_PEP 253 +#define MAX_HARDNESS 253 + +errtype sanity_check_obj_props() +{ + int i; + extern Id posture_bases[]; + extern Id critter_id_table[]; + + for (i = 0; i < NUM_OBJECT; i++) + { + if (ObjProps[i].physics_model != 0) + { + if (ObjProps[i].hardness > MAX_HARDNESS) + Warning(("object index %d, triple 0x%x, has hardness %d!\n",i,nth_after_triple(0,i), + ObjProps[i].hardness)); + if (ObjProps[i].mass < 0) + Warning(("object index %d, triple 0x%x, has mass %d!\n",i,nth_after_triple(0,i), + ObjProps[i].mass)); + if (ObjProps[i].physics_xr > MAX_SIZE) + Warning(("object index %d, triple 0x%x, has physics_xr %d!\n",i,nth_after_triple(0,i), + ObjProps[i].physics_xr)); + if (ObjProps[i].pep > MAX_PEP) + Warning(("object index %d, triple 0x%x, has pep %d (model %d) !\n",i,nth_after_triple(0,i), + ObjProps[i].pep,ObjProps[i].physics_model)); + } + } + return(OK); +} +*/ diff --git a/engine/src/GameSrc/objsim.c b/engine/src/GameSrc/objsim.c new file mode 100644 index 0000000..4dcfc26 --- /dev/null +++ b/engine/src/GameSrc/objsim.c @@ -0,0 +1,2815 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/objsim.c $ + * $Revision: 1.292 $ + * $Author: xemu $ + * $Date: 1994/11/21 21:06:58 $ + */ + +// Object simulator code for Citadel +#define __OBJSIM_SRC + +#include +#include + +#include "Shock.h" + +#include "amap.h" +#include "citmat.h" +#include "citres.h" +#include "colors.h" +#include "criterr.h" +#include "cybstrng.h" // for word bitmaps +#include "froslew.h" // objslew and camera stuff.. +#include "gameobj.h" +#include "gamescr.h" // for citadel font +#include "gettmaps.h" +#include "lvldata.h" +#include "mapflags.h" +#include "objart2.h" +#include "objart3.h" +#include "objects.h" +#include "objapp.h" +#include "objbit.h" +#include "objload.h" +#include "objsim.h" +#include "objprop.h" +#include "objuse.h" +#include "objwpn.h" +#include "objwarez.h" +#include "objstuff.h" +#include "objver.h" +#include "otrip.h" +#include "objgame.h" +#include "objcrit.h" +#include "physics.h" +#include "physunit.h" +#include "refstuf.h" +#include "textmaps.h" +#include "tools.h" +#include "weapons.h" +#include "gamestrn.h" +#include "mainloop.h" +#include "shodan.h" +#include "player.h" +#include "tpolys.h" +#include "render.h" +#include "faketime.h" +#include "damage.h" +#include "pathfind.h" +#include "cyber.h" +#include "sfxlist.h" +#include "ai.h" +#include "cybrnd.h" +#include "trigger.h" +#include "effect.h" // for anim_list adding and removing +#include "mfdext.h" +#include "musicai.h" + +// Some useful constants + +#define OBSELETE_WARES_USELESS +#define NUM_CONTENTS 4 +#define MAX_CONTAINER_OBJS NUM_CONTENTS + +#define CUSTOM_MATERIAL_BASE RES_customTextureMaps + +#define PLAYER_TRIP MAKETRIP(CLASS_CRITTER, 0, 6) + +#define inv_coor(fixval) ((fix_int((fixval)) << MAP_SH) + (fix_frac((fixval)) >> MAP_MS)) +#define obj_inv_coor_x(oloc) ((OBJ_LOC_BIN_X(oloc) << MAP_SH) + (OBJ_LOC_FINE_X(oloc) >> MAP_MS)) +#define obj_inv_coor_y(oloc) ((OBJ_LOC_BIN_Y(oloc) << MAP_SH) + (OBJ_LOC_FINE_Y(oloc) >> MAP_MS)) +#define obj_inv_coor_z(oid) (inv_coor(fix_from_obj_height(oid))) + +// global symbol for the player camera... +cams player_cam; + +uchar cam_mode = OBJ_PLAYER_CAMERA; +cams objmode_cam; +uchar new_cyber_orient = TRUE; +uchar ocp_settle_the_player = TRUE; + +uchar properties_changed = FALSE; +uchar trigger_check = TRUE; +ObjID physics_handle_id[MAX_OBJ]; +int physics_handle_max = -1; + +// Internal Prototypes +errtype ObjClassInit(ObjID id, ObjSpecID specid, int subclass); +errtype obj_set_secondary_properties(); +errtype do_ecology_triggers(); +grs_bitmap *get_text_bitmap_from_string(int d1, char dest_type, char *s, uchar scroll, int scroll_index); +grs_bitmap *obj_get_model_data(ObjID id, fix *x, fix *y, fix *z, grs_bitmap *bm2, Ref *ref1, Ref *ref2); +void place_obj_at_objloc(ObjID id, ObjLoc *newloc, ushort xsize, ushort ysize); +Ref ref_from_critter_data(ObjID oid, int triple, byte posture, short frame, short view); +void spew_contents(ObjID id, int d1, int d2); +uchar obj_is_useless(ObjID oid); +errtype obj_settle_func(ObjID id); +uchar death_check(ObjID id, bool *destr); + +errtype set_door_data(ObjID id) { + // Do we block the renderer? + if (objs[id].info.current_frame == 0) { + if (ObjProps[OPNUM(id)].flags & RENDER_BLOCK) + objs[id].info.inst_flags |= RENDER_BLOCK_FLAG; + } else { + objs[id].info.inst_flags &= ~RENDER_BLOCK_FLAG; + } + + // Are we vertical? + if ((objs[id].loc.p == 0) && (objs[id].loc.b == 0)) + objs[id].info.inst_flags |= CLASS_INST_FLAG; + else + objs[id].info.inst_flags &= ~CLASS_INST_FLAG; + + return (OK); +} + +#ifdef PLAYTEST +int extra_object_frames(int triple) { +#ifdef SPEW_ON + char temp[100]; +#endif + int retval; + retval = FRAME_NUM_3D(ObjProps[OPTRIP(triple)].bitmap_3d); + // Spew(DSRC_OBJSIM_Editor, ("extra_frames for %x %s",triple, get_object_long_name(triple,temp,100))); + // Spew(DSRC_OBJSIM_Editor, ("= %hd (from %hx) OTRIP=%d\n", + // retval,ObjProps[OPTRIP(triple)].bitmap_3d,OPTRIP(triple))); + return (retval); +} +#endif + +Id critter_id_table[NUM_CRITTER]; + +grs_bitmap *get_text_bitmap(int d1, int d2, char dest_type, uchar scroll); + +#define NUM_TEXT_BITMAPS 2 +short text_bitmaps_x[NUM_TEXT_BITMAPS] = {128, 64}; +short text_bitmaps_y[NUM_TEXT_BITMAPS] = {32, 64}; +ushort text_bitmap_flags[NUM_TEXT_BITMAPS] = {BMF_TRANS, 0}; +Ref text_bitmap_refs[NUM_TEXT_BITMAPS] = {REF_STR_WordZero, REF_STR_ScreenZero}; + +grs_bitmap *text_bitmap_ptrs[NUM_TEXT_BITMAPS]; +grs_canvas text_canvases[NUM_TEXT_BITMAPS]; + +int memcount = 0; + +errtype obj_init() { + uchar c_class, c_subclass; + int i, j, count, class_count = 0; + Id ids; + + // Create the word-buffer bitmap + for (i = 0; i < NUM_TEXT_BITMAPS; i++) { + text_bitmap_ptrs[i] = gr_alloc_bitmap(BMT_FLAT8, text_bitmap_flags[i], text_bitmaps_x[i], text_bitmaps_y[i]); + gr_make_canvas(text_bitmap_ptrs[i], &text_canvases[i]); + } + + // initialize physics handle mapping to objID + for (i = 0; i < MAX_OBJ; i++) + physics_handle_id[i] = OBJ_NULL; + physics_handle_max = -1; + + // Load object properties from disk + obj_load_properties(); + + // Create base array + count = 0; + // Spew(DSRC_OBJSIM_Editor, ("num_classes = %d num_objects = %d\n",NUM_CLASSES,NUM_OBJECT)); + for (c_class = 0; c_class < NUM_CLASSES; c_class++) { + class_count = 0; + for (c_subclass = 0; c_subclass < num_subclasses[c_class]; c_subclass++) { + // Set Base pointer for offset of class + ObjBaseArray[OBJBASE(MAKETRIP(c_class, c_subclass, 0))] = count; + ClassBaseArray[c_class][c_subclass] = class_count; + for (i = 0; i < num_types(c_class, c_subclass); i++) + bitmaps_2d[count + i] = NULL; + class_count += i; + count += i; + } + } + + // Initialize critter cache stuff + count = 0; + ids = 0; + for (i = 0; i < num_subclasses[CLASS_CRITTER]; i++) { + for (j = 0; j < num_types(CLASS_CRITTER, i); j++) { + critter_id_table[count] = ids; + ids += CritterProps[CPTRIP(MAKETRIP(CLASS_CRITTER, i, j))].views; + // Spew(DSRC_GFX_Anim, ("views for %d,%d = %d, ids = %d corpse = %x\n", i,j, + // CritterProps[CPTRIP(MAKETRIP(CLASS_CRITTER,i,j))].views, ids, + // CritterProps[CPTRIP(MAKETRIP(CLASS_CRITTER,i,j))].corpse)); + count++; + } + } + obj_set_secondary_properties(); + + return (OK); +} + +Id posture_bases[] = { + // Full direction postures + CRITTER_STAND_BASE, CRITTER_MOVE_BASE, + // Front-only postures + CRITTER_ATTACK_BASE, CRITTER_ATTACK_REST_BASE, CRITTER_KNOCKBACK_BASE, CRITTER_DEATH_BASE, CRITTER_DISRUPT_BASE, + CRITTER_ATTACK2_BASE}; + +#define CRITTER_LOADING_PAGE_LIMIT 45000 + +Ref ref_from_critter_data(ObjID oid, int triple, byte posture, short frame, short view) //, uchar *pmirror) +{ + Ref retval; + ubyte v, p; + ulong old_ticks; + extern ulong last_real_time; + Id our_id; + RefTable *prt; + char curr_frames; + uchar load_all_views = TRUE; + // extern ulong page_amount; + + // Set mirror pointer + // if (pmirror != NULL) + // *pmirror = CritterProps[CPTRIP(triple)].mirror; + switch (triple) { + case ROBOBABE_TRIPLE: + posture = STANDING_CRITTER_POSTURE; + view = 0; + break; + } + + // if (page_amount > CRITTER_LOADING_PAGE_LIMIT) + // load_all_views = FALSE; + // else + { + for (p = STANDING_CRITTER_POSTURE; p <= MOVING_CRITTER_POSTURE; p++) { + for (v = 0; v < 8; v++) { + Id id; + id = critter_id_table[CPTRIP(triple)] + v + posture_bases[p]; + if (ResPtr(id)) + load_all_views = FALSE; + } + } + } + // mprintf("lav %d page_amt = %d\n",load_all_views,page_amount); + + // Maybe this should be default_posture if view != FRONT_VIEW? + if (posture >= FIRST_FRONT_POSTURE) + retval = MKREF(posture_bases[posture] + get_nth_from_triple(triple), frame); + else { + our_id = critter_id_table[get_nth_from_triple(triple)] + view + posture_bases[posture]; + if (CritterProps[CPTRIP(triple)].frames[posture] == 0) + posture = DEFAULT_CRITTER_POSTURE; + if (view == FRONT_VIEW) + curr_frames = CritterProps[CPTRIP(triple)].frames[posture]; + else { + // prt = ResReadRefTable(our_id); + prt = (RefTable *)ResLock(our_id); + curr_frames = prt->numRefs; + // ResFreeRefTable(prt); + ResUnlock(our_id); + } + if (frame >= curr_frames) + frame = frame % curr_frames; + retval = MKREF(our_id, frame); + } + + // See if we're loaded from all views, if not load us! + // This may slow down the universe a bit too much...if so then + // we'll need to streamline & optimize it some. + old_ticks = *tmd_ticks; + + if (load_all_views) { + for (p = STANDING_CRITTER_POSTURE; p <= MOVING_CRITTER_POSTURE; p++) { + for (v = 0; v < 8; v++) { + Id id; + id = critter_id_table[CPTRIP(triple)] + v + posture_bases[p]; + if ((id != our_id) && (ResPtr(id) == NULL)) { + ResLock(id); + ResUnlock(id); + } + } + } + } + + // suspend game time for the duration of the loading + last_real_time += *tmd_ticks - old_ticks; + + return (retval); +} + +#include "MacTune.h" + +// deparses the bitmap_3d data field for a tpoly object into the bitmap to be textured onto it +grs_bitmap *bitmap_from_tpoly_data(int tpdata, ubyte *scale, int *index, uchar *type, Ref *use_ref) { + extern grs_bitmap *static_bitmap; + extern char camera_map[NUM_HACK_CAMERAS]; + extern char num_customs; + short style; + int32_t use_index; + Id useme; + grs_bitmap *result; + + if (*use_ref != ID_NULL) + *use_ref = 0; + + tpdata = tpdata & 0xFFF; + style = (tpdata & TPOLY_STYLE_MASK) >> (TPOLY_INDEX_BITS + TPOLY_TYPE_BITS + TPOLY_SCALE_BITS); + *scale = ((tpdata & TPOLY_SCALE_MASK) >> (TPOLY_INDEX_BITS + TPOLY_TYPE_BITS)) << TPOLY_SCALE_SHIFT; + *index = tpdata & TPOLY_INDEX_MASK; + *type = (tpdata & TPOLY_TYPE_MASK) >> TPOLY_INDEX_BITS; + switch (*type) { + case TPOLY_TYPE_ALT_TMAP: + if (!ResInUse(TEXTURE_SMALL_ID + *index)) { + WARN("%s: Invalid tpoly alt texture type request!", __FUNCTION__); + useme = 0; + } else + useme = TEXTURE_SMALL_ID + *index; + if (use_ref != ID_NULL) + *use_ref = MKREF(useme, 0); + return (lock_bitmap_from_ref_anchor(MKREF(useme, 0), NULL)); + break; + case TPOLY_TYPE_CUSTOM_MAT: + if ((*index >= FIRST_CAMERA_TMAP) && (*index <= FIRST_CAMERA_TMAP + NUM_HACK_CAMERAS)) { + extern uchar hack_cameras_needed; + extern grs_bitmap *hack_cam_bitmaps[NUM_HACK_CAMERAS]; + short temp_val = (*index) - FIRST_CAMERA_TMAP; + hack_cameras_needed |= (1 << temp_val); + do_screen_static(); + if (camera_map[temp_val]) + return (hack_cam_bitmaps[camera_map[temp_val] - 1]); + else + return (static_bitmap); + } else if ((*index == REGULAR_STATIC_MAGIC_COOKIE) || (*index == SHODAN_STATIC_MAGIC_COOKIE)) { + do_screen_static(); + return (static_bitmap); + } else if ((*index >= FIRST_AUTOMAP_MAGIC_COOKIE) && + (*index <= FIRST_AUTOMAP_MAGIC_COOKIE + NUM_AUTOMAP_MAGIC_COOKIES)) { + return (screen_automap_bitmap(*index - FIRST_AUTOMAP_MAGIC_COOKIE)); + } + + if (!ResInUse(CUSTOM_MATERIAL_BASE + *index)) { + // Warning(("Invalid custom material request (%d)!\n",*index)); + do_screen_static(); + return (static_bitmap); + } else { + *use_ref = MKREF(CUSTOM_MATERIAL_BASE + *index, 0); + return (lock_bitmap_from_ref_anchor(*use_ref, NULL)); + } + break; + case TPOLY_TYPE_TEXT_BITMAP: + // style=style?2:3; + style = 3 - style; + if (*index == RANDOM_TEXT_MAGIC_COOKIE) { + char use_buf[10]; + int seed; + + seed = *tmd_ticks >> 7; + use_index = ((seed * 9277 + 7) % 14983) % 10; + sprintf(use_buf, "%d", use_index); + return get_text_bitmap_from_string(style, 1, use_buf, FALSE, 0); + // return(get_text_bitmap_from_string(style, 1, itoa(use_index, use_buf, 10), FALSE, 0)); + } else { + return get_text_bitmap(style, *index, 1, FALSE); + } + break; + case TPOLY_TYPE_SCROLL_TEXT: + // style=style?2:3; + style = 3 - style; + return get_text_bitmap(style, *index, 1, TRUE); + } + + return (NULL); +} + +#define BARRICADE_DEF_X 0x4 +#define BARRICADE_DEF_Y 0x1 +#define BARRICADE_DEF_Z 0x10 + +#define BRIDGE_DEF_X 0x4 +#define BRIDGE_DEF_Y 0x4 +#define BRIDGE_DEF_Z 0x01 +#define BRIDGE_DEF_TEXTURE 0x80 +#define BRIDGE_DEF_TEXTURE2 0x80 + +#define CATWALK_DEF_X 0x2 +#define CATWALK_DEF_Y 0x4 +#define CATWALK_DEF_Z 0x01 +#define CATWALK_DEF_TEXTURE 0x80 +#define CATWALK_DEF_TEXTURE2 0x80 + +#define PILLAR_DEF_X 0x2 +#define PILLAR_DEF_Y 0x2 +#define PILLAR_DEF_Z 0xB0 +#define PILLAR_DEF_TEXTURE 0x80 +#define PILLAR_DEF_TEXTURE2 0x81 + +#define SMALL_CRT_DEF_X 0x08 +#define SMALL_CRT_DEF_Y 0x08 +#define SMALL_CRT_DEF_Z 0x08 +#define SMALL_CRT_DEF_TEXTURE 0x0C +#define SMALL_CRT_DEF_TEXTURE2 0x0B + +#define LG_CRT_DEF_X 0x10 +#define LG_CRT_DEF_Y 0x10 +#define LG_CRT_DEF_Z 0x10 +#define LG_CRT_DEF_TEXTURE 0x0C +#define LG_CRT_DEF_TEXTURE2 0x0B + +#define SECURE_CONTR_DEF_X 0x20 +#define SECURE_CONTR_DEF_Y 0x20 +#define SECURE_CONTR_DEF_Z 0x20 +#define SECURE_CONTR_DEF_TEXTURE 0x0A +#define SECURE_CONTR_DEF_TEXTURE2 0x0A + +#define BIGSTUFF_MODEL_XY_SHF 13 +#define BIGSTUFF_MODEL_Z_SHF 10 +#define CONTAINER_MODEL_SHF 10 + +#define FULL_EXTENSION 255 +// This is a TOTAL guess +#define FULL_EXTENSION_Z 8 +#define EXTENDED_FRAMES 32 + +// hack_foo is the amount of the full object to show. +// hack_type is the kind of hack == +// 0 is extending out from one side, +// 1 is expanding out from center +// 2 is rising from the floor/descending from the ceiling +errtype obj_model_hack(ObjID id, uchar *hack_x, uchar *hack_y, uchar *hack_z, uchar *hack_type) { + switch (ID2TRIP(id)) { + case FORCE_BRIJ2_TRIPLE: + *hack_type = 1; + *hack_x = FULL_EXTENSION * (EXTENDED_FRAMES - objs[id].info.current_frame) / EXTENDED_FRAMES; + *hack_y = FULL_EXTENSION * (EXTENDED_FRAMES - objs[id].info.current_frame) / EXTENDED_FRAMES; + break; + case FORCE_BRIJ_TRIPLE: + *hack_type = 0; + *hack_y = FULL_EXTENSION * (EXTENDED_FRAMES - objs[id].info.current_frame) / EXTENDED_FRAMES; + break; + case LABFORCE_TRIPLE: + case RESFORCE_TRIPLE: + *hack_type = 2; + *hack_z = FULL_EXTENSION_Z * (EXTENDED_FRAMES - objs[id].info.current_frame) / EXTENDED_FRAMES; + break; + default: + *hack_type = 0xFF; + break; + } + return (OK); +} + +grs_bitmap *obj_get_model_data(ObjID id, fix *x, fix *y, fix *z, grs_bitmap *bm2, Ref *ref1, Ref *ref2) { + int pval; + uchar p1, p2, p3, p4, p5; + grs_bitmap *retval = NULL; + grs_bitmap *temp_bm = NULL; + + if (!objs[id].active) { + WARN("%s: Attempted to get model params from invalid object!", __FUNCTION__); + return (NULL); + } + switch (objs[id].obclass) { + case CLASS_BIGSTUFF: + pval = objBigstuffs[objs[id].specID].data1; + p1 = pval & 0xF; + p2 = (pval & 0xF0) >> 4; + p3 = (pval & 0xFF00) >> 8; + p4 = (pval & 0xFF0000) >> 16; + p5 = (pval & 0xFF000000) >> 24; + break; + case CLASS_SMALLSTUFF: + pval = objSmallstuffs[objs[id].specID].data1; + p1 = pval & 0xF; + p2 = (pval & 0xF0) >> 4; + p3 = (pval & 0xFF00) >> 8; + p4 = (pval & 0xFF0000) >> 16; + p5 = (pval & 0xFF000000) >> 24; + break; + case CLASS_CONTAINER: + p1 = objContainers[objs[id].specID].dim_x; + p2 = objContainers[objs[id].specID].dim_y; + p3 = objContainers[objs[id].specID].dim_z; + p4 = objContainers[objs[id].specID].data1 & 0xFF; + p5 = (objContainers[objs[id].specID].data1 & 0xFF00) >> 8; + break; + default: + // Warning(("Object %d not of correct class (class = %d) to extract model data!\n",id, + // objs[id].obclass)); + return (NULL); + } + + // Now convert into appropriate units! + // and do defaults if zero. + + // Okay, this could be done more elegantly with arrays, but hey, it's already + // done this way. + switch (objs[id].obclass) { + case CLASS_BIGSTUFF: // bridge, catwalk, pillar + switch (ID2TRIP(id)) { + case BRIDGE_TRIPLE: + if (p1 == 0) + p1 = BRIDGE_DEF_X; + if (p2 == 0) + p2 = BRIDGE_DEF_Y; + if (p3 == 0) + p3 = BRIDGE_DEF_Z; + if (p4 == 0) + p4 = BRIDGE_DEF_TEXTURE; + if (p5 == 0) + p5 = BRIDGE_DEF_TEXTURE2; + break; + case FORCE_BRIJ_TRIPLE: + case CATWALK_TRIPLE: + if (p1 == 0) + p1 = CATWALK_DEF_X; + if (p2 == 0) + p2 = CATWALK_DEF_Y; + if (p3 == 0) + p3 = CATWALK_DEF_Z; + if (p4 == 0) + p4 = CATWALK_DEF_TEXTURE; + if (p5 == 0) + p5 = CATWALK_DEF_TEXTURE2; + break; + case PILLAR_TRIPLE: + if (p1 == 0) + p1 = PILLAR_DEF_X; + if (p2 == 0) + p2 = PILLAR_DEF_Y; + if (p3 == 0) + p3 = PILLAR_DEF_Z; + if (p4 == 0) + p4 = PILLAR_DEF_TEXTURE; + if (p5 == 0) + p5 = PILLAR_DEF_TEXTURE2; + break; + } + + // Transform into fix + *x = p1 << BIGSTUFF_MODEL_XY_SHF; + *y = p2 << BIGSTUFF_MODEL_XY_SHF; + *z = p3 << BIGSTUFF_MODEL_Z_SHF; + break; + case CLASS_SMALLSTUFF: // bridge, catwalk, pillar + switch (ID2TRIP(id)) { + case BARRICADE_TRIPLE: + if (p1 == 0) + p1 = BARRICADE_DEF_X; + if (p2 == 0) + p2 = BARRICADE_DEF_Y; + if (p3 == 0) + p3 = BARRICADE_DEF_Z; + break; + } + + // Transform into fix + // okay, this is using BIGSTUFF defines but really, who cares? + *x = p1 << BIGSTUFF_MODEL_XY_SHF; + *y = p2 << BIGSTUFF_MODEL_XY_SHF; + *z = p3 << BIGSTUFF_MODEL_Z_SHF; + break; + case CLASS_CONTAINER: + switch (ID2TRIP(id)) { + case SML_CRT_TRIPLE: + if (p1 == 0) + p1 = SMALL_CRT_DEF_X; + if (p2 == 0) + p2 = SMALL_CRT_DEF_Y; + if (p3 == 0) + p3 = SMALL_CRT_DEF_Z; + if (p4 == 0) + p4 = SMALL_CRT_DEF_TEXTURE; + if (p5 == 0) + p5 = SMALL_CRT_DEF_TEXTURE2; + break; + case LG_CRT_TRIPLE: + if (p1 == 0) + p1 = LG_CRT_DEF_X; + if (p2 == 0) + p2 = LG_CRT_DEF_Y; + if (p3 == 0) + p3 = LG_CRT_DEF_Z; + if (p4 == 0) + p4 = LG_CRT_DEF_TEXTURE; + if (p5 == 0) + p5 = LG_CRT_DEF_TEXTURE2; + break; + case SECURE_CONTR_TRIPLE: + if (p1 == 0) + p1 = SECURE_CONTR_DEF_X; + if (p2 == 0) + p2 = SECURE_CONTR_DEF_Y; + if (p3 == 0) + p3 = SECURE_CONTR_DEF_Z; + if (p4 == 0) + p4 = SECURE_CONTR_DEF_TEXTURE; + if (p5 == 0) + p5 = SECURE_CONTR_DEF_TEXTURE2; + break; + } + // Transform into fix + *x = p1 << CONTAINER_MODEL_SHF; + *y = p2 << CONTAINER_MODEL_SHF; + *z = p3 << CONTAINER_MODEL_SHF; + break; + } + + // if bm2 is NULL then we basically don't need to get the texture map + if (bm2 == NULL) { + // a NON-NULL pointer + return ((grs_bitmap *)1); + } + + // Now get the texture map + + // Okay, this is wacky that we process p5 before p4, but it is necessary that the bitmap that + // we actually copy out (as opposed to returning) be done first -- specifically that the thing + // we return has to have been done the most recently. + if (p5 & 0x80) { + if ((p5 & 0x7F) >= NUM_LOADED_TEXTURES) { + temp_bm = SAFE_TEXTURE; + *bm2 = *temp_bm; + WARN("%s: Bogus Tmap", __FUNCTION__); + } // doofy you screwed up thing + else { + temp_bm = get_texture_map(p5 & 0x7F, TEXTURE_64_INDEX); + *bm2 = *temp_bm; + } + } else { + if (ref2 != NULL) { + *ref2 = MKREF(CUSTOM_MATERIAL_BASE + (p5 & 0x7F), 0); + temp_bm = lock_bitmap_from_ref_anchor(*ref2, NULL); + *bm2 = *temp_bm; + } + } + + if (p4 & 0x80) { + if ((p4 & 0x7F) >= NUM_LOADED_TEXTURES) { + retval = SAFE_TEXTURE; + WARN("%s: Bogus Tmap", __FUNCTION__); + } else // doofy you screwed up thing + retval = get_texture_map(p4 & 0x7F, TEXTURE_64_INDEX); + } else { + if (ref1 != NULL) { + *ref1 = MKREF(CUSTOM_MATERIAL_BASE + (p4 & 0x7F), 0); + retval = lock_bitmap_from_ref_anchor(*ref1, NULL); + } + } + + return (retval); +} + +char extract_object_special_color(ObjID id) { + switch (objs[id].obclass) { + case CLASS_BIGSTUFF: + return (objBigstuffs[objs[id].specID].data2); + break; + case CLASS_SMALLSTUFF: + return (objSmallstuffs[objs[id].specID].data2); + break; + case CLASS_DOOR: + return (objDoors[objs[id].specID].cosmetic_value); + break; + default: + return (0); + break; + } +} + +// Shutdown the object system and free up memory as appropriate +errtype obj_shutdown() { + // Free the word-buffer bitmap + for (int i = 0; i < NUM_TEXT_BITMAPS; i++) { + if (text_bitmap_ptrs[i] != NULL) + free(text_bitmap_ptrs[i]); + } + + obj_load_art(TRUE); + + return (OK); +} + +void spew_contents(ObjID id, int d1, int d2) { + char i, num_objs; + ObjLoc newloc; + ObjID id_list[MAX_CONTAINER_OBJS]; + num_objs = container_extract(id_list, d1, d2); + for (i = 0; i < num_objs; i++) { + if (id_list[i] != OBJ_NULL) { + newloc = objs[id].loc; + newloc.x += rand() & 0x6F; + newloc.y += rand() & 0x6F; + obj_move_to(id_list[i], &newloc, TRUE); + } + } +} + +uchar obj_is_useless(ObjID oid) { + uchar useless; + + useless = (ObjProps[OPNUM(oid)].flags & USELESS_FLAG) != 0; +#ifdef OBSELETE_WARES_USELESS + if (!useless && objs[oid].obclass == CLASS_HARDWARE) + useless = player_struct.hardwarez[CPTRIP(ID2TRIP(oid))] >= objHardwares[objs[oid].specID].version; +#endif + useless = useless && !(objs[oid].info.inst_flags & USEFUL_FLAG); + return useless; +} + +#define OBJ_DESTROY_TRIES 5 +#define MIN_OBJKILL_DIST 8 + +// Creates the basic object, but does not place it into the world +uchar obj_autodelete = TRUE; +ObjID obj_create_base(int triple) { + ObjID new_id; + ObjSpecID new_specid; + + if (obj_autodelete) { + while (!ObjAndSpecGrab(TRIP2CL(triple), &new_id, &new_specid)) { + ObjID kill_obj = OBJ_NULL, oid; + ObjID kill_container = OBJ_NULL; + int kill_index, num_objs; + ObjID idlist[NUM_CONTENTS]; + ObjLoc ploc = objs[PLAYER_OBJ].loc; + ObjLoc killobjloc; + int *d1, *d2, content, dist, obclass, maxdist = 0; + + // Warning(("ObjAndSpecGrab could not find ObjSpec for this class: %d.\n", TRIP2CL(triple))); + + for (oid = (objs[OBJ_NULL]).headused; oid != OBJ_NULL; oid = objs[oid].next) { + if (oid == player_struct.panel_ref) + continue; + obclass = objs[oid].obclass; + if (obclass == TRIP2CL(triple)) { + + if (objs[oid].ref == OBJ_REF_NULL) + continue; + + if (obj_is_useless(oid)) { + killobjloc = objs[oid].loc; + dist = long_fast_pyth_dist(killobjloc.x - ploc.x, killobjloc.y - ploc.y); + if (dist >= maxdist) { + kill_obj = oid; + kill_container = OBJ_NULL; + maxdist = dist; + } + } + } else if (is_container(oid, &d1, &d2)) { + if (!(((ID2TRIP(oid) >= MUT_CORPSE1_TRIPLE) && (ID2TRIP(oid) <= OTH_CORPSE8_TRIPLE)) || + ((ID2TRIP(oid) >= CORPSE1_TRIPLE) && (ID2TRIP(oid) <= CORPSE8_TRIPLE))) && + (objs[oid].info.inst_flags & CLASS_INST_FLAG)) { + num_objs = container_extract(idlist, *d1, (d2 == NULL) ? 0 : *d2); + for (content = 0; content < num_objs; content++) { + if (idlist[content] == OBJ_NULL) + continue; + if (objs[idlist[content]].obclass != TRIP2CL(triple)) + continue; + + if (obj_is_useless(idlist[content])) { + killobjloc = objs[oid].loc; + dist = long_fast_pyth_dist(killobjloc.x - ploc.x, killobjloc.y - ploc.y); + if (dist >= maxdist) { + kill_obj = idlist[content]; + kill_container = oid; + kill_index = content; + maxdist = dist; + } + } + } + } + } + } + if (maxdist < MIN_OBJKILL_DIST) + kill_obj = OBJ_NULL; + // Warning(("Destroying remote instance to accomodate...obj ID 0x%x\n",kill_obj)); + if (kill_obj != OBJ_NULL) { + if (kill_container != OBJ_NULL) { + is_container(kill_container, &d1, &d2); + num_objs = container_extract(idlist, *d1, (d2 == NULL) ? 0 : *d2); + idlist[kill_index] = OBJ_NULL; + container_stuff(idlist, num_objs - 1, d1, d2); + } + if (is_container(kill_obj, &d1, &d2) && !(objs[kill_obj].info.inst_flags & CLASS_INST_FLAG)) { + do_random_loot(kill_obj); + spew_contents(kill_obj, *d1, (d2 == NULL) ? 0 : *d2); + } + obj_destroy(kill_obj); + } else + return (OBJ_NULL); + } + } else if (!ObjAndSpecGrab(TRIP2CL(triple), &new_id, &new_specid)) { + // Warning(("ObjAndSpecGrab could not find ObjSpec for this obclass: %d.\n", TRIP2CL(triple))); + return (OBJ_NULL); + } + + objs[new_id].info.type = TRIP2TY(triple); // needs type info - so it knows the correct hp + ObjClassInit(new_id, new_specid, TRIP2SC(triple)); + objs[new_id].info.ph = -1; + objs[new_id].loc.x = -1; + objs[new_id].info.current_frame = 0; + objs[new_id].info.time_remainder = 0; + switch (objs[new_id].obclass) { + case CLASS_GUN: + if ((TRIP2SC(triple) == GUN_SUBCLASS_BEAM) || (TRIP2SC(triple) == GUN_SUBCLASS_BEAMPROJ)) { + objGuns[new_specid].ammo_type = 30; + objGuns[new_specid].ammo_count = 0; + } + break; + case CLASS_PHYSICS: + LG_memset(&objPhysicss[new_specid].p1, 0, (sizeof(ObjLoc))); + LG_memset(&objPhysicss[new_specid].p2, 0, (sizeof(ObjLoc))); + LG_memset(&objPhysicss[new_specid].p3, 0, (sizeof(ObjLoc))); + break; + case CLASS_DOOR: + if (ObjProps[OPTRIP(triple)].flags & RENDER_BLOCK) + objs[new_id].info.inst_flags |= RENDER_BLOCK_FLAG; + break; + case CLASS_SMALLSTUFF: + if ((triple >= CORPSE1_TRIPLE) && (triple <= CORPSE8_TRIPLE)) { + objs[new_id].info.inst_flags |= CLASS_INST_FLAG; + } + break; + case CLASS_CRITTER: + objCritters[new_specid].path_id = -1; + break; + } + + if (ANIM_3D(ObjProps[OPTRIP(triple)].bitmap_3d)) { + switch (TRIP2CL(triple)) { + case CLASS_BIGSTUFF: + case CLASS_SMALLSTUFF: + obj_screen_animate(new_id); + break; + default: + add_obj_to_animlist(new_id, REPEAT_3D(ObjProps[OPTRIP(triple)].bitmap_3d), FALSE, FALSE, 0, 0, 0, 0); + break; + } + } + increment_shodan_value(new_id, TRUE); + if (trigger_check) + do_ecology_triggers(); + + return (new_id); +} + +// Clones an object, including copying all of it's instance data +// WARNING: Do not try this function with objects of CLASS_DINOSAUR!! -- D. Nedry + +ObjID obj_create_clone(ObjID dna) { + ObjID new_obj; + ubyte *pspec, *pdef; + ObjSpecID specid, osi; + ObjSpecHeader *spec_hdr = &objSpecHeaders[objs[dna].obclass]; + + if (dna == OBJ_NULL) { + return (OBJ_NULL); + } + new_obj = obj_create_base(MAKETRIP(objs[dna].obclass, objs[dna].subclass, objs[dna].info.type)); + if (new_obj == OBJ_NULL) { + return (OBJ_NULL); + } + specid = objs[new_obj].specID; + osi = objs[dna].specID; + + switch (objs[dna].obclass) { + case CLASS_GUN: + pspec = (ubyte *)&objGuns[specid]; + pdef = (ubyte *)&objGuns[osi]; + break; + case CLASS_AMMO: + pspec = (ubyte *)&objAmmos[specid]; + pdef = (ubyte *)&objAmmos[osi]; + break; + case CLASS_PHYSICS: + pspec = (ubyte *)&objPhysicss[specid]; + pdef = (ubyte *)&objPhysicss[osi]; + break; + case CLASS_GRENADE: + pspec = (ubyte *)&objGrenades[specid]; + pdef = (ubyte *)&objGrenades[osi]; + break; + case CLASS_DRUG: + pspec = (ubyte *)&objDrugs[specid]; + pdef = (ubyte *)&objDrugs[osi]; + break; + case CLASS_HARDWARE: + pspec = (ubyte *)&objHardwares[specid]; + pdef = (ubyte *)&objHardwares[osi]; + break; + case CLASS_SOFTWARE: + pspec = (ubyte *)&objSoftwares[specid]; + pdef = (ubyte *)&objSoftwares[osi]; + break; + case CLASS_BIGSTUFF: + pspec = (ubyte *)&objBigstuffs[specid]; + pdef = (ubyte *)&objBigstuffs[osi]; + break; + case CLASS_SMALLSTUFF: + pspec = (ubyte *)&objSmallstuffs[specid]; + pdef = (ubyte *)&objSmallstuffs[osi]; + break; + case CLASS_FIXTURE: + pspec = (ubyte *)&objFixtures[specid]; + pdef = (ubyte *)&objFixtures[osi]; + break; + case CLASS_DOOR: + pspec = (ubyte *)&objDoors[specid]; + pdef = (ubyte *)&objDoors[osi]; + break; + case CLASS_ANIMATING: + pspec = (ubyte *)&objAnimatings[specid]; + pdef = (ubyte *)&objAnimatings[osi]; + break; + case CLASS_TRAP: + pspec = (ubyte *)&objTraps[specid]; + pdef = (ubyte *)&objTraps[osi]; + break; + case CLASS_CONTAINER: + pspec = (ubyte *)&objContainers[specid]; + pdef = (ubyte *)&objContainers[osi]; + break; + case CLASS_CRITTER: + pspec = (ubyte *)&objCritters[specid]; + pdef = (ubyte *)&objCritters[osi]; + break; + } + + // copy instance data from default + LG_memcpy(pspec + sizeof(ObjSpec), pdef + sizeof(ObjSpec), spec_hdr->struct_size - sizeof(ObjSpec)); + + // Give back the object ID of the fresh clone + return (new_obj); +} + +// ------------------------------------------------------ +// place_obj_at_objloc() puts an object down in the map, +// adding the appropriate refs for placing an object with the +// specified xsize and ysize in object coordinates. +#define MAX_PLACE_SIZE 0x100 + +void place_obj_at_objloc(ObjID id, ObjLoc *newloc, ushort xsize, ushort ysize) { + ObjLocState newstate; + ushort nxl, nxh, nyl, nyh, bx, by, ox, oy; + short refcount = 0; + ObjRefID refid, origref; + + if ((xsize > MAX_PLACE_SIZE) || (ysize > MAX_PLACE_SIZE)) { + //printf("place_obj_at_objloc: obj %d size too large!! (xsize = 0x%x ysize = 0x%x)\n", id, xsize, ysize); + xsize = lg_min(0x200, xsize); + ysize = lg_min(0x200, ysize); + } + + // Clear old homesquare data + origref = refid = objs[id].ref; + do { + CitrefClearHomeSq(refid); + refid = objRefs[refid].nextref; + } while (refid != origref); + + newstate.obj = id; + newstate.loc = *newloc; + ox = bx = OBJ_LOC_BIN_X(*newloc); + oy = by = OBJ_LOC_BIN_Y(*newloc); + newstate.refs[refcount++].bin.sq = MakePoint(bx, by); + nxl = lg_max(0, (newloc->x - xsize) >> 8); + nyl = lg_max(0, (newloc->y - ysize) >> 8); + nxh = lg_min(global_fullmap->x_size, (newloc->x + xsize) >> 8); + nyh = lg_min(global_fullmap->y_size, (newloc->y + ysize) >> 8); + + if ((ObjProps[OPNUM(id)].render_type == FAUBJ_TEXBITMAP) || (ObjProps[OPNUM(id)].render_type == FAUBJ_TPOLY)) { + if (!(newloc->h & ~0xC0)) { + if (newloc->h & 0x40) { // Don't go along x axis + nxl = nxh = ox; + } else { // Don't go along y axis + nyl = nyh = oy; + } + } + } + + // Cardinal directions... + for (bx = nxl; bx <= nxh; bx++) + for (by = nyl; by <= nyh; by++) + if ((bx != ox) || (by != oy)) + newstate.refs[refcount++].bin.sq = MakePoint(bx, by); + ObjRefStateBinSetNull(newstate.refs[refcount].bin); + ObjUpdateLocs(&newstate); + + // Place homesquare -- we can't do this earlier since we don't + // know refids until earlier + origref = refid = objs[id].ref; + do { + if ((objRefs[refid].state.bin.sq.x == ox) && (objRefs[refid].state.bin.sq.y == oy)) { + CitrefSetHomeSq(refid); + break; + } + refid = objRefs[refid].nextref; + } while (refid != origref); +} + +#define COMPRESSION_FACTOR fix_make(0, 0xD000) + +#define FUNKY_GRAV(oloc) \ + (level_gamedata.hazard.zerogbio && me_hazard_bio(MAP_GET_XY(OBJ_LOC_BIN_X(oloc), OBJ_LOC_BIN_Y(oloc)))) +#define FUNKY_GRAV_VAL (fix_make(level_gamedata.hazard.bio, 0)) + +// Moves an object to an objloc with a given velocity (in physics units, whatever +// they are). +errtype obj_move_to_vel(ObjID id, ObjLoc *newloc, uchar phys_tel, fix x_dot, fix y_dot, fix z_dot) { + State new_state; + ushort xsize = 0, ysize = 0; + extern cams *_def_cam; + + if (phys_tel && ((ObjProps[OPNUM(id)].physics_model != EDMS_NONE) || (id == PLAYER_OBJ))) { + if (CHECK_OBJ_PH(id)) + EDMS_get_state(objs[id].info.ph, &new_state); + else + new_state = standard_state; + new_state.X = fix_from_obj_coord(newloc->x); + new_state.Y = fix_from_obj_coord(newloc->y); + new_state.Z = fix_from_obj_height_val(newloc->z); + new_state.X_dot = x_dot; + new_state.Y_dot = y_dot; + new_state.Z_dot = z_dot; + new_state.alpha = phys_angle_from_obj(newloc->h); + + if (!(CHECK_OBJ_PH(id))) { + assemble_physics_object(id, &new_state); + if (!CHECK_OBJ_PH(id)) + return (ERR_NOEFFECT); + } else + EDMS_holistic_teleport(objs[id].info.ph, &new_state); + } + + if (objs[id].loc.x == -1) { + ObjRefState newref; + + ObjPlace(id, newloc); + newref.bin.sq.x = OBJ_LOC_BIN_X(*newloc); + newref.bin.sq.y = OBJ_LOC_BIN_Y(*newloc); + ObjRefMake(id, newref); + } + + if (!CHECK_OBJ_PH(id)) { + switch (ID2TRIP(id)) { + case WORDS_TRIPLE: { + grs_bitmap *bmap; + char scale; + bmap = get_text_bitmap_obj(id, 0, &scale); + + // In theory, we take the size of the bitmap, scale it down, then mutliply by 3/4 + xsize = ysize = ((bmap->w << scale) * 3) >> 2; + } break; + default: + xsize = ysize = obj_coord_from_fix(fix_make(ObjProps[OPNUM(id)].physics_xr, 0) / PHYSICS_RADIUS_UNIT); + break; + } + } else { + switch (ObjProps[OPNUM(id)].physics_model) { + case EDMS_ROBOT: { + Robot new_robot; + EDMS_get_robot_parameters(objs[id].info.ph, &new_robot); + if (!global_fullmap->cyber) { + if (!((objs[id].obclass == CLASS_CRITTER) && (CritterProps[CPNUM(id)].flags & AI_FLAG_FLYING))) { + if (FUNKY_GRAV(objs[id].loc)) { + if (new_robot.gravity == STANDARD_GRAVITY) + apply_gravity_to_one_object(id, FUNKY_GRAV_VAL); + } else if ((new_robot.gravity != STANDARD_GRAVITY) && (objs[id].obclass != CLASS_PHYSICS)) + apply_gravity_to_one_object(id, STANDARD_GRAVITY); + } + + if (objs[id].obclass == CLASS_PHYSICS) { + if (PhysicsProps[CPNUM(id)].flags & PROJ_LIGHT_FLAG) { + ObjSpecID osid = objs[id].specID; + + if ((OBJ_LOC_TO_LIGHT_LOC(newloc->x) != OBJ_LOC_TO_LIGHT_LOC(objPhysicss[osid].p1.x)) || + (OBJ_LOC_TO_LIGHT_LOC(newloc->y) != OBJ_LOC_TO_LIGHT_LOC(objPhysicss[osid].p1.y))) { + MapElem *mmp; + + if (objPhysicss[osid].p2.x) { + mmp = MAP_GET_XY(OBJ_LOC_TO_LIGHT_LOC(objPhysicss[osid].p1.x), + OBJ_LOC_TO_LIGHT_LOC(objPhysicss[osid].p1.y)); + me_rend3_set(mmp, me_bits_rend3(mmp) - 1); + objPhysicss[osid].p2.x = 0; + } + mmp = MAP_GET_XY(OBJ_LOC_TO_LIGHT_LOC(newloc->x), OBJ_LOC_TO_LIGHT_LOC(newloc->y)); + if (!me_bits_rend3(mmp)) { + me_rend3_set(mmp, me_bits_rend3(mmp) + 1); + objPhysicss[osid].p2.x = 1; + } + } + objPhysicss[osid].p1 = *newloc; + } + } + } + xsize = ysize = obj_coord_from_fix(fix_mul(new_robot.size, COMPRESSION_FACTOR)); + break; + } + + case EDMS_PELVIS: { +#ifdef DIRAC_EDMS + if (global_fullmap->cyber) { + Dirac_frame new_dirac; + EDMS_get_Dirac_frame_parameters(objs[id].info.ph, &new_dirac); + // Do appropiate gravity hacks here someday... + + // Bleh, got to figure out the right size. Ack! + // Note that this is completely not the right thing at all. + // DIRAC_FIX +#ifdef WE_HAD_ANY_IDEA + xsize = ysize = obj_coord_from_fix(fix_mul(new_dirac.?????, COMPRESSION_FACTOR)); +#else + xsize = ysize = 0; +#endif + } else +#endif + { + Pelvis new_pelvis; + EDMS_get_pelvis_parameters(objs[id].info.ph, &new_pelvis); + if (!global_fullmap->cyber) { + if (FUNKY_GRAV(objs[id].loc)) { + if (new_pelvis.gravity == STANDARD_GRAVITY) + apply_gravity_to_one_object(id, FUNKY_GRAV_VAL); + } else if (new_pelvis.gravity != STANDARD_GRAVITY) + apply_gravity_to_one_object(id, STANDARD_GRAVITY); + } + xsize = ysize = obj_coord_from_fix(fix_mul(new_pelvis.size, COMPRESSION_FACTOR)); + } + break; + } + + default: + xsize = ysize = obj_coord_from_fix(fix_make(ObjProps[OPNUM(id)].physics_xr, 0) / PHYSICS_RADIUS_UNIT); + break; + } + } + place_obj_at_objloc(id, newloc, xsize, ysize); + + // If the camera is on the object we are moving, then hey + if ((_def_cam != NULL) && (_def_cam->obj_id == id)) + chg_set_flg(_current_3d_flag); + + return (OK); +} + +// Moves an object to a given objloc, with no velocity +errtype obj_move_to(ObjID id, ObjLoc *newloc, uchar phys_tel) { + return (obj_move_to_vel(id, newloc, phys_tel, 0, 0, 0)); +} + +// Destroys an object, deals automagically with it's DOS and EDMS +// representations +uchar obj_destroy(ObjID id) { + int retval = -1; + short x, y; + uchar terrain_object = FALSE; + + decrement_shodan_value(id, TRUE); + if (id != OBJ_NULL) { + if (player_struct.panel_ref == id) { + check_panel_ref(TRUE); + } + if (objs[id].active) { + check_deathwatch_triggers(id, TRUE); + remove_obj_from_animlist(id); + switch (objs[id].obclass) { + case CLASS_CRITTER: + if (objCritters[objs[id].specID].path_id != -1) + delete_path(objCritters[objs[id].specID].path_id); + break; + } + terrain_object = ((ObjProps[OPNUM(id)].flags & TERRAIN_OBJECT) != 0); + if (terrain_object) { + x = OBJ_LOC_BIN_X(objs[id].loc); + y = OBJ_LOC_BIN_Y(objs[id].loc); + } + retval = ObjDel(id); + if (CHECK_OBJ_PH(id)) { + EDMS_kill_object(objs[id].info.ph); + physics_handle_id[objs[id].info.ph] = OBJ_NULL; + + if (objs[id].info.ph == physics_handle_max) // are we the last valid ph? + while (physics_handle_id[--physics_handle_max] == OBJ_NULL) + ; // count down through physics_handles till we find an object + } + if ((terrain_object) && (x != -1)) + obj_physics_refresh(x, y, FALSE); + } + if (trigger_check) { + trigger_check_destroyed(id); + do_ecology_triggers(); + } + } + return (retval); +} + +// Create the player's object, both in DOS and EDMS and set appropriate +// player structure state, as well as setting up the camera structures. +errtype obj_create_player(ObjLoc *plr_loc) { + State new_state; + uchar use_new = FALSE; + physics_handle ph; + Pelvis player_pelvis; +#ifdef DIRAC_EDMS + Dirac_frame player_dirac; +#endif + fix pos_list[3]; + + player_struct.rep = obj_create_base(PLAYER_TRIP); + if (player_struct.rep == OBJ_NULL) { + WARN("%s: MAJOR BADNESS!! Could not create player!", __FUNCTION__); + return (ERR_NOEFFECT); + } + player_dos_obj = &(objs[PLAYER_OBJ]); + fr_camera_create(&player_cam, CAMTYPE_OBJ | CAMMOD_USEMOD, player_struct.rep, NULL, NULL); + if (cam_mode == OBJ_PLAYER_CAMERA) + fr_camera_setdef(&player_cam); + + pos_list[0] = fix_from_obj_coord(plr_loc->x) >> 8; + pos_list[1] = fix_from_obj_coord(plr_loc->y) >> 8; + pos_list[2] = fix_from_obj_height_val(plr_loc->z) >> 8; + fr_objslew_go_real_height(NULL, (int32_t *)pos_list); + plr_loc->z = obj_height_from_fix(pos_list[2] << 8); + + if ((player_struct.edms_state[0]) && (!global_fullmap->cyber)) { + LG_memcpy(&new_state, player_struct.edms_state, sizeof(fix) * 12); + state_to_objloc(&new_state, plr_loc); + use_new = TRUE; + } else { + new_state = standard_state; + new_state.X = pos_list[0] << 8; + new_state.Y = pos_list[1] << 8; + new_state.Z = pos_list[2] << 8; + new_state.alpha = phys_angle_from_obj(plr_loc->h); + new_state.beta = 0; + new_state.gamma = 0; + } + +#ifdef DIRAC_EDMS + if (global_fullmap->cyber) { + instantiate_dirac(PLAYER_TRIP, &player_dirac); + objs[PLAYER_OBJ].info.ph = ph = EDMS_make_Dirac_frame(&player_dirac, &new_state); + new_cyber_orient = TRUE; + } else +#endif + { + instantiate_pelvis(PLAYER_TRIP, &player_pelvis); + objs[PLAYER_OBJ].info.ph = ph = EDMS_make_pelvis(&player_pelvis, &new_state); + } + physics_handle_id[ph] = PLAYER_OBJ; + + if (ph > physics_handle_max) + physics_handle_max = ph; + + obj_move_to(PLAYER_OBJ, plr_loc, !use_new); + + if ((!global_fullmap->cyber) && (ocp_settle_the_player)) + EDMS_settle_object(ph); + + return (OK); +} + +// ------------------------------------------------- +// ObjClassInit() +// +// ## INSERT NEW OBJ PROPS HERE +// + +errtype ObjClassInit(ObjID id, ObjSpecID specid, int subclass) { + ubyte *pspec, *pdef; + ObjSpecHeader *spec_hdr = &objSpecHeaders[objs[id].obclass]; + + objs[id].subclass = subclass; + objs[id].active = TRUE; + if (id != PLAYER_OBJ) { + objs[id].info.current_hp = ObjProps[OPNUM(id)].hit_points; + objs[id].info.make_info = 0; + } + switch (objs[id].obclass) { + case CLASS_GUN: + pspec = (ubyte *)&objGuns[specid]; + pdef = (ubyte *)&default_gun; + break; + case CLASS_AMMO: + pspec = (ubyte *)&objAmmos[specid]; + pdef = (ubyte *)&default_ammo; + break; + case CLASS_PHYSICS: + pspec = (ubyte *)&objPhysicss[specid]; + pdef = (ubyte *)&default_physics; + break; + case CLASS_GRENADE: + pspec = (ubyte *)&objGrenades[specid]; + pdef = (ubyte *)&default_grenade; + break; + case CLASS_DRUG: + pspec = (ubyte *)&objDrugs[specid]; + pdef = (ubyte *)&default_drug; + break; + case CLASS_HARDWARE: + pspec = (ubyte *)&objHardwares[specid]; + pdef = (ubyte *)&default_hardware; + break; + case CLASS_SOFTWARE: + pspec = (ubyte *)&objSoftwares[specid]; + pdef = (ubyte *)&default_software; + break; + case CLASS_BIGSTUFF: + pspec = (ubyte *)&objBigstuffs[specid]; + pdef = (ubyte *)&default_bigstuff; + break; + case CLASS_SMALLSTUFF: + pspec = (ubyte *)&objSmallstuffs[specid]; + pdef = (ubyte *)&default_smallstuff; + break; + case CLASS_FIXTURE: + pspec = (ubyte *)&objFixtures[specid]; + pdef = (ubyte *)&default_fixture; + break; + case CLASS_DOOR: + pspec = (ubyte *)&objDoors[specid]; + pdef = (ubyte *)&default_door; + break; + case CLASS_ANIMATING: + pspec = (ubyte *)&objAnimatings[specid]; + pdef = (ubyte *)&default_animating; + break; + case CLASS_TRAP: + pspec = (ubyte *)&objTraps[specid]; + pdef = (ubyte *)&default_trap; + break; + case CLASS_CONTAINER: + pspec = (ubyte *)&objContainers[specid]; + pdef = (ubyte *)&default_container; + break; + case CLASS_CRITTER: + pspec = (ubyte *)&objCritters[specid]; + pdef = (ubyte *)&default_critter; + break; + } + + // copy instance data from default + LG_memcpy(pspec + sizeof(ObjSpec), pdef + sizeof(ObjSpec), spec_hdr->struct_size - sizeof(ObjSpec)); + return (OK); +} + +// ------------------------------------------------ +// obj_load_properties() +// +// ## INSERT NEW CLASS HERE +// ## INSERT NEW SUBCLASS HERE + +errtype obj_load_properties() { + // Handle res; + int version, i, j; + char *cp; + + // extern void SwapLongBytes(void *pval4); + // extern void SwapShortBytes(void *pval2); + + // For Mac version, replaced with GetResource + + // Spew(DSRC_GFX_Anim, ("objprop path = %s\n",path)); + FILE *f = fopen_caseless("res/data/objprop.dat", "rb"); + + if (f == NULL) { + return (ERR_FOPEN); + } + + fseek(f, 0, SEEK_END); + int len = ftell(f); + rewind(f); + + cp = (char *)malloc((len + 1) * sizeof(char)); + fread(cp, len, 1, f); + fclose(f); + + // Check to make sure we have the right version. + version = *(int *)cp; + cp += 4; + // SwapLongBytes(&version); + if (version != OBJPROP_VERSION_NUMBER) { + ERROR("Bad version!"); + critical_error(CRITERR_MISC | 0); + } + + // Copy the data to the various global arrays, converting as needed. + + //------------- + // GUNS + //------------- + memmove(GunProps, cp, sizeof(GunProps)); + cp += sizeof(GunProps); + + // BlockMoveData(cp, PistolGunProps, NUM_PISTOL_GUN); Dummies + cp += NUM_PISTOL_GUN; + + // BlockMoveData(cp, AutoGunProps, NUM_AUTO_GUN); Dummies + cp += NUM_AUTO_GUN; + + // fread(&SpecialGunProps, sizeof(SpecialGunProps), 1, f); + + for (i = 0; i < NUM_SPECIAL_GUN; i++) { + SpecialGunProp *sgp = &SpecialGunProps[i]; + + sgp->damage_modifier = *(short *)cp; + cp += 2; + // SwapShortBytes(&sgp->damage_modifier); + sgp->offense_value = *cp++; + sgp->damage_type = *(int *)cp; + cp += 4; + // SwapLongBytes(&sgp->damage_type); + sgp->penetration = *cp++; + + sgp->speed = *cp++; + sgp->proj_triple = *(int *)cp; + cp += 4; + // SwapLongBytes(&sgp->proj_triple); + sgp->attack_mass = *cp++; + sgp->attack_speed = *(short *)cp; + cp += 2; + // SwapShortBytes(&sgp->attack_speed); + } + + // fread(&HandtohandGunProps, sizeof(HandtohandGunProps), 1, f); + for (i = 0; i < NUM_HANDTOHAND_GUN; i++) { + HandtohandGunProp *hhgp = &HandtohandGunProps[i]; + + hhgp->damage_modifier = *(short *)cp; + cp += 2; + // SwapShortBytes(&hhgp->damage_modifier); + hhgp->offense_value = *cp++; + hhgp->damage_type = *(int *)cp; + cp += 4; + // SwapLongBytes(&hhgp->damage_type); + hhgp->penetration = *cp++; + + hhgp->energy_use = *cp++; + hhgp->attack_mass = *cp++; + hhgp->attack_range = *cp++; + hhgp->attack_speed = *(short *)cp; + cp += 2; + // SwapShortBytes(&hhgp->attack_speed); + } + + for (i = 0; i < NUM_BEAM_GUN; i++) { + BeamGunProp *bgp = &BeamGunProps[i]; + + bgp->damage_modifier = *(short *)cp; + cp += 2; + // SwapShortBytes(&bgp->damage_modifier); + bgp->offense_value = *cp++; + bgp->damage_type = *(int *)cp; + cp += 4; + // SwapLongBytes(&bgp->damage_type); + bgp->penetration = *cp++; + + bgp->max_charge = *cp++; + bgp->attack_mass = *cp++; + bgp->attack_range = *cp++; + bgp->attack_speed = *(short *)cp; + cp += 2; + // SwapShortBytes(&bgp->attack_speed); + } + + for (i = 0; i < NUM_BEAMPROJ_GUN; i++) { + BeamprojGunProp *bgp = &BeamprojGunProps[i]; + + bgp->damage_modifier = *(short *)cp; + cp += 2; + // SwapShortBytes(&bgp->damage_modifier); + bgp->offense_value = *cp++; + bgp->damage_type = *(int *)cp; + cp += 4; + // SwapLongBytes(&bgp->damage_type); + bgp->penetration = *cp++; + + bgp->max_charge = *cp++; + bgp->attack_mass = *cp++; + bgp->attack_speed = *(short *)cp; + cp += 2; + // SwapShortBytes(&bgp->attack_speed); + bgp->speed = *cp++; + bgp->proj_triple = *(int *)cp; + cp += 4; + // SwapLongBytes(&bgp->proj_triple); + bgp->flags = *cp++; + } + + //------------- + // AMMO + //------------- + for (i = 0; i < NUM_AMMO; i++) { + AmmoProp *ap = &AmmoProps[i]; + + ap->damage_modifier = *(short *)cp; + cp += 2; + // SwapShortBytes(&ap->damage_modifier); + ap->offense_value = *cp++; + ap->damage_type = *(int *)cp; + cp += 4; + // SwapLongBytes(&ap->damage_type); + ap->penetration = *cp++; + + ap->cartridge_size = *cp++; + ap->bullet_mass = *cp++; + ap->bullet_speed = *(short *)cp; + cp += 2; + // SwapShortBytes(&ap->bullet_speed); + ap->range = *cp++; + ap->recoil_force = *cp++; + } + + // BlockMoveData(cp, PistolAmmoProps, NUM_PISTOL_AMMO); Dummies + cp += NUM_PISTOL_AMMO; + + // BlockMoveData(cp, PistolAmmoProps, NUM_NEEDLE_AMMO); Dummies + cp += NUM_NEEDLE_AMMO; + + // BlockMoveData(cp, PistolAmmoProps, NUM_MAGNUM_AMMO); Dummies + cp += NUM_MAGNUM_AMMO; + + // BlockMoveData(cp, PistolAmmoProps, NUM_RIFLE_AMMO); Dummies + cp += NUM_RIFLE_AMMO; + + // BlockMoveData(cp, PistolAmmoProps, NUM_FLECHETTE_AMMO); Dummies + cp += NUM_FLECHETTE_AMMO; + + // BlockMoveData(cp, PistolAmmoProps, NUM_AUTO_AMMO); Dummies + cp += NUM_AUTO_AMMO; + + // BlockMoveData(cp, PistolAmmoProps, NUM_PROJ_AMMO); Dummies + cp += NUM_PROJ_AMMO; + + //------------- + // PHYSICS + //------------- + + for (i = 0; i < NUM_PHYSICS; i++) + PhysicsProps[i].flags = *cp++; + + for (i = 0; i < NUM_TRACER_PHYSICS; i++) { + TracerPhysicsProp *tpp = &TracerPhysicsProps[i]; + + memmove(tpp->xcoords, cp, 4 * 2); + cp += 4 * 2; + memmove(tpp->ycoords, cp, 4 * 2); + cp += 4 * 2; + memmove(tpp->zcoords, cp, 4); + cp += 4; + + for (j = 0; j < 4; j++) { + // SwapShortBytes(&tpp->xcoords[j]); + // SwapShortBytes(&tpp->ycoords[j]); + } + } + + memmove(SlowPhysicsProps, cp, sizeof(SlowPhysicsProps)); + cp += sizeof(SlowPhysicsProps); + + // BlockMoveData(cp, CameraPhysicsProps, NUM_CAMERA_PHYSICS); Dummies + cp += NUM_CAMERA_PHYSICS; + + //------------- + // GRENADES + //------------- + + for (i = 0; i < NUM_GRENADE; i++) { + GrenadeProp *gp = &GrenadeProps[i]; + + gp->damage_modifier = *(short *)cp; + cp += 2; + // SwapShortBytes(&gp->damage_modifier); + gp->offense_value = *cp++; + gp->damage_type = *(int *)cp; + cp += 4; + // SwapLongBytes(&gp->damage_type); + gp->penetration = *cp++; + + gp->touchiness = *cp++; + gp->radius = *cp++; + gp->radius_change = *cp++; + gp->damage_change = *cp++; + gp->attack_mass = *cp++; + gp->flags = *(short *)cp; + cp += 2; + // SwapShortBytes(&gp->flags); + } + + // BlockMoveData(cp, DirectGrenadeProps, NUM_DIRECT_GRENADE); Dummies + cp += NUM_DIRECT_GRENADE; + + for (i = 0; i < NUM_TIMED_GRENADE; i++) { + TimedGrenadeProp *tgp = &TimedGrenadeProps[i]; + + tgp->min_time_set = *cp++; + tgp->max_time_set = *cp++; + tgp->timing_deviation = *cp++; + } + + //------------- + // DRUGS + //------------- + // For Mac version: The data for these are all zero (I don't think they're used at all), so don't worry about + // converting, just zero the arrays. + + LG_memset(DrugProps, 0, sizeof(DrugProps)); + cp += NUM_DRUG * 17; + LG_memset(StatsDrugProps, 0, sizeof(StatsDrugProps)); + cp += NUM_STATS_DRUG * 7; + + //------------- + // HARDWARE + //------------- + // For Mac version: The data for these are all zero, so don't worry about + // converting, just zero the arrays. + + LG_memset(HardwareProps, 0, sizeof(HardwareProps)); + cp += NUM_HARDWARE * 2; + LG_memset(GoggleHardwareProps, 0, sizeof(GoggleHardwareProps)); + cp += NUM_GOGGLE_HARDWARE; + LG_memset(HardwareHardwareProps, 0, sizeof(HardwareHardwareProps)); + cp += NUM_HARDWARE_HARDWARE * 2; + + //------------- + // SOFTWARE + //------------- + // For Mac version: The data for these are all zero, so don't worry about + // converting, just zero the arrays. + + LG_memset(SoftwareProps, 0, sizeof(SoftwareProps)); + cp += NUM_SOFTWARE * 2; + cp += NUM_OFFENSE_SOFTWARE; + cp += NUM_DEFENSE_SOFTWARE; + cp += NUM_ONESHOT_SOFTWARE; + cp += NUM_MISC_SOFTWARE; + cp += NUM_DATA_SOFTWARE; + + //------------- + // BIGSTUFF + //------------- + // For Mac version: The data for these are all zero, so don't worry about + // converting, just zero the arrays. + + LG_memset(BigstuffProps, 0, sizeof(BigstuffProps)); + cp += NUM_BIGSTUFF * 4; + cp += NUM_ELECTRONIC_BIGSTUFF; + cp += NUM_FURNISHING_BIGSTUFF; + cp += NUM_ONTHEWALL_BIGSTUFF; + cp += NUM_LIGHT_BIGSTUFF; + cp += NUM_LABGEAR_BIGSTUFF; + cp += NUM_TECHNO_BIGSTUFF; + cp += NUM_DECOR_BIGSTUFF; + cp += NUM_TERRAIN_BIGSTUFF; + + //------------- + // SMALLSTUFF + //------------- + // For Mac version: Most of this data is all zeros, so in those cases don't worry about + // converting, just zero the arrays. + + LG_memset(SmallstuffProps, 0, sizeof(SmallstuffProps)); + cp += NUM_SMALLSTUFF * 2; + cp += NUM_USELESS_SMALLSTUFF; + cp += NUM_BROKEN_SMALLSTUFF; + cp += NUM_CORPSELIKE_SMALLSTUFF; + cp += NUM_GEAR_SMALLSTUFF; + cp += NUM_CARDS_SMALLSTUFF; + + memmove(CyberSmallstuffProps, cp, sizeof(CyberSmallstuffProps)); + cp += sizeof(CyberSmallstuffProps); + + cp += NUM_ONTHEWALL_SMALLSTUFF; + + LG_memset(PlotSmallstuffProps, 0, sizeof(PlotSmallstuffProps)); + cp += NUM_PLOT_SMALLSTUFF * 2; + + //------------- + // FIXTURES + //------------- + // For Mac version: The data for these are all zero, so don't worry about + // converting, just zero the arrays. + + LG_memset(FixtureProps, 0, sizeof(FixtureProps)); + cp += NUM_FIXTURE; + cp += NUM_CONTROL_FIXTURE; + cp += NUM_RECEPTACLE_FIXTURE; + cp += NUM_TERMINAL_FIXTURE; + cp += NUM_PANEL_FIXTURE; + cp += NUM_VENDING_FIXTURE; + cp += NUM_CYBER_FIXTURE; + + //------------- + // DOORS + //------------- + // For Mac version: The data for these are all zero, so don't worry about + // converting, just zero the arrays. + + LG_memset(DoorProps, 0, sizeof(DoorProps)); + cp += NUM_DOOR; + cp += NUM_NORMAL_DOOR; + cp += NUM_DOORWAYS_DOOR; + cp += NUM_FORCE_DOOR; + cp += NUM_ELEVATOR_DOOR; + cp += NUM_SPECIAL_DOOR; + + cp -= 2; // We got off here somehow. Check into it!!! + + //---------------- + // ANIMATING OBJECTS + //---------------- + + memmove(AnimatingProps, cp, sizeof(AnimatingProps)); + cp += sizeof(AnimatingProps); + + cp += NUM_OBJECT_ANIMATING; + cp += NUM_TRANSITORY_ANIMATING; + + for (i = 0; i < NUM_EXPLOSION_ANIMATING; i++) + ExplosionAnimatingProps[i].frame_explode = *cp++; + + //---------------- + // TRAPS + //---------------- + // For Mac version: These are all dummy arrays, so just skip over the data here. + + cp += NUM_TRAP; + cp += NUM_TRIGGER_TRAP; + cp += NUM_FEEDBACKS_TRAP; + cp += NUM_SECRET_TRAP; + + //---------------- + // CONTAINERS + //---------------- + // For Mac version: The data for these are all zero, so don't worry about + // converting, just zero the arrays. + + LG_memset(ContainerProps, 0, sizeof(ContainerProps)); + cp += NUM_CONTAINER * 3; + cp += NUM_ACTUAL_CONTAINER; + cp += NUM_WASTE_CONTAINER; + cp += NUM_LIQUID_CONTAINER; + cp += NUM_MUTANT_CORPSE_CONTAINER; + cp += NUM_ROBOT_CORPSE_CONTAINER; + cp += NUM_CYBORG_CORPSE_CONTAINER; + cp += NUM_OTHER_CORPSE_CONTAINER; + + //---------------- + // CRITTERS + //---------------- + + for (i = 0; i < NUM_CRITTER; i++) { + CritterProp *crp = &CritterProps[i]; + + crp->intelligence = *cp++; + for (j = 0; j < NUM_ALTERNATE_ATTACKS; j++) { + crp->attacks[j].damage_type = *(int *)cp; + cp += 4; + // SwapLongBytes(&crp->attacks[j].damage_type); + crp->attacks[j].damage_modifier = *(short *)cp; + cp += 2; + // SwapShortBytes(&crp->attacks[j].damage_modifier); + crp->attacks[j].offense_value = *cp++; + crp->attacks[j].penetration = *cp++; + crp->attacks[j].attack_mass = *cp++; + crp->attacks[j].attack_velocity = *(short *)cp; + cp += 2; + // SwapShortBytes(&crp->attacks[j].attack_velocity); + crp->attacks[j].accuracy = *cp++; + crp->attacks[j].att_range = *cp++; + crp->attacks[j].speed = *(int *)cp; + cp += 4; + // SwapLongBytes(&crp->attacks[j].speed); + crp->attacks[j].slow_proj = *(int *)cp; + cp += 4; + // SwapLongBytes(&crp->attacks[j].slow_proj); + } + crp->perception = *cp++; + crp->defense = *cp++; + crp->proj_offset = *cp++; + crp->flags = *(int *)cp; + cp += 4; + // SwapLongBytes(&crp->flags); + crp->mirror = *cp++; + for (j = 0; j < NUM_CRITTER_POSTURES; j++) + crp->frames[j] = *cp++; + crp->anim_speed = *cp++; + crp->attack_sound = *cp++; + crp->near_sound = *cp++; + crp->hurt_sound = *cp++; + crp->death_sound = *cp++; + crp->notice_sound = *cp++; + crp->corpse = *(int *)cp; + cp += 4; + // SwapLongBytes(&crp->corpse); + crp->views = *cp++; + crp->alt_perc = *cp++; + crp->disrupt_perc = *cp++; + crp->treasure_type = *cp++; + crp->hit_effect = *cp++; + crp->fire_frame = *cp++; + } + + cp += NUM_MUTANT_CRITTER; + + memmove(RobotCritterProps, cp, sizeof(RobotCritterProps)); + cp += sizeof(RobotCritterProps); + + memmove(CyborgCritterProps, cp, sizeof(CyborgCritterProps)); + cp += sizeof(CyborgCritterProps); + for (i = 0; i < NUM_CYBORG_CRITTER; i++) + // SwapShortBytes(&CyborgCritterProps[i].shield_energy); + + for (i = 0; i < NUM_CYBER_CRITTER; i++) { + CyberCritterProp *ccp = &CyberCritterProps[i]; + + for (j = 0; j < NUM_VCOLORS; j++) + ccp->vcolors[j] = *cp++; + for (j = 0; j < NUM_VCOLORS; j++) + ccp->alt_vcolors[j] = *cp++; + } + + cp += NUM_ROBOBABE_CRITTER; + + //----------------------- + // GENERAL OBJECT PROPERTIES + //----------------------- + + for (i = 0; i < NUM_OBJECT; i++) { + ObjProp *op = &ObjProps[i]; + + memmove(op, cp, 27); + cp += 27; + } + + // HUnlock(res); + // ReleaseResource(res); + + return (OK); +} + +errtype obj_set_secondary_properties() { + char i, j; + RefTable *prt; + int fn, fn2; + + fn = ResOpenFile("res/data/objart2.res"); + fn2 = ResOpenFile("res/data/objart3.res"); + if ((fn < 0) || (fn2 < 0)) { + // Warning(("Problem opening object art cache!\n")); + return (ERR_NOEFFECT); + } + + // Now go and set specific computed data, like frame counts + for (i = 0; i < NUM_CRITTER; i++) { + // KLC size = 0; + if (CritterProps[i].views > 1) { + for (j = 0; j < NUM_CRITTER_POSTURES; j++) { + Id id; + if (j >= FIRST_FRONT_POSTURE) { + id = posture_bases[j] + i; + prt = ResReadRefTable(id); // prt = (RefTable *)ResLock(id); + // KLC size += ResSize(posture_bases[j] + i); + } else { + id = critter_id_table[i] + posture_bases[j] + 6; // assumes view = front = 6 + prt = ResReadRefTable(id); // prt = (RefTable *)ResLock(id); + // KLC size += ResSize(posture_bases[j] + critter_id_table[i] + 6); + } + if (prt == NULL) + ; + // Warning (("Could not read RefTable for creature type %d (j = %d) (id = %d + %d + //= %d (0x%x))!\n",i,j, + // critter_id_table[i],posture_bases[j],critter_id_table[i] + posture_bases[j], + //critter_id_table[i] + posture_bases[j])); + else { + CritterProps[i].frames[j] = prt->numRefs; + ResFreeRefTable(prt); + // ResUnlock(id); + // ResDrop(id); + } + } + } + } + ResCloseFile(fn); + ResCloseFile(fn2); + return (OK); +} + +// this function isnt a rep exposure at all, except knowing internal structure order +errtype obj_zero_unused(void) { + ObjID id; + int cl; + ObjSpecID specid; + int counters[2][2]; + + LG_memset(counters, 0, 4 * sizeof(int)); + for (cl = CLASS_GUN; cl < NUM_CLASSES; cl++) { + ObjSpecHeader *curHead = &objSpecHeaders[cl]; /* get our special class header data */ + for (specid = 1; specid < curHead->size; + specid++) { /* find the base of our obj and cast it to a ObjSpec common header struct */ + ObjSpec *curSpec = (ObjSpec *)(curHead->data + (curHead->struct_size * specid)); + if (!objs[id = curSpec->bits.id].active) /* toast all but the Spec part */ + LG_memset(((char *)curSpec) + sizeof(ObjSpec), 0, curHead->struct_size - sizeof(ObjSpec)); + counters[0][objs[id].active ? 1 : 0]++; + } + } + for (id = 0; id < NUM_OBJECTS; id++) /* go through all objects, though i bet they are all seen above */ + if (!objs[id].active) /* and thus we should be able to skip this, i bet */ + { + LG_memset(&objs[id].active, 0, ((uchar *)&objs[id].ref) - ((uchar *)&objs[id].active)); + LG_memset(&objs[id].loc, 0, sizeof(ObjLoc) + sizeof(ObjInfo)); + counters[1][0]++; + } else + counters[1][1]++; + // mprintf("Counters were %d %d and %d %d\n",counters[0][0],counters[0][1],counters[1][0],counters[1][1]); + return OK; +} + +ObjID physics_handle_to_id(physics_handle p) { + if (p > MAX_OBJ) + return (OBJ_NULL); + else + return (physics_handle_id[p]); +} + + /* KLC - not used + uchar get_obj_radii(Obj *objp, fix *rad) + { + Robot temp_robot; + + if (objp->info.ph != -1) + { + EDMS_get_robot_parameters(objp->info.ph, &temp_robot); + *rad = temp_robot.size; + } + else + *rad = 0; + + // *rad1=fix_make(ObjProps[objtrip].physics_xr,0)/96; + // *rad2=fix_make(0,0x4000); // probably should be something else here, eh? + // return TRUE; + return FALSE; + } + */ + +#define ICON_ID_BASE RES_bmIconArt_0 +#define GRAF_ID_BASE RES_bmGraffitiArt_0 +#define REPL_ID_BASE RES_bmRepulsArt_0 + +Ref obj_cache_ref(ObjID id) { + Ref retval = OBJ_REF_NULL; + switch (objs[id].obclass) { + case CLASS_DOOR: + retval = MKREF(door_id(id), objs[id].info.current_frame); + break; + case CLASS_BIGSTUFF: + if (objs[id].subclass == BIGSTUFF_SUBCLASS_ONTHEWALL) { + switch (ID2TRIP(id)) { + case ICON_TRIPLE: + retval = MKREF(ICON_ID_BASE, objs[id].info.current_frame); + break; + case GRAF_TRIPLE: + retval = MKREF(GRAF_ID_BASE, objs[id].info.current_frame); + break; + case REPULSWALL_TRIPLE: + retval = MKREF(REPL_ID_BASE, objs[id].info.current_frame); + break; + default: + break; + } + } + break; + } + return (retval); +} + +#define MEDIAN_WORD_SCALE 4 + +grs_bitmap *get_text_bitmap_from_string(int d1, char dest_type, char *s, uchar scroll, int scroll_index) { + Id currfont; + short w, h, c, x; + char sc_count = 0; + char size_remaining = text_bitmaps_y[dest_type]; + char curr_y = 1; + + gr_push_canvas(&text_canvases[dest_type]); + + gr_clear(0); + + c = d1 >> 16; + if (c == 0) + c = RED_BASE + 6; + gr_set_fcolor(c); + switch (d1 & 0xF) { + case 1: + currfont = RES_graffitiFont; + break; + case 2: + currfont = RES_smallTechFont; + break; + case 3: + currfont = RES_largeTechFont; + break; + default: + currfont = RES_citadelFont; + break; + } + gr_set_font(ResLock(currfont)); + gr_string_size(s, &w, &h); + while (size_remaining > h) { + if (scroll) + gr_string(s, 1, curr_y); + else { + x = (text_bitmaps_x[dest_type] - w) >> 1; + if (x < 1) + x = 1; + gr_string(s, x, (text_bitmaps_y[dest_type] - h) >> 1); + } + if (!scroll) + size_remaining = 0; + else { + sc_count++; + size_remaining -= h; + curr_y += h + 1; + s = get_temp_string(text_bitmap_refs[dest_type] + scroll_index + sc_count); + gr_string_size(s, &w, &h); + } + } + ResUnlock(currfont); + gr_pop_canvas(); + return (text_bitmap_ptrs[dest_type]); +} + +grs_bitmap *get_text_bitmap(int d1, int d2, char dest_type, uchar scroll) { + char *str = get_temp_string(text_bitmap_refs[dest_type] + d2); + if (str == NULL) { + return NULL; + } + return (get_text_bitmap_from_string(d1, dest_type, str, scroll, d2)); +} + +grs_bitmap *get_text_bitmap_obj(ObjID cobjid, char dest_type, char *pscale) { + char sval = (objBigstuffs[objs[cobjid].specID].data1 & 0xF0) >> 4; + if (sval == 0) + *pscale = 0; + else + *pscale = sval - MEDIAN_WORD_SCALE; + return (get_text_bitmap(objBigstuffs[objs[cobjid].specID].data1, objBigstuffs[objs[cobjid].specID].cosmetic_value, + dest_type, FALSE)); +} + +errtype obj_settle_func(ObjID id) { + int retval; + if (!CHECK_OBJ_PH(id)) + return (OK); + // if ((!global_fullmap->cyber) && (id == PLAYER_OBJ)) + if (!global_fullmap->cyber) + retval = EDMS_settle_object(objs[id].info.ph); + else + retval = TRUE; + // if (retval < 0) + // Warning(("EDMS_settle on id %d is unhappy!\n",id)); + return (OK); +} + +#define DESTROYED_SCREEN_ANIM_BASE 0x1B + +void destroy_screen_callback_func(ObjID id, intptr_t data) { + ObjSpecID osid = objs[id].specID; + objBigstuffs[osid].cosmetic_value = 1; + objBigstuffs[osid].data2 = DESTROYED_SCREEN_ANIM_BASE + 3; + objs[id].info.current_frame = 0; +} + +void diego_teleport_callback(ObjID id, intptr_t data) { obj_destroy(id); } + +// A critter has been killed -- do we let it die like usual or +// do we do something wacky? + +// in gameobj.c also +#define DIEGO_DEATH_BATTLE_LEVEL 8 + +uchar death_check(ObjID id, bool *b) { + extern char damage_sound_fx; + if (ID2TRIP(id) == DIEGO_TRIPLE && player_struct.level != DIEGO_DEATH_BATTLE_LEVEL) { + damage_sound_fx = -1; + play_digi_fx_obj(SFX_TELEPORT, 1, id); + remove_obj_from_animlist(id); + add_obj_to_animlist(id, FALSE, FALSE, FALSE, 32, 1, 0, ANIMCB_REMOVE); + } + return FALSE; +} + +// An object has been destroyed -- now we must consider doing some +// special stuff. Returns TRUE if the regular destruction process +// should continue. +uchar obj_combat_destroy(ObjID id) { + bool retval = TRUE; + ObjSpecID osid = objs[id].specID; + int i, *d1, *d2; + extern ObjID hack_cam_objs[NUM_HACK_CAMERAS]; + extern ObjID hack_cam_surrogates[NUM_HACK_CAMERAS]; + extern ObjID damage_sound_id; + extern char damage_sound_fx; + + // Check to see if we are a camera-surrogate + // or a hack camera itself + for (i = 0; i < NUM_HACK_CAMERAS; i++) { + if ((hack_cam_surrogates[i] == id) || (hack_cam_objs[i] == id)) { + hack_cam_objs[i] = OBJ_NULL; + hack_cam_surrogates[i] = OBJ_NULL; + } + } + + // Actually do any stuff relevant to us in specific + if (is_container(id, &d1, &d2)) { + do_random_loot(id); + spew_contents(id, *d1, (d2 == NULL) ? 0 : *d2); + } + switch (objs[id].obclass) { + case CLASS_CRITTER: + check_deathwatch_triggers(id, FALSE); + mai_monster_defeated(); + if (!global_fullmap->cyber) { + if ((id == player_struct.curr_target) || (player_struct.curr_target == OBJ_NULL)) { + player_struct.curr_target = OBJ_NULL; + mfd_notify_func(MFD_TARGET_FUNC, MFD_TARGET_SLOT, FALSE, MFD_ACTIVE, TRUE); + } + player_struct.num_victories++; + } + ai_critter_die(osid); + if (death_check(id, &retval)) + return retval; + retval = FALSE; + break; + case CLASS_GRENADE: + ADD_DESTROYED_OBJECT(id); + // do_grenade_explosion(id,TRUE); + // objGrenades[osid].unique_id = 0; + break; + case CLASS_SMALLSTUFF: + switch (ID2TRIP(id)) { + case TARGET_TRIPLE: + if (objSmallstuffs[objs[id].specID].data1) + obj_destroy(objSmallstuffs[objs[id].specID].data1); + break; + case MULTIPLEXR_TRIPLE: + player_struct.cspace_time_base = lg_min(CSPACE_MAX_TIME, player_struct.cspace_time_base + CSPACE_MUX_BONUS); + break; + } + break; + case CLASS_BIGSTUFF: + switch (ID2TRIP(id)) { + case TV_TRIPLE: + case MONITOR2_TRIPLE: + case SCREEN_TRIPLE: + case BIGSCREEN_TRIPLE: + case SUPERSCREEN_TRIPLE: + remove_obj_from_animlist(id); + objs[id].info.current_frame = 0; + objBigstuffs[osid].cosmetic_value = 4; + objBigstuffs[osid].data1 = 0; + objBigstuffs[osid].data2 = DESTROYED_SCREEN_ANIM_BASE; + add_obj_to_animlist(id, 0, 0, 0, 0, 2, 0, ANIMCB_REMOVE); + damage_sound_fx = SFX_MONITOR_EXPLODE; + damage_sound_id = id; + retval = FALSE; + break; + case CAMERA_TRIPLE: + damage_sound_fx = SFX_CAMERA_EXPLODE; + damage_sound_id = id; + break; + case LARGCPU_TRIPLE: + damage_sound_fx = SFX_CPU_EXPLODE; + damage_sound_id = id; + break; + default: + damage_sound_fx = SFX_DESTROY_BARREL; + damage_sound_id = id; + break; + } + break; + case CLASS_CONTAINER: { + MapElem *pme = MAP_GET_XY(OBJ_LOC_BIN_X(objs[id].loc), OBJ_LOC_BIN_Y(objs[id].loc)); + switch (ID2TRIP(id)) { + case RAD_BARREL_TRIPLE: + damage_sound_fx = SFX_DESTROY_BARREL; + damage_sound_id = id; + me_hazard_rad_set(pme, TRUE); + break; + case TOXIC_BARREL_TRIPLE: + case CHEM_TANK_TRIPLE: + damage_sound_fx = SFX_DESTROY_BARREL; + damage_sound_id = id; + if (!level_gamedata.hazard.zerogbio) + me_hazard_bio_set(pme, TRUE); + break; + case SML_CRT_TRIPLE: + case LG_CRT_TRIPLE: + case SECURE_CONTR_TRIPLE: + damage_sound_fx = SFX_DESTROY_CRATE; + damage_sound_id = id; + break; + } + } break; + } + return (retval); +} + +errtype obj_floor_func(ObjID id); + +#define DEFAULT_AI_WAIT 15 +ObjID object_place(int triple, LGPoint square) { + ObjID new_id; + ObjLoc loc; + short flrh; + errtype retval; + int newsize; + + new_id = obj_create_base(triple); + if (new_id == OBJ_NULL) { + return (OBJ_NULL); + } + flrh = me_height_flr(MAP_GET_XY(square.x, square.y)); + newsize = ObjProps[OPTRIP(triple)].physics_xr; + if (newsize == 0) + newsize = standard_robot.size; + else // convert from newstuff.otx format to appropriate physics units + newsize = fix_make(newsize, 0) / PHYSICS_RADIUS_UNIT; + + loc.z = obj_height_from_fix(fix_from_map_height(flrh) + newsize); + + loc.p = 0; + loc.h = 0; + loc.b = 0; + loc.x = (square.x << 8) + 0x80; + loc.y = (square.y << 8) + 0x80; + if (ObjProps[OPNUM(new_id)].flags & EDMS_PRESERVE) + retval = obj_move_to(new_id, &loc, TRUE); + else + retval = + obj_move_to(new_id, &loc, FALSE); // so that things that don't care about physics don't get physics models + if (retval != OK) + ObjDel(new_id); + if (CHECK_OBJ_PH(new_id)) { + obj_settle_func(new_id); + cit_sleeper_callback(objs[new_id].info.ph); // rock-a-bye, object. + edms_delete_go(); + } else + obj_floor_func(new_id); + if (objs[new_id].obclass == CLASS_CRITTER) { + // Randomize some initial data + objCritters[objs[new_id].specID].wait_frames = rand() % DEFAULT_AI_WAIT; + objCritters[objs[new_id].specID].path_id = -1; + objs[new_id].info.current_frame = rand() % (lg_max(1, CritterProps[CPNUM(new_id)].frames[0] - 2)); + } + return (new_id); +} + +ushort obj_floor_compute(ObjID id, uchar flrh) { + fix newsize; + + if (ObjProps[OPNUM(id)].render_type == FAUBJ_TEXTPOLY +#ifndef NO_ANTIGRAV_CRATES + || ObjProps[OPNUM(id)].render_type == FAUBJ_SPECIAL +#endif + ) + newsize = 0; + else { + newsize = ObjProps[OPNUM(id)].physics_z; + if (newsize == 0) + newsize = ObjProps[OPNUM(id)].physics_xr; + if (newsize == 0) + newsize = standard_robot.size; + else // convert from newstuff.otx format to appropriate physics units + newsize = fix_make(newsize, 0) / PHYSICS_RADIUS_UNIT; + } + return (obj_height_from_fix(fix_from_map_height(flrh) + newsize)); +} + +ushort obj_floor_height(ObjID id) { + return (obj_floor_compute(id, me_height_flr(MAP_GET_XY(OBJ_LOC_BIN_X(objs[id].loc), OBJ_LOC_BIN_Y(objs[id].loc))))); +} + +errtype obj_floor_func(ObjID id) { + void edms_delete_go(void); + + ObjLoc newloc = objs[id].loc; + newloc.z = obj_floor_height(id); + obj_move_to(id, &newloc, TRUE); + obj_settle_func(id); + edms_delete_go(); + return (OK); +} + +#ifdef NOT_YET // later + +#ifdef PLAYTEST +#pragma disable_message(202) +uchar global_settle_func(short keycode, ulong context, void *data) { + ObjID oid; + message_info("settling all objects."); + FORALLOBJS(oid) { obj_settle_func(oid); } + return (FALSE); +} + +uchar global_floor_func(short keycode, ulong context, void *data) { + ObjID oid; + message_info("flooring all objects."); + FORALLOBJS(oid) { obj_floor_func(oid); } + return (FALSE); +} + +uchar check_objsys_func(short keycode, ulong context, void *data) { + int i; + char buf[64]; + extern char *get_object_lookname(ObjID id, char use_string[], int sz); + Warning(("Checking objsys, looking for bad geninv\n")); + for (i = 0; i < NUM_GENERAL_SLOTS; i++) { + if (player_struct.inventory[i] != OBJ_NULL) { + if (!objs[player_struct.inventory[i]].active) + Warning(("HEY, geninv %d, id 0x%x, is not active! Ack!!!\n", i, player_struct.inventory[i])); + else + Warning(("%d: %s\n", i, get_object_lookname(player_struct.inventory[i], buf, 64))); + } + } + if (ObjSysOkay()) + message_info("ObjSys OKAY"); + else + message_info("ObjSys BAD!"); + return (FALSE); +} + + // Just compile in whichever hack it is you want to + // use to munge all the objects on the level + + //#define TEXTURE_CRUNCH_HACK + //#define DELTA_FILENAME "changepx.lst" + //#define SEVERED_HEAD_MUNGE + //#define NO_REFS_MUNGE + //#define CLEAR_CREATURE_PATHFIND + //#define CRITTER_HP_CONVERT + //#define CRITTER_FLAG_CLEAR + //#define CRITTER_HP_SETNORM + //#define NULL_OBJ_OBJREF_HACK + //#define REFLOOR_CRATES_HACK + //#define DOOR_HEIGHT_SQUARE + //#define ELDER_DEMON_EXORCISM + //#define TEETH + //#define ELEVATOR_CHECKERBOARD + //#define PARAMETER_DESTRUCTION + +#ifdef PARAMETER_DESTRUCTION +#include +#include +#endif + +#ifdef ELDER_DEMON_EXORCISM +#define MAX_EXOR 10 +#endif + +#ifdef CRITTER_HP_CONVERT +static short old_critter_hp[] = {25, 325, 400, 160, 200, 65, 60, 300, 150, 0, 50, 20, 160, + 0, 125, 225, 450, 15, 110, 60, 0, 65, 180, 45, 275, 400, + 550, 450, 30, 60, 250, 250, 150, 400, 60, 750, 400}; +#endif + +errtype obj_level_munge() { + short count = 0; +#ifdef ELDER_DEMON_EXORCISM + ObjID oid; + ObjRefID oref; + short x, y; + MapElem *pme; + uchar found; + char buf[128]; + ObjID exorcism[MAX_EXOR]; + char exorcise_count = 0; +#endif +#ifdef REFLOOR_CRATES_HACK + ObjID oid; +#endif +#ifdef NO_REFS_MUNGE + ObjID oid, next; +#endif +#ifdef CLEAR_CREATURE_PATHFIND + ObjSpecID osid; + ObjID id; +#endif +#ifdef NULL_OBJ_OBJREF_HACK + ObjRefID orefid, nextref, oref2; + short x, y; +#endif +#ifdef ELEVATOR_CHECKERBOARD + short x, y; + MapElem *pme; + + for (x = 0; x < MAP_XSIZE; x++) { + for (y = 0; y < MAP_YSIZE; y++) { + pme = MAP_GET_XY(x, y); + if ((count % 2) == 0) + me_bits_music_set(pme, 7); + count++; + } + } + +#endif + +#ifdef PARAMETER_DESTRUCTION + { + short x, y; + MapElem *pme; + uchar par, chgt, mir, tt; + + for (x = 0; x < MAP_XSIZE; x++) { + for (y = 0; y < MAP_YSIZE; y++) { + pme = MAP_GET_XY(x, y); + tt = me_tiletype(pme); + + if (tt < TILE_SLOPEUP_N && (par = me_param(pme)) != 0) { + mir = me_bits_mirror(pme); + if (mir == MAP_MATCH || mir == MAP_FFLAT) { + chgt = me_height_ceil(pme) + par; + if (chgt >= MAP_HEIGHTS) + chgt = MAP_HEIGHTS - 1; + me_height_ceil_set(pme, chgt); + } + me_param_set(pme, 0); + } + } + } + } +#endif +#ifdef ELDER_DEMON_EXORCISM + FORALLOBJS(oid) { +#ifdef TEETH + Warning(("checking id %x\n", oid)); +#endif + found = FALSE; + for (x = 0; x < MAP_XSIZE; x++) { + for (y = 0; y < MAP_YSIZE; y++) { + pme = MAP_GET_XY(x, y); + oref = me_objref(pme); + while (oref != OBJ_REF_NULL) { + if (objRefs[oref].obj == oid) { + found = TRUE; + x = MAP_XSIZE; + y = MAP_YSIZE; + break; + } + oref = objRefs[oref].next; + } + } + } + if (!found) { + extern char *get_object_lookname(ObjID id, char use_string[], int sz); + Warning(("HEY, id %x, a %s, may have the taint of Shadow!\n", oid, get_object_lookname(oid, buf, 128))); + Warning(("id %x, ref = %x\n", oid, objs[oid].ref)); + exorcism[exorcise_count++] = oid; + } + } + Warning(("done scanning...\n")); +#ifdef TEETH + for (x = 0; x < exorcise_count; x++) { + extern ObjID ObjRefFree(ObjRefID this, uchar cleanup); + oref = objs[exorcism[x]].ref; + objRefs[oref].next = OBJ_REF_NULL; + ObjRefFree(oref, TRUE); + Warning(("Hey, deleted the ref (%x) for %x!\n", oref, exorcism[x])); + } + for (x = 0; x < exorcise_count; x++) { + objs[exorcism[x]].ref = OBJ_REF_NULL; + obj_destroy(exorcism[x]); + // ObjDel(exorcism[x]); + Warning(("deleted object %x!\n", exorcism[x])); + } +#endif + Warning(("done with ritual (ok = %d)!\n", ObjSysOkay())); +#endif + +#ifdef REFLOOR_CRATES_HACK + for (oid = (objs[OBJ_NULL]).headused; oid != OBJ_NULL; oid = objs[oid].next) { + switch (ID2TRIP(oid)) { + case SML_CRT_TRIPLE: + case LG_CRT_TRIPLE: + case SECURE_CONTR_TRIPLE: + case RAD_BARREL_TRIPLE: + case TOXIC_BARREL_TRIPLE: + case CHEM_TANK_TRIPLE: + obj_floor_func(oid); + break; + } + } +#endif + +#ifdef NULL_OBJ_OBJREF_HACK + for (x = 0; x < MAP_XSIZE; x++) { + for (y = 0; y < MAP_YSIZE; y++) { + orefid = me_objref(MAP_GET_XY(x, y)); + while (orefid != OBJ_REF_NULL) { + nextref = objRefs[orefid].next; + if (objRefs[orefid].obj == OBJ_NULL) { + Warning(("****** Deleting objref %d!\n", orefid)); + me_objref_set(MAP_GET_XY(x, y), OBJ_REF_NULL); + oref2 = objRefs[0].next; + while (objRefs[oref2].next != OBJ_REF_NULL) { + mprintf("."); + oref2 = objRefs[oref2].next; + } + Warning(("objRefs[%d].next = %d\n", oref2, objRefs[oref2].next)); + objRefs[oref2].next = orefid; + Warning(("after: objRefs[%d].next = %d\n", oref2, objRefs[oref2].next)); + objRefs[orefid].next = OBJ_REF_NULL; + } + orefid = nextref; + } + } + } +#endif + +#ifdef CRITTER_FLAG_CLEAR + ObjSpecID osid; + ObjID id; + osid = objCritters[0].id; + while (osid != OBJ_SPEC_NULL) { + id = objCritters[osid].id; + objCritters[osid].flags = 0; + osid = objCritters[osid].next; + } +#endif + +#ifdef DOOR_HEIGHT_SQUARE + { + ObjSpecID osid; + ObjID id; + int z; + + osid = objDoors[0].id; + while (osid != OBJ_SPEC_NULL) { + id = objDoors[osid].id; + + if (objs[id].loc.p == 0) { + z = objs[id].loc.z; + z = (z + 4) & (~7); + objs[id].loc.z = z; + } + osid = objDoors[osid].next; + } + } +#endif + +#ifdef CLEAR_CREATURE_PATHFIND + osid = objCritters[0].id; + while (osid != OBJ_SPEC_NULL) { + id = objCritters[osid].id; + objCritters[osid].path_id = -1; + objCritters[osid].des_speed = 0; + objCritters[osid].urgency = 0; + osid = objCritters[osid].next; + } + used_paths = 0; +#endif + +#ifdef TEXTURE_CRUNCH_HACK + extern errtype texture_crunch_go(); + texture_crunch_go(); +#endif + +#ifdef SEVERED_HEAD_MUNGE + ObjSpecID osid; + ObjID id; + + osid = objSmallstuffs[0].id; + while (osid != OBJ_SPEC_NULL) { + id = objSmallstuffs[osid].id; + if (ID2TRIP(id) == HEAD_TRIPLE || ID2TRIP(id) == HEAD2_TRIPLE) { + objs[id].info.make_info = 0; + } + osid = objSmallstuffs[osid].next; + } +#endif + +#ifdef CRITTER_HP_CONVERT + { + ObjSpecID osid; + ObjID id; + int hp; + + osid = objCritters[0].id; + while (osid != OBJ_SPEC_NULL) { + id = objCritters[osid].id; + hp = objs[id].info.current_hp; + { + hp *= ObjProps[OPNUM(id)].hit_points; + hp /= old_critter_hp[get_nth_from_triple(ID2TRIP(id))]; + objs[id].info.current_hp = hp; + } + + osid = objCritters[osid].next; + } + } +#endif + +#ifdef CRITTER_HP_SETNORM + { + ObjSpecID osid; + ObjID id; + int hp; + + osid = objCritters[0].id; + while (osid != OBJ_SPEC_NULL) { + id = objCritters[osid].id; + objs[id].info.current_hp = ObjProps[OPNUM(id)].hit_points; + + osid = objCritters[osid].next; + } + } +#endif + +#ifdef NO_REFS_MUNGE + for (oid = objs[OBJ_NULL].headused; oid != OBJ_NULL; oid = next) { + next = objs[oid].next; + if (objs[oid].ref == OBJ_REF_NULL) { + ObjDel(oid); + } + } +#endif + +#ifdef LEVEL_MUNGE_HP_HACK + ObjID id; + ObjSpecID osid; + + osid = objBigstuffs[0].id; + while (osid != OBJ_SPEC_NULL) { + id = objBigstuffs[osid].id; + switch (ID2TRIP(id)) { + case GENE_SPLICER_TRIPLE: + case LARGCPU_TRIPLE: + case SCREEN_TRIPLE: + case BIGSCREEN_TRIPLE: + case SUPERSCREEN_TRIPLE: + if (objs[id].info.current_hp != 0) { + objs[id].info.current_hp = ObjProps[OPNUM(id)].hit_points; + count++; + } + break; + } + osid = objBigstuffs[osid].next; + } +#endif + +#ifdef APPLY_SIZE_DELTA_HACK + short size_conv[NUM_OBJECT]; + FILE *f; + short i, sc; + char temp1[50]; + fix old_ht; + ObjID oid; + + // Fill it up with vomitous spew + f = fopen(DELTA_FILENAME, "r"); + i = 0; + while (!feof(f) && (i < NUM_OBJECT)) { + fgets(temp1, 50, f); + size_conv[i] = atoi(temp1); + Spew(DSRC_TESTING_Test4, ("size_conv[%d] = %d temp1=%s", i, size_conv[i], temp1)); + i++; + } + fclose(f); + + FORALLOBJS(oid) { + if ((ObjProps[OPNUM(oid)].render_type == FAUBJ_BITMAP) || + (ObjProps[OPNUM(oid)].render_type == FAUBJ_MULTIVIEW)) { + sc = size_conv[OPNUM(oid)]; + if (sc != 0) { + count++; + + old_ht = fix_from_obj_height(oid); + objs[oid].loc.z = obj_height_from_fix(old_ht + (fix_make(sc, 0) / PHYSICS_RADIUS_UNIT)); + } + } + } +#endif + +#ifdef FRESHEN_CORPSES_HACK + ObjID oid; + FORALLOBJS(oid) { + if ((ID2TRIP(oid) >= CORPSE1_TRIPLE) && (ID2TRIP(oid) <= CORPSE8_TRIPLE)) { + objs[oid].info.inst_flags |= CLASS_INST_FLAG; + count++; + } + } +#endif + + Spew(DSRC_EDITOR_Modify, ("munged %d objects!\n", count)); + Spew(DSRC_TESTING_Test4, ("munged %d objects!\n", count)); + + return (OK); +} +#pragma enable_message(202) +#endif + +#ifdef LOUD_REFRESH +void spew_about_stuff(char *txt, ObjID id) { + State new_state; + EDMS_get_state(objs[id].info.ph, &new_state); + mprintf("id %x %s: %x %x %x %x %x %x \n dots %x %x %x %x %x %x\n", id, txt, new_state.X, new_state.Y, + new_state.Z, new_state.alpha, new_state.beta, new_state.gamma, new_state.X_dot, new_state.Y_dot, + new_state.Z_dot, new_state.alpha_dot, new_state.beta_dot, new_state.gamma_dot); +} +#else +#define spew_about_stuff(txt, id) +#endif + +#endif // NOT_YET + +extern uchar robot_antisocial; + +#define MAX_MOVE_OBJS 32 +// uchar of height above the ground to refresh within +#define REFRESH_HEIGHT 0x10 +errtype obj_physics_refresh(short x, short y, uchar use_floor) { + ObjRefID oref; + ObjID id; + State goof; + int count = 0, i; + ObjID move_list[MAX_MOVE_OBJS]; + + oref = me_objref(MAP_GET_XY(x, y)); + while (oref != OBJ_REF_NULL) { + ObjID oid = objRefs[oref].obj; + if (!ObjCheckDealt(oid)) { + ObjSetDealt(oid); + move_list[count++] = oid; + if (count == MAX_MOVE_OBJS) { + oref = OBJ_REF_NULL; + } else + oref = objRefs[oref].next; + } else + oref = objRefs[oref].next; + } + for (i = 0; i < count; i++) { + id = move_list[i]; + if (id == OBJ_NULL) + ; + else if (id != PLAYER_OBJ) { + // If we are on the floor, then refresh us! + if ((ObjProps[OPNUM(id)].physics_model) && + (use_floor || (objs[id].loc.z > obj_floor_height(id) + REFRESH_HEIGHT))) { + // Spew("objsim", "We're on the floor!\n"); + if (CHECK_OBJ_PH(id)) { + EDMS_get_state(objs[id].info.ph, &goof); + obj_move_to_vel(id, &objs[id].loc, TRUE, goof.X_dot, goof.Y_dot, goof.Z_dot); + EDMS_crystal_meth(objs[id].info.ph); + } else { + // if we're going to wake it up - make it antisocial + // cause everybody is antisocial when they wake up! + robot_antisocial = TRUE; + obj_move_to(id, &objs[id].loc, TRUE); + robot_antisocial = FALSE; + } + } + } + } + return (OK); +} + +errtype obj_physics_refresh_area(short x, short y, uchar use_floor) { + // Spew("objsim", "obj_physics_refresh_area %i %i %i\n", x, y, use_floor); + ObjsClearDealt(); + obj_physics_refresh(x - 1, y, use_floor); + obj_physics_refresh(x + 1, y, use_floor); + obj_physics_refresh(x, y - 1, use_floor); + obj_physics_refresh(x, y + 1, use_floor); + return (obj_physics_refresh(x, y, use_floor)); +} + +uchar obj_is_display(int triple) { + if (ObjProps[OPTRIP(triple)].render_type == FAUBJ_TEXTPOLY) + return (TRUE); + switch (triple) { + case SCREEN_TRIPLE: + case SUPERSCREEN_TRIPLE: + case BIGSCREEN_TRIPLE: + return (TRUE); + } + return (FALSE); +} diff --git a/engine/src/GameSrc/objuse.c b/engine/src/GameSrc/objuse.c new file mode 100644 index 0000000..2080af1 --- /dev/null +++ b/engine/src/GameSrc/objuse.c @@ -0,0 +1,1761 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/objuse.c $ + * $Revision: 1.194 $ + * $Author: dc $ + * $Date: 1994/11/28 06:38:32 $ + */ + +#include +#include + +#include "ai.h" +#include "amap.h" +#include "audiolog.h" +#include "bark.h" +#include "criterr.h" +#include "cyber.h" +#include "cybstrng.h" +#include "damage.h" +#include "diffq.h" +#include "doorparm.h" +#include "effect.h" +#include "email.h" +#include "faketime.h" +#include "fullscrn.h" +#include "gamestrn.h" +#include "gr2ss.h" +#include "hud.h" +#include "ice.h" +#include "input.h" +#include "invent.h" +#include "mainloop.h" +#include "mapflags.h" +#include "mfdfunc.h" +#include "mfdgump.h" +#include "mfdpanel.h" +#include "musicai.h" +#include "newmfd.h" +#include "objbit.h" +#include "objprop.h" +#include "objsim.h" +#include "objstuff.h" +#include "objuse.h" +#include "otrip.h" +#include "physics.h" +#include "player.h" +#include "render.h" +#include "saveload.h" +#include "schedule.h" +#include "sfxlist.h" +#include "target.h" +#include "tilename.h" +#include "tools.h" +#include "trigger.h" +#include "mouselook.h" + +#define MFD_FIXTURE_FLAG 0x8 // class flag for mfd fixtures + +errtype accesspanel_trigger(ObjID id); + +// ----------- +// PROTOTYPES +// ----------- +void zoom_mfd(int mfd, bool shifted); +int grab_and_zoom_mfd(int mfd_func, int mfd_slot, bool shifted); +errtype obj_access_fail_message(int stringref, char access_level, char offset); +uchar really_really_locked(int qvar); +uchar use_door(ObjID id, uchar in_inv, ObjID cursor_obj); +void container_check(ObjID obj, char *count, ObjID *pidlist); +uchar obj_fixture_zoom(ObjID id, uchar in_inv, uchar *messagep); +uchar obj_keypad_crunch(int p, uchar digits[]); +uchar try_use_epick(ObjID panel, ObjID cursor_obj); +ObjID door_in_square(ObjLoc *loc, uchar usable); +void regenetron_door_hack(); +errtype elevator_janitor_run(); + +// not in any way the right way to fix this bug, except +// that it is the fastest way. Doug says go. Ask TJS. +uchar shameful_obselete_flag; + +#define DOOR_TIME_UNIT 2 // how many time-setting units in a second + +void zoom_mfd(int mfd, bool shifted) { + LGRect start = {{-5, -5}, {5, 5}}; + extern LGPoint use_cursor_pos; + LGPoint ucp; + + extern bool DoubleSize; + + if (!shifted) + mouse_look_off(); + + ucp = use_cursor_pos; + if (!DoubleSize) + ss_point_convert(&ucp.x, &ucp.y, TRUE); + RECT_MOVE(&start, ucp); + mfd_zoom_rect(&start, mfd); +} + +int grab_and_zoom_mfd(int mfd_func, int mfd_slot, bool shifted) { + int mfd = mfd_grab_func(mfd_func, mfd_slot); + zoom_mfd(mfd, shifted); + return mfd; +} + +errtype obj_access_fail_message(int stringref, char access_level, char offset) { + char temp[40]; + strcpy(temp, get_temp_string(MKREF(RES_accessCards, access_level << 1))); + strcat(temp, get_string(stringref + offset, NULL, 0)); + message_info(temp); + return (OK); +} + +// dir true for door_closing +uchar door_moving(ObjID door, uchar dir) { + //uchar anim_data_from_id(ObjID id, bool *reverse, bool *cycle); + + bool moving, closing; + + moving = anim_data_from_id(door, &closing, NULL); + return (moving && (!closing == !dir)); +} + +#define COMPAR_ESC_ACCESS 0xFF + +uchar door_locked(ObjID obj) { + ObjSpecID spec = objs[obj].specID; + if (!DOOR_CLOSED(obj)) + return FALSE; + if (objDoors[spec].access_level != COMPAR_ESC_ACCESS && objDoors[spec].access_level != 0) { + int i; + ulong try_combo = 1 << objDoors[spec].access_level; + for (i = 0; i < NUM_GENERAL_SLOTS; i++) { + ObjID try_card = player_struct.inventory[i]; + if (ID2TRIP(try_card) == GENCARDS_TRIPLE) { + if (objSmallstuffs[objs[try_card].specID].data1 & try_combo) { + return FALSE; + } else + return TRUE; + } + } + return TRUE; + } + if (objDoors[spec].locked == 0) + return FALSE; + if (objDoors[spec].access_level == COMPAR_ESC_ACCESS) { + uchar special; + int comp = objTraps[objs[objDoors[spec].locked].specID].comparator; + // zorch the failure message out of the comparator before + // checking. + comp = comp & (~(0xFF << 24)); + return !comparator_check(comp, obj, &special); + } + + return (QUESTBIT_GET(objDoors[spec].locked)); +} + +#ifdef DOOM_EMULATION_MODE +// questbits for doors that are "broken beyond repair" or such, +// and should not be openable even in doom emulation mode. +uchar really_really_locked(int qvar) { return (qvar == 0x5E || qvar == 0xE7); } +#endif + +// Okay, secretly we use our in_inv parameter not to really +// indicate that the door is in anybody's inventory, but as +// follows: +// the 0x1 bit indicates not to use the door's other_half +// the 0x2 bit indicates not to spew any relevant messages, +// and should be used by anyone other than the player trying +// to use the door. +// +uchar use_door(ObjID id, uchar in_inv, ObjID cursor_obj) { + uchar retval = FALSE; + uchar play_fx = FALSE; + uchar use_card = FALSE; + int lqb; + DoorSchedEvent new_event; + ObjID try_card, other; + char i; + + if ((objDoors[objs[id].specID].access_level != 0) +#ifdef DOOM_EMULATION_MODE + && (QUESTVAR_GET(MISSION_DIFF_QVAR) >= 2)) +#else + ) +#endif + { + int try_combo; + uchar rv; + uchar special; + int comp; + + if (objDoors[objs[id].specID].access_level > NUM_ACCESS_CODES) { + if (objDoors[objs[id].specID].access_level == COMPAR_ESC_ACCESS) { + comp = objTraps[objs[objDoors[objs[id].specID].locked].specID].comparator; + rv = comparator_check(comp, id, &special); + if (!rv) { + return ((comp >> 24) && (special == 0)); + } + objDoors[objs[id].specID].access_level = 0; + objDoors[objs[id].specID].locked = 0; + goto access_ok; + } + } + + try_combo = 1 << objDoors[objs[id].specID].access_level; + { + uchar some_card = FALSE; + try_card = OBJ_NULL; + for (i = 0; i < NUM_GENERAL_SLOTS; i++) { + try_card = player_struct.inventory[i]; + if (ID2TRIP(try_card) == GENCARDS_TRIPLE) { + some_card = TRUE; + if (objSmallstuffs[objs[try_card].specID].data1 & try_combo) { + use_card = TRUE; + break; + } + } + } + if (!use_card) { + if (!(in_inv & 0x2)) + obj_access_fail_message(REF_STR_DoorWrongAccess, objDoors[objs[id].specID].access_level, + objDoors[objs[id].specID].stringnum); + retval = TRUE; + goto out; + } + } + } +access_ok: + if (DOOR_CLOSED(id) || door_moving(id, TRUE)) { + if (((lqb = objDoors[objs[id].specID].locked) != 0) && QUESTBIT_GET(objDoors[objs[id].specID].locked) +#ifdef DOOM_EMULATION_MODE + && ((QUESTVAR_GET(MISSION_DIFF_QVAR) >= 1) || really_really_locked(lqb))) +#else + ) +#endif + { + if (!(in_inv & 0x2)) { + if (use_card) + string_message_info(REF_STR_DoorCardGoodButLocked + objDoors[objs[id].specID].stringnum); + else + string_message_info(REF_STR_DoorLocked + objDoors[objs[id].specID].stringnum); + } + retval = TRUE; + } else { + int closetime; + + // This string is strangely here rather than above so as to + // insure that we do no collide with any other messages + if ((use_card) && (!(in_inv & 0x2))) { + char tempbuf[30], tb2[30]; + get_object_short_name(ID2TRIP(try_card), tempbuf, 30); + strcpy(tempbuf, get_temp_string(MKREF(RES_accessCards, (objDoors[objs[id].specID].access_level) << 1))); + strcat(tempbuf, " "); + strcat(tempbuf, get_string(REF_STR_DoorCardGood, tb2, 30)); + message_info(tempbuf); + retval = TRUE; + } + + add_obj_to_animlist(id, FALSE, FALSE, FALSE, 32, 0, 0, 0); // play anim forwards + play_fx = TRUE; + + // remove render-blocking bit, since well, we're open. + objs[id].info.inst_flags &= ~(RENDER_BLOCK_FLAG); + + // If appropriate, have door automatically close again + if (((closetime = objDoors[objs[id].specID].autoclose_time) > 0) && (closetime != NEVER_AUTOCLOSE_COOKIE)) { + ushort new_code; + new_event.timestamp = TICKS2TSTAMP(player_struct.game_time + (CIT_CYCLE * closetime) / DOOR_TIME_UNIT); + new_event.type = DOOR_SCHED_EVENT; + new_event.door_id = id; + + // Compute new secret code, poke it into event and door + new_code = ((objs[id].info.inst_flags >> 6) + 1) & 0x3; + new_event.secret_code = new_code; + objs[id].info.inst_flags &= ~(0x3 << 6); // clear out space for new code + objs[id].info.inst_flags |= (new_code << 6); // set new code + + // schedule us! + schedule_event(&global_fullmap->sched[MAP_SCHEDULE_GAMETIME], (SchedEvent *)&new_event); + } + } + } else { + char real_frame = objs[id].info.current_frame; + add_obj_to_animlist(id, FALSE, TRUE, FALSE, 32, 0, 0, 0); // play anim backwards + play_fx = TRUE; + } + if (play_fx) { + int sfx_id; + switch (ID2TRIP(id)) { + case ACCESS_DOOR_TRIPLE: + case REACTR_DOOR_TRIPLE: + case BLAST_DOOR_TRIPLE: + case STOR_DOOR_TRIPLE: + sfx_id = SFX_DOOR_METAL; + break; + case EXEC_DOOR_TRIPLE: + case IRIS_TRIPLE: + sfx_id = SFX_DOOR_IRIS; + break; + case DOUB_LEFTDOOR_TRIPLE: + case DOUB_RITEDOOR_TRIPLE: + sfx_id = SFX_DOOR_BULKHEAD; + break; + case LABFORCE_TRIPLE: + case BROKLABFORCE_TRIPLE: + case RESFORCE_TRIPLE: + case BROKRESFORCE_TRIPLE: + sfx_id = -1; + break; + case GENFORCE_TRIPLE: + case CYBGENFORCE_TRIPLE: + sfx_id = SFX_DOOR_GRATING; + break; + default: + sfx_id = SFX_DOOR_NORMAL; + break; + } + if (sfx_id != -1) + play_digi_fx_obj(sfx_id, 1, id); + } + if (((other = objDoors[objs[id].specID].other_half) != OBJ_NULL) && !(in_inv & 0x1)) { + uchar otherdoor = objs[other].obclass == CLASS_DOOR; + // use other half if we don't have the same closed-ness, in order to + // cause us to have the same closed-ness. + if (!otherdoor || (door_moving(id, TRUE) != (DOOR_REALLY_CLOSED(other) || door_moving(other, TRUE)))) + object_use(objDoors[objs[id].specID].other_half, otherdoor ? (in_inv | 0x1) : FALSE, OBJ_NULL); + } +out: + return retval; +} + +// Maximum number of objects within another object +#define MAX_CONTAINER_CONTENTS 4 + +// If mission difficulty is low, returns TRUE on objects +// which might require literacy. +uchar obj_too_smart(ObjID id) { + switch (QUESTVAR_GET(MISSION_DIFF_QVAR)) { + case 0: + switch (ID2TRIP(id)) { + case PAPERS_TRIPLE: + case TEXT1_TRIPLE: + case EMAIL1_TRIPLE: + case MAP1_TRIPLE: + case VIDTEX_HARD_TRIPLE: + return (TRUE); + break; + } + case 1: + switch (ID2TRIP(id)) { + case GENCARDS_TRIPLE: + case STDCARD_TRIPLE: + case SCICARD_TRIPLE: + case STORECARD_TRIPLE: + case ENGCARD_TRIPLE: + case MEDCARD_TRIPLE: + case MAINTCARD_TRIPLE: + case ADMINCARD_TRIPLE: + case SECCARD_TRIPLE: + case COMCARD_TRIPLE: + case GROUPCARD_TRIPLE: + case PERSCARD_TRIPLE: + case CYBERCARD_TRIPLE: + return (TRUE); + break; + } + break; + default: + return (FALSE); + break; + } + return (FALSE); +} + +void container_check(ObjID obj, char *count, ObjID *pidlist) { + if (objs[obj].active && !obj_too_smart(obj)) { + if (USE_MODE(obj) == PICKUP_USE_MODE) + pidlist[(*count)++] = obj; + // else + // Warning(("Non-pickup: trip %#x (%#x) in container!!\n",ID2TRIP(obj),obj)); + } +} + +char container_extract(ObjID *pidlist, int d1, int d2) { + char retval = 0; + container_check(d1 & 0xFFFF, &retval, pidlist); + container_check(d1 >> 16, &retval, pidlist); + container_check(d2 & 0xFFFF, &retval, pidlist); + container_check(d2 >> 16, &retval, pidlist); + return (retval); +} + +void container_stuff(ObjID *pidlist, int numobjs, int *d1, int *d2) { + int i; + for (i = numobjs; i < MAX_CONTAINER_CONTENTS; i++) + pidlist[i] = OBJ_NULL; + i = 0; + *d1 = pidlist[i++]; + *d1 |= pidlist[i++] << 16; + if (d2 == NULL) + return; + *d2 = pidlist[i++]; + *d2 |= pidlist[i++] << 16; +} + +// Determines whether or not an object is a "container" and then +// figures out what part of it's instance data is used for holding +// the objects. +// If we want to make more objects have these properties later, here is +// the place to add 'em. +uchar is_container(ObjID id, int **d1, int **d2) { + ObjSpecID specid = objs[id].specID; + uchar retval = FALSE; + if (objs[id].obclass == CLASS_CONTAINER) { + // Containers + *d1 = &objContainers[specid].contents1; + *d2 = &objContainers[specid].contents2; + retval = TRUE; + } else { + switch (ID2TRIP(id)) { + // Smallstuff + case CORPSE1_TRIPLE: + case CORPSE2_TRIPLE: + case CORPSE3_TRIPLE: + case CORPSE4_TRIPLE: + case CORPSE5_TRIPLE: + case CORPSE6_TRIPLE: + case CORPSE7_TRIPLE: + case CORPSE8_TRIPLE: + case BRIEFCASE_TRIPLE: + *d1 = &objSmallstuffs[specid].data1; + *d2 = &objSmallstuffs[specid].data2; + retval = TRUE; + break; + // Bigstuff + case CABINET_TRIPLE: + // Contents in data1, since cosmetic value and data2 taken for texturing + // Maybe we can use cosmetic_value too, although thats mighty non-intuitive + retval = TRUE; + *d1 = &objBigstuffs[specid].data1; + *d2 = NULL; + break; + } + } + return (retval); +} + +uchar obj_fixture_zoom(ObjID id, uchar in_inv, uchar *messagep) { + uchar retval = FALSE; + uchar zoom = (objs[id].info.inst_flags & CLASS_INST_FLAG); + if (zoom && !in_inv) { + int mfd = grab_and_zoom_mfd(MFD_FIXTURE_FUNC, MFD_INFO_SLOT, FALSE); + save_mfd_slot(mfd); + mfd_notify_func(MFD_FIXTURE_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, TRUE); + player_struct.panel_ref = id; + mfd_change_slot(mfd, MFD_INFO_SLOT); + } else { + retval = trap_activate(id, messagep); + if (retval) { + if (objs[id].info.current_frame == 0) + objs[id].info.current_frame = FRAME_NUM_3D(ObjProps[OPTRIP(ID2TRIP(id))].bitmap_3d); + else + objs[id].info.current_frame = 0; + } + } + return (retval); +} + + // GEAR, GEAR, GEAR! + +#define BATTERY_ENERGY_BONUS 0x50 +#define TRACBEAM_ENERGY_COST 0x20 +#define TRACBEAM_DIST_MOD 0x20 +extern ubyte pickup_distance_mod; + +errtype obj_tractor_beam_func(ObjID id, uchar on) { + if (on) { + // Tractor beam turning on + if (player_struct.energy_spend + TRACBEAM_ENERGY_COST > MAX_ENERGY || + player_struct.energy < TRACBEAM_ENERGY_COST) + string_message_info(REF_STR_WareNoPower); + else { + string_message_info(REF_STR_TractorActivate); + pickup_distance_mod += TRACBEAM_DIST_MOD; + objs[id].info.inst_flags |= CLASS_INST_FLAG; + set_player_energy_spend(player_struct.energy_spend + TRACBEAM_ENERGY_COST); + } + } else { + // Tractor beam turning off + set_player_energy_spend(player_struct.energy_spend - TRACBEAM_ENERGY_COST); + objs[id].info.inst_flags &= ~CLASS_INST_FLAG; + pickup_distance_mod -= TRACBEAM_DIST_MOD; + string_message_info(REF_STR_TractorDeactivate); + } + return (OK); +} + +errtype gear_power_outage() { + ObjID obj; + char i; + for (i = 0; i < NUM_GENERAL_SLOTS; i++) { + obj = player_struct.inventory[i]; + if ((obj != OBJ_NULL) && (objs[obj].active)) { + // Turn off any active gear + switch (ID2TRIP(obj)) { + case TRACBEAM_TRIPLE: + if (objs[obj].info.inst_flags & CLASS_INST_FLAG) + obj_tractor_beam_func(obj, FALSE); + break; + } + } + } + return (OK); +} + +// returns TRUE iff we tried to use an electronic pick on the panel. +// +uchar try_use_epick(ObjID panel, ObjID cursor_obj) { + uchar sol; + + if (cursor_obj != OBJ_NULL) { + if (ID2TRIP(cursor_obj) == EPICK_TRIPLE) { + if ((sol = mfd_solve_accesspanel(panel)) == EPICK_SOLVED) { + obj_destroy(cursor_obj); + pop_cursor_object(); + } else + string_message_info(REF_STR_EPickFailure + sol - 1); + } + return TRUE; + } + return FALSE; +} + +#define PLASTIQUE_TIME 10 +extern bool gKeypadOverride; + +bool ObjectUseShifted = FALSE; //set if shift key was held when using object + +// We return whether or not we used the message line. +uchar object_use(ObjID id, uchar in_inv, ObjID cursor_obj) { + uchar retval = FALSE, rv; + ObjFixture *pfixt; + ObjBigstuff *pbigs; + char i; + ObjSpecID osid; + uchar special; + extern char camera_map[NUM_HACK_CAMERAS]; + extern ObjID hack_cam_objs[NUM_HACK_CAMERAS]; + extern ubyte next_text_line; + int *d1, *d2; + bool shifted; + + shifted = ObjectUseShifted; + ObjectUseShifted = FALSE; + + // First, the multi-class behavior objects + if (is_container(id, &d1, &d2)) { + int mfd; + if (id == player_struct.panel_ref && object_on_cursor == 0) { + gump_get_useful(shifted); + return TRUE; + } + mfd = grab_and_zoom_mfd(MFD_GUMP_FUNC, MFD_INFO_SLOT, shifted); + do_random_loot(id); + save_mfd_slot(mfd); + mfd_notify_func(MFD_GUMP_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, TRUE); + + // oh, how we are filled with shame. The gump_idlist doesn't + // get filled until the expose func, one frame from now, so for + // this one frame the idlist and the panel_ref will be out of + // synch. During this frame it is possible to gump_get_useful + // from the wrong idlist for our current panel_ref! A simple + // but inelegant fix to the problem is to null the idlist so that + // gump_pickups cannot work and we do not container_stuff the + // altered idlist into the wrong container. Get it? Good. + gump_clear(); + + player_struct.panel_ref = id; + mfd_change_slot(mfd, MFD_INFO_SLOT); + retval = TRUE; + return retval; + } + osid = objs[id].specID; + + if (global_fullmap->cyber) { + uchar did_something = FALSE; + switch (ID2TRIP(id)) { + case CYBERHEAL_TRIPLE: + if (player_struct.cspace_hp != PLAYER_MAX_HP) { + player_struct.cspace_hp = + lg_min(player_struct.cspace_hp + CYBERHEAL_QUANTITY + objSmallstuffs[objs[id].specID].data1, + PLAYER_MAX_HP); + hud_unset(HUD_CYBERDANGER); + string_message_info(REF_STR_CspaceHeal); + ADD_DESTROYED_OBJECT(id); + chg_set_flg(VITALS_UPDATE); + } else + string_message_info(REF_STR_CspaceMaxHealth); + did_something = TRUE; + break; + case CYBERMINE_TRIPLE: + damage_object(PLAYER_OBJ, CYBERMINE_DAMAGE * (objSmallstuffs[objs[id].specID].data1 + 1), 1, 0); + did_something = TRUE; + break; + case DATALET_TRIPLE: + long_bark(OBJ_NULL, 0, REF_STR_DataletZero + objSmallstuffs[objs[id].specID].data1, 0x4c); + did_something = TRUE; + break; + case CSPACE_EXIT_TRIPLE: + player_struct.cspace_time_base = + lg_max(CSPACE_MIN_TIME, player_struct.cspace_time_base - CSPACE_EXIT_PENALTY); + go_to_different_level(player_struct.realspace_level); + did_something = TRUE; + retval = obj_move_to(PLAYER_OBJ, &player_struct.realspace_loc, TRUE); + break; + case INFONODE_TRIPLE: + long_bark(OBJ_NULL, 0, REF_STR_CspaceInfoBase + objSmallstuffs[objs[id].specID].data1, 0x4c); + did_something = TRUE; + break; + case CYBERCARD_TRIPLE: + if (QUESTVAR_GET(MISSION_DIFF_QVAR) > 1) { + ObjLocState del_loc_state; + // yank the object out of the map. + del_loc_state.obj = id; + del_loc_state.loc = objs[id].loc; + del_loc_state.loc.x = -1; + ObjRefStateBinSetNull(del_loc_state.refs[0].bin); + ObjUpdateLocs(&del_loc_state); + inventory_add_object(id, FALSE); + } + break; + } + if (did_something) + return (TRUE); + } + + switch (objs[id].obclass) { + case CLASS_TRAP: + if (ID2TRIP(id) == MAPNOTE_TRIPLE) { + char buf[80]; + + sprintf(buf, "\"%s\"", amap_note_string(id)); + message_info(buf); + retval = TRUE; + } + break; + case CLASS_DOOR: + retval = use_door(id, in_inv, cursor_obj); + break; + + case CLASS_FIXTURE: + // Deal with some specifics.... + switch (ID2TRIP(id)) { + case ELEPANEL1_TRIPLE: + case ELEPANEL2_TRIPLE: + case ELEPANEL3_TRIPLE: { + if (me_bits_music(MAP_GET_XY(PLAYER_BIN_X, PLAYER_BIN_Y)) != ELEVATOR_ZONE) { + string_message_info(REF_STR_UseTooFar); + retval = TRUE; + } else { + pfixt = &objFixtures[objs[id].specID]; +#ifdef DOOM_EMULATION_MODE + if (QUESTVAR_GET(MISSION_DIFF_QVAR) == 0) { + rv = TRUE; + special = FALSE; + } else +#endif + rv = comparator_check(pfixt->comparator, id, &special); + if (rv || (special != 0)) { + int mfd = grab_and_zoom_mfd(MFD_ELEV_FUNC, MFD_INFO_SLOT, FALSE); + // Set our reference... + save_mfd_slot(mfd); + + // Call appropriate MFD function so that later, in turn, we get called + // First force the slot... + mfd_setup_elevator(pfixt->p4 >> 16, pfixt->p4 & 0xFFFF, player_struct.level, special); + player_struct.panel_ref = id; + mfd_change_slot(mfd, MFD_INFO_SLOT); + } + } + retval = TRUE; + break; + } + + case KEYPAD1_TRIPLE: + case KEYPAD2_TRIPLE: { + pfixt = &objFixtures[objs[id].specID]; + rv = comparator_check(pfixt->comparator, id, &special); +#ifdef DOOM_EMULATION_MODE + if (rv && (QUESTVAR_GET(MISSION_DIFF_QVAR) <= 1)) { + do_multi_stuff(qdata_get(pfixt->p1 >> 16)); + do_multi_stuff(qdata_get(pfixt->p2 >> 16)); + do_multi_stuff(qdata_get(pfixt->p3 >> 16)); + play_digi_fx_obj(SFX_MFD_SUCCESS, 1, id); + } +#endif + else if (rv || (special != 0)) { + int mfd = grab_and_zoom_mfd(MFD_KEYPAD_FUNC, MFD_INFO_SLOT, FALSE); + // Set our reference... + save_mfd_slot(mfd); + + // Call appropriate MFD function so that later, in turn, we get called + // First force the slot... + objs[id].info.current_frame = 1; + gKeypadOverride = TRUE; + mfd_setup_keypad(special); + player_struct.panel_ref = id; + mfd_change_slot(mfd, MFD_INFO_SLOT); + } + retval = TRUE; + break; + } + + case ACCPANEL1_TRIPLE: + case ACCPANEL2_TRIPLE: + case ACCPANEL3_TRIPLE: + case ACCPANEL4_TRIPLE: + case ACCPANEL5_TRIPLE: + case ACCPANEL6_TRIPLE: { + pfixt = &objFixtures[objs[id].specID]; + rv = comparator_check(pfixt->comparator, id, &special); + if (rv) { + int accessmfd = NUM_MFDS; + + if (player_struct.panel_ref != id) { + accessmfd = grab_and_zoom_mfd(mfd_type_accesspanel(id), MFD_INFO_SLOT, FALSE); + save_mfd_slot(accessmfd); + + // Call appropriate MFD function so that later, in turn, we get called + // First force the slot... + objs[id].info.current_frame = 1; + mfd_setup_accesspanel(special, id); + player_struct.panel_ref = id; + } else { + int mfd_id = NUM_MFDS; + if (mfd_yield_func(mfd_type_accesspanel(id), &mfd_id)) { + zoom_mfd(mfd_id, FALSE); + } + } + + // electronic picks work even at difficulty 0, though + // you don't need them. Only automatically solve the + // puzzle if a pick doesn't try to do so for you. + if (!try_use_epick(id, cursor_obj)) { + int info; + + if (player_struct.difficulty[PUZZLE_DIFF_INDEX] == 0) + mfd_solve_accesspanel(id); + else { + + // give us another info MFD for help text. + // note that this all works without assuming + // that we have exactly 2 mfd's. If you want + // to pitch that, you can compact this code + // a little bit just by setting mfd=0, info=1. + + for (info = 0; info < NUM_MFDS; info++) + if (info != accessmfd) { + save_mfd_slot(info); + mfd_change_slot(info, MFD_INFO_SLOT); + break; + } + } + } + if (accessmfd < NUM_MFDS) + mfd_change_slot(accessmfd, MFD_INFO_SLOT); + } + retval = TRUE; + break; + } + + case ENRG_CHARGE_TRIPLE: + pfixt = &objFixtures[objs[id].specID]; + rv = comparator_check(pfixt->comparator, id, &special); + if (rv) { + if (player_struct.game_time >= objFixtures[osid].p4) { + // give the player some juice! + player_struct.energy = lg_min(255, player_struct.energy + objFixtures[osid].p1); + + // update the vitals window + chg_set_flg(VITALS_UPDATE); + + // don't let us get energy until some time later + objFixtures[osid].p4 = (int)(player_struct.game_time + (CIT_CYCLE * objFixtures[osid].p2)); + + string_message_info(REF_STR_Recharge); + play_digi_fx_obj(SFX_ENERGY_RECHARGE, 1, id); + + // Trigger any associated trap + do_multi_stuff(objFixtures[osid].p3); + } else + string_message_info(REF_STR_NoRecharge); + } + retval = TRUE; + break; + + case ANTENNA_PAN_TRIPLE: + if (ID2TRIP(cursor_obj) == PLASTIQUE_TRIPLE) { + DoorSchedEvent new_event; + + if (objs[id].info.current_frame == 1) { + // Putting plastique on the panel. + string_message_info(REF_STR_PlastiqueOn); + obj_destroy(cursor_obj); + pop_cursor_object(); + + // Transmogrify der objectenhausen into a plastiqued panel + objs[id].info.type = 4; + objs[id].info.current_frame = 0; + + // Set a timer to go boom + new_event.type = DOOR_SCHED_EVENT; + new_event.timestamp = TICKS2TSTAMP(player_struct.game_time + (CIT_CYCLE * PLASTIQUE_TIME)); + new_event.door_id = id; + schedule_event(&(global_fullmap->sched[MAP_SCHEDULE_GAMETIME]), (SchedEvent *)&new_event); + + // do any appropriate trap + do_multi_stuff(objFixtures[objs[id].specID].p2); + } else + string_message_info(REF_STR_OpenPanelFirst); + } else { + if (objs[id].info.current_frame) + objs[id].info.current_frame = 0; + else + objs[id].info.current_frame = 1; + } + retval = TRUE; + break; + + default: + switch (objs[id].subclass) { + case FIXTURE_SUBCLASS_RECEPTACLE: + case FIXTURE_SUBCLASS_VENDING: + if ((cursor_obj != OBJ_NULL) || (QUESTVAR_GET(MISSION_DIFF_QVAR) < 1)) { + if (ID2TRIP(id) == RETSCANNER_TRIPLE) { + int head_count = 0; + if (ID2TRIP(cursor_obj) == HEAD_TRIPLE) + head_count = objs[cursor_obj].info.current_frame + 1; + else if (ID2TRIP(cursor_obj) == HEAD2_TRIPLE) + head_count = objs[cursor_obj].info.current_frame + 11 + 1; + if (head_count > 0) { + if (((objFixtures[objs[id].specID].comparator & 0xFFFFFF) == head_count - 1) || + (QUESTVAR_GET(MISSION_DIFF_QVAR) < 1)) { + objs[id].info.current_frame = 1; + obj_fixture_zoom(id, in_inv, &retval); + } else { + objs[id].info.current_frame = 2; + string_message_info(REF_STR_WrongHead); + } + } + } else if (ID2TRIP(cursor_obj) == + (objFixtures[objs[id].specID].comparator & 0xFFFFFF) || + QUESTVAR_GET(MISSION_DIFF_QVAR) < 1) { + obj_fixture_zoom(id, in_inv, &retval); + obj_destroy(cursor_obj); + pop_cursor_object(); + } else if (objFixtures[objs[id].specID].comparator >> 24) { + string_message_info(REF_STR_TrapZeroMessage + (objFixtures[objs[id].specID].comparator >> 24)); +#ifdef AUDIOLOGS + audiolog_bark_play(objFixtures[objs[id].specID].comparator >> 24); +#endif + } + } else if (ID2TRIP(id) == RETSCANNER_TRIPLE) + string_message_info(REF_STR_WrongHead); + retval = TRUE; + break; + default: { + uchar access_okay = FALSE; + int try_combo; + if ((objFixtures[osid].access_level == 0) +#ifdef DOOM_EMULATION_MODE + || (QUESTVAR_GET(MISSION_DIFF_QVAR) < 2) +#endif + ) + access_okay = TRUE; + else { + ObjID try_card; + uchar had_card = FALSE; + try_combo = 1 << objFixtures[osid].access_level; + for (i = 0; i < NUM_GENERAL_SLOTS; i++) { + try_card = player_struct.inventory[i]; + if (ID2TRIP(try_card) == GENCARDS_TRIPLE) { + had_card = TRUE; + if (objSmallstuffs[objs[try_card].specID].data1 & try_combo) + access_okay = TRUE; + else + obj_access_fail_message(REF_STR_FixtureAccessBad, objFixtures[osid].access_level, 0); + } + } + if (!had_card) + obj_access_fail_message(REF_STR_FixtureAccessBad, objFixtures[osid].access_level, 0); + } + if (access_okay) { + if (comparator_check(objFixtures[objs[id].specID].comparator, id, &special)) { + switch (ID2TRIP(id)) { + case BUTTON1_TRIPLE: + case BUTTON2_TRIPLE: + play_digi_fx_obj(SFX_BUTTON, 1, id); + break; + case BIGRED_TRIPLE: + play_digi_fx_obj(SFX_BIGBUTTON, 1, id); + break; + case BIGLEVER_TRIPLE: + play_digi_fx_obj(SFX_BIGLEVER, 1, id); + break; + case LEVER1_TRIPLE: + case LEVER2_TRIPLE: + play_digi_fx_obj(SFX_NORMAL_LEVER, 1, id); + break; + case SWITCH1_TRIPLE: + case SWITCH2_TRIPLE: + play_digi_fx_obj(SFX_MECH_BUTTON, 1, id); + break; + } + obj_fixture_zoom(id, in_inv, &retval); + if (objs[id].subclass == FIXTURE_SUBCLASS_CYBER) { + if (ID2TRIP(id) == CYBERTOG1_TRIPLE) { + objs[id].info.type = 1; + } +#ifdef BROKEN_CYBERTOGS + else if (ID2TRIP(id) == CYBERTOG2_TRIPLE) + objs[id].info.type = 0; +#endif + } + } + } + break; + } + } + retval = TRUE; + break; + } + break; + case CLASS_CRITTER: { + if (cursor_obj == OBJ_NULL) { + if (id != player_struct.curr_target) + select_current_target(id, TRUE); + else + select_current_target(OBJ_NULL, TRUE); + } + } + retval = TRUE; + break; + + case CLASS_SMALLSTUFF: + if (in_inv) { + switch (ID2TRIP(id)) { + case AIDKIT_TRIPLE: + string_message_info(REF_STR_MedikitUse); + player_struct.hit_points = PLAYER_MAX_HP; + goto yankinv; + case BATTERY2_TRIPLE: + player_struct.energy = 255; + case BATTERY_TRIPLE: + player_struct.energy = lg_min(255, player_struct.energy + BATTERY_ENERGY_BONUS); + play_digi_fx(SFX_BATTERY_USE, 1); + yankinv: + remove_general_item(id); + + // Make appropriate UI parts redraw + chg_set_flg(VITALS_UPDATE); + chg_set_flg(INVENTORY_UPDATE); + retval = TRUE; + break; + case TRACBEAM_TRIPLE: + obj_tractor_beam_func(id, (objs[id].info.inst_flags & CLASS_INST_FLAG) == 0); + retval = TRUE; + break; + default: +#ifdef SUPPORT_STUFF_OBJUSE + if (((ObjProps[OPNUM(id)].flags & CLASS_FLAGS) >> CLASS_FLAGS_SHF) == STUFF_OBJUSE_FLAG) { + do_multi_stuff(objSmallstuffs[objs[id].specID].data1 & 0xFFFF); + do_multi_stuff(objSmallstuffs[objs[id].specID].data1 >> 16); + retval = TRUE; + } else +#endif + break; + } + mfd_notify_func(NOTIFY_ANY_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + } else { + switch (ID2TRIP(id)) { + case PAPERS_TRIPLE: + // Note secret perversion of email system + read_email(RES_paper0, objSmallstuffs[objs[id].specID].data1); +#ifdef OLD_WAY + next_text_line = 0; + current_email = 0; + inventory_draw_new_page(EMAILTEXT_INV_PAGE); + if (ResInUse(RES_paper0 + objSmallstuffs[objs[id].specID].data1)) + email_draw_text(RES_paper0 + objSmallstuffs[objs[id].specID].data1, FALSE); + else + email_draw_text(RES_paper0, FALSE); +#endif + retval = TRUE; + break; + } + } + break; + + case CLASS_BIGSTUFF: + switch (ID2TRIP(id)) { + case SURG_MACH_TRIPLE: + pbigs = &objBigstuffs[objs[id].specID]; + rv = comparator_check(pbigs->data1, id, &special); + if (rv) { + if (pbigs->data2 & 0xFFFF) + player_struct.hit_points = + lg_min(PLAYER_MAX_HP, player_struct.hit_points + (pbigs->data2 & 0xFFFF)); + else + player_struct.hit_points = PLAYER_MAX_HP; + chg_set_flg(VITALS_UPDATE); + string_message_info(REF_STR_SurgeryHeal); + trap_sfx_func(0, 0, 2, CIT_CYCLE << 1); + do_multi_stuff(pbigs->data2 >> 16); + play_digi_fx_obj(SFX_SURGERY_MACHINE, 1, id); + } + // return here to prevent 'data1' from being interpreted as object IDs below + return TRUE; + + case CONTPAN_TRIPLE: + case CONTPED_TRIPLE: + if (objBigstuffs[osid].data1) { + break; + } + case TV_TRIPLE: + case MONITOR2_TRIPLE: + case SCREEN_TRIPLE: + case BIGSCREEN_TRIPLE: + case SUPERSCREEN_TRIPLE: { + short v = objBigstuffs[osid].data2 & 0x7F; + if ((v >= FIRST_CAMERA_TMAP) && (v < FIRST_CAMERA_TMAP + NUM_HACK_CAMERAS)) { + if (camera_map[v - FIRST_CAMERA_TMAP] && hack_cam_objs[v - FIRST_CAMERA_TMAP]) + hack_camera_takeover(v - FIRST_CAMERA_TMAP); + } else + string_message_info(REF_STR_NormalScreen); + retval = TRUE; + break; + } + + default: + break; + } + + if (((ObjProps[OPNUM(id)].flags & CLASS_FLAGS) >> CLASS_FLAGS_SHF) == STUFF_OBJUSE_FLAG) { + do_multi_stuff(objBigstuffs[objs[id].specID].data1 & 0xFFFF); + do_multi_stuff(objBigstuffs[objs[id].specID].data1 >> 16); + if (objBigstuffs[objs[id].specID].data1) + retval = TRUE; + } + break; + default: + break; + } + return (retval); +} + +ObjID door_in_square(ObjLoc *loc, uchar usable) { + ObjRefID oref; + ObjID id; + + oref = me_objref(MAP_GET_XY(OBJ_LOC_BIN_X(*loc), OBJ_LOC_BIN_Y(*loc))); + while (oref != OBJ_REF_NULL) { + id = objRefs[oref].obj; + + if (objs[id].obclass == CLASS_DOOR) { + if (!usable || USE_MODE(id) == USE_USE_MODE) + return id; + } + + oref = objRefs[oref].next; + } + return (OBJ_NULL); +} + +void regenetron_door_hack() { + ObjID id; + + id = door_in_square(&(objs[PLAYER_OBJ].loc), TRUE); + + if (id && !door_moving(id, TRUE) && !door_moving(id, FALSE)) + objs[id].info.current_frame = 0; +} + +// Collects all the objects already in the elevator that you are going to +// and move them outside the door +#define MAX_JANITOR_OBJS 32 +errtype elevator_janitor_run() { + short x0, x1, y0, y1, x, y; + int i, j, obj_count = 0; + ObjLoc dump_loc = {0, 0, 0, 0, 0, 0}, newloc; + ObjID objlist[MAX_JANITOR_OBJS], id; + uchar dupe; + ObjRefID orefid; + extern uchar robot_antisocial; + + // clear out our movelist + for (i = 0; i < MAX_JANITOR_OBJS; i++) + objlist[i] = OBJ_NULL; + dump_loc.x = 65535; + + // Compute bounding box of the elevator, see comments in compute_elev_objs + x0 = PLAYER_BIN_X; + y0 = PLAYER_BIN_Y; + while (me_bits_music(MAP_GET_XY(x0, y0)) == ELEVATOR_ZONE) + x0--; + x0++; + while (me_bits_music(MAP_GET_XY(x0, y0)) == ELEVATOR_ZONE) + y0--; + y0++; + x1 = x0; + y1 = y0; + while (me_bits_music(MAP_GET_XY(x1, y1)) == ELEVATOR_ZONE) + x1++; + x1--; + while (me_bits_music(MAP_GET_XY(x1, y1)) == ELEVATOR_ZONE) + y1++; + y1--; + + // Collect all the objects + for (x = x0; x <= x1; x++) { + for (y = y0; y <= y1; y++) { + orefid = me_objref(MAP_GET_XY(x, y)); + while (orefid != OBJ_NULL) { + id = objRefs[orefid].obj; + if ((objs[id].obclass == CLASS_DOOR) && (dump_loc.x == 65535)) { + dump_loc.x = objs[id].loc.x & (~0xFF); + dump_loc.y = objs[id].loc.y & (~0xFF); + if ((objs[id].loc.y & 0xFF) <= 0x2) + dump_loc.y -= 0x100; + else if ((objs[id].loc.y & 0xFF) >= 0xFC) + dump_loc.y += 0x100; + else if ((objs[id].loc.x & 0xFF) <= 0x2) + dump_loc.x -= 0x100; + else + dump_loc.x += 0x100; + } else if ((id != OBJ_NULL) && (id != PLAYER_OBJ) && (ObjProps[OPNUM(id)].physics_model)) { + dupe = FALSE; + for (j = 0; j < obj_count; j++) { + if (objlist[j] == id) { + dupe = TRUE; + break; + } + } + if (!dupe) + objlist[obj_count++] = id; + } + orefid = objRefs[orefid].next; + } + } + } + + // Move all the stuff there + robot_antisocial = TRUE; + for (i = 0; i < obj_count; i++) { + newloc = objs[objlist[i]].loc; + newloc.x = dump_loc.x + (rand() & 0xBF) + 0x20; + newloc.y = dump_loc.y + (rand() & 0xBF) + 0x20; + newloc.z = + obj_floor_compute(objlist[i], me_height_flr(MAP_GET_XY(OBJ_LOC_BIN_X(dump_loc), OBJ_LOC_BIN_Y(dump_loc)))); + obj_move_to(objlist[i], &newloc, TRUE); + } + robot_antisocial = FALSE; + + return (OK); +} + +#ifdef ELEVATOR_PACKRAT + +#define MAX_ELEV_OBJS 16 + +// Goes through all the objects in the same elevator as the player, and fills objlist with them +errtype compute_elev_objs(ObjID *objlist) { + short i, j, x, y; + short x0, x1, y0, y1; + ObjRefID oref; + ObjID id; + MapElem *pme; + uchar dupe; + + for (i = 0; i < MAX_ELEV_OBJS; i++) + objlist[i] = OBJ_NULL; + i = 0; + + // Wow, this is an wacky way of finding the bounding rectangle + // of elevator music, but hey, it should work for any rectangle... + x0 = PLAYER_BIN_X; + y0 = PLAYER_BIN_Y; + while (me_bits_music(MAP_GET_XY(x0, y0)) == ELEVATOR_ZONE) + x0--; + x0++; + while (me_bits_music(MAP_GET_XY(x0, y0)) == ELEVATOR_ZONE) + y0--; + y0++; + x1 = x0; + y1 = y0; + while (me_bits_music(MAP_GET_XY(x1, y1)) == ELEVATOR_ZONE) + x1++; + x1--; + while (me_bits_music(MAP_GET_XY(x1, y1)) == ELEVATOR_ZONE) + y1++; + y1--; + + // Go through all the elevator squares, collecting objects + for (x = x0; x <= x1; x++) { + for (y = y0; y <= y1; y++) { + pme = MAP_GET_XY(x, y); + oref = me_objref(pme); + while (oref != OBJ_REF_NULL) { + dupe = FALSE; + id = objRefs[oref].obj; + if ((id != OBJ_NULL) && (id != PLAYER_OBJ) && (ObjProps[OPNUM(id)].physics_model)) { + for (j = 0; j < i; j++) { + if (objlist[j] == id) { + dupe = TRUE; + break; + } + } + if (!dupe) { + ObjLoc newloc; + State st; + extern void state_to_objloc(State * s, ObjLoc * l); + int ph = objs[id].info.ph; + int *pd1, *pd2; + + // Make sure it is okay to come with us.... first check physics then check + // for containerism, and grenadeliness. + if (ph != -1) { + // if we are in physics, force us to the floor, etc. before going + EDMS_settle_object(ph); + EDMS_get_state(ph, &st); + state_to_objloc(&st, &newloc); + obj_move_to(id, &newloc, FALSE); + EDMS_kill_object(ph); + objs[id].info.ph = -1; + } + // This will cruelly strand the container's contents to the + // eternal limbo of the unreferenced object. + // Life is a grim place sometimes. + if (is_container(id, &pd1, &pd2)) { + *pd1 = 0; + *pd2 = 0; + } + + // Boom go the grenades + if ((objs[id].obclass == CLASS_GRENADE) && + (objGrenades[objs[id].specID].flags & GREN_ACTIVE_FLAG)) + ADD_DESTROYED_OBJECT(id); + // do_grenade_explosion(id,TRUE); + else + objlist[i++] = id; + } + } + oref = objRefs[oref].next; + } + } + } + return (OK); +} + +#endif + +// uncomment the next 2 lines for playable demo only!!! +//#define MAC_DEMO +// extern Boolean gPlayingGame; +// + +// Eventually, this code will also go and do some nice level-changing +// animation. For now, we just telemaport you. +// dest_level is the target level to be teleported to +// which_panel is an index into the list of "equivalent" panels that each panel keeps around. +// returns whether or not the elevator actually went anywhere +uchar elevator_use(short dest_level, ubyte which_panel) { +#ifdef MAC_DEMO + // extern errtype trap_cutscene_func(int p1, int p2, int p3, int p4); + // trap_cutscene_func(2,TRUE,0,0); + uiHideMouse(NULL); + ShowCursor(); + Alert(1999, NULL); // Show "thanks for playing demo" alert. + gPlayingGame = FALSE; // Hop out of the game loop. +#else + errtype retval = TRUE; + short xdiff, ydiff, zdiff; + ObjLoc panel_loc, newloc; + char old_zsh; + int new_panel; + ObjRefID oref; + ObjID id; + int nuframe; +#ifdef ELEVATOR_PACKRAT + char i; + ObjLoc temploc; + ObjID tempid; + ObjID elev_obj_list[MAX_ELEV_OBJS]; + ObjLoc elev_obj_diffs[MAX_ELEV_OBJS]; + extern void store_objects(char **buf, ObjID *obj_array, char obj_count); + extern void restore_objects(char *buf, ObjID *obj_array, char obj_count); + extern errtype obj_load_art(uchar flush_all); + extern uchar robot_antisocial; + char *buf; +#endif + + if (dest_level == player_struct.level) { + string_message_info(REF_STR_ElevatorSameFloor); + return (FALSE); + } else + nuframe = (dest_level > player_struct.level); + + panel_loc = objs[player_struct.panel_ref].loc; + + oref = me_objref(MAP_GET_XY(OBJ_LOC_BIN_X(panel_loc), OBJ_LOC_BIN_Y(panel_loc))); + + while (oref != OBJ_REF_NULL) { + ObjID id = objRefs[oref].obj; + + if ((objs[id].obclass == CLASS_DOOR) && (!(DOOR_REALLY_CLOSED(id)))) { + // there is an open elevator in the square, so don't let the elevator go + string_message_info(REF_STR_ElevatorDoorOpen); + return (FALSE); + } + oref = objRefs[oref].next; + } + + string_message_info(REF_STR_ElevatorMove); + +#ifdef ELEVATOR_PACKRAT + // Fill list of elevator-contained objects + compute_elev_objs(elev_obj_list); + store_objects(&buf, elev_obj_list, MAX_ELEV_OBJS); +#endif + + // Compute our offset from the panel we actually frobbed with. + xdiff = panel_loc.x - player_dos_obj->loc.x; + ydiff = panel_loc.y - player_dos_obj->loc.y; + zdiff = panel_loc.z - player_dos_obj->loc.z; +#ifdef ELEVATOR_PACKRAT + for (i = 0; i < MAX_ELEV_OBJS; i++) { + if (elev_obj_list[i] != OBJ_NULL) { + elev_obj_diffs[i].x = panel_loc.x - objs[elev_obj_list[i]].loc.x; + elev_obj_diffs[i].y = panel_loc.y - objs[elev_obj_list[i]].loc.y; + elev_obj_diffs[i].z = panel_loc.z - objs[elev_obj_list[i]].loc.z; + } + } +#endif + old_zsh = MAP_ZSHF; + + if (full_game_3d) { + render_run(); + } + + // Find what the equivalent panel on the new level is + // we do this by deparsing the data stuffed into the trap data + // of the panel. + switch (which_panel) { + case 0: + case 1: + new_panel = objFixtures[objs[player_struct.panel_ref].specID].p1; + break; + case 2: + case 3: + new_panel = objFixtures[objs[player_struct.panel_ref].specID].p2; + break; + case 4: + case 5: + new_panel = objFixtures[objs[player_struct.panel_ref].specID].p3; + break; + } + if ((which_panel % 2) == 0) + new_panel = new_panel >> 16; + else + new_panel = new_panel & 0xFFFF; + + objs[player_struct.panel_ref].info.current_frame = nuframe; + check_panel_ref(TRUE); + + // Teleport the player to that level, at the same relative distance + // from the new panel as to the old. + begin_wait(); + retval = trap_teleport_func(0xF000, 0xF000, 0xF000, dest_level); // no change in x, y, or z + + if (retval == OK) { + panel_loc = objs[new_panel].loc; + objs[new_panel].info.current_frame = nuframe; + newloc = player_dos_obj->loc; + newloc.x = panel_loc.x - xdiff; + newloc.y = panel_loc.y - ydiff; +#ifdef BROKEN_CODE + newloc.z = panel_loc.z - zdiff; + if (MAP_ZSHF > old_zsh) + newloc.z = newloc.z << (MAP_ZSHF - old_zsh); + else + newloc.z = newloc.z << (old_zsh - MAP_ZSHF); +#endif + if (MAP_ZSHF == old_zsh) + newloc.z = panel_loc.z - zdiff; + else if (MAP_ZSHF > old_zsh) + newloc.z = panel_loc.z - (zdiff << (MAP_ZSHF - old_zsh)); + else + newloc.z = panel_loc.z - (zdiff >> (old_zsh - MAP_ZSHF)); + + obj_move_to(PLAYER_OBJ, &newloc, TRUE); + + // Clear out old cruft in the new elevator squares + elevator_janitor_run(); + +#ifdef ELEVATOR_PACKRAT + // Reconsitute elevator-objects + restore_objects(buf, elev_obj_list, MAX_ELEV_OBJS); + + // Move 'em to the right place + robot_antisocial = TRUE; + for (i = 0; i < MAX_ELEV_OBJS; i++) { + tempid = elev_obj_list[i]; + if (tempid != OBJ_NULL) { + temploc = objs[tempid].loc; + temploc.x = panel_loc.x - elev_obj_diffs[i].x; + temploc.y = panel_loc.y - elev_obj_diffs[i].y; + if (MAP_ZSHF == old_zsh) + temploc.z = panel_loc.z - elev_obj_diffs[i].z; + else if (MAP_ZSHF > old_zsh) + temploc.z = panel_loc.z - (elev_obj_diffs[i].z << (MAP_ZSHF - old_zsh)); + else + temploc.z = panel_loc.z - (elev_obj_diffs[i].z >> (old_zsh - MAP_ZSHF)); + obj_move_to(tempid, &temploc, TRUE); + } + } + robot_antisocial = FALSE; + obj_load_art(FALSE); +#endif + end_wait(); + + stop_digi_fx(); // KLC - Moved this to before the door tries to open. + + // open the door, unless freight elevator + id = door_in_square(&panel_loc, TRUE); + if (DOOR_REALLY_CLOSED(id) && !door_locked(id) && objDoors[objs[id].specID].other_half == 0) { + object_use(id, FALSE, OBJ_NULL); + } + } else + critical_error(CRITERR_FILE); +#endif + return (TRUE); +} + +errtype obj_door_lock(ObjID door_id, uchar new_lock) { + if (new_lock) + QUESTBIT_ON(objDoors[objs[door_id].specID].locked); + else + QUESTBIT_OFF(objDoors[objs[door_id].specID].locked); + return (OK); +} + +uchar in_anim_callback = FALSE; + +void unmulti_anim_callback(ObjID id, intptr_t user_data) { + int orig_parm = (int)user_data; + int *pp2, *pp1; + + if (in_anim_callback || !time_passes) + return; + in_anim_callback = TRUE; + switch (objs[id].obclass) { + case CLASS_BIGSTUFF: + pp1 = &objBigstuffs[objs[id].specID].data1; + pp2 = &objBigstuffs[objs[id].specID].data2; + break; + case CLASS_SMALLSTUFF: + pp1 = &objSmallstuffs[objs[id].specID].data1; + pp2 = &objSmallstuffs[objs[id].specID].data2; + break; + } + objs[id].info.current_frame = 0; + add_obj_to_animlist(id, TRUE, *pp1 & 0x2, *pp1 & 0x1, 0, 5, 0, ANIMCB_REPEAT | ANIMCB_CYCLE); + *pp2 = orig_parm; + in_anim_callback = FALSE; +} + +void multi_anim_callback(ObjID id, intptr_t data) { + int *pp2, *pp1; + uchar do_swap = FALSE; + + if (in_anim_callback || !time_passes) + return; + in_anim_callback = TRUE; + switch (objs[id].obclass) { + case CLASS_BIGSTUFF: + pp1 = &objBigstuffs[objs[id].specID].data1; + pp2 = &objBigstuffs[objs[id].specID].data2; + break; + case CLASS_SMALLSTUFF: + pp1 = &objSmallstuffs[objs[id].specID].data1; + pp2 = &objSmallstuffs[objs[id].specID].data2; + break; + } + + if ((*pp1) >> 16) { + // As we have other ways of determining when to switch, they can just go + // into this case statement. + switch ((*pp1) >> 28) { + case 0: + if (rand() % ((*pp1 & 0xFFF0000) >> 16) == 1) + do_swap = TRUE; + break; + } + if (do_swap) { + remove_obj_from_animlist(id); + objs[id].info.current_frame = 0; + add_obj_to_animlist(id, FALSE, FALSE, FALSE, 0, 4, *pp2, ANIMCB_REMOVE); + *pp2 = *pp2 >> 16; + } + } + in_anim_callback = FALSE; +} + +errtype obj_screen_animate(ObjID id) { + errtype retval = OK; + remove_obj_from_animlist(id); + switch (objs[id].obclass) { + case CLASS_BIGSTUFF: + if (objBigstuffs[objs[id].specID].data2 >> 16) + retval = add_obj_to_animlist(id, TRUE, objBigstuffs[objs[id].specID].data1 & 0x2, + objBigstuffs[objs[id].specID].data1 & 0x1, 0, 5, 0, + ANIMCB_REPEAT | ANIMCB_CYCLE); + else + retval = add_obj_to_animlist(id, TRUE, objBigstuffs[objs[id].specID].data1 & 0x2, + objBigstuffs[objs[id].specID].data1 & 0x1, 0, 0, 0, 0); + break; + case CLASS_SMALLSTUFF: + if (objSmallstuffs[objs[id].specID].data2 >> 16) + retval = add_obj_to_animlist(id, TRUE, objSmallstuffs[objs[id].specID].data1 & 0x2, + objSmallstuffs[objs[id].specID].data1 & 0x1, 0, 5, 0, + ANIMCB_REPEAT | ANIMCB_CYCLE); + else + retval = add_obj_to_animlist(id, TRUE, objBigstuffs[objs[id].specID].data1 & 0x2, + objBigstuffs[objs[id].specID].data1 & 0x1, 0, 0, 0, 0); + break; + } + return (OK); +} + +#define MAX_KEYPAD_DIGITS 3 + +uchar obj_keypad_crunch(int p, uchar digits[MAX_KEYPAD_DIGITS]) { + uchar retval = TRUE; + int i; + short combo = qdata_get(p & 0xFFFF); + ObjID id = qdata_get(p >> 16); + + if (combo == 0) + return (FALSE); + for (i = 0; i < MAX_KEYPAD_DIGITS; i++) { + if (((combo >> (4 * i)) & 0xF) != digits[MAX_KEYPAD_DIGITS - 1 - i]) + retval = FALSE; + } + if (retval) { + gKeypadOverride = FALSE; + play_digi_fx_obj(SFX_MFD_SUCCESS, 1, id); + do_multi_stuff(id); + } + return (retval); +} + +errtype keypad_trigger(ObjID id, uchar digits[MAX_KEYPAD_DIGITS]) { + ObjSpecID osid = objs[id].specID; + char match = -1; + if ((objs[id].obclass != CLASS_FIXTURE) || (!objs[id].active)) { + return (ERR_NOEFFECT); + } + if (obj_keypad_crunch(objFixtures[osid].p1, digits)) + match = 1; + if (obj_keypad_crunch(objFixtures[osid].p2, digits)) + match = 1; + if (obj_keypad_crunch(objFixtures[osid].p3, digits)) + match = 1; + if (match == -1) { + if (qdata_get(objFixtures[osid].p4 >> 16) == 0) + string_message_info(REF_STR_KeypadBad); + else { + string_message_info(REF_STR_TrapZeroMessage + qdata_get(objFixtures[osid].p4 >> 16)); +#ifdef AUDIOLOGS + audiolog_bark_play(qdata_get(objFixtures[osid].p4 >> 16)); +#endif + } + play_digi_fx_obj(SFX_MFD_BUZZ, 1, id); + do_multi_stuff(qdata_get(objFixtures[osid].p4 & 0xFFFF)); + } else { + if (match == CLASS_DOOR) + string_message_info(REF_STR_KeypadGood); + } + return (OK); +} + +// Access panels stick their door to be opened in P1 +errtype accesspanel_trigger(ObjID id) { + TrapSchedEvent new_ev; + ObjID trap; + errtype err; + + trap = objFixtures[objs[id].specID].p1; + + new_ev.timestamp = TICKS2TSTAMP(player_struct.game_time) + 1; + new_ev.type = TRAP_SCHED_EVENT; + new_ev.target_id = trap; + new_ev.source_id = -1; + err = schedule_event(&(global_fullmap->sched[MAP_SCHEDULE_GAMETIME]), (SchedEvent *)&new_ev); + + if (err) { + // failed to schedule! Forge ahead anyway. As far as I know, the + // worst thing that happens is your MFD state gets screwed up, and + // not having a puzzle fire at all can lose you the game. + + do_multi_stuff(trap); + } + + play_digi_fx_obj(SFX_PANEL_SUCCESS, 1, id); + return (OK); +} + +// Hmm, I wonder whether this could be a problem after loading a game... +#define CSPACE_OBJECT_DELAY_TIME CIT_CYCLE + +ObjID last_obj; +ulong last_obj_time; + +// Collision with something while in cyberspace +errtype obj_cspace_collide(ObjID id, ObjID collider) { + char str_buf[60], temp[20]; + int bigstuff_fake = 0, trip; + uchar select = FALSE; + + if (collider != PLAYER_OBJ) { + // generate a fake physics-like collision callback + collide_objects(collider, id, 0); + return (OK); + } + if (objs[id].obclass == CLASS_TRAP) + return (OK); + if ((last_obj == id) && (player_struct.game_time < last_obj_time)) + return (OK); + last_obj = id; + last_obj_time = player_struct.game_time + CSPACE_OBJECT_DELAY_TIME; + if (ICE_ICE_BABY(id)) { + if (player_struct.hud_modes & HUD_FAKEID) + hud_unset(HUD_FAKEID); + else { + string_message_info(REF_STR_IceEncrusted); + return (OK); + } + } +#ifdef MATCHBOX_SUPPORT + switch (ID2TRIP(id)) { + case ARROW_TRIPLE: + cspace_effect_times[CS_MATCHBOX_EFF] = player_struct.game_time + cspace_effect_durations[CS_MATCHBOX_EFF]; + return (OK); + break; + } +#endif + switch (objs[id].obclass) { + case CLASS_FIXTURE: + case CLASS_SMALLSTUFF: + do_multi_stuff(id); + break; + case CLASS_BIGSTUFF: + bigstuff_fake = + MAKETRIP(CLASS_SOFTWARE, objBigstuffs[objs[id].specID].data1, objBigstuffs[objs[id].specID].data2); + case CLASS_SOFTWARE: + default: + shameful_obselete_flag = FALSE; + // if player has a valid currently selected combat soft, do not select + // a new one. + select = FALSE; + if (bigstuff_fake != 0) + trip = bigstuff_fake; + else + trip = ID2TRIP(id); + if (TRIP2CL(trip) == CLASS_SOFTWARE && TRIP2SC(trip) == SOFTWARE_SUBCLASS_OFFENSE && + !player_struct.softs.combat[player_struct.actives[ACTIVE_COMBAT_SOFT]]) { + select = TRUE; + } + if ((bigstuff_fake != 0) || (USE_MODE(id) == PICKUP_USE_MODE && inventory_add_object(id, select))) { + ObjLocState del_loc_state; + int version = 0; + // yank the object out of the map. + del_loc_state.obj = id; + del_loc_state.loc = objs[id].loc; + del_loc_state.loc.x = -1; + ObjRefStateBinSetNull(del_loc_state.refs[0].bin); + ObjUpdateLocs(&del_loc_state); + + if (bigstuff_fake != 0) { + inventory_add_object(id, select); +#ifdef SWITCH_BY_COLLIDE + switch (objBigstuffs[objs[id].specID].data1) { + case SOFTWARE_SUBCLASS_OFFENSE: + player_struct.actives[ACTIVE_COMBAT_SOFT] = objBigstuffs[objs[id].specID].data2; + break; + case SOFTWARE_SUBCLASS_DEFENSE: + player_struct.actives[ACTIVE_DEFENSE_SOFT] = objBigstuffs[objs[id].specID].data2; + break; + } +#endif + get_object_short_name(bigstuff_fake, str_buf, 40); + version = objBigstuffs[objs[id].specID].cosmetic_value; + } else { + // set it to be the "active" object under certain circumstances + if (objs[id].obclass == CLASS_SOFTWARE) { + switch (objs[id].subclass) { +#ifdef SWITCH_BY_COLLIDE + case SOFTWARE_SUBCLASS_OFFENSE: + player_struct.actives[ACTIVE_COMBAT_SOFT] = objs[id].info.type; + break; + case SOFTWARE_SUBCLASS_DEFENSE: + player_struct.actives[ACTIVE_DEFENSE_SOFT] = objs[id].info.type; + break; +#endif + case SOFTWARE_SUBCLASS_DATA: + string_message_info(REF_STR_CspaceData); + return (OK); + } + } + get_object_short_name(ID2TRIP(id), str_buf, 40); + version = objSoftwares[objs[id].specID].version; + } + if (version && trip == GAMES_TRIPLE) { + int game = 0; + while ((version & 1) == 0) + game++, version = version >> 1; + version = 0; + sprintf(str_buf, "\"%s\" %s", (char *)RefGet(REF_STR_GameName0 + game), + (char *)RefGet(MKREF(RES_objshortnames, OPTRIP(GAMES_TRIPLE)))); + } + if (version) { + sprintf(str_buf + strlen(str_buf), " %s%d", (char *)RefGet(REF_STR_VersionPrefix), version); + } + strcat(str_buf, get_string(REF_STR_CspaceAcquire, temp, 20)); + if (shameful_obselete_flag) + string_message_info(REF_STR_AlreadyHaveOne); + else + message_info(str_buf); + } + break; + } + return (OK); +} diff --git a/engine/src/GameSrc/olh.c b/engine/src/GameSrc/olh.c new file mode 100644 index 0000000..78366da --- /dev/null +++ b/engine/src/GameSrc/olh.c @@ -0,0 +1,513 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/olh.c $ + * $Revision: 1.27 $ + * $Author: dc $ + * $Date: 1994/11/21 09:02:38 $ + * + */ + +#include +#include + +#include "Prefs.h" +#include "Shock.h" + +#include "player.h" +#include "gamestrn.h" +#include "objapp.h" +#include "objects.h" +#include "objprop.h" +#include "hudobj.h" +#include "hud.h" +#include "mainloop.h" +#include "gamescr.h" +#include "olhint.h" +#include "faketime.h" +#include "objbit.h" +#include "objuse.h" +#include "olhscan.h" +#include "doorparm.h" +#include "render.h" +#include "sdl_events.h" +#include "strwrap.h" +#include "tools.h" +#include "trigger.h" +#include "game_screen.h" +#include "fullscrn.h" +#include "input.h" +#include "grenades.h" +#include "mfdext.h" +#include "olhext.h" +#include "cit2d.h" +#include "gr2ss.h" +#include "hkeyfunc.h" +#include "status.h" + +#include "otrip.h" +#include "cybstrng.h" + +// ------------------------------------------------- +// ON-LINE HELP FOR SYSTEM SHOCK +// ------------------------------------------------- + +uchar olh_active = TRUE; +uchar olh_overlay_on = FALSE; +olh_data olh_object = {OBJ_NULL, {0, 0}}; + +// --------- +// INTERNALS +// --------- + +char *get_olh_string(ObjID obj, char *buf); +LGPoint draw_olh_string(char *s, short xl, short yl); +void olh_do_panel_ref(short xl, short yl); +void olh_do_callout(short xl, short yl); +uchar is_compound_use_obj(ObjID obj); +void olh_do_cursor(short xl, short yl); + +#define IS_SCREEN(obj) \ + (objs[obj].obclass == CLASS_BIGSTUFF && objs[obj].subclass == BIGSTUFF_SUBCLASS_ONTHEWALL && \ + (objs[obj].info.type == TRIP2TY(SCREEN_TRIPLE) || objs[obj].info.type == TRIP2TY(SUPERSCREEN_TRIPLE) || \ + objs[obj].info.type == TRIP2TY(BIGSCREEN_TRIPLE))) + +uchar olh_candidate(ObjID obj) { + uchar check_dist = FALSE; + uchar retval = FALSE; + + if (objs[obj].info.inst_flags & OLH_INST_FLAG) + return FALSE; + switch (ID2TRIP(obj)) { + case CAMERA_TRIPLE: + case LARGCPU_TRIPLE: + check_dist = FALSE; + retval = TRUE; + break; + default: + if (USE_MODE(obj) == NULL_USE_MODE) + return FALSE; + break; + } + switch (objs[obj].obclass) { + case CLASS_DOOR: + if ((ID2TRIP(obj) == LABFORCE_TRIPLE) || (ID2TRIP(obj) == RESFORCE_TRIPLE)) + return FALSE; + check_dist = !door_locked(obj) && !door_moving(obj, FALSE) && DOOR_REALLY_CLOSED(obj); + break; + case CLASS_BIGSTUFF: + if (IS_SCREEN(obj)) { + extern char camera_map[NUM_HACK_CAMERAS]; + extern ObjID hack_cam_objs[NUM_HACK_CAMERAS]; + ObjSpecID sid = objs[obj].specID; + short v = objBigstuffs[sid].data2 & 0x7F; + if ((v >= FIRST_CAMERA_TMAP) && (v < FIRST_CAMERA_TMAP + NUM_HACK_CAMERAS)) { + if (camera_map[v - FIRST_CAMERA_TMAP] && hack_cam_objs[v - FIRST_CAMERA_TMAP]) + check_dist = TRUE; + } + break; + } + if (ID2TRIP(obj) == SURG_MACH_TRIPLE) { + check_dist = TRUE; + break; + } + if (((ObjProps[OPNUM(obj)].flags & CLASS_FLAGS) >> CLASS_FLAGS_SHF) == STUFF_OBJUSE_FLAG) { + if (objBigstuffs[objs[obj].specID].data1 != 0) + check_dist = TRUE; + break; + } + case CLASS_SMALLSTUFF: + if (USE_MODE(obj) == USE_USE_MODE) { + check_dist = TRUE; + break; + } + if (USE_MODE(obj) == PICKUP_USE_MODE) { + if (ObjProps[OPNUM(obj)].flags & INVENTORY_GENERAL) + check_dist = TRUE; + break; + } + // smallstuff falls through to default. + default: + if (USE_MODE(obj) == PICKUP_USE_MODE || USE_MODE(obj) == USE_USE_MODE) + check_dist = TRUE; + break; + } + + if (check_dist) { + int mode = USE_MODE(obj); + fix crit = (mode == PICKUP_USE_MODE) ? MAX_PICKUP_DIST : MAX_USE_DIST; + + if (check_object_dist(obj, PLAYER_OBJ, crit)) + retval = TRUE; + } + return retval; +} + +short use_mode_idx[] = { + REFINDEX(REF_STR_helpTake), + REFINDEX(REF_STR_helpUse), + -1, + -1, +}; + +short weap_subclass_idx[] = { + REFINDEX(REF_STR_helpAttackGun), REFINDEX(REF_STR_helpAttackAuto), REFINDEX(REF_STR_helpAttackGun), + REFINDEX(REF_STR_helpAttackHTH), REFINDEX(REF_STR_helpAttackGun), REFINDEX(REF_STR_helpAttackGun), +}; + +// basically we chose a string id for a string +// that has %s, and lg_strintf the name into the +// the string. +char *get_olh_string(ObjID obj, char *buf) { + + int *d1, *d2; + int obclass = objs[obj].obclass; + Ref r = 0; + short mode; + + switch (obclass) { + case CLASS_CRITTER: { + int w = player_struct.actives[ACTIVE_WEAPON]; + int type = player_struct.weapons[w].type; + if (type == EMPTY_WEAPON_SLOT) + return strcpy(buf, get_object_long_name(ID2TRIP(obj), NULL, 0)); + r = MKREF(RES_olh_strings, weap_subclass_idx[type]); + goto got_id; + } + case CLASS_DOOR: + r = REF_STR_helpDoor; + goto got_id; + default: + break; + } + switch (ID2TRIP(obj)) { + case CAMERA_TRIPLE: + case LARGCPU_TRIPLE: + r = REF_STR_helpSecurity; + goto got_id; + } + mode = USE_MODE(obj); + if (is_container(obj, &d1, &d2) && mode == USE_USE_MODE) { + r = REF_STR_helpSearch; + goto got_id; + } + if (use_mode_idx[mode] != -1) { + r = MKREF(RES_olh_strings, use_mode_idx[mode]); + goto got_id; + } + // more cases go here... + +got_id: + if (r != 0) { + char *s = (char *)RefLock(r); + sprintf(buf, s, get_object_long_name(ID2TRIP(obj), NULL, 0)); + RefUnlock(r); + } + return buf; +} + +// --------- +// EXTERNALS +// --------- +extern bool DoubleSize; + +//----------------------------- +// olh_scan_objects() +// +// This gets called to detect objects in front of the player that have +// help strings. It sets up for olh_do_hudobjs. + +#define SCAN_FREQ_SHF (APPROX_CIT_CYCLE_SHFT - 2) + +void olh_scan_objects(void) { + static uint last_scan = 0; + + if (*tmd_ticks >> SCAN_FREQ_SHF <= last_scan) + return; + if (input_cursor_mode == INPUT_OBJECT_CURSOR) + return; + if (player_struct.panel_ref != OBJ_NULL) + return; + olh_scan_objs(); + if (olh_object.obj == OBJ_NULL) + return; +#ifdef SET_HUDOBJ + if (hudobj_rect_capable(olh_object.obj)) { + hudobj_set_id(olh_object.obj, TRUE); + } +#endif // SET_HUDOBJ +} + +LGPoint draw_olh_string(char *s, short xl, short yl) { + short w, h; + short x, y; + + string_replace_char(s, '\n', CHAR_SOFTSP); + gr_set_font(ResGet(RES_tinyTechFont)); + gr_set_fcolor(hud_colors[hud_color_bank][2]); + gr_string_wrap(s, OLH_WRAP_WID); + gr_string_size(s, &w, &h); + ss_point_convert(&xl, &yl, TRUE); + if (DoubleSize) { + xl *= 2; + yl = yl * 2 + 1; + } + x = SCREEN_VIEW_X - xl + SCREEN_VIEW_WIDTH - w - 1; + y = SCREEN_VIEW_Y - yl + SCREEN_VIEW_HEIGHT - h - 1; + draw_shadowed_string(s, x, y, TRUE); + return MakePoint(x - 1, y + (h / 2)); +} + +ushort fixture_panel_stringrefs[] = { + REFINDEX(REF_STR_helpPanel), REFINDEX(REF_STR_helpPanel), REFINDEX(REF_STR_helpPanel), + REFINDEX(REF_STR_helpPanel), REFINDEX(REF_STR_helpElevator), REFINDEX(REF_STR_helpElevator), + REFINDEX(REF_STR_helpElevator), REFINDEX(REF_STR_helpKeypad), REFINDEX(REF_STR_helpKeypad), + REFINDEX(REF_STR_helpPanel), REFINDEX(REF_STR_helpPanel), +}; + +void olh_do_panel_ref(short xl, short yl) { + int *d1, *d2; + ObjID obj = player_struct.panel_ref; + char buf[80]; + + buf[0] = '\0'; + if (is_container(obj, &d1, &d2) && ((d1 != NULL && *d1 != 0) || (d2 != NULL && *d2 != 0))) { + char namebuf[80]; + + get_object_lookname(obj, namebuf, sizeof(namebuf)); + sprintf(buf, get_temp_string(REF_STR_helpGump), namebuf); + } else if (objs[obj].obclass == CLASS_FIXTURE) { + Ref ref = 0; + if (objs[obj].subclass == FIXTURE_SUBCLASS_CONTROL) + ref = REF_STR_helpSwitch; + else if (objs[obj].subclass == FIXTURE_SUBCLASS_PANEL) { + uchar special; + ObjFixture *pfixt = &objFixtures[objs[obj].specID]; + uchar rv = comparator_check(pfixt->comparator, obj, &special); + if (special == 0) + ref = MKREF(RES_olh_strings, fixture_panel_stringrefs[objs[obj].info.type]); + } + if (ref != 0) + get_string(ref, buf, sizeof(buf)); + } + if (buf[0] != '\0') + draw_olh_string(buf, xl, yl); +} + +void olh_do_callout(short xl, short yl) { + int best_rect = -1; + ObjID obj = olh_object.obj; + + if (obj == OBJ_NULL) + return; +#ifdef SET_HUDOBJ + if (hudobj_rect_capable(ID2TRIP(obj))) { + int j; + for (j = 0; j < current_num_hudobjs; j++) { + struct _hudobj_data *dat = &hudobj_vec[j]; + if (dat->id == obj) { + best_rect = j; + break; + } + } + /* perhaps in studlier versions we'll do computations + to decide whether we're no longer a candidate, + rather than just blow away its candidacy */ + hudobj_set_id(obj, FALSE); + if (best_rect == -1) + olh_object.obj = OBJ_NULL; + } +#endif // SET_HUDOBJ + // if (obj != OBJ_NULL) + { + char buf[80]; + char *s = get_olh_string(obj, buf); + LGPoint spos = draw_olh_string(s, xl, yl); + LGPoint pos = olh_object.loc; + pos.x = (int)(pos.x + 1) * SCAN_RATIO; + pos.y = (int)(pos.y + 1) * SCAN_RATIO; + if (DoubleSize) { + pos.x *= 2; + pos.y *= 2; + } + ss_int_line(spos.x - 1, spos.y - 1, pos.x, pos.y); + } + if (best_rect != -1) { + struct _hudobj_data *dat = &hudobj_vec[best_rect]; + gr_set_fcolor(hud_colors[hud_color_bank][0]); + ss_box(dat->xl - 1, dat->yl - 1, dat->xh + 1, dat->yh + 1); + } +} + +// A "compound use" object is one of those nasty objects +// that used on another object by double clicking on that +// object. + +uchar is_compound_use_obj(ObjID obj) { + if (objs[obj].obclass != CLASS_SMALLSTUFF) + return FALSE; + if (objs[obj].subclass == SMALLSTUFF_SUBCLASS_PLOT) + return TRUE; + switch (ID2TRIP(obj)) { + case EPICK_TRIPLE: + case HEAD_TRIPLE: + case HEAD2_TRIPLE: + return TRUE; + default: + break; + } + return FALSE; +} + +void olh_do_cursor(short xl, short yl) { + ObjID obj = object_on_cursor; + // this should be a different string if the cursor is + // a live grenade. + char buf[80]; + if ((objs[obj].obclass == CLASS_GRENADE && + objGrenades[objs[obj].specID].flags & GREN_ACTIVE_FLAG) || + ObjProps[OPNUM(obj)].flags & USELESS_FLAG) + get_string(REF_STR_helpGrenade, buf, sizeof(buf)); + else { + char stringbuf[80]; + char namebuf[80]; + Ref id = is_compound_use_obj(obj) ? REF_STR_helpCompound : REF_STR_helpCursor; + + get_string(id, stringbuf, sizeof(stringbuf)); + get_object_lookname(obj, namebuf, sizeof(namebuf)); + sprintf(buf, stringbuf, namebuf); + } + draw_olh_string(buf, xl, yl); +} + +// ------------------------------------------------- +// olh_do_hudobjs is called by the hud system to +// draw olh hud. + +void olh_do_hudobjs(short xl, short yl) { + extern uchar saveload_static; + if (global_fullmap->cyber || saveload_static) + return; + if (input_cursor_mode == INPUT_OBJECT_CURSOR) + olh_do_cursor(xl, yl); + else if (player_struct.panel_ref != OBJ_NULL) { + int i; + for (i = 0; i < NUM_MFDS; i++) + if (player_struct.mfd_current_slots[i] == MFD_INFO_SLOT) { + olh_do_panel_ref(xl, yl); + return; + } + } else + olh_do_callout(xl, yl); +} + +/*KLC - no longer used +void olh_init(void) +{ + extern void olh_init_scan(void); + olh_init_scan(); +} +*/ + +void olh_closedown(void) { olh_object.obj = OBJ_NULL; } + +void olh_shutdown(void) { + olh_free_scan(); +} + +short _olh_overlay_keys[] = { + ' ' | KB_FLAG_DOWN, + '?' | KB_FLAG_DOWN, +}; + +#define NUM_OVERLAY_KEYS (sizeof(_olh_overlay_keys) / sizeof(_olh_overlay_keys[0])) + +void olh_overlay(void) { + extern LGCursor globcursor; + extern char which_lang; + uchar done = FALSE; + + status_bio_end(); + uiPushGlobalCursor(&globcursor); + gr_push_canvas(grd_screen_canvas); + uiHideMouse(NULL); + draw_res_bm(REF_IMG_bmHelpOverlayEnglish + MKREF(which_lang, 0), 0, 0); + uiShowMouse(NULL); + gr_pop_canvas(); + uiFlush(); + + while (!done) { + ushort key; + ss_mouse_event me; + pump_events(); // DG: apparently this can loop for a long time waiting for input w/o game_loop() being able to + // update events + + tight_loop(FALSE); + // FIXME It crash on Linux + /*if (mouse_next(&me) == OK) + { + if (me.type == MOUSE_LDOWN) + done = TRUE; + }*/ + if (kb_get_cooked(&key)) { + int i; + for (i = 0; i < NUM_OVERLAY_KEYS; i++) + if (_olh_overlay_keys[i] == key) { + done = TRUE; + if (i != 0) + hotkey_dispatch(key); + } + } + + SDLDraw(); + } + + uiPopGlobalCursor(); + uiFlush(); + olh_overlay_on = FALSE; + gr_clear(0); //makes red pixels go away, but real problem is probably in REF_IMG_bmBlankMFD + screen_draw(); + status_bio_start(); +} + +uchar toggle_olh_func(ushort keycode, uint32_t context, intptr_t data) { + if (!olh_active) { + string_message_info(REF_STR_helpOn); + olh_active = TRUE; + } else { + string_message_info(REF_STR_helpOff); + olh_active = FALSE; + ResUnlock(RES_olh_strings); // KLC - added to free strings. + } + gShockPrefs.goOnScreenHelp = olh_active; // KLC - Yeah, got to update this one too and + SavePrefs(); // KLC - save the prefs out to disk. + return TRUE; +} + +uchar olh_overlay_func(ushort keycode, uint32_t context, intptr_t data) { + if (global_fullmap->cyber) { + string_message_info(REF_STR_NotAvailCspace); + return TRUE; + } + if (full_game_3d) { + change_mode_func(keycode, context, GAME_LOOP); + } + olh_overlay_on = TRUE; + return TRUE; +} diff --git a/engine/src/GameSrc/olhscan.c b/engine/src/GameSrc/olhscan.c new file mode 100644 index 0000000..04e255c --- /dev/null +++ b/engine/src/GameSrc/olhscan.c @@ -0,0 +1,196 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/olhscan.c $ + * $Revision: 1.6 $ + * $Author: dc $ + * $Date: 1994/11/21 09:10:45 $ + */ + +#include +#include + +#include "faketime.h" + +#include "frtypes.h" +#include "frintern.h" +#include "frparams.h" +#include "frflags.h" +#include "olhint.h" +#include "objects.h" +#include "game_screen.h" +#include "fullscrn.h" + +#define SCAN_OBJ_LIST 8 + +#define MIN_OLH_RADIUS 2 +#define MAX_OLH_RADIUS 5 + +ubyte olh_radius = MIN_OLH_RADIUS; + +#define BOTTOM_MARGIN (SCAN_HGT - 15 / SCAN_RATIO) +#define RIGHT_MARGIN (OLH_WRAP_WID / SCAN_RATIO) + +fauxrend_context *olh_full_context = NULL; + +extern fauxrend_context *svga_render_context; + +#define FULL_SCAN_WID (FULL_VIEW_WIDTH / SCAN_RATIO) +#define FULL_SCAN_HGT (FULL_VIEW_HEIGHT / SCAN_RATIO) + + +void olh_init_single_scan(fauxrend_context **outxt, fauxrend_context *intxt) { + uchar *mem = ((grs_canvas *)fr_get_canvas(intxt))->bm.bits; + + *outxt = (fauxrend_context *)fr_place_view(FR_NEWVIEW, FR_DEFCAM, mem, + FR_CURVIEW_STRT | FR_DOUBLEB_MASK | FR_PICKUPM_MASK, 0, 0, 0, 0, + intxt->xwid / SCAN_RATIO, intxt->ywid / SCAN_RATIO); + fr_set_callbacks(*outxt, NULL, NULL, NULL); +} + +/*KLC - no longer used +void olh_init_scan(void) +{ + olh_init_single_scan(&olh_full_context, (fauxrend_context *)full_game_fr_context); +} +*/ +void olh_free_scan(void) { + if (olh_full_context) + fr_free_view(olh_full_context); +} + +fix x_mul = fix_make(1, 0), y_mul = fix_make(1, 0); +void olh_svga_deal(void) { + if (olh_full_context) + fr_free_view(olh_full_context); + olh_init_single_scan(&olh_full_context, svga_render_context); + x_mul = fix_div(fix_make(320, 0), fix_make(grd_mode_cap.w, 0)); + y_mul = fix_div(fix_make(200, 0), fix_make(grd_mode_cap.h, 0)); +} + +extern int last_real_time; + +ushort olh_scan_objs(void) { + int xl, yl; + int col; + int (*fr_ptr_idx)(void) = fr_get_idx; + short x, y; + struct _obj_scandata { + ObjID obj; + int x, y; // TOTAL x and y coordinates for all samples + ushort count; + } objdata[SCAN_OBJ_LIST]; + short objcount = 0; +#ifdef SVGA_SUPPORT + fauxrend_context *fr = olh_full_context; +#else + fauxrend_context *fr = (full_game_3d) ? olh_full_context : olh_context; +#endif + ubyte save_radius = _frp.view.radius; + + // Do a monochrome render + // olh_replace_view(SCREEN_CONTEXT->xwid/SCAN_RATIO,SCREEN_CONTEXT->ywid/SCAN_RATIO); + // _fr_top(fr); + _fr_glob_flags |= FR_PICKUPM_MASK; + fr_cur_obj_col = FR_CUR_OBJ_BASE; + fr_get_idx = fr_pickup_idx; + _frp.view.radius = olh_radius; + fr_rend(fr); + _frp.view.radius = save_radius; + fr_get_idx = fr_ptr_idx; + _fr_glob_flags &= ~FR_PICKUPM_MASK; + + if (*tmd_ticks - last_real_time < CIT_CYCLE / 15) + olh_radius = lg_min(olh_radius + 1, MAX_OLH_RADIUS); + else if (*tmd_ticks - last_real_time > CIT_CYCLE / 10) + olh_radius = lg_max(olh_radius - 1, MIN_OLH_RADIUS); + + xl = yl = 0; + +#ifdef DEBUGGING_BLIT + gr_push_canvas(grd_screen_canvas); + gr_bitmap(&fr->draw_canvas.bm, 0, 200 - fr->draw_canvas.bm.h); + gr_pop_canvas(); +#endif + + olh_object.obj = OBJ_NULL; + // collect samples + for (y = yl; y < fr->draw_canvas.bm.h; y++) + for (x = xl; x < fr->draw_canvas.bm.w; x++) { + if (y > BOTTOM_MARGIN && x > fr->draw_canvas.bm.w - RIGHT_MARGIN) + break; + col = (int)(*((fr->draw_canvas.bm.bits) + (y * fr->draw_canvas.bm.row) + (x))); + if ((col >= FR_CUR_OBJ_BASE) && (col < fr_cur_obj_col)) // if we are actually exactly over an object + { + ObjID obj = (ObjID)fr_col_to_obj[col - FR_CUR_OBJ_BASE]; + if (olh_candidate(obj)) { + int i; + for (i = 0; i < objcount; i++) + if (objdata[i].obj == obj) { + objdata[i].x += x; + objdata[i].y += y; + objdata[i].count++; + goto found; + } + // not found, so add the object + if (objcount >= SCAN_OBJ_LIST) // no more room in list, how sad. + continue; + i = objcount++; + objdata[i].obj = obj; + objdata[i].x = x; + objdata[i].y = y; + objdata[i].count = 1; + found: + // mprintf ("found %d at (%d,%d) (x,y) now (%d,%d), count now + // %d\n",obj,x,y,objdata[i].x,objdata[i].y,objdata[i].count); + ; + } + } + } + + // now pick the best one. + if (objcount > 0) { + int i; + uint best_weight = 0; + for (i = 0; i < objcount; i++) { + struct _obj_scandata *dat = &objdata[i]; + LGPoint pos = MakePoint((short)(dat->x / dat->count), (short)(dat->y / dat->count)); + uint weight = + dat->count * (fr->xwid + fr->ywid) / (abs(2 * pos.x - fr->xwid) + abs(2 * pos.y - fr->ywid) + 1); + if (weight > best_weight) { + olh_object.obj = dat->obj; + olh_object.loc = pos; + best_weight = weight; + } + } +#ifdef SVGA_SUPPORT + { + fix tmp; + tmp = fix_make(olh_object.loc.x, 0); + tmp = fix_mul(tmp, x_mul); + olh_object.loc.x = fix_int(tmp); + tmp = fix_make(olh_object.loc.y, 0); + tmp = fix_mul(tmp, y_mul); + olh_object.loc.y = fix_int(tmp); + } +#endif + } else + olh_object.obj = OBJ_NULL; + return OBJ_NULL; +} diff --git a/engine/src/GameSrc/palfx.c b/engine/src/GameSrc/palfx.c new file mode 100644 index 0000000..0ab9b50 --- /dev/null +++ b/engine/src/GameSrc/palfx.c @@ -0,0 +1,125 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/palfx.c $ + * $Revision: 1.10 $ + * $Author: minman $ + * $Date: 1994/08/27 04:34:07 $ + * + */ + +#include +#include + +#include "Shock.h" +#include "palfx.h" +#include "sdl_events.h" + + +byte pal_fade_id; +byte cyc_id0, cyc_id1, cyc_id2, cyc_id3, cyc_id4, cyc_id5; + +static Uint32 FadeStartTicks; + +#define FADE_DOWN_DELAY 30 +#define FADE_DOWN_STEPS (1000/30) + +#define FADE_UP_DELAY 0 +#define FADE_UP_STEPS 500 + +extern uchar ppall[]; // pointer to main shadow palette + + +//------------------------------------- +void finish_pal_effect(byte id) { + Uint32 inc, inc_last = 0; + + while (palette_query_effect(id) == ACTIVE) { + + Uint32 elapsed = SDL_GetTicks() - FadeStartTicks; + inc = elapsed - inc_last; + inc_last = elapsed; + + while (inc > 0 && palette_query_effect(id) == ACTIVE) { + palette_advance_effect(id, 1); + inc--; + } + + // Update the screen + SDLDraw(); + pump_events(); + } +} + +//------------------------------------- +void palfx_fade_down() { + byte id; + static uchar blackp[768]; + static uchar savep[768]; + + FadeStartTicks = SDL_GetTicks(); + + LG_memset(blackp, 0, sizeof(blackp)); + gr_get_pal(0, 256, savep); + + if ((num_installed_shifts >= 1) && (pal_fade_id >= 0) && (palette_query_effect(pal_fade_id) == ACTIVE)) { + palette_remove_effect(pal_fade_id); + } + + id = palette_install_fade(REAL_TIME, 0, 255, FADE_DOWN_DELAY, FADE_DOWN_STEPS, savep, blackp); + finish_pal_effect(id); +} + +//------------------------------------- +byte palfx_start_fade_up(uchar *new_pal) { + static uchar blackp[768]; + byte id; + + FadeStartTicks = SDL_GetTicks(); + + LG_memset(blackp, 0, sizeof(blackp)); + id = palette_install_fade(REAL_TIME, 0, 255, FADE_UP_DELAY, FADE_UP_STEPS, blackp, new_pal); + palette_advance_effect(id, 1); + return (id); +} + +//------------------------------------- +void palfx_fade_up(uchar do_now) { + + FadeStartTicks = SDL_GetTicks(); + + // ppall is defined as the main shadow palette in init.c + pal_fade_id = palfx_start_fade_up(ppall); + + if (do_now) + finish_pal_effect(pal_fade_id); +} + +//------------------------------------- +void palfx_init() { + palette_initialize(8); // 1 time unit per frame, 8 effects max + palette_set_rate(1); + + cyc_id0 = palette_install_cbank(REAL_TIME, 0x03, 0x07, 68); // 80 + cyc_id1 = palette_install_cbank(REAL_TIME, 0x0b, 0x0f, 40); // 50 + cyc_id2 = palette_install_cbank(REAL_TIME, 0x10, 0x14, 20); // 25 + cyc_id3 = palette_install_cbank(REAL_TIME, 0x15, 0x17, 108); // 125 + cyc_id4 = palette_install_cbank(REAL_TIME, 0x18, 0x1a, 84); // 100 + cyc_id5 = palette_install_cbank(REAL_TIME, 0x1b, 0x1f, 64); // 75 +} diff --git a/engine/src/GameSrc/pathfind.c b/engine/src/GameSrc/pathfind.c new file mode 100644 index 0000000..48d7b34 --- /dev/null +++ b/engine/src/GameSrc/pathfind.c @@ -0,0 +1,696 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/pathfind.c $ + * $Revision: 1.13 $ + * $Author: xemu $ + * $Date: 1994/09/06 23:44:56 $ + */ + +#define __PATHFIND_SRC + +#include +#include + +#include "pathfind.h" +#include "player.h" +#include "faketime.h" +#include "tilename.h" +#include "otrip.h" +#include "objgame.h" +#include "objprop.h" +#include "gameobj.h" +#include "objbit.h" +#include "cybmem.h" +#include "doorparm.h" + +//------------ +// PROTOTYPES +//------------ +errtype find_path(char path_id); +short tile_height(MapElem *pme, char dir, uchar floor); +uchar pf_obj_height(MapElem *pme, uchar old_z); + +// So, the paradigm is that a given creature makes a pathfind request +// At the next suitable time, the pathfinder fills in all the pending requests +// If you want a step from your path and your request hasn't been filled yet, +// then you just wait. + +// Each path is a Source location and a set of NSEW movements, 64 move locations +// There are MAX_PATHS available paths, so if lots of things are already pathfinding +// then new requests cannot be made. + +// Which char is it? +#define HIGH_STEP(stepnum) ((stepnum) >> 2) +#define LOW_STEP(stepnum) ((stepnum)&0x3) +#define PATH_CHAR(pathid, stepnum) (paths[(pathid)].moves[HIGH_STEP(stepnum)]) +#define PATH_STEP(pathid, stepnum) ((PATH_CHAR(pathid, stepnum) >> (LOW_STEP(stepnum) << 1)) & 0x3) +// Good god, there has got to be a better way to do this! -- Rob +// Clear out the old set of 2 bits, and or in the new 2 bits +#define SET_PATH_STEP(pathid, stepnum, newval) \ + do { \ + PATH_CHAR(pathid, stepnum) = \ + PATH_CHAR(pathid, stepnum) & ~(0x3 << (LOW_STEP(stepnum) << 1)) | ((newval) << (LOW_STEP(stepnum) << 1)); \ + } while (0) +#define CLEAR_PATH(pathid) \ + do { \ + LG_memset((void *)(paths[(pathid)].moves), 0, NUM_PATH_STEPS / 4); \ + } while (0) + +// Note that a dest_z of 0 means to ignore that whole concept +// dest_z and start_z are in objLoc height coordinates, since that is the easiest API +char request_pathfind(LGPoint source, LGPoint dest, uchar dest_z, uchar start_z, uchar priority) { + char i = 0; + + // Find the first available path number + while ((i < MAX_PATHS) && (used_paths & (1 << i))) + i++; + + // If no paths free, return -1; + if (i == MAX_PATHS) { + return (-1); + } + if (PointsEqual(source, dest)) { + return (-1); + } + + // Grab the path + used_paths |= (1 << i); + + // Init the path struct + paths[i].source = source; + paths[i].dest = dest; + // Path height units and API height units are the same, so don't + paths[i].dest_z = dest_z; + paths[i].start_z = start_z; + paths[i].num_steps = priority ? -2 : -1; + paths[i].curr_step = -1; // to indicate that we haven't been filled yet + + // clear it + CLEAR_PATH(i); + + // go + return (i); +} + +// Updates pt to reflect the next step on path path_id from step number step_num. +// pt must already reflect the location reached by the path in step_num steps. +// A step_num of -1 indicates to use the curr_step contained in the path. +// Unlike next_step_on_path, this procedure does not affect the paths array at all, +// although it does modify it's pt parameter. +// Returns direction of that next step +char compute_next_step(char path_id, LGPoint *pt, char step_num) { + char movecode = -22; + if (step_num == -1) + step_num = paths[path_id].curr_step; + if (step_num != -1) { + movecode = PATH_STEP(path_id, paths[path_id].num_steps - step_num - 1); + switch (movecode) { + case 0: + pt->y++; + break; // N + case 1: + pt->x++; + break; // E + case 2: + pt->y--; + break; // S + case 3: + pt->x--; + break; // W + default: + break; + } + } + return (movecode); +} + +// Note that this is NOT an idempotent procedure.... as you call it +// it moves "source" along the path and increments the path step +// Since we still have the path information, it is possible to go +// backwards along the path, but I won't write that until it seems +// needed. +// Returns the direction one travels in to get to this next step +char next_step_on_path(char path_id, LGPoint *next, char *steps_left) { + char retval; + *next = paths[path_id].source; + retval = compute_next_step(path_id, next, -1); // -1 for "use path data" + paths[path_id].source = *next; + paths[path_id].curr_step++; + if (PointsEqual(*next, paths[path_id].dest)) { + *steps_left = 0; + } else { + *steps_left = paths[path_id].num_steps - paths[path_id].curr_step; + } + return (retval); +} + +#define LOOKAHEAD_STEPS 3 +// Checks whether or not we have skipped ahead to some square within LOOKAHEAD_STEPS +// of the "current" location for the specified path. If so, jumps the path to that point and +// returns TRUE. +uchar check_path_cutting(LGPoint new_sq, char path_id) { + LGPoint pt; + char count; + // Hey, first check if we've finished the darn path... + if ((path_id == -1) || (paths[path_id].num_steps == 0)) + return (FALSE); + if (PointsEqual(new_sq, paths[path_id].dest)) { + paths[path_id].num_steps = 0; + return (TRUE); + } + + // Now do the standard lookahead check... + pt = paths[path_id].source; + for (count = 0; count < LOOKAHEAD_STEPS; count++) { + compute_next_step(path_id, &pt, paths[path_id].curr_step + count); + if (PointsEqual(pt, new_sq)) { + // hey look, we cut ahead to a more advanced point in our pathfinding... + paths[path_id].source = new_sq; + paths[path_id].curr_step += count + 1; + return (TRUE); + } + } + + // We didn't find anything, how sad. + return (FALSE); +} + +#define PATHFIND_INTERVAL (CIT_CYCLE >> 2) + +// We could probably save a ulong by just doing this strictly on +// the clock rather than actually doing it right. +ulong last_pathfind_time = 0; +// priority means only check priority requests, but always check +errtype check_requests(uchar priority_only) { + char i; + // Don't bother checking unless it has been at least N ticks, + if (priority_only || (player_struct.game_time > last_pathfind_time)) { + // If it is time to check, go through all the paths, find the + // unfilled requests among them, and go satisfy them. + for (i = 0; i < MAX_PATHS; i++) { + if (paths[i].num_steps == -2) + find_path(i); + if (!priority_only) { + if (paths[i].num_steps == -1) + find_path(i); + } + } + + // Set requirements for next cycle of checking + if (!priority_only) + last_pathfind_time = player_struct.game_time + PATHFIND_INTERVAL; + } + return (OK); +} + +errtype delete_path(char path_id) { + // To delete, just mark the path as unused and zero out its data + if ((path_id < 0) || (path_id >= MAX_PATHS)) + return (ERR_NOEFFECT); + LG_memset(&paths[path_id], 0, sizeof(Path)); + used_paths &= ~(1 << path_id); + return (OK); +} + +// Resets appropriate secret pathfinding state not saved +// in the save game +errtype reset_pathfinding() { + last_pathfind_time = 0; + return (OK); +} + +// THE ACTUAL GRUNGY PART + +// spt is a "point" which only has 8 bits each for x & y. +typedef short spt; +#define SPT_X(s) ((s)&0xFF) +#define SPT_Y(s) ((s) >> 8) +#define SPT_X_SET(s, newx) ((s) = ((s)&0xFF00) | (newx)) +#define SPT_Y_SET(s, newy) ((s) = ((s)&0x00FF) | ((newy) << 8)) +#define PT2SPT(pt) ((((pt).y & 0xFF) << 8) | ((pt).x & 0xFF)) + +#define FORALLINSPTLIST(pspt, iter, loop) for (iter = pspt[0], i = 0; SPT_X(pspt[i]) != 0; i++, iter = pspt[i]) + +//#define CLEARSPTLIST(pspt, num, loop) do { for (loop=0; loop < num; loop++) { pspt[loop] = 0; } } while (0) +#define CLEARSPTLIST(pspt, num) LG_memset(pspt, 0, sizeof(spt) * num) + +// A given element in the pathfind buffer is 8 bits +// 5 bits of Z +// 2 bits of from-directionality +// 1 bit of whether or not it's been visited +#define PFE_Z_MASK 0x1F +#define PFE_DIR_MASK 0x60 +#define PFE_USE_MASK 0x80 +#define PFE_DIR_SHIFT 5 +#define PFE_USE_SHIFT 7 + +#define PFE_OBJ_ZSHIFT 3 +// PFE_Z just returns the raw stored z value (5 bits) +// PFE_Z_MAPHT returns the value as a map height (5 bits) +// PFE_Z_OBJHT returns the value as an object height (8 bits) +#define PFE_Z(ppfe) (*(ppfe)&PFE_Z_MASK) +#define PFE_Z_MAPHT(ppfe) PFE_Z(ppfe) +#define PFE_Z_OBJHT(ppfe) PFE_Z(ppfe) << PFE_OBJ_ZSHIFT +#define OBJZ_TO_PFEZ(zval) ((zval) >> PFE_OBJ_ZSHIFT) +#define PFEZ_TO_OBJZ(zval) ((zval) << PFE_OBJ_ZSHIFT) +#define MAPZ_TO_PFEZ(zval) (zval) +#define PFEZ_TO_MAPZ(zval) (zval) + +// like above, but setting... +#define PFE_Z_SET_RAW(ppfe, z) (*(ppfe) = ((*(ppfe) & ~PFE_Z_MASK) | (z))) +#define PFE_Z_SET_MAPHT(ppfe, mapz) PFE_Z_SET_RAW(ppfe, mapz) +#define PFE_Z_SET_OBJHT(ppfe, objz) PFE_Z_SET_RAW(ppfe, objz >> PFE_OBJ_ZSHIFT) + +#define PFE_DIR(ppfe) ((*(ppfe)&PFE_DIR_MASK) >> PFE_DIR_SHIFT) +#define PFE_DIR_SET(ppfe, d) (*(ppfe) = (*(ppfe) & ~PFE_DIR_MASK) | ((d) << PFE_DIR_SHIFT)) + +#define PFE_USED(ppfe) ((*(ppfe)&PFE_USE_MASK) >> PFE_USE_SHIFT) +#define PFE_USED_SET(ppfe, d) (*(ppfe) = (*(ppfe) & ~PFE_USE_MASK) | ((d) << PFE_USE_SHIFT)) + +// Hmm, I think this will work.... pathfind_buffer is a big chunk +// of memory, and we want to access it like a big array of uchars... +#define PFE_GET_XY(x, y) ((uchar *)pathfind_buffer + x + (y * MAP_XSIZE)) + +// l1 and l2 are two lists of spts, and expand_into_list gets +// pointed to whichever is the current actual expand_into_list (the +// other being prepped to be the expand_into_list next time). +#define EXPAND_LIST_SIZE 64 +spt *expand_into_list, *expand_from_list, *exp_l1, *exp_l2; +char expand_count; +uchar *pathfind_buffer; + +uchar map_connectivity(spt sq1, spt sq2, char dir, uchar flr1, uchar *new_z, uchar dest_z); +uchar expand_one_square(spt sq, char path_id); +uchar expand_fill_list(char path_id); + +// Returns the height at the edge of the tile pme, in the direction dir +// Return value is in map units! +short tile_height(MapElem *pme, char dir, uchar floor) { + uchar retval; + if (floor) + retval = me_height_flr(pme); + else + retval = MAP_HEIGHTS - me_height_ceil(pme); + switch (me_tiletype(pme)) { + case TILE_SOLID: + return (-1); + break; + case TILE_SOLID_NW: + if ((dir == 2) || (dir == 1)) + return (-1); + break; + case TILE_SOLID_NE: + if ((dir == 2) || (dir == 3)) + return (-1); + break; + case TILE_SOLID_SE: + if ((dir == 0) || (dir == 3)) + return (-1); + break; + case TILE_SOLID_SW: + if ((dir == 0) || (dir == 1)) + return (-1); + break; + // Ask doug how to do the sloping cases right... + case TILE_SLOPEUP_N: + if (dir == 2) + break; + if (dir == 0) + retval += me_param(pme); + else + retval += me_param(pme) / 2; + break; + case TILE_SLOPEUP_S: + if (dir == 0) + break; + if (dir == 2) + retval += me_param(pme); + else + retval += me_param(pme) / 2; + break; + case TILE_SLOPEUP_E: + if (dir == 3) + break; + if (dir == 1) + retval += me_param(pme); + else + retval += me_param(pme) / 2; + break; + case TILE_SLOPEUP_W: + if (dir == 1) + break; + if (dir == 3) + retval += me_param(pme); + else + retval += me_param(pme) / 2; + break; + } + return (retval); +} + +#define CRITTERS_OPEN_UNLOCKED_DOORS + +uchar pf_check_doors(MapElem *pme, char dir, ObjID *open_door) { + ObjRefID curr; + ObjID id, which_obj = OBJ_NULL; + curr = me_objref(pme); + *open_door = OBJ_NULL; + while (curr != OBJ_REF_NULL) { + id = objRefs[curr].obj; + if (objs[id].obclass == CLASS_DOOR) { + // Warning(("contemplating id %x, loc = %x, %x, dir = %d\n",id,objs[id].loc.x,objs[id].loc.y,dir)); + switch (dir) { + case 0: // N + if (((objs[id].loc.y & 0xFF) >= 0x80) && !(objs[id].loc.h & 0x40)) + which_obj = id; + break; + case 1: // E + if (((objs[id].loc.x & 0xFF) >= 0x80) && (objs[id].loc.h & 0x40)) + which_obj = id; + break; + case 2: // S + if (((objs[id].loc.y & 0xFF) <= 0x80) && !(objs[id].loc.h & 0x40)) + which_obj = id; + break; + case 3: // W + if (((objs[id].loc.x & 0xFF) <= 0x80) && (objs[id].loc.h & 0x40)) + which_obj = id; + break; + } + } + curr = objRefs[curr].next; + } + if (which_obj != OBJ_NULL) { + // If there is a door in the way, and it is closed, and + // it is either locked or requires access, we can't get through + if ((DOOR_CLOSED(which_obj)) && ((ObjProps[OPNUM(which_obj)].flags & TERRAIN_OBJECT) != 0) && + ((QUESTBIT_GET(objDoors[objs[which_obj].specID].locked)) || + (objDoors[objs[which_obj].specID].access_level))) { + return (FALSE); + } else + *open_door = which_obj; + } + return (TRUE); +} + +// Returns whether or not the two squares can be freely traveled +// between with respect to door-like objects in the squares. +uchar pf_obj_doors(MapElem *pme1, MapElem *pme2, char dir, ObjID *open_door) { + uchar retval; + // Warning(("Top of pf_obj_door!\n")); + retval = pf_check_doors(pme1, dir, open_door); + // Warning(("A: *open_door = %x\n",*open_door)); + if (retval && (*open_door == OBJ_NULL)) { + retval = pf_check_doors(pme2, (dir + 2) % 4, open_door); + // Warning(("B: *open_door = %x\n",*open_door)); + } + return (retval); +} + +// Returns the height (not downshifted) attainable by entering the +// square at height old_z (downshifted). Specifically, accounts for +// bridges and repulsorlifts keeping the player elevated. + +// Wow, this really doesn't deal with bridges right at all, I don't think +// Which is to say, I'm pretty sure it confuses doors with bridges outrageously +// maybe that is worth a specific hack... + +// old_z is almost certainly in PFEZ units, as is the return value +uchar pf_obj_height(MapElem *pme, uchar old_z) { + ObjRefID curr; + ObjID id; + uchar retval = MAPZ_TO_PFEZ(me_height_flr(pme)); + + curr = me_objref(pme); + // Spew(DSRC_AI_Pathfind, ("pf_o_ht: initial retval = %x\n",retval)); + while (curr != OBJ_REF_NULL) { + id = objRefs[curr].obj; +#ifdef PATHFIND_REPULSORS + if (ID2TRIP(id) == REPULSOR_TRIPLE) { + // Check to see if height is sufficient for entry + if ((objTraps[objs[id].specID].p2 < old_z) && (objTraps[objs[id].specID].p3 > old_z)) { + retval = max(retval, objTraps[objs[id].specID].p3); + // Spew(DSRC_AI_Pathfind, ("pf_o_ht: repulsor retval = %x\n",retval)); + } + } else +#endif + if (ObjProps[OPNUM(id)].flags & TERRAIN_OBJECT) { + switch (ObjProps[OPNUM(id)].render_type) { + case FAUBJ_TL_POLY: + case FAUBJ_TPOLY: + case FAUBJ_SPECIAL: + retval = lg_max(retval, OBJZ_TO_PFEZ(objs[id].loc.z)); + // Spew(DSRC_AI_Pathfind, ("pf_o_ht: obj retval = %x (id = %x)\n",retval,id)); + break; + } + } + curr = objRefs[curr].next; + } + return (retval); +} + + // Tells whether or not we can get from one square (at a given z) + // to a new square, and if so, what our new z will be. This will + // start out very very stupid and hopefully get smarter as it + // needs to. + +#define PF_HEIGHT 1 +#define PF_CLIMB 1 + +// flr1, new_z, and dest are all in PFE Z units +uchar map_connectivity(spt sq1, spt sq2, char dir, uchar flr1, uchar *new_z, uchar dest_z) { + MapElem *pme1, *pme2; + ObjID temp; + short flr2, ceil2; + uchar retval; + + pme1 = MAP_GET_XY(SPT_X(sq1), SPT_Y(sq1)); + pme2 = MAP_GET_XY(SPT_X(sq2), SPT_Y(sq2)); + flr2 = MAPZ_TO_PFEZ(tile_height(pme2, (dir + 2) % 4, TRUE)); + ceil2 = MAPZ_TO_PFEZ(tile_height(pme2, (dir + 2) % 4, FALSE)); + if (flr2 == -1) + return (FALSE); + +#ifdef ALLOW_DESTZ_OVERIDE + // Allow final destination overriding, and downshift z + if (dest_z) + flr2 = dest_z; +#endif + + if ((ceil2 < flr1 + PF_HEIGHT) || (flr2 > flr1 + PF_CLIMB)) { + retval = FALSE; + } else { + retval = TRUE; + *new_z = pf_obj_height(pme2, flr1); + } + if (retval) + retval = pf_obj_doors(pme1, pme2, dir, &temp); + + return (retval); +} + +// Expand out a single square. Don't let through any expansions +// that point to places that other expansions have been to or +// that we can't reach. +// Returns whether or not we reached the destination. +uchar expand_one_square(spt sq, char path_id) { + spt newsq, dest = PT2SPT(paths[path_id].dest); + char i; + uchar *ppfe, *ppfe2; + + ppfe2 = PFE_GET_XY(SPT_X(sq), SPT_Y(sq)); + // Spew(DSRC_AI_Pathfind, ("expanding %x\n",sq)); + for (i = 0; i < 4; i++) { + newsq = sq; + switch (i) { + case 0: + SPT_Y_SET(newsq, SPT_Y(newsq) + 1); + break; // N + case 1: + SPT_X_SET(newsq, SPT_X(newsq) + 1); + break; // E + case 2: + SPT_Y_SET(newsq, SPT_Y(newsq) - 1); + break; // S + case 3: + SPT_X_SET(newsq, SPT_X(newsq) - 1); + break; // W + } + ppfe = PFE_GET_XY(SPT_X(newsq), SPT_Y(newsq)); + if (!PFE_USED(ppfe)) // dont bother if we can already get there + { + uchar new_z, dest_z = 0; + if (newsq == dest) + dest_z = OBJZ_TO_PFEZ(paths[path_id].dest_z); + if (map_connectivity(sq, newsq, i, PFE_Z(ppfe2), &new_z, dest_z)) { + PFE_USED_SET(ppfe, TRUE); + // return value from map_conn is already in PFE Z units + PFE_Z_SET_RAW(ppfe, new_z); + PFE_DIR_SET(ppfe, i); + expand_into_list[expand_count++] = newsq; + // Spew(DSRC_AI_Pathfind,("can reach %x\n",newsq)); + if (newsq == dest) + return (TRUE); + } + // else + // Spew(DSRC_AI_Pathfind, ("%x does not connect\n",newsq)); + } + // else + // Spew(DSRC_AI_Pathfind, ("%x already used\n",newsq)); + } + return (FALSE); +} + +// Go through the list of last-iteration's reached squares, and +// generate a new list of places to go to. +// Returns whether or not we reached the destination. +uchar expand_fill_list(char path_id) { + spt s; + char i; + uchar done = FALSE; + expand_count = 0; + CLEARSPTLIST(expand_into_list, EXPAND_LIST_SIZE); + FORALLINSPTLIST(expand_from_list, s, i) { + done = expand_one_square(s, path_id); + if (done) + break; + } + // Spew(DSRC_AI_Pathfind, ("expand into: ")); + // FORALLINSPTLIST(expand_into_list, s, i) + // { + // Spew(DSRC_AI_Pathfind, ("%x ",s)); + // } + // Spew(DSRC_AI_Pathfind, ("\n")); + return (done); +} + +// So, for each map square we keep track of what square we took to get +// here. Since our fill is breadth-first, we know if a square has already +// been reached, it has been reached by a quicker or equally quick path, +// so we only need 2 bits of "from-directionality". We also keep track of +// our z, so that we can have better gnosis of connectivity (you can go down +// cliffs, but not up them, unless you are on a bridge, for example). + +// The basic algorithm is that we expand our list of interesting squares +// out by one on each loop iteration, then check all of those squares for +// being our destination, and failing finding our target, assemble a new list +// of old squares to expand on the next iteration. + +// When we find the target, we just follow the from-directionality backwards to +// get the shortest path. I fully admit this is far from the best, fastest, +// or cleverest algorithm in the world. + +// I don't think this algorithm deals at all with being able to jump over +// one-square pits. In fact, I worry whether or not 2 bits of directionality +// is sufficient to the task. I guess we'll find out. + +// use big_buffer rather than being le memory hog +#define PATHFIND_WITH_BIG_BUFFER +errtype find_path(char path_id) { + uchar done = FALSE; + char i, j, step_count = 0; + uchar *ppfe; + + // Malloc our expand lists & the buffer + exp_l1 = (spt *)big_buffer; + exp_l2 = (spt *)(big_buffer + (sizeof(spt) * EXPAND_LIST_SIZE)); + pathfind_buffer = (uchar *)(big_buffer + (sizeof(spt) * 2 * EXPAND_LIST_SIZE)); + + // Clear the lists + CLEARSPTLIST(exp_l1, EXPAND_LIST_SIZE); + CLEARSPTLIST(exp_l2, EXPAND_LIST_SIZE); + LG_memset(pathfind_buffer, 0, MAP_XSIZE * MAP_YSIZE * sizeof(uchar)); +#ifdef REALLY_SLOW_PATHFIND_CLEARING + for (i = 0; i < MAP_XSIZE; i++) { + for (j = 0; j < MAP_YSIZE; j++) { + PFE_USED_SET(PFE_GET_XY(i, j), FALSE); + } + } +#endif + + // set up initial pointings + expand_into_list = exp_l1; + expand_from_list = exp_l2; + + // Prep the first one to be our source + expand_from_list[0] = PT2SPT(paths[path_id].source); + ppfe = PFE_GET_XY(paths[path_id].source.x, paths[path_id].source.y); + PFE_Z_SET_OBJHT(ppfe, paths[path_id].start_z); + PFE_USED_SET(ppfe, TRUE); + + while (!done && step_count < NUM_PATH_STEPS) { + // Expand out last iteration's list + done = expand_fill_list(path_id); + + // If we haven't found it, swap pointers, etc. + if (!done) { + if (expand_count == 0) { + // Warning(("expand_count = 0!\n")); + step_count = NUM_PATH_STEPS; + } + if (expand_into_list == exp_l1) { + expand_into_list = exp_l2; + expand_from_list = exp_l1; + } else { + expand_into_list = exp_l1; + expand_from_list = exp_l2; + } + step_count++; + } else + // If we HAVE found it, go poke in the right info into the path + { + LGPoint currpt; + i = 0; + currpt = paths[path_id].dest; + while (!PointsEqual(currpt, paths[path_id].source)) { + assert(i < NUM_PATH_STEPS); + j = PFE_DIR(PFE_GET_XY(currpt.x, currpt.y)); + SET_PATH_STEP(path_id, i, j); + i++; + + // Compute one step backwards, according to our direction, j + // so that we have a new currpt + switch (j) { + case 0: + currpt.y--; + break; // N, so backwards is S + case 1: + currpt.x--; + break; // E, so backwards is W + case 2: + currpt.y++; + break; // S, so backwards is N + case 3: + currpt.x++; + break; // W, so backwards is E + } + } + paths[path_id].num_steps = i; + } + } + if (step_count >= NUM_PATH_STEPS) { + paths[path_id].num_steps = 0; + // Warning(("Failed to find path from (%x,%x) to + // (%x,%x)\n",paths[path_id].source.x,paths[path_id].source.y, + // paths[path_id].dest.x, paths[path_id].dest.y)); + } + + return (OK); +} diff --git a/engine/src/GameSrc/physics.c b/engine/src/GameSrc/physics.c new file mode 100644 index 0000000..90f3144 --- /dev/null +++ b/engine/src/GameSrc/physics.c @@ -0,0 +1,1757 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/physics.c $ + * $Revision: 1.259 $ + * $Author: xemu $ + * $Date: 1994/11/10 16:05:25 $ + * + */ + +#define __PHYSICS_SRC + +#include +#include + +#include "ai.h" +#include "combat.h" +#include "criterr.h" +#include "cyber.h" +#include "cybmem.h" +#include "cybstrng.h" +#include "damage.h" +#include "diffq.h" // for time limit +#include "drugs.h" +#include "effect.h" +#include "faketime.h" +#include "framer8.h" +#include "froslew.h" +#include "grenades.h" +#include "ice.h" +#include "input.h" +#include "leanmetr.h" +#include "loops.h" +#include "lvldata.h" +#include "map.h" +#include "mapflags.h" +#include "musicai.h" +#include "objbit.h" +#include "objects.h" +#include "objsim.h" +#include "objprop.h" +#include "objuse.h" +#include "otrip.h" +#include "physics.h" +#include "physunit.h" +#include "player.h" +#include "render.h" +#include "sfxlist.h" +#include "tilename.h" +#include "tools.h" +#include "trigger.h" +#include "wares.h" +#include "weapons.h" +#include "mouselook.h" + +#define EXPAND_FIX(x) fix_int(x), fix_frac(x) + +#define sqr(fixval) (fix_mul(fixval, fixval)) + +// INTERNAL PROTOTYPES +// -------------------- +uchar safety_net_wont_you_back_me_up(ObjID oid); +void add_edms_delete(int ph); +errtype compare_locs(void); +void relax_axis(int axis); +void terrain_object_collide(physics_handle src, ObjID target); +errtype run_cspace_collisions(ObjID obj, ObjID exclude, ObjID exclude2); + +// STANDARD MODELS +// --------------- + +#define STANDARD_HARDNESS fix_make(15, 0) +#define STANDARD_ROUGHNESS fix_make(5, 0) +#define STANDARD_PEP fix_make(5, 0) +#define DEFAULT_SIZE (FIX_UNIT / 2) +#define STANDARD_HEIGHT fix_make(0, 0xbd00) + +TerrainData terrain_info; +State standard_state; + +Robot standard_robot = {STANDARD_MASS, DEFAULT_SIZE, STANDARD_HARDNESS, STANDARD_PEP, STANDARD_GRAVITY, FALSE}; + +Pelvis standard_pelvis = {STANDARD_MASS, DEFAULT_SIZE, STANDARD_HARDNESS, STANDARD_PEP, STANDARD_GRAVITY, + STANDARD_HEIGHT, FALSE}; + +fix standard_corner[4] = {0, 0, 0, 0}; +Dirac_frame standard_dirac = { + STANDARD_MASS, STANDARD_HARDNESS, STANDARD_ROUGHNESS, STANDARD_GRAVITY, +#ifdef HMMMM + standard_corner, standard_corner, standard_corner, standard_corner, standard_corner, + standard_corner, standard_corner, standard_corner, standard_corner, standard_corner, +#endif +}; + +extern ObjID physics_handle_id[]; +extern int physics_handle_max; + +#define check_up(num) + +cams *motion_cam = NULL; // what to move, default null is the default camera + +// CONTROLS +// -------- + +byte player_controls[CONTROL_BANKS][DEGREES_OF_FREEDOM] = { + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0} +}; + +long old_ticks; + +// Collision callback testing.... +void cit_collision_callback(physics_handle C, physics_handle V, int32_t bad, int32_t DATA1, int32_t DATA2, fix location[3]); +void cit_awol_callback(physics_handle caller); +void cit_autodestruct_callback(physics_handle caller); + +// mapping from physics controls to camera controls +int ctrl2cam[DEGREES_OF_FREEDOM] = {EYE_X, EYE_Y, EYE_Z, EYE_H, EYE_P, EYE_B}; + +#define SLEW_SCALE_N 16 +#define SLEW_SCALE_D 100 + +#define MAX_EDMS_DELETE_OBJS 50 + +#ifdef EDMS_SAFETY_NET +short safety_fail_oid = -1; +uchar safety_fail_count = 0; +#define SECRET_NET_OUT_P(x, y) (x > 0x20) +#define TOGGLEABLE_SNET +#endif + +uchar safety_net_on = TRUE; +short curr_edms_del = 0; +short edms_delete_queue[MAX_EDMS_DELETE_OBJS]; + +uchar safety_net_wont_you_back_me_up(ObjID oid) { + obj_move_to(oid, &objs[oid].loc, TRUE); + if (safety_fail_oid == oid) { + safety_fail_oid = -1; + return FALSE; + } + if (oid == PLAYER_OBJ) + safety_fail_oid = oid; + else if (safety_fail_oid == -1) + safety_fail_oid = oid; + safety_fail_count = 3; + return TRUE; +} + +void add_edms_delete(int ph) { + int i = 0; + uchar bad = FALSE; + + for (i = 0; i < curr_edms_del; i++) + if (edms_delete_queue[i] == ph) + bad = TRUE; + + if (ph == -1) + bad = TRUE; + + if (!bad) { + edms_delete_queue[curr_edms_del] = ph; + curr_edms_del++; + objs[physics_handle_id[ph]].info.ph = -1; + physics_handle_id[ph] = OBJ_NULL; + } +} + +void edms_delete_go() { + int i; + for (i = 0; i < curr_edms_del; i++) { + if (edms_delete_queue[i] != -1) + EDMS_kill_object(edms_delete_queue[i]); + edms_delete_queue[i] = -1; + } + curr_edms_del = 0; +} + +void get_phys_state(int ph, State *new_state, ObjID id) { + char use_mod = EDMS_ROBOT; + if (id != OBJ_NULL) { + use_mod = ObjProps[OPNUM(id)].physics_model; +#ifdef DIRAC_EDMS + // This is hacked on account of the player having 2 physics models. + // We may want to take an unused critter slot like the lifter bot to be the dirac-player + // like sonic is our pelvis-player. + if (id == PLAYER_OBJ) + use_mod = (global_fullmap->cyber) ? EDMS_DIRAC : EDMS_PELVIS; +#endif + } + switch (use_mod) { + case EDMS_PELVIS: + EDMS_get_pelvic_viewpoint(ph, new_state); + break; + case EDMS_DIRAC: + EDMS_get_Dirac_frame_viewpoint(ph, new_state); + break; + default: + EDMS_get_state(ph, new_state); + break; + } +} + +void physics_zero_all_controls() { LG_memset(player_controls, 0, sizeof(player_controls)); } + +errtype physics_set_player_controls(int bank, byte xvel, byte yvel, byte zvel, byte xyrot, byte yzrot, byte xzrot) { + if (xvel != CONTROL_NO_CHANGE) + player_controls[bank][CONTROL_XVEL] = xvel; + if (yvel != CONTROL_NO_CHANGE) + player_controls[bank][CONTROL_YVEL] = yvel; + if (zvel != CONTROL_NO_CHANGE) + player_controls[bank][CONTROL_ZVEL] = zvel; + if (xyrot != CONTROL_NO_CHANGE) + player_controls[bank][CONTROL_XYROT] = xyrot; + if (yzrot != CONTROL_NO_CHANGE) + player_controls[bank][CONTROL_YZROT] = yzrot; + if (xzrot != CONTROL_NO_CHANGE) + player_controls[bank][CONTROL_XZROT] = xzrot; + return OK; +} + +errtype physics_set_one_control(int bank, int num, byte val) { + if (val != CONTROL_NO_CHANGE) + player_controls[bank][num] = val; + return OK; +} + +errtype physics_get_one_control(int bank, int num, byte *val) { + *val = player_controls[bank][num]; + return OK; +} + +int old_x = -1, old_y = -1, old_lev = -1; +char old_bits = 0; +extern uchar decon_count; +extern uchar in_deconst; +extern uchar in_peril; + +#define TUNNEL_CONTROL_MAX fix_make(0x8, 0) +#define MATCHBOX_SPEED fix_make(0x3F, 0) + +// The concept here is that if you want something to happen when the player switches +// square, put it here... + +errtype compare_locs(void) { + MapElem *newElem, *oldElem; + extern int score_playing; + + if ((old_x != PLAYER_BIN_X) || (old_y != PLAYER_BIN_Y) || (old_lev != player_struct.level)) { + newElem = MAP_GET_XY(PLAYER_BIN_X, PLAYER_BIN_Y); + oldElem = MAP_GET_XY(old_x, old_y); + + // Change music + if (music_on) { + if (!global_fullmap->cyber) { + in_deconst = me_bits_deconst(newElem); + if (!in_deconst) + decon_count = 0; + in_peril = me_bits_peril(newElem); + if (old_bits != me_bits_music(newElem)) { + fade_into_location(PLAYER_BIN_X, PLAYER_BIN_Y); + old_bits = me_bits_music(newElem); + } + } + } + + // Abort physics if bad karma + if (physics_running && time_passes && (me_tiletype(newElem) == TILE_SOLID)) { + physics_running = FALSE; + critical_error(CRITERR_EXEC | 2); + } + + // Look for traps + if (time_passes) + check_entrance_triggers(old_x, old_y, PLAYER_BIN_X, PLAYER_BIN_Y); + + check_hazard_regions(newElem); + + old_x = PLAYER_BIN_X; + old_y = PLAYER_BIN_Y; + old_lev = player_struct.level; + } + + return (OK); +} + +uchar control_relax[DEGREES_OF_FREEDOM]; + +void physics_set_relax(int axis, uchar relax) { control_relax[axis] = relax; } + +void relax_axis(int axis) { + switch (axis) { + case CONTROL_XZROT: + player_set_lean(0, player_struct.leany); + break; + case CONTROL_YZROT: + player_set_eye(0); + break; + } +} + +static ubyte crouch_controls[NUM_POSTURES] = {0, 6, 10}; + +#define CSPACE_COLLIDE_DIST 0xD0 +#define CSPACE_FAR_COLLIDE_DIST 0x120 + +#define MAX_PITCH_RATE (FIXANG_PI / 4) +#define MAX_LEAN_RATE 400 + +#define ITER_OBJSPECS(pmo, objspec) \ + for (pmo = (objspec[OBJ_SPEC_NULL]).id; pmo != OBJ_SPEC_NULL; pmo = objspec[pmo].next) + +// Yow, we have GOT to be able to make this faster/better... -- Xemu +errtype run_cspace_collisions(ObjID obj, ObjID exclude, ObjID exclude2) { + ObjRefID oref = me_objref(MAP_GET_XY(OBJ_LOC_BIN_X(objs[obj].loc), OBJ_LOC_BIN_Y(objs[obj].loc))); + short use_dist; + while (oref != OBJ_REF_NULL) { + ObjID oid = objRefs[oref].obj; + ObjLoc l1 = objs[oid].loc; + ObjLoc l2 = objs[obj].loc; + switch (ID2TRIP(oid)) { + case CYBERTOG1_TRIPLE: + case CYBERTOG2_TRIPLE: + case CYBERTOG3_TRIPLE: + use_dist = CSPACE_FAR_COLLIDE_DIST; + break; + default: + use_dist = CSPACE_COLLIDE_DIST; + break; + } + if ((oid != obj) && (oid != exclude) && (oid != exclude2) && (objs[oid].obclass != CLASS_PHYSICS)) { + // This really ought to take PX into account! + if ((abs(l1.x - l2.x) < use_dist) && (abs(l1.y - l2.y) < use_dist) && + ((abs(l1.z - l2.z) << SLOPE_SHIFT_D) < use_dist)) { + obj_cspace_collide(oid, obj); + } + } + oref = objRefs[oref].next; + } + return (OK); +} + +#define PLAYER_JIGGLE_THRESHOLD 0x3 // this is in objsys coords + +#define MAX_SPRINT (fix_make(25, 0)) +#define MAX_JOG (fix_make(8, 0)) +#define MAX_BOOSTER (fix_make(50, 0)) +#define SKATE_ALPHA_CUTOFF (fix_make(8, 0)) +#define MAX_BOOSTER_ALPHA (fix_make(40, 0)) + +// Takes a physics state and converts it into an Objloc +// externed in objsim.c +void state_to_objloc(State *s, ObjLoc *l) { + l->x = obj_coord_from_fix(s->X); + l->y = obj_coord_from_fix(s->Y); + l->z = obj_height_from_fix(s->Z); + l->h = obj_angle_from_phys(s->alpha); +#ifdef WHY_DOESNT_THIS_WORK + l->p = obj_angle_from_phys(s->beta); + l->b = obj_angle_from_phys(s->gamma); +#endif +} + +#define NO_DEFAULT_FORWARD_IN_CSPACE + +#define CYB_VEL_DELTA 32 +#define CYB_VEL_DELTA2 16 +ubyte old_head = 0, old_pitch = 0; +short last_deltap = 0, last_deltah = 0; + +errtype physics_run(void) { + int i; + + uchar update = FALSE; + int deltat = player_struct.deltat; + fix plr_y, plr_z, time_diff; + fix plr_alpha; + State new_state; + uchar some_move = FALSE; + extern int fire_kickback; + extern uchar hack_takeover; + static long kickback_time = 0; // i bet this static will someday bite our butts, like save/rest mid kickback? +#ifdef EDMS_SAFETY_NET + uchar allow_move = TRUE; +#endif + + // Run the mouse look + mouse_look_physics(); + + // Here we are computing the values of the player's controls + // from the values of the original control banks. The value + // of each control is the average of its non-zero control + // values from each control bank, or zero if all are zero. + for (i = 0; i < DEGREES_OF_FREEDOM; i++) { + int b, n = 0; + short control = 0; + for (b = 0; b < CONTROL_BANKS; b++) + if (player_controls[b][i] != 0) { + control += player_controls[b][i]; + n++; + } + if (n > 0) { + some_move = TRUE; // should really do the n!=1 for 2 and 4 as shift too + if (n > 1) { + control /= n; + } + } else if (control_relax[i]) { + relax_axis(i); + } + player_struct.controls[i] = control; + } + update = some_move; // well, set one of them only once + if (physics_running && time_passes) { + int i; + ObjID oid; + int damp; + fix plr_side; + fix plr_lean; + ObjSpecID osid; + { + fix crouch = fix_make(0, player_struct.lean_filter_state); + damp = 3 * sqr(STANDARD_HEIGHT - crouch) / sqr(STANDARD_HEIGHT) + 1; + } + osid = objPhysicss[0].id; + while (osid != OBJ_SPEC_NULL) { + // clear the terrain flag + + if (objPhysicss[osid].p3.x > 0) + objPhysicss[osid].p3.x--; + + // clear the collision flag + if (objPhysicss[osid].p3.y > 0) + objPhysicss[osid].p3.y--; + + osid = objPhysicss[osid].next; + } + + if (motionware_mode == MOTION_SKATES && damp > 1) + damp--; + // Here' s where we do leaning. + if (player_struct.foot_planted) { + plr_y = plr_alpha = plr_side = 0; + physics_set_relax(CONTROL_XZROT, FALSE); + } else { + byte leanx = player_struct.leanx; + byte ycntl = player_struct.controls[CONTROL_YVEL]; + short maxlean = (int)(SPRINT_CONTROL_THRESHOLD - abs(ycntl)) * CONTROL_MAX_VAL / SPRINT_CONTROL_THRESHOLD; + plr_side = fix_make(player_struct.controls[CONTROL_XVEL], 0) / 7 / damp; + plr_alpha = fix_make(player_struct.controls[CONTROL_XYROT], 0) / -5; // / damp; + if (ycntl <= SPRINT_CONTROL_THRESHOLD) + plr_y = ycntl * MAX_JOG / SPRINT_CONTROL_THRESHOLD; + else + plr_y = (ycntl - SPRINT_CONTROL_THRESHOLD) * (MAX_SPRINT - MAX_JOG) / + (CONTROL_MAX_VAL - SPRINT_CONTROL_THRESHOLD) + + MAX_JOG; + plr_y /= damp; + physics_set_relax(CONTROL_XZROT, abs((short)leanx) > maxlean); + } + if (player_struct.drug_status[DRUG_REFLEX] > 0 && !global_fullmap->cyber) { + // Increase some controls to reflect smaller timestep + plr_y = plr_y << 2; + plr_alpha = plr_alpha << 2; + plr_side = plr_side << 2; + } + switch (motionware_mode) { + case MOTION_BOOST: { + plr_y = MAX_BOOSTER; + // whoop whoop hardcoded version number. + if (player_struct.hardwarez[CPTRIP(MOTION_HARD_TRIPLE)] < 3) { + if (plr_alpha < 0) + plr_alpha = -MAX_BOOSTER_ALPHA; + if (plr_alpha > 0) + plr_alpha = MAX_BOOSTER_ALPHA; + } + break; + } + case MOTION_SKATES: + plr_y *= 2; + if (plr_y > 3 * MAX_SPRINT / 2) { + plr_y = 3 * MAX_SPRINT / 2; + } else if (plr_y < -MAX_SPRINT) { + plr_y = -MAX_SPRINT; + } + plr_side >>= 2; + if (abs(plr_alpha) > SKATE_ALPHA_CUTOFF) { + if (plr_alpha > 0) + plr_alpha = SKATE_ALPHA_CUTOFF + (plr_alpha - SKATE_ALPHA_CUTOFF) / 2; + else + plr_alpha = -(SKATE_ALPHA_CUTOFF + (-plr_alpha - SKATE_ALPHA_CUTOFF) / 2); + } + + break; + } + + time_diff = fix_make(deltat, 0); // do this here for constant length kickback hack + + if (time_diff > fix_make(CIT_CYCLE, 0) / MIN_FRAME_RATE) + time_diff = fix_make(CIT_CYCLE, 0) / MIN_FRAME_RATE; + + if (player_struct.controls[CONTROL_XZROT] != 0) { + short delta = player_struct.controls[CONTROL_XZROT] * MAX_LEAN_RATE / CONTROL_MAX_VAL; + int leanx = player_struct.leanx; + leanx = lg_min(CONTROL_MAX_VAL, lg_max(leanx + delta * deltat / CIT_CYCLE, -CONTROL_MAX_VAL)); + player_set_lean(leanx, player_struct.leany); + } + if (player_struct.controls[CONTROL_YZROT] != 0) { + int delta = player_struct.controls[CONTROL_YZROT] * MAX_PITCH_RATE / CONTROL_MAX_VAL; + int eye = player_get_eye_fixang(); + if (player_struct.drug_status[DRUG_REFLEX] > 0 && !global_fullmap->cyber) + delta <<= 2; + eye = lg_min(FIXANG_PI / 2, lg_max(eye + delta * deltat / CIT_CYCLE, -FIXANG_PI / 2)); + player_set_eye_fixang(eye); + } + + plr_lean = fix_make((int)player_struct.leanx, 0) / 3; + if (player_struct.controls[CONTROL_ZVEL] > 0) { + extern uchar jumpjets_active; + + player_set_posture(POSTURE_STAND); + plr_z = fix_make(player_struct.controls[CONTROL_ZVEL], 0); + activate_jumpjets(&plr_side, &plr_y, &plr_z); + jumpjets_active = plr_z < 0; + } else { + extern uchar jumpjets_active; + plr_z = fix_make(player_struct.controls[CONTROL_ZVEL], 0); /* /3/damp; */ + jumpjets_active = FALSE; + } + + if (global_fullmap->cyber) { + MapElem *pme = MAP_GET_XY(PLAYER_BIN_X, PLAYER_BIN_Y); + if (me_light_flr(pme)) { + if (plr_y > TUNNEL_CONTROL_MAX) + plr_y = TUNNEL_CONTROL_MAX; + } + // make controls non-linear. + plr_side = fix_mul(plr_side, abs(plr_side)) / 16; + plr_alpha = fix_mul(plr_alpha, abs(plr_alpha)) / 16; + +#ifdef NO_DEFAULT_FORWARD_IN_CSPACE + if (QUESTVAR_GET(CYBER_DIFF_QVAR) > 1) + plr_z += fix_make(QUESTVAR_GET(CYBER_DIFF_QVAR) * 5, 0); +#endif + + // Effects of turbo, multiply all movement axes by 4 + if (cspace_effect_times[CS_TURBO_EFF]) { + plr_y = plr_y << 1; + plr_z = plr_z << 1; + plr_side = plr_side << 1; + } +#ifdef MATCHBOX_SUPPORT + else if (cspace_effect_times[CS_MATCHBOX_EFF]) { + plr_z = MATCHBOX_SPEED; + } +#endif + } + if (!global_fullmap->cyber && player_struct.drug_status[CPTRIP(GENIUS_DRUG_TRIPLE)] > 0) { + // Reverse! + plr_alpha = -plr_alpha; + plr_side = -plr_side; + plr_lean = -plr_lean; + } + +#ifdef DIRAC_EDMS + if (global_fullmap->cyber) { + // printf("EDMS_control_Dirac_frame\n"); + EDMS_control_Dirac_frame(PLAYER_PHYSICS, plr_z, plr_alpha + fix_make(mlook_vel_x, 5), + plr_y - fix_make(mlook_vel_y, 5), plr_side); + } else +#endif + // printf("EDMS_control_pelvis %i %i %i %i %i %i %i\n", PLAYER_PHYSICS, plr_y, plr_alpha, plr_side, + // plr_lean, plr_z, crouch_controls[player_struct.posture]); + EDMS_control_pelvis(PLAYER_PHYSICS, plr_y, plr_alpha, plr_side, plr_lean, plr_z, + crouch_controls[player_struct.posture]); + +#ifdef SOLITON_HACK_REFLEX + if (player_struct.drug_status[DRUG_REFLEX] > 0 && !global_fullmap->cyber) + EDMS_soliton_vector((time_diff / CIT_CYCLE) >> 2); + else +#endif + EDMS_soliton_vector(time_diff / CIT_CYCLE); + + edms_delete_go(); + + if (--safety_fail_count == 0) + safety_fail_oid = -1; + for (i = 0; i <= physics_handle_max; i++) // all ph + { + if ((oid = physics_handle_to_id(i)) != OBJ_NULL) // valid ph, get oid + { + ObjLoc newloc; + + newloc = objs[oid].loc; + + EDMS_get_state(objs[oid].info.ph, &new_state); + state_to_objloc(&new_state, &newloc); + + if ((oid == PLAYER_OBJ) && global_fullmap->cyber) { + ubyte new_head, new_pitch; //, new_bank; + short new_deltah, new_deltap; + State cyber_state; + extern uchar new_cyber_orient; + + get_phys_state(objs[PLAYER_OBJ].info.ph, &cyber_state, PLAYER_OBJ); + + new_head = obj_angle_from_phys(cyber_state.alpha); + new_pitch = obj_angle_from_phys(cyber_state.beta); + + new_deltah = abs(new_head - old_head); + new_deltap = abs(new_pitch - old_pitch); + + if (((new_deltah < CYB_VEL_DELTA) && (new_deltap < CYB_VEL_DELTA)) || new_cyber_orient || + ((abs(last_deltah - new_deltah) < CYB_VEL_DELTA2) && + (abs(last_deltap - new_deltap) < CYB_VEL_DELTA2))) { + old_head = new_head; + old_pitch = new_pitch; + last_deltah = last_deltap = 0; + + if (new_cyber_orient) + new_cyber_orient = FALSE; + } else { + last_deltah = new_deltah; + last_deltap = new_deltap; + } + } + + // See if we should snap the player out of his + // reverie, either hack camera or automap induced. + if (oid == PLAYER_OBJ) { + if (hack_takeover && + (some_move || (abs(newloc.x - objs[oid].loc.x) + abs(newloc.y - objs[oid].loc.y) + + abs(newloc.z - objs[oid].loc.z) > + PLAYER_JIGGLE_THRESHOLD))) + hack_camera_relinquish(); + } + +#ifdef EDMS_SAFETY_NET +#ifdef TOGGLEABLE_SNET + if (safety_net_on) +#endif + { + if (me_tiletype(MAP_GET_XY(OBJ_LOC_BIN_X(newloc), OBJ_LOC_BIN_Y(newloc))) == TILE_SOLID) { + safety_net_wont_you_back_me_up(oid); + allow_move = FALSE; + } else if (new_state.Z < fix_from_map_height(me_height_flr( + MAP_GET_XY(OBJ_LOC_BIN_X(newloc), OBJ_LOC_BIN_Y(newloc))))) { + if (safety_net_wont_you_back_me_up(oid)) + allow_move = FALSE; + else { + if (objs[oid].obclass == CLASS_CRITTER) { + newloc = objs[oid].loc; + newloc.z += 3; + safety_fail_oid = -1; + allow_move = TRUE; + } else { + add_edms_delete(objs[oid].info.ph); + allow_move = FALSE; // just plain sit here and lose, eh? + } + } + } + } + + if (allow_move) + obj_move_to(oid, &newloc, FALSE); + else + allow_move = TRUE; +#else + obj_move_to(oid, &newloc, FALSE); +#endif + } + } + } else if (some_move) { // what is going on here... ah-ha, we objslew.. no wrong + for (i = 0; i < DEGREES_OF_FREEDOM; i++) + if (player_struct.controls[i] != 0) + fr_camera_slewcam(motion_cam, ctrl2cam[i], player_struct.controls[i] * SLEW_SCALE_N / SLEW_SCALE_D); + } + old_ticks = *tmd_ticks; + if (update || hack_takeover) + chg_set_flg(_current_3d_flag); + if (physics_running && global_fullmap->cyber) { + ObjSpecID specid; + // check for the player + run_cspace_collisions(PLAYER_OBJ, OBJ_NULL, OBJ_NULL); + + // check for all slow projectiles + ITER_OBJSPECS(specid, objPhysicss) { + run_cspace_collisions(objPhysicss[specid].id, PLAYER_OBJ, objPhysicss[specid].owner); + } + } + compare_locs(); + return (OK); +} + +#ifdef NOT_YET // later, dude + +#ifdef WACKY_OLD_TERR_FUNC + +/// --------------------------------------------------- +/// HERE COMES THE TERRAIN FUNCTION + +/// 9/20 ML I ain't tellin' you a secret... +/// I ain't tellin' you goodBIYEYE... + +/* --------------------------------- + HAQ ALERT! HAQ ALERT! + Ok, oftimes we're squaring small numbers. + So we're going to use our own special + 8-24 intermediate fixpoint representation to + store the squares + -------------------------------- */ + +typedef fix wacky; + +// Our own special 8-24 fixmul +wacky wacky_mul(fix a, fix b); +#pragma aux wacky_mul = "imul edx" \ + "shr eax,8" \ + "shl edx,24" \ + "or eax,edx" parm[eax][edx] modify[eax edx]; + +wacky wacky_div(wacky a, wacky b); +#pragma aux wacky_div = "mov edx,eax" \ + "sar edx,8" \ + "shl eax,24" \ + "idiv ebx" parm[eax][ebx] modify[eax edx]; + +typedef fix pt3d[3]; + +#define PTARGS(pt) fix_float((pt)[0]), fix_float((pt)[1]), fix_float((pt)[2]) + +#define dotprod(vec1, vec2, result) \ + { \ + fix *v1 = (vec1); \ + fix *v2 = (vec2); \ + (result) = 0; \ + result += fix_mul(*(v1++), *(v2++)); \ + result += fix_mul(*(v1++), *(v2++)); \ + result += fix_mul(*(v1++), *(v2++)); \ + } + +#define wsqr(fixval) (wacky_mul(fixval, fixval)) +#define magsquared(vec, res) \ + { \ + fix *v1 = (vec); \ + (res) = 0; \ + (res) += wsqr(*(v1++)); \ + (res) += wsqr(*(v1++)); \ + (res) += wsqr(*(v1++)); \ + } + +uchar vec_equal(fix *v1, fix *v2) { + if (*(v1++) == *(v2++) && *(v1++) == *(v2++) && *(v1++) == *(v2++)) + return TRUE; + else + return FALSE; +} + +#define wacky2fix(m2) (m2 >> 8) +#define fix2wacky(m) (m << 8) +#define wacky_float(w) fix_float(wacky2fix(w)) + +#define NORMAL_X 0 +#define NORMAL_Y 1 +#define NORMAL_Z 2 + +int compute_normal_code(pt3d norm) { + if (abs(norm[0]) > abs(norm[1])) { + if (abs(norm[0]) > abs(norm[2])) + return NORMAL_X; + else + return NORMAL_Z; + } else { + if (abs(norm[1]) > abs(norm[2])) + return NORMAL_Y; + else + return NORMAL_Z; + } +} + +#define PHYS_SPEW(x, y) + +#define crossprod(v1, v2, out) \ + { \ + fix *a1 = (v1); \ + fix *a2 = (v1); \ + fix *b1 = (v2); \ + fix *b2 = (v2); \ + fix *o = (out); \ + fix z = fix_mul(*(v1), (*++b2)) - fix_mul((*++a2), *(v2)); \ + *(o++) = fix_mul((*++a1), (*++b2)) - fix_mul((*++a2), (*++b1)); \ + *(o++) = fix_mul((*++a1), *(v2)) - fix_mul(*(v1), (*++b1)); \ + *(o++) = z; \ + } + +fix project_onto_facelet(pt3d in, pt3d out, pt3d flet[NUM_POINTS]) { + g3s_vector topt; + fix dp; + int i; + g3s_vector norm = *(g3s_vector *)(flet[NORM_IDX]); + g3_vec_sub(&topt, (g3s_vector *)in, (g3s_vector *)(flet[0])); + dp = g3_vec_dotprod(&topt, &norm); + g3_vec_scale(&norm, &norm, dp); + // Subtract out normal component + g3_vec_sub((g3s_vector *)out, (g3s_vector *)in, &norm); + PHYS_SPEW(DSRC_PHYSICS_Terrain, + ("Projection onto facelet: %q %q %q --> %q %q %q\nproj =%q\n", PTARGS(in), PTARGS(out), fix_float(dp))); + return dp; +} + +int normcode_indices[3][2] = {{1, 2}, {0, 2}, {0, 1}}; + +// Takes a point on the plane of a facelet, and computes +// the distance from the projection to the facelet. Returns +// zero if the point projects onto the interior of the facelet. +fix facelet_distance_sq_4points(pt3d pt, pt3d flet[NUM_POINTS], uchar normcode) { + fix best_dsq = FIX_MAX; // minimum distance squared + int best_vert = -1; + fix *best_edge; + fix *cprod_edge; + fix cprod_msq; + pt3d edgen; + pt3d cprod; + pt3d edgep; + pt3d topt; // vertex to point; + { + pt3d *vert = flet; + fix result; + + g3_vec_sub((g3s_vector *)topt, (g3s_vector *)pt, (g3s_vector *)vert); + result = g3_vec_mag((g3s_vector *)topt); + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("topt %q %q %q dsq %d best_dsq %d\n", PTARGS(topt), result, best_dsq)); + if (result < best_dsq) { + best_dsq = result; + best_vert = 0; + } + vert++; + g3_vec_sub((g3s_vector *)topt, (g3s_vector *)pt, (g3s_vector *)vert); + result = g3_vec_mag((g3s_vector *)topt); + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("topt %q %q %q dsq %d best_dsq %d\n", PTARGS(topt), result, best_dsq)); + if (result < best_dsq) { + best_dsq = result; + best_vert = 1; + } + vert++; + g3_vec_sub((g3s_vector *)topt, (g3s_vector *)pt, (g3s_vector *)vert); + result = g3_vec_mag((g3s_vector *)topt); + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("topt %q %q %q dsq %d best_dsq %d\n", PTARGS(topt), result, best_dsq)); + if (result < best_dsq) { + best_dsq = result; + best_vert = 2; + } + vert++; + g3_vec_sub((g3s_vector *)topt, (g3s_vector *)pt, (g3s_vector *)vert); + result = g3_vec_mag((g3s_vector *)topt); + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("topt %q %q %q dsq %d best_dsq %d\n", PTARGS(topt), result, best_dsq)); + if (result < best_dsq) { + best_dsq = result; + best_vert = 3; + } + } + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Closest vertex %d\n", best_vert)); + { + fix a1, a2, b1, b2, p1, p2, c1, c2; + int i1, i2; + fix d; + int nx = (best_vert + 1) & 0x3; + int pr = (best_vert + 3) & 0x3; + fix *ep = edgep; + fix *en = edgen; + fix *tp = topt; + fix *p = pt; + fix *vert = &flet[best_vert][0]; + fix *prev = &flet[pr][0]; + fix *next = &flet[nx][0]; + if (vec_equal(prev, vert)) { + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Caught multiple on prev\n")); + pr = (pr + 3) & 0x3; + prev = flet[pr]; + } + if (vec_equal(next, vert)) { + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Caught multiple on next\n")); + nx = (nx + 1) & 0x3; + next = flet[nx]; + } + g3_vec_sub((g3s_vector *)ep, (g3s_vector *)prev, (g3s_vector *)vert); + g3_vec_sub((g3s_vector *)en, (g3s_vector *)next, (g3s_vector *)vert); + g3_vec_sub((g3s_vector *)tp, (g3s_vector *)p, (g3s_vector *)vert); + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("vert: (%d) %q %q %q\n", best_vert, PTARGS(vert))); + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("prev: (%d) %q %q %q\n", pr, PTARGS(prev))); + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("next: (%d) %q %q %q\n", nx, PTARGS(next))); + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Normal code is %d\n", normcode)); + + // now throw out one of the coordinates + i1 = normcode_indices[normcode][0]; + i2 = normcode_indices[normcode][1]; + + a1 = edgep[i1]; + a2 = edgep[i2]; + b1 = edgen[i1]; + b2 = edgen[i2]; + p1 = topt[i1]; + p2 = topt[i2]; + // now that we've picked the coordinate system, transform the point into the edge basis. + c1 = fix_mul(b2, p1) - fix_mul(b1, p2); + c2 = fix_mul(a1, p2) - fix_mul(a2, p1); + d = fix_mul(a1, b2) - fix_mul(a2, b1); + if (d < 0) + c1 = -c1, c2 = -c2, d = -d; + PHYS_SPEW(DSRC_PHYSICS_Terrain, + ("a: %q %q, b %q %q, c %q %q, p %q %q d %q\n", fix_float(a1), fix_float(a2), fix_float(b1), + fix_float(b2), fix_float(c1), fix_float(c2), fix_float(p1), fix_float(p2), fix_float(d))); + if (c1 < 0 || c2 < 0) { + fix mag; + // we're outside the quadrilateral + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Outside the quad\n")); + if (c1 > 0) { + p1 -= fix_mul(c1, a1); + p2 -= fix_mul(c1, a2); + } else if (c2 > 0) { + p1 -= fix_mul(c2, b1); + p2 -= fix_mul(c2, b2); + } else { + mag = g3_vec_mag((g3s_vector *)topt); + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("topt %q %q %q mag %q\n", PTARGS(topt), fix_float(mag))); + return mag; + } + topt[i1] = p1; + topt[i2] = p2; + + mag = g3_vec_mag((g3s_vector *)topt); + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("topt %q %q %q mag %q\n", PTARGS(topt), fix_float(mag))); + return mag; + } else + return 0; + } +} + +#define mod3(x) (((x) > 2) ? (x)-3 : (x)) + +fix facelet_distance_sq_3points(pt3d pt, pt3d flet[NUM_POINTS], uchar normcode) { + int i; + fix best_dsq = FIX_MAX; // minimum distance squared + int best_vert = -1; + fix *best_edge; + fix *cprod_edge; + fix cprod_msq; + pt3d edgen; + pt3d cprod; + pt3d edgep; + pt3d topt; // vertex to point; + for (i = 0; i < 3; i++) { + int next = mod3(i + 1); + pt3d edge; // edge vector + // build the edge & point vectors + { + // fix* e = edge; + // fix* n = flet[next]; + fix *t = topt; + fix *p = pt; + fix *v = flet[i]; + // *(e++) = *(n++) - *(v); + *(t++) = *(p++) - *(v++); + // *(e++) = *(n++) - *(v); + *(t++) = *(p++) - *(v++); + // *(e++) = *(n++) - *(v); + *(t++) = *(p++) - *(v++); + } + { + fix result; + dotprod(topt, topt, result); + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("topt %q %q %q dsq %d best_dsq %d\n", PTARGS(topt), result, best_dsq)); + if (result < best_dsq) { + best_dsq = result; + best_vert = i; + } + } + } + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Closest vertex %d\n", best_vert)); + { + fix a1, a2, b1, b2, p1, p2, c1, c2; + fix d; + int nx = (best_vert < 2) ? best_vert + 1 : 0; + int pr = (best_vert > 0) ? best_vert - 1 : 2; + fix *ep = edgep; + fix *en = edgen; + fix *tp = topt; + fix *p = pt; + fix dp; + fix cp, cn; + fix dn; + fix msp; + fix msn; + fix *vert = &flet[best_vert][0]; + fix *prev = &flet[pr][0]; + fix *next = &flet[nx][0]; + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("vert: (%d) %q %q %q\n", best_vert, PTARGS(vert))); + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("prev: (%d) %q %q %q\n", pr, PTARGS(prev))); + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("next: (%d) %q %q %q\n", nx, PTARGS(next))); + *(ep++) = *(prev++) - *(vert); + *(en++) = *(next++) - *(vert); + *(tp++) = *(p++) - *(vert); + vert++; + *(ep++) = *(prev++) - *(vert); + *(en++) = *(next++) - *(vert); + *(tp++) = *(p++) - *(vert); + vert++; + *(ep++) = *(prev++) - *(vert); + *(en++) = *(next++) - *(vert); + *(tp++) = *(p++) - *(vert); + vert++; + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Normal code is %d\n", normcode)); + + // now throw out one of the coordinates + switch (normcode) { + case NORMAL_X: + a1 = edgep[1]; + a2 = edgep[2]; + b1 = edgen[1]; + b2 = edgen[2]; + p1 = topt[1]; + p2 = topt[2]; + break; + case NORMAL_Y: + a1 = edgep[0]; + a2 = edgep[2]; + b1 = edgen[0]; + b2 = edgen[2]; + p1 = topt[0]; + p2 = topt[2]; + break; + case NORMAL_Z: + a1 = edgep[0]; + a2 = edgep[1]; + b1 = edgen[0]; + b2 = edgen[1]; + p1 = topt[0]; + p2 = topt[1]; + break; + } + // now that we've picked the coordinate system, transform the point into the edge basis. + c1 = fix_mul(b2, p1) - fix_mul(b1, p2); + c2 = -fix_mul(a2, p1) + fix_mul(a1, p2); + d = fix_mul(a1, b2) - fix_mul(a2, b1); + if (d < 0) + c1 = -c1, c2 = -c2, d = -d; + PHYS_SPEW(DSRC_PHYSICS_Terrain, + ("a: %q %q, b %q %q, c %q %q, p %q %q d %q\n", fix_float(a1), fix_float(a2), fix_float(b1), + fix_float(b2), fix_float(c1), fix_float(c2), fix_float(p1), fix_float(p2), fix_float(d))); + if (c1 < 0 || c2 < 0) { + fix mag; + // we're outside the quadrilateral + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Outside the quad\n")); + if (c1 > 0) { + p1 -= fix_mul(c1, a1); + p2 -= fix_mul(c1, a2); + } else if (c2 > 0) { + p1 -= fix_mul(c2, b1); + p2 -= fix_mul(c2, b2); + } else { + mag = g3_vec_mag((g3s_vector *)topt); + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("topt %q %q %q mag %q\n", PTARGS(topt), fix_float(mag))); + return mag; + } + switch (normcode) { + case NORMAL_X: + topt[1] = p1; + topt[2] = p2; + break; + case NORMAL_Y: + topt[0] = p1; + topt[2] = p2; + break; + case NORMAL_Z: + topt[0] = p1; + topt[1] = p2; + break; + } + + mag = g3_vec_mag((g3s_vector *)topt); + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("topt %q %q %q mag %q\n", PTARGS(topt), fix_float(mag))); + return mag; + } else + return 0; + } +} + +// Given a facelet, translate it r units in the direction of its normal. +// (Mutates the facelet in place, leaves the normal intact) + +void grow_facelet(fix r, fix flet[NUM_POINTS][3]) { + pt3d *coor = flet; + g3s_vector norm; + fix *realnorm = flet[NORM_IDX]; + + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Growing by %q\n", fix_float(r))); + // scale the normal vector + g3_vec_scale(&norm, (g3s_vector *)realnorm, r); + // now translate the coords three times + g3_vec_add((g3s_vector *)*coor, (g3s_vector *)*coor, &norm); + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Point 0: %q %q %q\n", PTARGS(*coor))); + coor++; + g3_vec_add((g3s_vector *)*coor, (g3s_vector *)*coor, &norm); + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Point 1: %q %q %q\n", PTARGS(*coor))); + coor++; + g3_vec_add((g3s_vector *)*coor, (g3s_vector *)*coor, &norm); + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Point 2: %q %q %q\n", PTARGS(*coor))); + coor++; + if (**coor != NO_POINT) // is there a fourth point? + { + g3_vec_add((g3s_vector *)*coor, (g3s_vector *)*coor, &norm); + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Point 3: %q %q %q\n", PTARGS(*coor))); + } +} +#else +typedef fix pt3d[3]; +#endif + +#pragma disable_message(202) +uchar FF_terrain(fix X, fix Y, fix Z, uchar fast, void *TFF) { return (TRUE); } +uchar FF_raycast(fix x, fix y, fix z, fix vec[3], fix range, fix where_hit[3], terrain_ff *tff) { return (TRUE); } +#pragma disable_message(202) + +// ????? +void Terrain(fix fix_x, fix fix_y, fix fix_z, fix rad) {} + +#ifdef OLD_TERR_FUNC +extern fix tfunc_rad, tfunc_pt[3]; +extern fix tfunc_sum[3]; +extern int tfunc_cnt[3]; +extern fix tfunc_norms[3][3]; // floor, wall, ceil + +void full_3d_facelet_action(fix (*fleto)[3], int which) // fix (*norm)[3], int *cnt, fix *sum) +{ + fix proj; + pt3d projpt; + fix dist; + pt3d *flet = fleto; + + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("full 3d %d\n", which)) + grow_facelet(tfunc_rad, flet); + proj = project_onto_facelet(tfunc_pt, projpt, flet); + // proj must be between 0 and -rad + if (proj > 0 || proj < -tfunc_rad) + return; + if (flet[3][0] == NO_POINT) + dist = facelet_distance_sq_3points(projpt, flet, compute_normal_code(flet[NORM_IDX])); + else + dist = facelet_distance_sq_4points(projpt, flet, compute_normal_code(flet[NORM_IDX])); + + // PHYS_SPEW(DSRC_PHYSICS_Terrain,("Distance from facelet %d is %q, proj %q + // \n",i,fix_float(dist),fix_float(proj))); + // if we're closer than our radius, + // scale and add to wall gradient. + if (-proj <= tfunc_rad - dist) { + tfunc_cnt[which]++; + g3_vec_add((g3s_vector *)tfunc_norms[which], (g3s_vector *)tfunc_norms[which], (g3s_vector *)flet[NORM_IDX]); + tfunc_sum[which] -= proj; + PHYS_SPEW(DSRC_PHYSICS_Terrain, ("sum %q, which %d\n", fix_float(-proj), which)); + } +} +#endif + +ubyte param_matters[MAP_TYPES] = { + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, +}; +#endif // NOT_YET + +errtype physics_init() { + EDMS_data init_data; + + // Start EDMS + init_data.playfield_size = fix_make(MAP_XSIZE + 4, 0); // note assumption that X and Y sizes are same + init_data.min_physics_handle = 0; + init_data.collision_callback = cit_collision_callback; + init_data.snooz_callback = cit_sleeper_callback; + init_data.autodestruct_callback = cit_autodestruct_callback; + init_data.awol_callback = cit_awol_callback; + init_data.argblock_pointer = (void *)big_buffer; + EDMS_startup(&init_data); + + // Create some defaults + LG_memset(&standard_state, 0, sizeof(State)); // _memset32l(&standard_state,0,12); + + return (OK); +} + +#define NUM_WARMUP_ITERATIONS 10 +#define EDMS_WARMUP_TIMESTEP fix_make(0, 0x2000) + +/* KLC - doesn't do anything. +errtype physics_warmup() +{ +// int i; +// for (i=0; i < NUM_WARMUP_ITERATIONS; i++) +// EDMS_soliton_vector(EDMS_WARMUP_TIMESTEP); + return(OK); +} +*/ + +errtype apply_gravity_to_one_object(ObjID oid, fix new_grav) { + errtype err = OK; + if (CHECK_OBJ_PH(oid)) { + switch (ObjProps[OPNUM(oid)].physics_model) { + case EDMS_ROBOT: { + Robot parms; + EDMS_get_robot_parameters(objs[oid].info.ph, &parms); + parms.gravity = new_grav; + EDMS_set_robot_parameters(objs[oid].info.ph, &parms); + break; + } + case EDMS_PELVIS: { + Pelvis parms; + EDMS_get_pelvis_parameters(objs[oid].info.ph, &parms); + parms.gravity = new_grav; + EDMS_set_pelvis_parameters(objs[oid].info.ph, &parms); + break; + } + case EDMS_DIRAC: { + Dirac_frame parms; + EDMS_get_Dirac_frame_parameters(objs[oid].info.ph, &parms); + parms.gravity = new_grav; + EDMS_set_Dirac_frame_parameters(objs[oid].info.ph, &parms); + break; + } + default: + err = ERR_RANGE; + break; + } + } + return err; +} + + // ---------------------------------------------------------------- + // Yucky coordinate transformation code. + +#define SIN(x) fix_sin(x) +#define COS(x) fix_cos(x) + + // NOTE, the first index of the matrix is the ROW + + // ---------------------------------------------- + // AND NOW, THE THROWING CODE + +#define THROW_YANG_MAX FIXANG_PI / 6 +#define THROW_XANG_MAX FIXANG_PI / 4 + +fix ID2radius(ObjID id) { + int rad = ObjProps[OPNUM(id)].physics_xr; + if (rad > 0) + return fix_make(rad, 0) / PHYSICS_RADIUS_UNIT; + else + return standard_robot.size; +} + +#define THROW_DISPLACE_RANGE (FIX_UNIT / 2) +#define THROW_ORDER2_SCALE (FIX_UNIT * 2 / 3) +#define THROW_RAYCAST_MASS fix_make(0, 0x2000) +#define THROW_RAYCAST_SPEED fix_make(1, 0) + +uchar player_throw_object(ObjID proj_id, int x, int y, int lastx, int lasty, fix vel) { + LGPoint pos = MakePoint(x, y); + LGPoint lastpos = MakePoint(lastx, lasty); + ObjLoc loc; + Combat_Pt posvec; + Combat_Pt vector; + Combat_Pt vector2; + Combat_Pt locvec; + fix scale_mag; + State new_state; + fix radius = ID2radius(proj_id); + + find_fire_vector(&pos, &vector); + g3_vec_scale((g3s_vector *)&posvec, (g3s_vector *)&vector, THROW_DISPLACE_RANGE + radius); + posvec.z += radius; // put the bottom of the object at the cursor, not the center. + g3_vec_normalize((g3s_vector *)&posvec); + if (CHECK_OBJ_PH(PLAYER_OBJ)) { + if (global_fullmap->cyber) + EDMS_get_Dirac_frame_viewpoint(objs[PLAYER_OBJ].info.ph, &new_state); + else + EDMS_get_pelvic_viewpoint(objs[PLAYER_OBJ].info.ph, &new_state); + } + + // loc = objs[PLAYER_OBJ].loc; + locvec.x = new_state.X; + locvec.y = new_state.Y; + locvec.z = new_state.Z; + vector2 = locvec; + + // raycast out from the player to find a good place to put the object + ray_cast_vector(PLAYER_OBJ, &locvec, posvec, THROW_RAYCAST_MASS, radius, THROW_RAYCAST_SPEED, + THROW_DISPLACE_RANGE + radius); + + g3_vec_sub((g3s_vector *)&vector2, (g3s_vector *)&locvec, (g3s_vector *)&vector2); + scale_mag = g3_vec_mag((g3s_vector *)&vector2); + loc.p = loc.h = loc.b = 0; + + if (scale_mag < THROW_DISPLACE_RANGE + radius) { + g3_vec_scale((g3s_vector *)&locvec, (g3s_vector *)&vector, lg_max(0, scale_mag - radius / 2)); + loc.x = obj_coord_from_fix(new_state.X + locvec.x); + loc.y = obj_coord_from_fix(new_state.Y + locvec.y); + loc.z = obj_height_from_fix(new_state.Z + locvec.z + radius); + scale_mag = 0; + } else { + g3_vec_scale((g3s_vector *)&locvec, (g3s_vector *)&vector, lg_max(0, THROW_DISPLACE_RANGE)); + loc.x = obj_coord_from_fix(new_state.X + locvec.x); + loc.y = obj_coord_from_fix(new_state.Y + locvec.y); + loc.z = obj_height_from_fix(new_state.Z + locvec.z + radius); + scale_mag = FIX_UNIT; + } + if (scale_mag == 0 || vel == 0) + string_message_info(REF_STR_DropMessage); + + find_fire_vector(&lastpos, &vector2); + g3_vec_scale((g3s_vector *)&vector2, (g3s_vector *)&vector2, THROW_ORDER2_SCALE); + g3_vec_sub((g3s_vector *)&vector, (g3s_vector *)&vector, (g3s_vector *)&vector2); + g3_vec_normalize((g3s_vector *)&vector); + g3_vec_scale((g3s_vector *)&vector, (g3s_vector *)&vector, fix_mul(vel, scale_mag)); + + obj_move_to_vel(proj_id, &loc, TRUE, vector.x, vector.y, vector.z); + // let's ignore the thrown object + EDMS_ignore_collisions(objs[PLAYER_OBJ].info.ph, objs[proj_id].info.ph); + if (objs[proj_id].obclass == CLASS_GRENADE) { + // let's get it to hit player after a little time + if (objs[proj_id].subclass == GRENADE_SUBCLASS_DIRECT) + // if (GrenadeProps[CPNUM(proj_id)].flags & GREN_MINE_TYPE) + reactivate_mine(proj_id); + } + chg_set_flg(INVENTORY_UPDATE); + return TRUE; +} + +uchar get_phys_info(int ph, fix *list, int cnt) { + ObjID id = physics_handle_id[ph]; + State new_state; + if (ph == -1) + return FALSE; + get_phys_state(ph, &new_state, id); + if (ObjProps[OPNUM(id)].physics_model == EDMS_ROBOT) { + if (cnt > 4) + cnt = 4; + } + + switch (cnt - 1) { + default: + if (cnt <= 0) + return FALSE; + case 5: + list[5] = fixrad_to_fixang(new_state.gamma); + case 4: + list[4] = fixrad_to_fixang(-new_state.beta); + case 3: + list[3] = fixang_from_phys_angle(new_state.alpha); + case 2: + list[2] = new_state.Z; + case 1: + list[1] = new_state.Y; + case 0: + list[0] = new_state.X; + } + return TRUE; +} + +//------------------------------------------- +// POSTURE STUFF + +errtype player_set_posture(ubyte new_posture) { + player_struct.posture = new_posture; + return OK; +} + +// -------------------------------------------- +// LEANING + +errtype player_set_lean(byte x, byte y) { + player_struct.leanx = x; + player_struct.leany = y; + return OK; +} + +#define FIRST_ENRG_PROJ_TYPE 7 + + // ----------------------------------------------- + // here is our wacky collision callback... + +#define ENERGY_MINE_DRAIN 10 + +short PULSER_DAMAGE[10] = {10, 15, 25, 40, 60, 85, 125, 170, 260, 500}; + +#define NUM_DRILL_LEVELS 5 +#define MAX_DRILL_DAMAGE 725 +#define DRILL_DAMAGE(lev) (MAX_DRILL_DAMAGE / (NUM_DRILL_LEVELS - (lev))) + +ubyte ice_offense_values[] = {1, 2, 3, 4}; +ubyte ice_penetration[] = {5, 5, 10, 15}; +ubyte ice_damage_modifiers[] = {10, 20, 40, 80}; + +errtype collide_objects(ObjID collision, ObjID victim, int bad) { + uchar destroy_me = TRUE; + + if (objs[collision].obclass == CLASS_GRENADE) { + grenade_contact(collision, bad); + } else if ((objs[collision].obclass == CLASS_PHYSICS) && (objs[collision].subclass == PHYSICS_SUBCLASS_SLOW)) { + ObjID owner = objPhysicss[objs[collision].specID].owner; + int a = objPhysicss[objs[collision].specID].bullet_triple; + int cp_num; + ObjLoc loc = objs[collision].loc; + extern ObjID damage_sound_id; + extern char damage_sound_fx; + uchar special_proj = FALSE; + + // set that we've already collided this time + if (objPhysicss[objs[collision].specID].p3.y) + return OK; + else + objPhysicss[objs[collision].specID].p3.y = 3; + + if (owner != PLAYER_OBJ) { + cp_num = CPNUM(owner); + switch (objs[owner].obclass) { + case CLASS_CRITTER: + attack_object(victim, CritterProps[cp_num].attacks[a].damage_type, + CritterProps[cp_num].attacks[a].damage_modifier, + CritterProps[cp_num].attacks[a].offense_value, + CritterProps[cp_num].attacks[a].penetration, 0, 100, NULL, 0, 0, NULL); + break; + default: + if (global_fullmap->cyber && ICE_ICE_BABY(owner)) { + attack_object(victim, CYBER_PROJECTILE_TYPE, ice_damage_modifiers[a], ice_offense_values[a], + ice_penetration[a], 0, 100, NULL, 0, 0, NULL); + } + break; + } + } else { + // Was the player firing a special projectile? + switch (ID2TRIP(collision)) { + case DRILLSLOW_TRIPLE: + if (ICE_ICE_BABY(victim)) { + char soft_lvl = objPhysicss[objs[collision].specID].bullet_triple; + short dmg; + + // Damage the ice -- higher level ICEs are tough, but we always + // do at least a little tiny bit of damage + dmg = lg_max(1, DRILL_DAMAGE(soft_lvl - 1) >> (ICE_LEVEL(victim)) * 2); + + if (dmg < objs[victim].info.current_hp) { + // If it is still alive, agitate it + objs[victim].info.current_hp -= dmg; + SET_ICE_AGIT(victim, lg_min(ICE_AGIT(victim) + soft_lvl, MAX_AGIT)); + } else { + objs[victim].info.current_hp = 0; + DEICE(victim); + } + } + special_proj = TRUE; + break; + case CYBERSLOW_TRIPLE: + if (!(ICE_ICE_BABY(victim))) { + char soft_lvl = objPhysicss[objs[collision].specID].bullet_triple; + simple_damage_object(victim, PULSER_DAMAGE[soft_lvl - 1], CYBER_PROJECTILE_TYPE, 0); + special_proj = TRUE; + } + break; +#ifdef MANY_CYBERSPACE_WEAPONS + case DISCSLOW_TRIPLE: { + char soft_lvl = objPhysicss[objs[collision].specID].bullet_triple; + simple_damage_object(victim, disc_damage[soft_lvl - 1], CYBER_PROJECTILE_TYPE, 0); + special_proj = TRUE; + } break; + case SPEWSLOW_TRIPLE: { + char soft_lvl = objPhysicss[objs[collision].specID].bullet_triple; + simple_damage_object(victim, cyberspew_damage[soft_lvl - 1], CYBER_PROJECTILE_TYPE, 0); + special_proj = TRUE; + } +#else + case SPEWSLOW_TRIPLE: + case DISCSLOW_TRIPLE: + special_proj = TRUE; + break; +#endif + break; + } + + if (!special_proj) + slow_proj_hit(collision, victim); + } + + // Why are we destroying "victim" if it's a slow projectile?!?! + // cause the other one can't damage this one - because it'll be destroyed. + // only one call allowed + + // this big if here - says that if both objects are physics objects + // do the thing the flags say! (what to destroy) + if ((objs[victim].obclass == CLASS_PHYSICS) && (objs[victim].subclass == PHYSICS_SUBCLASS_SLOW)) { + if (!(PhysicsProps[CPNUM(victim)].flags & PROJ_PRESERVE_PROJ_HIT)) + ADD_DESTROYED_OBJECT(victim); + + if (!(PhysicsProps[CPNUM(collision)].flags & PROJ_PRESERVE_PROJ_HIT)) + ADD_DESTROYED_OBJECT(collision); + } else if (!(PhysicsProps[CPNUM(collision)].flags & PROJ_PRESERVE_HIT)) + ADD_DESTROYED_OBJECT(collision); + + if (damage_sound_fx != -1) { + play_digi_fx_obj(damage_sound_fx, 1, damage_sound_id); + } + damage_sound_fx = -1; + } else if ((ID2TRIP(collision) == ENERGY_MINE_TRIPLE) && (victim == PLAYER_OBJ)) { + player_struct.energy = lg_max(0, player_struct.energy - ENERGY_MINE_DRAIN); + if (!digi_fx_playing(SFX_ENERGY_DRAIN, NULL)) + play_digi_fx(SFX_ENERGY_DRAIN, 1); + chg_set_flg(VITALS_UPDATE); + } + return (OK); +} + +void terrain_object_collide(physics_handle src, ObjID target) { + ObjID hit_obj = physics_handle_to_id(src); + if (is_obj_destroyed(hit_obj) || is_obj_destroyed(target)) + return; + collide_objects(target, hit_obj, 0); +} + +void cit_collision_callback(physics_handle C, physics_handle V, int32_t bad, int32_t DATA1, int32_t DATA2, fix location[3]) { + ObjID collision; + ObjID victim; + + collision = physics_handle_to_id(C); + if (is_obj_destroyed(collision)) + return; + victim = physics_handle_to_id(V); + if (is_obj_destroyed(victim)) + return; + collide_objects(collision, victim, bad); +} + +void cit_awol_callback(physics_handle caller) { + State s; + + if (caller != -1) + get_phys_state(caller, &s, OBJ_NULL); + else { + return; + } + if ((fix_int(s.X) > global_fullmap->x_size) || (s.X < 0) || (fix_int(s.Y) > global_fullmap->y_size) || (s.Y < 0)) { + if (caller != -1) { + ADD_DESTROYED_OBJECT(physics_handle_to_id(caller)); + } + } +} + +void cit_sleeper_callback(physics_handle caller) { + ObjID id; + id = physics_handle_to_id(caller); + + // Is this a kind of thing that wants to maintain it's physics-ness? + // Live grenades should do this... + if (ObjProps[OPNUM(id)].flags & EDMS_PRESERVE) { + // Do put-to-sleep code here. + } else { + if (ID2TRIP(id) == L_MINE_TRIPLE) { + if (objGrenades[objs[id].specID].flags & GREN_ACTIVE_FLAG) { + objGrenades[objs[id].specID].flags |= GREN_MINE_STILL; + EDMS_obey_collisions(caller); + return; // let's not put it to sleep + } + } + add_edms_delete(caller); + } +} + +void cit_autodestruct_callback(physics_handle h) {} + +uchar robot_antisocial = FALSE; + +// Build the model given a state and object ID, and assign appropriate +// data into the object and do appropriate bookkeeping +errtype assemble_physics_object(ObjID id, State *pnew_state) { + switch (ObjProps[OPNUM(id)].physics_model) { + case EDMS_ROBOT: { + Obj *pObj = &objs[id]; + + Robot new_robot; + instantiate_robot(ID2TRIP(id), &new_robot); + pObj->info.ph = EDMS_make_robot(&new_robot, pnew_state); + if (CHECK_OBJ_PH(id)) { + if (robot_antisocial) + EDMS_make_robot_antisocial(pObj->info.ph); + +#ifdef SECRET_NON_COLLISION_BITS + if ((global_fullmap->cyber) && (pObj->obclass == CLASS_PHYSICS) && + (pObj->subclass == PHYSICS_SUBCLASS_SLOW)) + set_secret_non_collision_bit(pObj->info.ph); +#endif + physics_handle_id[pObj->info.ph] = id; + } + break; + } + + case EDMS_PELVIS: { + Pelvis new_pelvis; + instantiate_pelvis(ID2TRIP(id), &new_pelvis); + objs[id].info.ph = EDMS_make_pelvis(&new_pelvis, pnew_state); + physics_handle_id[objs[id].info.ph] = id; + break; + } + case EDMS_DIRAC: { + Dirac_frame new_dirac; + instantiate_dirac(ID2TRIP(id), &new_dirac); + objs[id].info.ph = EDMS_make_Dirac_frame(&new_dirac, pnew_state); + physics_handle_id[objs[id].info.ph] = id; + break; + } + } + if ((CHECK_OBJ_PH(id)) && (objs[id].info.ph > physics_handle_max)) + physics_handle_max = objs[id].info.ph; + return (OK); +} + +// ====================================================================== +// MODEL STUFF + +// ------------------------------------------- +// instantiate_robot() fills in the fields of a robot +// structure from object properties specified by triple, +// and level properties. + +void instantiate_robot(int triple, Robot *new_robot) { + int newmass, newsize; + short hard, pep; + + *new_robot = standard_robot; + switch (level_gamedata.gravity) { + case LEVEL_GRAV_LOW: + new_robot->gravity = fix_div(standard_robot.gravity, fix_make(3, 0)); + break; + case LEVEL_GRAV_ZERO: + new_robot->gravity = 0; + break; + default: + if (global_fullmap->cyber || + (((TRIP2CL(triple) == CLASS_CRITTER) && (CritterProps[CPTRIP(triple)].flags & AI_FLAG_FLYING)) || + (triple == ENERGY_MINE_TRIPLE))) + new_robot->gravity = 0; + break; + } + newmass = ObjProps[OPTRIP(triple)].mass; + if (newmass > 0) + new_robot->mass = fix_make(newmass, 0) / (PHYS_MASS_UNIT * PHYS_MASS_C_NUM / PHYS_MASS_C_DEN); + newsize = ObjProps[OPTRIP(triple)].physics_xr; + if (newsize > 0) + new_robot->size = fix_make(newsize, 0) / PHYSICS_RADIUS_UNIT; + hard = ObjProps[OPTRIP(triple)].hardness; + if (hard > 0) + new_robot->hardness = fix_make(hard, 0) / PHYS_HARDNESS_UNIT; + pep = ObjProps[OPTRIP(triple)].pep; + if (pep > 0) + new_robot->pep = fix_make(pep, 0) / PHYS_PEP_UNIT; + // new_robot->cyber_space = global_fullmap->cyber ? 2 : 0; + if (new_robot->gravity) + new_robot->cyber_space = 0; + else + new_robot->cyber_space = 1; +} + +// ------------------------------------------- +// instantiate_pelvis() fills in the fields of a pelvis +// structure from object properties specified by triple, +// and level properties. + +void instantiate_pelvis(int triple, Pelvis *new_pelvis) { + int newmass, newsize; + short hard, pep; + + *new_pelvis = standard_pelvis; + switch (level_gamedata.gravity) { + case LEVEL_GRAV_LOW: + new_pelvis->gravity = fix_div(standard_pelvis.gravity, fix_make(3, 0)); + break; + case LEVEL_GRAV_ZERO: + new_pelvis->gravity = 0; + break; + default: + if (global_fullmap->cyber) + new_pelvis->gravity = 0; + break; + } + + newmass = ObjProps[OPTRIP(triple)].mass; + if (newmass > 0) + new_pelvis->mass = fix_make(newmass, 0) / (PHYS_MASS_UNIT * PHYS_MASS_C_NUM / PHYS_MASS_C_DEN); + newsize = ObjProps[OPTRIP(triple)].physics_xr; + if (newsize > 0) + new_pelvis->size = fix_make(newsize, 0) / PHYSICS_RADIUS_UNIT; + hard = ObjProps[OPTRIP(triple)].hardness; + if (hard > 0) + new_pelvis->hardness = fix_make(hard, 0) / PHYS_HARDNESS_UNIT; + pep = ObjProps[OPTRIP(triple)].pep; + if (pep > 0) + new_pelvis->pep = fix_make(pep, 0) / PHYS_PEP_UNIT; + if (global_fullmap->cyber) + new_pelvis->cyber_space = PELVIS_MODE_CYBER; + else + new_pelvis->cyber_space = (motionware_mode == MOTION_SKATES) ? PELVIS_MODE_SKATES : PELVIS_MODE_NORMAL; +} + +// ------------------------------------------- +// instantiate_dirac() fills in the fields of a Dirac_frame +// structure from object properties specified by triple, +// and level properties. + +void instantiate_dirac(int triple, Dirac_frame *new_dirac) { + int newmass; + short hard, rough; + + *new_dirac = standard_dirac; + switch (level_gamedata.gravity) { + case LEVEL_GRAV_LOW: + new_dirac->gravity = fix_div(standard_dirac.gravity, fix_make(3, 0)); + break; + case LEVEL_GRAV_ZERO: + WARN("%s: Zero gravity level!", __FUNCTION__); + new_dirac->gravity = 0; + break; + default: + // if (global_fullmap->cyber) + // new_dirac->gravity = 0; + break; + } + newmass = ObjProps[OPTRIP(triple)].mass; + if (newmass > 0) + new_dirac->mass = fix_make(newmass, 0) / (PHYS_MASS_UNIT * PHYS_MASS_C_NUM / PHYS_MASS_C_DEN); + hard = ObjProps[OPTRIP(triple)].hardness; + if (hard > 0) + new_dirac->hardness = fix_make(hard, 0) / PHYS_HARDNESS_UNIT; + rough = ObjProps[OPTRIP(triple)].pep; + if (rough > 0) + new_dirac->roughness = fix_make(rough, 0) / PHYS_ROUGHNESS_UNIT; + + // Whut the heck do we do for the corners of a Dirac_frame? Beyond my feeble ken, I fear. + // DIRAC_FIX +} diff --git a/engine/src/GameSrc/player.c b/engine/src/GameSrc/player.c new file mode 100644 index 0000000..80ec5e3 --- /dev/null +++ b/engine/src/GameSrc/player.c @@ -0,0 +1,261 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/player.c $ + * $Revision: 1.153 $ + * $Author: tjs $ + * $Date: 1994/11/21 21:40:54 $ + */ + +#include + +#include "objwpn.h" +#include "player.h" +#include "diffq.h" // for time limit +#include "hud.h" +#include "leanmetr.h" +#include "wares.h" +#include "objsim.h" +#include "otrip.h" +#include "faketime.h" +#include "cybrnd.h" +#include "cyber.h" +#include "emailbit.h" + +#include "miscqvar.h" + +#define CFG_FATIGUE_VAR "fatigue" + +Player player_struct; +Obj *player_dos_obj; + +// couldnt we atob this and have a cfg file??? +// then we could read it in, turn them on, free the memory, and not have to recompile +// for instance +// void setup_qbits(char *fn) +// { FILE *fp=fopen(fn,"rb"); if (fp!=NULL) { while(!feof(fp)) { fread(&cow,1,2,fp); qbit[cow]=1; } close(fp); } } + +// yeah yeah, goddamn, gotta fix this.....someday....soon....i promise....really +short turnon_questbits[] = { + 0x1, 0x2, 0x3, 0x10, 0x12, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x20, 0x21, 0x24, 0x25, + 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, + 0xcd, 0xce, 0xcf, 0xe1, 0xe3, 0xe5, 0xe7, 0xe9, 0xeb, 0xed, 0xef, 0xf1, 0xf3, 0xf5, 0xf7, + 0xf9, 0xfb, 0xfd, 0xff, 0x101, 0x103, 0x105, 0x107, 0x109, 0x10b, 0x10d, 0x10f, 0x111, 0x113, 0x115, + 0x117, 0x119, 0x11b, 0x11d, 0x11f, 0x121, 0x123, 0x125, 0x127, 0x129, 0x12b, +}; + +#define NUM_INIT_QV 3 +ushort init_questvars[NUM_INIT_QV][2] = { + {0x3, 2}, // engine state + {0xC, 3}, // num groves + {0x33, 256}, // joystick sensitivity +}; + +#define NUM_ON_QUESTBITS (sizeof(turnon_questbits) / sizeof(short)) + +// ------------------------------------------------------------- +// init_player() +// +// ------------------------------------------------------------- + +#define REACTOR_COMBO_QVAR (0x1F) + +errtype init_player(Player *pplr) { + int i, j; + char tmp[sizeof(pplr->name)]; + long tmpdiff; + extern int _fr_global_detail; + extern uchar which_lang; + + LG_memcpy(tmp, pplr->name, sizeof(tmp)); // Save these so they won't be cleared. + tmpdiff = *(long *)pplr->difficulty; + + // Zero out whole structure + LG_memset(pplr, 0, sizeof(Player)); + + LG_memcpy(pplr->name, tmp, sizeof(pplr->name)); // Now restore them. + memmove(pplr->difficulty, &tmpdiff, 4); + + // Set appropriate non-zero things. + pplr->detail_level = _fr_global_detail; + pplr->level = 1; + for (i = 0; i < NUM_LEVELS; i++) + pplr->initial_shodan_vals[i] = -1; + pplr->rep = OBJ_NULL; + pplr->curr_target = OBJ_SPEC_NULL; + for (i = 0; i < NUM_GENERAL_SLOTS; i++) + pplr->inventory[i] = OBJ_NULL; + pplr->hit_points = PLAYER_MAX_HP * 5 / 6; + pplr->cspace_hp = PLAYER_MAX_HP; + pplr->cspace_time_base = BASE_CSPACE_TIME; + pplr->hit_points_regen = 0; + LG_memset(pplr->hit_points_lost, 0, NUM_DAMAGE_TYPES * sizeof(pplr->hit_points_lost[0])); + pplr->accuracy = MAX_ACCURACY; + pplr->energy = MAX_ENERGY; + pplr->shield_absorb_rate = 0; + pplr->last_fire = 0; + + // Here is where we set the questbits for doors that start locked, + // things that start frobbed or what not + // To set your very own questbit, just add a line of the form: + + // QUESTBIT_ON(number); + + for (i = 0; i < NUM_ON_QUESTBITS; i++) + QUESTBIT_ON(turnon_questbits[i]); + + for (i = 0; i < NUM_INIT_QV; i++) + QUESTVAR_SET(init_questvars[i][0], init_questvars[i][1]); + + while (QUESTVAR_GET(REACTOR_COMBO_QVAR) == QUESTVAR_GET(REACTOR_COMBO_QVAR + 1)) { + // randomize reactor combination. use effect_rnd 'cause why not. + j = (RndRange(&effect_rnd, 0, 9) << 8) | (RndRange(&effect_rnd, 0, 9) << 4) | RndRange(&effect_rnd, 0, 9); + QUESTVAR_SET(REACTOR_COMBO_QVAR, j); + j = (RndRange(&effect_rnd, 0, 9) << 8) | (RndRange(&effect_rnd, 0, 9) << 4) | RndRange(&effect_rnd, 0, 9); + QUESTVAR_SET(REACTOR_COMBO_QVAR + 1, j); + } + QUESTVAR_SET(LANGUAGE_QVAR, which_lang); + + pplr->fatigue_regen = 0; + pplr->fatigue_regen_base = 100; + pplr->fatigue_regen_max = 400; + + // Initialize MFD dynamic variables + + for (i = 0; i < NUM_MFDS; i++) + pplr->mfd_save_slot[i] = -1; + + for (i = 0; i < NUM_MFDS; i++) { + pplr->mfd_empty_funcs[i] = MFD_EMPTY; + + for (j = 0; j < MFD_NUM_VIRTUAL_SLOTS; j++) + pplr->mfd_virtual_slots[i][j] = j; + } + for (i = 0; i < MFD_NUM_REAL_SLOTS; i++) + pplr->mfd_all_slots[i] = i; + + for (i = 0; i < NUM_WEAPON_SLOTS; i++) + pplr->weapons[i].type = EMPTY_WEAPON_SLOT; + + for (i = 0; i < NUM_GRENADEZ; i++) + pplr->grenades_time_setting[i] = 70; + + pplr->hardwarez[CPTRIP(FULLSCR_HARD_TRIPLE)] = 1; + pplr->email[26] = EMAIL_GOT; + pplr->active_bio_tracks = 0xFF; + pplr->actives[ACTIVE_EMAIL] = 0xFF; + + // init physics stuff + player_reset_eye(); + + return OK; +} + + /* + // --------------------------------------------------------- + // player_dead() + // + // called if game is over and you don't run a cutscene!!! + // + + #define FADE_DOWN_TIME 300 + void player_dead(void) + { + extern void mouse_unconstrain(void); + + // ulong dead_time = *tmd_ticks; + // extern void object_data_flush(void); + + // palfx_fade_down(); + // while (*tmd_ticks < (dead_time+FADE_DOWN_TIME)) ; + // gr_clear(0); + + #ifdef AUDIOLOGS + { + extern char secret_pending_hack; + secret_pending_hack = 0; + } + #endif + + mouse_unconstrain(); + + change_mode_func(0,0,SETUP_LOOP); + + // palfx_fade_up(FALSE); + } + + errtype player_tele_to(int x, int y) + { + ObjLoc newloc; + newloc = objs[PLAYER_OBJ].loc; + newloc.x = (x << 8) + 0x80; + newloc.y = (y << 8) + 0x80; + return(OK); + } + + */ + +#define INITIAL_PLAYER_X 0x1E00 +#define INITIAL_PLAYER_Y 0x1620 + +errtype player_create_initial() { + ObjLoc plr_loc; + extern Pelvis standard_pelvis; + + plr_loc.x = (INITIAL_PLAYER_X) + 0x80; + plr_loc.y = (INITIAL_PLAYER_Y) + 0x80; + plr_loc.h = 192; + plr_loc.z = obj_height_from_fix((standard_pelvis.height >> 1) + + fix_from_map_height(me_height_flr(MAP_GET_XY(plr_loc.x >> 8, plr_loc.y >> 8)))); + plr_loc.p = 0; + plr_loc.b = 0; + // of course z is about to be overwritten, but hey... + player_struct.edms_state[0] = 0; + obj_create_player(&plr_loc); + return (OK); +} + +#define CFG_PLAYER_VAR "eye" +errtype player_startup(void) { + return OK; + // return player_create_initial(); +} + +errtype player_shutdown(void) { return OK; } + +ubyte set_player_energy_spend(ubyte new_val) { + if (player_struct.energy_spend != new_val) { + hud_set_time(HUD_ENERGYUSE, 3 * CIT_CYCLE); + player_struct.energy_spend = new_val; + } + return (player_struct.energy_spend); +} + +//---------------------------------------------------------------- +// KLC - Probably a goofy way to do this, but what the hey! This tells me if the player struct +// has the fullscreen ware on. +//---------------------------------------------------------------- +bool IsFullscreenWareOn(void) { + ubyte status = player_struct.hardwarez_status[CPTRIP(FULLSCR_HARD_TRIPLE)]; + + return ((bool)(status & WARE_ON)); +} diff --git a/engine/src/GameSrc/plotware.c b/engine/src/GameSrc/plotware.c new file mode 100644 index 0000000..547310d --- /dev/null +++ b/engine/src/GameSrc/plotware.c @@ -0,0 +1,355 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/plotware.c $ + * $Revision: 1.19 $ + * $Author: xemu $ + * $Date: 1994/11/09 16:27:33 $ + * + * + */ + +#include +#include + +#include "mfdint.h" +#include "mfdext.h" +#include "mfdfunc.h" +#include "mfddims.h" +#include "objsim.h" +#include "gamestrn.h" +#include "tools.h" +#include "mfdgadg.h" +#include "wares.h" +#include "gr2ss.h" + +#include "mfdart.h" +#include "gamescr.h" +#include "otrip.h" +#include "cybstrng.h" +#include "plotware.h" +#include "shodan.h" +#include "trigger.h" + +// ============================================================ +// DA PLOTWARE +// ============================================================ + +// ------- +// DEFINES +// ------- + +#define MFD_PLOTWARE_FUNC 16 +#define ITEM_COLOR 0x5A +#define TITLE_COLOR 0x35 + +#define NULL_PAGE 0xFF +#define PLOTWARE_VERSION (player_struct.hardwarez[CPTRIP(STATUS_HARD_TRIPLE)]) +#define NUM_PAGES 3 + +typedef struct _plot_display { + ubyte page; // Page number of this display. + int name; // string id of name. + ubyte color; // color to display in + int baseval; // base string id for val, zero means display as int + short questvar; // Quest variable +} plot_display; + +#define INT_TYPE 0 +#define COUNTDOWN_TYPE 1 +#define HACK_TYPE 2 +#define PLAYER_FIELD(fld) (char *)&(((Player *)(0))->fld) + +#define MAIN_PROGRAM_QDATA 0x1009 + +// ---------- +// PROTOTYPES +// ---------- +void fill_time(short val, char *vbuf); +bool do_plotware_hack(int hack_num, char *vbuf); +void plotware_showpage(uchar page); +uchar plotware_button_handler(MFD *m, LGPoint bttn, uiEvent *ev, void *data); + +// -------- +// GLOBALS +// -------- +plot_display PlotDisplays[] = { + {0, REF_STR_pwShodometer, ITEM_COLOR, HACK_TYPE, 2}, + {0, REF_STR_pwLaser, ITEM_COLOR, REF_STR_pwLaser0, 0x2008}, + {0, REF_STR_pwLifePods, ITEM_COLOR, REF_STR_pwEnabled, 0x2014}, + {0, REF_STR_pwShield, ITEM_COLOR, REF_STR_pwShield0, 0x2006}, + {0, REF_STR_pwCooling, ITEM_COLOR, REF_STR_pwCooling0, 0x2014}, + {0, REF_STR_pwDestructTime, ITEM_COLOR, HACK_TYPE, 1}, + + {1, REF_STR_pwNodes, ITEM_COLOR, HACK_TYPE, 6}, + // {1, REF_STR_pwMulti, ITEM_COLOR, INT_TYPE, 0x1000}, + // {1, REF_STR_pwCPUcool, ITEM_COLOR, REF_STR_pwCPUcool0, 0x2010}, + {1, REF_STR_pwMainProgram, ITEM_COLOR, REF_STR_pwNull, 0}, + {1, REF_STR_Null, ITEM_COLOR, REF_STR_pwMainProgram0, MAIN_PROGRAM_QDATA}, + // {1, REF_STR_pwDownLoadTime, ITEM_COLOR, HACK_TYPE, 0 }, + // {1, REF_STR_pwBridgeTime, ITEM_COLOR, HACK_TYPE, 4 }, + // {1, REF_STR_pwVirusTime, ITEM_COLOR, HACK_TYPE, 5 }, + {1, REF_STR_pwComm, ITEM_COLOR, REF_STR_pwComm0, 0x1002}, + + {2, REF_STR_pwGroveStatus, TITLE_COLOR, REF_STR_pwNull, 0}, + {2, REF_STR_pwAlpha, ITEM_COLOR, HACK_TYPE, 7}, + {2, REF_STR_pwBeta, ITEM_COLOR, HACK_TYPE, 8}, + {2, REF_STR_pwGamma, ITEM_COLOR, REF_STR_pwGamma0, 0}, + {2, REF_STR_pwDelta, ITEM_COLOR, HACK_TYPE, 9}, + + {NULL_PAGE} +}; + +void fill_time(short val, char *vbuf) { + if (val != 0) { + int16_t hours = val / 3600; + int16_t minutes = (val - (3600 * hours)) / 60; + int16_t seconds = (val - (3600 * hours) - minutes * 60); + sprintf(vbuf, "%d:%02d:%02d", hours, minutes, seconds); + } else { + strcpy(vbuf, "-:--:--"); + } +} + +#define DOWNLOAD_TIME 100 +#define DOWNLOAD_PROGNUM 2 +#define VIRUS_PROGNUM 1 +#define BRIDGE_PROGNUM 4 + +#define REACTOR_QDATA 0x1002 +#define REACTOR_DESTRUCT 1 + +#define SHODOMETER_BASE 0x1010 +#define SHODOMETER_LEVELS MAX_SHODOMETER_LEVEL + 1 + +#define NODES_QDATA 0x1001 +#define TOTAL_NODES 27 + +bool do_plotware_hack(int hack_num, char *vbuf) { + switch (hack_num) { + case 0: + if (qdata_get(MAIN_PROGRAM_QDATA) != DOWNLOAD_PROGNUM) + return false; + sprintf(vbuf, "%d%%", player_struct.time2comp * 100 / DOWNLOAD_TIME); + return true; + case 1: + if (qdata_get(REACTOR_QDATA) != REACTOR_DESTRUCT) + return false; + fill_time(player_struct.time2comp, vbuf); + return true; + case 2: + if (player_struct.level >= SHODOMETER_LEVELS) + return false; + else { + sprintf(vbuf, "%d%%", + QUESTVAR_GET(0x10 + player_struct.level) * 100 / player_struct.initial_shodan_vals[player_struct.level]); + return true; + } + // There is nooooooooooooooooooo case 3. + // Mostly because we haven't necessarily loaded the other levels to figure + // out what their shodometer levels were. + case 4: + if (qdata_get(MAIN_PROGRAM_QDATA) != BRIDGE_PROGNUM) + return false; + fill_time(player_struct.time2comp, vbuf); + return true; + case 5: + if (qdata_get(MAIN_PROGRAM_QDATA) != VIRUS_PROGNUM) + return false; + fill_time(player_struct.time2comp, vbuf); + return true; + case 6: + sprintf(vbuf, "%d", TOTAL_NODES - qdata_get(NODES_QDATA)); + return true; + case 7: + if (QUESTBIT_GET(0x0B)) + get_string(REF_STR_pwAlpha0 + 1, vbuf, 9); + else + get_string(REF_STR_pwAlpha0, vbuf, 9); + return true; + case 8: + if (QUESTBIT_GET(0x0F)) + get_string(REF_STR_pwBeta0 + 2, vbuf, 9); + else if (QUESTBIT_GET(0x0C)) + get_string(REF_STR_pwBeta0 + 1, vbuf, 9); + else + get_string(REF_STR_pwBeta0, vbuf, 9); + return true; + case 9: + if (QUESTBIT_GET(0x0A)) + get_string(REF_STR_pwDelta0 + 1, vbuf, 9); + else + get_string(REF_STR_pwDelta0, vbuf, 9); + return true; + + default: + *vbuf = '\0'; + return false; + } +} + +// --------------- +// EXPOSE FUNCTION +// --------------- + +#define PLOTWARE_MFD_FUNC 16 + +#define DISPLAY_TOP_MARGIN 10 +#define LEFT_X 2 +#define RIGHT_X (MFD_VIEW_WID - 2) +#define PLOTWARE_PAGENUM (player_struct.mfd_func_data[PLOTWARE_MFD_FUNC][0]) + +#define BUTTON_Y (MFD_VIEW_HGT - res_bm_height(REF_IMG_PrevPage) - 2) + +void mfd_plotware_expose(MFD *mfd, ubyte control) { + uchar full = control & MFD_EXPOSE_FULL; + if (control == 0) // MFD is drawing stuff + { + // Do unexpose stuff here. + } + if (control & MFD_EXPOSE) // Time to draw stuff + { + short y = DISPLAY_TOP_MARGIN; + int i; + // clear update rects + mfd_clear_rects(); + // set up canvas + gr_push_canvas(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + mfd_item_micro_expose(true, STATUS_HARD_TRIPLE); + // mfd_item_micro_hires_expose(true,STATUS_HARD_TRIPLE); + if (!full) + mfd_clear_rects(); + + // INSERT GRAPHICS CODE HERE + gr_set_font(ResLock(MFD_FONT)); + for (i = 0; PlotDisplays[i].page < NUM_PAGES; i++) { + char buf[40], vbuf[40]; + short val; + short w, h; + plot_display *dp = &PlotDisplays[i]; + if (dp->page != PLOTWARE_PAGENUM) + continue; + switch (dp->baseval) { + case INT_TYPE: + val = qdata_get(dp->questvar); + sprintf(vbuf, "%d", val); + break; + case COUNTDOWN_TYPE: + val = *(short *)(((char *)&player_struct) + dp->questvar); + fill_time(val, vbuf); + break; + case HACK_TYPE: + if (!do_plotware_hack(dp->questvar, vbuf)) + continue; + break; + default: + val = qdata_get(dp->questvar); + if (dp->questvar & 0x2000) + if (val) + val = 1; + get_string(dp->baseval + val, vbuf, sizeof(vbuf)); + break; + } + get_string(dp->name, buf, sizeof(buf)); + mfd_full_draw_string(buf, LEFT_X, y, dp->color, MFD_FONT, true, true); + gr_string_size(vbuf, &w, &h); + mfd_full_draw_string(vbuf, RIGHT_X - w, y, dp->color, MFD_FONT, true, true); + y += h + 1; + } + if (full) { + char buf[50]; + short w, h; + // Draw the page number + get_string(REF_STR_pwPage0 + PLOTWARE_PAGENUM, buf, sizeof(buf)); + gr_string_size(buf, &w, &h); + mfd_draw_string(buf, (MFD_VIEW_WID - w) / 2, BUTTON_Y + (res_bm_height(REF_IMG_NextPage) - h) / 2, + ITEM_COLOR, true); + // Draw the page buttons + draw_raw_resource_bm(REF_IMG_PrevPage, LEFT_X, BUTTON_Y); + draw_raw_resource_bm(REF_IMG_NextPage, RIGHT_X - res_bm_width(REF_IMG_NextPage), BUTTON_Y); + } + + ResUnlock(MFD_FONT); + // on a full expose, make sure to draw everything + + if (full) + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + // Pop the canvas + gr_pop_canvas(); + // Now that we've popped the canvas, we can send the + // updated mfd to screen + mfd_update_rects(mfd); + } +} + +void plotware_showpage(uchar page) { + if (PLOTWARE_VERSION == 0 || page >= NUM_PAGES) + return; + PLOTWARE_PAGENUM = page; + plotware_turnon(true, true); +} + +// -------------- +// BUTTON HANDLER +// -------------- +uchar plotware_button_handler(MFD *n, LGPoint bttn, uiEvent *ev, void *data) { + if (!(ev->subtype & MOUSE_LDOWN)) + return true; + if (bttn.x == 0) + PLOTWARE_PAGENUM = (PLOTWARE_PAGENUM == 0) ? NUM_PAGES - 1 : PLOTWARE_PAGENUM - 1; + if (bttn.x == 1) + PLOTWARE_PAGENUM = (PLOTWARE_PAGENUM >= NUM_PAGES - 1) ? 0 : PLOTWARE_PAGENUM + 1; + mfd_notify_func(MFD_PLOTWARE_FUNC, MFD_ITEM_SLOT, false, MFD_ACTIVE, true); + return true; +} + +// -------------- +// INITIALIZATION +// -------------- +errtype mfd_plotware_init(MFD_Func *f) { + int cnt = 0; + LGPoint bsize; + LGPoint bdims; + LGRect r; + errtype err; + bsize.x = res_bm_width(REF_IMG_PrevPage); + bsize.y = res_bm_height(REF_IMG_NextPage); + bdims.x = 2; + bdims.y = 1; + r.ul.x = LEFT_X; + r.ul.y = BUTTON_Y; + r.lr.x = RIGHT_X; + r.lr.y = r.ul.y + bsize.y; + err = MFDBttnArrayInit(&f->handlers[cnt++], &r, bdims, bsize, plotware_button_handler, NULL); + if (err != OK) + return err; + f->handler_count = cnt; + return OK; +} + +void plotware_turnon(uchar visible, uchar realstart) { + if (visible) { + set_inventory_mfd(MFD_INV_HARDWARE, CPTRIP(STATUS_HARD_TRIPLE), true); + mfd_change_slot(mfd_grab_func(MFD_PLOTWARE_FUNC, MFD_ITEM_SLOT), MFD_ITEM_SLOT); + } + player_struct.hardwarez_status[CPTRIP(STATUS_HARD_TRIPLE)] &= ~(WARE_ON); +} diff --git a/engine/src/GameSrc/popups.c b/engine/src/GameSrc/popups.c new file mode 100644 index 0000000..76650df --- /dev/null +++ b/engine/src/GameSrc/popups.c @@ -0,0 +1,186 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/popups.c $ + * $Revision: 1.9 $ + * $Author: xemu $ + * $Date: 1994/10/31 06:31:46 $ + * + */ + +#include + +#include "popups.h" +#include "citres.h" +#include "criterr.h" +#include "gamestrn.h" + +#include "gamescr.h" +#include "cybstrng.h" +#include "colors.h" +#include "cit2d.h" +#include "gr2ss.h" + +#include "curdat.h" + +#define BUF_SIZ 80 +#define EMAIL_CURS_WID 40 +#define EMAIL_CURS_MARG 5 +#define EMAIL_CURS_FONT RES_tinyTechFont + +uchar popup_cursors = TRUE; +grs_bitmap popup_bitmaps[NUM_POPUPS]; +LGRect popup_rects[NUM_POPUPS]; +LGPoint popup_hotspots[NUM_POPUPS] = { + // in 0-16 dimension-independent units + {0, 8}, {16, 8}, {8, 16}, {0, 8}, {16, 8}, +}; + +#define CURSOR_TEXT_COLOR 0x36 + +void init_popups(void) { + for (int i = 0; i < NUM_POPUPS; i++) { + Ref id = MKREF(RES_popups, i); + FrameDesc *f = RefGet(id); + popup_rects[i] = f->anchorArea; + if (load_res_bitmap(&popup_bitmaps[i], id, TRUE) != OK) + critical_error(CRITERR_RES | 0xA); + } + ResUnlock(RES_popups); + ResDrop(RES_popups); +} + +void make_popup_cursor(LGCursor *c, grs_bitmap *bm, char *s, uint tmplt, uchar allocate, LGPoint offset) { + LGRect *r = &popup_rects[tmplt]; + grs_bitmap *pbm = &popup_bitmaps[tmplt]; + grs_canvas gc; + short x, y, w, h; + LGPoint p, ph; + uchar old_over = gr2ss_override; + uchar *bptr; + uchar *bits = bm->bits; + + MouseLock++; + *bm = *pbm; + + // CC - Convert this to be the right size for the screen mode + int sw, sh; + sw = SCONV_X(bm->w); + sh = SCONV_Y(bm->h); + + if (allocate) { + bptr = (uchar *)malloc(sw * sh * 2); + if (bptr == NULL) + critical_error(CRITERR_MEM | 4); + } else { + bptr = bits; + } + gr_init_bm(bm, bptr, BMT_FLAT8, BMF_TRANS, sw, sh); + gr_make_canvas(bm, &gc); + gr_push_canvas(&gc); + + gr_clear(0); + ss_bitmap(pbm, 0, 0); + gr_set_font(ResLock(RES_tinyTechFont)); + gr_string_size(s, &w, &h); + // ss_point_convert(&w, &h, FALSE); + + x = (r->ul.x + r->lr.x - w) / 2 + offset.x + 1; + y = (r->ul.y + r->lr.y - h) / 2 + offset.y - 1; + + // ss_point_convert(&x, &y, TRUE); + gr_set_fcolor(CURSOR_TEXT_COLOR); + ss_string(s, x, y + 1); + ResUnlock(RES_tinyTechFont); + gr_pop_canvas(); + ph = popup_hotspots[tmplt]; + p.x = (bm->w * ph.x) >> 4; + p.y = (bm->h * ph.y) >> 4; + uiMakeBitmapCursor(c, bm, p); + + MouseLock--; +} + +void load_string_array(Ref first, char *arry[], char buf[], int bufsz, int n) { + int off = 0; + int i; + for (i = 0; i < n; i++) { + short sz = bufsz - off; + get_string(first + i, buf + off, sz); + arry[i] = buf + off; + off += strlen(buf + off) + 1; + } +} + +#ifdef SVGA_SUPPORT +static char cursor_buf[4096]; +#else +static char cursor_buf[512]; +#endif + +void make_email_cursor(LGCursor *c, grs_bitmap *bm, uchar page, bool init) { + grs_canvas gc; + short x, y, w, h; + int len; + LGPoint p; + char s[BUF_SIZ]; +#ifdef SVGA_SUPPORT + short temp; + uchar old_over = gr2ss_override; + gr2ss_override = OVERRIDE_ALL; + ss_set_hack_mode(2, &temp); +#endif + + gr_font_char_size(ResGet(EMAIL_CURS_FONT), 'X', &w, &h); + h += 2; + w = EMAIL_CURS_WID; +#ifdef SVGA_SUPPORT + ss_point_convert(&w, &h, FALSE); +#endif + if (init) + gr_init_bm(bm, NULL, BMT_FLAT8, BMF_TRANS, w, h); + sprintf(s, "%s %d", get_string(REF_STR_WordPage, NULL, BUF_SIZ), page); + MouseLock++; + if (sizeof(cursor_buf) < w * h) + critical_error(CRITERR_MEM | 7); + bm->bits = (uchar *)cursor_buf; + gr_make_canvas(bm, &gc); + gr_push_canvas(&gc); + gr_clear(0); + gr_set_font(ResLock(EMAIL_CURS_FONT)); + gr_string_size("Page goof", &w, &h); + x = EMAIL_CURS_MARG; + y = 1; + gr_set_fcolor(CURSOR_TEXT_COLOR); + draw_shadowed_string(s, x, y, TRUE); + ResUnlock(RES_tinyTechFont); + gr_set_fcolor(BLACK + 1); + ss_vline(1, h / 2 - 1, h / 2 + 1); + ss_hline(0, h / 2, 2); + ss_set_pixel(CURSOR_TEXT_COLOR, 1, h / 2); + gr_pop_canvas(); + p.x = 1; + p.y = h / 2; + uiMakeBitmapCursor(c, bm, p); + MouseLock--; +#ifdef SVGA_SUPPORT + ss_set_hack_mode(0, &temp); + gr2ss_override = old_over; +#endif +} diff --git a/engine/src/GameSrc/render.c b/engine/src/GameSrc/render.c new file mode 100644 index 0000000..dddd625 --- /dev/null +++ b/engine/src/GameSrc/render.c @@ -0,0 +1,353 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/render.c $ + * $Revision: 1.43 $ + * $Author: xemu $ + * $Date: 1994/10/27 04:55:24 $ + */ + +#include + +#include "gamerend.h" +#include "render.h" +#include "frprotox.h" +#include "faketime.h" +#include "game_screen.h" +#include "fullscrn.h" +#include "hudobj.h" +#include "cybmem.h" + +#include "frcamera.h" +#include "frflags.h" +#include "froslew.h" +#include "player.h" +#include "rcolors.h" +#include "tools.h" +#include "map.h" +#include "mapflags.h" +#include "view360.h" +#include "wares.h" +#include "gr2ss.h" + +frc *hack_cam_frcs[MAX_CAMERAS_VISIBLE]; +grs_canvas hack_cam_canvases[MAX_CAMERAS_VISIBLE], static_canvas; +grs_bitmap *hack_cam_bitmaps[MAX_CAMERAS_VISIBLE], *static_bitmap; +cams hack_cam; +char camera_map[NUM_HACK_CAMERAS]; +ObjID hack_frc_objs[MAX_CAMERAS_VISIBLE]; +ObjID hack_cam_objs[NUM_HACK_CAMERAS]; +ObjID hack_cam_surrogates[NUM_HACK_CAMERAS]; + +#define HACK_CAMERA_X 0 +#define HACK_CAMERA_Y 0 +#define HACK_CAMERA_WIDTH ((start_mem < BIG_HACKCAM_THRESHOLD) ? 64 : 128) +#define HACK_CAMERA_HEIGHT ((start_mem < BIG_HACKCAM_THRESHOLD) ? 64 : 128) +//#define HACK_CAMERA_WIDTH 128 +//#define HACK_CAMERA_HEIGHT 128 +//#define HACK_CAMERA_WIDTH 64 +//#define HACK_CAMERA_HEIGHT 64 + +#define STATIC_HEIGHT 32 +#define STATIC_WIDTH 32 + +#define FRAME_SKIP_MASK 0x03 +#define FRAME_PARITY_SHF 2 // skip every 4 frames when rendering screen images + +uchar fr_texture = TRUE; +uchar fr_txt_walls = TRUE; +uchar fr_txt_floors = TRUE; +uchar fr_txt_ceilings = TRUE; +uchar fr_lights_out = FALSE; +uchar fr_lighting = TRUE; +uchar fr_play_lighting = FALSE; +uchar fr_normal_lights = TRUE; +int fr_detail_value = 100; +int fr_drop[TM_SIZE_CNT] = {1, 4, 10}; +uchar fr_show_tilecursor = FALSE; +uchar fr_cont_tilecursor = FALSE; +uchar fr_show_all = TRUE; +int fr_qscale_obj = 2; +int fr_qscale_crit = 2; +uchar fr_highlights = FALSE; +int fr_normal_shf = 2; +int fr_lite_rad1 = 0, fr_lite_base1 = 10, fr_lite_rad2 = 7, fr_lite_base2 = 0; +fix fr_lite_slope = (-fix_make(2, 0) + fix_make(0, 0x5000)), fr_lite_yint = fix_make(12, 0x2000); +int fr_detail_master = 3; /* 0-3 master detail */ +int fr_pseudo_spheres = 0; + +uchar hack_cameras_needed = 0; +char curr_hack_cam = 0; +ulong hack_cam_fr_count = 0; +uchar screen_static_drawn = FALSE; + +// ----------- +// PROTOTYPES +//------------ +int hack_camera_draw_callback(grs_canvas *cvs, grs_bitmap *bm, int u1, int u2, int u3); +void update_cspace_tiles(void); + +int hack_camera_draw_callback(grs_canvas *cvs, grs_bitmap *bm, int u1, int u2, int u3) { + // gr_push_canvas(&hack_cam_canvases[curr_hack_cam]); + // gr_bitmap(bm,0,0); + // gr_pop_canvas(); + return (FALSE); +} + +typedef int (*frdraw)(void *dstc, void *dstbm, int x, int y, int flg); + +errtype init_hack_cameras() { + int i; + grs_canvas *tmp_cnv; + uchar *tmp_mem; + + static_bitmap = gr_alloc_bitmap(BMT_FLAT8, 0, STATIC_WIDTH, STATIC_HEIGHT); + gr_make_canvas(static_bitmap, &static_canvas); + gr_push_canvas(&static_canvas); + gr_clear(0); + gr_pop_canvas(); + + fr_camera_create(&hack_cam, CAMTYPE_OBJ, player_struct.rep, NULL, NULL); + for (i = 0; i < MAX_CAMERAS_VISIBLE; i++) { + // hack_cam_bitmaps[i] = gr_alloc_bitmap(BMT_FLAT8, 0, HACK_CAMERA_WIDTH, HACK_CAMERA_HEIGHT); + // gr_make_canvas(hack_cam_bitmaps[i], &hack_cam_canvases[i]); + // hack_cam_frcs[i] = fr_place_view(FR_NEWVIEW, &hack_cam, &hack_cam_canvases[i], + // hack_cam_frcs[i] = fr_place_view(FR_NEWVIEW, &hack_cam, &hack_cam_bitmaps[i], + + // Warning(("start_mem = %d, BIG_HACKCAM = %d!\n",start_mem,BIG_HACKCAM_THRESHOLD)); + // Warning(("HACK_CAMERA_WID = %d!\n",HACK_CAMERA_WIDTH)); + tmp_mem = (uchar *)malloc(HACK_CAMERA_WIDTH * HACK_CAMERA_HEIGHT); + hack_cam_frcs[i] = fr_place_view(FR_NEWVIEW, &hack_cam, tmp_mem, FR_DOUBLEB_MASK | FR_HACKCAM_FLAG, 0, 0, + HACK_CAMERA_X, HACK_CAMERA_Y, HACK_CAMERA_WIDTH, HACK_CAMERA_HEIGHT); + fr_set_callbacks(hack_cam_frcs[i], (frdraw)hack_camera_draw_callback, NULL, NULL); + tmp_cnv = (grs_canvas *)fr_get_canvas(hack_cam_frcs[i]); + hack_cam_bitmaps[i] = &tmp_cnv->bm; + + // gr_push_canvas(&hack_cam_canvases); + // gr_clear(0); + // gr_pop_canvas(); + } + + for (i = 0; i < NUM_HACK_CAMERAS; i++) { + camera_map[i] = 0; + hack_cam_objs[i] = OBJ_NULL; + hack_cam_surrogates[i] = OBJ_NULL; + } + + return (OK); +} + +errtype shutdown_hack_cameras() { + int i; + for (i = 0; i < MAX_CAMERAS_VISIBLE; i++) { + fr_free_view(hack_cam_frcs[i]); + free(hack_cam_bitmaps[i]->bits); + } + return (OK); +} + +errtype do_screen_static() { + if (!screen_static_drawn) + draw_full_static(static_bitmap, GRAY_8_BASE); + return (OK); +} + +errtype render_hack_cameras() { + int i, count; + + // efficiency good.... + if (hack_cameras_needed == 0) + return OK; + + hack_cam_fr_count++; + count = 0; + for (i = 0; i < NUM_HACK_CAMERAS; i++) { + if ((hack_cameras_needed & (1 << i)) && !((hack_cam_objs[i] == OBJ_NULL) || (count == MAX_CAMERAS_VISIBLE))) + camera_map[i] = ++count; + else + camera_map[i] = 0; + } + for (i = 0; i < NUM_HACK_CAMERAS; i++) { + if (camera_map[i] && (hack_frc_objs[camera_map[i] - 1] != hack_cam_objs[i] || + (((hack_cam_fr_count & FRAME_SKIP_MASK) == 0) && + (count == 1 || (((hack_cam_fr_count >> FRAME_PARITY_SHF) ^ camera_map[i]) & 1) == 0)))) { + curr_hack_cam = camera_map[i] - 1; + hack_frc_objs[curr_hack_cam] = hack_cam_objs[i]; + fr_camera_update(&hack_cam, (unsigned int)hack_cam_objs[i], 0, 0); + fr_rend(hack_cam_frcs[camera_map[i] - 1]); + } + } + curr_hack_cam = 0; + hack_cameras_needed = 0; + return (OK); +} + +// Okay, takeover and relinquish are what we do when you double +// click on a hack-camera-ed screen. Note that we always relinquish +// back to the player, if this is insufficient it's easy to store off +// some of the camera data. +uchar hack_takeover = 0; +int hack_eye; // the fierce pirate + +errtype hack_camera_takeover(int hack_cam) { + LGRect start = {{-5, -5}, {5, 5}}; + extern LGPoint use_cursor_pos; + LGPoint ucp; + extern bool DoubleSize; + extern LGRect mainview_rect; + extern LGRect fscrn_rect; + cams *cam = fr_camera_getdef(); + + // Turn off the 360 ware, if it is on + if (WareActive(player_struct.hardwarez_status[HARDWARE_360])) + use_ware(WARE_HARD, HARDWARE_360); + + // do a wacky zoom thing + ucp = use_cursor_pos; + if (!DoubleSize) + ss_point_convert(&(ucp.x), &(ucp.y), TRUE); + RECT_MOVE(&start, ucp); + zoom_rect(&start, (full_game_3d) ? &fscrn_rect : &mainview_rect); + + // level out the eye & store off old position + hack_eye = eye_mods[1]; + eye_mods[1] = 0; + + fr_camera_update(cam, (unsigned int)hack_cam_objs[hack_cam], 0, 0); + hack_takeover = hack_cam + 1; + return (OK); +} + +errtype hack_camera_relinquish() { + cams *cam = fr_camera_getdef(); + fr_camera_update(cam, (unsigned int)PLAYER_OBJ, 0, 0); + + // force refresh of this hack camera. + hack_frc_objs[camera_map[hack_takeover - 1] - 1] = OBJ_NULL; + camera_map[hack_takeover - 1] = 0; + + hack_takeover = 0; + eye_mods[1] = hack_eye; + return (OK); +} + +#define inc_area(tp) \ + if (((tp >= 0) && (tp < 64 * 64))) \ + (*(tmp_ptr + tp))++ +#define val_area(tp) (*(tmp_ptr + tp)) + +void update_cspace_tiles(void) { + int cur_tp, i, j, s, t, x; + uchar *tmp_ptr = (uchar *)big_buffer; + MapElem *mmp; + + LG_memset(tmp_ptr, 0, 64 * 64); + mmp = MAP_GET_XY(0, 0); + for (i = 0, cur_tp = 0; i < 64; i++) + for (j = 0; j < 64; j++, cur_tp++, mmp++) + if (me_bits_flip_x(mmp)) + for (s = -1; s <= 1; s++) + for (t = -1; t <= 1; t++) + if (s | t) { + x = cur_tp + (s * 64) + t; + inc_area(x); + } + mmp = MAP_GET_XY(0, 0); + for (i = 0, cur_tp = 0; i < 64; i++) + for (j = 0; j < 64; j++, cur_tp++, mmp++) + if (me_bits_flip(mmp)) { + if (me_bits_flip(mmp) == 1) + me_flip_set(mmp, 3); + else if (me_bits_flip(mmp) == 3) + me_flip_set(mmp, 2); + if (val_area(cur_tp) != 0) + if ((val_area(cur_tp) < 2) || (val_area(cur_tp) > 3)) + me_flip_set(mmp, 0); + } else if (val_area(cur_tp) == 3) + me_flip_set(mmp, 2); + +#ifdef STATE_RULES + switch (me_bits_flip(mmp)) { + case 2: + if ((val_area(cur_tp) < 2) || (val_area(cur_tp) > 3)) + me_flip_set(mmp, 0); + break; + case 1: + me_flip_set(mmp, 3); + break; + case 0: + if (val_area(cur_tp) == 3) + me_flip_set(mmp, 2); + break; + case 3: + if (((val_area(cur_tp) + i + j) & 0xf) < 4) + me_flip_set(mmp, 2); + } +#endif +} + +void tile_hit(int mx, int my) { + static int lx, ly; + static long last_time = 0; + int dsq = ((lx - mx) | (ly - my)); + MapElem *mp; + + if (dsq || ((last_time + (CIT_CYCLE >> 2) < *tmd_ticks))) { + last_time = *tmd_ticks; + mp = MAP_GET_XY(mx, my); + if (dsq) { + me_flip_set(mp, 1); + lx = mx; + ly = my; + } else if (me_bits_flip(mp)) + me_flip_set(mp, 0); + else + me_flip_set(mp, 1); + } +} + +#define LIFE_UPDATE_RATE (256 >> 2) + +errtype render_run(void) { + extern uchar view360_render_on; + static long last_cspace_update = 0; + +#ifdef POPUPS_ALLOWED + if (region_obscured(mainview_region, mainview_region->r) == UNOBSCURED) +#endif + { + // printf("render_run_start\n"); + screen_static_drawn = FALSE; + current_num_hudobjs = 0; // clear the hud objects + rendrect = mainview_region->r; + + // printf("fr_rend\n"); + fr_rend(NULL); + + if (view360_render_on) + view360_render(); + render_hack_cameras(); + if (global_fullmap->cyber) + if ((last_cspace_update + LIFE_UPDATE_RATE) < (*tmd_ticks)) { + last_cspace_update = (*tmd_ticks) & (~(LIFE_UPDATE_RATE - 1)); + update_cspace_tiles(); + } + } + return OK; +} diff --git a/engine/src/GameSrc/rendtool.c b/engine/src/GameSrc/rendtool.c new file mode 100644 index 0000000..16c1ccb --- /dev/null +++ b/engine/src/GameSrc/rendtool.c @@ -0,0 +1,567 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/rendtool.c $ + * $Revision: 1.37 $ + * $Author: buzzard $ + * $Date: 1994/11/25 09:16:45 $ + * + * + * support for render functions and tools, such as mouse and so on + */ + +#include "map.h" +#include "frintern.h" +#include "fullscrn.h" +#include "gamerend.h" +#include "textmaps.h" +#include "gettmaps.h" + +#include "frcamera.h" +#include "player.h" + +#include "objects.h" +#include "objprop.h" +#include "objbit.h" +#include "objsim.h" + +#include "modtext.h" +#include "citmat.h" + +// should probably wimp out and take frintern +// or have a separate fritrfce or something +// rather than externing everything a couple of 30 lines down + +#include "rendtool.h" +#include "frtypes.h" // so we get _fr, so we can get size + canvas and all +#include "frparams.h" +#include "frflags.h" +#include "tilemap.h" +#include "fr3d.h" +#include "frquad.h" + +#include "gamesort.h" +#include "citres.h" + +#include "star.h" + +#include "curdat.h" + +// pain, stupidity, yes +char model_vtext_data[] = { + 29, -1, + 21, 15, 16, -1, + 37, 15, 21, -1, + 38, 3, -1, + 39, -1, + 21, -1, + 21, -1, + 1, 2, 3, 4, -1, + 23, 21, 16, -1, + 25, 47, -1, + 9, 48, -1, + 40, 49, -1, + 49, -1, + 22, 21, -1, + 5, 7, -1, + 50, -1, + 7, -1, + 5, 7, 8, -1, + 10, 11, 15, 16, 21, -1, + 0, -1, + 13, 15, 46, -1, + 18, 19, -1, + 0, 26, -1, + 8, 28, 7, -1, + 20, 15, -1, + 1, 21, 39, -1, + 18, 21, -1, + 12, -1, + 41, 39, 2, 15, 6, 21, -1, + 8, -1, + 10, 11, 12, 13, 21, -1, + 14, 15, 16, 17, -1, + 26, -1, + 0, -1, + 0, -1, + 0, -1, + 0, -1, + 0, -1, + 36, -1, + 24, -1, + 0, -1, + 0, -1, + 0, -1, + 0, -1, + 0, -1, + 0, -1, + 0, -1, + 0, -1, + 0, -1, + 17, 32, 21, -1, + 43, -1, + -1, +}; + + +LGRect *rendrect; +extern fauxrend_context *_fr; +ubyte mouselocked = 0; + +#define NUM_STARS 1000 + +sts_vec star_vec[NUM_STARS]; +uchar star_col[NUM_STARS]; + +//------------------------ +// Internal Prototypes +//------------------------ +void rend_mouse_hide(void); +void rend_mouse_show(void); +uchar game_obj_block_home(void *vmptr, uchar *_sclip, int *loc); +uchar game_obj_block(void *vmptr, uchar *_sclip, int *loc); +int game_fr_idx(void); +grs_bitmap *game_fr_tmap_128(void); +grs_bitmap *game_fr_tmap_64(void); +grs_bitmap *game_fr_tmap_full(void); +void game_rend_start(void); +void game_fr_clip_start(uchar headnorth); + +void fauxrend_camera_setfunc(TileCamera *tc); + +// Note that I have fixed this so that the cursor does not flicker. +// It just works. Note its simplistic beauty. I love this job. +void rend_mouse_hide(void) { + extern bool DoubleSize; + extern grs_canvas gDoubleSizeOffCanvas; + extern bool view360_is_rendering; + + MouseLock++; + if (MouseLock == 1 && CurrentCursor != NULL) { + int cmd = CURSOR_DRAW; + LGPoint pos = LastCursorPos; + grs_canvas *old_canvas = CursorCanvas; + + pos.x -= _fr->xtop; + pos.y -= _fr->ytop; + + /*KLC - this is never true, now + if (_fr_curflags & FR_DOHFLIP_MASK) + { + pos.x = _fr->xwid - pos.x - 1; + cmd = CURSOR_DRAW_HFLIP; + } + */ + mouselocked = 1; + MouseLock++; // keep mouse locked while we blit + + if (DoubleSize && !view360_is_rendering) // If double-sizing, then draw the cursor in the + { // temporary doubled canvas. + CursorCanvas = &gDoubleSizeOffCanvas; + if (!full_game_3d) // In slot view, adjust the cursor position. + { + pos.x -= 28; + pos.y -= 29; + } + } else + CursorCanvas = &_fr->draw_canvas; + if (LastCursor) + LastCursor->func(cmd, CursorRegion, LastCursor, pos); + CursorCanvas = old_canvas; + } else + mouselocked = 0; + MouseLock--; // decrement mouselock after frame buffer blit. +} + +void rend_mouse_show(void) { MouseLock -= mouselocked; } + +extern int _game_fr_tmap; // current tmap +extern MapElem *_fdt_mptr; + +extern grs_bitmap tmap_bm[32]; // this is dumb, yea yea + +static MapElem *home_ptr; + +uchar game_obj_block_home(void *vmptr, uchar *_sclip, int *loc) { + MapElem *mptr = (MapElem *)vmptr; + Obj *cobj; + ObjID cobjid; + ObjRefID curORef; + + curORef = mptr->objRef; + while (curORef != OBJ_REF_NULL) { + cobjid = objRefs[curORef].obj; + if (ObjProps[OPNUM(cobjid)].flags & RENDER_BLOCK) { // if we are a block type object + cobj = &objs[cobjid]; + if (((cobj->loc.p | cobj->loc.b) == 0) && ((cobj->loc.h & 0x3f) == 0) && (cobj->info.current_frame == 0)) + if ((objRefs[curORef].state.bin.sq.x == (cobj->loc.x >> 8)) && + (objRefs[curORef].state.bin.sq.y == (cobj->loc.y >> 8))) { // is it at correct heading + if ((cobj->loc.h + 0x20) & 0x40) { + // mprintf("Thinking about X subclip from %x and %x, clip + //%x\n",cobj->loc.x,fr_camera_last[0],_sclip[1]); + if ((home_ptr == mptr) || (loc[0] >> 16 == _fr_x_cen)) { + if (_sclip[1] == FMK_INT_WW) { + if (cobj->loc.x < (fr_camera_last[0] >> 8)) { + _me_subclip(mptr) |= _sclip[1]; + return TRUE; + } + } else if (cobj->loc.x > (fr_camera_last[0] >> 8)) { + _me_subclip(mptr) |= _sclip[1]; + return TRUE; + } + // mprintf("Guess not\n"); + return FALSE; + } else { + _me_subclip(mptr) |= _sclip[1]; /* mprintf("xsubclip standard..."); */ + return TRUE; + } + } else { + if ((home_ptr == mptr) || (loc[1] >> 16 == _fr_y_cen)) { + if (_sclip[0] == FMK_INT_SW) { + if (cobj->loc.y < (fr_camera_last[1] >> 8)) { + _me_subclip(mptr) |= _sclip[0]; + } // mprintf("ysubclip yep south.."); } + } else if (cobj->loc.y > (fr_camera_last[1] >> 8)) { + _me_subclip(mptr) |= _sclip[0]; + } // mprintf("ysubclip yep north.."); } + // mprintf(" Yo: Y subclip from %x and %x, clip + //%x\n",cobj->loc.y,fr_camera_last[1],_sclip[0]); + } else { + _me_subclip(mptr) |= _sclip[0]; + } // mprintf("ysubclip standard results.."); } + return FALSE; // y direction + } + } + } + curORef = objRefs[curORef].next; + } + return FALSE; // for now, no blockage in home square +} + +uchar game_obj_block(void *vmptr, uchar *_sclip, int *loc) { + MapElem *mptr = (MapElem *)vmptr; + Obj *cobj; + ObjID cobjid; + ObjRefID curORef; + + if ((home_ptr == mptr) || (((loc[0] >> 16) == _fr_x_cen) || ((loc[1] >> 16) == _fr_y_cen))) + return game_obj_block_home(vmptr, _sclip, loc); + + curORef = mptr->objRef; + while (curORef != OBJ_REF_NULL) { + cobjid = objRefs[curORef].obj; + if (ObjProps[OPNUM(cobjid)].flags & RENDER_BLOCK) { // if we are a block type object + cobj = &objs[cobjid]; + if (((cobj->loc.p | cobj->loc.b) == 0) && ((cobj->loc.h & 0x3f) == 0) && (cobj->info.current_frame == 0)) + if ((objRefs[curORef].state.bin.sq.x == (cobj->loc.x >> 8)) && + (objRefs[curORef].state.bin.sq.y == (cobj->loc.y >> 8))) { // is it at correct heading + if ((cobj->loc.h + 0x20) & 0x40) { + _me_subclip(mptr) |= _sclip[1]; + return TRUE; // x direction + } else { + _me_subclip(mptr) |= _sclip[0]; + // mprintf("Set %d %d to %x from sclip %x\n", + // objRefs[curORef].state.bin.sq.x,objRefs[curORef].state.bin.sq.y,me_subclip(mptr),_sclip[0]); + return FALSE; // y direction + } + } + } + curORef = objRefs[curORef].next; + } + return FALSE; +} + +int game_fr_idx(void) { return _game_fr_tmap; } + +#define TIM_WERE_AWAKE +#ifdef TIM_WERE_AWAKE +#define IsTpropStars() (textprops[_game_fr_tmap].force_dir > 0) +#define IsTpStarDraw() (textprops[_game_fr_tmap].force_dir == 2) +#else +#define IsTpropStars() (_game_fr_tmap < 4) +#define IsTpStarDraw() (_game_fr_tmap < 2) +#endif + +extern g3s_phandle _fdt_tmppts[8]; /* these are used for all temporary point sets */ + +// should i draw this texture/map/so on +uchar draw_tmap_p(int ptcnt) { + // JAEMZ JAEMZ JAEMZ JAEMZ + // notify yourself here, i would guess.... + if (IsTpropStars()) { + if (IsTpStarDraw()) { + // texture map, don't draw, just eval + star_empty(ptcnt, _fdt_tmppts); + return TRUE; + } else { + star_poly(ptcnt, _fdt_tmppts); + return FALSE; + // g3_draw_poly(152,ptcnt,_fdt_tmppts); + } + } + + return TRUE; +} + +// SPEED THIS UP +// major changes left to do: +// -- rewrite in assembler and stop being a wuss, self modify in drop vals and full screen adjust +// -- currently doesnt have support for full screen adjust.. should be easy assembler though, just need the structure +// offsets +grs_bitmap *game_fr_tmap_128(void) { + grs_bitmap *draw_me; + register int loop = TEXTURE_128_INDEX; + register int cur_drop = _frp.view.drop_rad[0] + textprops[_game_fr_tmap].distance_mod; + + if (cur_drop < _fdt_dist) { + cur_drop += _frp.view.drop_rad[1]; + loop++; + if (cur_drop < _fdt_dist) { + loop++; + if (cur_drop + _frp.view.drop_rad[2] < _fdt_dist) + loop++; + } + } + + draw_me = get_texture_map(_game_fr_tmap + ANIMTEXT_FRAME(_game_fr_tmap), loop); + return draw_me; +} + +grs_bitmap *game_fr_tmap_64(void) { + grs_bitmap *draw_me; + register int loop = TEXTURE_64_INDEX; + register int cur_drop = _frp.view.drop_rad[1] + textprops[_game_fr_tmap].distance_mod; + + if (cur_drop < _fdt_dist) { + loop++; // now 32 + if (cur_drop + _frp.view.drop_rad[2] < _fdt_dist) + loop++; // now 16 + } + + draw_me = get_texture_map(_game_fr_tmap + ANIMTEXT_FRAME(_game_fr_tmap), loop); + return draw_me; +} + +grs_bitmap *game_fr_tmap_full(void) { + grs_bitmap *draw_me; + int loop = TEXTURE_128_INDEX, lmask; + int cur_drop = _frp.view.drop_rad[0] + textprops[_game_fr_tmap].distance_mod; + + if (cur_drop < _fdt_dist) { + cur_drop += _frp.view.drop_rad[1]; + loop++; + if (cur_drop < _fdt_dist) { + loop++; + if (cur_drop + _frp.view.drop_rad[2] < _fdt_dist) { + loop++; + goto draw_it; + } + } + } + lmask = (1 << loop); +#ifdef CAN_MISS + if (((texture_array[_game_fr_tmap].sizes_loaded) & lmask) == 0) { + do { + loop++; + lmask <<= 1; + } while ((loop < TEXTURE_16_INDEX) && (((texture_array[_game_fr_tmap].sizes_loaded) & lmask) == 0)); + } +#endif + +draw_it: + draw_me = get_texture_map(_game_fr_tmap + ANIMTEXT_FRAME(_game_fr_tmap), loop); + return draw_me; +} + +void game_rend_start(void) { + extern ObjID no_render_obj; + extern uchar cam_mode; + cams *cur_cam; + // hey, gots to do this somewhere + // remove self from object list + if (cam_mode == OBJ_PLAYER_CAMERA) + no_render_obj = PLAYER_OBJ; + else { + cur_cam = fr_camera_getdef(); + if (cur_cam->type & CAMBIT_OBJ) + no_render_obj = cur_cam->obj_id; + else + no_render_obj = -1; + } + + render_sort_start(); +} + +void game_fr_clip_start(uchar headnorth) { + if (headnorth) { + home_ptr = MAP_GET_XY(_fr_x_cen, _fr_y_cen); + fr_obj_block = game_obj_block; + } +} + + +/*KLC - no longer used here +// new regieme, has gruesome hacks for memory saving.... +#define FRAME_BUFFER_SIZE (320*200) +static uchar *frameBufferFreePtr=NULL; +extern uchar frameBuffer[]; +*/ + +uchar model_base_nums[MAX_VTEXT_OBJS]; + +void game_fr_startup(void) { + short curr, index; + extern int std_alias_size; + + // we know that the main screen we support is 320x200, so..... + // KLC frameBufferFreePtr=frameBuffer; + // has to fixed, clearly + fr_set_global_callbacks(gamesys_draw_func, NULL, gamesys_render_func); + fr_mouse_hide = rend_mouse_hide; + fr_mouse_show = rend_mouse_show; + fr_get_idx = game_fr_idx; + fr_get_tmap = game_fr_tmap_full; + fr_clip_start = game_fr_clip_start; + fr_rend_start = game_rend_start; + // this has to be fixed as well, should be real_ship or something + curr = 1; + index = 0; + model_base_nums[0] = 0; + while (model_vtext_data[index] != -1) // check for a -1 in what was supposedly a nice place + { + while (model_vtext_data[index] != -1) + index++; // eat up numbers until index points at -1 + model_base_nums[curr++] = index + 1; // point at nice one beyond our -1 delimter + index++; // increment past the -1 + } + + // for (i=0; icyber) { + case 1: + _fr_glob_flags |= FR_SHOWALL_MASK; + _frp.view.radius = 13; + break; + case 0: + _fr_glob_flags &= ~FR_SHOWALL_MASK; + _frp.view.radius = 18; + break; + } +} + +void game_redrop_rad(int rad_mod) { + _frp.view.drop_rad[0] = _frp.view.odrop_rad[0] + rad_mod; + _frp.view.drop_rad[1] = _frp.view.odrop_rad[1] + rad_mod; + _frp.view.drop_rad[2] = _frp.view.odrop_rad[2] + rad_mod; +} +//#pragma enable_message(202) + +// errtype is icky +extern int _fr_global_detail; +void change_detail_level(byte new_level) { _fr_global_detail = new_level; } + +void set_global_lighting(short l_lev) { _frp.lighting.global_mod += l_lev; } + +void rendedit_process_tilemap(FullMap *fmap, LGRect *r, uchar newMap) { + // mprintf("RPT %d\n",new); + if (fmap == NULL) /* support null pass in */ + fmap = global_fullmap; + if (newMap) + fr_compile_restart(fmap); + fr_compile_rect(fmap, r->ul.x, r->ul.y, r->lr.x, r->lr.y, FALSE); +} + +// lets move this to the tilemap, eh? +void fauxrend_camera_setfunc(TileCamera *tc) { + tc->x = last_coor(EYE_X); + tc->y = last_coor(EYE_Y); + tc->theta = last_ang(EYE_H) - FIXANG_PI / 2; + tc->show = TRUE; +} + +// Like fr_get_at, but takes real screen coordinates. +ushort fr_get_at_raw(frc *fr, int x, int y, uchar again, uchar transp) { + extern fauxrend_context *_sr; + _fr_top(fr); + if (again) + return fr_get_again(_fr, x - _fr->xtop, y - _fr->ytop); + else + return fr_get_at(_fr, x - _fr->xtop, y - _fr->ytop, transp); +} + + /*KLC - no longer used + // this is a hack for render canvas memory usage.... + uchar *get_free_frame_buffer_bits(int size) + { + if ((size==FRAME_BUFFER_SIZE)||(size<=0)) + return frameBuffer; + else + { + uchar *tmp=frameBufferFreePtr; + if (frameBufferFreePtr-frameBuffer+size>FRAME_BUFFER_SIZE) + { + // Warning(("Mini Frame Buffers Too Big %d\n",size)); + return NULL; + } + frameBufferFreePtr+=size; + return tmp; + } + } + */ + +#define MATERIAL_BASE RES_materialMaps + +void load_model_vtexts(char model_num) { + short curr = model_base_nums[model_num]; + grs_bitmap *stupid; + if (model_num >= MAX_VTEXT_OBJS) + return; + while (model_vtext_data[curr] != -1) { + stupid = lock_bitmap_from_ref_anchor(MKREF(MATERIAL_BASE + model_vtext_data[curr], 0), NULL); + g3_set_vtext(model_vtext_data[curr], stupid); + curr++; + } +} + +void free_model_vtexts(char model_num) { + short curr = model_base_nums[model_num]; + if (model_num >= MAX_VTEXT_OBJS) + return; + while (model_vtext_data[curr] != -1) { + RefUnlock(MKREF(MATERIAL_BASE + model_vtext_data[curr], 0)); + curr++; + } +} diff --git a/engine/src/GameSrc/saveload.c b/engine/src/GameSrc/saveload.c new file mode 100644 index 0000000..7e4758c --- /dev/null +++ b/engine/src/GameSrc/saveload.c @@ -0,0 +1,1388 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/saveload.c $ + * $Revision: 1.145 $ + * $Author: xemu $ + * $Date: 1994/11/21 21:07:36 $ + */ +#include +#include + +#include "MacTune.h" + +#include "saveload.h" +#include "archiveformat.h" +#include "criterr.h" +#include "cyber.h" +#include "cybmem.h" +#include "dynmem.h" +#include "effect.h" +#include "frflags.h" +#include "frprotox.h" +#include "gametime.h" +#include "gamewrap.h" +#include "hkeyfunc.h" +#include "input.h" +#include "lvldata.h" +#include "objapp.h" +#include "objects.h" +#include "objload.h" +#include "objprop.h" +#include "objsim.h" +#include "objwpn.h" +#include "objwarez.h" +#include "objstuff.h" +#include "objgame.h" +#include "objcrit.h" +#include "objver.h" +#include "objuse.h" +#include "otrip.h" +#include "map.h" +#include "mfdext.h" +#include "musicai.h" +#include "pathfind.h" +#include "physics.h" +#include "player.h" +#include "render.h" +#include "rendtool.h" +#include "schedule.h" +#include "sfxlist.h" +#include "shodan.h" +#include "statics.h" +#include "textmaps.h" +#include "tools.h" +#include "trigger.h" +#include "verify.h" + + +// INTERNAL PROTOTYPES +// ----------------- +void load_level_data(); +void store_objects(char **buf, ObjID *obj_array, char obj_count); +void restore_objects(char *buf, ObjID *obj_array, char obj_count); +errtype write_id(Id id_num, short index, uint32_t version, void *ptr, long sz, int fd, short flags); + +const ResourceFormat *format_from_idx_version(int index, uint32_t version) { + // I believe these are the only formats extant. + const ResourceFormat *table = (version > MAP_VERSION_NUMBER) ? + LevelVersion12Format : LevelVersion11Format; + assert(index <= MAX_LEVEL_INDEX); + return &table[index]; +} + +// Extract a level resource. Uses the version to look up the appropriate formats +// table and the index to figure out the actual format. +void extract_level_resource(Id id_num, int index, uint32_t version, void *ptr) { + // I believe these are the only formats extant. + ResExtract(id_num + index, format_from_idx_version(index, version), ptr); +} + +#define FIRST_CSPACE_LEVEL 14 + +#define SAVE_AUTOMAP_STRINGS + +char saveload_string[30]; +uchar display_saveload_checkpoints = FALSE; +uchar saveload_static = FALSE; +uint dynmem_mask = DYNMEM_ALL; + +extern ObjID hack_cam_objs[NUM_HACK_CAMERAS]; +extern ObjID hack_cam_surrogates[NUM_HACK_CAMERAS]; +extern height_semaphor h_sems[NUM_HEIGHT_SEMAPHORS]; +extern uchar trigger_check; + +//------------------------------------------------------- +void store_objects(char **buf, ObjID *obj_array, char obj_count) { + char *s = (char *)malloc(obj_count * sizeof(Obj) * 3); + int i; + if (s == NULL) + critical_error(CRITERR_MEM | 3); + *buf = s; + for (i = 0; i < obj_count; i++) { + check_null: + if (obj_array[i] != OBJ_NULL) { + ObjID id = obj_array[i]; + ObjSpecHeader *sh = &objSpecHeaders[objs[id].obclass]; + // dumb hack to avoid page fault for now, are not we cool + if (!objs[id].active) { + obj_array[i] = OBJ_NULL; + goto check_null; + } + *(Obj *)s = objs[id]; + s += sizeof(Obj); + LG_memcpy(s, sh->data + sh->struct_size * objs[id].specID, sh->struct_size); + s += sh->struct_size; + ObjDel(id); + } else { + + ((Obj *)s)->active = FALSE; + s += sizeof(Obj); + } + } +} + +//------------------------------------------------------- +void restore_objects(char *buf, ObjID *obj_array, char obj_count) { + char *s = buf; + int i; + for (i = 0; i < obj_count; i++) { + Obj *next = (Obj *)s; + if (!next->active) { + s += sizeof(Obj); + obj_array[i] = OBJ_NULL; + } else { + ObjID id = obj_create_base(MAKETRIP(next->obclass, next->subclass, next->info.type)); + ObjSpecHeader *sh = &objSpecHeaders[next->obclass]; + char *spec; + s += sizeof(Obj); + if (id != OBJ_NULL) { + objs[id].info = next->info; + spec = (sh->data + sh->struct_size * objs[id].specID); + LG_memcpy(spec + sizeof(ObjSpec), s + sizeof(ObjSpec), sh->struct_size - sizeof(ObjSpec)); + obj_array[i] = id; + } + s += sh->struct_size; + } + } + free(buf); +} + +//------------------------------------------------------- +#define SECRET_VOODOO_ENDGAME_CSPACE 10 + +uchar go_to_different_level(int targlevel) { + State player_state; + char *buf; + uchar in_cyber = global_fullmap->cyber; + uchar retval = FALSE; + errtype rv; + + dynmem_mask = DYNMEM_ALL; + if ((targlevel >= FIRST_CSPACE_LEVEL) || (targlevel == SECRET_VOODOO_ENDGAME_CSPACE)) { + if (!in_cyber) { + // indicate static + saveload_static = TRUE; + + // force to fullscreen + + enter_cyberspace_stuff(targlevel); + retval = TRUE; + } + } else if (in_cyber) { + saveload_static = TRUE; + } + if (saveload_static) { + uchar old_music; + physics_zero_all_controls(); + fr_global_mod_flag(FR_SOLIDFR_STATIC, FR_SOLIDFR_MASK); + render_run(); + play_digi_fx(SFX_ENTER_CSPACE, -1); + old_music = music_on; + if (music_on) + stop_music(); + music_on = old_music; + // KLC start_asynch_digi_fx(); + dynmem_mask = DYNMEM_PARTIAL; + } + // else if (music_on) // Don't play music + // while + // MacTuneShutdown(); // elevator switches levels + + // KLC - if changing levels via the elevator, we need to make sure the elevator music + // continues playing. We'll do this by queueing up 3 additional chunks of music. + else if (music_on) { + for (int t = 0; t < 3; t++) { + MacTuneQueueTune(mlimbs_boredom); + mlimbs_boredom++; + if (mlimbs_boredom >= 8) + mlimbs_boredom = 0; + } + } + + // make a note of when we left; + level_gamedata.exit_time = player_struct.game_time; + + // Zap the player to there... + store_objects(&buf, player_struct.inventory, NUM_GENERAL_SLOTS); + if (!in_cyber) { + EDMS_get_state(objs[PLAYER_OBJ].info.ph, &player_state); + LG_memcpy(player_struct.edms_state, &player_state, sizeof(fix) * 12); + } else { + early_exit_cyberspace_stuff(); + } + + rv = write_level_to_disk(ResIdFromLevel(player_struct.level), TRUE); + if (rv) + critical_error(CRITERR_FILE | 4); + + rv = load_level_from_file(targlevel); + // Hmm, should we criterr out on this case? + restore_objects(buf, player_struct.inventory, NUM_GENERAL_SLOTS); + obj_load_art(FALSE); + + // reset renderer data + game_fr_reparam(-1, -1, -1); + + // move the level to the present + if (!in_cyber && !global_fullmap->cyber) + update_level_gametime(); + + // exit cyberspace + if (in_cyber) { + exit_cyberspace_stuff(); + } + + // Now do any level-entry triggers + do_level_entry_triggers(); + + // Set the current target to null. + player_struct.curr_target = OBJ_NULL; + + // Undo static if we did it before + if (saveload_static) { + saveload_static = FALSE; + fr_global_mod_flag(0, FR_SOLIDFR_MASK); + if (music_on) + start_music(); + // KLC stop_asynch_digi_fx(); + clear_digi_fx(); + } + /* if no music while elevator moves + else if (music_on) + { + if (MacTuneInit() == 0) + { + mlimbs_on = TRUE; + mlimbs_AI_init(); + mlimbs_boredom = TickCount() % 8; + load_score_guts(7); + MacTuneStartCurrentTheme(); + } + } + */ + /*KLC + else if (music_on) + mlimbs_return_to_synch(); + */ + dynmem_mask = DYNMEM_ALL; + mfd_force_update(); + return (retval); +} + +#define ANOTHER_DEFINE_FOR_NUM_LEVELS 16 + +errtype write_id(Id id_num, short index, uint32_t version, void *ptr, long sz, int fd, short flags) { + ResMake(id_num + index, ptr, sz, RTYPE_APP, fd, flags, + format_from_idx_version(index, version)); + if (ResWrite(id_num + index) == -1) + critical_error(CRITERR_FILE | 6); + ResUnmake(id_num + index); + return (OK); +} + +errtype save_current_map(char *fname, Id id_num, uchar flush_mem, uchar pack) { + int i, goof; + int idx = 0; + int fd; + int map_version = MAP_VERSION_NUMBER; + int ovnum = OBJECT_VERSION_NUMBER; + int mvnum = MISC_SAVELOAD_VERSION_NUMBER; + ObjLoc plr_loc; + uchar make_player = FALSE; + State player_edms; + uint32_t verify_cookie = 0; + + INFO("Save current map: %s", fname); + + begin_wait(); + + // make pathfinding state stable by fulfilling PF requests + check_requests(FALSE); + + /* KLC - not needed for game + if (id_num - LEVEL_ID_NUM < ANOTHER_DEFINE_FOR_NUM_LEVELS) + { // were in the editor, so clear out game state hack stupid i suck kill me + fr_compile_rect(global_fullmap,0,0,MAP_XSIZE,MAP_YSIZE,TRUE); + } + */ + + // do not ecology while player is being destroyed and created. + trigger_check = FALSE; + + // save off physics stuff + EDMS_get_state(objs[PLAYER_OBJ].info.ph, &player_edms); + if (PLAYER_OBJ != OBJ_NULL) { + plr_loc = objs[PLAYER_OBJ].loc; + obj_destroy(PLAYER_OBJ); + make_player = TRUE; + } + + // Read appropriate state modifiers + // if (flush_mem) + // free_dynamic_memory(DYNMEM_PARTIAL); + + // Open the file we're going to save into. + fd = ResEditFile(CURRENT_GAME_FNAME, TRUE); + if (fd < 0) { + ERROR("No file!"); + end_wait(); + return ERR_FOPEN; + } + +#define REF_WRITE(id_num, index, x) \ + write_id(id_num, index, map_version, &(x), sizeof(x), fd, 0) +#define REF_WRITE_LZW(id_num, index, x) \ + write_id(id_num, index, map_version, &(x), sizeof(x), fd, RDF_LZW) +#define REF_WRITE_RAW(id_num, index, ptr, sz) \ + write_id(id_num, index, map_version, ptr, sz, fd, RDF_LZW) + + REF_WRITE(SAVELOAD_VERIFICATION_ID, 0, verify_cookie); + + // xx02 Map version number. + REF_WRITE(id_num, idx++, map_version); + // xx03 Object version number. + REF_WRITE(id_num, idx++, ovnum); + // xx04 Fullmap. + REF_WRITE(id_num, idx++, *global_fullmap); + // xx05 Tile map. + REF_WRITE_RAW(id_num, idx++, MAP_MAP, sizeof(MapElem) * 64 * 64); + + // Here we are writing out the schedules. It's only a teeny tiny rep exposure. + for (i = 0; i < NUM_MAP_SCHEDULES; i++) { + int sz = lg_min(global_fullmap->sched[i].queue.fullness + 1, global_fullmap->sched[i].queue.size); + REF_WRITE_RAW(id_num, idx++, global_fullmap->sched[i].queue.vec, sizeof(SchedEvent) * sz); + } + // xx07 Textures. + REF_WRITE(id_num, idx++, loved_textures); + + obj_zero_unused(); + // xx08 Main object list. Always EASYSAVES, so no conversion needed. + REF_WRITE_LZW(id_num, idx++, objs); + // xx09 Object refs. + REF_WRITE_LZW(id_num, idx++, objRefs); + // xx10-xx24 Object specific stuff. All EASYSAVES again. + REF_WRITE(id_num, idx++, objGuns); + REF_WRITE(id_num, idx++, objAmmos); + REF_WRITE_LZW(id_num, idx++, objPhysicss); + REF_WRITE(id_num, idx++, objGrenades); + REF_WRITE(id_num, idx++, objDrugs); + REF_WRITE(id_num, idx++, objHardwares); + REF_WRITE(id_num, idx++, objSoftwares); + REF_WRITE_LZW(id_num, idx++, objBigstuffs); + REF_WRITE_LZW(id_num, idx++, objSmallstuffs); + REF_WRITE_LZW(id_num, idx++, objFixtures); + REF_WRITE_LZW(id_num, idx++, objDoors); + REF_WRITE(id_num, idx++, objAnimatings); + REF_WRITE_LZW(id_num, idx++, objTraps); + REF_WRITE_LZW(id_num, idx++, objContainers); + REF_WRITE_LZW(id_num, idx++, objCritters); + + // xx25-xx29 Default objects + REF_WRITE(id_num, idx++, default_gun); + REF_WRITE(id_num, idx++, default_ammo); + REF_WRITE(id_num, idx++, default_physics); + REF_WRITE(id_num, idx++, default_grenade); + REF_WRITE(id_num, idx++, default_drug); + REF_WRITE(id_num, idx++, default_hardware); + REF_WRITE(id_num, idx++, default_software); + REF_WRITE(id_num, idx++, default_bigstuff); + REF_WRITE(id_num, idx++, default_smallstuff); + REF_WRITE(id_num, idx++, default_fixture); + REF_WRITE(id_num, idx++, default_door); + REF_WRITE(id_num, idx++, default_animating); + REF_WRITE(id_num, idx++, default_trap); + REF_WRITE(id_num, idx++, default_container); + REF_WRITE(id_num, idx++, default_critter); + + idx++; // KLC - not used REF_WRITE(id_num,idx++,mvnum); + + // idx++; // where flickers once lived + idx++; // KLC - not used REF_WRITE(id_num,idx++,filler); + // xx42 Texture animation. + REF_WRITE(id_num, idx++, animtextures); + // xx43-xx44 Surveillance + REF_WRITE(id_num, idx++, hack_cam_objs); + REF_WRITE(id_num, idx++, hack_cam_surrogates); + + // xx45 Other level data -- at resource id right after maps + level_gamedata.size = sizeof(level_gamedata); + REF_WRITE(id_num, idx++, level_gamedata); +#ifdef SAVE_AUTOMAP_STRINGS + // REF_WRITE(id_num, idx++, amap_str_reref(0)); + // LZW later ResMake(id_num + (idx++), &(amap_str_reref(0)), AMAP_STRING_SIZE, RTYPE_APP, fd, RDF_LZW); + ResMake(id_num + (idx++), (amap_str_reref(0)), AMAP_STRING_SIZE, RTYPE_APP, fd, 0, FORMAT_RAW); + ResWrite(id_num + (idx - 1)); + ResUnmake(id_num + (idx - 1)); + goof = amap_str_deref(amap_str_next()); + REF_WRITE(id_num, idx++, goof); +#endif + idx++; // KLC - no need to be saved. REF_WRITE(id_num, idx++, player_edms); + // xx49-xx50 Paths + REF_WRITE(id_num, idx++, paths); + REF_WRITE(id_num, idx++, used_paths); + REF_WRITE(id_num, idx++, animlist); + REF_WRITE(id_num, idx++, anim_counter); + REF_WRITE(id_num, idx++, h_sems); + + /* KLC - not used + if (pack) + { + int reclaim; + reclaim = ResPack(fd); + if (reclaim == 0) + Warning(("%d bytes reclaimed from ResPack!\n",reclaim)); + } + */ + verify_cookie = VERIFY_COOKIE_VALID; + REF_WRITE(SAVELOAD_VERIFICATION_ID, 0, verify_cookie); + ResCloseFile(fd); + + // FlushVol(nil, fSpec->vRefNum); // Make sure everything is saved. + + if (make_player) + obj_create_player(&plr_loc); + trigger_check = TRUE; + // if (flush_mem) + // load_dynamic_memory(DYNMEM_PARTIAL); + EDMS_holistic_teleport(objs[PLAYER_OBJ].info.ph, &player_edms); + + end_wait(); + { + // extern void spoof_mouse_event(); + // what does this do??? spoof_mouse_event(); + } + + INFO("Saved level."); + + return OK; +} + +/*KLC - no map conversion needed in Mac version. + +extern uchar init_done; +extern int loadcount; + +#ifdef SUPPORT_9_TO_10 +#pragma disable_message(202) +void convert_map_element_9_10(oMapElem *ome, MapElem *me, int x, int y) +{ + me->tiletype=ome->tiletype; + if (ome->param && (tile_floors[ome->tiletype].flags&FRFLRFLG_USEPR)) + { // cool, erik has params and flags which mean NOTHING... neat... now need to detect + int tmp=(ome->flags&MAP_MIRROR_MASK)>>MAP_MIRROR_SHF; + + if ((tmp==MAP_MATCH)||(tmp==MAP_FFLAT)) + { + if (ome->ceil_heightparam) + { + me->ceil_height=0; + Warning(("Bad input Map Format at %d,%d, param %d and ceil %d, mirror +%d\n",x,y,ome->param,ome->ceil_height,tmp)); + } + else + me->ceil_height=ome->ceil_height-ome->param; // since this is negative + } + else me->ceil_height=ome->ceil_height; + + me->flr_height=ome->flr_height; + me->param=ome->param; + } + else + { + me->flr_height=ome->flr_height; + me->ceil_height=ome->ceil_height; + me->param=0; + } + me->templight=ome->templight; + me->space=ome->space; + me->flags=ome->flags; + me->objRef=ome->objRef; + // new stuff + // wow... wild internal secret gnosis + me->rinfo.sub_clip=SUBCLIP_OUT_OF_CONE; + me->rinfo.clear=0; + me->rinfo.rotflr=0; + me->rinfo.rotceil=0; + me->rinfo.flicker=0; +} +#pragma enable_message(202) +#endif + +#pragma disable_message(202) +void convert_map_element_10_11(oMapElem *ome, MapElem *me, int x, int y) +{ + me_tiletype_set(me,ome_tiletype(ome)); + me_height_flr_set(me,ome_height_flr(ome)); + me_height_ceil_set(me,ome_height_ceil(ome)); + me_param_set(me,ome_param(ome)); + me_templight_flr_set(me,ome_templight_flr(ome)); + me_templight_ceil_set(me,ome_templight_ceil(ome)); + me_tmap_flr_set(me,ome_tmap_flr(ome)); + me_tmap_ceil_set(me,ome_tmap_ceil(ome)); + me_tmap_wall_set(me,ome_tmap_wall(ome)); + me_objref_set(me,ome_objref(ome)); + + // flags nightmare + me->flag1=ome->flags&0xff; // low word is easy + me->flag2=(ome_bits_friend(ome)|(ome_bits_deconst(ome)<<1)| + (ome_bits_mirror(ome)<<2)|(ome_bits_peril(ome)<<4)|(ome_bits_music(ome)<<5)); + me->flag3=(ome_light_flr(ome)|(ome_bits_rend4(ome)<<4)); + me->flag4=(ome_light_ceil(ome)|(ome_bits_rend3(ome)<<4)); // seen is 0 at start + + // new stuff + // wow... wild internal secret gnosis + me->sub_clip=SUBCLIP_OUT_OF_CONE; + me->clearsolid=0; + me_rotflr_set(me,ome->rinfo.rotflr); + me_rotceil_set(me,ome->rinfo.rotceil); + me_hazard_bio_set(me,0); + me_hazard_rad_set(me,0); + me->flick_qclip=0; +} +#pragma enable_message(202) + +void convert_cit_map(oFullMap *omp, FullMap **mp) +{ + int i, j, ibase; + if ((*mp)!=NULL) + { Free((*mp)->map); Free(*mp); } + *mp=Malloc(sizeof(FullMap)); + LG_memcpy(*mp,omp,sizeof(oFullMap)); + (*mp)->map=Malloc(sizeof(MapElem)<<(omp->x_shft+omp->y_shft)); + for (ibase=0,i=0; i<(1<y_shft); i++, ibase+=(1<<(omp->x_shft))) + for (j=0; j<(1<x_shft); j++) + convert_map_element_10_11(omp->map+ibase+j,(*mp)->map+ibase+j,j,i); +} + +#ifdef COMPRESS_OBJSPECS +extern uchar HeaderObjSpecFree (ObjClass obclass, ObjSpecID id, ObjSpecHeader *head); +extern ObjSpecID HeaderObjSpecGrab (ObjClass obclass, ObjSpecHeader *head); +extern uchar HeaderObjSpecCopy (ObjClass cls, ObjSpecID old, ObjSpecID new, ObjSpecHeader *head); +extern const ObjSpecHeader old_objSpecHeaders[NUM_CLASSES]; + +errtype fix_free_chain(char cl, short limit) +{ + ObjSpec *osp, *next_item, *next_next_item; + short ss; + char *data; + uchar cont = TRUE; + + ss = old_objSpecHeaders[cl].struct_size; + data = old_objSpecHeaders[cl].data; + osp = (ObjSpec *)data; // Get the first elem + + // iterate through free chain, leapfrogging anything which is too high. + while (cont) + { + if (osp->next >= limit) + { + next_item = (ObjSpec *)(data + (osp->next * ss)); + osp->next = next_item->next; + if (osp->next == OBJ_SPEC_NULL) + cont = FALSE; + else + { + next_next_item = (ObjSpec *)(data + (osp->next * ss)); + next_next_item->prev = next_item->prev; + } + } + else if (osp->next == OBJ_SPEC_NULL) + cont = FALSE; + else + osp = (ObjSpec *)(data + (osp->next * ss)); + } + return(OK); +} + +uchar one_compression_pass(char cl,short start) +{ + ObjSpec *osp; + short ss; + char *data; + ObjID fugitive = OBJ_NULL; + ObjSpec *p1; + ObjSpecID new_objspec, old_specid; + uchar cont = TRUE; + + ss = old_objSpecHeaders[cl].struct_size; + data = old_objSpecHeaders[cl].data; + osp = (ObjSpec *)data; // Get the first elem + osp = (ObjSpec *)(data + (osp->id * ss)); // Extract secret gnosis + while(cont) + { + if (objs[osp->id].specID >= start) + { + fugitive = osp->id; + cont = FALSE; + } + else if (osp->next == OBJ_SPEC_NULL) + { + cont = FALSE; + } + else + osp = (ObjSpec *)(data + (osp->next * ss)); + } + if (fugitive == OBJ_NULL) + return(FALSE); + + // Now that we have a guy who ought to be moved, lets move him + // Really, I guess we have no particular reason to believe that our new location + // is towards the low end of stuff, but hey, it usually is.... + new_objspec = HeaderObjSpecGrab(cl,&old_objSpecHeaders[cl]); + p1 = (ObjSpec *)(data + new_objspec * ss); + if (new_objspec == OBJ_SPEC_NULL) + { + Warning(("we have a problem here folks. Can't complete HeaderObjSpecGrab!\n")); + critical_error(22); + } + // copy in the data... + HeaderObjSpecCopy(cl,objs[osp->id].specID, new_objspec, &old_objSpecHeaders[cl]); + + p1->id = osp->id; + old_specid = objs[osp->id].specID; + objs[osp->id].specID = new_objspec; + HeaderObjSpecFree(cl,old_specid, &old_objSpecHeaders[cl]); + fix_free_chain(cl,start); + return(TRUE); +} + +errtype compress_old_class(char cl) +{ + uchar cont = TRUE; + fix_free_chain(cl, objSpecHeaders[cl].size); + while (cont) + cont = one_compression_pass(cl,objSpecHeaders[cl].size); + return(OK); +} +#endif + +errtype expand_old_class(char cl, short new_start) +{ + ObjSpec *osp, *next_item; + ObjSpecID osid; + short ss; + char *data; + uchar cont = TRUE; + + ss = objSpecHeaders[cl].struct_size; + data = objSpecHeaders[cl].data; + osp = (ObjSpec *)data; // Get the first elem + osid = 0; + + // iterate through free chain until we find the old end, then extend it a bit further + while (cont) + { + if (osp->next == OBJ_SPEC_NULL) + { + osp->next = new_start; + Warning(("setting osid %d to %d\n",osid,new_start)); + next_item = (ObjSpec *)(data + osp->next * ss); + next_item->prev = osid; + cont = FALSE; + } + else + { + osid = osp->next; + osp = (ObjSpec *)(data + (osp->next * ss)); + } + } + return(OK); +} +*/ + +void load_level_data() { + // KLC-removed from here obj_load_art(FALSE); + load_small_texturemaps(); +} + +void SwapLongBytes(void *pval4); +void SwapShortBytes(void *pval2); +#define MAKE4(c0, c1, c2, c3) ((((ulong)c0) << 24) | (((ulong)c1) << 16) | (((ulong)c2) << 8) | ((ulong)c3)) + +// --------------------------------------------------------- +// Â¥ Put this in some more appropriate, global place. +void SwapLongBytes(void *pval4) { + long *temp = (long *)pval4; + *temp = MAKE4(*temp & 0xFF, (*temp >> 8) & 0xFF, (*temp >> 16) & 0xFF, *temp >> 24); +} + +void SwapShortBytes(void *pval2) { + short *temp = (short *)pval2; + *temp = ((*temp & 0xFF) << 8) | ((*temp >> 8) & 0xFF); +} + +//--------------------------------------------------------------------------------- +// Loads in the map for a level, and all the other related resources (2+ MB worth). +//--------------------------------------------------------------------------------- +// errtype load_current_map(char* fn, Id id_num, Datapath* dpath) +errtype load_current_map(Id id_num) { + extern int physics_handle_max; + extern ObjID physics_handle_id[MAX_OBJ]; + extern char old_bits; + + int i, idx = 0, fd; + uint32_t map_version; + uint32_t object_version; + LGRect bounds; + errtype retval = OK; + bool make_player = FALSE; + ObjLoc plr_loc; + char *schedvec; // KLC - don't need an array. Only one in map. + // State player_edms; + curAMap saveAMaps[NUM_O_AMAP]; + uchar savedMaps; + bool do_anims = FALSE; + + // _MARK_("load_current_map:Start"); + + INFO("Loading map %x", id_num); + + begin_wait(); + free_dynamic_memory(dynmem_mask); + trigger_check = FALSE; + if (PLAYER_OBJ != OBJ_NULL) { + plr_loc = objs[PLAYER_OBJ].loc; + obj_destroy(PLAYER_OBJ); + make_player = TRUE; + } + if (input_cursor_mode == INPUT_OBJECT_CURSOR) { + pop_cursor_object(); + } + + // Open the saved-game (or archive) file. + fd = ResOpenFile(CURRENT_GAME_FNAME); + if (fd < 0) { + // Warning(("Could not load map file %s (%s) , rv = %d!\n",dpath_fn,fn,retval)); + ERROR("Could not load map file %d", retval); + if (make_player) + obj_create_player(&plr_loc); + trigger_check = TRUE; + load_dynamic_memory(dynmem_mask); + end_wait(); + + return ERR_FOPEN; + } + + if (ResInUse(SAVELOAD_VERIFICATION_ID)) { + uint32_t verify_cookie; + ResExtract(SAVELOAD_VERIFICATION_ID, FORMAT_U32, &verify_cookie); + if ((verify_cookie != VERIFY_COOKIE_VALID) && (verify_cookie != OLD_VERIFY_COOKIE_VALID)) + critical_error(CRITERR_FILE | 5); + } + + // Resource xx02: map version. + ResExtract(id_num + idx++, FORMAT_U32, &map_version); + + // Check the version number of the map for this level. + if (map_version < MAP_VERSION_NUMBER) { + INFO("OLD MAP FORMAT!"); + } + +#define REF_READ(id,index,x) extract_level_resource(id, index, map_version, &(x)) + + // object version number! + REF_READ(id_num, idx++, object_version); + // SwapLongBytes(&version); // Mac + + // Clear out old physics data and object data + ObjsInit(); + physics_init(); + + // Read in the global fullmap (without disrupting schedule vec ptr) + schedvec = global_fullmap->sched[0].queue.vec; // KLC - Only one schedule, so just save it. + // Preserve the old schedule size in case the one being loaded is different + int schedsize = global_fullmap->sched[0].queue.size; + + // convert_from is the version we are coming from. + // for now, this is only defined for coming from version 9 + REF_READ(id_num, idx++, *global_fullmap); + + MAP_MAP = (MapElem *)static_map; + REF_READ(id_num, idx++, *static_map); + + // Load schedules, performing some voodoo. + global_fullmap->sched[0].queue.vec = schedvec; + global_fullmap->sched[0].queue.comp = compare_events; + + // Might have to allocate more memory for the queue + if (global_fullmap->sched[0].queue.size > schedsize) { + schedule_free(&global_fullmap->sched[0]); + schedule_init(&global_fullmap->sched[0], global_fullmap->sched[0].queue.size, FALSE); + } else { + // Preserve the existing size. + global_fullmap->sched[0].queue.size = schedsize; + } + + char *dst_ptr = global_fullmap->sched[0].queue.vec; + ResExtract(id_num + idx++, FORMAT_RAW, dst_ptr); + + // KLC��� Big hack! Force the schedule to growable. + global_fullmap->sched[0].queue.grow = TRUE; + + REF_READ(id_num, idx++, loved_textures); + /* + for (i = 0; i < NUM_LOADED_TEXTURES; i++) + { + SwapShortBytes(&loved_textures[i]); + } + */ + map_set_default(global_fullmap); + + /*��� Leave conversion from old objects out for now + + // Now set up for object conversion if necessary + convert_from = -1; + + if (version != OBJECT_VERSION_NUMBER) + { + retval = ERR_NOEFFECT; + Warning(("Old Object Version Number (%d)!! Current V. Num = %d\n",version,OBJECT_VERSION_NUMBER)); + if (version >= 17) + { + Warning(("Auto-converting objects to v. %d from %d\n", OBJECT_VERSION_NUMBER,version)); + convert_from = version; + } + else + { + for (x=0; xx_size; x++) + { + for (y=0; yy_size; y++) + { + MAP_GET_XY(x,y)->objRef = 0; + } + } + goto obj_out; + } + } + #ifdef SUPPORT_VERSION_26_OBJS + if ((convert_from < 27) && (convert_from != -1)) + { + extern old_Obj old_objs[NUM_OBJECTS]; + REF_READ(id_num,idx++,old_objs); + for (x=0; x < NUM_OBJECTS; x++) + { + objs[x].active = old_objs[x].active; + objs[x].obclass = old_objs[x].obclass; + objs[x].subclass = old_objs[x].subclass; + objs[x].specID = old_objs[x].specID; + objs[x].ref = old_objs[x].ref; + objs[x].next = old_objs[x].next; + objs[x].prev = old_objs[x].prev; + objs[x].loc = old_objs[x].loc; + objs[x].info.ph = (char)(old_objs[x].info.ph); + objs[x].info.type = old_objs[x].info.type; + objs[x].info.current_hp = old_objs[x].info.current_hp; + objs[x].info.make_info = old_objs[x].info.make_info; + objs[x].info.current_frame = old_objs[x].info.current_frame; + objs[x].info.time_remainder = old_objs[x].info.time_remainder; + objs[x].info.inst_flags = old_objs[x].info.inst_flags; + } + } + else + #endif + */ + + // Read in object information. + REF_READ(id_num, idx++, objs); + + // Read in and convert the object refs. + REF_READ(id_num, idx++, objRefs); + /* for (i=0; i < NUM_REF_OBJECTS; i++) + { + SwapShortBytes(&objRefs[i].state.bin.sq.x); + SwapShortBytes(&objRefs[i].state.bin.sq.y); + SwapShortBytes(&objRefs[i].obj); + SwapShortBytes(&objRefs[i].next); + SwapShortBytes(&objRefs[i].nextref); + } */ + + // Read in and convert the gun objects. + REF_READ(id_num, idx++, objGuns); + /* for (i=0; i < NUM_OBJECTS_GUN; i++) + { + SwapShortBytes(&objGuns[i].id); + SwapShortBytes(&objGuns[i].next); + SwapShortBytes(&objGuns[i].prev); + }*/ + + // Read in and convert the ammo objects. + REF_READ(id_num, idx++, objAmmos); + /* for (i=0; i < NUM_OBJECTS_AMMO; i++) + { + SwapShortBytes(&objAmmos[i].id); + SwapShortBytes(&objAmmos[i].next); + SwapShortBytes(&objAmmos[i].prev); + }*/ + + // Read in and convert the physics objects. + REF_READ(id_num, idx++, objPhysicss); + /* for (i=0; i < NUM_OBJECTS_PHYSICS; i++) + { + SwapShortBytes(&objPhysicss[i].id); + SwapShortBytes(&objPhysicss[i].next); + SwapShortBytes(&objPhysicss[i].prev); + SwapShortBytes(&objPhysicss[i].owner); + SwapLongBytes(&objPhysicss[i].bullet_triple); + SwapLongBytes(&objPhysicss[i].duration); + SwapShortBytes(&objPhysicss[i].p1.x); + SwapShortBytes(&objPhysicss[i].p1.y); + SwapShortBytes(&objPhysicss[i].p2.x); + SwapShortBytes(&objPhysicss[i].p2.y); + SwapShortBytes(&objPhysicss[i].p3.x); + SwapShortBytes(&objPhysicss[i].p3.y); + }*/ + + // Read in and convert the grenades. + REF_READ(id_num, idx++, objGrenades); + /* for (i=0; i < NUM_OBJECTS_GRENADE; i++) + { + SwapShortBytes(&objGrenades[i].id); + SwapShortBytes(&objGrenades[i].next); + SwapShortBytes(&objGrenades[i].prev); + SwapShortBytes(&objGrenades[i].flags); + SwapShortBytes(&objGrenades[i].timestamp); + }*/ + + // Read in and convert the drugs. + REF_READ(id_num, idx++, objDrugs); + /* for (i=0; i < NUM_OBJECTS_DRUG; i++) + { + SwapShortBytes(&objDrugs[i].id); + SwapShortBytes(&objDrugs[i].next); + SwapShortBytes(&objDrugs[i].prev); + }*/ + + // Read in and convert the hardwares. + REF_READ(id_num, idx++, objHardwares); + + // Read in and convert the softwares. + REF_READ(id_num, idx++, objSoftwares); + + // Read in and convert the big stuff. + REF_READ(id_num, idx++, objBigstuffs); + /* for (i=0; i < NUM_OBJECTS_BIGSTUFF; i++) + { + SwapShortBytes(&objBigstuffs[i].id); + SwapShortBytes(&objBigstuffs[i].next); + SwapShortBytes(&objBigstuffs[i].prev); + SwapShortBytes(&objBigstuffs[i].cosmetic_value); + SwapLongBytes(&objBigstuffs[i].data1); + SwapLongBytes(&objBigstuffs[i].data2); + }*/ + + // Read in and convert the small stuff. + REF_READ(id_num, idx++, objSmallstuffs); + /* for (i=0; i < NUM_OBJECTS_SMALLSTUFF; i++) + { + SwapShortBytes(&objSmallstuffs[i].id); + SwapShortBytes(&objSmallstuffs[i].next); + SwapShortBytes(&objSmallstuffs[i].prev); + SwapShortBytes(&objSmallstuffs[i].cosmetic_value); + SwapLongBytes(&objSmallstuffs[i].data1); + SwapLongBytes(&objSmallstuffs[i].data2); + }*/ + + // Read in and convert the fixtures. + REF_READ(id_num, idx++, objFixtures); + /* for (i=0; i < NUM_OBJECTS_FIXTURE; i++) + { + SwapShortBytes(&objFixtures[i].id); + SwapShortBytes(&objFixtures[i].next); + SwapShortBytes(&objFixtures[i].prev); + SwapLongBytes(&objFixtures[i].comparator); + SwapLongBytes(&objFixtures[i].p1); + SwapLongBytes(&objFixtures[i].p2); + SwapLongBytes(&objFixtures[i].p3); + SwapLongBytes(&objFixtures[i].p4); + SwapShortBytes(&objFixtures[i].access_level); + }*/ + + // Read in and convert the doors. + REF_READ(id_num, idx++, objDoors); + /* for (i=0; i < NUM_OBJECTS_DOOR; i++) + { + SwapShortBytes(&objDoors[i].id); + SwapShortBytes(&objDoors[i].next); + SwapShortBytes(&objDoors[i].prev); + SwapShortBytes(&objDoors[i].locked); + SwapShortBytes(&objDoors[i].other_half); + }*/ + + // Read in and convert the animating objects. + REF_READ(id_num, idx++, objAnimatings); + /* for (i=0; i < NUM_OBJECTS_ANIMATING; i++) + { + SwapShortBytes(&objAnimatings[i].id); + SwapShortBytes(&objAnimatings[i].next); + SwapShortBytes(&objAnimatings[i].prev); + SwapShortBytes(&objAnimatings[i].owner); + }*/ + + // Read in and convert the traps. + REF_READ(id_num, idx++, objTraps); + /* for (i=0; i < NUM_OBJECTS_TRAP; i++) + { + SwapShortBytes(&objTraps[i].id); + SwapShortBytes(&objTraps[i].next); + SwapShortBytes(&objTraps[i].prev); + SwapLongBytes(&objTraps[i].comparator); + SwapLongBytes(&objTraps[i].p1); + SwapLongBytes(&objTraps[i].p2); + SwapLongBytes(&objTraps[i].p3); + SwapLongBytes(&objTraps[i].p4); + } */ + + // Read in and convert the containers. + REF_READ(id_num, idx++, objContainers); + + // Read in and convert the critters. + REF_READ(id_num, idx++, objCritters); + /* for (i=0; i < NUM_OBJECTS_CRITTER; i++) + { + SwapShortBytes(&objCritters[i].id); + SwapShortBytes(&objCritters[i].next); + SwapShortBytes(&objCritters[i].prev); + SwapLongBytes(&objCritters[i].des_heading); + SwapLongBytes(&objCritters[i].des_speed); + SwapLongBytes(&objCritters[i].urgency); + SwapShortBytes(&objCritters[i].wait_frames); + SwapShortBytes(&objCritters[i].flags); + SwapLongBytes(&objCritters[i].attack_count); + SwapShortBytes(&objCritters[i].loot1); + SwapShortBytes(&objCritters[i].loot2); + SwapLongBytes(&objCritters[i].sidestep); + } */ + + //------------------------------- + // Read in the default objects. + //------------------------------- + + // Convert the default gun. + REF_READ(id_num, idx++, default_gun); + /* SwapShortBytes(&default_gun.id); + SwapShortBytes(&default_gun.next); + SwapShortBytes(&default_gun.prev);*/ + + // Convert the default ammo. + REF_READ(id_num, idx++, default_ammo); + /* SwapShortBytes(&default_ammo.id); + SwapShortBytes(&default_ammo.next); + SwapShortBytes(&default_ammo.prev);*/ + + // Read in and convert the physics objects. + REF_READ(id_num, idx++, default_physics); + /* SwapShortBytes(&default_physics.id); + SwapShortBytes(&default_physics.next); + SwapShortBytes(&default_physics.prev); + SwapShortBytes(&default_physics.owner); + SwapLongBytes(&default_physics.bullet_triple); + SwapLongBytes(&default_physics.duration); + SwapShortBytes(&default_physics.p1.x); + SwapShortBytes(&default_physics.p1.y); + SwapShortBytes(&default_physics.p2.x); + SwapShortBytes(&default_physics.p2.y); + SwapShortBytes(&default_physics.p3.x); + SwapShortBytes(&default_physics.p3.y);*/ + + // Convert the default grenade. + REF_READ(id_num, idx++, default_grenade); + /* SwapShortBytes(&default_grenade.id); + SwapShortBytes(&default_grenade.next); + SwapShortBytes(&default_grenade.prev); + SwapShortBytes(&default_grenade.flags); + SwapShortBytes(&default_grenade.timestamp);*/ + + // Convert the default drug. + REF_READ(id_num, idx++, default_drug); + /* SwapShortBytes(&default_drug.id); + SwapShortBytes(&default_drug.next); + SwapShortBytes(&default_drug.prev);*/ + + // Convert the default hardware. + REF_READ(id_num, idx++, default_hardware); + + // Convert the default software. + REF_READ(id_num, idx++, default_software); + + // Convert the default big stuff. + REF_READ(id_num, idx++, default_bigstuff); + /* SwapShortBytes(&default_bigstuff.id); + SwapShortBytes(&default_bigstuff.next); + SwapShortBytes(&default_bigstuff.prev); + SwapShortBytes(&default_bigstuff.cosmetic_value); + SwapLongBytes(&default_bigstuff.data1); + SwapLongBytes(&default_bigstuff.data2);*/ + + // Convert the default small stuff. + REF_READ(id_num, idx++, default_smallstuff); + /* SwapShortBytes(&default_smallstuff.id); + SwapShortBytes(&default_smallstuff.next); + SwapShortBytes(&default_smallstuff.prev); + SwapShortBytes(&default_smallstuff.cosmetic_value); + SwapLongBytes(&default_smallstuff.data1); + SwapLongBytes(&default_smallstuff.data2);*/ + + // Convert the fixture. + REF_READ(id_num, idx++, default_fixture); + /* SwapShortBytes(&default_fixture.id); + SwapShortBytes(&default_fixture.next); + SwapShortBytes(&default_fixture.prev); + SwapLongBytes(&default_fixture.comparator); + SwapLongBytes(&default_fixture.p1); + SwapLongBytes(&default_fixture.p2); + SwapLongBytes(&default_fixture.p3); + SwapLongBytes(&default_fixture.p4); + SwapShortBytes(&default_fixture.access_level);*/ + + // Convert the default door. + REF_READ(id_num, idx++, default_door); + /* SwapShortBytes(&default_door.id); + SwapShortBytes(&default_door.next); + SwapShortBytes(&default_door.prev); + SwapShortBytes(&default_door.locked); + SwapShortBytes(&default_door.other_half);*/ + + // Convert the default animating object. + REF_READ(id_num, idx++, default_animating); + /* SwapShortBytes(&default_animating.id); + SwapShortBytes(&default_animating.next); + SwapShortBytes(&default_animating.prev); + SwapShortBytes(&default_animating.owner);*/ + + // Read in and convert the traps. + REF_READ(id_num, idx++, default_trap); + /* SwapShortBytes(&default_trap.id); + SwapShortBytes(&default_trap.next); + SwapShortBytes(&default_trap.prev); + SwapLongBytes(&default_trap.comparator); + SwapLongBytes(&default_trap.p1); + SwapLongBytes(&default_trap.p2); + SwapLongBytes(&default_trap.p3); + SwapLongBytes(&default_trap.p4);*/ + + // Convert the default container. + REF_READ(id_num, idx++, default_container); + + // Convert the default critter. + REF_READ(id_num, idx++, default_critter); + /* SwapShortBytes(&default_critter.id); + SwapShortBytes(&default_critter.next); + SwapShortBytes(&default_critter.prev); + SwapLongBytes(&default_critter.des_heading); + SwapLongBytes(&default_critter.des_speed); + SwapLongBytes(&default_critter.urgency); + SwapShortBytes(&default_critter.wait_frames); + SwapShortBytes(&default_critter.flags); + SwapLongBytes(&default_critter.attack_count); + SwapShortBytes(&default_critter.loot1); + SwapShortBytes(&default_critter.loot2); + SwapLongBytes(&default_critter.sidestep);*/ + + idx++; + /* KLC - don't need this any more. + + REF_READ(id_num, idx++, version); + SwapLongBytes(&version); // Mac + if (version != MISC_SAVELOAD_VERSION_NUMBER && version < 5) + { + retval = ERR_NOEFFECT; + anim_counter = 0; + goto obj_out; + } + */ + idx++; // skip over resource where flickers once lived + + // Convert the anim textures. + REF_READ(id_num, idx++, animtextures); + + // Read in and convert the hack camera objects. + REF_READ(id_num, idx++, hack_cam_objs); + REF_READ(id_num, idx++, hack_cam_surrogates); + /* for (i = 0; i < NUM_HACK_CAMERAS; i++) + { + SwapShortBytes(&hack_cam_objs[i]); + SwapShortBytes(&hack_cam_surrogates[i]); + }*/ + + savedMaps = 0; + for (i = 0; i < NUM_O_AMAP; i++) { + if (oAMap(i)->init) { + savedMaps |= (1 << i); + amap_settings_copy(oAMap(i), &saveAMaps[i]); + amap_invalidate(i); + } + } + + // Get other level data at next id + REF_READ(id_num, idx++, level_gamedata); + +#ifdef SAVE_AUTOMAP_STRINGS + { + int amap_magic_num; + char *cp = amap_str_reref(0); + REF_READ(id_num, idx++, *cp); + // REF_READ(id_num, idx++, amap_str_reref(0)); old way + REF_READ(id_num, idx++, amap_magic_num); + // SwapLongBytes(&amap_magic_num); + amap_str_startup(amap_magic_num); + } +#endif + + idx++; // Doesn't appear that this does anything + /* + REF_READ(id_num, idx++, player_edms); + SwapLongBytes(&player_edms.X); + SwapLongBytes(&player_edms.Y); + SwapLongBytes(&player_edms.Z); + SwapLongBytes(&player_edms.alpha); + SwapLongBytes(&player_edms.beta); + SwapLongBytes(&player_edms.gamma); + SwapLongBytes(&player_edms.X_dot); + SwapLongBytes(&player_edms.Y_dot); + SwapLongBytes(&player_edms.Z_dot); + SwapLongBytes(&player_edms.alpha_dot); + SwapLongBytes(&player_edms.beta_dot); + SwapLongBytes(&player_edms.gamma_dot); + */ + + REF_READ(id_num, idx++, paths); + /* for(i=0; i < MAX_PATHS; i++) + { + SwapShortBytes(&paths[i].source.x); + SwapShortBytes(&paths[i].source.y); + SwapShortBytes(&paths[i].dest.x); + SwapShortBytes(&paths[i].dest.y); + }*/ + REF_READ(id_num, idx++, used_paths); + // SwapShortBytes(&used_paths); + + REF_READ(id_num, idx++, animlist); + + REF_READ(id_num, idx++, anim_counter); + // SwapShortBytes(&anim_counter); + + REF_READ(id_num, idx++, h_sems); // Unbelievably, no conversion needed. + +obj_out: + bounds.ul.x = bounds.ul.y = 0; + bounds.lr.x = global_fullmap->x_size; + bounds.lr.y = global_fullmap->y_size; + + rendedit_process_tilemap(global_fullmap, &bounds, TRUE); + + for (i = 0; i < MAX_OBJ; i++) + physics_handle_id[i] = OBJ_NULL; + physics_handle_max = -1; + + if (anim_counter == 0) + do_anims = TRUE; + + for (ObjID oid = (objs[OBJ_NULL]).headused; oid != OBJ_NULL; oid = objs[oid].next) { + switch (objs[oid].obclass) { + case CLASS_DOOR: + set_door_data(oid); + break; + } + + if (do_anims && ANIM_3D(ObjProps[OPNUM(oid)].bitmap_3d)) { + switch (TRIP2CL(ID2TRIP(oid))) { + case CLASS_BIGSTUFF: + case CLASS_SMALLSTUFF: + obj_screen_animate(oid); + break; + default: + add_obj_to_animlist(oid, REPEAT_3D(ObjProps[OPNUM(oid)].bitmap_3d), FALSE, FALSE, 0, 0, 0, 0); + break; + } + } + + objs[oid].info.ph = -1; + if (objs[oid].loc.x != 0xFFFF) { + obj_move_to(oid, &objs[oid].loc, TRUE); + } + + // sleep the object (this may become "settle" the object) + if (objs[oid].info.ph != -1) { + cit_sleeper_callback(objs[oid].info.ph); + edms_delete_go(); + } + } + + // DO NOT call this from here. We haven't necessarily yet set + // player_struct.level, which means the wrong shodometer quest + // variable gets set!!! + // + // compute_shodometer_value(FALSE); + + if (make_player) { + extern int score_playing; + obj_create_player(&plr_loc); + if (object_version > 9) { + // Regenerate physics state from player_state here + } + //��� if (music_on && (score_playing != ELEVATOR_ZONE)) + //��� load_score_for_location(PLAYER_BIN_X,PLAYER_BIN_Y); + } + +out: + ResCloseFile(fd); + + reset_pathfinding(); + old_bits = -1; + + trigger_check = TRUE; + + load_dynamic_memory(dynmem_mask); + load_level_data(); + + for (i = 0; i < NUM_O_AMAP; i++) { + if (!oAMap(i)->init && (savedMaps & (1 << i))) { + automap_init(player_struct.hardwarez[CPTRIP(NAV_HARD_TRIPLE)], i); + amap_settings_copy(&saveAMaps[i], oAMap(i)); + } + } + reload_motion_cursors(global_fullmap->cyber); + + // Debug print the map +#ifdef DEBUG_MAP_PRINT + for (int y = 0; y < 64; y++) { + for (int x = 0; x < 64; x++) { + uchar tiletype = global_fullmap->map[x + y * 64].tiletype; + if (tiletype == 0) + printf(" "); + else + printf(" %i", tiletype); + } + printf("\n"); + } +#endif + + // KLC physics_warmup(); + + end_wait(); + /*��� { + extern void spoof_mouse_event(); + spoof_mouse_event(); + } + _MARK_("load_current_map:End"); + */ + + return retval; +} diff --git a/engine/src/GameSrc/schedule.c b/engine/src/GameSrc/schedule.c new file mode 100644 index 0000000..852cb38 --- /dev/null +++ b/engine/src/GameSrc/schedule.c @@ -0,0 +1,481 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/schedule.c $ + * $Revision: 1.60 $ + * $Author: xemu $ + * $Date: 1994/11/22 15:39:20 $ + * + * + */ + +#include "map.h" +#include "player.h" +#include "schedule.h" +#include "grenades.h" +#include "invent.h" +#include "leanmetr.h" +#include "wares.h" +#include "objsim.h" +#include "objclass.h" +#include "otrip.h" +#include "faketime.h" +#include "trigger.h" +#include "objwpn.h" +#include "combat.h" +#include "objgame.h" +#include "objuse.h" +#include "rendtool.h" +#include "gamesys.h" +#include "frparams.h" +#include "damage.h" +#include "mainloop.h" +#include "colors.h" +#include "doorparm.h" +#include "mfdext.h" + +#include "musicai.h" // for the explosion +#include "sfxlist.h" +#include "effect.h" +#include "objprop.h" + +#include "frflags.h" +#include "frprotox.h" + +// this will need to be initialized. +height_semaphor h_sems[NUM_HEIGHT_SEMAPHORS]; + +// --------- +// INTERNALS +// --------- +int expand_tstamp(ushort s); +int compare_tstamps(ushort t1, ushort t2); + +void unregister_h_event(char sem); + +void null_event_handler(Schedule *s, SchedEvent *ev); +void trap_event_handler(Schedule *s, SchedEvent *ev); +void height_event_handler(Schedule *s, SchedEvent *ev); +void door_event_handler(Schedule *s, SchedEvent *ev); +void grenade_event_handler(Schedule *s, SchedEvent *ev); +void explosion_event_handler(Schedule *s, SchedEvent *ev); +void exposure_event_handler(Schedule *s, SchedEvent *ev); +void light_event_handler(Schedule *s, SchedEvent *ev); +void bark_event_handler(Schedule *s, SchedEvent *ev); +void email_event_handler(Schedule *s, SchedEvent *ev); + + +// comparison function. +// 8/27/94 THIS FIX FOR WRAPPING BUG MAKES THE COMPARE FUNCTION MUCH LESS GENERAL. +// I.e. it relies on the fact that timestamps are in terms of TICKS2TSTAMP(player_struct.game_time) +// in the future, this should be fixed by adding more bits to the timestamp so it doesn't wrap, and +// then you can have schedule that don't go on gametime. + +#define MAX_USHORT (0xFFFF) + +int expand_tstamp(ushort s) { + int gametime = TICKS2TSTAMP(player_struct.game_time); + int stamp = s; + if (stamp <= gametime - (MAX_USHORT / 2)) + stamp += MAX_USHORT; + else if (stamp >= gametime + (MAX_USHORT / 2)) + stamp -= MAX_USHORT; + return stamp; +} + +int compare_tstamps(ushort t1, ushort t2) { return expand_tstamp(t1) - expand_tstamp(t2); } + +int compare_events(void *e1, void *e2) { + return compare_tstamps(((SchedEvent *)e1)->timestamp, ((SchedEvent *)e2)->timestamp); +} + +// ----------------- +// SUPPORT FUNCTIONS +// ----------------- + +void stop_terrain_elevator_sound(short sem); + +uchar register_h_event(uchar x, uchar y, uchar floor, char *sem, char *key, uchar no_sfx) { + int i, fr; + + fr = -1; + for (i = 0; i < NUM_HEIGHT_SEMAPHORS; i++) { + if (h_sems[i].inuse == 0) + fr = i; + else if (h_sems[i].x == x && h_sems[i].y == y && h_sems[i].floor == floor) { + + // conflict. Take over the old semaphor. + + stop_terrain_elevator_sound(i); + + if (h_sems[i].key < MAX_HSEM_KEY) + h_sems[i].key++; + else + h_sems[i].key = 0; + + *key = h_sems[i].key; + *sem = i; + if (!no_sfx) { + h_sems[i].inuse = 2; + if (play_digi_fx_loc(SFX_TERRAIN_ELEV_LOOP, -1, x << 8, y << 8) < 0) + h_sems[i].inuse = 1; + } else + h_sems[i].inuse = 1; + return (TRUE); + } + } + // no conflict. Allocate new semaphor. + if (fr < 0) { + // no free semaphor. Fail. + return (FALSE); + } else { + h_sems[fr].x = x; + h_sems[fr].y = y; + h_sems[fr].floor = floor; + h_sems[fr].key = 0; + *key = h_sems[fr].key; + *sem = fr; + if (!no_sfx) { + h_sems[fr].inuse = 2; + if (play_digi_fx_loc(SFX_TERRAIN_ELEV_LOOP, -1, x << 8, y << 8) < 0) + h_sems[fr].inuse = 1; + } else + h_sems[fr].inuse = 1; + return (TRUE); + } +} + +void unregister_h_event(char sem) { + if (sem < 0 || sem >= NUM_HEIGHT_SEMAPHORS) + return; + + stop_terrain_elevator_sound(sem); + + h_sems[sem].inuse = 0; +} + +// -------------- +// EVENT HANDLERS +// -------------- + +void null_event_handler(Schedule *s, SchedEvent *ev) {} + +void trap_event_handler(Schedule *s, SchedEvent *ev) { + ObjID id1, id2; + uchar dummy; + + id1 = ((TrapSchedEvent *)ev)->target_id; + id2 = ((TrapSchedEvent *)ev)->source_id; + + do_multi_stuff(id1); + if (id2 != -1) + trap_activate(id2, &dummy); +} + +#define FLOOR_HEIGHT_DELTA 0x4 +void height_event_handler(Schedule *s, SchedEvent *ev) { + MapElem *pme; + HeightSchedEvent hse = *(HeightSchedEvent *)ev; + short x, y; + LGRect bounds; + char ht, sign = (hse.steps_remaining > 0) ? 1 : -1; + + // has someone else claimed this square? + if (h_sems[hse.semaphor].key != hse.key) { + return; + } + x = h_sems[hse.semaphor].x; + y = h_sems[hse.semaphor].y; + pme = MAP_GET_XY(x, y); + // look at top bit of step_size to determine floor or ceiling + if (hse.type == CEIL_SCHED_EVENT) { + ht = me_height_ceil(pme); + me_height_ceil_set(pme, ht + sign); + } else { + ObjRefID oref; + ObjID id; + ObjLoc newloc; + ht = me_height_flr(pme); + // Change height of objects, as well... + oref = me_objref(pme); + while (oref != OBJ_REF_NULL) { + // If we are on the old height, move us to the new height + id = objRefs[oref].obj; + if (abs(obj_floor_height(id) - objs[id].loc.z) < FLOOR_HEIGHT_DELTA) { + if (id == PLAYER_OBJ) { + slam_posture_meter_state(); + } else if (ObjProps[OPNUM(id)].physics_model) { + newloc = objs[id].loc; + newloc.z = obj_floor_compute(id, ht + sign); + obj_move_to(id, &newloc, TRUE); + } + } + // iterate + oref = objRefs[oref].next; + } + // Crank us to new height; + me_height_flr_set(pme, ht + sign); + } + + hse.steps_remaining -= sign; + if (hse.steps_remaining != 0) { + hse.timestamp = TICKS2TSTAMP(player_struct.game_time + (CIT_CYCLE * HEIGHT_STEP_TIME) / HEIGHT_TIME_UNIT); + schedule_event(&(global_fullmap->sched[MAP_SCHEDULE_GAMETIME]), (SchedEvent *)&hse); + } else { + unregister_h_event(hse.semaphor); + } + + { + bounds.ul.x = bounds.lr.x = x; + bounds.ul.y = bounds.lr.y = y; + rendedit_process_tilemap(global_fullmap, &bounds, FALSE); + } +} + +#define ANTENNA_DESTROYED_QVAR 0x2 +#define ANTENNAE_ALL_GONE_QBIT 0x99 +#define PLOTWARE_QVAR 0x9 +#define NUM_ANTENNAE_TO_DESTROY 4 + +void door_event_handler(Schedule *s, SchedEvent *ev) { + ObjID id; + short old_dest; + ushort code, curr_code; + + id = ((DoorSchedEvent *)ev)->door_id; + code = ((DoorSchedEvent *)ev)->secret_code; + if (objs[id].obclass == CLASS_DOOR) { + // construct code out of top 2 bits of inst_flags + curr_code = objs[id].info.inst_flags >> 6; + + // Make sure that we actually care about this autoclose event + if (code == curr_code) { + // note the secret dont-autoclose-me-even-if-I-already- + // have-an-autoclose-scheduled cookie. + if (!(DOOR_REALLY_CLOSED(id) || door_moving(id, TRUE) || + objDoors[objs[id].specID].autoclose_time == NEVER_AUTOCLOSE_COOKIE)) + object_use(id, FALSE, OBJ_NULL); + } + } else if (ID2TRIP(id) == PLAS_ANTENNA_TRIPLE) { + ObjID ground0, p3obj; + ObjLoc blastLoc; + ExplosionData *kaboom; + extern short fr_sfx_time; + + // An earth-shattering kaboom. + // turn the panel into a destroyed one + objs[id].info.type += 1; + + // flash the screen + fr_global_mod_flag(FR_SOLIDFR_SLDCLR, FR_SOLIDFR_MASK); + fr_solidfr_color = GRENADE_COLOR; + + // shake yer bootie + fr_global_mod_flag(FR_SFX_SHAKE, FR_SFX_MASK); + fr_sfx_time = CIT_CYCLE << 1; // 2 seconds of shake + + // qvar tricks + old_dest = QUESTVAR_GET(ANTENNA_DESTROYED_QVAR); + QUESTVAR_SET(ANTENNA_DESTROYED_QVAR, old_dest + 1); + if (old_dest + 1 >= NUM_ANTENNAE_TO_DESTROY) { + QUESTVAR_SET(PLOTWARE_QVAR, QUESTVAR_GET(PLOTWARE_QVAR) + 1); + QUESTBIT_ON(ANTENNAE_ALL_GONE_QBIT); + do_multi_stuff(objFixtures[objs[id].specID].p1); + } + + kaboom = &game_explosions[LARGE_GAME_EXPL]; + p3obj = ground0 = objFixtures[objs[id].specID].p3; + if (ground0 == OBJ_NULL) + ground0 = id; + ObjLocCopy(objs[ground0].loc, blastLoc); + blastLoc.z = obj_height_from_fix(fix_from_obj_height(ground0) + 4 * RAYCAST_ATTACK_SIZE); + if (p3obj) { + ADD_DESTROYED_OBJECT(p3obj); + destroy_destroyed_objects(); + } + do_explosion(blastLoc, ground0, M_EXPL2, kaboom); + fr_global_mod_flag(FR_SFX_SHAKE, FR_SFX_MASK); + fr_sfx_time = CIT_CYCLE << 1; + play_digi_fx_obj(SFX_EXPLOSION_1, 1, id); + mfd_notify_func(MFD_PLOTWARE_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, TRUE); + } +} + +void grenade_event_handler(Schedule *s, SchedEvent *ev) { + ObjID id; + ubyte unique_id; + + id = ((GrenSchedEvent *)ev)->gren_id; + + if (is_obj_destroyed(id)) + return; + + // make sure we have a real grenade + unique_id = objGrenades[objs[id].specID].unique_id; + if ((((GrenSchedEvent *)ev)->unique_id == unique_id) && unique_id) { + if (GrenadeProps[CPNUM(id)].flags & GREN_TIMING_TYPE) + ADD_DESTROYED_OBJECT(id); + // do_grenade_explosion(id, TRUE); + else + EDMS_obey_collisions(objs[id].info.ph); + } +} + +void explosion_event_handler(Schedule *s, SchedEvent *ev) {} + +void exposure_event_handler(Schedule *s, SchedEvent *ev) { + SchedExposeData *xd = (SchedExposeData *)&ev->data; + int count = xd->count; + int damage = xd->damage; + if (damage < 0) + damage = (damage - 1) / 2; + else + damage = (damage + 1) / 2; + expose_player(damage, xd->type, 0); + // count --; + if (damage != xd->damage) + // if (count > 0) + { + SchedEvent copy; + xd->damage -= damage; + xd->count = (ubyte)count; + copy = *ev; + copy.timestamp += xd->tsecs; + schedule_event(s, ©); + } +} + +extern uchar muzzle_fire_light; + +void light_event_handler(Schedule *s, SchedEvent *ev) { + muzzle_fire_light = FALSE; + + // is the player's lantern on - if not turn our faux lantern off, otherwise + // just turn the lantern on - to reset its values + if (!(player_struct.hardwarez_status[CPTRIP(LANTERN_HARD_TRIPLE)] & WARE_ON)) + lamp_turnoff(TRUE, FALSE); + else + lamp_turnon(TRUE, FALSE); +} + +void bark_event_handler(Schedule *s, SchedEvent *ev) { + ubyte mfd_id; + ubyte mfd_get_func(ubyte mfd_id, ubyte s); + + // timeout bark, if it's still there at all. + for (mfd_id = 0; mfd_id < NUM_MFDS; mfd_id++) { + if (mfd_get_func(mfd_id, MFD_INFO_SLOT) == MFD_BARK_FUNC) { + check_panel_ref(TRUE); + break; + } + } +} + +void email_event_handler(Schedule *s, SchedEvent *ev) { + EmailSchedEvent *e = (EmailSchedEvent *)ev; + add_email_datamunge(e->datamunge, TRUE); +} + +// HERE IS THE ARRAY OF ALL EVENT HANDLERS + +static SchedHandler sched_handlers[] = { + null_event_handler, grenade_event_handler, explosion_event_handler, door_event_handler, + trap_event_handler, exposure_event_handler, height_event_handler, height_event_handler, + light_event_handler, bark_event_handler, email_event_handler, +}; + +#define NUM_EVENT_TYPES (sizeof(sched_handlers) / sizeof(SchedHandler)) + +// --------- +// EXTERNALS +// --------- + +static ushort current_tstamp = 0; + +errtype schedule_init(Schedule *s, int size, uchar grow) { + TRACE("%s: schedule_init", __FUNCTION__); + return pqueue_init(&s->queue, size, sizeof(SchedEvent), compare_events, grow); +} + +errtype schedule_free(Schedule *s) { + TRACE("%s: schedule_free", __FUNCTION__); + return pqueue_destroy(&s->queue); +} + +errtype schedule_event(Schedule *s, SchedEvent *ev) { + errtype retval = OK; + if (!time_passes) + return ERR_NOEFFECT; + if (current_tstamp > 0 && compare_tstamps(ev->timestamp, current_tstamp) < 0) { + return ERR_NOEFFECT; + } + + TRACE("%s: Scheduling an event.", __FUNCTION__); + + retval = pqueue_insert(&s->queue, ev); + if (retval != OK) { + printf("Could not schedule event?\n"); + } + return retval; +} + +errtype schedule_reset(Schedule *s) { + s->queue.fullness = 0; + return OK; +} + +void reset_schedules(void) { + int i; + for (i = 0; i < NUM_MAP_SCHEDULES; i++) + schedule_reset(&global_fullmap->sched[i]); + schedule_reset(&game_seconds_schedule); +} + +errtype schedule_run(Schedule *s, ushort time) { + SchedEvent ev; + errtype err; + current_tstamp = time; + for (err = pqueue_least(&s->queue, &ev); err == OK && compare_tstamps(ev.timestamp, time) < 0; + err = pqueue_least(&s->queue, &ev)) { + if (ev.type < NUM_EVENT_TYPES) + sched_handlers[ev.type](s, &ev); + pqueue_extract(&s->queue, &ev); + } + current_tstamp = 0; + return OK; +} + +void run_schedules(void) { + schedule_run(&global_fullmap->sched[MAP_SCHEDULE_GAMETIME], TICKS2TSTAMP(player_struct.game_time)); + schedule_run(&game_seconds_schedule, TICKS2TSTAMP(player_struct.game_time)); +} + +/* +uchar schedule_test_hotkey(short keycode, ulong context, void* data) +{ + SchedEvent e; +#ifndef NO_DUMMIES + int dummy; dummy = keycode + context + (int)data; +#endif + e.timestamp = player_struct.game_time/CIT_CYCLE + 100; + e.type = 0; + schedule_event(&game_seconds_schedule,&e); + return TRUE; +} +*/ diff --git a/engine/src/GameSrc/screen.c b/engine/src/GameSrc/screen.c new file mode 100644 index 0000000..3c3639f --- /dev/null +++ b/engine/src/GameSrc/screen.c @@ -0,0 +1,407 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/screen.c $ + * $Revision: 1.95 $ + * $Author: mahk $ + * $Date: 1994/11/22 22:53:04 $ + */ + +// Source code for the Citadel Screen routines + +#ifdef SVGA_SUPPORT +#include "fullscrn.h" +#endif +#include "frcursors.h" +#include "game_screen.h" +#include "tools.h" +#include "gamescr.h" +#include "mfdext.h" +#include "sideicon.h" +#include "status.h" +#include "input.h" +#include "mainloop.h" +#include "frflags.h" +#include "citres.h" +#include "gr2ss.h" +#include "invent.h" +#include "invdims.h" +#include "leanmetr.h" +#include "wrapper.h" +#include "Shock.h" + +/* +KLC - stereo +#include "LG_menus.h" // can't be just "menus" since thats a mac include +#include "config.h" +#include "inp6d.h" +#include "i6dvideo.h" +*/ + +#define STATUS_X 4 +#define STATUS_Y 1 +#define STATUS_HEIGHT 20 +#define STATUS_WIDTH 312 +#define GAMESCR_BIO 0 + +#ifdef CURSOR_BACKUPS +#include "loopdbg.h" +#include "string.h" +#endif + +#define CFG_TIME_VAR "time_passes" + +LGRect Inv_rect; + +#ifdef SVGA_SUPPORT +extern grs_screen *svga_screen; +extern frc *svga_render_context; +extern short svga_mode_data[]; +extern short mode_id; +#endif + +LGRect *inventory_rect = &Inv_rect, *status_rect, mess_rect; +LGRect real_status_rect; +LGRegion mv_region_data, msg_region_data, status_region_data; +uiSlab main_slab; +LGRegion *msg_region; + +uchar *default_font_buf; +LGRegion *root_region, *mainview_region, *status_region, *inventory_region_game; +LGRegion *pagebutton_region_game; +LGCursor globcursor, wait_cursor, fire_cursor; +frc *normal_game_fr_context; + +errtype _screen_init_mouse(LGRegion *r, uiSlab *slab, uchar do_init); +errtype _screen_background(void); + +byte pal_shf_id; +LGCursor vmail_cursor; + +LGRect fscrn_rect = {{0, 0}, {320, 200}}; +LGRect svga_rect = {{0, 0}, {1024, 768}}; + +LGRegion root_region_data; +LGRegion *root_region = &root_region_data; + +// prototypes + +void generic_reg_init(uchar create_it, LGRegion *reg, LGRect *rct, uiSlab *slb, uiHandlerProc key_h, + uiHandlerProc maus_h) { + int callid; + if (rct == NULL) + rct = &fscrn_rect; + if (create_it) + region_create(NULL, reg, rct, 0, 0, REG_USER_CONTROLLED | AUTODESTROY_FLAG, NULL, NULL, NULL, NULL); + if (key_h != NULL) + uiInstallRegionHandler(reg, UI_EVENT_KBD_COOKED, key_h, 0, &callid); + if (maus_h != NULL) + uiInstallRegionHandler(reg, UI_EVENT_MOUSE, maus_h, 0, &callid); + if (slb != NULL) { + uiMakeSlab(slb, reg, &globcursor); + uiGrabSlabFocus(slb, reg, ALL_EVENTS); + } +} + +// Initialize the main game screen and draw it's initial state +LGRect mainview_rect; +errtype screen_init(void) { + extern LGRegion *fullview_region; + int callid; + extern void (*ui_mouse_convert)(short *px, short *py, uchar down); + extern void (*ui_mouse_convert_round)(short *px, short *py, uchar down); + + // God this is stupid, maybe I'll get it right next project + status_rect = &real_status_rect; + + // Create all the appropriate regions for to make input happen + // Root LGRegion + generic_reg_init(TRUE, root_region, NULL, NULL, NULL, NULL); + + // Main view LGRegion + mainview_rect.ul.x = SCREEN_VIEW_X; + mainview_rect.ul.y = SCREEN_VIEW_Y; + mainview_rect.lr.x = mainview_rect.ul.x + SCREEN_VIEW_WIDTH; + mainview_rect.lr.y = mainview_rect.ul.y + SCREEN_VIEW_HEIGHT; + mainview_region = &mv_region_data; + region_create(root_region, mainview_region, &mainview_rect, 0, 0, REG_USER_CONTROLLED | AUTODESTROY_FLAG, NULL, + NULL, NULL, NULL); + + // Initialize da mousie + _screen_init_mouse(root_region, &main_slab, TRUE); // KLC - moved here + + install_motion_mouse_handler(mainview_region, NULL); + +#ifdef SVGA_SUPPORT + gr2ss_register_init(0, 320, 200); + gr2ss_register_mode(0, 320, 400); + gr2ss_register_mode(0, 640, 400); + gr2ss_register_mode(0, 640, 480); + gr2ss_register_mode(0, 1024, 768); +#ifdef STEREO_SUPPORT + if (i6d_device == I6D_VFX1) { + Warning(("size = %d, %d!\n", i6d_ss->scr_w, i6d_ss->scr_h)); + gr2ss_register_mode(0, 320, 240); + gr2ss_register_mode(0, 640, 240); // VFX Hack Mode + } else { + gr2ss_register_mode(0, 320, 100); // note secret stereo mode + gr2ss_register_mode(0, 640, 350); // CTM Hack Mode + } +#endif +#endif + + // Install mouse converter... + ui_mouse_convert = ss_mouse_convert; + ui_mouse_convert_round = ss_mouse_convert_round; + + // Inventory LGRegion + create_invent_region(root_region, &pagebutton_region_game, &inventory_region_game); + screen_init_mfd(FALSE); // sets up regions + mouse callbacks + screen_init_side_icons(root_region); + + // Message-line LGRegion + mess_rect.ul.x = GAME_MESSAGE_X; + mess_rect.ul.y = GAME_MESSAGE_Y; + mess_rect.lr.x = mess_rect.ul.x + INVENTORY_PANEL_WIDTH; + mess_rect.lr.y = mess_rect.ul.y + 10; + msg_region = &msg_region_data; + region_create(root_region, msg_region, &mess_rect, 0, 0, REG_USER_CONTROLLED | AUTODESTROY_FLAG, NULL, NULL, NULL, + NULL); + + // Status LGRegion + status_rect->ul.x = STATUS_X; + status_rect->ul.y = STATUS_Y; + status_rect->lr.x = status_rect->ul.x + STATUS_WIDTH; + status_rect->lr.y = status_rect->ul.y + STATUS_HEIGHT; + status_region = &status_region_data; + region_create(root_region, status_region, status_rect, 0, 0, REG_USER_CONTROLLED | AUTODESTROY_FLAG, NULL, NULL, + NULL, NULL); + + status_bio_init(); + + // lean-o-meter + init_posture_meters(root_region, FALSE); + + // option LGCursor LGRegion + wrapper_create_mouse_region(root_region); + + // Install basic input handlers + uiInstallRegionHandler(root_region, UI_EVENT_KBD_COOKED, &main_kb_callback, 0, &callid); + + install_motion_keyboard_handler(root_region); + + // we already do this in generic_reg_init + // Grab all input! + uiGrabSlabFocus(&main_slab, root_region, ALL_EVENTS); + + return (OK); +} + +extern void game_redrop_rad(int rad_mod); + +void screen_start() { + extern LGRegion *pagebutton_region, *inventory_region; + + /* Not yet + // Check the config system to see if time should automatically be running + if (config_get_raw(CFG_TIME_VAR, NULL, 0)) time_passes = TRUE; + */ + + HotkeyContext = DEMO_CONTEXT; + uiSetCurrentSlab(&main_slab); + inventory_region = inventory_region_game; + pagebutton_region = pagebutton_region_game; + + // A rather strange function for a Mac program, but we'll keep it. + change_svga_screen_mode(); + + gr_clear(0x00); + status_bio_set(GAMESCR_BIO); + screen_draw(); + // KLC uiShowMouse(NULL); + chg_set_flg(DEMOVIEW_UPDATE); + chg_set_flg(MFD_UPDATE); + chg_set_flg(VITALS_UPDATE); + status_bio_start(); + status_vitals_update(TRUE); +// KLC - not needed anymore mouse_unconstrain(); +#ifdef PALFX_FADES +// later if (pal_fx_on) palfx_fade_up(FALSE); +#endif + + CaptureMouse(true); + SetMotionCursorForMouseXY(); +} + +void screen_exit() { +#ifdef SVGA_SUPPORT + uchar cur_pal[768]; + extern grs_screen *cit_screen; + uchar *s_table; +#endif + + status_bio_end(); + uiHideMouse(NULL); + +#ifdef SVGA_SUPPORT + if ((_new_mode != GAME_LOOP) && (_new_mode != FULLSCREEN_LOOP)) { + + s_table = gr_get_light_tab(); + gr_get_pal(0, 256, &cur_pal[0]); + gr_set_screen(cit_screen); + convert_use_mode = 0; + // KLC change_svga_cursors(); + // KLC status_bio_update_screenmode(); + } +#endif + if (_new_mode == -1) + return; + + /* KLC + #ifdef PALFX_FADES + if (pal_fx_on) + palfx_fade_down(); + else { + gr_set_fcolor(BLACK); + gr_rect(0,0,320,200); + } + #endif + + #ifdef SVGA_SUPPORT + if ((_new_mode != GAME_LOOP) && (_new_mode != FULLSCREEN_LOOP)) + { + gr_set_pal(0,256,&cur_pal[0]); + gr_set_light_tab(s_table); + } + #endif + */ +} + +// Draw the whole durned screen! +// Note that this algorithm does NOT draw the main view area, +// or the associated HUD -- +// that is handled by direct calls from the main loop + +errtype screen_draw(void) { + // Just go through and draw all the component parts.... + // In theory, they should all be clever enough to redraw + // efficiently. Alternatively, this should only be called + // very few times, and in general just the changing parts + // get a signal to draw themselves. + uiHideMouse(NULL); + _screen_background(); + + screen_init_mfd_draw(); + side_icon_expose_all(); + + inventory_clear(); + inventory_draw(); + status_bio_draw(); + status_vitals_init(); + status_vitals_update(TRUE); + update_meters(TRUE); + uiShowMouse(NULL); + + return (OK); +} + +errtype _screen_background(void) { + Ref back_id = REF_IMG_bmGamescreenBackground; + draw_raw_res_bm_temp(back_id, 0, 0); + // draw_hires_resource_bm(REF_IMG_bmGamescreenBackground, 0, 0); + return (OK); +} + +// Stop doing graphics things +errtype screen_shutdown(void) { + region_destroy(status_region, FALSE); + region_destroy(msg_region, FALSE); + region_destroy(mainview_region, FALSE); + + // Free(status_rect); umm, see, now we point at it, so dont free it + return (OK); +} + +static grs_bitmap _targbm; +static grs_bitmap _waitbm; +static grs_bitmap _firebm; +static grs_bitmap _vmailbm; +extern grs_bitmap slider_cursor_bmap; +extern LGCursor slider_cursor; + +errtype load_misc_cursors(void) { + if (_targbm.bits != NULL) { + free(_targbm.bits); + memset(&globcursor, 0, sizeof(LGCursor)); + memset(&_targbm, 0, sizeof(grs_bitmap)); + } + load_res_bitmap_cursor(&globcursor, &_targbm, REF_IMG_bmTargetCursor, TRUE); + + if (_waitbm.bits != NULL) { + free(_waitbm.bits); + memset(&wait_cursor, 0, sizeof(LGCursor)); + memset(&_waitbm, 0, sizeof(grs_bitmap)); + } + load_res_bitmap_cursor(&wait_cursor, &_waitbm, REF_IMG_bmWaitCursor, TRUE); + + if (_firebm.bits != NULL) { + free(_firebm.bits); + memset(&fire_cursor, 0, sizeof(LGCursor)); + memset(&_firebm, 0, sizeof(grs_bitmap)); + } + load_res_bitmap_cursor(&fire_cursor, &_firebm, REF_IMG_bmFireCursor, TRUE); + + if (_vmailbm.bits != NULL) { + free(_vmailbm.bits); + memset(&vmail_cursor, 0, sizeof(LGCursor)); + memset(&_vmailbm, 0, sizeof(grs_bitmap)); + } + load_res_bitmap_cursor(&vmail_cursor, &_vmailbm, REF_IMG_bmVmailCursor, TRUE); + + if (slider_cursor_bmap.bits != NULL) { + free(slider_cursor_bmap.bits); + memset(&slider_cursor, 0, sizeof(LGCursor)); + memset(&slider_cursor_bmap, 0, sizeof(grs_bitmap)); + } + load_res_bitmap_cursor(&slider_cursor, &slider_cursor_bmap, REF_IMG_bmMfdPhaserCursor, TRUE); + + return OK; +} + +errtype _screen_init_mouse(LGRegion *r, uiSlab *slab, uchar do_init) { + + ui_init_cursors(); // KLC - do this here, take out of uiInit. + load_misc_cursors(); + + // Entirely arbitrarily, screen does the uiInit. + // only one of the slab creators needs to do this. + uiMakeSlab(slab, r, &globcursor); + if (do_init) + uiInit(slab); +#ifdef INPUT_CHAINING +/* Êdo we ever need this? + if (config_get_raw(CHAINING_VAR,NULL,0)) + kb_set_flags(kb_get_flags()|KBF_CHAIN);*/ +#endif // INPUT_CHAINING + + uiHideMouse(NULL); + // KLC - no longer needed if (mouse_put_xy(100,100) == ERR_NODEV) + // critical_error(CRITERR_CFG|0); + return (OK); +} diff --git a/engine/src/GameSrc/setup.c b/engine/src/GameSrc/setup.c new file mode 100644 index 0000000..f27033a --- /dev/null +++ b/engine/src/GameSrc/setup.c @@ -0,0 +1,1555 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/setup.c $ + * $Revision: 1.141 $ + * $Author: dc $ + * $Date: 1994/11/23 00:05:51 $ + */ + +#include +#include + +// TODO: extract this into a compatibility header +#ifdef _MSC_VER +#ifndef F_OK +#define F_OK 0 +#endif +#else +#include +#endif + +#ifdef SVGA_SUPPORT +#include "fullscrn.h" +#endif + +#include "archiveformat.h" +#include "setup.h" +#include "colors.h" +#include "cybmem.h" +#include "diffq.h" +#include "gamewrap.h" +#include "gr2ss.h" +#include "hud.h" +#include "init.h" +#include "miscqvar.h" +#include "player.h" +#include "version.h" +#include "wrapper.h" +#include "verify.h" +#include "sdl_events.h" +#include "cybstrng.h" +#include "gamestrn.h" + +#include "mainloop.h" +#include "tools.h" +#include "input.h" +#include "game_screen.h" +#include "hkeyfunc.h" +#include "loops.h" +#include "keydefs.h" +#include "status.h" +#include "cutsloop.h" +#include "musicai.h" +#include "palfx.h" +#include "gamescr.h" +#include "faketime.h" + +#include "2d.h" +#include "splash.h" +#include "splshpal.h" +#include "tickcount.h" +#include "MacTune.h" +#include "Shock.h" +#include "Xmi.h" + +#ifdef PLAYTEST +#include +#endif + +#define KEYBOARD_FOCUS_COLOR (RED_BASE + 3) +#define NORMAL_ENTRY_COLOR (RED_BASE + 7) +#define CURRENT_DIFF_COLOR (RED_BASE + 3) +#define SELECTED_COLOR (RED_BASE) +#define UNAVAILABLE_COLOR (RED_BASE + 11) + +uiSlab setup_slab; +LGRegion setup_root_region; +uchar play_intro_anim; +uchar save_game_exists = FALSE; +SetupMode setup_mode; +SetupMode last_setup_mode; +int intro_num; +int splash_num; +int diff_sum = 0; +bool do_fades = false; +ubyte valid_save; +uchar setup_bio_started = FALSE; +uchar start_first_time = TRUE; +bool waiting_for_key = false; + +static uchar direct_into_cutscene = FALSE; + +extern char which_lang; +extern uchar clear_player_data; +extern char current_cutscene; +extern char curr_vol_lev; +extern char curr_sfx_vol; +extern uchar fullscrn_vitals; +extern uchar fullscrn_icons; +extern uchar map_notes_on; +extern uchar audiolog_setting; +extern uchar mouseLefty; +#ifdef AUDIOLOGS +extern char curr_alog_vol; +#endif + +errtype draw_difficulty_char(int char_num); +errtype draw_difficulty_description(int which_cat, int color); +errtype journey_newgame_func(void); +errtype journey_continue_func(uchar draw_stuff); +errtype draw_username(int color, char *string); + +//***************************************************************************** + +// DIFFICULTY + +#define DIFF_DONE_X1 119 +#define DIFF_DONE_Y1 179 +#define DIFF_DONE_X2 203 +#define DIFF_DONE_Y2 198 + +#define DIFF_NAME_X 57 +#define DIFF_NAME_Y 49 +#define DIFF_NAME_X2 253 +#define DIFF_NAME_Y2 65 +#define DIFF_NAME_TEXT_X 124 + +#define DIFF_X_BASE 28 +#define DIFF_W_BASE 156 +#define DIFF_W_ELEM 32 +#define DIFF_H_ELEM 20 + +#define DIFF_OPT_TOP 99 +#define DIFF_OPT_DELTA 53 +#define DIFF_OPT_HEIGHT 24 +#define DIFF_STRING_OFFSET_Y 10 +#define DIFF_STRING_OFFSET_X 13 + +#define DIFF_TITLE1_X1 12 +#define DIFF_TITLE1_Y1 70 +#define DIFF_TITLE1_X2 155 +#define DIFF_TITLE1_Y2 93 +#define DIFF_TITLE1_OPT_TOP 94 +#define DIFF_TITLE1_OPT_BOTTOM 118 +#define DIFF_TITLE1_LEFT1 25 +#define DIFF_TITLE1_RIGHT1 45 + +#define DIFF_TITLE2_X1 169 +#define DIFF_TITLE2_Y1 70 +#define DIFF_TITLE2_X2 311 +#define DIFF_TITLE2_Y2 93 + +#define DIFF_TITLE3_X1 12 +#define DIFF_TITLE3_Y1 123 +#define DIFF_TITLE3_X2 155 +#define DIFF_TITLE3_Y2 146 + +#define DIFF_TITLE4_X1 169 +#define DIFF_TITLE4_Y1 123 +#define DIFF_TITLE4_X2 311 +#define DIFF_TITLE4_Y2 146 + +#define DIFF_SIZE_X DIFF_TITLE1_RIGHT1 - DIFF_TITLE1_LEFT1 +#define DIFF_SIZE_Y DIFF_TITLE1_OPT_BOTTOM - DIFF_TITLE1_OPT_TOP + +// why +2? +#define build_diff_x(char_num) \ + ((DIFF_X_BASE + (DIFF_W_ELEM * (char_num & 3)) + (((char_num >> 2) & 1) * DIFF_W_BASE)) + 2) +#define build_diff_y(char_num) ((DIFF_OPT_TOP + ((char_num >> 3) * DIFF_OPT_DELTA)) + 2) + +#define NUM_DIFF_CATEGORIES 4 + +#define CATEGORY_STRING_BASE REF_STR_diffCategories +#define DIFF_STRING_BASE REF_STR_diffStrings +#define DIFF_NAME REF_STR_diffName +#define DIFF_START REF_STR_diffStart + +#define FLASH_TIME (CIT_CYCLE / 8) + +#define COMPUTE_DIFF_STRING_X(wcat) (DIFF_X_BASE + (DIFF_W_BASE * (wcat & 1)) - DIFF_STRING_OFFSET_X) +#define COMPUTE_DIFF_STRING_Y(wcat) (DIFF_OPT_TOP + ((wcat >> 1) * DIFF_OPT_DELTA) - DIFF_STRING_OFFSET_Y) + +#define REF_IMG_bmDifficultyScreen 0x26d0000 + +char curr_diff = 0; +uchar start_selected = FALSE; + +short diff_titles_x[] = {DIFF_TITLE1_X1, DIFF_TITLE2_X1, DIFF_TITLE3_X1, DIFF_TITLE4_X1}; +short diff_titles_y[] = {DIFF_TITLE1_Y1, DIFF_TITLE2_Y1, DIFF_TITLE3_Y1, DIFF_TITLE4_Y1}; +char *valid_char_string = "0123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz "; + +LGRect name_rect = {{DIFF_NAME_TEXT_X, DIFF_NAME_Y}, {DIFF_NAME_X2, DIFF_NAME_Y2}}; + +errtype compute_new_diff(void) { + int i, new_sum = 0; + + for (i = 0; i < 4; i++) + new_sum += player_struct.difficulty[i]; + diff_sum = new_sum; + + return OK; +} + +errtype difficulty_draw(uchar full) { + int i; + + uiHideMouse(NULL); + + if (full) { + draw_raw_res_bm_temp(REF_IMG_bmDifficultyScreen, 0, 0); + status_bio_draw(); + } + + setup_mode = SETUP_DIFFICULTY; + + for (i = 0; i < NUM_DIFF_CATEGORIES; i++) { + if (i == curr_diff && !start_selected) + gr_set_fcolor(KEYBOARD_FOCUS_COLOR); + else + gr_set_fcolor(NORMAL_ENTRY_COLOR); + + res_draw_string(RES_citadelFont, CATEGORY_STRING_BASE + i, diff_titles_x[i] + 12, diff_titles_y[i] + 2); + } + + if (start_selected) + gr_set_fcolor(KEYBOARD_FOCUS_COLOR); + else + gr_set_fcolor(NORMAL_ENTRY_COLOR); + + res_draw_string(RES_citadelFont, DIFF_START, DIFF_DONE_X1 + 13, DIFF_DONE_Y1 + 2); + + if (full) { + for (i = 0; i < 16; i++) + draw_difficulty_char(i); + for (i = 0; i < 4; i++) + draw_difficulty_description(i, NORMAL_ENTRY_COLOR); + + gr_set_fcolor(KEYBOARD_FOCUS_COLOR); + res_draw_string(RES_citadelFont, DIFF_NAME, DIFF_NAME_X, DIFF_NAME_Y); + draw_username(NORMAL_ENTRY_COLOR, player_struct.name); + } + + uiShowMouse(NULL); + + return OK; +} + +errtype draw_username(int color, char *string) { + gr_set_fcolor(color); + uiHideMouse(&name_rect); + res_draw_text(RES_citadelFont, string, DIFF_NAME_TEXT_X, DIFF_NAME_Y); + uiShowMouse(&name_rect); + + return OK; +} + +void flash_username(void) { + long flash_done; + + uiHideMouse(&name_rect); + gr_set_fcolor(SELECTED_COLOR - 4); + res_draw_string(RES_citadelFont, DIFF_NAME, DIFF_NAME_X, DIFF_NAME_Y); + flash_done = *tmd_ticks + FLASH_TIME; + while (TickCount() < flash_done) { + SDLDraw(); + } + gr_set_fcolor(KEYBOARD_FOCUS_COLOR); + res_draw_string(RES_citadelFont, DIFF_NAME, DIFF_NAME_X, DIFF_NAME_Y); + uiShowMouse(&name_rect); +} + +errtype draw_difficulty_line(int which_line) { + int i; + + for (i = 0; i < 4; i++) + draw_difficulty_char((which_line * 4) + i); + draw_difficulty_description(which_line, NORMAL_ENTRY_COLOR); + + return OK; +} + +errtype draw_difficulty_description(int which_cat, int color) { + if (color != -1) + gr_set_fcolor(color); + res_draw_string(RES_smallTechFont, DIFF_STRING_BASE + (which_cat * 4) + player_struct.difficulty[which_cat], + COMPUTE_DIFF_STRING_X(which_cat), COMPUTE_DIFF_STRING_Y(which_cat)); + + return OK; +} + +// char_num 1-16 +errtype draw_difficulty_char(int char_num) { + char buff[] = "X"; + + uiHideMouse(NULL); + if (player_struct.difficulty[char_num / 4] == char_num % 4) + gr_set_fcolor(CURRENT_DIFF_COLOR); + else + gr_set_fcolor(NORMAL_ENTRY_COLOR); + buff[0] = (char_num & 3) + '0'; + res_draw_text(RES_citadelFont, buff, build_diff_x(char_num), build_diff_y(char_num)); + uiShowMouse(NULL); + + return OK; +} +//***************************************************************************** + +//***************************************************************************** + +// JOURNEY + +#define JOURNEY_OPT1_TOP 66 +#define JOURNEY_OPT1_BOT 88 + +#define JOURNEY_OPT2_TOP 97 +#define JOURNEY_OPT2_BOT 119 + +#define JOURNEY_OPT3_TOP 129 +#define JOURNEY_OPT3_BOT 160 + +#define JOURNEY_OPT4_TOP 160 +#define JOURNEY_OPT4_BOT 182 + +#define SETUP_STRING_BASE REF_STR_journeyOpts + +#define NUM_SETUP_LINES 4 + +#define JOURNEY_OPT_LEFT 79 +#define JOURNEY_OPT_RIGHT 247 + +#define REF_IMG_bmJourneyOnwards 0x26c0000 + +#ifdef DEMO +char curr_setup_line = 1; +#else +char curr_setup_line = 0; +#endif + +int journey_y[8] = {JOURNEY_OPT1_TOP, JOURNEY_OPT1_BOT, JOURNEY_OPT2_TOP, JOURNEY_OPT2_BOT, + JOURNEY_OPT3_TOP, JOURNEY_OPT3_BOT, JOURNEY_OPT4_TOP, JOURNEY_OPT4_BOT}; + +short setup_tops[] = {JOURNEY_OPT1_TOP, JOURNEY_OPT2_TOP, JOURNEY_OPT3_TOP, JOURNEY_OPT4_TOP}; + +errtype journey_draw(char part) { + char i; + + uiHideMouse(NULL); + + if (setup_bio_started) { + status_bio_end(); + setup_bio_started = FALSE; + } + + // extract into buffer - AFTER we've stopped biorhythms (which used that buffer.....) + if (part == 0) + draw_raw_res_bm_temp(REF_IMG_bmJourneyOnwards, 0, 0); + + for (i = 0; i < NUM_SETUP_LINES; i++) { + if ((part == 0) || (part - 1 == i)) { + int col; + + if (i == curr_setup_line) + col = KEYBOARD_FOCUS_COLOR; + else + col = NORMAL_ENTRY_COLOR; + +#ifdef DEMO + if ((i == NUM_SETUP_LINES - 1) || (i == 0)) +#else + if (i == NUM_SETUP_LINES - 1) // why is NUM_SETUP_LINES-1 necessarily continue? +#endif + { + +#ifndef DEMO + if (!save_game_exists) +#endif + + col = UNAVAILABLE_COLOR; + } + gr_set_fcolor(col); + res_draw_string(RES_citadelFont, SETUP_STRING_BASE + i, JOURNEY_OPT_LEFT + 15, setup_tops[i] + 4); + } + } + uiShowMouse(NULL); + + setup_mode = SETUP_JOURNEY; + + return OK; +} + +errtype journey_intro_func(uchar draw_stuff) { + +#ifdef DEMO + uiShowMouse(NULL); // need to leave it hidden + return OK; +#else + if (draw_stuff) + res_draw_string(RES_citadelFont, SETUP_STRING_BASE, JOURNEY_OPT_LEFT + 15, JOURNEY_OPT1_TOP + 2); + uiShowMouse(NULL); // need to leave it hidden + + MacTuneKillCurrentTheme(); + + return play_cutscene(START_CUTSCENE, FALSE); +#endif +} + +errtype journey_newgame_func(void) { + clear_player_data = TRUE; + + DEBUG("Load object data"); + object_data_load(); + + player_struct.level = 0xFF; + + DEBUG("Create initial game"); + create_initial_game_func(0, 0, 0); + + INFO("Started new game!"); + + change_mode_func(0, 0, GAME_LOOP); + + return OK; +} + +errtype journey_difficulty_func(uchar draw_stuff) { + if (draw_stuff) + res_draw_string(RES_citadelFont, SETUP_STRING_BASE + 1, JOURNEY_OPT_LEFT + 15, JOURNEY_OPT2_TOP + 2); + uiShowMouse(NULL); + difficulty_draw(TRUE); + compute_new_diff(); + status_bio_set(DIFF_BIO); + status_bio_start(); + setup_bio_started = TRUE; + + return OK; +} +//***************************************************************************** + +//***************************************************************************** + +// CREDITS + +#define CredResFnt (RES_coloraliasedFont) +#define CredColor (GREEN_BASE + 4) +#define CredResource (RES_credits) + +int credits_inp = 0; + +void *credits_txtscrn; + +int CreditsTune; + +// set this when game is won, then stats will be shown once before credits +int WonGame_ShowStats = 0; + +// ticks: 0 wait forever +int WaitForKey(ulong ticks) { + ulong end_ticks = (ulong)TickCount(); + ulong key_ticks = end_ticks + (!ticks ? 500 : (ticks * 1 / 8)); + end_ticks = ticks ? end_ticks + ticks : 0; + int ch; + + // wait for specified elapsed ticks or keypress + for (;;) { + + // loop win or elevator music + int i = 0; + if (music_on && !IsPlaying(i)) { + int track; + + if (WonGame_ShowStats) + track = 0; + else + track = 1 + CreditsTune; + + if (track >= 0 && track < NumTracks) { + // int volume = (int)curr_vol_lev * 127 / 100; //convert from 0-100 to 0-127 + StartTrack(i, track); + + if (!WonGame_ShowStats) + CreditsTune = (CreditsTune + 1) % 8; + } + } + + pump_events(); + SDLDraw(); + + kbs_event ev = kb_next(); + ch = ev.ascii; + ticks = (ulong)TickCount(); + + if ((ch == 27 || ch == ' ' || ch == '\r') && ticks >= key_ticks) + break; + if (end_ticks && ticks >= end_ticks) + break; + } + + return ch; +} + +void PrintWinStats(void) { + char buf[256], buf_temp[256]; + int x, y = 15; + short w, h; + + grs_font *fon = gr_get_font(); + gr_set_font(ResLock(RES_coloraliasedFont)); + + gr_clear(0); + + sprintf(buf, "CONGRATULATIONS!"); + gr_string_size(buf, &w, &h); + ss_string(buf, (320 - w) / 2, y); + y += 12 * 2; + + sprintf(buf, "YOU HAVE COMPLETED SYSTEM SHOCK!"); + gr_string_size(buf, &w, &h); + ss_string(buf, (320 - w) / 2, y); + y += 12; + + sprintf(buf, "HIT ESC TO VIEW CREDITS."); + gr_string_size(buf, &w, &h); + ss_string(buf, (320 - w) / 2, y); + y += 12 * 2; + + sprintf(buf, "STATISTICS"); + gr_string_size(buf, &w, &h); + ss_string(buf, (320 - w) / 2, y); + y += 12; + + // underline + short x1 = SCONV_X((320 - w) / 2); + short x2 = SCONV_X((320 - w) / 2 + w - 1); + for (; x1 <= x2; x1++) { + short y1 = SCONV_Y(y); + short y2 = SCONV_Y(y + 1); + y2 = y1 + (y2 - y1) / 3; + for (; y1 <= y2; y1++) + gr_set_pixel(GREEN_BASE + 4, x1, y1); + } + + y += 4; + + sprintf(buf, "TIME: %u", player_struct.game_time); + gr_string_size(buf, &w, &h); + ss_string(buf, (320 - w) / 2, y); + y += 12; + + sprintf(buf, "KILLS: %d", player_struct.num_victories); + gr_string_size(buf, &w, &h); + ss_string(buf, (320 - w) / 2, y); + y += 12; + + sprintf(buf, "REGENERATIONS: %d", player_struct.num_deaths); + gr_string_size(buf, &w, &h); + ss_string(buf, (320 - w) / 2, y); + y += 12; + + uint8_t stupid = 0; + for (uint8_t i = 0; i < 4; i++) + stupid += (player_struct.difficulty[i] * player_struct.difficulty[i]); + sprintf(buf, "DIFFICULTY INDEX: %d", stupid); + gr_string_size(buf, &w, &h); + ss_string(buf, (320 - w) / 2, y); + y += 12; + + int score; + // death is 10 anti-kills, but you always keep at least a third of your kills. + score = player_struct.num_victories - lg_min(player_struct.num_deaths * 10, player_struct.num_victories * 2 / 3); + score = score * 10000; + score = score - lg_min(score * 2 / 3, ((player_struct.game_time / (CIT_CYCLE * 36)) * 100)); + score = score * (stupid + 1) / 37; // 9 * 4 + 1 is best difficulty factor + if (stupid == 36) { + score += 2222222; // secret kevin bonus + } + sprintf(buf, "SCORE: %d", score); + gr_string_size(buf, &w, &h); + ss_string(buf, (320 - w) / 2, y); + + ResUnlock(RES_coloraliasedFont); + gr_set_font(fon); + + WaitForKey(0); +} + +void PrintCredits(void) { + // Credits display reverse-engineered from text resources + + int end = 0, line = 0, x, y = 15, columns = 1, cur_col = 0; + int underline = 0, last_underline = 0; + char buf[256]; + + gr_clear(0); + + while (!end) { + get_string((RES_credits << 16) | line, buf, sizeof(buf)); + line++; + + int len = strlen(buf); + + if (*buf == '^') { + for (int i = 1; i < len; i++) { + switch (buf[i]) { + case 'E': + WaitForKey(0); + end = 1; + break; + case 'p': + WaitForKey(200); + break; + case 'G': + if (WaitForKey(2000) == 27) + end = 1; + gr_clear(0); + y = 15; + break; + case 'N': + break; // dunno + case '1': + columns = 1; + cur_col = 0; + break; + case '2': + columns = 2; + cur_col = 0; + break; + case 'H': + underline = 3; + break; + case 'T': + underline = 2; + break; + case 'h': + underline = 1; + break; + case 'c': + underline = 0; + break; + case 'S': + y += 10; + break; + case 'L': + y = 15 * (buf[++i] - '0'); + break; + } + } + continue; + } + + grs_font *fon = gr_get_font(); + gr_set_font(ResLock(RES_coloraliasedFont)); + short w, h; + gr_string_size(buf, &w, &h); + x = (columns == 1) ? (320 - w) / 2 : (cur_col == 0) ? 320 / 2 - 8 - w : 320 / 2 + 8; + ss_string(buf, x, y); + ResUnlock(RES_coloraliasedFont); + gr_set_font(fon); + + if (underline) { + short x1, x2, y1, y2; + + x1 = SCONV_X(x); + x2 = SCONV_X(x + w - 1); + for (; x1 <= x2; x1++) { + y1 = SCONV_Y(y + h + 1); + y2 = SCONV_Y(y + h + 2); + if (underline == 2) + y2 = y1 + (y2 - y1) / 2; + else + y2 = y1 + (y2 - y1) / 3; + for (; y1 <= y2; y1++) + gr_set_pixel(GREEN_BASE + 4, x1, y1); + } + + if (underline == 3) { + x1 = SCONV_X(x + 1); + x2 = SCONV_X(x + w - 1 - 1); + for (; x1 <= x2; x1++) { + y1 = SCONV_Y(y + h + 1); + y2 = SCONV_Y(y + h + 2); + y1 = y1 + (y2 - y1) / 3; + for (; y1 <= y2; y1++) + gr_set_pixel(GREEN_BASE + 4, x1, y1); + } + } + } + + if (columns == 1) { + y += underline ? 14 : 11; + underline = 0; + } else { + if (!cur_col) + last_underline = underline; + else { + y += (underline || last_underline) ? 14 : 11; + underline = 0; + } + cur_col ^= 1; + } + } +} + +errtype journey_credits_func(uchar draw_stuff) { + setup_mode = SETUP_CREDITS; + + if (draw_stuff) { + if (music_on) { + if (WonGame_ShowStats) { + MacTuneLoadTheme("endloop", 0); + } else { + CreditsTune = 0; + load_score_guts(7); // elevator + } + } + + if (WonGame_ShowStats) + PrintWinStats(); + PrintCredits(); + + WonGame_ShowStats = 0; + + journey_credits_done(); + } else { + if (credits_inp != 0) { + journey_credits_done(); + } + } + + return OK; +} + +void journey_credits_done(void) { + kb_flush(); + mouse_flush(); + + credits_inp = 0; + + uiShowMouse(NULL); + + journey_draw(0); + + if (music_on) + MacTuneLoadTheme("titloop", 0); +} +//***************************************************************************** + +//***************************************************************************** + +// CONTINUE + +#define SG_SLOT_HT 17 +#define SG_SLOT_WD 169 +#define SG_SLOT_OFFSET_X 21 +#define SG_SLOT_OFFSET_Y 6 +#define SG_SLOT_X 79 +#define SG_SLOT_Y 62 + +#define REF_IMG_bmContinueScreen 0x26e0000 + +char curr_sg = 0; + +errtype draw_sg_slot(int slot_num) { + char temp[64]; + short sz, x, y; + + uiHideMouse(NULL); + + if (curr_sg == slot_num) + gr_set_fcolor(KEYBOARD_FOCUS_COLOR); + else + gr_set_fcolor(NORMAL_ENTRY_COLOR); + + // if slot_num == -1 highlight the curr_sg slot with the SELECTED_COLOR color + if (slot_num == -1) { + gr_set_fcolor(SELECTED_COLOR); + slot_num = curr_sg; + } + + if (valid_save & (1 << slot_num)) { + sz = strlen(comments[slot_num]); + strcpy(temp, comments[slot_num]); + } else { + get_string(REF_STR_UnusedSave, temp, 64); + } + + gr_set_font(ResLock(RES_smallTechFont)); + gr_string_size(temp, &x, &y); + + while ((x > SG_SLOT_WD - SG_SLOT_OFFSET_X) && (sz > 0)) { + sz--; + strcpy(temp, ""); + strncpy(temp, comments[slot_num], sz); + temp[sz] = '\0'; + gr_string_size(temp, &x, &y); + } + + ResUnlock(RES_smallTechFont); // was RES_CitadelFont + + res_draw_text(RES_smallTechFont, temp, SG_SLOT_X + SG_SLOT_OFFSET_X, + SG_SLOT_Y + SG_SLOT_OFFSET_Y + (slot_num * SG_SLOT_HT)); + + uiShowMouse(NULL); + + return OK; +} + +errtype draw_savegame_names(void) { + int i; + + for (i = 0; i < NUM_SAVE_SLOTS; i++) + draw_sg_slot(i); + + return OK; +} + +errtype load_that_thar_game(int which_slot) { + DEBUG("load_that_thar_game %i", which_slot); + + errtype retval; + + if (valid_save & (1 << which_slot)) { + draw_sg_slot(-1); // highlight the current save game slot with SELECTED_COLOR + + clear_player_data = TRUE; // initializes the player struct in object_data_load + object_data_load(); + player_create_initial(); + player_struct.level = 0xFF; // make sure we load textures + Poke_SaveName(which_slot); + change_mode_func(0, 0, GAME_LOOP); + retval = load_game(save_game_name); + if (retval != OK) { + strcpy(comments[which_slot], "<< INVALID GAME >>"); + uiHideMouse(NULL); + journey_continue_func(TRUE); + return retval; + } + + gr2ss_override = OVERRIDE_ALL; // CC: This fixed popups cursors drawing tiny after loading + } + + return OK; +} + +errtype journey_continue_func(uchar draw_stuff) { + +#ifndef DEMO + if (save_game_exists) { + // draw_raw_res_bm_extract(REF_IMG_bmContinueScreen, 0, 0); + + // do what the above line does, but hack bitmap height + Ref rid = REF_IMG_bmContinueScreen; + int i = REFINDEX(rid); + RefTable *rt = ResReadRefTable(REFID(rid)); + if (RefIndexValid(rt, i)) { + FrameDesc *f = RefLock(rid); + grs_bitmap bm = f->bm; + bm.h = 200; // SUPER HACK: resource reports 320 + ss_bitmap(&bm, 0, 0); + RefUnlock(rid); + } + ResFreeRefTable(rt); + + setup_mode = SETUP_CONTINUE; + draw_savegame_names(); + } +#endif + + uiShowMouse(NULL); + + return OK; +} +//***************************************************************************** + +//***************************************************************************** + +// SETUP + +#define DO_FADES + +#define SECRET_MISSION_DIFFICULTY_QB 0xB0 + +#define ALT(x) ((x) | KB_FLAG_ALT) + +#define CFG_NAME_VAR "name" + +char diff_qvars[4] = {COMBAT_DIFF_QVAR, MISSION_DIFF_QVAR, PUZZLE_DIFF_QVAR, CYBER_DIFF_QVAR}; + +typedef errtype (*journey_func)(uchar draw_stuff); + +journey_func journey_funcs[4] = {journey_intro_func, journey_difficulty_func, journey_credits_func, + journey_continue_func}; + +// if there are two different input events - only lets one call a journey_func +uchar journey_lock = FALSE; + +void go_and_start_the_game_already(void) { + INFO("New Journey"); + + char i; + +#ifdef GAMEONLY + if (strlen(player_struct.name) == 0) { + flash_username(); + return; + } +#endif + + uiHideMouse(NULL); + gr_set_fcolor(SELECTED_COLOR); + res_draw_string(RES_citadelFont, DIFF_START, DIFF_DONE_X1 + 13, DIFF_DONE_Y1 + 2); + uiShowMouse(NULL); + + journey_newgame_func(); + +#ifdef SVGA_SUPPORT + QUESTVAR_SET(SCREENMODE_QVAR, convert_use_mode); +#endif + + QUESTVAR_SET(MUSIC_VOLUME_QVAR, (curr_vol_lev * curr_vol_lev) / 100); + QUESTVAR_SET(SFX_VOLUME_QVAR, (curr_sfx_vol * curr_sfx_vol) / 100); + +#ifdef AUDIOLOGS + QUESTVAR_SET(ALOG_VOLUME_QVAR, (curr_alog_vol * curr_alog_vol) / 100); + QUESTVAR_SET(ALOG_OPT_QVAR, audiolog_setting); +#endif + + QUESTVAR_SET(FULLSCRN_ICON_QVAR, fullscrn_icons); + QUESTVAR_SET(FULLSCRN_VITAL_QVAR, fullscrn_vitals); + QUESTVAR_SET(AMAP_NOTES_QVAR, map_notes_on); + QUESTVAR_SET(HUDCOLOR_QVAR, hud_color_bank); + QUESTVAR_SET(SCREENMODE_QVAR, 3); + QUESTVAR_SET(DCLICK_QVAR, FIX_UNIT / 3); + + for (i = 0; i < 4; i++) + QUESTVAR_SET(diff_qvars[i], player_struct.difficulty[i]); + if (QUESTVAR_GET(MISSION_DIFF_QVAR) == 3) + hud_set(HUD_GAMETIME); + + strncpy(player_struct.version, SYSTEM_SHOCK_VERSION, 6); + + if (setup_bio_started) { + status_bio_end(); + setup_bio_started = FALSE; + } + + gr2ss_override = OVERRIDE_ALL; // CC: This fixed popups cursors drawing tiny +} + +uchar intro_mouse_handler(uiEvent *ev, LGRegion *r, intptr_t user_data) { + int which_one = -1; + int i = 0; + int old_diff; + uchar diff_changed; + +#ifndef NO_DUMMIES + intptr_t dummy = user_data; + LGRegion *dummy2 = r; +#endif + + if (ev->mouse_data.action & MOUSE_LDOWN) { + // If in the splash screen, advance + if (waiting_for_key) { + waiting_for_key = false; + return OK; + } + + switch (setup_mode) { + case SETUP_JOURNEY: + if (!journey_lock) { + if ((ev->pos.x > JOURNEY_OPT_LEFT) && (ev->pos.x < JOURNEY_OPT_RIGHT)) { + while ((which_one == -1) && (i <= 6)) { + if ((ev->pos.y > journey_y[i]) && (ev->pos.y < journey_y[i + 1])) + which_one = i >> 1; + else + i += 2; + } + TRACE("%s: which_one = %d", __FUNCTION__, which_one); + if (which_one != -1) { + uiHideMouse(NULL); + gr_set_fcolor(SELECTED_COLOR); + journey_lock = TRUE; + journey_funcs[which_one](TRUE); + journey_lock = FALSE; + } + } + } + break; + + case SETUP_CREDITS: + credits_inp = -1; + break; + + case SETUP_CONTINUE: + if ((ev->pos.x >= SG_SLOT_X) && (ev->pos.x <= SG_SLOT_X + SG_SLOT_WD) && (ev->pos.y >= SG_SLOT_Y) && + (ev->pos.y <= SG_SLOT_Y + (NUM_SAVE_SLOTS * SG_SLOT_HT))) { + char which = (ev->pos.y - SG_SLOT_Y) / SG_SLOT_HT; + char old_sg = curr_sg; + + curr_sg = which; + draw_sg_slot(old_sg); + load_that_thar_game(which); + } + break; + + case SETUP_DIFFICULTY: + diff_changed = FALSE; + // given that these are all rectangles, i bet you could just get mouse pos and divide, eh? + for (i = 0; i < 16; i++) { + if ((ev->pos.x > (build_diff_x(i) - 2)) && (ev->pos.x < (build_diff_x(i) - 2 + DIFF_SIZE_X)) && + (ev->pos.y > (build_diff_y(i) - 2)) && (ev->pos.y < (build_diff_y(i) - 2 + DIFF_SIZE_Y))) { + old_diff = player_struct.difficulty[i / 4]; + draw_difficulty_description(i / 4, 0); + player_struct.difficulty[i / 4] = i % 4; + TRACE("%s: difficulty %d set to %d (verify = %d)", __FUNCTION__, i / 4, i % 4, + player_struct.difficulty[i / 4]); + draw_difficulty_char(((i / 4) * 4) + old_diff); + draw_difficulty_char(i); + draw_difficulty_description(i / 4, NORMAL_ENTRY_COLOR); + diff_changed = TRUE; + } + } + if (diff_changed) + compute_new_diff(); + else if ((ev->pos.x > DIFF_DONE_X1) && (ev->pos.x < DIFF_DONE_X2) && (ev->pos.y > DIFF_DONE_Y1) && + (ev->pos.y < DIFF_DONE_Y2)) + go_and_start_the_game_already(); + break; + } + } + + return TRUE; +} + +uchar intro_key_handler(uiEvent *ev, LGRegion *r, intptr_t user_data) { + int code = ev->cooked_key_data.code & ~(KB_FLAG_DOWN | KB_FLAG_2ND); + char old_diff, old_setup_line = curr_setup_line, n = 0; + + if (ev->cooked_key_data.code & KB_FLAG_DOWN) { + // If in the splash screen, advance + if (waiting_for_key) { + waiting_for_key = false; + return OK; + } + + switch (setup_mode) { + case SETUP_JOURNEY: + switch (code) { + case KEY_UP: + n = NUM_SETUP_LINES - 2; // sneaky fallthrough action + case KEY_DOWN: + n++; + curr_setup_line = (curr_setup_line + n) % NUM_SETUP_LINES; +#ifdef DEMO + if (curr_setup_line == NUM_SETUP_LINES - 1) // why is NUM_SETUP_LINES-1 necessarily continue? + curr_setup_line = 2; + if (curr_setup_line == 0) + curr_setup_line = 1; +#else + if (curr_setup_line == NUM_SETUP_LINES - 1) // why is NUM_SETUP_LINES-1 necessarily continue? + if (!save_game_exists) + curr_setup_line = (curr_setup_line + n) % NUM_SETUP_LINES; +#endif + journey_draw(old_setup_line + 1); + journey_draw(curr_setup_line + 1); + break; + + case KEY_ENTER: + if (!journey_lock) { + uiHideMouse(NULL); + gr_set_fcolor(SELECTED_COLOR); + journey_lock = TRUE; + journey_funcs[curr_setup_line](TRUE); + journey_lock = FALSE; + } + break; + } + break; + + case SETUP_CREDITS: + credits_inp = code; + break; + + case SETUP_CONTINUE: + switch (code) { + case KEY_UP: + case KEY_LEFT: + n = NUM_SAVE_SLOTS - 2; + case KEY_DOWN: + case KEY_RIGHT: + n++; + old_diff = curr_sg; + curr_sg = (curr_sg + n) % NUM_SAVE_SLOTS; + draw_sg_slot(old_diff); + draw_sg_slot(curr_sg); + break; + + case KEY_ENTER: + load_that_thar_game(curr_sg); + break; + + case KEY_ESC: + journey_draw(0); + break; + } + break; + + case SETUP_DIFFICULTY: + switch (code) { + case ALT('X'): // Don't print the X when user ALT-X's out of the game + case ALT('x'): + break; + + case '-': + case KEY_LEFT: + n = NUM_DIFF_CATEGORIES - 2; // note sneaky -2 for fallthrough + case '+': + case KEY_RIGHT: + n++; // n now NDC-1 or 1 + if (!start_selected) { + old_diff = player_struct.difficulty[curr_diff]; + draw_difficulty_description(curr_diff, 0); + player_struct.difficulty[curr_diff] = + (player_struct.difficulty[curr_diff] + n) % NUM_DIFF_CATEGORIES; + draw_difficulty_char(curr_diff * NUM_DIFF_CATEGORIES + player_struct.difficulty[curr_diff]); + draw_difficulty_char(curr_diff * NUM_DIFF_CATEGORIES + old_diff); + draw_difficulty_description(curr_diff, NORMAL_ENTRY_COLOR); + compute_new_diff(); + } + break; + + case KEY_UP: + case (KEY_TAB | KB_FLAG_SHIFT): + n = NUM_DIFF_CATEGORIES - 2; // sneaky fallthrough + case KEY_DOWN: + case KEY_TAB: + n++; // now -1 or 1 + if (start_selected && n == 1) { + start_selected = FALSE; + curr_diff = 0; + } else if (start_selected && n == NUM_DIFF_CATEGORIES - 1) { + start_selected = FALSE; + curr_diff = NUM_DIFF_CATEGORIES - 1; + } else if ((curr_diff == NUM_DIFF_CATEGORIES - 1 && n == 1) || + (curr_diff == 0 && n == NUM_DIFF_CATEGORIES - 1)) + start_selected = TRUE; + else { + start_selected = FALSE; + curr_diff = (curr_diff + n) % NUM_DIFF_CATEGORIES; + } + difficulty_draw(FALSE); + break; + + case KEY_ENTER: + go_and_start_the_game_already(); + break; + + case KEY_ESC: + journey_draw(0); + break; + + default: { + draw_username(0, player_struct.name); + n = strlen(player_struct.name); + short c = ev->cooked_key_data.code; + if (kb_isprint(c) && (n < sizeof(player_struct.name)) && + (((c & 0xff) >= 128 && (c & 0xff) <= 155) || ((c & 0xff) >= 160 && (c & 0xff) <= 165) || + strchr(valid_char_string, c & 0xFF) != NULL)) { + player_struct.name[n] = (c & 0xFF); + player_struct.name[n + 1] = '\0'; + } + if (((c & 0xFF) == KEY_BS) && (n > 0)) + player_struct.name[n - 1] = '\0'; + draw_username(NORMAL_ENTRY_COLOR, player_struct.name); + } break; + } + break; + } + } + + return (main_kb_callback(ev, r, user_data)); +} + +errtype load_savegame_names(void) { + int i; + int file; + + valid_save = 0; + + DEBUG("Grabbing save game names"); + + for (i = 0; i < NUM_SAVE_SLOTS; i++) { + Poke_SaveName(i); + +#ifdef __APPLE__ + char full_save_game_name[1024]; + sprintf(full_save_game_name, "%s%s", SDL_GetPrefPath("Interrupt", "SystemShock"), save_game_name); + if (access(full_save_game_name, F_OK) != -1) { +#else + if (access(save_game_name, F_OK) != -1) { +#endif + file = ResOpenFile(save_game_name); + if (ResInUse(OLD_SAVE_GAME_ID_BASE)) { +#ifdef OLD_SG_FORMAT + ResExtract(OLD_SAVE_GAME_ID_BASE, FORMAT_RAW, comments[i]); + valid_save |= (1 << i); +#else + strcpy(comments[i], "<< BAD VERSION >>"); +#endif + } else { + if (ResInUse(SAVELOAD_VERIFICATION_ID)) { + int verify_cookie; + + ResExtract(SAVELOAD_VERIFICATION_ID, FORMAT_U32, &verify_cookie); + switch (verify_cookie) { + case OLD_VERIFY_COOKIE_VALID: + // Uncomment these lines to reject Shock Floppy save games + // sprintf(comments[i], "<< %s >>",get_temp_string(REF_STR_BadVersion + 1)); + // break; + + case VERIFY_COOKIE_VALID: + ResExtract(SAVE_GAME_ID_BASE, FORMAT_RAW, comments[i]); + valid_save |= (1 << i); + break; + + default: + sprintf(comments[i], "<< %s >>", get_temp_string(REF_STR_BadVersion)); + break; + } + } else + sprintf(comments[i], "<< %s >>", get_temp_string(REF_STR_BadVersion)); + } + + ResCloseFile(file); + } else + *(comments[i]) = '\0'; + } + + return OK; +} + +errtype setup_init(void) { +#ifndef GAMEONLY + int data[1]; + int cnt; +#endif + + generic_reg_init(TRUE, &setup_root_region, NULL, &setup_slab, intro_key_handler, intro_mouse_handler); + +#ifndef GAMEONLY + cnt = 1; + // if (config_get_value("intro", CONFIG_INT_TYPE, data, &cnt)) + { + physics_running = TRUE; + time_passes = TRUE; + _current_loop = SETUP_LOOP; + } + if (!config_get_raw(CFG_NAME_VAR, player_struct.name, 40)) + strcpy(player_struct.name, get_temp_string(REF_STR_DefaultPlayName)); + load_savegame_names(); +#endif + + setup_mode = SETUP_JOURNEY; + + return OK; +} + +void pause_for_key(ulong wait_time) { + waiting_for_key = true; + + ulong wait_until = TickCount() + wait_time; + + while (waiting_for_key && ((ulong)TickCount() < wait_until)) { + input_chk(); + pump_events(); + SDLDraw(); + } + + waiting_for_key = false; +} + +void splash_draw(bool show_splash) { + int pal_file; + + if (!show_splash) + return; + + // Need to load the splash palette file + + INFO("Loading splshpal.res"); + pal_file = ResOpenFile("res/data/splshpal.res"); + + INFO("Loading splash.res"); + splash_num = ResOpenFile("res/data/splash.res"); + + if (pal_file < 0) + INFO("Could not open splshpal.res!"); + + uchar splash_pal[768]; + ResExtract(RES_splashPalette, FORMAT_RAW, splash_pal); + + // Set initial palette + + gr_set_pal(0, 256, splash_pal); + + // Set screen mode + +#ifdef SVGA_SUPPORT + change_svga_screen_mode(); +#endif + + // clear the screen + gr_clear(0); + + HotkeyContext = SETUP_CONTEXT; + uiSetCurrentSlab(&setup_slab); + + // Draw Origin Logo + +#ifdef DO_FADES + do_fades = true && pal_fx_on; +#endif + + uiHideMouse(NULL); + draw_full_res_bm(REF_IMG_bmOriginSplash, 0, 0, do_fades); + pause_for_key(500); + + if (do_fades) + palfx_fade_down(); + + // Draw LGS Logo + + uiHideMouse(NULL); + draw_full_res_bm(REF_IMG_bmLGSplash, 0, 0, do_fades); + pause_for_key(500); + + if (do_fades) + palfx_fade_down(); + + // Draw System Shock title + + uiHideMouse(NULL); + draw_full_res_bm(REF_IMG_bmSystemShockTitle, 0, 0, do_fades); + pause_for_key(500); + + if (do_fades) + palfx_fade_down(); + + // Original palette + gr_set_pal(0, 256, ppall); +} + +void setup_loop(void) { + bool draw_stuff = FALSE; + + // loop title music + int i = 0; + if (music_on && !IsPlaying(i)) { + int track = 0; + if (track >= 0 && track < NumTracks) { + // int volume = (int)curr_vol_lev * 127 / 100; //convert from 0-100 to 0-127 + StartTrack(i, track); + } + } + + if (last_setup_mode != setup_mode) { + uiHideMouse(NULL); + gr_clear(0xFF); + draw_stuff = TRUE; + } + + last_setup_mode = setup_mode; + + switch (setup_mode) { + case SETUP_DIFFICULTY: + difficulty_draw(draw_stuff); + break; + case SETUP_JOURNEY: + journey_draw(draw_stuff); + break; + case SETUP_CONTINUE: + journey_continue_func(draw_stuff); + break; + case SETUP_CREDITS: + journey_credits_func(draw_stuff); + break; + } +} + +// if these don't get reset, sticky residue of any old game sticks around +void empty_slate(void) { + flush_resource_cache(); + + extern uint _fr_glob_flags; + _fr_glob_flags = 0; + + extern uchar *shodan_bitmask; + shodan_bitmask = NULL; + + extern uchar alternate_death; + alternate_death = FALSE; + + extern uiSlab fullscreen_slab; + extern uiSlab main_slab; + uiPopSlabCursor(&fullscreen_slab); + uiPopSlabCursor(&main_slab); + + extern uchar fire_slam; + fire_slam = FALSE; + + extern short inventory_page; + inventory_page = 0; + + extern short inv_last_page; + inv_last_page = -1; + + extern void physics_zero_all_controls(void); + physics_zero_all_controls(); + + extern int mlook_enabled; + mlook_enabled = 0; + + extern uchar weapon_button_up; + weapon_button_up = TRUE; +} + +void setup_start(void) { + int do_i_svg = -1, i_invuln = 0; + + empty_slate(); + + player_struct.name[0] = 0; + + for (int i = 0; i < 4; i++) + player_struct.difficulty[i] = 2; + + MacTuneKillCurrentTheme(); + + // Check to see whether or not to play the intro cut scene +#ifdef GAMEONLY + load_savegame_names(); +#endif + + save_game_exists = (valid_save != 0); + + if (setup_mode != SETUP_CREDITS) { + if (!save_game_exists && start_first_time) { + play_intro_anim = TRUE; + } else { + play_intro_anim = FALSE; + } + setup_mode = SETUP_JOURNEY; + } + + if (!start_first_time) + closedown_game(TRUE); + start_first_time = FALSE; + +#ifdef GADGET + // got rid of pointer type mismatch since one was a region and the other a gadget + // someone should probably go and figure it out + _current_root = NULL; +#endif + _current_3d_flag = ANIM_UPDATE; + _current_fr_context = NULL; + _current_view = &setup_root_region; + static_change_copy(); + message_info(""); + +#ifdef SVGA_SUPPORT + change_svga_screen_mode(); +#endif + + // clear the screen + gr_clear(0); + + HotkeyContext = SETUP_CONTEXT; + uiSetCurrentSlab(&setup_slab); + + // flush the keyboard and mouse - so we don't read old events + kb_flush(); + mouse_flush(); + + intro_num = ResOpenFile("res/data/intro.res"); + + // slam in the right palette + load_da_palette(); + + if (do_i_svg != -1) { +#ifdef PLAYTEST + player_invulnerable = i_invuln; +#endif + uiShowMouse(NULL); + } else if (!play_intro_anim) { + uiShowMouse(NULL); + switch (setup_mode) { + case SETUP_DIFFICULTY: + difficulty_draw(TRUE); + break; + case SETUP_JOURNEY: + journey_draw(0); + break; + default: + break; + } + direct_into_cutscene = FALSE; + } else { + direct_into_cutscene = TRUE; + + play_cutscene(START_CUTSCENE, FALSE); + } + + if (music_on) + MacTuneLoadTheme("titloop", 0); + + CaptureMouse(false); +} + +void setup_exit(void) { + ResCloseFile(intro_num); + ResCloseFile(splash_num); + +#ifdef PALFX_FADES + if (pal_fx_on) + palfx_fade_down(); + else { + gr_set_fcolor(BLACK); + gr_rect(0, 0, 320, 200); + } +#endif + + // must get rid of mouse - to maintain hidden mouse after loop + if (!direct_into_cutscene) + uiHideMouse(NULL); + + direct_into_cutscene = FALSE; + + MacTuneKillCurrentTheme(); +} +//***************************************************************************** diff --git a/engine/src/GameSrc/shodan.c b/engine/src/GameSrc/shodan.c new file mode 100644 index 0000000..f2ae57e --- /dev/null +++ b/engine/src/GameSrc/shodan.c @@ -0,0 +1,166 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include "shodan.h" +#include "player.h" +#include "objclass.h" +#include "otrip.h" +#include "trigger.h" +#include "newmfd.h" +#include "hud.h" +#include "faketime.h" + +// --------------- +// Internal Prototypes +// --------------- +errtype update_shodometer(short new_val, uchar game_stuff); + +short compute_shodometer_value(uchar game_stuff) { + if (player_struct.level <= MAX_SHODOMETER_LEVEL) { + QUESTVAR_SET(SHODAN_QV, 0); + for (ObjID oid = (objs[OBJ_NULL]).headused; oid != OBJ_NULL; oid = objs[oid].next) { + increment_shodan_value(oid, game_stuff); + } + return (QUESTVAR_GET(SHODAN_QV)); + } else + return (0); +} + +#define HUD_SHODOMETER_TICKS (CIT_CYCLE << 1) + +errtype update_shodometer(short new_val, uchar game_stuff) { + QUESTVAR_SET(SHODAN_QV, new_val); + if (game_stuff) { + mfd_notify_func(MFD_PLOTWARE_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); + do_shodan_triggers(); + hud_set_time(HUD_SHODOMETER, HUD_SHODOMETER_TICKS); + } + return (OK); +} + +short increment_shodan_value(ObjID oid, uchar game_stuff) { + short curr_shodan_val = QUESTVAR_GET(SHODAN_QV); + + if (player_struct.level <= MAX_SHODOMETER_LEVEL) { + switch (ID2TRIP(oid)) { +#ifdef CREATURE_SHODOMETER + case CYBORG_DRONE_TRIPLE: + curr_shodan_val += CYBORG_DRONE_TRIPLE_VALUE; + break; + case WARRIOR_TRIPLE: + curr_shodan_val += WARRIOR_TRIPLE_VALUE; + break; + case ASSASSIN_TRIPLE: + curr_shodan_val += ASSASSIN_TRIPLE_VALUE; + break; + case CYBERBABE_TRIPLE: + curr_shodan_val += CYBERBABE_TRIPLE_VALUE; + break; + case ELITE_GUARD_TRIPLE: + curr_shodan_val += ELITE_GUARD_TRIPLE_VALUE; + break; + case CORTEX_REAVER_TRIPLE: + curr_shodan_val += CORTEX_REAVER_TRIPLE_VALUE; + break; + case MUTANT_BORG_TRIPLE: + curr_shodan_val += MUTANT_BORG_TRIPLE_VALUE; + break; + case SECURITY_BOT1_TRIPLE: + curr_shodan_val += SECURITY_BOT1_TRIPLE_VALUE; + break; + case SECURITY_BOT2_TRIPLE: + curr_shodan_val += SECURITY_BOT2_TRIPLE_VALUE; + break; + case EXECBOT_TRIPLE: + curr_shodan_val += EXECBOT_TRIPLE_VALUE; + break; +#endif + case CAMERA_TRIPLE: + curr_shodan_val += CAMERA_TRIPLE_VALUE; + break; + case SMALL_CPU_TRIPLE: + curr_shodan_val += SMALL_CPU_TRIPLE_VALUE; + break; + case LARGCPU_TRIPLE: + curr_shodan_val += LARGCPU_TRIPLE_VALUE; + break; + } + if (curr_shodan_val != QUESTVAR_GET(SHODAN_QV)) + update_shodometer(curr_shodan_val, game_stuff); + return (curr_shodan_val); + } else + return (0); +} + +short decrement_shodan_value(ObjID oid, uchar game_stuff) { + short curr_shodan_val = QUESTVAR_GET(SHODAN_QV); + + if (player_struct.level <= MAX_SHODOMETER_LEVEL) { + switch (ID2TRIP(oid)) { +#ifdef CREATURE_SHODOMETER + case CYBORG_DRONE_TRIPLE: + curr_shodan_val -= CYBORG_DRONE_TRIPLE_VALUE; + break; + case WARRIOR_TRIPLE: + curr_shodan_val -= WARRIOR_TRIPLE_VALUE; + break; + case ASSASSIN_TRIPLE: + curr_shodan_val -= ASSASSIN_TRIPLE_VALUE; + break; + case CYBERBABE_TRIPLE: + curr_shodan_val -= CYBERBABE_TRIPLE_VALUE; + break; + case ELITE_GUARD_TRIPLE: + curr_shodan_val -= ELITE_GUARD_TRIPLE_VALUE; + break; + case CORTEX_REAVER_TRIPLE: + curr_shodan_val -= CORTEX_REAVER_TRIPLE_VALUE; + break; + case MUTANT_BORG_TRIPLE: + curr_shodan_val -= MUTANT_BORG_TRIPLE_VALUE; + break; + case SECURITY_BOT1_TRIPLE: + curr_shodan_val -= SECURITY_BOT1_TRIPLE_VALUE; + break; + case SECURITY_BOT2_TRIPLE: + curr_shodan_val -= SECURITY_BOT2_TRIPLE_VALUE; + break; + case EXECBOT_TRIPLE: + curr_shodan_val -= EXECBOT_TRIPLE_VALUE; + break; +#endif + case CAMERA_TRIPLE: + curr_shodan_val -= CAMERA_TRIPLE_VALUE; + break; + case SMALL_CPU_TRIPLE: + curr_shodan_val -= SMALL_CPU_TRIPLE_VALUE; + break; + case LARGCPU_TRIPLE: + curr_shodan_val -= LARGCPU_TRIPLE_VALUE; + break; + } + if (curr_shodan_val < 0) { + // Warning(("SHODOMETER negative after decrementing of id = 0x%x\n",oid)); + curr_shodan_val = 0; + } + if (curr_shodan_val != QUESTVAR_GET(SHODAN_QV)) + update_shodometer(curr_shodan_val, game_stuff); + return (curr_shodan_val); + } else + return (0); +} diff --git a/engine/src/GameSrc/sideicon.c b/engine/src/GameSrc/sideicon.c new file mode 100644 index 0000000..457064a --- /dev/null +++ b/engine/src/GameSrc/sideicon.c @@ -0,0 +1,540 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/sideicon.c $ + * $Revision: 1.53 $ + * $Author: mahk $ + * $Date: 1994/11/23 04:31:51 $ + * + */ + +#include +#include + +#include "sideicon.h" +#include "sideart.h" +#include "popups.h" +#include "cybstrng.h" +#include "tools.h" +#include "fullscrn.h" +#include "newmfd.h" +#include "tools.h" +#include "wares.h" +#include "objsim.h" +#include "objclass.h" +#include "otrip.h" +#include "player.h" +#include "faketime.h" +#include "mfdext.h" +#include "objapp.h" +#include "musicai.h" +#include "sfxlist.h" +#include "gr2ss.h" +#include "canvchek.h" + +// --------- +// Constants +// --------- + +#define NUM_SIDE_ICONS 10 + +#define SIDE_ICONS_TOP_Y 24 +#define SIDE_ICONS_LEFT_X 4 +#define SIDE_ICONS_RIGHT_X 300 +#define SIDE_ICONS_HEIGHT 15 +#define SIDE_ICONS_WIDTH 15 +#define SIDE_ICONS_VSPACE 8 + +#define ICON_ART_ITEMS 2 +#define ICON_ART_OFF 0 +#define ICON_ART_ON 1 +#define ICON_ART_BACKGROUND 255 + +#define FLASH_RATE 256 +#define MAX_FLASH_COUNT 12 + +// ---------------- +// Local Prototypes +// ---------------- + +uchar side_icon_mouse_callback(uiEvent *e, LGRegion *r, intptr_t udata); +void zoom_side_icon_to_mfd(int icon, int waretype, int wnum); +uchar side_icon_hotkey_func(ushort keycode, uint32_t context, intptr_t i); +void side_icon_draw_bm(LGRect *r, ubyte icon, ubyte art); + +// ---------- +// Structures +// ---------- + +typedef struct _side_icon { + LGRect r; + uchar flashstate; + ubyte flashcount; + ubyte state; +} SIDE_ICON; + +typedef struct _icon_data { + byte waretype; + long waretrip; + int flashfx; +} ICON_DATA; + +// ------- +// Globals +// ------- + +SIDE_ICON side_icons[NUM_SIDE_ICONS]; +#ifdef PRELOAD_BITMAPS +grs_bitmap side_icon_bms[NUM_SIDE_ICONS][ICON_ART_ITEMS]; +grs_bitmap side_icon_background; +#else +#define side_icon_bmid(icon, art) (MKREF(RES_SideIconArt, (ICON_ART_ITEMS * icon) + art + 1)) +#define side_icon_backid (MKREF(RES_SideIconArt, 0)) +#endif + +#ifdef PROGRAM_SIDEICON +static char shiftnums[] = ")!@#$%^&*("; +static uchar programmed_sideicon = 0; +#endif + +// this is in wares.c +extern long ware_base_triples[NUM_WARE_TYPES]; + +#define IDX_OF_TYPE(type, trip) (OPTRIP(trip) - OPTRIP(ware_base_triples[type])) + +static ICON_DATA icon_data[NUM_SIDE_ICONS] = { + {WARE_HARD, BIOSCAN_HARD_TRIPLE}, + {WARE_HARD, FULLSCR_HARD_TRIPLE}, + {WARE_HARD, SENS_HARD_TRIPLE}, + {WARE_HARD, LANTERN_HARD_TRIPLE}, + {WARE_HARD, SHIELD_HARD_TRIPLE}, + {WARE_HARD, INFRA_GOG_TRIPLE}, + {WARE_HARD, NAV_HARD_TRIPLE}, + {WARE_HARD, VIDTEX_HARD_TRIPLE, SFX_EMAIL}, + {WARE_HARD, MOTION_HARD_TRIPLE}, + {WARE_HARD, JET_HARD_TRIPLE}, +}; + +grs_bitmap icon_cursor_bm[2]; +LGCursor icon_cursor[2]; +static char *cursor_strings[NUM_SIDE_ICONS]; +static char cursor_strbuf[128]; + +// ============ +// INITIALIZERS +// ============ + +// --------------------------------------------------------------------------- +// init_all_side_icons() +// +// Initialize all side icons to "unset" settings (should be called before +// wares_init()!). Also sets up their on-screen locations. +// And, as of 7/22, loads in all the bitmaps from memory. + +void side_icon_language_change(void) { + load_string_array(REF_STR_IconCursor, cursor_strings, cursor_strbuf, sizeof(cursor_strbuf), NUM_SIDE_ICONS); +} + +void init_all_side_icons() { + int i; + + // Now, figure out on-screen locations + + for (i = 0; i < (NUM_SIDE_ICONS / 2); i++) // left side first + { + side_icons[i].r.ul.x = SIDE_ICONS_LEFT_X; + side_icons[i].r.ul.y = SIDE_ICONS_TOP_Y + (i * (SIDE_ICONS_HEIGHT + SIDE_ICONS_VSPACE)); + + side_icons[i].r.lr.x = side_icons[i].r.ul.x + SIDE_ICONS_WIDTH; + side_icons[i].r.lr.y = side_icons[i].r.ul.y + SIDE_ICONS_HEIGHT; + } + + for (i = (NUM_SIDE_ICONS / 2); i < NUM_SIDE_ICONS; i++) // now right side + { + side_icons[i].r.ul.x = SIDE_ICONS_RIGHT_X; + side_icons[i].r.ul.y = side_icons[i - (NUM_SIDE_ICONS / 2)].r.ul.y; + + side_icons[i].r.lr.x = side_icons[i].r.ul.x + SIDE_ICONS_WIDTH; + side_icons[i].r.lr.y = side_icons[i].r.ul.y + SIDE_ICONS_HEIGHT; + } +} + +void init_side_icon_popups(void) { + side_icon_language_change(); + for (int i = 0; i < 2; i++) { + LGPoint offset = {0, -1}; + LGCursor *c = &icon_cursor[i]; + grs_bitmap *bm = &icon_cursor_bm[i]; + make_popup_cursor(c, bm, cursor_strings[i * NUM_SIDE_ICONS / 2], i + POPUP_ICON_LEFT, TRUE, offset); + } +} + +#ifdef DUMMY // not yet, bucko + +void init_side_icon_hotkeys(void) { + uchar side_icon_hotkey_func(ushort key, uint32_t context, intptr_t i); + uchar side_icon_progset_hotkey_func(ushort key, uint32_t context, intptr_t i); + uchar lantern_change_setting_hkey(ushort key, uint32_t context, intptr_t i); + uchar shield_change_setting_hkey(ushort key, uint32_t context, intptr_t i); + uchar side_icon_prog_hotkey_func(ushort key, uint32_t context, intptr_t notused); + int i; + + hotkey_add(KB_FLAG_ALT | KB_FLAG_DOWN | '4', DEMO_CONTEXT, lantern_change_setting_hkey, 0); + hotkey_add(KB_FLAG_ALT | KB_FLAG_DOWN | '5', DEMO_CONTEXT, shield_change_setting_hkey, 0); + + hotkey_add(KB_FLAG_DOWN | '0', DEMO_CONTEXT, side_icon_hotkey_func, NUM_SIDE_ICONS - 1); +#ifdef PROGRAM_SIDEICON + hotkey_add('`', DEMO_CONTEXT, ide_icon_prog_hotkey_func, 0); + hotkey_add(KB_FLAG_DOWN | shiftnums[0], DEMO_CONTEXT, side_icon_progset_hotkey_func, + NUM_SIDE_ICONS - 1); +#endif + for (i = 0; i < NUM_SIDE_ICONS - 1; i++) { + hotkey_add(KB_FLAG_DOWN | ('1' + i), DEMO_CONTEXT, side_icon_hotkey_func, i); +#ifdef PROGRAM_SIDEICON + hotkey_add(KB_FLAG_DOWN | shiftnums[1 + i], DEMO_CONTEXT, side_icon_progset_hotkey_func, + i); +#endif + } +} + +#endif // DUMMY + +// --------------------------------------------------------------------------- +// init_side_icon() +// + +// --------------------------------------------------------------------------- +// screen_init_side_icons(); +// +// Declare the appropriate regions for the side icons +// (called from screen_start() in screen.c) + +void screen_init_side_icons(LGRegion *root) { + int id; + LGRegion *left_region, *right_region; + LGRect r; + left_region = (LGRegion *)malloc(sizeof(LGRegion)); + right_region = (LGRegion *)malloc(sizeof(LGRegion)); + + // Wow, having a LGRegion for each of the side icons is totally uncool + // Let's just have two regions, and figure out from there. + + r.ul = side_icons[0].r.ul; + r.lr = side_icons[(NUM_SIDE_ICONS - 1) / 2].r.lr; + macro_region_create_with_autodestroy(root, left_region, &r); + uiInstallRegionHandler(left_region, UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE, &side_icon_mouse_callback, 0, + &id); + uiSetRegionDefaultCursor(left_region, NULL); + + r.ul = side_icons[(NUM_SIDE_ICONS + 1) / 2].r.ul; + r.lr = side_icons[(NUM_SIDE_ICONS - 1)].r.lr; + macro_region_create_with_autodestroy(root, right_region, &r); + uiInstallRegionHandler(right_region, UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE, &side_icon_mouse_callback, + ((NUM_SIDE_ICONS + 1) / 2), &id); + uiSetRegionDefaultCursor(right_region, NULL); + + return; +} + +// ========= +// SELECTION +// ========= + +// ---------------------------------------------------------------- +// select_side_icon() selects a given side icon. + +void zoom_side_icon_to_mfd(int icon, int waretype, int wnum) { + extern ubyte waretype2invtype[]; + + int mfd; + + mfd = mfd_grab_func(MFD_EMPTY_FUNC, MFD_ITEM_SLOT); + mfd_zoom_rect(&side_icons[icon].r, mfd); + set_inventory_mfd(waretype2invtype[waretype], wnum, TRUE); + mfd_change_slot(mfd, MFD_ITEM_SLOT); +} + +// --------------------------------------------------------------------------- +// side_icon_mouse_callback() +// +// Callback function for mouse clicks inside the side icons. + +extern LGCursor globcursor; + +int last_side_icon = -1; +uchar side_icon_mouse_callback(uiEvent *e, LGRegion *r, intptr_t udata) { + extern uchar fullscrn_icons; + uchar retval = FALSE; + int i, type, num; + + if (!global_fullmap->cyber && !(full_game_3d && !fullscrn_icons)) { + int ver; + + i = (int)udata + (e->pos.y - SIDE_ICONS_TOP_Y) / (SIDE_ICONS_HEIGHT + SIDE_ICONS_VSPACE); + type = icon_data[i].waretype; + num = IDX_OF_TYPE(type, icon_data[i].waretrip); + ver = get_player_ware_version(type, num); + + if (!RECT_TEST_PT(&side_icons[i].r, e->pos) || ver == 0) { + uiSetRegionDefaultCursor(r, &globcursor); + last_side_icon = -1; + return FALSE; + } + if (popup_cursors) { + if (last_side_icon != i) { + uchar side = i * 2 / NUM_SIDE_ICONS; + LGCursor *c = &icon_cursor[side]; + grs_bitmap *bm = &icon_cursor_bm[side]; + LGPoint offset = {0, -1}; + + free(bm->bits); + make_popup_cursor(c, bm, cursor_strings[i], side + POPUP_ICON_LEFT, TRUE, offset); + uiSetRegionDefaultCursor(r, c); + last_side_icon = i; + } + } + /* + if (m->action & MOUSE_RDOWN) + { + zoom_side_icon_to_mfd(i,type,num); + retval = TRUE; + } + */ + + if (!(e->mouse_data.action & MOUSE_LDOWN)) + return retval; // ignore click releases + // mprintf(" Side Icon %d: CYBER(%d,%d) [%x] REAL(%d,%d) [%x]\n", + // i, side_icons[i].cyber_type, side_icons[i].cyber_num, + // side_icons[i].cyber_set, side_icons[i].real_type, + // side_icons[i].real_num, side_icons[i].real_set); + + if (type >= 0) + use_ware(type, num); + retval = TRUE; + } + if (global_fullmap->cyber || (full_game_3d && !fullscrn_icons) || !popup_cursors) { + last_side_icon = -1; + uiSetRegionDefaultCursor(r, NULL); + } + + return retval; +} + +uchar side_icon_hotkey_func(ushort keycode, uint32_t context, intptr_t i) { + int type = icon_data[i].waretype; + int num = IDX_OF_TYPE(type, icon_data[i].waretrip); + if ((!global_fullmap->cyber) || (i == 1)) { + if (type >= 0) + use_ware(type, num); + } + return TRUE; +} + +#ifdef PROGRAM_SIDEICON +uchar side_icon_progset_hotkey_func(ushort keycode, uint32_t context, intptr_t i) { + char mess[80]; + int l; + programmed_sideicon = i; + get_string(REF_STR_PresetSideicon, mess, 80); + l = strlen(mess); + get_object_short_name(icon_data[i].waretrip, mess + l, 80 - l); + message_info(mess); + return TRUE; +} + +uchar side_icon_prog_hotkey_func(ushort keycode, uint32_t context, intptr_t notused) { + return (side_icon_hotkey_func(keycode, context, programmed_sideicon)); +} +#endif + +// ======== +// GRAPHICS +// ======== + +// --------------------------------------------------------------------------- +// side_icon_expose_all() +// +// Sort of an initial-draw-everything type of routine + +void side_icon_expose_all() { + for (uint8_t i = 0; i < NUM_SIDE_ICONS; i++) + side_icon_expose(i); +} + +// ---------------------------------------------------- +// zoom_to_side_icon(Point from, int icon) +// zooms a LGRect to a side icon and then exposes it. + +void zoom_to_side_icon(LGPoint from, int icon) { + LGRect start = {{-3, -3}, {3, 3}}; + LGRect dest; + RECT_MOVE(&start, from); + dest = side_icons[icon].r; + zoom_rect(&start, &dest); + side_icon_expose(icon); +} + +// --------------------------------------------------------------------------- +// side_icon_draw_bm() +// +// Draws a side icon of the specified ware, version, and status. + +void side_icon_draw_bm(LGRect *r, ubyte icon, ubyte art) { +#ifdef SVGA_SUPPORT + uchar old_over = gr2ss_override; + gr2ss_override = OVERRIDE_ALL; +#endif + if (is_onscreen()) + uiHideMouse(r); + if (art == ICON_ART_BACKGROUND) + draw_raw_resource_bm(side_icon_backid, r->ul.x, r->ul.y); + // draw_hires_resource_bm(side_icon_backid, SCONV_X(r->ul.x), SCONV_Y(r->ul.y)); + else + draw_raw_resource_bm(side_icon_bmid(icon, art), r->ul.x, r->ul.y); + // draw_hires_resource_bm(side_icon_bmid(icon,art), SCONV_X(r->ul.x), SCONV_Y(r->ul.y)); + if (is_onscreen()) + uiShowMouse(r); +#ifdef SVGA_SUPPORT + gr2ss_override = old_over; +#endif + return; +} + +// --------------------------------------------------------------------------- +// side_icon_expose() +// +// Draw a side icon appropriately, depending on the ware it points at, +// and its state. + +void side_icon_expose(ubyte icon_num) { + ubyte *player_wares, *player_status; + WARE *wares; + int type, num, n; + LGRect *r; + extern uchar fullscrn_icons; + + if (full_game_3d && (global_fullmap->cyber || !(fullscrn_icons))) + return; + r = &(side_icons[icon_num].r); + + type = icon_data[icon_num].waretype; + num = IDX_OF_TYPE(type, icon_data[icon_num].waretrip); + + if (type < 0) + return; + get_ware_pointers(type, &player_wares, &player_status, &wares, &n); + + // Possible expose cases: + // + // 1) We don't have the appropriate ware for that side icon. + if (player_wares[num] == 0) { + + // Expose screen background + // Spew(DSRC_TESTING_Test9,("icon number %d is empty\n",icon_num)); + if (!full_game_3d) + side_icon_draw_bm(r, icon_num, ICON_ART_BACKGROUND); + } + + // 3) We have the ware, and it's trying to get our attention. + // + else if (player_status[num] & WARE_FLASH) { + uchar fs = ((*tmd_ticks / FLASH_RATE) % 2); + if (fs == side_icons[icon_num].flashstate && !full_game_3d) + return; + + if (fs) { + side_icon_draw_bm(r, icon_num, ICON_ART_ON); + if (fs != side_icons[icon_num].flashstate) + if (icon_data[icon_num].flashfx != 0) + if (QUESTBIT_GET(0x12c)) + play_digi_fx(icon_data[icon_num].flashfx, 1); + } else + side_icon_draw_bm(r, icon_num, ICON_ART_OFF); + if (fs != side_icons[icon_num].flashstate) { + side_icons[icon_num].flashcount++; + if (side_icons[icon_num].flashcount >= MAX_FLASH_COUNT) { + side_icons[icon_num].flashcount = 0; + player_status[num] &= ~WARE_FLASH; + } + } + side_icons[icon_num].flashstate = fs; + } + + // 2) We have the ware, but we're turning it off. + // + else if (!(player_status[num] & WARE_ON)) { + + // Expose darkened bitmap of current version + // Spew(DSRC_TESTING_Test9,("icon number %d is off\n",icon_num)); + side_icon_draw_bm(r, icon_num, ICON_ART_OFF); + } else { + + // Expose normal (active) bitmap of current ware version + side_icon_draw_bm(r, icon_num, ICON_ART_ON); + } + + return; +} + +// --------------------------------------------------------------------------- +// side_icon_load_bitmaps() +// +// Load the bitmaps for all side icons and states from the resource system. + +errtype side_icon_load_bitmaps() { +#ifdef PRELOAD_BITMAPS + RefTable *side_icon_rft; + int i, j, index /*, file_handle */; + + // file_handle = ResOpenFile("sideart.res"); + // if (file_handle < 0) critical_error(CRITERR_RES|6); + + side_icon_rft = ResLock(RES_SideIconArt); + load_bitmap_from_res(&side_icon_background, RES_SideIconArt, 0, side_icon_rft, FALSE, NULL, NULL); + + for (i = 0; i < NUM_SIDE_ICONS; i++) { + + for (j = 0; j < ICON_ART_ITEMS; j++) { + + index = (ICON_ART_ITEMS * i) + j + 1; + load_bitmap_from_res(&(side_icon_bms[i][j]), RES_SideIconArt, index, side_icon_rft, FALSE, NULL, NULL); + } + } + ResUnlock(RES_SideIconArt); +// ResCloseFile(file_handle); +#endif + + return (OK); +} + +errtype side_icon_free_bitmaps() { +#ifdef PRELOAD_BITMAPS + int i, j, index; + Free(side_icon_background.bits); + for (i = 0; i < NUM_SIDE_ICONS; i++) { + + for (j = 0; j < ICON_ART_ITEMS; j++) { + + index = (ICON_ART_ITEMS * i) + j; + Free(side_icon_bms[i][j].bits); + } + } +#endif + return (OK); +} diff --git a/engine/src/GameSrc/sndcall.c b/engine/src/GameSrc/sndcall.c new file mode 100644 index 0000000..d177e53 --- /dev/null +++ b/engine/src/GameSrc/sndcall.c @@ -0,0 +1,60 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/sndcall.c $ + * $Revision: 1.2 $ + * $Author: dc $ + * $Date: 1994/11/28 08:31:43 $ + */ + +#include "digifx.h" +#include "musicai.h" + +#define MAX_UNLOCK 32 +int rulock_list[MAX_UNLOCK]; +int rulock_ptr = 0; + +/* KLC - not used in Mac version. +void cdecl simple_xmi_stop(snd_midi_parms *seq) +{ +// if (seq->snd_ref==0xc1c1) + Free(seq->data); + mono_ch(60,'A'+tmp); + tlc(tmp=(tmp+1)&0xf); + mono_ch(61,'a'+simple_xmi_sound_on); + simple_xmi_sound_on--; +} +*/ + +void digifx_EOS_callback(snd_digi_parms *sdp) { + /*if (sdp->snd_ref>0x10) + if (rulock_ptrsnd_ref;*/ +} + +void sound_frame_update(void) { + int i; + snd_digi_parms *sdp; + + for (i = 0; i < SND_MAX_SAMPLES; i++) { + snd_digi_parms *sdp = snd_sample_parms(i); + if (set_sample_pan_gain(sdp)) + snd_end_sample(i); + } +} diff --git a/engine/src/GameSrc/star.c b/engine/src/GameSrc/star.c new file mode 100644 index 0000000..8c8d301 --- /dev/null +++ b/engine/src/GameSrc/star.c @@ -0,0 +1,474 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/star/RCS/star.c $ + * $Revision: 1.2 $ + * $Author: buzzard $ + * $Date: 1994/11/11 19:51:04 $ + * + * Main star library source + * + * $Log: star.c $ + * Revision 1.2 1994/11/11 19:51:04 buzzard + * Anti/aliasing stars in high resolutions. + * Ugly hacked for system shock rather than changing interface. + * + * Revision 1.1 1994/10/24 23:27:31 jaemz + * Initial revision + * + * Revision 1.2 1994/10/21 16:45:47 jaemz + * *** empty log message *** + * + * Revision 1.1 1994/09/07 17:41:29 jaemz + * Initial revision + * + */ + +#include +#include "star.h" + +#include "OpenGL.h" + +//#define STAR_SPEW +#define STARS_ANTI_ALIAS + +#ifdef STEREO_ON +extern uchar g3d_stereo; +extern fix g3d_eyesep_raw; +extern uchar *g3d_rt_canv_bits; +extern uchar *g3d_lt_canv_bits; +#endif + +// globals for state +sts_vec *std_vec; +uchar *std_col; +int std_num; + +fix std_min_z = 0x7fffffff; +fix std_max_rad = 0; + +int std_size = 1; + +#ifdef STARS_ANTI_ALIAS +// The canvas must be more than pixels wide +// for us to anti-alias the stars (which makes them bigger, +// hence the size restriction) +int std_alias_size = 640; +// This size is chosen so anti-aliasing starts happening +// in full screen 640x400 modes. Won't happen in demo mode +// if demo mode uses a subcanvas + +// We must record the meaning of the colors of stars so +// we can anti-alias them +int std_color_base, std_color_range; + +// gamma-correct star colors +uchar std_alias_color_table[256]; +#endif + +extern g3s_vector _matrix_scale; +extern g3s_phandle _vbuf2; +extern int _n_verts; + +// prototypes +fix mag2_point(g3s_phandle p); +void do_aa_star_pixel(int x, int y, int fx, int fy, int c); +void do_aa_star(fix fx, fix fy, int c); +void star_init_alias_table(void); + +// sets global pointers in the star library +// to the number of stars, their positions, their colors +void star_set(int n, sts_vec *vlist, uchar *clist) { + std_num = n; + std_vec = vlist; + std_col = clist; +} + +// allocates the necessary space for stars using alloc +int star_alloc(int n) { + std_vec = (sts_vec *)malloc(n * sizeof(sts_vec) + n); + if (std_vec == NULL) + return -1; + std_col = (uchar *)(std_vec + n); + std_num = n; + return n; +} + +// frees star space using free, only if you've used +// alloc to allocate it +void star_free(void) { free(std_vec); } + +// renders star field in the polygon defined by the vertex list +// uses your 3d context, so make sure that's been set +// what we do is render a zero polygon, which is black, +// then render stars on that field. Beware of CLUT modes or +// other FILL modes. In general, will hurt this and we'll +// have to code around it. +// Then we rotate star field around viewer and draw them +// wherever there is black. Not dissimilar to Kevins +// per tmap hack + +extern g3s_vector view_position; + +#ifdef STAR_SPEW +extern int star_num_behind; +extern int star_num_projected; +#endif + +// for the love of god, I hate 3d scaling. +fix mag2_point(g3s_phandle p) { + fix a, f; + + a = fix_div(p->gX, _matrix_scale.gX); + f = fix_mul(a, a); + + a = fix_div(p->gY, _matrix_scale.gY); + f += fix_mul(a, a); + + a = fix_div(p->gZ, _matrix_scale.gZ); + f += fix_mul(a, a); + + return f; +} + +// in stereo mode, we can leave this normal +// and it should work, assuming the two +// polygons are similar enough +void star_poly(int n, g3s_phandle *vp) { + if(use_opengl()) { + // Stencil out this area where we want stars to draw + opengl_set_stencil(0xFF); + opengl_draw_poly(0x00, n, vp, 0); + opengl_set_stencil(0x00); + } + else { + // draw star poly in color zero (note that 255 is hacked black). This part very + // important, if not zero, won't work. + g3_draw_poly(0xff, n, vp); + } + + star_empty(n, vp); //fix disappearing stars; std_min_z was not being set below its max value, + //which caused star_render() to abort +} + +void star_empty(int n, g3s_phandle *vp) { + int i; + fix m; + int n1; + g3s_phandle dest[10]; // assume max clip 10 + + // clip it just like you would when you render + // then run through it + // set max out the maximum it could be, which would be eyesep raw + n1 = g3_clip_polygon(n, vp, dest); + for (i = 0; i < n1; ++i) { + if (dest[i]->gZ < std_min_z) + std_min_z = dest[i]->gZ; + m = mag2_point(dest[i]); + if (m > std_max_rad) + std_max_rad = m; + } +} + +// render stars to a sky-like thing, +// no viewports, clips to half sphere +void star_sky(void) { + std_min_z = 0; + std_max_rad = FIX_UNIT; +} + +#ifdef STARS_ANTI_ALIAS +// render a single pixel of an anti-aliased star +void do_aa_star_pixel(int x, int y, int fx, int fy, int c) { + int q; + + if (gr_get_pixel(x, y) == 0xff) { + q = fx * fy * c; + + // fx is % 0..256; fy is % 0..256; c is % 0..255. + + q = q >> 16; + + // q is now 0..255, which we rescale back into color range + + gr_set_pixel(std_alias_color_table[q], x, y); + } +} + +// render an anti-aliased star. +// this is a prime candidate for being made table driven +void do_aa_star(fix fx, fix fy, int c) { + // isolate the fractions and the integers + int x_frac = fix_frac(fx) >> 8; + int y_frac = fix_frac(fy) >> 8; + int x = fix_int(fx); + int y = fix_int(fy); + + int color = (std_color_base + std_color_range - 1 - c); + + // rescale the color so that it's 0..255, 0 = dark, 255 = light + color = (255 * color) / (std_color_range + 1); + + // ok, now compute the weightings for each pixel + + do_aa_star_pixel(x, y, 256 - x_frac, 256 - y_frac, color); + do_aa_star_pixel(x + 1, y, x_frac, 256 - y_frac, color); + do_aa_star_pixel(x, y + 1, 256 - x_frac, y_frac, color); + do_aa_star_pixel(x + 1, y + 1, x_frac, y_frac, color); +} + +void star_init_alias_table(void) { + // init gamma corrected table + int i, a; + fix b, gamma; + + gamma = fix_make(0, 30000); + + a = std_color_base + std_color_range - 1; + b = fix_make(-(std_color_range - 1), 0); + + for (i = 0; i < 256; ++i) + std_alias_color_table[i] = a + fix_int(fix_mul(b, fix_pow(fix_make(i, 0) / 255, gamma))); +} + +#endif + +void star_render(void) { + int i; + g3s_phandle s; + int x, y; + int x1, y1; + g3s_vector v; +#ifdef STEREO_ON + uchar old_stereo; +#endif +#ifdef STARS_ANTI_ALIAS + int anti_alias = grd_bm.w >= std_alias_size; +#endif + +#ifdef STAR_SPEW + star_num_behind = 0; + star_num_projected = 0; +#endif + +#if defined(STARS_ANTI_ALIAS) && defined(STEREO_ON) + if (g3d_stereo) + anti_alias = 0; +#endif + + // exit if no one every drew a star field anywhere visible + if (std_min_z == 0x7fffffff) { +#ifdef STAR_SPEW + mprintf("ignored\n"); +#endif + return; + } + + if(use_opengl()) { + opengl_begin_stars(); + } + +#ifdef STAR_SPEW + mprintf("max_rad = %x min_z = %x\n", fix_sqrt(star_max_rad), star_min_z); +#endif + if (std_min_z < 0) + std_min_z = 0; + +// scale by max radius +#ifndef STEREO_ON + g3_scale_object(fix_sqrt(std_max_rad)); +#else + // add in eyesep raw cause that's as much bigger it could be + g3_scale_object(fix_sqrt(std_max_rad) + (g3d_stereo ? g3d_eyesep_raw : 0)); +#endif + +#ifdef STEREO_ON + old_stereo = g3d_stereo; + g3d_stereo = 0; +#endif + + for (i = 0; i < std_num; ++i) { + // in theory if codes aren't set it's on the screen + + // unpack star vec to a normal vec + v.gX = ((fix)std_vec[i].x) << 1; + v.gY = ((fix)std_vec[i].y) << 1; + v.gZ = ((fix)std_vec[i].z) << 1; + + s = star_transform_point(&v); + + if (s->codes == 0) { + x = fix_rint(s->sx); + y = fix_rint(s->sy); + + if(use_opengl()) { + opengl_draw_star(s->sx, s->sy, std_col[i], anti_alias); + continue; + } + + if (std_size <= 1) { +#ifdef STARS_ANTI_ALIAS + if (anti_alias) { + do_aa_star(s->sx, s->sy, std_col[i]); + } else +#endif + if (gr_get_pixel(x, y) == 0xff) + gr_set_pixel(std_col[i], x, y); + } else { + for (x1 = x; x1 < x + std_size; ++x1) { + for (y1 = y; y1 < y + std_size; ++y1) { + if (gr_get_pixel(x1, y1) == 0xff) + gr_set_pixel(std_col[i], x1, y1); + } + } + } + +#ifdef STEREO_ON + if (old_stereo) { + // switch canvases quickly + grd_bm.bits = g3d_rt_canv_bits; + if (std_size <= 1) { + if (gr_get_pixel(x, y) == 0xff) + gr_set_pixel(std_col[i], x, y); + } else { + for (x1 = x; x1 < x + std_size; ++x1) { + for (y1 = y; y1 < y + std_size; ++y1) { + if (gr_get_pixel(x1, y1) == 0xff) + gr_set_pixel(std_col[i], x1, y1); + } + } + } + // switch back + grd_bm.bits = g3d_lt_canv_bits; + } +#endif + } + + g3_free_point(s); + } + + if(use_opengl()) { + opengl_end_stars(); + } + +#ifdef STEREO_ON + g3d_stereo = old_stereo; +#endif + +#ifdef STAR_SPEW + mprintf("stars = %d behind = %d proj = %d\n", st_num, star_num_behind, star_num_projected); +#endif + + // reset min z and max rad + std_min_z = 0x7fffffff; + std_max_rad = 0; +} + +// stuffs random vectors and colors into the set areas +// randomly assigning a color range to them +// feel free to seed +void star_rand(uchar col, uchar range) { + int i; + g3s_vector v; + sts_vec *s; + fix m; + +#ifdef STARS_ANTI_ALIAS + // SYSTEM SHOCK HACK! + std_color_base = 208; // col; + std_color_range = 16; // range; + + star_init_alias_table(); +#endif + + for (i = 0; i < std_num; ++i) { + s = &std_vec[i]; + + v.gX = ((rand() % 4000) - 2000) << 8; + v.gY = ((rand() % 4000) - 2000) << 8; + v.gZ = ((rand() % 4000) - 2000) << 8; + + m = fix_mul(v.gX, v.gX) + fix_mul(v.gY, v.gY) + fix_mul(v.gZ, v.gZ); + + if (m < FIX_UNIT / 100) { + i = i - 1; + continue; + } + + m = fix_sqrt(m); + + // normalize for fun hack + v.gX = fix_div(v.gX, m); + v.gY = fix_div(v.gY, m); + v.gZ = fix_div(v.gZ, m); + + // put into star vec + s->x = (v.gX >> 1); + s->y = (v.gY >> 1); + s->z = (v.gZ >> 1); + + // assign color + std_col[i] = rand() % range + col; + } +} + +extern g3s_point *first_free; +extern g3s_matrix view_matrix; + +// matrix rotate and code a star point. Project if clip codes +// are not set, rotate fully if in front of viewer +// (or smaller than a boundary +// takes pointer to vector +// returns point +g3s_phandle star_transform_point(g3s_vector *v) { + g3s_point *point; + int64_t r; + fix temp; + + getpnt(point); + point->p3_flags = 0; + + // third column (z) + r = fix64_mul(v->gX, vm3) + fix64_mul(v->gY, vm6) + fix64_mul(v->gZ, vm9); + temp = fix64_to_fix(r); + + // check out z, see if behind + if (temp < std_min_z) { + point->codes = CC_BEHIND; + return (point); + } + + point->gZ = temp; // save z + + // first column (x) + r = fix64_mul(v->gX, vm1) + fix64_mul(v->gY, vm4) + fix64_mul(v->gZ, vm7); + point->gX = fix64_to_fix(r); + + // second column (y) + r = fix64_mul(v->gX, vm2) + fix64_mul(v->gY, vm5) + fix64_mul(v->gZ, vm8); + point->gY = fix64_to_fix(r); + + // call clip codes + if (code_point(point)) + return (point); + + // transform if not clipped + g3_project_point(point); + return (point); +} diff --git a/engine/src/GameSrc/statics.c b/engine/src/GameSrc/statics.c new file mode 100644 index 0000000..43621bf --- /dev/null +++ b/engine/src/GameSrc/statics.c @@ -0,0 +1,81 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/statics.c $ + * $Revision: 1.8 $ + * $Author: xemu $ + * $Date: 1994/11/01 09:18:31 $ + * + * static variables and the people who love them + */ + +// the idea is you can take just obj mem, tmap and obj, or both +// + either big buffer or the frame buffer or both + +// still need to have static.h for people to use this stuff... + +// how do we get this aligned right. +// perhaps do this file in asm for real? +// if we did it in asm, and had these point, then we could use +// labels for top and bottom, which would be good... +// sadly, this alphabetizes, since it is so cool + +// put big buffer here? and have a define after it + +#include "textmaps.h" +uchar tmap_static_mem[NUM_STATIC_TMAPS * SIZE_STATIC_TMAP]; +#ifdef SVGA_CUTSCENES +uchar tmap_big_buffer[NUM_STATIC_TMAPS * SIZE_BIG_TMAP]; +#endif + +#include "objects.h" +#include "objapp.h" +Obj objs[NUM_OBJECTS]; +ObjRef objRefs[NUM_REF_OBJECTS]; +uchar objsDealt[NUM_OBJECTS / 8]; + +// put rest of obj system here, define after it + +#include "mfddims.h" +#define FRAME_BUFFER_SIZE (320 * 200) + 4096 +uchar frameBuffer[FRAME_BUFFER_SIZE]; +#define WACKY_SVGA_MFD_SIZE 52744 +uchar frameBuffer2[WACKY_SVGA_MFD_SIZE]; + +uchar *mfd_canvas_bits = NULL; + +#define ALTERNATE_BUFFER_SIZE ((MFD_VIEW_HGT * MFD_VIEW_WID) + FRAME_BUFFER_SIZE) + +#include "map.h" +#define STATIC_MAP_SIZE 16 << (DEFAULT_XSHF + DEFAULT_YSHF) + +uchar static_map[STATIC_MAP_SIZE]; + +#include "objprop.h" + +#define OBJ_BITMAP_POOL_SIZE ((NUM_OBJECT * 2) + 230) +grs_bitmap obj_bitmap_pool[OBJ_BITMAP_POOL_SIZE]; + +#include "svgacurs.h" +uchar svga_cursor_bits[SVGA_CURSOR_WIDTH * SVGA_CURSOR_HEIGHT]; +grs_bitmap svga_cursor_bmp; + +#define MAX_OPT_WID 154 +#define MAX_OPT_HT 58 +uchar svga_options_cursor_bits[MAX_OPT_WID * MAX_OPT_HT]; diff --git a/engine/src/GameSrc/status.c b/engine/src/GameSrc/status.c new file mode 100644 index 0000000..40211e2 --- /dev/null +++ b/engine/src/GameSrc/status.c @@ -0,0 +1,963 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/status.c $ + * $Revision: 1.77 $ + * $Author: kevin $ + * $Date: 1994/11/29 11:50:25 $ + */ + +// Source code for handling the status indicators for the +// Citadel game screen +// +// Source is divided into two segments; first comes all the +// biorhythm stuff (upper left hand corner of the game screen) +// and second, the upper right hand healthy/suit-energy measuring +// bar graphs + +#include + +#include "status.h" +#include "player.h" +#include "tools.h" +#include "gamescr.h" +#include "faketime.h" +#include "fullscrn.h" +#include "gamesys.h" +#include "physics.h" +#include "objsim.h" +#include "biotrax.h" +#include "otrip.h" + +#include "curdat.h" + +#include "gr2ss.h" + +// Defines +#define NUM_BIO_TRACKS 8 + +#define GAMESCR_BIO 0 +#define GAMESCR_BIO_X 5 +#define GAMESCR_BIO_Y 1 +//#define GAMESCR_BIO_WIDTH 149 +#define GAMESCR_BIO_WIDTH 131 +#define GAMESCR_BIO_HEIGHT 17 + +#define DIFF_BIO 1 +#define DIFF_BIO_X 6 +#define DIFF_BIO_Y 181 +#define DIFF_BIO_WIDTH 307 +#define DIFF_BIO_HEIGHT 17 + +#define STATUS_CHI_AMP 8 + +#define STATUS_BIO_X curr_bio_x +#define STATUS_BIO_Y curr_bio_y +#define STATUS_BIO_WIDTH curr_bio_w +#define STATUS_BIO_HEIGHT curr_bio_h + +#define STATUS_START_OFFSET 0 +#define STATUS_BIO_Y_DELTA 1 +#define MAX_BIO_LENGTH 307 +#define STATUS_BIO_LENGTH (STATUS_BIO_WIDTH - STATUS_START_OFFSET) +#define STATUS_BIO_TAIL 30 +#define STATUS_BIO_PEAK (STATUS_BIO_HEIGHT - 3) // 3 because of zany art size +#define STATUS_BIO_X_BASE (STATUS_BIO_X + STATUS_START_OFFSET) +#define STATUS_BIO_Y_BASE (STATUS_BIO_Y + STATUS_BIO_HEIGHT - STATUS_BIO_Y_DELTA - 2) + +#define SPIKE_THRESHOLD 4 +#define COLOR_CHANGES 6 +#define COLOR_LENGTH (STATUS_BIO_TAIL / COLOR_CHANGES) + +#define MAX_TAIL_LENGTH 5 +#define NO_HEIGHT 0x1f +#define INVALID_HEIGHT 0xE0 + +#define COLOR_BIO_MASK 0xE0 // Top Three bits signify depth of color +#define HEIGHT_BIO_MASK 0x1f // Bottom Five bits signify height +#define BIT6 0x20 // First bit of the color field. + +#define COLOR_BIT_SHIFT(x) ((x) << 5) + +//#define FIND_OVERLAP(x,y) (((x) - y + STATUS_BIO_LENGTH) % STATUS_BIO_LENGTH) + +#define STATUS_VITALS_X 184 +#define STATUS_VITALS_Y 0 +#define STATUS_VITALS_WIDTH 130 +#define STATUS_VITALS_HEIGHT 17 + +#define STATUS_VITALS_X_BASE (STATUS_VITALS_X + 4) +#define STATUS_VITALS_Y_TOP (STATUS_VITALS_Y + 1) +#define STATUS_VITALS_Y_BOTTOM (STATUS_VITALS_Y + 11) +#define STATUS_VITALS_H 8 +#define STATUS_VITALS_W (STATUS_VITALS_WIDTH - 9) + +#define STATUS_X 4 +#define STATUS_Y 1 +#define STATUS_HEIGHT 20 +#define STATUS_WIDTH 312 + +#define GAMESCR_BIO_REF REF_IMG_bmBiorhythm +#define DIFF_BIO_REF REF_IMG_bmDiffBio +#define STATUS_RESID curr_bio_ref +#define STATUS_RES_VITALSID REF_IMG_bmVitals +#define STATUS_RES_HEALTH_ID REF_IMG_bmVitalInnardsTop +#define STATUS_RES_ENERGY_ID REF_IMG_bmVitalInnardsBottom + +// Special Status Biorhythm variables + +#define NO_SPIKE 0x01 +#define SPIKE_NOISE 0x02 + +// Look ma, a new biorhythm option. +#define BOTTOMLESS 0x04 // things that fall off the bottom don't get drawn. + +// For now, turn these on so it won't try to draw any biorhythm stuff +#define TIMING_PROCEDURES_OFF +//#define TIMING_CALLBACK_OFF + +// Struct to contain information about tail + +typedef struct { + uchar free; + int *data; + uchar height[MAX_BIO_LENGTH]; + int head; + uchar tail; + uchar tail_length; + uchar color_length; + int update_time; + int counter; + int max_value; + uchar special; + uchar active; +} bio_data_block; + +uchar gBioInited = FALSE; + +uchar status_background[(DIFF_BIO_WIDTH + 4) * (DIFF_BIO_HEIGHT + 2)]; +// uchar status_background[(266+4)*(44+2)]; +uchar bio_data_buffer[NUM_BIO_TRACKS * sizeof(bio_data_block)]; + +#define FRAME_RATE_SCALE 20 +#define LOOPLINE_SCALE 0x2F + +bio_data_block *bio_data; + +int bio_time_id; + +int curr_bio_x, curr_bio_y; +int curr_bio_w, curr_bio_h; +short curr_bio_mode; +Ref curr_bio_ref; + +static uchar track_colors[NUM_BIO_TRACKS] = { + 0x66, // turquoise + 0x57, // yellow green? + 0x73, // blue + 0x40, // orange + 0x4A, // yellow + 0x28, // gold + 0x33, // red + 0x20, // purple +}; + +/* bio keeps private canvas so we don't have to save/restore stuff from + the real screen canvas. */ +static grs_canvas bio_canvas; + +#ifdef SYNCH_BIORHYTHMS +grs_bitmap bio_bitmap; + +#define BIO_BITMAP_SIZE ((GAMESCR_BIO_X + GAMESCR_BIO_WIDTH) * (GAMESCR_BIO_Y + GAMESCR_BIO_HEIGHT)) +uchar bio_bitmap_bits[BIO_BITMAP_SIZE]; +#endif + +extern LGPoint LastCursorPos; + +// Internal Prototype + +uchar under_bio(int x); +void ss_save_under_set_pixel(int color, short i, short j); +void bio_set_pixel(int color, short x, short y); +void bio_restore_pixel(grs_bitmap *bmp, short x, short y); +void bio_vline(int color, int x, int y, int y1); +uchar status_track_free(int track); +uchar status_track_active(int track); +void status_track_activate(int track, uchar active); +void draw_lower_tracks(int track_number, int location); +void draw_one_location_tracks(int location); +void draw_bio_height(int track_number, int draw_location); +errtype clear_bio_tracks(void); +void clear_tail(int track_number, int delete_location); +int FIND_OVERLAP(int x, int y); + +void change_bio_vars(void); + +void (*bio_funcs[])(void) = {gamescr_bio_func, diff_bio_func}; + +uchar under_bio(int x) { + if (((x >= 16) && (x <= 22)) || ((x >= 69) && (x <= 74)) || ((x >= 114) && (x <= 208)) || + ((x >= 252) && (x <= 258)) || ((x >= 298) && (x <= 303))) + return (TRUE); + return (FALSE); +} + +// --------------------------------------------------------- +// bio_set_pixel(int color, int x, int y) +// +// calls gr_set_pixel as normal, unless the biorhythms is +// going "under" something + +void ss_save_under_set_pixel(int color, short i, short j) { + extern LGPoint LastCursorPos; + + if (LastCursor != NULL) { + if ((LastCursorPos.x + SaveUnder.bm.w > i) && (LastCursorPos.x <= i)) { + if ((LastCursorPos.y + SaveUnder.bm.h > j) && (LastCursorPos.y <= j)) { + grs_bitmap *bm = (grs_bitmap *)(LastCursor->state); + int k = (i - LastCursorPos.x) + SaveUnder.bm.row * (j - LastCursorPos.y); + SaveUnder.bm.bits[k] = color; + k = (i - LastCursorPos.x) + bm->row * (j - LastCursorPos.y); + if (bm->bits[k]) { + return; + } + } + } + } + gr_set_pixel(color, i, j); + // mprintf("x:%i, y:%i, w:%i, h:%i\n",LastCursorPos.x,LastCursorPos.y,SaveUnder.bm.w,SaveUnder.bm.h); +} + +void bio_set_pixel(int color, short x, short y) { + short x0, x1, y0, y1, i, j; + + if ((curr_bio_mode == DIFF_BIO) && (under_bio(x))) + return; + if (convert_use_mode) { + x0 = SCONV_X(x); + y0 = SCONV_Y(y); + x1 = SCONV_X(x + 1); + y1 = SCONV_Y(y + 1); + for (i = x0; i < x1; i++) + for (j = y0; j < y1; j++) + ss_save_under_set_pixel(color, i, j); + } else + ss_save_under_set_pixel(color, x, y); +} + +// --------------------------------------------------------- +// Restore the pixels from the offscreen background bitmap. +// --------------------------------------------------------- +void bio_restore_pixel(grs_bitmap *bmp, short x, short y) { + if ((curr_bio_mode == DIFF_BIO) && (under_bio(x))) + return; + + short a, b, c, d; + STORE_CLIP(a, b, c, d); + ss_cset_cliprect(&bio_canvas, x, y, x + 1, y + 1); + ss_bitmap(bmp, STATUS_BIO_X, STATUS_BIO_Y); + RESTORE_CLIP(a, b, c, d); +} + +// --------------------------------------------------------- +// bio_vline(int x, int y, int y1) +// +// calls bio_vline as normal, unless the biorhythms is +// going "under" something + +void bio_vline(int color, int x, int y, int y1) { + if ((curr_bio_mode == DIFF_BIO) && (under_bio(x))) + return; +#ifdef SVGA_SUPPORT + if (convert_use_mode != 0) { + short x0, x1, i, j; + x0 = SCONV_X(x); + x1 = SCONV_X(x + 1); + y = SCONV_Y(y); + y1 = SCONV_Y(y1); + if (y > y1) { + int foo = y; + y = y1; + y1 = foo; + } + for (i = x0; i < x1; i++) + for (j = y; j <= y1; j++) + ss_save_under_set_pixel(color, i, j); + } else +#endif + { + if (y > y1) { + int foo = y; + y = y1; + y1 = foo; + } + for (; y <= y1; y++) + ss_save_under_set_pixel(color, x, y); + } +} + +// --------------------------------------------------------- +// status_bio_set(short bio_mode) +// +// Tell the biorhythm code which screen we are on + +short bios_x[2] = {GAMESCR_BIO_X, DIFF_BIO_X}; +short bios_y[2] = {GAMESCR_BIO_Y, DIFF_BIO_Y}; +short bios_w[2] = {GAMESCR_BIO_WIDTH, DIFF_BIO_WIDTH}; +short bios_h[2] = {GAMESCR_BIO_HEIGHT, DIFF_BIO_HEIGHT}; +Ref bio_refs[2] = {GAMESCR_BIO_REF, DIFF_BIO_REF}; + +grs_bitmap bio_background_bitmap; + +void status_bio_set(short bio_mode) { + FrameDesc *f; + int i; +#ifdef SVGA_SUPPORT + uchar old_over = gr2ss_override; +#endif + + curr_bio_mode = bio_mode; + + // Assuming there are only 8 bio tracks!!!!! + /* set biorhythm positions & clipping rectangle. */ + curr_bio_x = bios_x[bio_mode]; + curr_bio_y = bios_y[bio_mode]; + curr_bio_w = bios_w[bio_mode]; + curr_bio_h = bios_h[bio_mode]; +#ifdef SVGA_SUPPORT + gr2ss_override = OVERRIDE_ALL; +#endif + ss_cset_cliprect(&bio_canvas, STATUS_BIO_X, STATUS_BIO_Y, STATUS_BIO_X + STATUS_BIO_WIDTH, + STATUS_BIO_Y + STATUS_BIO_HEIGHT); +#ifdef SVGA_SUPPORT + gr2ss_override = old_over; +#endif + + curr_bio_ref = bio_refs[bio_mode]; + f = RefLock(STATUS_RESID); + bio_background_bitmap = f->bm; + + // let's try to do the right thing! + bio_background_bitmap.bits = status_background; + + LG_memcpy(bio_background_bitmap.bits, (char *)(f + 1), sizeof(char) * f->bm.w * f->bm.h); + + bio_data = (bio_data_block *)bio_data_buffer; + for (i = 0; i < NUM_BIO_TRACKS; i++) + bio_data[i].free = TRUE; + + RefUnlock(STATUS_RESID); + bio_funcs[curr_bio_mode](); +} + +// --------------------------------------------------------- +// status_bio_init() +// +// Do the init stuff for the biorhythm. 2d should be initialized and +// screen canvas should be unchanged when this is called. + +void status_bio_update_screenmode() { bio_canvas = *grd_screen_canvas; /* make copy for int routine */ } + +void status_bio_init(void) { + status_bio_update_screenmode(); + +#ifndef TIMING_PROCEDURES_OFF + bio_time_id = tm_add_process((void (*)())status_bio_update, 0, TMD_FREQ / 140); +#endif +} + +void status_bio_start(void) { + if (!full_game_3d) + gBioInited = TRUE; + +#ifndef TIMING_PROCEDURES_OFF + tm_activate_process(bio_time_id); +#endif +} + +void status_bio_end(void) { + gBioInited = FALSE; +// Free(bio_background_bitmap.bits); +#ifndef TIMING_PROCEDURES_OFF + tm_deactivate_process(bio_time_id); +#endif +} + +// ----------------------------------------------------------- +// status_bio_draw() +// +// Draw the biorhythm background + +void status_bio_draw(void) { + int i; + + // Draw the background map + ss_bitmap(&bio_background_bitmap, STATUS_BIO_X, STATUS_BIO_Y); + // gr_bitmap(&bio_background_bitmap, SCONV_X(STATUS_BIO_X), SCONV_Y(STATUS_BIO_Y)); + + // Go from left to right and draw all the tracks + for (i = 0; i < STATUS_BIO_LENGTH; i++) + draw_one_location_tracks(i); +} + +// ----------------------------------------------- +// Accessors for the "active" field. + +uchar status_track_free(int track) { return bio_data[track].free; } + +uchar status_track_active(int track) { return bio_data[track].active; } + +void status_track_activate(int track, uchar active) { bio_data[track].active = active; } + +// +// ---------------------------------------------------------------------- +// status_bio_add() +// +// *var - address of variable to be tracked +// max_value - scale factor for value of variable +// update_time - "bio time blocks" between update (can be any value) +// track_number - track which this biorhythm should take (0 to NUM_BIO_TRACKS-1) +// tail_length - length of tail (value * STATUS_BIO_TAIL) +// special - special characteristics of this biorhythm +// +// Add a variable to be tracked by the biorhythm monitor. +// Track the NULL pointer to clear out a track slot. +// + +errtype status_bio_add(int *var, int max_value, int update_time, int track_number, int tail_length, uchar special) { + bio_data_block *new_block; + uchar value; + int var_value; + + if (var == NULL) // are we trying to clear out a track slot?? + { + if (bio_data[track_number].free == TRUE) + return (ERR_NOEFFECT); + else { + bio_data[track_number].free = TRUE; + bio_data[track_number].active = FALSE; + return (OK); + } + } + + // Make sure we have a valid entry + if ((bio_data[track_number].free == FALSE) || // unused track? + (track_number >= NUM_BIO_TRACKS) || // correct track number? + (tail_length < 1) || // non-negative tail length? + (tail_length > MAX_TAIL_LENGTH)) // too long of a tail? + return (ERR_NOEFFECT); + else { + // Initialize the new biorhythm + + new_block = bio_data + track_number; + new_block->free = FALSE; + new_block->data = var; + LG_memset(&(new_block->height), INVALID_HEIGHT, sizeof(uchar) * MAX_BIO_LENGTH); + + // We must first check if the variable is greater than max value, if so make it max_value + var_value = (*var > max_value) ? max_value : *var; + + // Then check that it's not below 0 + if (var_value < 0) + var_value = 0; + + value = (var_value * STATUS_BIO_PEAK) / max_value; + + // Set the end of the bio line to be the same as the initial + // This way - we don't have to check if we've started a tail + // when we erase the first value. + new_block->height[STATUS_BIO_LENGTH - 1] = value | INVALID_HEIGHT; + + // Initialize the struct + new_block->head = 0; // head represents the first "new" x location + new_block->tail = FALSE; + new_block->tail_length = tail_length * STATUS_BIO_TAIL; + new_block->color_length = tail_length * COLOR_LENGTH; + new_block->update_time = update_time; + new_block->counter = 0; + new_block->max_value = max_value; + new_block->special = special; + new_block->active = TRUE; + + return (OK); + } +} + +errtype clear_bio_tracks() { + int i; + for (i = 0; i < NUM_BIO_TRACKS; i++) + status_bio_add(NULL, 0, 0, i, 0, 0); + return (OK); +} + + +//#define FIND_OVERLAP(x,y) (((x) - y + STATUS_BIO_LENGTH) % STATUS_BIO_LENGTH) +int FIND_OVERLAP(int x, int y) { return (((x)-y + STATUS_BIO_LENGTH) % STATUS_BIO_LENGTH); } + +#ifdef SYNCH_BIORHYTHMS +#ifdef SVGA_SUPPORT +// ------------------------------------------------------------------------ +// status_bio_synchronous() +// +// Does the synchronous blitting of biorhythm canvas +void status_bio_synchronous() { + if (!convert_use_mode) + return; + gr_push_canvas(grd_screen_canvas); + ss_scale_bitmap(&bio_bitmap, 0, 0, GAMESCR_BIO_WIDTH, GAMESCR_BIO_HEIGHT); + gr_pop_canvas(); +} +#endif +#endif + +// ------------------------------------------------------------------------ +// status_bio_update() +// +// Draw in the biorhythm stuff +// Does clever incremental redraw (but not yet) + +void status_bio_update(void) { +#ifndef TIMING_CALLBACK_OFF + uchar color; + int i; + int j; + bio_data_block *curr_blk; + int the_head; + long color_base; + int draw_location; + int var_value; + static grs_canvas *old_canvas; + + if (!gBioInited) + return; + + MouseLock++; + if (MouseLock > 1) { + MouseLock--; + return; + } + change_bio_vars(); + old_canvas = grd_canvas; + gr_set_canvas(&bio_canvas); + curr_blk = bio_data; + +#ifdef SVGA_SUPPORT + gr_push_state(); +#endif + for (i = 0; i < NUM_BIO_TRACKS; i++, curr_blk++) { + if (curr_blk->free == FALSE) { + // We must check to see if this track should be drawn now, + // or must it wait until it's time + if (curr_blk->counter < curr_blk->update_time) { + curr_blk->counter++; + } else { + curr_blk->counter = 0; + color_base = track_colors[i]; // Prevent need to use index every time + the_head = curr_blk->head; // Prevent need to look inside data structure every time + + // We must first check if the variable is greater than max value, if so make it max_value + var_value = (*(curr_blk->data) > curr_blk->max_value) ? curr_blk->max_value : (*(curr_blk->data)); + + // Then check that it's not below 0 + curr_blk->height[the_head] = (var_value <= 0) ? 0 : (var_value * STATUS_BIO_PEAK) / curr_blk->max_value; + + if (curr_blk->special & BOTTOMLESS && curr_blk->height[the_head] == 0) + curr_blk->height[the_head] = INVALID_HEIGHT; + + // draw the head + draw_lower_tracks(i, the_head); + + // Draw the first trailer pixel - We know that this pixel must exist because we drew the first pixel + // when we started the biorhythm + + draw_location = FIND_OVERLAP(the_head, 1); + curr_blk->height[draw_location] |= COLOR_BIT_SHIFT(1); // shift color + draw_lower_tracks(i, draw_location); + + if (curr_blk->tail == FALSE) { + // Since we don't have a tail - + // we don't know how long the biorhythm is + // Let's find out what we have to dim!!!! + + for (j = 0, draw_location = the_head - curr_blk->color_length; + j < the_head / curr_blk->color_length; j++, draw_location -= curr_blk->color_length) { + color = curr_blk->height[draw_location] & COLOR_BIO_MASK; + if ((color != COLOR_BIT_SHIFT(j + 1)) || (rand() > (RAND_MAX / 2))) { + curr_blk->height[draw_location] &= HEIGHT_BIO_MASK; + curr_blk->height[draw_location] |= COLOR_BIT_SHIFT(j + 2); + draw_lower_tracks(i, draw_location); + } + } + + // Have we gotten to the point where we can see the end of the tail???? + + if (the_head == curr_blk->tail_length) { + curr_blk->tail = TRUE; // start the tail + clear_tail(i, 0); // clear the first spot + } + } else { + // Since we have a tail - we know that we will have three spots to dim and one + // to delete from the biorhythm - let's dim the three spots first. + + for (j = 0, draw_location = FIND_OVERLAP(the_head, curr_blk->color_length); j < (COLOR_CHANGES - 1); + j++, draw_location = FIND_OVERLAP(draw_location, curr_blk->color_length)) { + color = curr_blk->height[draw_location] & COLOR_BIO_MASK; + if ((color != COLOR_BIT_SHIFT(j + 1)) || (rand() > (RAND_MAX / 2))) { + curr_blk->height[draw_location] &= HEIGHT_BIO_MASK; + curr_blk->height[draw_location] |= COLOR_BIT_SHIFT(j + 2); + draw_lower_tracks(i, draw_location); + } + } + + // Let's delete the end of the tail + clear_tail(i, draw_location); + } + + // Advance the head + curr_blk->head = (curr_blk->head + 1) % STATUS_BIO_LENGTH; + } + } + } + +#ifdef SVGA_SUPPORT + gr_pop_state(); +#endif + gr_set_canvas(old_canvas); + MouseLock--; +#endif +} + +// --------------------------------------------------------------------------------------- +// draw_bio_height() +// +// Note: requires bio_mouse_rect's x coordinates have already +// been set. We do this since we only draw_one_location_tracks +// calls this function. + +void draw_bio_height(int track_number, int draw_location) { + bio_data_block *curr_blk; + long color; + int x; + int y; + int y1; + uchar height; + int prevHeight; + int prevSpike; + + curr_blk = bio_data + track_number; + + // Extract the raw "height" information + height = curr_blk->height[draw_location]; + + if ((height & HEIGHT_BIO_MASK) == 0 && (curr_blk->special & BOTTOMLESS)) + return; + + // If we have an invalid height, continue no further + if ((height & INVALID_HEIGHT) == INVALID_HEIGHT) + return; + + // Extract the color offset + color = track_colors[track_number] + ((height & COLOR_BIO_MASK) >> 5); + + // Extract the actual height + height &= HEIGHT_BIO_MASK; + + // Get the previous height - to check for a spike + prevHeight = prevSpike = curr_blk->height[FIND_OVERLAP(draw_location, 1)]; + prevHeight &= HEIGHT_BIO_MASK; + + x = STATUS_BIO_X_BASE + draw_location; + y = STATUS_BIO_Y_BASE - height; + + if ((abs(height - prevHeight) < SPIKE_THRESHOLD) || (curr_blk->special & NO_SPIKE) || + ((prevSpike & INVALID_HEIGHT) == INVALID_HEIGHT)) { + bio_set_pixel(color, x, y); + } else { + y1 = STATUS_BIO_Y_BASE - prevHeight; + + bio_vline(color, x, y, y1); + } +} + +// ---------------------------------------------------------- +// clear_tail() +// +// KLC - greatly simplified this for Mac version, where the offscreen background +// bitmap is the same size as onscreen. When clearing, we simply copy the back- +// ground bitmap directly back onto the screen. bio_restore_pixel() handles +// figuring out where to restore from. +// ---------------------------------------------------------- +void clear_tail(int track_number, int delete_location) { + bio_data_block *curr_blk; + int prevHeight; + int prevLocation; + int height; + int x, y, y1; + int i, delta, base; + + curr_blk = bio_data + track_number; + prevLocation = FIND_OVERLAP(delete_location, 1); + + prevHeight = curr_blk->height[prevLocation] & HEIGHT_BIO_MASK; + height = curr_blk->height[delete_location] & HEIGHT_BIO_MASK; + + x = STATUS_BIO_X_BASE + delete_location; + y = STATUS_BIO_Y_BASE - height; + + // First, check to see if we're restoring for a v-line spike. + delta = abs(height - prevHeight); + if ((delta >= SPIKE_THRESHOLD) && !((delete_location == 0) && (curr_blk->tail == FALSE))) { + y1 = STATUS_BIO_Y_BASE - prevHeight; + if (y > y1) + base = y1; + else + base = y; + for (i = 0; i < delta + 1; i++) { + bio_restore_pixel(&bio_background_bitmap, x, base + i); + } + } else { + bio_restore_pixel(&bio_background_bitmap, x, y); + } + + // Invalidate the height at the delete location + curr_blk->height[delete_location] |= INVALID_HEIGHT; + + draw_one_location_tracks(delete_location); +} + +// ------------------------------------------------------ +// draw_one_location_tracks() +// +// procedure draws all tracks at a location along +// the biorhythm +// use this to draw to preserve ordering of tracks (overlapping) + +void draw_one_location_tracks(int location) { + int i; + + for (i = (NUM_BIO_TRACKS - 1); i >= 0; i--) { + if (!bio_data[i].free && bio_data[i].active) + draw_bio_height(i, location); + } +} + +// -------------------------------------------- +// draw_lower_tracks() +// +// procedure differs from draw_one_location_tracks in that it only +// draws tracks that are under the track number, including the track +// number given. Saves a little work. +// + +void draw_lower_tracks(int track_number, int location) { + int i; + + for (i = track_number; i >= 0; i--) { + if (!bio_data[i].free && bio_data[i].active) + draw_bio_height(i, location); + } +} + + // RANDOM SPEW STUFF + // moved here by doug to make things looking pretty + +#define SIN_MAG 20 + +// Temporary biorhythm variables +ulong time1 = 0; +ulong time2 = 0; +ulong time3 = 0; +ulong time4 = 0; +ulong time5 = 0; +ulong time6 = 0; +ulong time7 = 0; +int bio_delta = 1; +extern int diff_sum; +extern int curr_ll; + +// this simulates the heart beat! +int test_bio_var = 10; +int test_bio_var2 = 2; + +// this simulates the sine wave +int test_bio_var3 = 19; + +int bio_energy_var; +fix sinX = 0; +fix sinChi = 0; + +void gamescr_bio_func(void) { + extern int rad_absorb, bio_absorb; + int i; + + clear_bio_tracks(); + + // KLC - the "update_time" parameter is halved for Mac version, because we're only + // getting called 70 times/sec rather than 140. + + status_bio_add(&bio_energy_var, MAX_ENERGY, 2, ENERGY_TRACK, 2, 0); + { + short ver = player_struct.hardwarez[CPTRIP(ENV_HARD_TRIPLE)]; + if (ver >= 1) + status_bio_add(&bio_absorb, 24, 2, BIOHAZARD_TRACK, 3, BOTTOMLESS); + if (ver >= 2) + status_bio_add(&rad_absorb, 24, 2, RADIATION_TRACK, 3, BOTTOMLESS); + } + status_bio_add(&test_bio_var, 20, 1, HEART_TRACK, 3, 0); + status_bio_add(&test_bio_var3, SIN_MAG, 4, SINE_TRACK, 2, 0); + + for (i = 0; i < NUM_BIO_TRACKS; i++) + status_track_activate(i, (player_struct.active_bio_tracks & (1 << i)) != 0); +} + +void diff_bio_func(void) { + clear_bio_tracks(); + // max_value, update_time, track_number, tail_length, special + status_bio_add(&diff_sum, 12, 6, 0, 2, 0); + status_bio_add(&test_bio_var, 20, 2, 1, 3, 0); + status_bio_add(&test_bio_var3, 20, 8, 2, 2, 0); + status_bio_add(&test_bio_var2, SIN_MAG, 4, 4, 3, 0); +} + +uchar heart_beat = 0; +ulong heart_time = 0; +ulong heart_delay = 5; +uchar flatline_heart; + +uchar chi_amp = STATUS_CHI_AMP; + +extern ubyte fatigue_threshold; + +#define FATIGUE_RANGE (CONTROL_MAX_VAL - SPRINT_CONTROL_THRESHOLD) +#define ENERGY_ZERO 50 + +void change_bio_vars(void) { + // Demo for biorhythm + int fatigue_ratio; + int fatigue_amp; + int chi_per; + + if ((*tmd_ticks - heart_time) > heart_delay) { + switch (heart_beat) { + case 0: + + { + int f = lg_max(0, (player_struct.fatigue - (CIT_CYCLE * fatigue_threshold)) / CIT_CYCLE); + int heart_ratio = 3400 / (f + 7); + if (heart_ratio > 500) + heart_ratio = 500; + else if (heart_ratio < 90) + heart_ratio = 90; + if (!flatline_heart && (*tmd_ticks - time1) > heart_ratio) { + test_bio_var = 19; + heart_delay = 5; + time1 = heart_time = *tmd_ticks; + heart_beat = 1; + } + } break; + + case 1: + heart_beat = 2; + test_bio_var = 20; + heart_delay = 5; + break; + case 2: + heart_beat = 3; + test_bio_var = 10; + heart_delay = 7; + break; + case 3: + heart_beat = 4; + test_bio_var = 2; + heart_delay = 5; + break; + case 4: + heart_beat = 5; + test_bio_var = 1; + heart_delay = 5; + break; + case 5: + heart_beat = 0; + test_bio_var = 10; + heart_delay = 5; + break; + } + heart_time = *tmd_ticks; + } + // bio_energy_var + { + static ubyte energy_spike = 0; + static int last_val = 0; + if (bio_energy_var != last_val) + energy_spike = 3; + if (energy_spike == 0) { + bio_energy_var = ENERGY_ZERO + player_struct.energy_spend - player_struct.energy_regen; + } else { + energy_spike--; + } + last_val = bio_energy_var; + } + // MR. SINUSOID biorhythm + if (player_struct.fatigue) + fatigue_ratio = fatigue_amp = (150000 / player_struct.fatigue); + else { + fatigue_ratio = 40; + fatigue_amp = SIN_MAG; + } + + if (fatigue_ratio < 5) + fatigue_ratio = 5; + else if (fatigue_ratio > 40) + fatigue_ratio = 40; + + fatigue_amp -= 5; + if (fatigue_amp < 0) + fatigue_amp = 0; + else if (fatigue_amp > SIN_MAG) + fatigue_amp = SIN_MAG; + + if ((*tmd_ticks - time2) > fatigue_ratio) { + sinX += FIX_UNIT / 10; + test_bio_var2 = (((FIX_UNIT - fix_fastsin(sinX)) / 2) * fatigue_amp) + 1 + ((SIN_MAG - fatigue_amp) / 2); + time2 = *tmd_ticks; + } + + chi_per = chi_amp; + if (player_struct.drug_status[CPTRIP(LSD_DRUG_TRIPLE)]) + chi_per <<= 1; + if (player_struct.drug_status[CPTRIP(GENIUS_DRUG_TRIPLE)]) + chi_per >>= 2; + if (chi_per < 2) { + test_bio_var3 = 10; + time3 = *tmd_ticks; + sinChi = 0; + } else if ((*tmd_ticks - time3) > chi_per) { + fix s; + sinChi += FIX_UNIT / 20; + if (sinChi > 6 * FIX_UNIT) + sinChi -= 6 * FIX_UNIT; + if (sinChi < fix_make(3, 0)) + s = -fix_mul(sinChi, sinChi - 3 * FIX_UNIT) * 4 / 9; + else + s = fix_mul(sinChi - 3 * FIX_UNIT, sinChi - 6 * FIX_UNIT) * 4 / 9; + test_bio_var3 = 10 + fix_int(chi_amp * s); + time3 = *tmd_ticks; + } +} diff --git a/engine/src/GameSrc/target.c b/engine/src/GameSrc/target.c new file mode 100644 index 0000000..cbc3b7a --- /dev/null +++ b/engine/src/GameSrc/target.c @@ -0,0 +1,578 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/target.c $ + * $Revision: 1.48 $ + * $Author: tjs $ + * $Date: 1994/11/09 01:03:30 $ + * + * + */ + +#include +#include + +#include "target.h" +#include "colors.h" +#include "mfdext.h" +#include "mfdfunc.h" +#include "mfddims.h" +#include "tools.h" +#include "objclass.h" +#include "objcrit.h" +#include "objsim.h" +#include "gamestrn.h" +#include "damage.h" +#include "ai.h" +#include "hudobj.h" +#include "combat.h" +#include "wares.h" +#include "strwrap.h" +#include "aiflags.h" +#include "visible.h" +#include "fullscrn.h" +#include "gamescr.h" +#include "cybstrng.h" +#include "otrip.h" +#include "mfdart.h" +#include "cit2d.h" +#include "gr2ss.h" + +#define sqr(x) ((x) * (x)) + +// ============================================== +// TARGET MFD CODE +// ============================================== + +#define LEFT_MARGIN 0 +#define RNG_FIELD 30 +#define TOP_MARGIN 2 +#define STATUS_TEXT_Y 35 +#define MOOD_Y 27 +#define Y_STEP 5 + +#define HP_BAR_MARGIN 0 + +#define TEXT_COLOR (RED_BASE + 5) +#define TBUFSIZ 80 +#define TARGET_FONT RES_tinyTechFont + +// get the damage string for a critter subclass and damage estimate +#define DAMAGE_STRING_BASE REF_STR_MutantDmg +#define GET_DAMAGE_STRING(scl, dmg) (DAMAGE_STRING_BASE + (scl)*DAMAGE_DEGREES + (dmg)) + +extern uchar full_game_3d; + +#define PAGEBUTT_W 8 +#define PAGEBUTT_H 11 +#define BUTTON_Y (MFD_VIEW_HGT - PAGEBUTT_H - 2) +#define TEXT_RIGHT_X 42 + +#define LAST_TARGET(mfd) (player_struct.mfd_func_data[MFD_TARGET_FUNC][mfd]) +#define LAST_MOOD(mfd) (player_struct.mfd_func_data[MFD_TARGET_FUNC][mfd + 2]) +#define LAST_STATUS(mfd) (player_struct.mfd_func_data[MFD_TARGET_FUNC][mfd + 4]) + +void right_justify_num(char *num, int dlen) { + int len = strlen(num); + int i; + int delta; + + if (len >= dlen) + return; + else + delta = dlen - len; + + for (i = len; i >= 0; i--) + num[delta + i] = num[i]; + + memset(num, '0', delta); +} + +// ---------------------------------------------------------------------------- +// mfd_target_expose() +// + +void mfd_target_expose(MFD *m, ubyte control) { + int version = player_struct.hardwarez[CPTRIP(TARG_GOG_TRIPLE)]; + uchar full = control & MFD_EXPOSE_FULL; + ObjSpecID target = objs[player_struct.curr_target].specID; + + if (version < 1) { + target = OBJ_SPEC_NULL; + player_struct.curr_target = OBJ_NULL; + } + if (player_struct.curr_target != LAST_TARGET(m->id)) + full = TRUE; + + if (control & MFD_EXPOSE) { + + // If we just turned to this page, select a target if none currently + // selected, because, hey, this is just a goofy version anyway. + // if ((control & MFD_EXPOSE_FULL) && (target == OBJ_SPEC_NULL)) + // { toggle_current_target(); target = objs[player_struct.curr_target].specID; } + + gr_push_canvas(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + mfd_clear_rects(); + + if (!full_game_3d) + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + + if (full && target == OBJ_SPEC_NULL) { + char buf[80]; + short w, h; + draw_res_bm(MKREF(RES_mfdArtOverlays, MFD_ART_TRIOP), 0, 0); + gr_set_font(ResLock(TARGET_FONT)); + get_string((version > 0) ? REF_STR_NoTarget : REF_STR_NoTargetWare, buf, sizeof(buf)); + gr_string_wrap(buf, MFD_VIEW_WID); + mfd_string_wrap = FALSE; + gr_string_size(buf, &w, &h); + mfd_full_draw_string(buf, (MFD_VIEW_WID - w) / 2, MFD_VIEW_HGT / 2 - ((version > 0) ? h : (h / 2)), + TEXT_COLOR, TARGET_FONT, TRUE, TRUE); + mfd_string_wrap = TRUE; + gr_font_string_unwrap(buf); + if (version > 0) { +#ifdef REF_STR_NumKills + sprintf(buf, "%s %d", get_string(REF_STR_NumKills, NULL, sizeof(buf)), player_struct.num_victories); +#else + sprintf(buf, "Kills: %d", player_struct.num_victories); +#endif + mfd_string_wrap = FALSE; + gr_string_size(buf, &w, &h); + mfd_full_draw_string(buf, (MFD_VIEW_WID - w) / 2, MFD_VIEW_HGT / 2, TEXT_COLOR, TARGET_FONT, TRUE, + TRUE); + mfd_string_wrap = TRUE; + gr_font_string_unwrap(buf); + } + if (version > 0) { + draw_raw_resource_bm(REF_IMG_PrevPage, 0, BUTTON_Y); + draw_raw_resource_bm(REF_IMG_NextPage, TEXT_RIGHT_X - PAGEBUTT_W, BUTTON_Y); + draw_raw_resource_bm(REF_IMG_Near, (TEXT_RIGHT_X + LEFT_MARGIN - res_bm_width(REF_IMG_Near)) / 2, + BUTTON_Y); + } + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + ResUnlock(TARGET_FONT); + } else { + LGPoint siz; + short x; + short y; + int triple = ID2TRIP(objCritters[target].id); + char buf[TBUFSIZ]; + int dmg; + int id = mfd_bmap_id(triple); + + // Aha! Herein lies the meat of the targeting display. + + // draw the creature mfd bitmap +#ifdef PLAYTEST + if (RefIndexValid((RefTable *)ResGet(REFID(id)), REFINDEX(id))) +#endif + { + // KLC - chg for new art + x = SCONV_X(MFD_VIEW_WID) - res_bm_width(id) - SCONV_X(HP_BAR_MARGIN); + y = (SCONV_Y(MFD_VIEW_HGT) - res_bm_height(id)) / 2; + // draw_raw_res_bm_extract(id,x,y,MFD_EXTRACT_BUF); + draw_hires_resource_bm(id, x, y); + } + if (full) + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + y = TOP_MARGIN; + + mfd_string_shadow = MFD_SHADOW_ALWAYS; + { + int len, sc; + char small_buf[15]; + + /// Draw target id. + sc = objs[player_struct.curr_target].subclass; + get_string(REF_STR_TargetID, buf, TBUFSIZ); + len = strlen(buf); + get_string(REF_STR_CritClasses + sc, buf + len, TBUFSIZ - len); + len = strlen(buf); + buf[len] = '-'; + + // it's edward - do him as cyborg 1 + if (sc == CRITTER_SUBCLASS_ROBOBABE) { + sprintf(small_buf, "%d", objs[player_struct.curr_target].info.type); + right_justify_num(small_buf, 5); + strcpy(buf + len + 1, small_buf); + } else { + // let's start with the level + *(buf + len + 1) = (player_struct.level < 10) ? '0' : player_struct.level / 10; + *(buf + len + 2) = player_struct.level % 10 + '0'; + // add the subclass + *(buf + len + 3) = sc + '0'; + // and lastly, add the SpecID + sprintf(small_buf, "%d", target + 1); + right_justify_num(small_buf, 2); + strcpy(buf + len + 4, small_buf); + } + siz = mfd_full_draw_string(buf, LEFT_MARGIN, y, TEXT_COLOR, TARGET_FONT, TRUE, TRUE); + y += siz.y; + + // draw target name. + get_object_long_name(triple, buf, TBUFSIZ); + string_replace_char(buf, '\n', CHAR_SOFTSP); + siz = mfd_full_draw_string(buf, LEFT_MARGIN, y, TEXT_COLOR, TARGET_FONT, TRUE, TRUE); + y += siz.y; + if (version > 0) { + // draw buttons + draw_raw_resource_bm(REF_IMG_PrevPage, 0, BUTTON_Y); + draw_raw_resource_bm(REF_IMG_NextPage, TEXT_RIGHT_X - PAGEBUTT_W, BUTTON_Y); + draw_raw_resource_bm(REF_IMG_Near, (TEXT_RIGHT_X + LEFT_MARGIN - res_bm_width(REF_IMG_Near)) / 2, + BUTTON_Y); + } + } + if (!full) + mfd_clear_rects(); + + // draw target range + { + LGRect r; + short x = TEXT_RIGHT_X; + short lx; + char rstr[10]; + ObjLoc loc = objs[player_struct.curr_target].loc; + ObjLoc ploc = objs[PLAYER_OBJ].loc; + long mapdist; + fix dist; + + mapdist = long_fast_pyth_dist(loc.x - ploc.x, loc.y - ploc.y); + mapdist = mapdist * 100 * 8 * 12 / (39 * 256); // convert to centimeters. + dist = fix_mul(FIX_UNIT / 100, fix_make(mapdist, 0)); // fix meters, 2d + dist = fix_fast_pyth_dist(dist, fix_from_obj_height(PLAYER_OBJ) - + fix_from_obj_height(player_struct.curr_target)); + gr_set_fcolor(TEXT_COLOR); + gr_set_font(ResLock(TARGET_FONT)); + // if (full) + siz = mfd_full_draw_string(get_temp_string(REF_STR_TargRange), LEFT_MARGIN, y, TEXT_COLOR, TARGET_FONT, + TRUE, TRUE); + // else + // gr_string_size(get_temp_string(REF_STR_TargRange),&siz.x,&siz.y); + sprintf(rstr, "%2.2fm", fix_float(dist)); + // lx used as dummy variable here, before we really need it. + gr_string_size(rstr, &x, &lx); + x = LEFT_MARGIN + siz.x + RNG_FIELD - x; + draw_shadowed_string(rstr, x, y, TRUE); + lx = LEFT_MARGIN + siz.x; + if (lx < x) { + r.ul = MakePoint(lx, y); + r.lr = MakePoint(x, y + siz.y); + mfd_partial_clear(&r); + } + mfd_add_rect(x, y, TEXT_RIGHT_X, y + siz.y); + mfd_notify_func(MFD_TARGET_FUNC, MFD_TARGET_SLOT, FALSE, MFD_ACTIVE, FALSE); + ResUnlock(TARGET_FONT); + } + + // Draw the target's mood + if (version >= 2) { + ubyte clr = TEXT_COLOR; + ubyte mood = objCritters[target].mood; + if (full || mood != LAST_MOOD(m->id)) { + // check if critter is asleep - only order we care about + if (objCritters[target].orders == AI_ORDERS_SLEEP) + mood = NUM_AI_MOODS; + else if (objCritters[target].flags & AI_FLAG_TRANQ) + mood = NUM_AI_MOODS + 1; + else if (objCritters[target].flags & AI_FLAG_CONFUSED) + mood = NUM_AI_MOODS + 2; + + get_string(REF_STR_CritMoods + mood, buf, TBUFSIZ); + if (mood == AI_MOOD_HOSTILE) + clr = RED_BASE + 1; + siz = mfd_full_draw_string(buf, LEFT_MARGIN, MOOD_Y, clr, TARGET_FONT, TRUE, TRUE); + mfd_add_rect(LEFT_MARGIN, MOOD_Y, MFD_VIEW_WID, MOOD_Y + siz.y); + LAST_MOOD(m->id) = mood; + } + } + + if (version >= 2) { + // draw target status + y = STATUS_TEXT_Y; + dmg = get_damage_estimate(target); + if (dmg < DAMAGE_MIN || dmg > DAMAGE_MAX) + dmg = DAMAGE_CRITICAL; + if (full || dmg != LAST_STATUS(m->id)) { + get_string(GET_DAMAGE_STRING(TRIP2SC(triple), dmg), buf, TBUFSIZ); + + // siz = mfd_draw_string(get_temp_string(REF_STR_Condition), LEFT_MARGIN, y, TEXT_COLOR, + // TRUE); y += siz.y + 1; + siz = mfd_full_draw_string(buf, LEFT_MARGIN, y, TEXT_COLOR, TARGET_FONT, TRUE, TRUE); + y += siz.y + 1; + LAST_STATUS(m->id) = dmg; + } + } + mfd_string_shadow = MFD_SHADOW_FULLSCREEN; + } + + gr_pop_canvas(); + mfd_update_rects(m); + } + LAST_TARGET(m->id) = player_struct.curr_target; + + return; +} + +// ============================================== +// TARGET HARDWARE CODE +// ============================================== + +#define NUM_TARG_FRAMES 5 +extern ubyte targ_frame; + +void select_current_target(ObjID id, uchar force_mfd) { +#ifdef ANNOY_PLAYERS_TRYING_TO_TARGET_THINGS + extern errtype change_current(ObjRefID new_current_ref); +#endif + if ((player_struct.hardwarez[CPTRIP(TARG_GOG_TRIPLE)] == 0) || (id == PLAYER_OBJ)) + return; + if (objs[id].info.current_hp <= 0) + return; + + hudobj_set_id(player_struct.curr_target, FALSE); + player_struct.curr_target = id; + hudobj_set_id(id, TRUE); + targ_frame = NUM_TARG_FRAMES; + if (force_mfd) { + int m = NUM_MFDS; + if (id != OBJ_NULL && !mfd_yield_func(MFD_TARGET_FUNC, &m)) { + use_ware(WARE_HARD, HARDWARE_TARGET); + mfd_yield_func(MFD_TARGET_FUNC, &m); + } + if (m != NUM_MFDS) + full_visible |= visible_mask(m); + } + mfd_notify_func(MFD_TARGET_FUNC, MFD_TARGET_SLOT, FALSE, MFD_ACTIVE, TRUE); +#ifdef ANNOY_PLAYERS_TRYING_TO_TARGET_THINGS + change_current(objs[id].ref); +#endif +} + +#define ELIGIBLE_TARGET_RANGE 20 +#define ELIGIBLE_TARGET_RANGE_SQUARED (ELIGIBLE_TARGET_RANGE * ELIGIBLE_TARGET_RANGE) +// --------------------------------------------------- +// iter_eligible_targets() +// takes a pointer to an objSpecId for a creature. If there is a later creature in the level's creature +// list that is a valid target, modifies the specid to point to that target and returns TRUE, otherwise, +// returns FALSE, setting the specID to OBJ_SPEC_NULL. If *sid is OBJ_SPEC_NULL, sets *sid to the first +// eligible critter, or returns FALSE if none exists. + +uchar iter_eligible_targets(ObjSpecID *sid) { + ObjLoc ploc = objs[PLAYER_OBJ].loc; + LGPoint plr; + plr.x = OBJ_LOC_BIN_X(ploc); + plr.y = OBJ_LOC_BIN_Y(ploc); + + if (*sid == OBJ_SPEC_NULL) + *sid = objCritters[0].id; + else + *sid = objCritters[*sid].next; + for (; *sid != OBJ_SPEC_NULL; *sid = objCritters[*sid].next) { + ObjID oid = objCritters[*sid].id; + ObjLoc loc = objs[oid].loc; + int dsq = sqr(OBJ_LOC_BIN_X(loc) - plr.x) + sqr(OBJ_LOC_BIN_Y(loc) - plr.y); + + // you cannot target yourself - well - not this game.... + // cause then you'll have nobody to take your tasks... + if (oid == PLAYER_OBJ) + continue; + + if (get_crit_posture(*sid) == DEATH_CRITTER_POSTURE || objs[PLAYER_OBJ].specID == *sid) + continue; + if (dsq > ELIGIBLE_TARGET_RANGE_SQUARED) + continue; + if (ray_cast_objects(PLAYER_OBJ, oid, VISIBLE_MASS, VISIBLE_SIZE, VISIBLE_SPEED, + fix_make(ELIGIBLE_TARGET_RANGE, 0)) != oid) + continue; + return TRUE; + } + return FALSE; +} + +// --------------------------------------------------------------------------- +// select_closest_target() +// +// finds the closest eligible target and selects it. Duh + +void select_closest_target(void) { + ObjLoc ploc = objs[PLAYER_OBJ].loc; + LGPoint plr; + ObjSpecID sid = OBJ_SPEC_NULL; + ObjID bestid = OBJ_NULL; + uint bestdist = 0xFFFFFFFF; + plr.x = OBJ_LOC_BIN_X(ploc); + plr.y = OBJ_LOC_BIN_Y(ploc); + while (iter_eligible_targets(&sid)) { + ObjID oid = objCritters[sid].id; + ObjLoc loc = objs[oid].loc; + int dsq = sqr(OBJ_LOC_BIN_X(loc) - plr.x) + sqr(OBJ_LOC_BIN_Y(loc) - plr.y); + if (dsq < bestdist) { + bestid = oid; + bestdist = dsq; + } + } + select_current_target(bestid, TRUE); + if (player_struct.curr_target == OBJ_NULL) + string_message_info(REF_STR_NoTargetsAround); +} + +// --------------------------------------------------------------------------- +// toggle_current_target_backwards() +// +// finds the closest eligible target and selects it. Duh + +void toggle_current_target_backwards(void) { + ObjSpecID sid = OBJ_SPEC_NULL; + ObjID bestid = OBJ_NULL; + if (player_struct.curr_target != OBJ_NULL) + while (iter_eligible_targets(&sid)) { + ObjID oid = objCritters[sid].id; + if (oid == player_struct.curr_target) + break; + bestid = oid; + } + if (player_struct.curr_target == OBJ_NULL || bestid == OBJ_NULL) { + while (iter_eligible_targets(&sid)) { + ObjID oid = objCritters[sid].id; + if (sid == OBJ_SPEC_NULL) + break; + bestid = oid; + } + } + select_current_target(bestid, TRUE); + if (player_struct.curr_target == OBJ_NULL) + string_message_info(REF_STR_NoTargetsAround); +} + +// --------------------------------------------------------------------------- +// toggle_current_target() +// +// Doofy code to cycle through all extant critters and pop 'em up on targeting + +void toggle_current_target() { + ObjSpecID oldsid, osid; + ObjID old; + + old = player_struct.curr_target; + if (!objs[old].active) + old = OBJ_NULL; + + if (old != OBJ_NULL) { + osid = oldsid = objs[old].specID; + iter_eligible_targets(&osid); + } else + osid = oldsid = OBJ_SPEC_NULL; + + if (osid == OBJ_SPEC_NULL) + iter_eligible_targets(&osid); + + if (osid != OBJ_SPEC_NULL && objCritters[osid].id != PLAYER_OBJ) { + select_current_target(objCritters[osid].id, TRUE); + } else if (oldsid != OBJ_SPEC_NULL) { + select_current_target(objCritters[oldsid].id, TRUE); + } else + player_struct.curr_target = OBJ_NULL; + + if (player_struct.curr_target != old) { + if (player_struct.curr_target == OBJ_NULL) + mfd_notify_func(MFD_TARGET_FUNC, MFD_TARGET_SLOT, FALSE, MFD_ACTIVE, TRUE); + else + mfd_notify_func(MFD_TARGET_FUNC, MFD_TARGET_SLOT, FALSE, MFD_FLASH, TRUE); + } + if (player_struct.curr_target == OBJ_NULL) + string_message_info(REF_STR_NoTargetsAround); + return; +} + +// --------------------------------------------------------------------------- +// mfd_target_handler() +// +// Iterates through all possible targets + +uchar mfd_target_handler(MFD *m, uiEvent *e) { + LGPoint pos; + + pos.x = e->pos.x - m->rect.ul.x; + pos.y = e->pos.y - m->rect.ul.y; + + if (player_struct.hardwarez[CPTRIP(TARG_GOG_TRIPLE)] == 0) + return FALSE; + + if (pos.y < BUTTON_Y || pos.x > TEXT_RIGHT_X) + return FALSE; + + if (!(e->mouse_data.action & MOUSE_LDOWN)) + return TRUE; + if (pos.x >= TEXT_RIGHT_X - PAGEBUTT_W) + toggle_current_target(); + else if (pos.x <= PAGEBUTT_W) + toggle_current_target_backwards(); + else + select_closest_target(); + return TRUE; +} + +// ---------------------------------------------------------- +// THE TARGET WARE ITEM MFD +// ---------------------------------------------------------- + +#define STRINGS_PER_WARE (REF_STR_wareSpew1 - REF_STR_wareSpew0) + +void mfd_targetware_expose(MFD *mfd, ubyte control) { + uchar n = CPTRIP(TARG_GOG_TRIPLE); + uchar v = player_struct.hardwarez[n]; + uchar full = control & MFD_EXPOSE_FULL; + if (control == 0) + return; + gr_push_canvas(pmfd_canvas); + mfd_clear_rects(); + mfd_item_micro_expose(full, TARG_GOG_TRIPLE); + // mfd_item_micro_hires_expose(full,TARG_GOG_TRIPLE); + draw_mfd_item_spew(REF_STR_wareSpew0 + STRINGS_PER_WARE * n, v); + if (full) { + draw_raw_resource_bm(REF_IMG_TargetButton, (MFD_VIEW_WID - res_bm_width(REF_IMG_TargetButton)) / 2, BUTTON_Y); + mfd_add_rect(0, 0, MFD_VIEW_WID - 1, MFD_VIEW_HGT - 1); + } + gr_pop_canvas(); + mfd_update_rects(mfd); +} + +#define BUTTON_SIZE 30 + +uchar mfd_targetware_handler(MFD *m, uiEvent *e) { + LGPoint pos; + + pos.x = e->pos.x - m->rect.ul.x; + pos.y = e->pos.y - m->rect.ul.y; + + if (pos.y < BUTTON_Y || + abs(2*(int)pos.x - (int)MFD_VIEW_WID) > BUTTON_SIZE || + e->type != UI_EVENT_MOUSE) + return FALSE; + + if (!(e->mouse_data.action & MOUSE_LDOWN)) + return TRUE; + if (player_struct.curr_target == OBJ_NULL) + select_closest_target(); + mfd_change_slot(m->id, MFD_TARGET_SLOT); + + return TRUE; +} diff --git a/engine/src/GameSrc/textmaps.c b/engine/src/GameSrc/textmaps.c new file mode 100644 index 0000000..5f4bf79 --- /dev/null +++ b/engine/src/GameSrc/textmaps.c @@ -0,0 +1,609 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/textmaps.c $ + * $Revision: 1.77 $ + * $Author: xemu $ + * $Date: 1994/11/21 17:38:46 $ + */ + +#define __TEXTMAPS_SRC + +#include + +#include "textmaps.h" +#include "gettmaps.h" +#include "tools.h" +#include "frprotox.h" +#include "cybmem.h" +#include "citres.h" +#include "rendtool.h" +#include "objects.h" +#include "objstuff.h" +#include "tpolys.h" +#include "statics.h" + +#include "OpenGL.h" + + +uchar textures_loaded = FALSE; + +#define READ(fd, x) read(fd, (char *)&(x), sizeof(x)) + +Id tmap_ids[NUM_TEXTURE_SIZES] = {TEXTURE_128_ID, TEXTURE_64_ID, TEXTURE_32_ID, TEXTURE_16_ID}; +ushort tmap_sizes[NUM_TEXTURE_SIZES] = {128, 64, 32, 16}; +uchar all_textures = TRUE; + +extern uchar tmap_big_buffer[]; + +// prototypes +uchar set_animations(short start, short frames, uchar *anim_used); +void setup_tmap_bitmaps(void); +errtype texture_crunch_init(void); +errtype texture_crunch_go(void); + +#define SET_ANIM_USED(x) anim_used[(x) >> 3] |= (1 << ((x)&0x7)) +#define CHECK_ANIM_USED(x) anim_used[(x) >> 3] & (1 << ((x)&0x7)) + +#define ACTUAL_SMALL_ANIMS 101 +uchar set_animations(short start, short frames, uchar *anim_used) { + int loop; + // Note that NUM_ACTUAL_SMALL_ANIMS contains the actual number of valid + // animations, BUT anything in the gap between them will get caught by the + // ResInUse call in load_small_texturemaps + if ((start < 0) || (start + frames > MAX_SMALL_TMAPS)) { + // mprintf("PAIN SUFFERING SET ANIM DEATH %d %d\n",start,frames); + return FALSE; + } + for (loop = start; loop < start + frames; loop++) + SET_ANIM_USED(loop); + return TRUE; +} + +errtype load_small_texturemaps(void) { + Id id = TEXTURE_SMALL_ID; + char i = 0; + extern uchar obj_is_display(int triple); + int d; + char rv = 0; + ObjSpecID osid; + uchar anim_used[MAX_SMALL_TMAPS / 8]; + + LG_memset(anim_used, 0, MAX_SMALL_TMAPS / 8); + + // Figure out which animations are in use + osid = objBigstuffs[0].id; + while (osid != OBJ_SPEC_NULL) { + if (obj_is_display(ID2TRIP(objBigstuffs[osid].id))) { //was objSmallstuffs + d = objBigstuffs[osid].data2; + if ((d & TPOLY_INDEX_MASK) && ((d & TPOLY_TYPE_MASK) == 0x100)) + rv = set_animations(d & TPOLY_INDEX_MASK, objBigstuffs[osid].cosmetic_value, anim_used); + if (((d >> 16) & TPOLY_INDEX_MASK) && ((d & TPOLY_TYPE_MASK) == 0x100)) + rv |= set_animations((d >> 16) & TPOLY_INDEX_MASK, objBigstuffs[osid].cosmetic_value, anim_used); + // if (!rv) mprintf("Big badness for o %x, osid %x, data2 %d\n",objBigstuffs[osid].id,osid,d); + } + osid = objBigstuffs[osid].next; + } + + osid = objSmallstuffs[0].id; + while (osid != OBJ_SPEC_NULL) { + if (obj_is_display(ID2TRIP(objSmallstuffs[osid].id))) { + d = objSmallstuffs[osid].data2; + if ((d & TPOLY_INDEX_MASK) && ((d & TPOLY_TYPE_MASK) == 0x100)) + rv = (char)set_animations(d & TPOLY_INDEX_MASK, objSmallstuffs[osid].cosmetic_value, anim_used); + if (((d >> 16) & TPOLY_INDEX_MASK) && ((d & TPOLY_TYPE_MASK) == 0x100)) + rv |= + (char)set_animations((d >> 16) & TPOLY_INDEX_MASK, objSmallstuffs[osid].cosmetic_value, anim_used); + // if (!rv) mprintf("Small badness for o %x, osid %x, data2 %d\n",objSmallstuffs[osid].id,osid,d); + } + osid = objSmallstuffs[osid].next; + } + + while (ResInUse(id + i)) { + if (CHECK_ANIM_USED(i)) { + ResLock(id + i); + ResUnlock(id + i); + } + i++; + } + return (OK); +} + +// should really do dynamic creation of grs_bitmap *'s for the textures +// but for now, we'll be lame + +extern uchar tmap_static_mem[]; +static uchar *tmap_dynamic_mem = NULL; + +#define get_tmap_128x128(i) ((uchar *)&tmap_dynamic_mem[i * (128 * 128)]) +#define get_tmap_64x64(i) ((uchar *)&tmap_static_mem[i * SIZE_STATIC_TMAP]) +#define get_tmap_32x32(i) ((uchar *)&tmap_static_mem[(i * SIZE_STATIC_TMAP) + (64 * 64)]) +#define get_tmap_16x16(i) ((uchar *)&tmap_static_mem[(i * SIZE_STATIC_TMAP) + (64 * 64) + (32 * 32)]) + +// have we built the tables, do we have the extra memory, so on +static uchar tmaps_setup = FALSE; + +static grs_bitmap tmap_bitmaps[NUM_TEXTURE_SIZES]; +void setup_tmap_bitmaps(void) { + int i; + for (i = 0; i < 4; i++) + gr_init_bm(&tmap_bitmaps[i], NULL, BMT_FLAT8, 0, tmap_sizes[i], tmap_sizes[i]); +} + +grs_bitmap *get_texture_map(int idx, int sz) { + ushort sz_add[NUM_TEXTURE_SIZES - 1] = {0, 64 * 64, (64 * 64) + (32 * 32)}; + uchar *bt; +// mprintf("Getting tmap %d, sz %d\n",idx,sz); +#ifdef DEMO + if (sz == 2) + sz = 1; +#endif + if (sz == 0) { + if (all_textures) + bt = get_tmap_128x128(idx); + else + sz = 1; + } + if (sz != 0) { + bt = get_tmap_64x64(idx) + sz_add[sz - 1]; + } + tmap_bitmaps[sz].bits = bt; + return &tmap_bitmaps[sz]; +} + +void load_textures(void) { + grs_bitmap *cur_bm; + int i, n, c; + errtype retval = OK; + int atext_tmp = 1; + + { + if (start_mem < EXTRA_TMAP_THRESHOLD) + atext_tmp = 0; + else { + // Spew(DSRC_SYSTEM_Memory, ("Sufficient Memory detected for Loading 128s!\n")); + atext_tmp = 1; + } + } + + // Spew(DSRC_GFX_Texturemaps, ("all_textures = %d\n",all_textures)); + all_textures = atext_tmp; + // Spew(DSRC_GFX_Texturemaps, ("GAME_TEXTURES = %d\n",GAME_TEXTURES)); + + if (!tmaps_setup) { + if (all_textures) // get our butts some memory + tmap_dynamic_mem = tmap_big_buffer; + + setup_tmap_bitmaps(); + tmaps_setup = TRUE; + } + + for (c = 0; c < NUM_LOADED_TEXTURES; c++) { + i = loved_textures[c]; + if (!ResInUse(TEXTURE_64_ID + i)) { + // Warning(("Hey, invalid texture in palette! slot %d = %d\n",c,i)); + i = 0; + } // Set local properties + for (n = SMALLEST_SIZE_INDEX; n < NUM_TEXTURE_SIZES; n++) { + if ((n != TEXTURE_128_INDEX) || all_textures) { +#ifdef DEMO + if (n == TEXTURE_32_INDEX) + break; +#endif + cur_bm = get_texture_map(c, n); + + // This is a BLATANT hack to get around the 1 Meg limit in the resource system + if ((n == TEXTURE_128_INDEX) || (n == TEXTURE_64_INDEX)) { + if (ResInUse(tmap_ids[n] + i)) { + retval = load_res_bitmap(cur_bm, MKREF(tmap_ids[n] + i, 0), FALSE); + cur_bm->flags = 0; + } else { + // Warning(("Hey, ResInUse failed in tmap_load and i'm so blue + // (%d,%d,%x)\n",n,i,tmap_ids[n]+i)); + // should abort !!! + } + } else { + retval = load_res_bitmap(cur_bm, MKREF(tmap_ids[n], i), FALSE); + cur_bm->flags = 0; + } + if ((cur_bm->w != tmap_sizes[n]) || (cur_bm->h != tmap_sizes[n])) { + // Warning(("Incorrect size in tmap %d! (%d)(%d x %d) vs (%d x %d)\n",i,c,cur_bm->w, + // cur_bm->h, tmap_sizes[n], tmap_sizes[n])); + // should abort !!! + // DBG(DSRC_GFX_Texturemaps, { + // gr_set_fcolor(0); + // gr_rect(0,0,320,200); + // gr_bitmap(cur_bm, 0, 0); + // }); + } + if(can_use_opengl()) + opengl_cache_wall_texture(c, n, cur_bm); + } + } + } + // Load in texture properties for all textures + load_master_texture_properties(); + + // Copy the appropriate things into textprops + for (i = 0; i < NUM_LOADED_TEXTURES; i++) + textprops[i] = texture_properties[loved_textures[i]]; + + // Get rid of the big set + unload_master_texture_properties(); + textures_loaded = TRUE; + game_fr_reparam(all_textures, -1, -1); +} + +void free_textures(void) { +#ifndef SVGA_CUTSCENES + if (all_textures && (tmap_dynamic_mem == NULL)) + free(tmap_dynamic_mem); +#endif + tmaps_setup = FALSE; +} + +errtype bitmap_array_unload(int *num_bitmaps, grs_bitmap *arr[]) { + int i; + + if (*num_bitmaps == 0) + return (ERR_NOEFFECT); + + // Spew(DSRC_SYSTEM_Memory, ("Freeing %d bitmaps...\n",*num_bitmaps)); + for (i = 0; i < *num_bitmaps; i++) { + // Spew(DSRC_SYSTEM_Memory, ("%d ",i)); + free(arr[i]->bits); + free(arr[i]); + } + *num_bitmaps = 0; + return (OK); +} + +uchar empty_bitmap(grs_bitmap *bmp) { + uchar *cur = &bmp->bits[0], *targ = cur + (bmp->w * bmp->h); + while (cur < targ) + if (*cur++ != 0) + return FALSE; + return TRUE; +} + +errtype Init_Lighting(void) { + int i; + FILE *fp; + + fp = fopen_caseless("res/data/shadtabl.dat", "rb"); + if (fp == NULL) + return (ERR_FOPEN); + fread(shading_table, 1, 256 * 16, fp); + fclose(fp); + + for (i = 0; i < 256 * 16; i += 256) { + shading_table[i] = 0xFF; // i love our shading table + } + + DEBUG("Set Light Table"); + gr_set_light_tab(shading_table); + + // now read bw shading table + fp = fopen_caseless("res/data/bwtabl.dat", "rb"); + if (fp == NULL) + return (ERR_FOPEN); + fread(bw_shading_table, 1, 256 * 16, fp); + fclose(fp); + + for (i = 0; i < 256 * 16; i += 256) + bw_shading_table[i] = 0; // i love our shading table + + fr_set_cluts(shading_table, bw_shading_table, bw_shading_table, bw_shading_table); + + return (OK); +} + +errtype load_master_texture_properties(void) { + int version, i; + char *cp; + + texture_properties = (TextureProp *)malloc(GAME_TEXTURES * sizeof(TextureProp)); + + // Load Properties from disk + clear_texture_properties(); + + DEBUG("Loading texture properties"); + + FILE *f = fopen_caseless("res/data/textprop.dat", "rb"); + + if (f == NULL) { + return (ERR_FOPEN); + } + + fseek(f, 0, SEEK_END); + int len = ftell(f); + rewind(f); + + cp = (char *)malloc((len + 1) * sizeof(char)); + fread(cp, len, 1, f); + fclose(f); + + { + memmove(&version, cp, sizeof(version)); + cp += sizeof(version); + + if (version == TEXTPROP_VERSION_NUMBER) { + // 363 seems magic. GAME_TEXTURES instead? + for (i = 0; i < 363; i++) { + memmove(&texture_properties[i], cp, 11); + cp += 11; + } + } else { + ERROR("Skipping loading textprops.dat, bad version!"); + } + } + + /*res = GetResource('tprp',1000); + if (res) + { + FlipLong((long *) (*res)); + version = * (int *) (*res); + if (version == TEXTPROP_VERSION_NUMBER) + { + // copy out the structs, fixing the 11->12 byte size difference + for (i=0; i<363; i++) + texture_properties[i] = * (TextureProp *) (*res+4+(i*11)); + + // fix shorts in texture_properties + for (i=0; i%d->%d\n",i,loved_textures[i],tmap_convert[loved_textures[i]],tmap_crunch[tmap_convert[loved_textures[i]]])); + loved_textures[i] = tmap_crunch[tmap_convert[loved_textures[i]]]; + } + load_textures(); + + return (OK); +} +#endif + + //#define TEXTURE_ANNIHILATION + +#ifdef TEXTURE_ANNIHILATION +#define NUM_DEMO_TEXTURES 32 +#pragma disable_message(202) +uchar salvation_list[GAME_TEXTURES]; +uchar texture_annihilate_func(ushort keycode, uint32_t context, intptr_t data) { + int fn; + int i, c; + extern int texture_fnum; + + mprintf("texture_fnum = %d\n", texture_fnum); + if (texture_fnum == 0) { + // Warning(("HEY, TEXTURE_FNUM is %d!\n",texture_fnum)); + return (TRUE); + } + + ResCloseFile(texture_fnum); + + fn = ResEditFile("texture.res", FALSE); + + for (i = 0; i < GAME_TEXTURES; i++) + salvation_list[i] = FALSE; + + // Determine which textures are fine and happy + for (i = 0; i < NUM_DEMO_TEXTURES; i++) { + salvation_list[loved_textures[i]] = TRUE; + } + + // Annhiliate all that do not conform... except for 16x16s which are in + // always since it's annoying to remove them! + for (i = 0; i < GAME_TEXTURES; i++) { + if (!salvation_list[i]) { + mprintf("destroying number %d, id = %x and %x\n", i, TEXTURE_64_ID + i, TEXTURE_128_ID + i); + if (!ResInUse(TEXTURE_64_ID + i)) + break; + else { + ResKill(TEXTURE_64_ID + i); + ResKill(TEXTURE_128_ID + i); + } + } + } + + ResPack(fn); + + ResCloseFile(fn); + + texture_fnum = ResOpenFile("texture.res"); + + return (TRUE); +} +#pragma enable_message(202) +#endif diff --git a/engine/src/GameSrc/tfdirect.c b/engine/src/GameSrc/tfdirect.c new file mode 100644 index 0000000..6bbe205 --- /dev/null +++ b/engine/src/GameSrc/tfdirect.c @@ -0,0 +1,769 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/tfdirect.c $ + * $Revision: 1.16 $ + * $Author: dc $ + * $Date: 1994/09/08 06:30:34 $ + */ + +#include +#include + +// pretty cool set of header files, eh? +#include "tfdirect.h" // ditto, yep yep yep +#include "ss_flet.h" // for everything, basically, constants mostly +#include "map.h" // for MAP_HEIGHTS, must be'fore frintern for fdt_mptr +#include "mapflags.h" // for light_flr and light_ceil +#include "objects.h" // for ObjsClearDealt +#include "frintern.h" // for _fdt_x, _y and _mptr +// gruesome annoyance to destroy projectiles +#include "damage.h" +#include "objsim.h" +#include "objbit.h" +#include "objprop.h" +#include "render.h" + +#define USE_OLD_PASSING +//#define SAFETY_RETURN +//#define DIAGONAL_CORNERS +#define STAIRS_NEAR_THE_TOP +#define STAIRS_ABOVE_DA_TOP + +// i love us +uchar v_to_cur[] = { + (SS_BCD_CURR_E | SS_BCD_CURR_LOW) >> SS_BCD_CURR_SHF, (SS_BCD_CURR_W | SS_BCD_CURR_LOW) >> SS_BCD_CURR_SHF, + (SS_BCD_CURR_N | SS_BCD_CURR_LOW) >> SS_BCD_CURR_SHF, (SS_BCD_CURR_S | SS_BCD_CURR_LOW) >> SS_BCD_CURR_SHF, + (SS_BCD_CURR_E | SS_BCD_CURR_MID) >> SS_BCD_CURR_SHF, (SS_BCD_CURR_W | SS_BCD_CURR_MID) >> SS_BCD_CURR_SHF, + (SS_BCD_CURR_N | SS_BCD_CURR_MID) >> SS_BCD_CURR_SHF, (SS_BCD_CURR_S | SS_BCD_CURR_MID) >> SS_BCD_CURR_SHF, + (SS_BCD_CURR_E | SS_BCD_CURR_HIGH) >> SS_BCD_CURR_SHF, (SS_BCD_CURR_W | SS_BCD_CURR_HIGH) >> SS_BCD_CURR_SHF, + (SS_BCD_CURR_N | SS_BCD_CURR_HIGH) >> SS_BCD_CURR_SHF, (SS_BCD_CURR_S | SS_BCD_CURR_HIGH) >> SS_BCD_CURR_SHF, + (SS_BCD_REPUL_UP | SS_BCD_CURR_LOW) >> SS_BCD_CURR_SHF, (SS_BCD_REPUL_DOWN | SS_BCD_CURR_LOW) >> SS_BCD_CURR_SHF, + (SS_BCD_REPUL_UP | SS_BCD_CURR_MID) >> SS_BCD_CURR_SHF, (SS_BCD_REPUL_DOWN | SS_BCD_CURR_MID) >> SS_BCD_CURR_SHF, + (SS_BCD_REPUL_UP | SS_BCD_CURR_HIGH) >> SS_BCD_CURR_SHF, (SS_BCD_REPUL_DOWN | SS_BCD_CURR_HIGH) >> SS_BCD_CURR_SHF +}; + +// Local Prototypes +uchar _tf_set_flet(int flags, fix att, fix dist, fix *norm); +uchar _tf_internal_chk(void); +fix _tf_border_check_2d(void); +void _tf_norm_create(void); +void _tf_get_crosses(void); +fix tf_solve_2d_case(int flags); +int _stair_check(fix walls[4][2], int flags); +void terrfunc_one_map_square(int fmask); +TerrainHit tf_direct(fix fix_x, fix fix_y, fix fix_z, fix rad, int ph, TFType tf_type); + +// old style physics... +extern TerrainData terrain_info; + +// passing globals +ss_facelet_return ss_edms_facelets[SS_MAX_FACELETS]; +uchar ss_edms_facelet_cnt; +int ss_edms_bcd_flags; +int ss_edms_bcd_param; + +// globals... +fix (*tf_vert_2d)[2]; // 2d vertices of the face, when reset +char tf_norm_hnts[4]; // normal hints for strange param stuff +fix *tf_pt; // 3 elements: first 2 in plane, 3 is distance from plane +fix tf_loc_pt[3]; // localized relative to current map tile +fix tf_raw_pt[3]; // raw world location of object +int tf_ph; // current physics handle +fix tf_rad; // rad of current physics'ed object + +// locals +static fix tf_norm_2d[4][2]; // computed normals +static char cross_face[4][2], cross_cnt; // which face is being crossed, count +static char tf_pcnt; // points for working face +static fix tf_cur_rad; // obj radius for this solve, changes if remetriced, say + +// sneaky sneaky, locals which arent, im such a bad person +#define tfunc_mptr _fdt_mptr +#define tfunc_map_x _fdt_x +#define tfunc_map_y _fdt_y + +static fix tfunc_minz, tfunc_maxz; + +// no unreachables... +// dbg system stuff + +#ifdef TF_TALK_SYSTEM + +#define FletList (1 << 0) +#define FletSet (1 << 1) +#define IntChk (1 << 2) +#define BordChk (1 << 3) +#define AlignFce (1 << 4) +#define RemetFce (1 << 5) +#define Grab (1 << 6) +#define Calls (1 << 7) +#define Area (1 << 8) +#define Ret (1 << 9) +#define Cylinder (1 << 10) + +//#define DEFAULT_TALK Ret|FletList +#define DEFAULT_TALK 0 +#define TF_TALK_STATICS 0xffff + +int tf_talk = DEFAULT_TALK, tf_tmp; + +#define tf_talk_setup() tf_talk = DEFAULT_TALK +#define tf_turn_on(flg) tf_tmp = tf_talk, tf_talk |= (flg) +#define tf_talk_check(flg) ((flg & TF_TALK_STATICS) && (tf_talk & flg)) +#define tf_undo_set(flg) tf_talk = tf_tmp | (flg) + +#define do_tf_Spew(flg, dat) mprintf dat +#define tf_Spew(flg, dat) \ + if (tf_talk_check(flg)) \ + do_tf_Spew(flg, dat) +#else +#define tf_talk_setup() +#define tf_turn_on(flg) +#define tf_talk_check(flg) FALSE +#define tf_undo_set(flg) +#define do_tf_Spew(flg, dat) +#define tf_Spew(flg, dat) +#endif +#define tf_Stat(dat) +#define terrfunc_it_calls_inc() + +// renderer stuff we call +extern void fr_tfunc_grab_fast(int mask); + +#define _tf_list_flet() + +// here we need the tmap/size parser stuff to happen.... + +// actually output a facelet +uchar _tf_set_flet(int flags, fix att, fix dist, fix *norm) { + fix full_norms[2] = {fix_make(1, 0), -fix_make(1, 0)}; + int pv; + ss_facelet_return *cur_fc = &ss_edms_facelets[ss_edms_facelet_cnt++]; + + tf_Spew(FletSet, ("Set %d.. vals %x %x %x, norm %x %x %x\n", ss_edms_facelet_cnt - 1, flags, att, dist, + norm != NULL ? norm[0] : 0xb, norm != NULL ? norm[1] : 0xa, norm != NULL ? norm[2] : 0xd)); + if (ss_edms_facelet_cnt >= SS_MAX_FACELETS) + return FALSE; + cur_fc->flags = flags; + cur_fc->att = att; + cur_fc->comp = tf_rad - dist; + switch (flags & SS_BCD_AXIS_MASK) { + case SS_BCD_PRIM_MULTI: + *(g3s_vector *)cur_fc->norm = *(g3s_vector *)norm; // _memcpy32l(cur_fc->norm,norm,3); +#ifdef USE_OLD_PASSING + goto i_hate_everyone; +#endif + return TRUE; + case SS_BCD_PRIM_XAXIS: + pv = 0; + break; + case SS_BCD_PRIM_YAXIS: + pv = 1; + break; + case SS_BCD_PRIM_ZAXIS: + pv = 2; + break; + } + + LG_memset(cur_fc->norm, 0, 3 * 4); // _memset32l(cur_fc->norm,0,3); + cur_fc->norm[pv] = full_norms[flags & SS_BCD_PRIM_NEG]; +#ifdef USE_OLD_PASSING +i_hate_everyone : { + int which, prim; + which = ((flags & SS_BCD_TYPE_MASK) == SS_BCD_TYPE_WALL) + ? 2 + : (((flags & SS_BCD_TYPE_MASK) == SS_BCD_TYPE_CEIL) ? 0 : 1); + prim = ((flags & SS_BCD_AXIS_MASK) >> 1) - 1; + if (prim == -1) + prim = FCE_NO_PRIM; + facelet_add(which, cur_fc->norm, cur_fc->att, cur_fc->comp, prim); + // if ((flags&SS_BCD_MISC_STAIR)&&(cur_fc->att!=fix_1)) + // flags&=~SS_BCD_MISC_STAIR; // no stair bit when attenuated + ss_edms_bcd_flags |= flags; +} +#endif + // if (ss_edms_bcd_flags&SS_BCD_MISC_CLIMB) + // tf_talk|=Ret|FletList; + return TRUE; +} + +// tf_internal_chk... +uchar _tf_internal_chk(void) { + fix dp[2]; + dp[0] = fix_mul(tf_norm_2d[cross_face[0][0]][0], (tf_pt[0] - tf_vert_2d[cross_face[0][1]][0])) + + fix_mul(tf_norm_2d[cross_face[0][0]][1], (tf_pt[1] - tf_vert_2d[cross_face[0][1]][1])); + dp[1] = fix_mul(tf_norm_2d[cross_face[1][0]][0], (tf_pt[0] - tf_vert_2d[cross_face[1][1]][0])) + + fix_mul(tf_norm_2d[cross_face[1][0]][1], (tf_pt[1] - tf_vert_2d[cross_face[1][1]][1])); + tf_Spew(IntChk, ("tic: %x and %x - cross faces %d and %d\n", dp[0], dp[1], cross_face[0][0], cross_face[1][0])); + return ((dp[0] > 0) && (dp[1] > 0)); +} + +// solver for border case check... + +// unit-normals version + +// non-unit normals version +// behind the edge? set and then check to see if valid range +#define set_n_chk_1(val) \ + _1d_pt[1] = (val); \ + if ((_1d_pt[1] > 0) || (_1d_pt[1] < -tf_cur_rad)) \ + continue + +// f_dist is distance away in 3rd dimension, cnt is number of points +// sign is sorta unknown, as f_dist is distance, not direction, hmmm... +fix _tf_border_check_2d(void) { + fix _1d_pt[2], _1d_endpt[2]; + int i; + for (i = 0; i < tf_pcnt; i++) { + if ((tf_norm_2d[i][0] != 0) && + (tf_norm_2d[i][1] != 0)) { // do the real case, have to learn about cross clock, i think + fix nfac, c_pt[2]; + tf_Spew(BordChk, + ("gc2d: face %d fixing to solve real case w %x %x\n", i, tf_norm_2d[i][0], tf_norm_2d[i][1])); + c_pt[0] = tf_pt[0] - tf_vert_2d[i][0]; + c_pt[1] = tf_pt[1] - tf_vert_2d[i][1]; + _1d_pt[1] = fix_mul(tf_norm_2d[i][0], c_pt[0]) + fix_mul(tf_norm_2d[i][1], c_pt[1]); + if (_1d_pt[1] >= 0) + continue; // behind the edge, here we can only check behind, as we are unnormalized as of yet + _1d_pt[0] = fix_mul(tf_norm_2d[i][1], c_pt[0]) - fix_mul(tf_norm_2d[i][0], c_pt[1]); + _1d_endpt[0] = fix_mul(tf_norm_2d[i][1], tf_norm_2d[i][1]) + fix_mul(tf_norm_2d[i][0], tf_norm_2d[i][0]); + nfac = fix_sqrt(_1d_endpt[0]); // well, its all true, sadly, need to normalize + _1d_pt[1] = fix_div(_1d_pt[1], nfac); + if (_1d_pt[1] < -tf_cur_rad) + continue; // too far away + if ((_1d_pt[0] > 0) && (_1d_pt[0] < _1d_endpt[0])) + return -_1d_pt[0]; // distance from facelet + else { // finish setting up the 1d case + _1d_pt[0] = fix_div(_1d_pt[0], nfac); + _1d_endpt[0] = fix_div(_1d_endpt[0], nfac); + } + } else // is clock gnosis correct or not? who the hell knows... + { + tf_Spew(BordChk, ("gc2: face %d simp case nrms %x %x\n", i, tf_norm_2d[i][0], tf_norm_2d[i][1])); + if (tf_norm_2d[i][0] == 0) // north/south normal + if (tf_norm_2d[i][1] > 0) // this is the east facing edge, north facing interior normal + { + set_n_chk_1(tf_pt[1] - tf_vert_2d[i][1]); + _1d_pt[0] = tf_vert_2d[i][0] - tf_pt[0]; + _1d_endpt[0] = tf_norm_2d[i][1]; + } else // south facing normal, ie. invert everything, perhaps this could be cooler, eh? + { + set_n_chk_1(tf_vert_2d[i][1] - tf_pt[1]); + _1d_pt[0] = tf_pt[0] - tf_vert_2d[i][0]; + _1d_endpt[0] = -tf_norm_2d[i][1]; + } + else // east/west normal + if (tf_norm_2d[i][0] > 0) // this is the south facing edge, east facing interior normal + { + set_n_chk_1(tf_pt[0] - tf_vert_2d[i][0]); + _1d_pt[0] = tf_pt[1] - tf_vert_2d[i][1]; + _1d_endpt[0] = tf_norm_2d[i][0]; + } else { + set_n_chk_1(tf_vert_2d[i][0] - tf_pt[0]); + _1d_pt[0] = tf_vert_2d[i][1] - tf_pt[1]; + _1d_endpt[0] = -tf_norm_2d[i][0]; + } + } + // ok, if we are here, we have already made sure _1d_pt[1] is tween 0 and -tf_cur_rad + // now we hack it totally for now, since we are lame... basically, just grow square + tf_Spew(BordChk, ("gc2: face %d 1d case pt0 %x pt1 %x endpt0 %x\n", i, _1d_pt[0], _1d_pt[1], _1d_endpt[0])); +#ifdef DIAGONAL_CORNERS + if ((_1d_pt[0] >= -tf_cur_rad + tf_pt[2]) && + (_1d_pt[0] <= _1d_endpt[0] + tf_cur_rad - tf_pt[2])) // over the attenuated facelet + return -_1d_pt[1]; +#else + if ((_1d_pt[0] >= -tf_cur_rad) && (_1d_pt[0] <= _1d_endpt[0] + tf_cur_rad)) // over the attenuated facelet + return -_1d_pt[1]; +#endif + } + return 0; +} + +// create normal set from vectors +#define set_norm(from, to) \ + tf_norm_2d[from][0] = tf_vert_2d[to][1] - tf_vert_2d[from][1]; \ + tf_norm_2d[from][1] = tf_vert_2d[from][0] - tf_vert_2d[to][0] + +void _tf_norm_create(void) { + int i; + for (i = 0; i < tf_pcnt - 1; i++) { + set_norm(i, i + 1); + } + set_norm(i, 0); +} + +#define cross_check(from, to) \ + if ((tf_vert_2d[to][0] < tf_pt[0]) != cross_lr) { \ + cross_lr = !cross_lr; \ + cross_face[cross_cnt][0] = from; \ + cross_face[cross_cnt][1] = to; \ + cross_cnt++; \ + } + +void _tf_get_crosses(void) { + int i, cross_lr; + + cross_cnt = 0; + cross_lr = tf_vert_2d[0][0] < tf_pt[0]; + + for (i = 0; i < tf_pcnt - 1; i++) { + cross_check(i, i + 1); + } + cross_check(i, 0); +} + +/* tf_solve_2d_case + * ok, the solver for facelet sets + * note... several things + * 1st: 5 vertices must be in the facelet list + * 2nd: box hints are used in the first pass and then split up + * 3rd: prebuilt normal/params set somehow? xtra params? + * 4th: pointlist is clockwise (from upper left if a box, else anywhere) + * + * first check for trivial cases (note easy box test and such) + * next build normals if necessary + * then do real internal check if triv case failed + * then the real border case if we have to + * + * returns the attenuation, so 0 means not involved + */ + +// todo: finish/optimize box cases +// get min/max during norm or cross create, then triv check prior to border mess + +fix tf_solve_2d_case(int flags) { + fix atv; + tf_pcnt = (flags & TF_FLG_3PNT_MASK) ? 3 : 4; + if (flags & TF_FLG_BOX_MASK) { + if (flags & TF_FLG_BOX_FULL) // really should do a standard point clip + { // do box internal/external check, set flag + fix xd, yd, aval; + xd = tf_vert_2d[0][0] - tf_pt[0]; // off left side? + if (xd < 0) + xd = tf_pt[0] - tf_vert_2d[2][0]; // if not, right? + if (xd < 0) + xd = 0; // in middle + yd = tf_vert_2d[2][1] - tf_pt[1]; // now top bottom + if (yd < 0) + yd = tf_pt[1] - tf_vert_2d[0][1]; // note reverse of 2 and 0 since + if (yd < 0) + yd = 0; // cartesian in lower left +#ifndef SET_FLAGS_ON_BOX + if ((xd | yd) == 0) + return fix_make(1, 0); // flags|=TF_FLG_ICHK_INT; + aval = (xd > yd) ? xd : yd; + if (aval > tf_cur_rad) + return 0; // flags|=TF_FLG_ICHK_OUT; + return fix_div(tf_cur_rad - aval, tf_cur_rad); // flags|=TF_FLG_ICHK_EDGE; +#else + if ((xd | yd) == 0) + flags |= TF_FLG_ICHK_INT; + else { + aval = (xd > yd) ? xd : yd; + if (aval > tf_cur_rad) + flags |= TF_FLG_ICHK_OUT; + else { + flags |= TF_FLG_ICHK_EDGE; + return fix_div(tf_cur_rad - aval, tf_cur_rad); + } + } + goto parse_ichk; +#endif + } + } + + // now set normals, really should check nhint and then do the right thing(tm) + // for now, create non-unitized normals from vertices, someday should do norm hints... + _tf_norm_create(); + + // if we havent gotten a real ichk value... + if ((flags & TF_FLG_ICHK_MASK) == 0) { + _tf_get_crosses(); + if ((cross_cnt == 2) && _tf_internal_chk()) + flags |= TF_FLG_ICHK_INT; + else + flags |= TF_FLG_ICHK_EDGE; + } + +#ifdef SET_FLAGS_ON_BOX +parse_ichk: +#endif + switch (flags & TF_FLG_ICHK_MASK) { + case TF_FLG_ICHK_INT: // return distance, set struct and all + return fix_make(1, 0); + case TF_FLG_ICHK_EDGE: // are we close enough? + break; // fall through to attentuation case + case TF_FLG_ICHK_NONE: + WARN("%s: tfd: no ichk data", __FUNCTION__); + case TF_FLG_ICHK_OUT: + return fix_0; + } + // if we are here, we are in the attentuation case... + atv = _tf_border_check_2d(); + if (atv > 0) + atv = fix_1 - fix_div(atv, tf_cur_rad); + return atv; +} + +// 3 feet, or so +#define STAIR_TOLERANCE fix_make(0, 0x6187) +#define STAIR_MIN fix_make(0, 0x0508) +int _stair_check(fix walls[4][2], int flags) { + if (flags & TF_FLG_BOX_FULL) { + int ad; + ad = walls[0][1] - walls[2][1]; + if (ad < STAIR_MIN) + return flags; + else if (ad < STAIR_TOLERANCE) + return flags | SS_BCD_MISC_STAIR; +#ifdef STAIRS_NEAR_THE_TOP + ad = walls[0][1] - tf_pt[1]; +#ifdef STAIRS_ABOVE_DA_TOP + if (ad < tf_cur_rad) +#else + if ((ad > 0) && (ad < tf_cur_rad)) +#endif + { + // mprintf("Pseudo-stair %x from %x and %x\n",ad,tf_pt[1],walls[0][1]); + return flags | SS_BCD_MISC_STAIR; + } +// else mprintf("no-pseudo-stair %x from %x and %x\n",ad,tf_pt[1],walls[0][1]); +#endif + } else { + if (tf_pcnt == 4) { + fix lv, rv, ad; +#ifdef STAIRS_NEAR_THE_TOP + fix xd, yd, slp, y; +#endif + lv = walls[0][1] - walls[3][1]; + rv = walls[1][1] - walls[2][1]; + ad = (lv + rv) >> 1; + if (ad < STAIR_MIN) + return flags; + if (ad < STAIR_TOLERANCE) + return flags | SS_BCD_MISC_STAIR; +#ifdef STAIRS_NEAR_THE_TOP + xd = walls[1][0] - walls[0][0]; + yd = walls[1][1] - walls[0][1]; + if (yd != 0) { + if (xd != 0) { + slp = fix_div(yd, xd); + y = fix_mul(slp, tf_pt[0] - walls[0][0]) + walls[0][1]; // y=mx+b + } else + return flags; + } else + y = walls[0][1]; + ad = y - tf_pt[1]; +#ifdef STAIRS_ABOVE_DA_TOP + if (ad < tf_cur_rad) +#else + if ((ad > 0) && (ad < tf_cur_rad)) +#endif + { + // mprintf("Pseudo-stair %x from %x and %x\n",ad,tf_pt[1],y); + return flags | SS_BCD_MISC_STAIR; + } +// else mprintf("no-pseudo-stair %x from %x and %x\n",ad,tf_pt[1],y); +#endif + } + } + return flags; +} + +// note: if it turns out aligned > 2*multi, we should do set and reset in multi for rad, and no rad set here +uchar tf_solve_aligned_face(fix pt[3], fix walls[4][2], int flags, fix *norm) { + fix att; + uchar rv = FALSE; + // if (norm!=NULL) + // tf_turn_on(0xffff); + tf_Spew(AlignFce, + ("tfd:aligned walls %x %x %x %x %x %x %x %x, pt %x %x %x, flg %x, nrm %x %x %x\n", walls[0][0], walls[0][1], + walls[1][0], walls[1][1], walls[2][0], walls[2][1], walls[3][0], walls[3][1], pt[0], pt[1], pt[2], flags, + norm != NULL ? norm[0] : 0xb, norm != NULL ? norm[1] : 0xa, norm != NULL ? norm[2] : 0xd)); + tf_cur_rad = tf_rad; // or reset in remetric + if ((pt[2] > 0) && (tf_cur_rad > pt[2])) // in range + { + tf_pt = pt; + tf_vert_2d = walls; + att = tf_solve_2d_case(flags); + if (att > 0) { + if (flags & SS_BCD_TYPE_WALL) + flags = _stair_check(walls, flags); + _tf_set_flet(flags, att, pt[2], norm); + rv = TRUE; + } + } + // if (norm!=NULL) + // tf_undo_set(Ret|FletList); + return rv; +} + +uchar tf_solve_remetriced_face(fix pt[3], fix walls[4][2], int flags, fix norm[3], fix metric) { + fix att; + uchar rv = FALSE; + // tf_turn_on(0xffff); + tf_Spew(RemetFce, ("tfd:remetric walls %x %x %x %x %x %x %x %x, pt %x %x %x, nrm %x %x %x, flg %x, metric %x\n", + walls[0][0], walls[0][1], walls[1][0], walls[1][1], walls[2][0], walls[2][1], walls[3][0], + walls[3][1], pt[0], pt[1], pt[2], norm[0], norm[1], norm[2], flags, metric)); + tf_pt = pt; + tf_vert_2d = walls; + // set current rad correctly + tf_cur_rad = fix_mul(metric, tf_rad); + if ((pt[2] > 0) && (tf_cur_rad > pt[2])) // in range + { + att = tf_solve_2d_case(flags); + if (att > 0) { + fix cdist = fix_div(pt[2], metric); + if (flags & SS_BCD_TYPE_WALL) + flags = _stair_check(walls, flags); + _tf_set_flet(flags, att, cdist, norm); + rv = TRUE; + } + } + // tf_undo_set(Ret|FletList); + return rv; +} + +// THIS IS BROKEN +// THE INITIAL CHECK SHOULD ADD TF_RAD^2 to rad +// BUT WE HAVE TO CUT FINAL IN 20 MINUTES, SO WE ARENT GOING TO CHANGE IT +uchar tf_solve_cylinder(fix pt[3], fix irad, fix height) { + uchar rv = FALSE, slv = FALSE; + int flags; + fix dist_sqrd, r_dist, rad = abs(irad), urad; + // first check height + if ((pt[2] < -tf_rad) || (pt[2] > height + tf_rad)) + return rv; // nope, high or low + dist_sqrd = fix_mul(pt[0], pt[0]) + fix_mul(pt[1], pt[1]); + urad = rad; + if (irad < 0) + urad += tf_cur_rad; + tf_Spew(Cylinder, ("ph %d pts %x %x scyl is in, dsqr is %x, rsq %x, rad %x\n", tf_ph, pt[0], pt[1], dist_sqrd, + fix_mul(rad, rad), rad)); + if ((irad < 0) || (fix_mul(rad, rad) > dist_sqrd)) { + tf_Spew(Cylinder, ("double rad hit..")); + if ((r_dist = fix_sqrt(dist_sqrd)) < urad) { + fix nrm[3], att = fix_1, cdist; + tf_Spew(Cylinder, ("scyl in..")); + if ((pt[2] < 0) || (pt[2] > height)) // flat top+bottom + { + LG_memset(nrm, 0, 3 * 4); // _memset32l(nrm,0,3); + + if (r_dist > (rad >> 1)) + att = fix_div(r_dist - (urad >> 1), (urad >> 1)); + else + att = fix_1; + if (irad > 0) { + if (pt[2] < 0) { + nrm[2] = -fix_1; + cdist = -pt[2]; + } else { + nrm[2] = fix_1; + cdist = pt[2] - height; + } + if (nrm[2] > 0) + flags = SS_BCD_PRIM_ZAXIS | SS_BCD_TYPE_FLOOR; + else + flags = SS_BCD_PRIM_NEG_Z | SS_BCD_TYPE_CEIL; + _tf_set_flet(flags, att, cdist, nrm); + tf_Spew(Cylinder, ("OverCyl")); + slv = TRUE; + } + } + if (!slv) // unitize normal, call us done + { + nrm[0] = fix_div(pt[0], r_dist); + nrm[1] = fix_div(pt[1], r_dist); + nrm[2] = 0; + if (r_dist > urad - tf_cur_rad) { + _tf_set_flet(SS_BCD_PRIM_MULTI | SS_BCD_TYPE_WALL, att, urad - r_dist, nrm); + tf_Spew(Cylinder, ("AroundCyl")); + } else { + _tf_set_flet(SS_BCD_PRIM_MULTI | SS_BCD_TYPE_WALL, att, 0, nrm); + tf_Spew(Cylinder, ("FullyInCyl")); + } + } + rv = TRUE; + // tf_turn_on(Ret|FletList); + } + } +#ifdef TF_TALK_SYSTEM + if (rv) + tf_Spew(Cylinder, ("\n")); +#endif + return rv; +} + +void terrfunc_one_map_square(int fmask) { // add appropriately set facelet_mask appropriately + if (fix_from_map_height(MAP_HEIGHTS - 1 - me_height_ceil(tfunc_mptr) - me_param(tfunc_mptr)) < tfunc_maxz) + fmask |= FACELET_MASK_C; + if (fix_from_map_height(me_height_flr(tfunc_mptr) + me_param(tfunc_mptr)) > tfunc_minz) + fmask |= FACELET_MASK_F; + tf_Spew(Grab, ("grabbing at %x %x, mask %x\n", tfunc_map_x, tfunc_map_y, fmask)); + fr_tfunc_grab_fast(fmask); // grab the facelets.. are these correct +} + +#define FACELET_MASK_Z 0 +#define fcs(v1, v2) ((FACELET_MASK_##v1) | (FACELET_MASK_##v2)) + +// probably could be just 5,5 by having it algorithimically flip the list if needed +uchar tf_wall_check[5][2][5] = { + {{fcs(Z, Z)}, {fcs(Z, Z)}}, + {{fcs(Z, Z), fcs(S, Z)}, {fcs(Z, N), fcs(Z, Z)}}, + {{fcs(Z, Z), fcs(S, N), fcs(Z, Z)}, {fcs(Z, Z), fcs(S, N), fcs(Z, Z)}}, + {{fcs(Z, Z), fcs(S, Z), fcs(S, N), fcs(Z, Z)}, {fcs(Z, Z), fcs(S, N), fcs(Z, N), fcs(Z, Z)}}, + {{fcs(Z, Z), fcs(S, Z), fcs(S, N), fcs(Z, N), fcs(Z, Z)}, {fcs(Z, Z), fcs(S, Z), fcs(S, N), fcs(Z, N), fcs(Z, Z)}}}; + +TerrainHit tf_direct(fix fix_x, fix fix_y, fix fix_z, fix rad, int32_t ph, TFType tf_type) { + int32_t fce_minc, xd, yd, xo, yo, centered; // fce_minc is map increment between lines, ?d LGRect size, xo clip offset + fix minx, miny, maxx, maxy, cenx, ceny; // for full radius of us, center for us, all really ints in the end + + tf_Spew(Calls, ("indoor %d at %x %x %x r %x\n", ph, fix_x, fix_y, fix_z, rad)); + cenx = fix_int(fix_x); + ceny = fix_int(fix_y); + ss_edms_facelet_cnt = ss_edms_bcd_flags = ss_edms_bcd_param = 0; + + // wacky bcd stuff.... + if (global_fullmap->cyber) { + MapElem *mp; + int32_t mb = -1, mt; + mp = MAP_GET_XY(cenx, ceny); + if ((mt = me_light_flr(mp)) != 0) + mb = mt - 1; + else if ((mt = me_light_ceil(mp)) != 0) + mb = 14 + mt; + if (mb != -1) + ss_edms_bcd_flags |= ((uint)v_to_cur[mb]) << SS_BCD_CURR_SHF; + } + if (tf_type == TFD_BCD) + return MISS; + + ObjsClearDealt(); + tf_talk_setup(); +#ifdef USE_OLD_PASSING + facelet_clear(); +#endif + + // find bounding map box + tf_rad = rad; + tf_ph = ph; + minx = fix_x - rad; + if (minx < 0) { + xo = fix_int(-minx) + 1; + minx = 0; + } else { + minx = fix_int(minx); + xo = 0; + } + miny = fix_y - rad; + if (miny < 0) { + yo = fix_int(-miny) + 1; + miny = 0; + } else { + miny = fix_int(miny); + yo = 0; + } + + maxx = fix_int(fix_x + rad); + xd = maxx - minx + 1 + xo; // get unclipped x width/distance stuff + if (maxx >= MAP_XSIZE) + maxx = MAP_XSIZE - 1; + maxy = fix_int(fix_y + rad); + yd = maxy - miny + 1 + yo; // get unclipped y width/distance stuff + if (maxy >= MAP_YSIZE) + maxy = MAP_YSIZE - 1; + tfunc_maxz = fix_z + rad; + tfunc_minz = fix_z - rad; // set top and bottom height for square + tfunc_mptr = MAP_GET_XY(minx, miny); + + tf_raw_pt[0] = fix_x; + tf_loc_pt[0] = fix_x - fix_make(minx, 0); // what is the coordinate system, eh? + tf_raw_pt[1] = fix_y; + tf_loc_pt[1] = fix_y - fix_make(miny, 0); + tf_raw_pt[2] = tf_loc_pt[2] = fix_z; + + tf_Spew(Area, ("looking from %x %x to %x %x, cen %x %x d's %d %d o's %d %d\n", minx, miny, maxx, maxy, cenx, ceny, + xd, yd, xo, yo)); + + if ((xd | yd) == 1) // only one square to do... + { + tfunc_map_x = minx; + tfunc_map_y = miny; // do we really need this? + terrfunc_one_map_square(FACELET_MASK_I); + tf_Stat(single); + } else { + uchar *xmsk_base, *ymsk_base, *xmsk_now, *ymsk_now; + int32_t xb; + + centered = (((xd & 1) == 0) && ((minx - xo + (xd >> 1)) == cenx)) ? 0 : 1; + xmsk_base = &tf_wall_check[xd - 1][centered][xo]; + centered = (((yd & 1) == 0) && ((miny - yo + (yd >> 1)) == ceny)) ? 0 : 1; + ymsk_base = &tf_wall_check[yd - 1][centered][yo]; + xb = tf_loc_pt[0]; + fce_minc = MAP_XSIZE - (maxx - minx) - 1; + for (tfunc_map_y = miny, ymsk_now = ymsk_base; tfunc_map_y <= maxy; + tfunc_mptr += fce_minc, tfunc_map_y++, ymsk_now++, tf_loc_pt[1] -= fix_1) + for (tfunc_map_x = minx, xmsk_now = xmsk_base, tf_loc_pt[0] = xb; tfunc_map_x <= maxx; + tfunc_mptr++, tfunc_map_x++, xmsk_now++, tf_loc_pt[0] -= fix_1) { + terrfunc_one_map_square(((*xmsk_now) << 1) | (*ymsk_now) | FACELET_MASK_I); + tf_Stat(multi); + if ((tf_type == TFD_RCAST) && ss_edms_facelet_cnt) + return HIT_FACELET; + } + } + if (tf_type == TFD_FULL) { // actually figure out what is up with the facelets, send and all +#ifdef USE_OLD_PASSING + facelet_send(); +#endif + } + tf_Spew(Ret, ("at %x %x %x r %x ret flg %x nrm %x %x %x, %x %x %x, %x %x %x\n", fix_x, fix_y, fix_z, rad, + ss_edms_bcd_flags, terrain_info.cx, terrain_info.cy, terrain_info.cz, terrain_info.fx, + terrain_info.fy, terrain_info.fz, terrain_info.wx, terrain_info.wy, terrain_info.wz)); + tf_Spew(Calls, ("indoor done for %d, tt %d, saw %d\n", tf_ph, tf_type, ss_edms_facelet_cnt)); + if (tf_talk_check(FletList)) + _tf_list_flet(); + + // should do something a little more real here, eh? + + if ((tf_ph != -1) && (ss_edms_facelet_cnt)) { + ObjID cobjid = physics_handle_to_id(tf_ph); + if (ObjProps[OPNUM(cobjid)].flags & SPCL_TERR_DMG) + special_terrain_hit(cobjid); + if (global_fullmap->cyber) + tile_hit(cenx, ceny); + } + + return (ss_edms_facelet_cnt == 0) ? MISS : HIT_FACELET; +} + +void tf_global_bcd_add(int flg, int param) { + if (flg & TF_FLG_HPARAM) { + flg &= ~TF_FLG_HPARAM; + ss_edms_bcd_param = param; + } + ss_edms_bcd_flags |= flg; +} + +/* actual call + * note us passing lots of annoying useless things on the stack and annoying everyone + */ +TerrainHit Indoor_Terrain(fix fix_x, fix fix_y, fix fix_z, fix rad, int ph, TFType type) { + return tf_direct(fix_x, fix_y, fix_z, rad, ph, type); +} diff --git a/engine/src/GameSrc/tfutil.c b/engine/src/GameSrc/tfutil.c new file mode 100644 index 0000000..d48b288 --- /dev/null +++ b/engine/src/GameSrc/tfutil.c @@ -0,0 +1,196 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/tfutil.c $ + * $Revision: 1.5 $ + * $Author: xemu $ + * $Date: 1994/09/01 20:19:27 $ + * + * contains utility routines to set, return, and modify tfunc returns + */ + +#include + +#include "tfdirect.h" + +// Internal Prototypes +void set_dumb_terrain_normal(int which, fix norm[3]); + +/* + * facelet system + * + * facelet_clear + * facelet_add + * facelet_send + */ + +typedef struct { + uchar cnt, prim, lprim, pad; + fix nrm[3], tval, bval, batt; +} tf_norm_cmp; + +typedef struct { + tf_norm_cmp ia, ua; +} tf_norm_set; + +// static data used by the facelet system +static tf_norm_set facets[3]; + +// really, have to say this is a pretty simple one, really +void facelet_clear(void) { + LG_memset(&facets[0], 0, sizeof(facets)); // _memset32l(&facets[0],0,sizeof(facets)/sizeof(long)); +} + +// facelet_add adds to the current facelet arrays +// it adds attenuation*compression*normal to the normal total, modifies cnts, if +// 1st unattenuated normal in a set or attenuated adds to total value and all + +// which is the facelet set we are adding to +// norm is the normal, unattenuated +// atten is the 0-1.0 attenuation factor for it +// value is the actual compression data +void facelet_add(int which, fix norm[3], fix atten, fix comp, int prim) { + tf_norm_set *cur_face; + tf_norm_cmp *cur_cmp; + + cur_face = &facets[which]; + if (atten == fix_1) { + cur_cmp = &cur_face->ua; + if (cur_cmp->cnt++ == 0) + cur_cmp->tval += atten; + } else { + cur_cmp = &cur_face->ia; + cur_cmp->tval += atten; + cur_cmp->cnt++; + comp = fix_mul(comp, atten); + if (cur_cmp->bval < comp) { + cur_cmp->bval = comp; + cur_cmp->batt = atten; + } + // cur_cmp->tmag+=comp; + } + if (cur_cmp->cnt == 1) + cur_cmp->prim = prim; + + if (prim != FCE_NO_PRIM) { + cur_cmp->nrm[prim] += fix_mul(comp, norm[prim]); + if (cur_cmp->prim != prim) + cur_cmp->prim = FCE_NO_PRIM; + } else { + cur_cmp->nrm[0] += fix_mul(comp, norm[0]); + cur_cmp->nrm[1] += fix_mul(comp, norm[1]); + cur_cmp->nrm[2] += fix_mul(comp, norm[2]); + } +} + +// for now, though wow, is this goofed out +extern TerrainData terrain_info; + +#ifdef COMPUTE_SEPARATES +// for now, since we dont have real distributed unit and mag +// we build it ourselves, for maximal pain +void set_real_terrain_normal(int which, fix mag, fix norm[3]) { + fix *targ_vec = &terrain_info.cx + (which * 3); + *targ_vec++ = fix_mul(norm[0], mag); + *targ_vec++ = fix_mul(norm[1], mag); + *targ_vec = fix_mul(norm[2], mag); +} +#else +void set_dumb_terrain_normal(int which, fix norm[3]) { // we aint proud + g3s_vector *targ_vec = (g3s_vector *)(&terrain_info.cx + (which * 3)); + + *targ_vec = *(g3s_vector *)norm; // _memcpy12(targ_vec,norm); +} +#endif + +#define sgn(x) ((x) & (1 << 31)) //&& (85*wtklwoii8y879t[p[p[p[[p[p)) + +void facelet_send(void) { + int i; + tf_norm_set *cur_face; + tf_norm_cmp *cur_cmp; + fix mag, *nrm; + + cur_face = &facets[0]; + for (i = 0; i < 3; i++, cur_face++) { + if ((cur_face->ia.cnt | cur_face->ua.cnt) == 0) { + LG_memset(&terrain_info.cx + (i * 3), 0, 3 * 4); // _memset32l(&terrain_info.cx+(i*3),0,3); + continue; // nope not nothing here... + } + nrm = cur_face->ua.nrm; + cur_cmp = &cur_face->ia; + if (cur_face->ua.cnt) { + fix *cvec, recip, lmag; + mag = fix_1 + cur_cmp->batt; + if (cur_cmp->cnt > 1) { + if (cur_cmp->prim != FCE_NO_PRIM) // well, we want bval*unitvec, here we are + { + if (sgn(nrm[cur_cmp->prim])) + nrm[cur_cmp->prim] -= cur_cmp->bval; + else // there has to be a smarter thing to do here.... + nrm[cur_cmp->prim] += cur_cmp->bval; + } else // grind through reality, ick + { + lmag = g3_vec_mag((g3s_vector *)&cur_cmp->nrm); + cvec = &cur_cmp->nrm[0]; + recip = fix_div(cur_cmp->bval, lmag); // look, make unitvector*bval all in one happy step + nrm[0] += fix_mul(*cvec, recip); + cvec++; + nrm[1] += fix_mul(*cvec, recip); + cvec++; + nrm[2] += fix_mul(*cvec, recip); + } + } else { + nrm[0] += cur_cmp->nrm[0]; + nrm[1] += cur_cmp->nrm[1]; + nrm[2] += cur_cmp->nrm[2]; + } + cvec = nrm; + recip = fix_div(fix_1, mag); + *cvec = fix_mul(*cvec, recip); + cvec++; + *cvec = fix_mul(*cvec, recip); + cvec++; + *cvec = fix_mul(*cvec, recip); + } else { + fix *cvec = &cur_cmp->nrm[0], lmag, recip; // hack_fac=fix_div(cur_cmp->bval,cur_cmp->tmag); + + nrm = cur_cmp->nrm; + if (cur_cmp->cnt > 1) { + if (cur_cmp->prim != FCE_NO_PRIM) // well, we want bval*unitvec, here we are + if (sgn(nrm[cur_cmp->prim])) + nrm[cur_cmp->prim] = -cur_cmp->bval; + else // there has to be a smarter thing to do here.... + nrm[cur_cmp->prim] = cur_cmp->bval; + else // grind through reality, ick + { + lmag = g3_vec_mag((g3s_vector *)&cur_cmp->nrm); + cvec = nrm; + recip = fix_div(cur_cmp->bval, lmag); // look, make unitvector*bval all in one happy step + nrm[0] += fix_mul(*cvec, recip); + cvec++; + nrm[1] += fix_mul(*cvec, recip); + cvec++; + nrm[2] += fix_mul(*cvec, recip); + } + } + } // now send the whole thing + set_dumb_terrain_normal(i, nrm); + } +} diff --git a/engine/src/GameSrc/tickcount.c b/engine/src/GameSrc/tickcount.c new file mode 100644 index 0000000..4de2e79 --- /dev/null +++ b/engine/src/GameSrc/tickcount.c @@ -0,0 +1,25 @@ +/* + +Copyright (C) 2019 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#include "tickcount.h" + +// number of ticks since system start (1 Tick is about 1/60 second) +uint32_t TickCount(void) { + return ((SDL_GetTicks() * 100) / 357); // 280 per second; +} \ No newline at end of file diff --git a/engine/src/GameSrc/tools.c b/engine/src/GameSrc/tools.c new file mode 100644 index 0000000..26582ee --- /dev/null +++ b/engine/src/GameSrc/tools.c @@ -0,0 +1,834 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/tools.c $ + * $Revision: 1.94 $ + * $Author: dc $ + * $Date: 1994/11/25 16:58:28 $ + */ + +// Source code from random useful tools and utilities + +#include +#include +#include + +#include "criterr.h" +#include "gr2ss.h" +#include "tools.h" +#include "mainloop.h" +#include "gamescr.h" +#include "musicai.h" +#include "colors.h" +#include "gamestrn.h" +#include "invdims.h" +#include "fullscrn.h" +#include "hud.h" +#include "canvchek.h" +#include "palfx.h" +#include "player.h" +#include "faketime.h" +#include "cit2d.h" + +#include "OpenGL.h" +#include "Shock.h" + +//------------ +// PROTOTYPES +//------------ +void simple_text_button(char *text, int xc, int yc, int col); +void Rect_gr_rect(LGRect *r); +void Rect_gr_box(LGRect *r); +char *itoa_2_10(char *s, int val); + +int str_to_hex(char val) { + int retval = 0; + if ((val >= '0') && (val <= '9')) + retval = val - '0'; + else if ((val >= 'A') && (val <= 'F')) + retval = 10 + val - 'A'; + else if ((val >= 'a') && (val <= 'f')) + retval = 10 + val - 'a'; + return (retval); +} + +void strtoupper(char *text) { + for (; *text; text++) { + if (islower(*text)) + (*text) += 'A' - 'a'; + } +} + +#ifdef SVGA_SUPPORT +uchar shadow_scale = TRUE; +#endif +void draw_shadowed_string(char *s, short x, short y, uchar shadow) { + LGPoint npt; + ubyte color = gr_get_fcolor(); + npt.x = x; + npt.y = y; + if (shadow && FONT_IS_MONO(gr_get_font())) // draw a black box + { +#ifdef SVGA_SUPPORT + extern char convert_use_mode; + if ((convert_use_mode > 0) && (perform_svga_conversion(OVERRIDE_FONT))) { + if (shadow_scale) + ss_point_convert(&(npt.x), &(npt.y), FALSE); + gr_set_fcolor(shadow); + ss_scale_string(s, npt.x - 1, npt.y - 1); + ss_scale_string(s, npt.x, npt.y - 1); + ss_scale_string(s, npt.x + 1, npt.y - 1); + ss_scale_string(s, npt.x, npt.y + 1); + ss_scale_string(s, npt.x - 1, npt.y + 1); + ss_scale_string(s, npt.x + 1, npt.y + 1); + ss_scale_string(s, npt.x - 1, npt.y); + ss_scale_string(s, npt.x + 1, npt.y); + gr_set_fcolor(color); + ss_scale_string(s, npt.x, npt.y); + } else +#endif + { + gr_set_fcolor(shadow); + gr_string(s, npt.x - 1, npt.y - 1); + gr_string(s, npt.x, npt.y - 1); + gr_string(s, npt.x + 1, npt.y - 1); + gr_string(s, npt.x, npt.y + 1); + gr_string(s, npt.x - 1, npt.y + 1); + gr_string(s, npt.x + 1, npt.y + 1); + gr_string(s, npt.x - 1, npt.y); + gr_string(s, npt.x + 1, npt.y); + gr_set_fcolor(color); + gr_string(s, npt.x, npt.y); + } + } else { + gr_set_fcolor(color); + // gr_string(s,npt.x,npt.y); + ss_string(s, npt.x, npt.y); + } +} + +void draw_hires_resource_bm(Ref id, int x, int y) { + FrameDesc *f = RefLock(id); + if (f == NULL) + critical_error(CRITERR_MEM | 9); + gr_bitmap(&f->bm, x, y); + RefUnlock(id); +} + +void draw_hires_halfsize_bm(Ref id, int x, int y) { + FrameDesc *f = RefLock(id); + if (f == NULL) + critical_error(CRITERR_MEM | 9); + gr_scale_bitmap(&f->bm, x, y, (f->bm.w >> 1), (f->bm.h >> 1)); + RefUnlock(id); +} + +errtype draw_raw_res_bm_temp(Ref id, int x, int y) { + FrameDesc *f = RefLock(id); + if (f == NULL) { + return ERR_FREAD; + } + ss_bitmap(&f->bm, x, y); + RefUnlock(id); + return OK; +} + +errtype draw_raw_resource_bm(Ref id, int x, int y) { + FrameDesc *f; + + f = RefLock(id); + if (f == NULL) + critical_error(CRITERR_MEM | 9); + ss_bitmap(&f->bm, x, y); + RefUnlock(id); + return (OK); +} + +errtype draw_res_bm_core(Ref id, int x, int y, uchar scale) { + FrameDesc *f; + LGRect mouse_rect; + + f = RefLock(id); + if (f == NULL) + critical_error(CRITERR_MEM | 9); + mouse_rect.ul.x = x; + mouse_rect.ul.y = y; + mouse_rect.lr.x = x + f->bm.w; + mouse_rect.lr.y = y + f->bm.h; + + // Set the palette right, if one is provided.... + if (is_onscreen()) + uiHideMouse(&mouse_rect); + if (scale) + ss_bitmap(&f->bm, x, y); + else + ss_noscale_bitmap(&f->bm, x, y); + if (is_onscreen()) + uiShowMouse(&mouse_rect); + RefUnlock(id); + return (OK); +} + +errtype draw_res_bm(Ref id, int x, int y) { return (draw_res_bm_core(id, x, y, TRUE)); } + +// Note, does no mouse code! +errtype draw_full_res_bm(Ref id, int x, int y, uchar fade_in) { + FrameDesc *f; + short *temp_pall; + byte pal_id; + + f = RefLock(id); + if (f == NULL) + critical_error(CRITERR_MEM | 9); + + // Set the palette right, if one is provided.... + if (f->pallOff) { + // FIXME nasty hack to get at the private palette; the private palette + // is an offset from the start of the raw on-disc resource, so it + // includes the (raw) reftable as well as any previous ref entries. + RefTable *prt = (RefTable *)ResGet(REFID(id)); + // Raw reftable size + size_t tabsize = sizeof(RefIndex) + (prt->numRefs+1) * sizeof(uint32_t); + temp_pall = (short *)((uchar *)prt->raw_data - tabsize + f->pallOff); + gr_set_pal(*temp_pall, *(temp_pall + 1), (uchar *)(temp_pall + 2)); + } + + if (fade_in && temp_pall != NULL) { + pal_id = palfx_start_fade_up((uchar *)(temp_pall + 2)); + } + + f->bm.bits = (uchar *)(f + 1); + ss_bitmap(&f->bm, x, y); // KLC ss_bitmap(&f->bm, x, y); + RefUnlock(id); + if (fade_in) + finish_pal_effect(pal_id); + ResDrop(REFID(id)); + return (OK); +} + +int res_bm_width(Ref id) { + FrameDesc *f; + int n; + + f = RefLock(id); + if (f == NULL) + critical_error(CRITERR_MEM | 9); + n = f->bm.w; + RefUnlock(id); + return (n); +} + +int res_bm_height(Ref id) { + FrameDesc *f; + int n; + + f = RefLock(id); + if (f == NULL) + critical_error(CRITERR_MEM | 9); + n = f->bm.h; + RefUnlock(id); + return (n); +} + +errtype res_draw_text_shadowed(Id id, char *text, int x, int y, uchar shadow) { + gr_set_font(ResLock(id)); + draw_shadowed_string(text, x, y, shadow); + ResUnlock(id); + return (OK); +} + +errtype res_draw_string(Id font, int strid, int x, int y) { return res_draw_text(font, get_temp_string(strid), x, y); } + +// have some god damn parameters +// xc,yc is position, usually text center +// w.h. is rectangle wid+hgt, note <0 means that the x|yc is the upper left, not the center +// shad is how much to shade down color (-1 is no shadow) +// col is color of rectangle and text +void text_button(char *text, int xc, int yc, int col, int shad, int w, int h) { + int ux, uy; + short tw, th; + + gr_string_size(text, &tw, &th); + + // do wacked out conversionitis + if (w < 0) { + ux = xc; + w = -w; + xc = ux + (w >> 1); + } else + ux = xc - (w >> 1); + if (h < 0) { + uy = yc; + h = -h; + yc = uy + (h >> 1); + } else + uy = yc - (h >> 1); + + // two rectangles... + gr_set_fcolor(col); + ss_rect(ux, uy, ux + w, uy + h); + if (shad >= 0) { + gr_set_fcolor(col + shad); + ss_rect(ux + 1, uy + 1, ux + w - 1, uy + h - 1); + gr_set_fcolor(col); + } + // some text, eh? + ss_string(text, xc - (tw >> 1), yc - (th >> 1)); +} + +// ok, the easy case... +// centered at xc,yc, color base, auto-shadowed, size out setting +void simple_text_button(char *text, int xc, int yc, int col) { + short w, h; + gr_string_size(text, &w, &h); + text_button(text, xc, yc, col, 4, w + 12, h + 8); +} + +void Rect_gr_rect(LGRect *r) { ss_rect(r->ul.x, r->ul.y, r->lr.x, r->lr.y); } + +void Rect_gr_box(LGRect *r) { ss_box(r->ul.x, r->ul.y, r->lr.x, r->lr.y); } + +char *itoa_2_10(char *s, int val) { + s[0] = '0' + (val / 10); + s[1] = '0' + (val % 10); + s[2] = '\0'; + return s; +} + +// max 99 hours... +void second_format(int sec_remain, char *s) { + int c_l; + if (sec_remain >= 3600) { + itoa_2_10(s, sec_remain / 3600); + sec_remain %= 3600; + c_l = 3; + s[2] = ':'; + } else + c_l = 0; + itoa_2_10(s + c_l, sec_remain / 60); + s[c_l + 2] = ':'; + sec_remain %= 60; + itoa_2_10(s + c_l + 3, sec_remain); + if (s[0] == '0') + s[0] = ' '; +} + +#ifdef NOT_YET // later, dude + +#define BIG_BUF + +#pragma disable_message(202) +uchar gifdump_func(short keycode, ulong context, void *data) { + unsigned char *temp_buf; + int giffp; + char harold[45]; + + strcpy(harold, "SHOCK000.GIF"); + giffp = open_gen(harold, O_CREAT | O_BINARY | O_WRONLY | O_TRUNC, S_IWRITE); + if (giffp == -1) { + message_info("GIF dump failed!"); + return (ERR_NOEFFECT); + } + { + temp_buf = big_buffer; + gd_dump_screen(giffp, temp_buf); + strcat(harold, " saved"); + message_info(harold); + } + return (TRUE); +} +#pragma enable_message(202) + +#endif // NOT_YET + +#define FULLSCREEN_MESSAGE_X 125 +#define FULLSCREEN_MESSAGE_Y 8 + +#define MESSAGE_BUFSZ 128 + +#ifdef SVGA_SUPPORT_HATE_HATE +void mouse_unconstrain(void) { + // Note we are not calling the UI here since we are looking + // at actual screen size + mouse_constrain_xy(0, 0, grd_cap->w - 1, grd_cap->h - 1); +} +#endif + +errtype string_message_info(int strnum) { + char buf[MESSAGE_BUFSZ]; + get_string(strnum, buf, MESSAGE_BUFSZ); + return message_info(buf); +} + +char last_message[128]; +ulong message_clear_time; + +#define MESSAGE_INTERVAL 1200 +#define CHAR_SOFTCR 0x01 // soft carriage return (wrapped text) +#define CHAR_SOFTSP 0x02 // soft space (wrapped text) +#define MESSAGE_LEN 80 + +LGRect msg_rect[2] = { + {GAME_MESSAGE_X, GAME_MESSAGE_Y, GAME_MESSAGE_X + GAME_MESSAGE_W, GAME_MESSAGE_Y + GAME_MESSAGE_H}, + {FULLSCREEN_MESSAGE_X, FULLSCREEN_MESSAGE_Y, FULLSCREEN_MESSAGE_X + GAME_MESSAGE_W, + FULLSCREEN_MESSAGE_Y + GAME_MESSAGE_H}}; + +uchar message_resend = FALSE; +extern uchar game_paused; +extern uchar view360_message_obscured; + +// Use the string wrapper's secret characters to delete newlines and double spaces. +void strip_newlines(char *buf) { + char *s; + for (s = buf; *s != '\0'; s++) { + if (*s == '\n') + *s = CHAR_SOFTSP; + if (isspace(*s) && isspace(*(s + 1))) + *s = CHAR_SOFTSP; + } +} + +errtype message_info(const char *info_text) { + int x, y; + char buf[MESSAGE_LEN]; + + if (info_text != NULL) { + strncpy(buf, info_text, MESSAGE_LEN); + strip_newlines(buf); + } else + buf[0] = '\0'; + if (_current_loop <= FULLSCREEN_LOOP) { + short a, b, c, d; + LGRect *r = &msg_rect[(full_game_3d && !game_paused) ? 1 : 0]; + + x = r->ul.x; + y = r->ul.y; + if (is_onscreen()) + uiHideMouse(r); + gr_push_canvas(grd_screen_canvas); + STORE_CLIP(a, b, c, d); + + ss_safe_set_cliprect(r->ul.x, r->ul.y, r->lr.x, r->lr.y); + if (!full_game_3d) { + y += 1; + if (!view360_message_obscured || game_paused) { + draw_raw_resource_bm(REF_IMG_bmBlankMessageLine, x, y); + // draw_hires_resource_bm(REF_IMG_bmBlankMessageLine, + // SCONV_X(x), + //SCONV_Y(y)); + } + x += 2; + } else if (game_paused) { + extern grs_canvas inv_view360_canvas; + ss_noscale_bitmap(&inv_view360_canvas.bm, x, y); + x += 2; + y += 1; + } + if (!message_resend && info_text != last_message && strcmp(last_message, info_text) == 0) { + message_resend = TRUE; + message_clear_time = *tmd_ticks + CIT_CYCLE / 10; + hud_unset(HUD_MSGLINE); + } else { + message_resend = FALSE; + if ((!full_game_3d && !view360_message_obscured) || game_paused) { + gr_set_fcolor(WHITE); + res_draw_text_shadowed(RES_tinyTechFont, buf, x, y, full_game_3d); + hud_unset(HUD_MSGLINE); + } else if (full_game_3d || view360_message_obscured) { + if (buf[0] != '\0') { + hud_set_time(HUD_MSGLINE, 5 << APPROX_CIT_CYCLE_SHFT); + } + } + } + RESTORE_CLIP(a, b, c, d); + gr_pop_canvas(); + if (is_onscreen()) + uiShowMouse(r); + } + if (!message_resend && info_text != last_message) { + message_clear_time = *tmd_ticks + MESSAGE_INTERVAL; + strcpy(last_message, info_text); + } + return (OK); +} + +uchar message_clear_on = TRUE; + +errtype message_clear_check() { + // much as I like spews that print every frame....... + // Spew(DSRC_GAMESYS_Messages, ("%d >? %d\n",player_struct.game_time,message_clear_time)); + if (*tmd_ticks < message_clear_time) + return OK; + if (message_resend) { + char buf[sizeof(last_message)]; + strcpy(buf, last_message); + return message_info(buf); + } + if (message_clear_on && !full_game_3d && !view360_message_obscured) { + errtype retval = message_info(""); + return retval; + } + return (OK); +} + +errtype message_box(char *box_text) { + message_info(box_text); + return (OK); +} + +#ifdef NOT_YET // later, dude + +#pragma disable_message(202) +uchar confirm_box(char *confirm_text) { return (TRUE); } +#pragma enable_message(202) + +FILE *fopen_gen(char *fname, char *t) { + Datapath gen_path; + FILE *retval; + char temp[64]; + + gen_path.numDatapaths = 0; + gen_path.noCurrent = 1; + DatapathAddDir(&gen_path, "gen"); + DatapathAddEnv(&gen_path, "GEN_DIR"); + strcpy(temp, getenv("CITHOME")); + strcat(temp, "\\gen"); + DatapathAddDir(&gen_path, temp); + DatapathNoCurrent(&gen_path); + next_number_dpath_fname(&gen_path, fname); + retval = DatapathOpen(&gen_path, fname, t); + DatapathFree(&gen_path); + return retval; +} + +int open_gen(char *fname, int access1, int access2) { + Datapath gen_path; + int retval; + + gen_path.numDatapaths = 0; + gen_path.noCurrent = 1; + DatapathAddDir(&gen_path, "gen"); + DatapathAddEnv(&gen_path, "GEN_DIR"); + DatapathNoCurrent(&gen_path); + next_number_dpath_fname(&gen_path, fname); + retval = DatapathFDOpen(&gen_path, fname, access1, access2); + DatapathFree(&gen_path); + return retval; +} + +char *next_number_dpath_fname(Datapath *dpath, char *fname) { + char *subname = strrchr(fname, '0'); + int fhnd, numlen = 1, i, num = 0; + + if (subname != NULL) { + while ((strlen(subname) != strlen(fname)) && (subname[0] == subname[-1])) { + subname--; + numlen++; + } + // try them, lets go, rock and roll, so on + while ((fhnd = DatapathFDOpen(dpath, fname, O_BINARY | O_RDONLY)) != -1) { /* Check next slot */ + close(fhnd); /* good idea to, like, close the opened file */ + ++num; + for (i = 0; i < numlen; i++) + subname[numlen - (i + 1)] = '0' + ((num >> (3 * i)) & 7); + } + close(fhnd); + } + return fname; +} + +char *next_number_fname(char *fname) { + char *subname = strrchr(fname, '0'); + int fhnd, numlen = 1, i, num = 0; + + while ((strlen(subname) != strlen(fname)) && (subname[0] == subname[-1])) { + subname--; + numlen++; + } + /* Look for files like uwpic000.gif */ + while ((fhnd = open(fname, O_BINARY | O_RDONLY)) != -1) { /* Check next slot */ + close(fhnd); /* good idea to, like, close the opened file */ + ++num; + for (i = 0; i < numlen; i++) + subname[numlen - (i + 1)] = '0' + ((num >> (3 * i)) & 7); + } + close(fhnd); + return fname; +} + +#endif // NOT_YET + +errtype tight_loop(uchar check_input) { + if (music_on) + mlimbs_do_ai(); + + // KLC - does nothing! + // if (music_on || sfx_on) + // synchronous_update(); + + if (check_input) { + uiPoll(); + kb_flush_bios(); + } + return (OK); +} + +// -------------------------------------------------- +// STRING WRAPPER + +#define HYPHEN '-' + +// FIXME This code duplicates gr_font_string_wrap() +int hyphenated_wrap_text(char *ps, char *out, short width) { + char *psbase; + char *p; + char *pmark; + short numLines; + short currWidth; + + // Set up to do wrapping + + psbase = ps; // psbase = string beginning + numLines = 0; // ps = base of current line + + // Do wrapping for each line till hit end + + while (*ps) { + pmark = NULL; // no SOFTCR insert LGPoint yet + currWidth = 0; // and zero width so far + p = ps; + + // Loop thru each word + + while (*p) { + + // Skip through to next CR or space or '\0', keeping track of width + + while ((*p != 0) && (*p != '\n') && (*p != ' ') && (*p != CHAR_SOFTSP)) { + currWidth += gr_char_width(*p); + p++; + } + + // If bypassed width, break out of word loop + + if (currWidth > width || + (*p == CHAR_SOFTSP && (currWidth + gr_char_width(HYPHEN)) > width)) { + if ((pmark == NULL) && (*p != 0) && (*p != '\n')) + pmark = p; + break; + } + + // Else set new mark LGPoint (unless eol or eos, then bust out) + + else { + if ((*p == 0) || (*p == '\n')) // hit end of line, wipe marker + { + pmark = NULL; + break; + } + pmark = p; // else advance marker + currWidth += gr_char_width(*p); // and account for space + p++; + } + } + + // Now insert soft cr if marked one + + if (pmark) { + strncpy(out, ps, pmark - ps); + out += pmark - ps; + if (*pmark == CHAR_SOFTSP) + *out++ = HYPHEN; + *out++ = CHAR_SOFTCR; + ps = pmark + 1; + if (*ps == ' ') // if wrapped and following space, + ps++; // turn into (ignored) soft space + } + + // Otherwise, bump past cr + else { + strncpy(out, ps, p - ps + 1); + out += p - ps + 1; + + if (*p) + ++p; + ps = p; + } + + // Bump line counter in any case + + ++numLines; + } + + // When hit end of string, return # lines encountered + + return (numLines); +} + +// -------------------------------------------------------------------- +// WAIT CURSOR + +char wait_count = 0; + +errtype begin_wait() { + extern LGCursor wait_cursor; + errtype retval; + if (wait_count == 0) { + uiHideMouse(NULL); + retval = uiPushGlobalCursor(&wait_cursor); + uiShowMouse(NULL); + + SDLDraw(); + } + wait_count++; + + return (retval); +} + +#ifdef NOT_YET // +errtype spoof_mouse_event(void) { + int i; + uiMouseEvent ev; + + uiMakeMotionEvent(&ev); + if (ev.buttons == 0) + return OK; + for (i = 0; i < NUM_MOUSE_BTNS; i++) { + if (ev.buttons & (1 << i)) + ev.action |= MOUSE_BTN2DOWN(i); + } + ev.type = UI_EVENT_MOUSE; + return uiQueueEvent((uiEvent *)&ev); +} +#endif // NOT_YET + +errtype end_wait() { + errtype retval; + wait_count--; + if (wait_count <= 0) { + uiHideMouse(NULL); + retval = uiPopGlobalCursor(); + uiShowMouse(NULL); + wait_count = 0; + uiFlush(); + // spoof_mouse_event(); + } + return (retval); +} + +// -------------------------------------------------------------------- +// ZOOM BOXES + +/* + * Original zoom timing was 8 * 10 ticks at a timer frequency of 280 Hz, i.e. + * around 286 milliseconds. Assuming a refresh rate of 60 Hz that's around 17 + * frames. + */ + +#define ZOOM_MS 286 + +#define INTERP(s, f, i) (((f) * (i) + (s) * (ZOOM_MS - (i)-1)) / (ZOOM_MS - 1)) + +bool ZoomEnable; +LGRect ZoomStart, ZoomEnd; +Uint32 ZoomTicks, ZoomI; + +void zoom_rect(LGRect *start, LGRect *end) +{ + //set global variables used by ZoomProc() + ZoomEnable = TRUE; + ZoomStart = *start; + ZoomEnd = *end; + ZoomTicks = SDL_GetTicks(); +} + +//called just before and after SDLDraw() in mainloop() +void ZoomDrawProc(int erase) +{ + if (!ZoomEnable) return; + + if (!erase) + { + ZoomI = SDL_GetTicks() - ZoomTicks; + if (ZoomI >= ZOOM_MS) {ZoomEnable = 0; return;} + } + + int ft = gr_get_fill_type(); + int c = gr_get_fcolor(); + gr_set_fill_type(FILL_XOR); + gr_set_fcolor(WHITE); + + // make the zoom rectanle visible in OpenGL as well + if(full_game_3d && use_opengl()) { + gr_set_fcolor(0x1); + } + + short ulx = INTERP(ZoomStart.ul.x, ZoomEnd.ul.x, ZoomI); + short uly = INTERP(ZoomStart.ul.y, ZoomEnd.ul.y, ZoomI); + short lrx = INTERP(ZoomStart.lr.x, ZoomEnd.lr.x, ZoomI); + short lry = INTERP(ZoomStart.lr.y, ZoomEnd.lr.y, ZoomI); + ss_box(ulx, uly, lrx, lry); + ss_box(ulx - 1, uly - 1, lrx + 1, lry + 1); + + gr_set_fill_type(ft); + gr_set_fcolor(c); +} + +// Returns the angle of difference between look_facing and the true direction +// that looker would have to be facing in order to see target, and puts that +// true direction into real_dir. + +// Wow, I really ought to someday make this not use icky trig +// but instead write some fast simple version +fixang point_in_view_arc(fix target_x, fix target_y, fix looker_x, fix looker_y, fixang look_facing, fixang *real_dir) { + fix x_diff, y_diff; + fixang retval; + + x_diff = target_x - looker_x; + y_diff = target_y - looker_y; + *real_dir = fix_atan2(y_diff, x_diff); + + // Compensate for difference between our coordinate system and fixpoint's + + // Hmmm, how do fixangs deal with negatives? + // Better normalize to absolute difference, just to be sure + // After all, they do have *real_dir to figure it out themselves + // if they want to. + if (*real_dir > look_facing) + retval = (*real_dir - look_facing); + else + retval = (look_facing - *real_dir); + if (retval > 0x8000) + retval = 0x10000 - retval; + return (retval); +} + +// convert occurances of the character "from" to the character "to" +// in the string "s" +void string_replace_char(char *s, char from, char to) { + for (; *s; s++) { + if (*s == from) + *s = to; + }; +} + +//gamma param not used here; see SetSDLPalette() in Shock.c +void gamma_dealfunc(ushort gamma_qvar) { + gr_set_gamma_pal(0, 256, 0); +} diff --git a/engine/src/GameSrc/trigger.c b/engine/src/GameSrc/trigger.c new file mode 100644 index 0000000..071e9c6 --- /dev/null +++ b/engine/src/GameSrc/trigger.c @@ -0,0 +1,2266 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/trigger.c $ + * $Revision: 1.161 $ + * $Author: xemu $ + * $Date: 1994/11/25 18:18:30 $ + * + */ + + +#include +#include + +#include "Prefs.h" + +#include "ai.h" +#include "audiolog.h" +#include "cybstrng.h" +#include "damage.h" +#include "diffq.h" +#include "doorparm.h" +#include "effect.h" +#include "faketime.h" +#include "frflags.h" +#include "frprotox.h" +#include "gameloop.h" // for VITALS_UPDATE +#include "gamerend.h" +#include "gamescr.h" +#include "gamestrn.h" +#include "leanmetr.h" +#include "mainloop.h" // for flag setting stuff +#include "map.h" +#include "mapflags.h" +#include "mfdext.h" +#include "musicai.h" +#include "objbit.h" +#include "objcrit.h" +#include "objgame.h" +#include "objsim.h" +#include "objstuff.h" +#include "objuse.h" +#include "olhext.h" +#include "otrip.h" +#include "physics.h" +#include "player.h" +#include "rendtool.h" +#include "saveload.h" +#include "schedule.h" +#include "sfxlist.h" +#include "shodan.h" +#include "tilename.h" +#include "tools.h" +#include "trigger.h" +#include "cyber.h" +#include "colors.h" +#include "grenades.h" +#include "bark.h" +#include "view360.h" +#include "objload.h" +#include "rendfx.h" +#include "statics.h" +#include "citres.h" +#include "setploop.h" +#include "cutsloop.h" + +#ifdef OLD_TELEPORT_BETWEEN_LEVELS +#include +#include +#include +#endif + +// As far as I can tell, these NEVER GET USED. So I thought I'd move them out of the file +// to remove a dependency problem. + +#define TRAP_NULL_CODE 0 +#define TRAP_TELEPORT_CODE 1 +#define TRAP_DAMAGE_CODE 2 +#define TRAP_CREATE_OBJ_CODE 3 +#define TRAP_QUESTBIT_CODE 4 +#define TRAP_ENDGAME_CODE 5 +#define TRAP_MULTI_CODE 6 +#define TRAP_LIGHT_CODE 7 +#define TRAP_SFX_CODE 8 +#define TRAP_HEIGHT_CODE 9 +#define TRAP_TERRAIN_CODE 10 +#define TRAP_SCHEDULER_CODE 11 +#define TRAP_ALT_SPLIT_CODE 12 +#define TRAP_DESTROY_OBJ_CODE 13 +#define TRAP_PLOT_CLOCK_CODE 14 +#define TRAP_EMAIL_CODE 15 +#define TRAP_EXPOSE_CODE 16 +#define TRAP_INSTANCE_CODE 17 +#define TRAP_ANIMATE_CODE 18 +#define TRAP_HACK_CODE 19 +#define TRAP_TEXTURE_CODE 20 +#define TRAP_AI_CODE 21 +#define TRAP_BARK_CODE 22 +#define TRAP_MONSTER_CODE 23 +#define TRAP_TRANSMOGRIFY_CODE 24 + +// Note that this will always give you boolean 0 or 1, as opposed to +// things like (p2 & 0x10000) that certain people once used which is +// always 0 when cast to a bool. + +#define BIT_SET(val, bit) (((val) & (1 << bit)) == (1 << bit)) + +#define REACTOR_BOOM_QB 0x14 +errtype do_special_reactor_hack(); + +errtype do_destroy(int victim_data); + +ObjID current_trap; +uchar _tr_use_message; +#define trap_use_message (&_tr_use_message) + +errtype qdata_set(short qdata, short new_val); +errtype set_trap_data(ObjID id, char num_param, int new_val); +errtype do_timed_multi_stuff(int p); + +errtype trap_null_func(int p1, int p2, int p3, int p4); +errtype trap_transmogrify_func(int p1, int p2, int p3, int p4); +uchar player_facing_square(LGPoint sq); +errtype trap_monster_func(int p1, int p2, int p3, int p4); +errtype do_ai_trap(ObjSpecID osid, int p1, int p3, int p4); +errtype trap_ai_func(int p1, int p2, int p3, int p4); +errtype trap_alternating_splitter_func(int p1, int p2, int p3, int p4); +errtype trap_main_light_func(int p1, int p2, int p3, int p4); +errtype trap_terrain_func(int p1, int p2, int p3, int p4); +errtype trap_height_func(int p1, int p2, int p3, int p4); +errtype real_instance_func(int p1, int p2, int p3, int p4); +errtype trap_instance_func(int p1, int p2, int p3, int p4); +errtype real_animate_func(ObjID id, int p2, int p3, int p4); +errtype trap_animate_func(int p1, int p2, int p3, int p4); +void hack_shodan_conquer_func(char bonus_fun); +void hack_armageddon_func(int otrip, int x0, int y0, int r); +void hack_multi_trans(int trip, int newtype); +void hack_change_comparator(int p2, int p3); +void hack_taunt_diego(int p2, int p3); +errtype trap_hack_func(int p1, int p2, int p3, int p4); +errtype trap_multi_func(int p1, int p2, int p3, int p4); +errtype trap_destroy_object_func(int p1, int p2, int p3, int p4); +errtype trap_plot_clock_func(int p1, int p2, int p3, int p4); +errtype trap_email_func(int mung, int time, int p3, int p4); +errtype trap_texture_func(int p1, int p2, int p3, int p4); +errtype trap_expose_func(int dmg, int dtype, int tsecs, int dummy); +errtype trap_bark_func(int speaker, int strnum, int color, int hud_bark); + +errtype grind_trap(char type, int p1, int p2, int p3, int p4, ubyte *destroy_count_ptr, ObjID id); +errtype do_ecology_triggers(); + +#define SHODOMETER_QVAR_BASE 0x10 + +short qdata_get(short qdata) { + short contents = qdata & 0xFFF; + if (qdata & 0x1000) { + if ((contents >= FIRST_SHODAN_QV) && (contents <= FIRST_SHODAN_QV + MAX_SHODOMETER_LEVEL)) { + short retval; + if (QUESTVAR_GET(MISSION_DIFF_QVAR) <= 1) + return (0); + retval = QUESTVAR_GET(contents) * 255 / player_struct.initial_shodan_vals[contents - FIRST_SHODAN_QV]; + if (retval > 255) + retval = 255; + return (retval); + } else + return (QUESTVAR_GET(contents)); + } else if (qdata & 0x2000) + return (QUESTBIT_GET(contents)); + else + return (contents); +} + +errtype qdata_set(short qdata, short new_val) { + if (qdata & 0x1000) + QUESTVAR_SET(qdata & 0xFFF, new_val); + else if (qdata & 0x2000) { + if ((new_val > 1) || (new_val < -1)) { + if (qdata_get(qdata)) + new_val = 0; + else + new_val = 1; + } + if (new_val) + QUESTBIT_ON(qdata & 0xFFF); + else + QUESTBIT_OFF(qdata & 0xFFF); + } + + mfd_notify_func(MFD_PLOTWARE_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, TRUE); + + return (OK); +} + +uchar comparator_check(int comparator, ObjID obj, uchar *special_code) { + short cval, compval; + uchar fail_code; + short fail_amt = 0; + uchar truthval = FALSE; + // shodo_qvar is the questvariable for the shodometer on the current levelle + char shodo_qvar = SHODOMETER_QVAR_BASE + player_struct.level; + + *special_code = 0; + fail_code = comparator >> 24; + comparator = comparator & 0xFFFFFF; + if (comparator == 0) + return (TRUE); + compval = comparator >> 16; + if (comparator & 0x1000) { + cval = qdata_get(0x1000 | (comparator & 0xFFF)); // QUESTVAR_GET(comparator & 0xFFF); + } else + cval = (QUESTBIT_GET(comparator & 0xFFF) != 0); + switch ((comparator & 0xE000) >> 13) { + case 0: + truthval = (cval == compval); + fail_amt = abs(cval - compval); + break; + case 1: + truthval = (cval < compval); + fail_amt = cval - compval; + break; + case 2: + truthval = (cval <= compval); + fail_amt = cval - compval; + break; + case 3: + truthval = (cval > compval); + fail_amt = compval - cval; + break; + case 4: + truthval = (cval >= compval); + fail_amt = compval - cval; + break; + case 5: + truthval = (cval != compval); + fail_amt = abs(cval - compval); + break; + case 6: + truthval = (compval > (rand() % 255)); + break; + } + if (!truthval && fail_code) { + if ((comparator & 0xFFF) == shodo_qvar) + *special_code = fail_amt; + if ((*special_code != 0) && (fail_code == SPECIAL_SHODAN_FAIL_CODE)) { + short shodan_amt; + shodan_amt = lg_min(NUM_SHODAN_MUGS - 1, *special_code >> SHODAN_INTERVAL_SHIFT); + long_bark(obj, FIRST_SHODAN_MUG + shodan_amt, SHODAN_FAILURE_STRING, 0x4c); + } else { + string_message_info(REF_STR_TrapZeroMessage + fail_code); +#ifdef AUDIOLOGS + audiolog_bark_play(fail_code); +#endif + } + } + + return (truthval); +} + +errtype set_trap_data(ObjID id, char num_param, int new_val) { + uint *pbase = NULL; + ObjSpecID osid = objs[id].specID; + + if (num_param < 1 || num_param > 4) + return (ERR_NOEFFECT); + + switch (objs[id].obclass) { + case CLASS_FIXTURE: + pbase = &(objFixtures[osid].p1); + break; + case CLASS_TRAP: + pbase = &(objTraps[osid].p1); + break; + } + + if (pbase == NULL) + return (ERR_NOEFFECT); + + *(pbase + (num_param - 1)) = new_val; + + return (OK); +} + +errtype trigger_check_destroyed(ObjID id) { + errtype retval = OK; + if ((objs[id].obclass == CLASS_FIXTURE) && (objs[id].subclass == FIXTURE_SUBCLASS_CYBER)) + retval = trap_activate(id, trap_use_message); + return (retval); +} + +errtype location_trigger_activate(ObjID id) { + errtype retval = ERR_NOEFFECT; + + if (objs[id].info.type == ENTRY_TRIGGER_TYPE) + retval = trap_activate(id, trap_use_message); + if (objs[id].info.type == FLOOR_TRIGGER_TYPE) { + if (fix_from_obj_height(PLAYER_OBJ) < + (STANDARD_SIZE + (fix_from_map_height(me_height_flr(MAP_GET_XY(PLAYER_BIN_X, PLAYER_BIN_Y)))))) + retval = trap_activate(id, trap_use_message); + } + + return (retval); +} + +errtype trap_null_func(int p1, int p2, int p3, int p4) { return (OK); } + +#define MAX_BRIDGE_FRAME 32 + +errtype trap_transmogrify_func(int p1, int p2, int p3, int p4) { + int source, dest, t1, t2; + + if (!objs[p1].active) { + return (ERR_NOEFFECT); + } + t1 = p2 & 0xFFFF; + + // okay, so this is kind of a dopey way to do things, but it's backwards- + // compatible with our initial transmog spec. For FC, we should not do + // this ... hopefully following ifdef trick will make sure we change it. + +#ifdef FIRST_SHODAN_MUG + t2 = t1 ^ (p2 >> 16); +#else +#error "hey, look at me, I don't compile. Look at me! t2 = (p2 >> 16);" +#endif + + if (((t1 > t2) ? t1 : t2) >= num_types(objs[p1].obclass, objs[p1].subclass)) { + return (ERR_NOEFFECT); + } + source = ID2TRIP(p1); + if (objs[p1].info.type == t1) + objs[p1].info.type = t2; + else + objs[p1].info.type = t1; + dest = ID2TRIP(p1); + // if (p3 > 0) + { + switch (source) { + case NON_BRIDGE_TRIPLE: + if ((source == NON_BRIDGE_TRIPLE) && ((dest == FORCE_BRIJ_TRIPLE) || (dest == FORCE_BRIJ2_TRIPLE))) { + remove_obj_from_animlist(p1); + objs[p1].info.current_frame = MAX_BRIDGE_FRAME; + objBigstuffs[objs[p1].specID].cosmetic_value = MAX_BRIDGE_FRAME; + // counting backwards from zero + add_obj_to_animlist(p1, FALSE, TRUE, FALSE, 16, 0, 0, 0); + if (source == NON_BRIDGE_TRIPLE) { + //hack: on level 4 only allow one simultaneously playing force bridge sound + if (player_struct.level != 4 || !digi_fx_playing(SFX_FORCE_BRIDGE, NULL)) + play_digi_fx_obj(SFX_FORCE_BRIDGE, 1, p1); + } + } + break; +#ifdef TWO_WAY_TRANSMOGGING + case FORCE_BRIJ_TRIPLE: + case FORCE_BRIJ2_TRIPLE: + if (dest == NON_BRIDGE_TRIPLE) { + remove_obj_from_animlist(p1); + objs[p1].info.current_frame = 0; + objBigstuffs[objs[p1].specID].cosmetic_value = MAX_BRIDGE_FRAME; + add_obj_to_animlist(p1, FALSE, FALSE, FALSE, 16, 0, 0, 0); + } +#endif + } + } + slam_posture_meter_state(); + obj_physics_refresh_area(OBJ_LOC_BIN_X(objs[p1].loc), OBJ_LOC_BIN_Y(objs[p1].loc), FALSE); + return (OK); +} + +// Above this search size, just go through list of objCritters, otherwise +// look at all the objects +#define SEARCH_AREA_THRESHOLD 81 + +#define MIN_MONSTER_DISTANCE 7 +#define NOLOOK_MONSTER_DISTANCE 14 + +#define CONSERVATIVE_OCTANT_ARC 0x2500 + +// Okay, this is stupid and uses fix_atan but WTF, I wanted to +// write something really quickly for alpha. + +// cast to signed-ness before taking abs +#define fixang_abs(x) abs((short)(x)) + +uchar player_facing_square(LGPoint sq) { + fixang plrh, sqh, delta; + plrh = fixang_from_phys_angle(phys_angle_from_obj(objs[PLAYER_OBJ].loc.h)); + // reflect x,y around the line x=y to sneakily convert fixang in regular- + // guy coordinates to fixang in north-is-zero-and-clockwise coordinates + // so, anyway, I'm swapping x and y for a reason here. + sqh = fix_atan2(fix_make(sq.x - PLAYER_BIN_X, 0), fix_make(sq.y - PLAYER_BIN_Y, 0)); + delta = sqh - plrh; + if (fixang_abs(delta) < CONSERVATIVE_OCTANT_ARC) + return (TRUE); + // note that we're working in a left-handed, clockwise world + if (view360_active_contexts[LEFT_CONTEXT] && fixang_abs(delta + 0x4000) < CONSERVATIVE_OCTANT_ARC) + return (TRUE); + if (view360_active_contexts[RIGHT_CONTEXT] && fixang_abs(delta - 0x4000) < CONSERVATIVE_OCTANT_ARC) + return (TRUE); + if (view360_active_contexts[MID_CONTEXT] && fixang_abs(delta + 0x8000) < CONSERVATIVE_OCTANT_ARC) + return (TRUE); + return (FALSE); +} + +// p1 = triple +// p2 = area of effect +// p3 = quantity of object to create +errtype trap_monster_func(int p1, int p2, int p3, int p4) { + ObjID new_id, id1, id2; + ObjSpecID osid; + ObjRefID oref; + char minx, miny, sizex, sizey, quan, failures = 0, num_gen = 0; + LGPoint sq; + uchar okay = FALSE; + char monster_count; + MapElem *pme; + + TRACE("%s: trigger", __FUNCTION__); + + quan = qdata_get(p3); + switch (player_struct.difficulty[COMBAT_DIFF_INDEX]) { + case 0: + quan = 0; + break; + case 3: + quan++; + } + while (quan > 0) { + id1 = p2 & 0xFFFF; + id2 = p2 >> 16; + if (id2 != OBJ_NULL) { + minx = lg_min(OBJ_LOC_BIN_X(objs[id1].loc), OBJ_LOC_BIN_X(objs[id2].loc)); + miny = lg_min(OBJ_LOC_BIN_Y(objs[id1].loc), OBJ_LOC_BIN_Y(objs[id2].loc)); + sizex = lg_max(OBJ_LOC_BIN_X(objs[id1].loc), OBJ_LOC_BIN_X(objs[id2].loc)) - minx; + sizey = lg_max(OBJ_LOC_BIN_Y(objs[id1].loc), OBJ_LOC_BIN_Y(objs[id2].loc)) - miny; + } else { + sizex = sizey = id1 * 2; + minx = OBJ_LOC_BIN_X(objs[current_trap].loc) - sizex; + miny = OBJ_LOC_BIN_Y(objs[current_trap].loc) - sizey; + } + okay = FALSE; + monster_count = 0; + while (!okay && (monster_count < 100)) { + int tiletype, room; + + okay = TRUE; + sq.x = minx + rand() % (sizex + 1); + sq.y = miny + rand() % (sizey + 1); + pme = MAP_GET_XY(sq.x, sq.y); + + tiletype = me_tiletype(pme); + room = MAP_HEIGHTS - me_height_ceil(pme) - me_height_flr(pme); + + if (tiletype >= TILE_SLOPEUP_N && tiletype <= TILE_SLOPECV_SW) { + if (me_bits_mirror(pme) == MAP_FFLAT) + room -= me_param(pme); + else + room = 0; + } else if (tiletype != TILE_OPEN) + room = 0; + room = (room << MAP_ZSHF) / 8; + if (room >= 8) { + oref = me_objref(pme); + while (okay && (oref != OBJ_REF_NULL)) { + if (objs[objRefs[oref].obj].obclass == CLASS_CRITTER) { + okay = FALSE; + } + oref = objRefs[oref].next; + } + } else { + okay = FALSE; + break; + } + if (p4 & 0x8) + okay = TRUE; + if (okay) { + char d; + d = abs(PLAYER_BIN_X - sq.x) + abs(PLAYER_BIN_Y - sq.y); + + // This should be in order of harshness + if ((p4 & 0x2) && player_facing_square(sq) && (d < NOLOOK_MONSTER_DISTANCE)) { + okay = FALSE; + } + if ((p4 & 0x1) && (d < MIN_MONSTER_DISTANCE)) { + okay = FALSE; + } + if (!(p4 & 0x4) && (me_bits_music(pme) == ELEVATOR_ZONE)) { + okay = FALSE; + } + } + monster_count++; + } + if (okay) { + new_id = object_place(p1, sq); + if (new_id == OBJ_NULL) { + quan = 0; + } else { + num_gen++; + // Set some default instance data + osid = objs[new_id].specID; + switch (objs[new_id].obclass) { + case CLASS_CRITTER: + objCritters[osid].mood = AI_MOOD_NEUTRAL; + objCritters[osid].orders = AI_ORDERS_ROAM; + break; + } + quan--; + } + } else { + failures++; + if (failures > quan) + quan = 0; + } + } + if (num_gen > 0) + obj_load_art(FALSE); + return (OK); +} + +errtype do_ai_trap(ObjSpecID osid, int p1, int p3, int p4) { + ObjID oid; + oid = objCritters[osid].id; + if ((p1 < 0) || ((objs[oid].subclass == (p1 & 0xFF00) >> 8) && (objs[oid].info.type == (p1 & 0xFF)))) { + // Now look at the solo bit + if ((p1 == -1) || // we are already OK + (p1 & 0x20000) || // or we don't care + ((p1 & 0x10000) && + (objs[oid].info.inst_flags & CLASS_INST_FLAG)) || // or if we want only loners, and we're a loner + ((!(p1 & 0x10000)) && + (!(objs[oid].info.inst_flags & CLASS_INST_FLAG)))) // or we want only non-loners, and we're a non-loner + { + if ((p3 < 0x1000) && ((QUESTVAR_GET(COMBAT_DIFF_QVAR) > 0) || (qdata_get(p3) == AI_MOOD_FRIENDLY))) + objCritters[osid].mood = qdata_get(p3); + if (p4 < 0x1000) + objCritters[osid].orders = qdata_get(p4); + } + } + return (OK); +} + +errtype trap_ai_func(int p1, int p2, int p3, int p4) { + int x1, x2, y1, y2, i, j; + ObjSpecID osid; + ObjRefID oref; + ObjID oid, o1, o2; + + TRACE("%s: trigger", __FUNCTION__); + + if (p1 & 0x40000) { + if ((objs[p1 & 0xFFFF].active) && (objs[p1 & 0xFFFF].obclass == CLASS_CRITTER)) + do_ai_trap(objs[p1 & 0xFFFF].specID, -1, p3, p4); + return (OK); + } + if ((p2 & 0xFFFF) == 0) { + // Radius or total AOE + if ((p2 >> 16) == 0) { + x1 = 0; + y1 = 0; + x2 = global_fullmap->x_size; + y2 = global_fullmap->y_size; + } else { + x1 = OBJ_LOC_BIN_X(objs[current_trap].loc) - (p2 >> 16); + y1 = OBJ_LOC_BIN_Y(objs[current_trap].loc) - (p2 >> 16); + x2 = x1 + (2 * (p2 >> 16)); + y2 = y1 + (2 * (p2 >> 16)); + } + } else { + // Object-demarked rectangle AOE + o1 = p2 & 0xFFFF; + o2 = p2 >> 16; + if ((o1 == OBJ_NULL) || (o2 == OBJ_NULL)) { + return (ERR_NOEFFECT); + } else { + x1 = lg_min(OBJ_LOC_BIN_X(objs[o1].loc), OBJ_LOC_BIN_X(objs[o2].loc)); + x2 = lg_max(OBJ_LOC_BIN_X(objs[o1].loc), OBJ_LOC_BIN_X(objs[o2].loc)); + y1 = lg_min(OBJ_LOC_BIN_Y(objs[o1].loc), OBJ_LOC_BIN_Y(objs[o2].loc)); + y2 = lg_max(OBJ_LOC_BIN_Y(objs[o1].loc), OBJ_LOC_BIN_Y(objs[o2].loc)); + } + } + + // If the area to search is greater than a certain threshold, use + // one search method, otherwise use another. + if ((x2 - x1) * (y2 - y1) > SEARCH_AREA_THRESHOLD) { + osid = objCritters[0].id; + while (osid != OBJ_SPEC_NULL) { + oid = objCritters[osid].id; + if ((OBJ_LOC_BIN_X(objs[oid].loc) >= x1) && (OBJ_LOC_BIN_X(objs[oid].loc) <= x2) && + (OBJ_LOC_BIN_Y(objs[oid].loc) >= y1) && (OBJ_LOC_BIN_Y(objs[oid].loc) <= y2)) { + do_ai_trap(osid, p1, p3, p4); + } + osid = objCritters[osid].next; + } + } else { + for (j = y1; j <= y2; j++) { + for (i = x1; i <= x2; i++) { + oref = me_objref(MAP_GET_XY(i, j)); + while (oref != OBJ_REF_NULL) { + // Make sure we only do this to the "true" ref + // on each obj + oid = objRefs[oref].obj; + if ((objs[oid].ref == oref) && (objs[oid].obclass == CLASS_CRITTER)) { + if (p1 & 0x80000) + do_ai_trap(objs[oid].specID, -2, p3, p4); + else + do_ai_trap(objs[oid].specID, p1, p3, p4); + } + oref = objRefs[oref].next; + } + } + } + } + return (OK); +} + +#define TRAP_TIME_UNIT 10 // how many time-setting units in a second + +errtype trap_scheduler_func(int p1, int p2, int p3, int p4) { + TrapSchedEvent new_event; + uint *p; + + TRACE("%s: trigger", __FUNCTION__); + + if ((p3 >= 0xFFFF) || ((p3 > 0x1000) && (QUESTBIT_GET(p3 & 0xFFF))) || ((p3 < 0x1000) && (p3 > 0))) { + switch (objs[current_trap].obclass) { + case CLASS_TRAP: + p = &(objTraps[objs[current_trap].specID].p3); + case CLASS_FIXTURE: + p = &(objFixtures[objs[current_trap].specID].p3); + } + if ((p3 < 0x1000) && (p3 > 0)) + (*p)--; + new_event.timestamp = + TICKS2TSTAMP(player_struct.game_time + (CIT_CYCLE * (ushort)qdata_get(p2)) / TRAP_TIME_UNIT) + 1; + if (qdata_get(p4) != 0) + new_event.timestamp += rand() % qdata_get(p4); + new_event.type = TRAP_SCHED_EVENT; + new_event.target_id = qdata_get(p1); + new_event.source_id = current_trap; + return (schedule_event(&(global_fullmap->sched[MAP_SCHEDULE_GAMETIME]), (SchedEvent *)&new_event)); + } + return (OK); +} + +errtype trap_alternating_splitter_func(int p1, int p2, int p3, int p4) { + uchar found_new = FALSE; + char loop_count = 0; + int n = p4; + ObjID tr = current_trap; + + while (!found_new) { + switch (n) { + case 0: + do_timed_multi_stuff(qdata_get(p1)); + found_new = TRUE; + break; + case 1: + do_timed_multi_stuff(qdata_get(p2)); + found_new = TRUE; + break; + case 2: + do_timed_multi_stuff(qdata_get(p3)); + found_new = TRUE; + break; + } + n += 1; + set_trap_data(tr, 4, n); + if ((n > 2) || ((n == 2) && (p3 == 0))) { + set_trap_data(tr, 4, 0); + } + loop_count++; + if (loop_count > 10) { + found_new = TRUE; + } + } + return (OK); +} + +errtype trap_lighting_func(uchar floor, int p1, int p2, int p3, int p4); + +// NUM_LIGHT_STEPS * LIGHT_TICKS should equal the total time of a transition, in this case .5 seconds +#define NUM_LIGHT_STEPS 8 +#define LIGHT_TICKS (CIT_CYCLE >> 4) + +errtype trap_main_light_func(int p1, int p2, int p3, int p4) { + uint *p; + if ((p3 & 0x10000) || (p3 & 0x20000)) + trap_lighting_func(FALSE, p1, p2, p3 & 0xffff, p4); + if (!(p3 & 0x10000)) + trap_lighting_func(TRUE, p1, p2, p3 & 0xffff, p4); + + TRACE("%s: trigger", __FUNCTION__); + + // Do transition rescheduling & incrementing + if (p2 & 0xFFFF) { + TrapSchedEvent new_event; + int num_steps = (p2 & 0xFFF0000) >> 16; + switch (objs[current_trap].obclass) { + case CLASS_TRAP: + p = &(objTraps[objs[current_trap].specID].p2); + break; + case CLASS_FIXTURE: + p = &(objFixtures[objs[current_trap].specID].p2); + break; + } + *p &= 0xF000FFFF; + if (num_steps < NUM_LIGHT_STEPS) { + // Now increment & re-schedule + num_steps++; + *p |= (num_steps << 16); + new_event.timestamp = TICKS2TSTAMP(player_struct.game_time + LIGHT_TICKS) + 1; + new_event.type = TRAP_SCHED_EVENT; + new_event.target_id = OBJ_NULL; + new_event.source_id = current_trap; + schedule_event(&(global_fullmap->sched[MAP_SCHEDULE_GAMETIME]), (SchedEvent *)&new_event); + } + } + return (OK); +} + +#define MAX_LIGHT_VAL 15 + +#define LIGHT_PLAIN_ALT 0 +#define LIGHT_EW_SMOOTH 1 +#define LIGHT_NS_SMOOTH 2 +#define LIGHT_RADIAL 3 + +errtype trap_lighting_func(uchar floor, int p1, int p2, int p3, int p4) { + int i, j; + int targ1, targ2, setme, delta; + int otarg1, otarg2; + int x1, x2, y1, y2; + fix rad_delt, rdx, rdy; + short num_steps = -1; + char trans_type; + ObjID o1, o2; + MapElem *pme; + char v[4]; + char light_state; + uint *p; + + TRACE("%s: trigger", __FUNCTION__); + + o1 = qdata_get(p1 & 0xFFFF); + o2 = qdata_get(p1 >> 16); + + // p2 will eventually be used for lighting transition types (interp or area staggered) + // although, the high bits of it are used for maintaining state. + + trans_type = p2 & 0xFFFF; + if (trans_type != 0) { + num_steps = (p2 & 0xFFF0000) >> 16; + } + + if (p3 == LIGHT_RADIAL) { + // compute the bounding box of the radius + x1 = OBJ_LOC_BIN_X(objs[current_trap].loc) - o1; + x2 = x1 + (2 * o1); + y1 = OBJ_LOC_BIN_Y(objs[current_trap].loc) - o1; + y2 = y1 + (2 * o1); + } else { + if ((o1 == OBJ_NULL) || (o2 == OBJ_NULL) || (!objs[o1].active) || (!objs[o2].active)) { + return (ERR_NOEFFECT); + } + + x1 = lg_min(OBJ_LOC_BIN_X(objs[o1].loc), OBJ_LOC_BIN_X(objs[o2].loc)); + x2 = lg_max(OBJ_LOC_BIN_X(objs[o1].loc), OBJ_LOC_BIN_X(objs[o2].loc)); + y1 = lg_min(OBJ_LOC_BIN_Y(objs[o1].loc), OBJ_LOC_BIN_Y(objs[o2].loc)); + y2 = lg_max(OBJ_LOC_BIN_Y(objs[o1].loc), OBJ_LOC_BIN_Y(objs[o2].loc)); + } + + v[0] = p4 & 0xFF; + v[1] = (p4 & 0xFF00) >> 8; + v[2] = (p4 & 0xFF0000) >> 16; + v[3] = p4 >> 24; + // radial light doesn't actually necessarily set any of its neighboring + // points to its lighting values ... lighting from trap may fall from + // an illegal value into the legal range due to distance from trap to + // floor vertex. + if (p3 != LIGHT_RADIAL) { + for (i = 0; i < 4; i++) { + if (v[i] > MAX_LIGHT_VAL) { + return (ERR_RANGE); + } + } + } + + // Compare against it and figure out which set of lighting + // values to use. Plain lighting cares about 0 vs 1, everyone + // else is 0 & 1 vs 2 & 3. + light_state = p2 >> 28; + if (p3 == LIGHT_PLAIN_ALT) { + if (light_state) { + targ1 = v[0]; + otarg1 = v[1]; + } else { + targ1 = v[1]; + otarg1 = v[0]; + } + } else { + if (light_state) { + targ1 = v[0]; + otarg1 = v[2]; + targ2 = v[1]; + otarg2 = v[3]; + } else { + targ1 = v[2]; + otarg1 = v[0]; + targ2 = v[3]; + otarg2 = v[1]; + } + } + + // Toggle state if done transiting (or not transiting) + switch (objs[current_trap].obclass) { + case CLASS_TRAP: + p = &(objTraps[objs[current_trap].specID].p2); + break; + case CLASS_FIXTURE: + p = &(objFixtures[objs[current_trap].specID].p2); + break; + } + if ((num_steps == -1) || (num_steps == NUM_LIGHT_STEPS)) { + if (!light_state) + *p |= 0x10000000; // turn on state + else + *p &= 0xFFFFFFF; // turn off highest bits + } else // otherwise, tone down the destination appropriately. + { + targ1 -= ((targ1 - otarg1) * (NUM_LIGHT_STEPS - num_steps) / NUM_LIGHT_STEPS); + if (p3 != LIGHT_PLAIN_ALT) + targ2 -= ((targ2 - otarg2) * (NUM_LIGHT_STEPS - num_steps) / NUM_LIGHT_STEPS); + } + + // Now go and crank through it... + switch (p3) { + case LIGHT_PLAIN_ALT: + delta = 0; + break; + case LIGHT_EW_SMOOTH: + delta = (targ2 - targ1) / (x2 - x1); + break; + case LIGHT_NS_SMOOTH: + delta = (targ2 - targ1) / (y2 - y1); + break; + } +#ifdef OLD_LIGHT + setme = targ1 - otarg1; +#endif + setme = targ1; + for (j = y1; j <= y2; j++) { + for (i = x1; i <= x2; i++) { + pme = MAP_GET_XY(i, j); + // This code now does lighting deltas + // Note, however, that it is still confused by light values getting "pegged" + +#define FIX_HALF (FIX_UNIT >> 1) + if (p3 == LIGHT_RADIAL) { + rdx = fix_make(i, 0) - (objs[current_trap].loc.x << 8); + rdy = fix_make(j, 0) - (objs[current_trap].loc.y << 8); + rad_delt = fix_fast_pyth_dist(rdx, rdy); + if (rad_delt <= fix_make(o1, 0)) + setme = targ1 + fix_int((rad_delt / o1) * (targ2 - targ1)); + else + setme = me_templight_flr(pme); + if (setme > MAX_LIGHT_VAL) + setme = MAX_LIGHT_VAL; + } + + if (floor) { +#ifdef OLD_LIGHT + // if ((setme + me_templight_flr(pme) > 0xF) || (me_templight_flr(pme) - setme < 0)) + // Spew(DSRC_GAMESYS_Traps, ("pegged lights at 0x%x, 0x%x -- %d + %d = %d\n", + // i,j,setme,me_templight_flr(pme),setme+me_templight_flr(pme))); + new_val = me_templight_flr(pme) + setme; + if (newval > 0xF) + newval = 0xF; + else if (newval < 0) + newval = 0; + me_templight_flr_set(pme, newval); +#endif + me_templight_flr_set(pme, setme); + } else { +#ifdef OLD_LIGHT + // if ((setme + me_templight_ceil(pme) > 0xF) || (me_templight_ceil(pme) - setme < 0)) + // Spew(DSRC_GAMESYS_Traps, ("pegged lights at 0x%x, 0x%x -- %d + %d = %d\n", + // i,j,setme,me_templight_ceil(pme),setme+me_templight_ceil(pme))); + new_val = me_templight_ceil(pme) + setme; + if (newval > 0xF) + newval = 0xF; + else if (newval < 0) + newval = 0; + me_templight_ceil_set(pme, newval); +#endif + me_templight_ceil_set(pme, setme); + } + if (p3 == LIGHT_EW_SMOOTH) { + if (i == x2 - 1) + setme = targ2; + else + setme += delta; + } + } + + // Now set values for next time around + switch (p3) { + case LIGHT_EW_SMOOTH: + setme = targ1; + break; + case LIGHT_NS_SMOOTH: + if (j == y2 - 1) + setme = targ2; + else + setme += delta; + break; + } + } + + return (OK); +} + +errtype trap_damage_func(int p1, int p2, int p3, int p4) { + short dval; + + // Can't be inverted! + dval = qdata_get(p1); + if (dval > 0) + damage_object(PLAYER_OBJ, EXPLOSION_TYPE, dval, 0); + + dval = qdata_get(p2 & 0xFFFF); + if (!(p2 & 0x10000)) + damage_object(PLAYER_OBJ, dval, p2 >> 24, 0x01); + else + player_struct.hit_points = lg_min((short)player_struct.hit_points + dval, PLAYER_MAX_HP); + + dval = qdata_get(p3 & 0xFFFF); + if (p3 < 0x10000) + player_struct.energy -= dval; + else + player_struct.energy += dval; + + dval = qdata_get(p4 & 0xFFFF); + if (p4 < 0x10000) + player_struct.fatigue += dval; + else + player_struct.fatigue -= dval; + + chg_set_flg(VITALS_UPDATE); + return (OK); +} + +uchar fake_endgame = FALSE; +#define ENDGAME_TICKS CIT_CYCLE * 2 + +errtype trap_sfx_func(int p1, int p2, int p3, int p4) { + extern short fr_solidfr_time; + extern short fr_sfx_time; + extern short fr_surge_time; + extern char surg_fx_frame; + short scr_fx, sfx_time, sound_fx; + short wacky, wacky_sev; + extern short surge_duration; + extern ulong player_death_time; + + sound_fx = qdata_get(p1 & 0xFFFF); + scr_fx = qdata_get(p3); + sfx_time = qdata_get(p4); + wacky = qdata_get(p2 & 0xFFFF); + wacky_sev = qdata_get(p2 >> 16); + + if (sound_fx) + play_digi_fx(sound_fx, qdata_get(p1 >> 16)); + + switch (wacky) { + // Power Surge effect + case 1: + if (fr_surge_time == 0) { + fr_surge_time = surge_duration; + surg_fx_frame = 0; + play_digi_fx(SFX_SURGE, 1); + } + break; + + // Shake that booty (or head) + case 2: + // if (!sound_fx) + play_digi_fx(SFX_RUMBLE, 2); + fr_global_mod_flag(FR_SFX_SHAKE, FR_SFX_MASK); + fr_sfx_time = CIT_CYCLE * 4; // 4 seconds of shake + break; + + // Fake endgame + case 3: { + extern ulong secret_sfx_time; + physics_zero_all_controls(); + secret_render_fx = FAKEWIN_REND_SFX; + secret_sfx_time = *tmd_ticks; + fr_surge_time = surge_duration; + chg_set_sta(GL_CHG_2); + } break; + + // Teleport special effect + case 4: + fr_global_mod_flag(FR_SFX_TELEPORT, FR_SFX_MASK); + fr_sfx_time = CIT_CYCLE; // 1 second of teleport effect + break; + + case 5: { + // let's do some damage static! + set_dmg_percentage(DMG_BLOOD, 100); // 100 is the amount of static (100/255) is the percent of static + } break; + } + switch (scr_fx) { + case 1: + fr_global_mod_flag(FR_SOLIDFR_SLDCLR, FR_SOLIDFR_MASK); + fr_solidfr_color = GRENADE_COLOR; + break; + case 2: + fr_global_mod_flag(FR_SOLIDFR_SLDCLR, FR_SOLIDFR_MASK); + fr_solidfr_color = RED_BASE; + break; + case 3: + fr_global_mod_flag(FR_SOLIDFR_STATIC, FR_SOLIDFR_MASK); + break; + case 4: { + extern short vhold_shift; + fr_global_mod_flag(FR_SFX_VHOLD, FR_SFX_MASK); + vhold_shift = 0; + break; + } + } + if (scr_fx) { + if (scr_fx >= 4) + fr_sfx_time = sfx_time; + else + fr_solidfr_time = sfx_time; + } + return (OK); +} + +errtype trap_create_obj_func(int p1, int p2, int p3, int p4) { + ObjID new_id, oid; + ObjLoc new_loc; + + TRACE("%s: trigger", __FUNCTION__); + + if ((p1 & 0xFFFF) == OBJ_NULL) { + return (ERR_NOEFFECT); + } + + // use questvar if asked for + oid = qdata_get(p1 & 0xFFFF); + + if (!objs[oid].active) { + return (ERR_NOEFFECT); + } + + // We are okay to go, so clone the darned thing + if ((p1 > 0xFFFF) && !(p1 & 0x10000000)) + new_id = oid; + else { + new_id = obj_create_clone(oid); + + // set the QV, if appropriate + qdata_set(p1 >> 16, new_id); + } + + // Now move it to where the trap says + new_loc = objs[oid].loc; + if (p2 < 0x4000) { + p2 = qdata_get(p2); + new_loc.x = (p2 << 8) | (new_loc.x & 0xFF); + } + if (p3 < 0x4000) { + p3 = qdata_get(p3); + new_loc.y = (p3 << 8) | (new_loc.y & 0xFF); + } + if (p4 < 0x4000) { + new_loc.z = qdata_get(p4); + } + obj_move_to(new_id, &new_loc, TRUE); + obj_physics_refresh_area(OBJ_LOC_BIN_X(new_loc), OBJ_LOC_BIN_Y(new_loc), TRUE); + return (OK); +} + +errtype trap_questbit_func(int p1, int p2, int p3, int p4) { + char message_buf[100]; + short qarg, mod; + qarg = p2 & 0xFFFF; + mod = p2 >> 16; + + TRACE("%s: trigger", __FUNCTION__); + + if ((p1 & 0xF000) == 0) + p1 |= 0x2000; + + if (p1 == 0x2091) // KLC - special hack for auto shutoff of on-line help. + { + olh_active = FALSE; // KLC - this is kept in a global now. + + gShockPrefs.goOnScreenHelp = FALSE; // Yeah, got to update this one too and + SavePrefs(); // save the prefs out to disk. + return (OK); + } + + if (p1 & 0x2000) { + switch (qarg) { + case 0: + qdata_set(p1, FALSE); + break; + case 1: + qdata_set(p1, TRUE); + break; + default: + if (qdata_get(p1)) + qdata_set(p1, FALSE); + else + qdata_set(p1, TRUE); + break; + } + if (((p1 & 0xFFF) == REACTOR_BOOM_QB) && (qdata_get(p1))) + do_special_reactor_hack(); + } else { + switch (mod) { + case 0: + qdata_set(p1, qdata_get(qarg)); + break; + case 1: + qdata_set(p1, qdata_get(p1) + qdata_get(qarg)); + break; + case 2: + qdata_set(p1, qdata_get(p1) - qdata_get(qarg)); + break; + case 3: + qdata_set(p1, qdata_get(p1) * qdata_get(qarg)); + break; + case 4: + qdata_set(p1, qdata_get(p1) / qdata_get(qarg)); + break; + case 5: + qdata_set(p1, qdata_get(p1) % qdata_get(qarg)); + break; + } + } + if (qdata_get(p1)) { + if (qdata_get(p3)) { + message_info(get_string(REF_STR_TrapZeroMessage + qdata_get(p3), message_buf, 80)); +#ifdef AUDIOLOGS + audiolog_bark_play(qdata_get(p3)); +#endif + *trap_use_message = TRUE; + } + } else { + if (qdata_get(p4)) { + message_info(get_string(REF_STR_TrapZeroMessage + qdata_get(p4), message_buf, 80)); +#ifdef AUDIOLOGS + audiolog_bark_play(qdata_get(p4)); +#endif + *trap_use_message = TRUE; + } + } + + return (OK); +} + +extern uchar alternate_death; + +errtype trap_cutscene_func(int p1, int p2, int p3, int p4) { + short cs = qdata_get(p1); + + INFO("Playing cutscene %i %i\n", cs, qdata_get(p2)); + + //if (qdata_get(p1) == 0) // KLC - if we are to play the endgame cutscene + //{ + //gGameCompletedQuit = TRUE; + + //gPlayingGame = FALSE; // Hop out of the game loop. + // KLC play_cutscene(qdata_get(p1), qdata_get(p2)); + + play_cutscene(WIN_CUTSCENE, TRUE); + setup_mode = SETUP_CREDITS; + extern int WonGame_ShowStats; + WonGame_ShowStats = 1; + //} + + alternate_death = (qdata_get(p2) != 0); + return (OK); +} + +errtype trap_terrain_func(int p1, int p2, int p3, int p4) { + TRACE("%s: trigger", __FUNCTION__); + MapElem *pme; + uchar reprocess = FALSE; + LGRect bounds; + + pme = MAP_GET_XY(qdata_get(p1), qdata_get(p2)); + if (pme == NULL) + return (ERR_NOEFFECT); + if (p3 < 0x4000) { + me_tiletype_set(pme, qdata_get(p3)); + reprocess = TRUE; + } + if (p4 < 0x4000) { + me_param_set(pme, qdata_get(p4)); + reprocess = TRUE; + } + if (reprocess) { + bounds.ul.x = bounds.lr.x = qdata_get(p1); + bounds.ul.y = bounds.lr.y = qdata_get(p2); + rendedit_process_tilemap(global_fullmap, &bounds, FALSE); + } + obj_physics_refresh_area(qdata_get(p1), qdata_get(p2), TRUE); + return (OK); +} + +errtype trap_height_func(int p1, int p2, int p3, int p4) { + TRACE("%s: trigger", __FUNCTION__); + MapElem *pme; + HeightSchedEvent hse; + ushort use_val; + uchar x, y; + char steps; + char ht; + uchar did_sfx = FALSE; + + x = qdata_get(p1); + y = qdata_get(p2); + pme = MAP_GET_XY(x, y); + + hse.timestamp = TICKS2TSTAMP(player_struct.game_time + (CIT_CYCLE * HEIGHT_STEP_TIME) / HEIGHT_TIME_UNIT) + 1; + use_val = qdata_get(p3 & 0xFFFF); + if (use_val < 0x100) { + if (me_height_flr(pme) != use_val) { + hse.type = FLOOR_SCHED_EVENT; + + steps = use_val - me_height_flr(pme); + hse.steps_remaining = steps; + hse.sfx_code = 0; + if (p4 >> 16) + did_sfx = TRUE; + if (register_h_event(x, y, TRUE, &hse.semaphor, &hse.key, p4 >> 16)) { + schedule_event(&(global_fullmap->sched[MAP_SCHEDULE_GAMETIME]), (SchedEvent *)&hse); + } + } + } + use_val = qdata_get(p3 >> 16); + if (use_val < 0x100) { + // convert to make life easier on Erik + use_val = 32 - use_val; + + if (me_height_ceil(pme) != use_val) { + hse.type = CEIL_SCHED_EVENT; + ht = me_height_ceil(pme); + steps = use_val - ht; + hse.steps_remaining = steps; + hse.sfx_code = p4 >> 16; + if (register_h_event(x, y, FALSE, &hse.semaphor, &hse.key, (did_sfx) ? FALSE : (p4 >> 16))) { + schedule_event(&(global_fullmap->sched[MAP_SCHEDULE_GAMETIME]), (SchedEvent *)&hse); + } + } + } + obj_physics_refresh_area(x, y, TRUE); + return (OK); +} + +errtype real_instance_func(int p1, int p2, int p3, int p4) { + ObjSpecID osid; + if (p1 == 0) + return (OK); + if (!objs[p1].active) { + return (ERR_NOEFFECT); + } + osid = objs[p1].specID; + switch (objs[p1].obclass) { + case CLASS_BIGSTUFF: + if (p2 != -1) + objBigstuffs[osid].cosmetic_value = p2; + if (p3 != -1) + objBigstuffs[osid].data1 = p3; + if (p4 != -1) + objBigstuffs[osid].data2 = p4; + break; + case CLASS_DOOR: + if (p2 != -1) + objDoors[osid].locked = p2; + if (p3 != -1) { + objDoors[osid].stringnum = p3 >> 8; + objDoors[osid].cosmetic_value = p3 & 0xFF; + } + if (p4 != -1) { + objDoors[osid].access_level = p4 >> 8; + objDoors[osid].autoclose_time = p4 & 0xFF; + } + break; + case CLASS_FIXTURE: + case CLASS_TRAP: + set_trap_data(p1, p2, p3); + break; + } + return (OK); +} + +errtype trap_instance_func(int p1, int p2, int p3, int p4) { + real_instance_func(qdata_get(p1 & 0xFFFF), p2, p3, p4); + real_instance_func(qdata_get(p1 >> 16), p2, p3, p4); + return (OK); +} + +void animate_callback_func(ObjID id, intptr_t user_data) { + int p3; + + p3 = (int)user_data; + + if (BIT_SET(p3, 17)) + do_multi_stuff(p3 & 0x7FFF); + else + real_animate_func(id, 0, p3, 0); +} + +errtype real_animate_func(ObjID id, int p2, int p3, int p4) { + errtype retval = OK; + int frames; + uchar reverse; + + if (id == 0) + return (OK); + if (!objs[id].active) { + return (ERR_NOEFFECT); + } + + // Don't allow animation updates of "destroyed" screens + if (objs[id].info.current_hp == 0) + return (OK); + + frames = objBigstuffs[objs[id].specID].cosmetic_value; + if (frames == 0) + frames = 4; + + if (p4 != 0) + remove_obj_from_animlist(id); + if (p2 == 0) { + reverse = BIT_SET(p2, 15); + if (p3 & 0xF0000000) + frames = p3 >> 28; + real_instance_func(id, frames, -1, p3 & 0x7FFF); + remove_obj_from_animlist(id); + objs[id].info.current_frame = reverse ? frames - 1 : 0; + add_obj_to_animlist(id, TRUE, BIT_SET(p3, 15), BIT_SET(p3, 16), 0, 0, 0, 0); + } else { + reverse = BIT_SET(p2, 15); + if (p2 & 0xF0000000) + frames = p2 >> 28; + real_instance_func(id, frames, -1, p2 & 0x7FFF); + objs[id].info.current_frame = reverse ? frames - 1 : 0; + if (p3 != 0) + retval = add_obj_to_animlist(id, FALSE, BIT_SET(p2, 15), BIT_SET(p2, 16), 0, 6, p3, ANIMCB_REMOVE); + else + retval = add_obj_to_animlist(id, FALSE, BIT_SET(p2, 15), BIT_SET(p2, 16), 0, 0, 0, 0); + } + return (retval); +} + +errtype trap_animate_func(int p1, int p2, int p3, int p4) { + real_animate_func(qdata_get(p1 & 0xFFFF), p2, p3, p4); + real_animate_func(qdata_get(p1 >> 16), p2, p3, p4); + return (OK); +} + +// Note that this blows away the usual shodan time countdown, +// so can only be used in endgame! +uchar *shodan_bitmask = NULL; +grs_bitmap shodan_draw_fs; +grs_bitmap shodan_draw_normal; + +void hack_shodan_conquer_func(char c) { + extern char thresh_fail; + shodan_bitmask = tmap_static_mem; + LG_memset(shodan_bitmask, 0, SHODAN_BITMASK_SIZE / 8); + shodan_draw_fs.bits = tmap_static_mem + (SHODAN_BITMASK_SIZE / 8); + shodan_draw_normal.bits = shodan_draw_fs.bits + (320 * 200); + load_res_bitmap(&shodan_draw_fs, SHODAN_FULLSCRN_CONQUER_REF, FALSE); + load_res_bitmap(&shodan_draw_normal, SHODAN_CONQUER_REF, FALSE); + thresh_fail = 0; + time_until_shodan_avatar = player_struct.game_time + SHODAN_INTERVAL; + + begin_shodan_conquer_fx(TRUE); +} + +void hack_armageddon_func(int otrip, int x0, int y0, int r) { + int ulx, uly, lrx, lry; + int i, j; + ObjRefID oref; + ObjID oid; + + ulx = x0 - r; + uly = y0 - r; + lrx = x0 + r; + lry = y0 + r; + + for (j = uly; j <= lry; j++) { + for (i = ulx; i <= lrx; i++) { + oref = me_objref(MAP_GET_XY(i, j)); + while (oref != OBJ_REF_NULL) { + // Make sure we only do this to the "true" ref + // on each obj + oid = objRefs[oref].obj; + if ((objs[oid].ref == oref) && (objs[oid].obclass == CLASS_CRITTER)) { + if (ID2TRIP(oid) == otrip) + ADD_DESTROYED_OBJECT(oid); + } + oref = objRefs[oref].next; + } + } + } +} + +void hack_area_spew(int p2, int p3, int p4) { + int ulx, uly, lrx, lry; + int x, y; + MapElem *pme; + uchar state; + ObjID obj; + + ulx = lg_min(OBJ_LOC_BIN_X(objs[current_trap].loc), OBJ_LOC_BIN_X(objs[(ObjID)p2].loc)); + lrx = lg_max(OBJ_LOC_BIN_X(objs[current_trap].loc), OBJ_LOC_BIN_X(objs[(ObjID)p2].loc)); + uly = lg_min(OBJ_LOC_BIN_Y(objs[current_trap].loc), OBJ_LOC_BIN_Y(objs[(ObjID)p2].loc)); + lry = lg_max(OBJ_LOC_BIN_Y(objs[current_trap].loc), OBJ_LOC_BIN_Y(objs[(ObjID)p2].loc)); + + obj = (ObjID)(p3 & 0xFFFF); + if (obj == OBJ_NULL) + state = 0; + else + state = (objs[obj].info.current_frame == 0); + state = state ^ ((p3 >> 16) != 0); + + for (x = ulx; x <= lrx; x++) { + for (y = uly; y <= lry; y++) { + pme = MAP_GET_XY(x, y); + switch (p4) { + case 0: + me_hazard_rad_set(pme, state); + break; + } + } + } +} + +void hack_multi_trans(int trip, int newtype) { + ObjSpecID osid; + ObjID oid; + + osid = objCritters[0].id; + while (osid != OBJ_SPEC_NULL) { + oid = objCritters[osid].id; + if (ID2TRIP(oid) == trip) { + if (newtype > 0xF) + do_destroy(oid); + else + trap_transmogrify_func(oid, newtype, 0, 0); + } + osid = objCritters[osid].next; + } +} + +void hack_change_comparator(int p2, int p3) { + ObjSpecID osid; + + osid = objs[p2].specID; + switch (objs[p2].obclass) { + case CLASS_TRAP: + objTraps[osid].comparator = p3; + return; + case CLASS_FIXTURE: + objFixtures[osid].comparator = p3; + return; + default: + return; + } +} + +// Wow. Pretty non-general here. +// Diego taunts the player. +#define FIXANG_OCT (FIXANG_PI / 4) +void hack_taunt_diego(int p2, int p3) { + fixang plrh; + + // triggers trap p3 if and only if player's facing is within + // an octant either way of fixang p2. + plrh = fixang_from_phys_angle(phys_angle_from_obj(objs[PLAYER_OBJ].loc.h)); + plrh -= p2; + if ((plrh < FIXANG_OCT) || (plrh >= ((fixang)-FIXANG_OCT))) + do_multi_stuff(p3); +} + +#define REPULSOR_TOGGLE_HACK 0x1 +#define REPULSOR_UP 0 +#define REPULSOR_DOWN 1 + +#define REACTOR_DIGIT_HACK 0x2 +#define REACTOR_COMBO_QVAR 0x1f +#define SCREEN_DIGIT_0 (REFINDEX(REF_STR_ScreenZero + 0x34)) + +#define REACTOR_KEYPAD_HACK 0x3 + +#define FIXTURE_FRAME_HACK 0x4 + +#define DOOR_HACK 0x5 + +#define GAME_OVER_HACK 0x6 + +#define TURN_OBJECT_HACK 0x7 + +#define ARMAGEDDON_HACK 0x8 + +#define SHODAN_CONQUER_HACK 0x9 + +#define COMPARATOR_HACK 0xA + +#define PLOTWARE_HACK 0xB + +#define AREASPEW_HACK 0xC + +#define DIEGO_HACK 0xD + +#define PANEL_REF_HACK 0xE + +#define EARTH_DESTROYED_HACK 0xF + +#define MULTI_TRANSMOG_HACK 0x10 + +errtype trap_hack_func(int p1, int p2, int p3, int p4) { + void plotware_showpage(uchar page); + void email_slam_hack(short which); + + TRACE("%s: trigger", __FUNCTION__); + + // As we need hacks in the game, just add new + // cases here to do your particular hack. + switch (p1) { + case REPULSOR_TOGGLE_HACK: + if (ID2TRIP(p2) == REPULSOR_TRIPLE) { + Obj *repul = &objs[p2]; + ObjRefID oref; + ObjID oid; + uint *upness = &(objTraps[repul->specID].p4); + int nu_tmap, old_tmap, nu_frame; + MapElem *pme; + ObjLoc where = repul->loc; + + switch (p4) { + case 0: + *upness = (*upness == REPULSOR_UP) ? REPULSOR_DOWN : REPULSOR_UP; + break; + case 1: + *upness = REPULSOR_UP; + break; + case 2: + *upness = REPULSOR_DOWN; + break; + } + + if (*upness == REPULSOR_UP) { + nu_tmap = (p3 >> 8) & 0x1F; + old_tmap = p3 & 0x1F; + nu_frame = 0; + } else { + old_tmap = (p3 >> 8) & 0x1F; + nu_tmap = p3 & 0x1F; + nu_frame = 1; + } + + pme = MAP_GET_XY(OBJ_LOC_BIN_X(where), OBJ_LOC_BIN_Y(where)); + if (nu_tmap != old_tmap) { + if (me_tmap_flr(pme) == old_tmap) + me_tmap_flr_set(pme, nu_tmap); + if (me_tmap_ceil(pme) == old_tmap) + me_tmap_ceil_set(pme, nu_tmap); + } + oref = me_objref(pme); + while (oref != OBJ_REF_NULL) { + oid = objRefs[oref].obj; + if ((objs[oid].ref == oref) && ID2TRIP(oid) == REPULSWALL_TRIPLE) + objs[oid].info.current_frame = nu_frame; + oref = objRefs[oref].next; + } + obj_physics_refresh(OBJ_LOC_BIN_X(where), OBJ_LOC_BIN_Y(where), FALSE); + } + break; + case REACTOR_DIGIT_HACK: + if (objs[p2].obclass == CLASS_BIGSTUFF) { + uint combo; + + combo = (QUESTVAR_GET(REACTOR_COMBO_QVAR) << 12) | (QUESTVAR_GET(REACTOR_COMBO_QVAR + 1)); + + objBigstuffs[ID2SPEC(p2)].data2 = 0x100 | (SCREEN_DIGIT_0 + (0xF & (combo >> (4 * (6 - p3))))); + } + break; + case REACTOR_KEYPAD_HACK: + if (objs[p2].obclass == CLASS_FIXTURE) { + uint *field = &(objFixtures[ID2SPEC(p2)].p1); + + field += (p3 - 1); + *field = (*field & ~0xFFFF) | QUESTVAR_GET(p4); + } + break; + case FIXTURE_FRAME_HACK: + objs[p2].info.current_frame = p3; + if (p4) + mfd_notify_func(MFD_FIXTURE_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, TRUE); + break; + case DOOR_HACK: + if (objs[p2].obclass == CLASS_DOOR) { + + uchar closed; + + // p3 indicates operation on door: + // 0=null; 1=open; 2=close; 3=toggle; 4=disable autoclose + + if (p3 < 4) { + closed = door_moving(p2, TRUE) || DOOR_REALLY_CLOSED(p2); + + if (p3 && ((p3 == 3) || ((p3 == 1) == closed))) + do_multi_stuff(p2); + } else { + objDoors[objs[p2].specID].autoclose_time = NEVER_AUTOCLOSE_COOKIE; + remove_obj_from_animlist(p2); + } + } + break; + case GAME_OVER_HACK: { + extern int curr_alog; + extern char secret_pending_hack; + if (curr_alog != -1) + secret_pending_hack = 1; + else { + + INFO("GAME OVER!\n"); + play_cutscene(DEATH_CUTSCENE, FALSE); + } + break; + } + case TURN_OBJECT_HACK: { + short head, lo, hi, phb; + + phb = (p3 >> 24) & 0xF; + switch (phb) { + case 1: + head = objs[p2].loc.p; + break; + case 2: + head = objs[p2].loc.b; + break; + default: + head = objs[p2].loc.h; + break; + } + hi = p4 & 0xFF; + if (hi == 0) + hi = 255; + lo = (p4 >> 8) & 0xFF; + if (!(p3 & 0xFF)) + p3 |= 0x10; + head += (p3 & 0xFF0000) ? -(p3 & 0xFF) : (p3 & 0xFF); + if ((p3 & 0xFF00) == 0) { + if (hi != lo) + head = lo + (head - lo) % (hi - lo); + } else { + if (head > hi) { + head = hi; + p3 = p3 ^ 0x10000; + } else if (head < lo) { + head = lo; + p3 = p3 ^ 0x10000; + } + } + switch (phb) { + case 1: + objs[p2].loc.p = head; + break; + case 2: + objs[p2].loc.b = head; + break; + default: + objs[p2].loc.h = head; + break; + } + objTraps[objs[current_trap].specID].p3 = p3; + break; + } + case ARMAGEDDON_HACK: + hack_armageddon_func(p2, OBJ_LOC_BIN_X(objs[current_trap].loc), OBJ_LOC_BIN_Y(objs[current_trap].loc), p3); + break; + case SHODAN_CONQUER_HACK: + hack_shodan_conquer_func(p2); + break; + case COMPARATOR_HACK: + hack_change_comparator(p2, p3); + break; + case PLOTWARE_HACK: + // show page p2 + plotware_showpage(p2); + break; + case AREASPEW_HACK: + hack_area_spew(p2, p3, p4); + break; + case DIEGO_HACK: + hack_taunt_diego(p2, p3); + break; + case PANEL_REF_HACK: + if (player_struct.panel_ref == p2) + check_panel_ref(TRUE); + break; + case EARTH_DESTROYED_HACK: + email_slam_hack(0x01D); + break; + case MULTI_TRANSMOG_HACK: + hack_multi_trans(p2, p3); + break; + } + return (OK); +} + +errtype do_multi_stuff(ObjID id) { + ObjID other; + + if (id != OBJ_NULL) { + if (objs[id].obclass == CLASS_TRAP) + trap_activate(id, trap_use_message); + else { + switch (objs[id].obclass) { + case CLASS_DOOR: + objDoors[objs[id].specID].locked = 0; + objDoors[objs[id].specID].access_level = 0; + other = objDoors[objs[id].specID].other_half; + if ((other) && objs[other].obclass == CLASS_DOOR) { + // door has a door other half. Unlock it too. + objDoors[objs[other].specID].locked = 0; + objDoors[objs[other].specID].access_level = 0; + } + break; + } + object_use(id, FALSE, OBJ_NULL); + } + } + return (OK); +} + +// number of multi delay units in a second +#define MULTI_TIME_UNIT 10 + +errtype do_timed_multi_stuff(int p) { + TrapSchedEvent new_event; + + if (p >> 16) { + // Time delay + new_event.timestamp = + TICKS2TSTAMP(player_struct.game_time + (ushort)(CIT_CYCLE * (p >> 16)) / MULTI_TIME_UNIT) + 1; + new_event.type = TRAP_SCHED_EVENT; + new_event.target_id = qdata_get(p & 0xFFFF); + new_event.source_id = -1; + schedule_event(&(global_fullmap->sched[MAP_SCHEDULE_GAMETIME]), (SchedEvent *)&new_event); + } else { + // Do immediately + do_multi_stuff(qdata_get(p & 0xFFFF)); + } + return (OK); +} + +errtype trap_multi_func(int p1, int p2, int p3, int p4) { + do_timed_multi_stuff(p1); + do_timed_multi_stuff(p2); + do_timed_multi_stuff(p3); + do_timed_multi_stuff(p4); + return (OK); +} + +errtype do_destroy(int victim_data) { + ObjID victim; + + victim = qdata_get(victim_data); + + if (victim != OBJ_NULL) + ADD_DESTROYED_OBJECT(victim); + return (OK); +} + +errtype trap_destroy_object_func(int p1, int p2, int p3, int p4) { + char message_buf[100]; + + do_destroy(p1); + do_destroy(p2); + do_destroy(p3); + if (p4 > 0) { + message_info(get_string(REF_STR_TrapZeroMessage + p4, message_buf, 80)); +#ifdef AUDIOLOGS + audiolog_bark_play(p4); +#endif + *trap_use_message = TRUE; + } + return (OK); +} + +errtype trap_plot_clock_func(int p1, int p2, int p3, int p4) { return (OK); } + +errtype trap_email_func(int mung, int time, int p3, int p4) { + void add_email_datamunge(short mung, uchar select); + +#ifdef DOOM_EMULATION_MODE + if (QUESTVAR_GET(MISSION_DIFF_QVAR) == 0) + return (OK); +#endif + if (time == 0) { + add_email_datamunge(mung, TRUE); + *trap_use_message = TRUE; + } else { + EmailSchedEvent ev; + ev.type = EMAIL_SCHED_EVENT; + ev.timestamp = TICKS2TSTAMP((time << APPROX_CIT_CYCLE_SHFT) + player_struct.game_time); + ev.datamunge = mung; + schedule_event(&game_seconds_schedule, (SchedEvent *)&ev); + } + return (OK); +} + +errtype trap_texture_func(int p1, int p2, int p3, int p4) { + ObjID id1, id2; + short cx, cy, minx, maxx, miny, maxy; + int i, src[3], dest[3]; + MapElem *pme; + + id2 = qdata_get(p1 >> 16); + if (id2 != OBJ_NULL) { + id1 = qdata_get(p1 & 0xFFFF); + if (!objs[id1].active || !objs[id2].active) { + return (OK); + } + minx = lg_min(OBJ_LOC_BIN_X(objs[id1].loc), OBJ_LOC_BIN_X(objs[id2].loc)); + maxx = lg_max(OBJ_LOC_BIN_X(objs[id1].loc), OBJ_LOC_BIN_X(objs[id2].loc)); + miny = lg_min(OBJ_LOC_BIN_Y(objs[id1].loc), OBJ_LOC_BIN_Y(objs[id2].loc)); + maxy = lg_max(OBJ_LOC_BIN_Y(objs[id1].loc), OBJ_LOC_BIN_Y(objs[id2].loc)); + } else { + minx = maxx = (p1 & 0xFF00) >> 8; + miny = maxy = (p1 & 0xFF); + } + + src[0] = p2 >> 16; + src[1] = p3 >> 16; + src[2] = p4 >> 16; + dest[0] = p2 & 0xFFFF; + dest[1] = p3 & 0xFFFF; + dest[2] = p4 & 0xFFFF; + + for (cx = minx; cx <= maxx; cx++) { + for (cy = miny; cy <= maxy; cy++) { + pme = MAP_GET_XY(cx, cy); + for (i = 0; i < 3; i++) { + if ((src[i] >= 0x1000) || (src[i] == me_tmap(pme, i))) { + if (dest[i] < 0x1000) { + me_tmap_set(pme, i, dest[i]); + } + } + } + } + } + return (OK); +} + +errtype trap_teleport_func(int targ_x, int targ_y, int targ_z, int targlevel) { + ObjLoc newloc; + errtype errcode = OK; + uchar to_cyber = FALSE; + + if (targlevel >= 0x1000) + targlevel = player_struct.level; + + // If going between cyber and real, static out the screen during the load + if (targlevel != player_struct.level) { + to_cyber = go_to_different_level(targlevel); + } + if (errcode == OK) { + newloc = objs[PLAYER_OBJ].loc; + if (targ_x < 0x4000) { + targ_x = qdata_get(targ_x); + newloc.x = (targ_x << 8) + 0x80; + } + if (targ_y < 0x4000) { + targ_y = qdata_get(targ_y); + newloc.y = (targ_y << 8) + 0x80; + } + if (targ_z < 0x4000) { + targ_z = qdata_get(targ_z); + newloc.z = targ_z; + } + obj_move_to(PLAYER_OBJ, &newloc, TRUE); + if (to_cyber) { + recall_objloc = newloc; + // KLC if (music_on) + // KLC start_music(); + } + } else { + return (ERR_FOPEN); + } + return (OK); +} + +errtype trap_expose_func(int dmg, int dtype, int tsecs, int p4) { + short damage = qdata_get(dmg & 0xFFFF); + + if (dmg & 0x10000) { + damage &= 0xFFFF; + if (dtype == RADIATION_TYPE) { + if (damage <= player_struct.rad_post_expose) { + player_struct.rad_post_expose -= damage; + return OK; + } + damage -= player_struct.rad_post_expose; + player_struct.rad_post_expose = 0; + } + if (dtype == BIO_TYPE) { + if (damage <= player_struct.bio_post_expose) { + player_struct.bio_post_expose -= damage; + return OK; + } + damage -= player_struct.bio_post_expose; + player_struct.bio_post_expose = 0; + } + damage = lg_max(-damage, -128); + } + expose_player((byte)damage, (ubyte)qdata_get(dtype), (ushort)qdata_get(tsecs)); + return OK; +} + +errtype trap_bark_func(int speaker, int strnum, int color, int hud_bark) { + int string_id = REF_STR_TrapZeroMessage + qdata_get(strnum); + SchedEvent new_event; + ushort special; + uint timeout = 0, len; + + special = (speaker < 0) ? -speaker : 0; + + if (hud_bark) { + // just message_info for now + string_message_info(string_id); +#ifdef AUDIOLOGS + audiolog_bark_play(string_id - REF_STR_TrapZeroMessage); +#endif + } else if (special) { + int mug; + + mug = (-special == SHODAN_BARK_CODE) ? SHODAN_MUG : DIEGO_MUG; + long_bark(PLAYER_OBJ, mug, string_id, (ubyte)color); + timeout = SHODAN_BARK_TIMEOUT; + } else if ((ObjID)speaker == OBJ_NULL) { + long_bark(PLAYER_OBJ, 0, string_id, (ubyte)color); + timeout = NULL_BARK_TIMEOUT; + } else { + // long_bark(speaker,0,string_id,(ubyte)color); + long_bark(PLAYER_OBJ, 0, string_id, (ubyte)color); + timeout = NULL_BARK_TIMEOUT; + } + if (timeout > 0) { + // convert to sixteenths of a second + timeout = (timeout * 16) / 10; + + len = strlen(get_temp_string(string_id)); + timeout *= 10 + len; + new_event.timestamp = TICKS2TSTAMP(player_struct.game_time) + timeout; + new_event.type = BARK_SCHED_EVENT; + schedule_event(&game_seconds_schedule, &new_event); + } + return OK; +} + +errtype (*trap_functions[])(int, int, int, int) = { + trap_null_func, + trap_teleport_func, + trap_damage_func, + trap_create_obj_func, + trap_questbit_func, + trap_cutscene_func, + trap_multi_func, + trap_main_light_func, + trap_sfx_func, + trap_height_func, + trap_terrain_func, + trap_scheduler_func, + trap_alternating_splitter_func, + trap_destroy_object_func, + trap_plot_clock_func, + trap_email_func, + trap_expose_func, + trap_instance_func, + trap_animate_func, + trap_hack_func, + trap_texture_func, + trap_ai_func, + trap_bark_func, + trap_monster_func, + trap_transmogrify_func +}; + +ubyte num_trap_types = (sizeof(trap_functions) / sizeof(trap_functions[0])); + +errtype grind_trap(char type, int p1, int p2, int p3, int p4, ubyte *destroy_count_ptr, ObjID id) { + trap_functions[type](p1, p2, p3, p4); + if (*destroy_count_ptr > 0) { + (*destroy_count_ptr) = (*destroy_count_ptr) - 1; + if (*destroy_count_ptr == 0) + ADD_DESTROYED_OBJECT(id); + } + return (OK); +} + +uchar trap_activate(ObjID id, uchar *use_message) { + uchar retval = FALSE; + ubyte traptype; + int comparator; + int p1, p2, p3, p4; + ubyte *destroy_count_ptr; + uchar special; + + *trap_use_message = *use_message; + current_trap = id; + + switch (objs[id].obclass) { + case CLASS_TRAP: + traptype = objTraps[objs[id].specID].trap_type; + destroy_count_ptr = &(objTraps[objs[id].specID].destroy_count); + if (objs[id].subclass == TRAP_SUBCLASS_TRIGGER) { + // Triggers that overwrite their comparator have their comparator + // set to zero to avoid problems in interpretation. + switch (objs[id].info.type) { + case DEATHWATCH_TRIGGER_TYPE: + case AREA_ENTRY_TRIGGER_TYPE: + case AREA_CONTINUOUS_TRIGGER_TYPE: + comparator = 0; + break; + default: + comparator = objTraps[objs[id].specID].comparator; + break; + } + } + p1 = objTraps[objs[id].specID].p1; + p2 = objTraps[objs[id].specID].p2; + p3 = objTraps[objs[id].specID].p3; + p4 = objTraps[objs[id].specID].p4; + break; + case CLASS_FIXTURE: + traptype = objFixtures[objs[id].specID].trap_type; + destroy_count_ptr = &(objFixtures[objs[id].specID].destroy_count); + if ((objs[id].subclass == FIXTURE_SUBCLASS_RECEPTACLE) || (objs[id].subclass == FIXTURE_SUBCLASS_VENDING)) + comparator = 0; + else + comparator = objFixtures[objs[id].specID].comparator; + p1 = objFixtures[objs[id].specID].p1; + p2 = objFixtures[objs[id].specID].p2; + p3 = objFixtures[objs[id].specID].p3; + p4 = objFixtures[objs[id].specID].p4; + break; + default: + retval = FALSE; + goto out; + } + + if (comparator_check(comparator, id, &special)) { + grind_trap(traptype, p1, p2, p3, p4, destroy_count_ptr, id); + retval = TRUE; + } +out: + *use_message = *trap_use_message; + return (retval); +} + +// Look through all deathwatch triggers on the level +// if lots of goofy things like explosions are being destroyed here, we may want to +// use some sort of heuristic to speed things up (don't search if destroyed thing +// was an anim, etc.) +// also, we might want to be able to override this behavior if destroying objects +// via the editor is causing unwanted trap problems.... + +errtype check_deathwatch_triggers(ObjID id, uchar really_dead) { + ObjSpecID osid, nextid; + int comp; + uchar dummy; + + if (id == OBJ_NULL) + return (ERR_NOEFFECT); + + osid = objTraps[0].id; + + while (osid != OBJ_SPEC_NULL) { + nextid = objTraps[osid].next; + if (objs[objTraps[osid].id].info.type == DEATHWATCH_TRIGGER_TYPE) { + comp = objTraps[osid].comparator; + + // Does this particular trigger care about us? + if (really_dead == !(comp & 0x2000000)) { + if (((comp & 0x1000000) && ((comp & 0xFFFF) == id)) || (ID2TRIP(id) == (comp & 0xFFFFFF))) { + trap_activate(objTraps[osid].id, &dummy); + } + } + } + osid = nextid; + } + return (OK); +} + +#define SQ(x) (x * x) +#define in_bin_radius(x1, y1, x2, y2, r) (SQ(r) > SQ((x1 - x2)) + SQ((y1 - y2))) + +errtype check_entrance_triggers(uchar old_x, uchar old_y, uchar new_x, uchar new_y) { + ObjID id; + ObjSpecID osid; + uchar trap_x, trap_y; + uchar rad; + uchar invert, in_rad_before, in_rad_now; + + osid = objTraps[0].id; + while (osid != OBJ_SPEC_NULL) { + id = objTraps[osid].id; + + switch (objs[id].subclass) { + case TRAP_SUBCLASS_TRIGGER: + trap_x = OBJ_LOC_BIN_X(objs[id].loc); + trap_y = OBJ_LOC_BIN_Y(objs[id].loc); + switch (objs[id].info.type) { + // Yer basic entry & floor trigger -- this will be used for tripbeams in the near future too + case FLOOR_TRIGGER_TYPE: + case ENTRY_TRIGGER_TYPE: + if ((trap_x == new_x) && (trap_y == new_y)) + location_trigger_activate(id); + break; + case AREA_ENTRY_TRIGGER_TYPE: + case AREA_CONTINUOUS_TRIGGER_TYPE: + rad = abs(objTraps[osid].comparator); + invert = (objTraps[osid].comparator >= 0x1000); + in_rad_now = in_bin_radius(trap_x, trap_y, new_x, new_y, rad); + if (objs[id].info.type == AREA_CONTINUOUS_TRIGGER_TYPE) { + if ((in_rad_now && !invert) || (!in_rad_now && invert)) + trap_activate(id, trap_use_message); + } else { + in_rad_before = in_bin_radius(trap_x, trap_y, old_x, old_y, rad); + if (in_rad_before != in_rad_now) { + if ((in_rad_now && !invert) || (!in_rad_now && invert)) + trap_activate(id, trap_use_message); + } + } + break; + } + break; + } + osid = objTraps[osid].next; + } + return (OK); +} + +#define REACTOR_BOOM_QB 0x14 + +errtype do_special_reactor_hack() { + ObjSpecID osid; + + if (!qdata_get(REACTOR_BOOM_QB | 0x2000)) + return (OK); + + // Secret reactor alarm hack + osid = objAnimatings[0].id; + while (osid != OBJ_SPEC_NULL) { + if ((ID2TRIP(objAnimatings[osid].id) == ALERT_PANEL_OFF_TRIPLE) || + (ID2TRIP(objAnimatings[osid].id) == HORZ_KLAXOFF_TRIPLE)) { + objs[objAnimatings[osid].id].info.type++; + } + osid = objAnimatings[osid].next; + } + obj_load_art(FALSE); + return (OK); +} + +errtype do_level_entry_triggers() { + ObjSpecID osid; + uchar special; + + osid = objTraps[0].id; + while (osid != OBJ_SPEC_NULL) { + if (ID2TRIP(objTraps[osid].id) == LEVEL_TRIG_TRIPLE) { + if (comparator_check(objTraps[osid].comparator, objTraps[osid].id, &special)) { + grind_trap(objTraps[osid].trap_type, objTraps[osid].p1, objTraps[osid].p2, objTraps[osid].p3, + objTraps[osid].p4, &objTraps[osid].destroy_count, objTraps[osid].id); + } + } + osid = objTraps[osid].next; + } + + do_special_reactor_hack(); + + return (OK); +} + +errtype do_shodan_triggers() { + ObjSpecID osid; + uchar special; + + osid = objTraps[0].id; + while (osid != OBJ_SPEC_NULL) { + if (ID2TRIP(objTraps[osid].id) == SHODO_TRIG_TRIPLE) { + if (comparator_check(objTraps[osid].comparator, objTraps[osid].id, &special)) { + grind_trap(objTraps[osid].trap_type, objTraps[osid].p1, objTraps[osid].p2, objTraps[osid].p3, + objTraps[osid].p4, &objTraps[osid].destroy_count, objTraps[osid].id); + } + } + osid = objTraps[osid].next; + } + return (OK); +} + +errtype do_ecology_triggers() { + ObjSpecID osid, osid2; + ObjClass cl; + char counter = 0, quan; + int trip; + ObjSpec ospec; + ObjID id; + extern uchar trigger_check; + + osid = objTraps[0].id; + while (osid != OBJ_SPEC_NULL) { + id = objTraps[osid].id; + if (ID2TRIP(id) == ECOLOGY_TRIG_TRIPLE) { + quan = objTraps[osid].comparator >> 24; + cl = (ObjClass)((objTraps[osid].comparator & 0xFF0000) >> 16); + trip = objTraps[osid].comparator & 0xFFFFFF; + osid2 = (*(ObjSpec *)objSpecHeaders[cl].data).bits.id; + counter = 0; + while (osid2 != OBJ_SPEC_NULL) { + ospec = *(ObjSpec *)(objSpecHeaders[cl].data + (osid2 * objSpecHeaders[cl].struct_size)); + if (ID2TRIP(ospec.bits.id) == trip) + counter++; + if (counter > quan) + osid2 = OBJ_SPEC_NULL; + else + osid2 = ospec.next; + } + if (counter < quan) { + trigger_check = FALSE; + grind_trap(objTraps[osid].trap_type, objTraps[osid].p1, objTraps[osid].p2, objTraps[osid].p3, + objTraps[osid].p4, &objTraps[osid].destroy_count, objTraps[osid].id); + trigger_check = TRUE; + } + } + osid = objTraps[osid].next; + } + return (OK); +} diff --git a/engine/src/GameSrc/view360.c b/engine/src/GameSrc/view360.c new file mode 100644 index 0000000..bf9a243 --- /dev/null +++ b/engine/src/GameSrc/view360.c @@ -0,0 +1,347 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/view360.c $ + * $Revision: 1.33 $ + * $Author: xemu $ + * $Date: 1994/10/27 04:53:06 $ + */ + +#include + +#include "frprotox.h" +#include "frcamera.h" +#include "frflags.h" +#include "invent.h" +#include "mfdint.h" +#include "mfdext.h" +#include "mfddims.h" +#include "wares.h" +#include "mainloop.h" +#include "gameloop.h" +#include "tools.h" +#include "frflags.h" +#include "musicai.h" +#include "sfxlist.h" +#include "objsim.h" +#include "faketime.h" +#include "gamestrn.h" +#include "colors.h" +#include "fullscrn.h" +#include "invpages.h" +#include "view360.h" +#include "gr2ss.h" + +#include "otrip.h" +#include "cybstrng.h" +#include "gamescr.h" + +#include "OpenGL.h" + +extern uchar dirty_inv_canvas; + +// ------- +// GLOBALS +// ------- +frc *view360_contexts[NUM_360_CONTEXTS]; // the renderer contexts for each view window +frc *view360_fullscreen_contexts[NUM_360_CONTEXTS]; +#define CONTEXT ((full_game_3d) ? view360_fullscreen_contexts : view360_contexts) +uchar view360_active_contexts[NUM_360_CONTEXTS]; // which contexts should actually draw +#define ACTIVE view360_active_contexts +uchar view360_context_views[NUM_360_CONTEXTS]; // which view is being shown by a given context +#define VIEW view360_context_views + +uchar view360_message_obscured = FALSE; +uchar view360_render_on = FALSE; +short view360_last_update = 0; +uchar view360_is_rendering = FALSE; + +// Set up/turn on all contexts & cameras for the specified mode. +void view360_setup_mode(uchar mode) { + ubyte version = player_struct.hardwarez[CPTRIP(SENS_HARD_TRIPLE)]; + if ((version > 2 && mode == MODE_360) || mode == MODE_270) { + VIEW[LEFT_CONTEXT] = CAMANG_LEFT; + ACTIVE[LEFT_CONTEXT] = TRUE; + VIEW[RIGHT_CONTEXT] = CAMANG_RIGHT; + ACTIVE[RIGHT_CONTEXT] = TRUE; + mfd_notify_func(MFD_3DVIEW_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, FALSE); + mfd_change_slot(MFD_LEFT, MFD_INFO_SLOT); + mfd_change_slot(MFD_RIGHT, MFD_INFO_SLOT); + } + if (mode == MODE_360) { + inventory_clear(); + VIEW[MID_CONTEXT] = CAMANG_BACK; + ACTIVE[MID_CONTEXT] = TRUE; + inv_last_page = inventory_page; + inventory_page = INV_3DVIEW_PAGE; + } + if (mode == MODE_REAR) { + int mfd; + for (mfd = 0; mfd < NUM_MFDS; mfd++) { + if (mfd_get_func(mfd, player_struct.mfd_current_slots[mfd]) == MFD_3DVIEW_FUNC) + break; + } + if (mfd >= NUM_MFDS) + mfd = mfd_grab(); + VIEW[mfd] = CAMANG_BACK; + ACTIVE[mfd] = TRUE; + mfd_notify_func(MFD_3DVIEW_FUNC, MFD_INFO_SLOT, TRUE, MFD_ACTIVE, FALSE); + mfd_change_slot(mfd, MFD_INFO_SLOT); + } +} + +void view360_restore_inventory() { + if (_current_loop == GAME_LOOP) { + chg_set_flg(INVENTORY_UPDATE); + inv_change_fullscreen(full_game_3d); + view360_message_obscured = FALSE; + inv_last_page = INV_BLANK_PAGE; + message_info(""); // This should be NULL as soon as the 2d can handle it. + } + ACTIVE[MID_CONTEXT] = FALSE; +} + +// what/where are these??? +extern grs_canvas _offscreen_mfd, _fullscreen_mfd, inv_view360_canvas; + +static uchar rendered_inv_fullscrn = FALSE; + +extern void shock_hflip_in_place(grs_bitmap *bm); + +int view360_fullscrn_draw_callback(void *v, void *vbm, int x, int y, int flg) { + // KLC shock_hflip_in_place((grs_bitmap *)vbm); + return FALSE; +} + +// --------- +// EXTERNALS +// --------- + +uchar inv_is_360_view(void) { return ACTIVE[MID_CONTEXT]; } + +// then or in with palette +//#define VIEW360_BASEFR (FR_DOUBLEB_MASK|FR_DOHFLIP_MASK) +#define VIEW360_BASEFR (FR_DOUBLEB_MASK) + +void view360_init(void) { + frc *c; + uchar *canv; + short x, y, w, h; + + canv = _offscreen_mfd.bm.bits; + x = MFD_VIEW_LFTX; + y = MFD_VIEW_Y; + w = MFD_VIEW_WID; + h = MFD_VIEW_HGT; +#ifdef SVGA_SUPPORT + ss_point_convert(&x, &y, FALSE); + ss_point_convert(&w, &h, FALSE); + h = lg_min(h, 137); +#endif + view360_contexts[LEFT_CONTEXT] = + fr_place_view(FR_NEWVIEW, FR_DEFCAM, canv, VIEW360_BASEFR | FR_CURVIEW_LEFT, 0, 0, x, y, w, h); + c = view360_fullscreen_contexts[LEFT_CONTEXT] = + fr_place_view(FR_NEWVIEW, FR_DEFCAM, canv, VIEW360_BASEFR | FR_CURVIEW_LEFT, 0, 0, x, y, w, h); + fr_set_callbacks(c, view360_fullscrn_draw_callback, NULL, NULL); + + x = MFD_VIEW_RGTX; + y = MFD_VIEW_Y; + w = MFD_VIEW_WID; + h = MFD_VIEW_HGT; +#ifdef SVGA_SUPPORT + ss_point_convert(&x, &y, FALSE); + ss_point_convert(&w, &h, FALSE); + h = lg_min(h, 137); +#endif + view360_contexts[RIGHT_CONTEXT] = + fr_place_view(FR_NEWVIEW, FR_DEFCAM, canv, VIEW360_BASEFR | FR_CURVIEW_RGHT, 0, 0, x, y, w, h); + canv = _fullscreen_mfd.bm.bits; + c = view360_fullscreen_contexts[RIGHT_CONTEXT] = + fr_place_view(FR_NEWVIEW, FR_DEFCAM, canv, VIEW360_BASEFR | FR_CURVIEW_RGHT, 0, 0, x, y, w, h); + fr_set_callbacks(c, view360_fullscrn_draw_callback, NULL, NULL); + + x = GAME_MESSAGE_X; + y = GAME_MESSAGE_Y; + w = INV_FULL_WD; + h = INV_FULL_HT; +#ifdef SVGA_SUPPORT + ss_point_convert(&x, &y, FALSE); + ss_point_convert(&w, &h, FALSE); +#endif + canv = inv_view360_canvas.bm.bits; + view360_contexts[MID_CONTEXT] = + fr_place_view(FR_NEWVIEW, FR_DEFCAM, canv, VIEW360_BASEFR | FR_CURVIEW_BACK, 0, REAR_FOV, x, y, w, h); + c = view360_fullscreen_contexts[MID_CONTEXT] = + fr_place_view(FR_NEWVIEW, FR_DEFCAM, canv, VIEW360_BASEFR | FR_CURVIEW_BACK, 0, REAR_FOV, x, y, w, h); + fr_set_callbacks(c, view360_fullscrn_draw_callback, NULL, NULL); +} + +void view360_shutdown(void) { + int i; + for (i = LEFT_CONTEXT; i <= MID_CONTEXT; i++) { + fr_free_view(view360_contexts[i]); + fr_free_view(view360_fullscreen_contexts[i]); + } +} + +void view360_update_screen_mode() { + view360_shutdown(); + view360_init(); +} + +char update_string[30] = ""; + +void view360_render(void) { + opengl_begin_sensaround(player_struct.hardwarez[CPTRIP(SENS_HARD_TRIPLE)]); + uchar on = FALSE; + + if (inventory_page != INV_3DVIEW_PAGE && ACTIVE[MID_CONTEXT]) { + view360_restore_inventory(); + } + view360_message_obscured = ACTIVE[MID_CONTEXT]; + if (ACTIVE[MID_CONTEXT] && player_struct.hardwarez[CPTRIP(SENS_HARD_TRIPLE)] == 1) { + short update = *tmd_ticks / CIT_CYCLE; + if (dirty_inv_canvas && update == view360_last_update && (full_game_3d || !rendered_inv_fullscrn)) { + short basex = INVENTORY_PANEL_X; + short basey = INVENTORY_PANEL_Y + INVENTORY_PANEL_HEIGHT; + LGRect r; + char buf[sizeof(update_string)]; + short w, h; + if (strlen(update_string) + 1 >= sizeof(update_string)) { + opengl_end_sensaround(); + return; + } + if (update_string[0] == '\0') + get_string(REF_STR_View360Update, buf, sizeof(buf)); + else + strcpy(buf, "."); + if (full_game_3d) { + basex = 0; + basey = INV_FULL_HT; + gr_push_canvas(&inv_view360_canvas); + } else { + gr_push_canvas(grd_screen_canvas); + } + gr_set_fcolor(WHITE); + gr_set_font(ResLock(RES_tinyTechFont)); + gr_string_size(update_string, &w, &h); + RECT_FILL(&r, basex + w + 2, basey - h - 2, basex + w + 2 + gr_string_width(buf), basey - h - 2 + h); + if (!full_game_3d) + uiHideMouse(&r); + res_draw_text(RES_tinyTechFont, buf, r.ul.x, r.ul.y); + if (!full_game_3d) + uiShowMouse(&r); + ResUnlock(RES_tinyTechFont); + gr_pop_canvas(); + strcat(update_string, buf); + + opengl_end_sensaround(); + return; + } + update_string[0] = '\0'; + view360_last_update = update; + rendered_inv_fullscrn = FALSE; + } + if (ACTIVE[MID_CONTEXT]) { + dirty_inv_canvas = TRUE; + } + + // Render the 360 view scenes. + view360_is_rendering = TRUE; + for (uint8_t i = 0; i < NUM_360_CONTEXTS; i++) + if (ACTIVE[i]) { + fr_rend(CONTEXT[i]); + if (full_game_3d) { +#ifdef STEREO_SUPPORT + if (convert_use_mode == 5) + full_visible = VISIBLE_BIT(i); + else +#endif + full_visible |= VISIBLE_BIT(i); + } + on = TRUE; + } + view360_is_rendering = FALSE; + view360_render_on = on; + + if (on == !(player_struct.hardwarez_status[HARDWARE_360] & WARE_ON)) + use_ware(WARE_HARD, HARDWARE_360); + + opengl_end_sensaround(); +} + +// ------------------ +// MFD FUNC FOR VIEWS +// ------------------ + +void mfd_view360_expose(MFD *mfd, ubyte control) { ACTIVE[mfd->id] = control; } + +// -------------------------- +// WARE STARTUP/SHUTDOWN CODE +// -------------------------- + +#define MODE_MASK 0xC0u +#define MODE_SHF 6u +#define VIEW_MODE(s) (((s)&MODE_MASK) >> MODE_SHF) +#define VIEW_MODE_SET(s, v) ((s) = ((s) & ~MODE_MASK) | ((v) << MODE_SHF)) + +void view360_turnon(uchar visible, uchar real_start) { + uint8_t s = player_struct.hardwarez_status[HARDWARE_360]; + + if (visible) { + view360_setup_mode(VIEW_MODE(s)); + chg_set_flg(_current_3d_flag); + } + view360_render_on = TRUE; +} + +void view360_turnoff(uchar visible, uchar real_stop) { + // restore inventory + if (visible) { + if (real_stop && ACTIVE[MID_CONTEXT]) { + inventory_page = inv_last_page; + if (inventory_page < 0) + inventory_page = 0; + if (full_game_3d) { + full_visible &= ~VISIBLE_BIT(MID_CONTEXT); + } else { + view360_restore_inventory(); + inventory_clear(); + inventory_draw(); + } + } + // empty the mfd slot + if ((ACTIVE[LEFT_CONTEXT] || ACTIVE[RIGHT_CONTEXT]) && real_stop) + mfd_notify_func(MFD_EMPTY_FUNC, MFD_INFO_SLOT, TRUE, MFD_EMPTY, FALSE); + // turn off all views + for (uint8_t i = 0; i < NUM_360_CONTEXTS; i++) { + ACTIVE[i] = false; + } + if (real_stop) + play_digi_fx(SFX_VIDEO_DOWN, 1); + } + view360_render_on = view360_message_obscured = FALSE; +} + +bool view360_check() { + extern uchar hack_takeover; + if (hack_takeover) + return false; + return true; +} diff --git a/engine/src/GameSrc/viewhelp.c b/engine/src/GameSrc/viewhelp.c new file mode 100644 index 0000000..a930976 --- /dev/null +++ b/engine/src/GameSrc/viewhelp.c @@ -0,0 +1,242 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/viewhelp.c $ + * $Revision: 1.7 $ + * $Author: xemu $ + * $Date: 1994/11/20 05:36:11 $ + * + */ +#include "mfdint.h" +#include "mfdext.h" +#include "mfddims.h" +#include "mfdgadg.h" +#include "gamestrn.h" +#include "tools.h" +#include "hud.h" +#include "fullscrn.h" +#include "gr2ss.h" + +#include "gamescr.h" +#include "cybstrng.h" +#include "mfdart.h" +#include "miscqvar.h" + +// ---------- +// PROTOTYPES +// ---------- +uchar mfd_viewhelp_button_handler(MFD *m, LGPoint bttn, uiEvent *ev, void *data); +uchar mfd_viewhelp_color_handler(MFD *, LGPoint bttn, uiEvent *ev, void *); +errtype install_color_handler(MFD_Func *f); + +// ============================================================ +// THE VIEW HELP MFD +// ============================================================ + +// --------------- +// EXPOSE FUNCTION +// --------------- + +/* This gets called whenever the MFD needs to redraw or + undraw. + The control value is a bitmask with the following bits: + MFD_EXPOSE: Update the mfd, if MFD_EXPOSE_FULL is not set, + update incrementally. + MFD_EXPOSE_FULL: Fully redraw the mfd, implies MFD_EXPOSE + + if no bits are set, the mfd is being "unexposed;" its display + being pulled off the screen to make room for a different func. +*/ + +#define MFD_VIEWHELP_FUNC 29 + +#define LEFT_MARGIN 1 +#define TOP_MARGIN 1 +#define NUM_BUTTONS 4 +#define BARRY_HGT (3 * MFD_VIEW_HGT / 4) +#define BARRY_WID (MFD_VIEW_WID - LEFT_MARGIN) +#define BUTTON_WID 12 +#define BUTTON_HGT 11 +#define TEXT_HGT 5 + +#define COLOR_TITLE_Y (BARRY_HGT) +#define COLORS_X (MFD_VIEW_WID / 4) +#define COLORS_Y (COLOR_TITLE_Y + 6) +#define COLORS_HGT (MFD_VIEW_HGT - COLORS_Y - 1) +#define COLORS_WID (MFD_VIEW_WID / 2) +#define COLORS_BWID 7 +#define COLORS_BHGT 7 + +#define ITEM_COLOR (0x37) +#define DULL_ITEM_COLOR (0x5F) + +#define LAST_ON_BITS(mfd) (player_struct.mfd_func_data[MFD_VIEWHELP_FUNC][mfd]) +#define LAST_HUD_COLOR(mfd) (player_struct.mfd_func_data[MFD_VIEWHELP_FUNC][mfd + 2]) + +#define BOOL_FIELD_STRING(n) (REF_STR_ViewHelpBase + (n)) +#define BOOL_FIELD_ON_MSG(n) (REF_STR_ViewHelpOnMsg + (n)) +#define BOOL_FIELD_OFF_MSG(n) (REF_STR_ViewHelpOffMsg + (n)) +#define BOOL_FIELD_BMAP(n) (REF_IMG_ViewIcon1 + (n)) + +#define BAR_SINISTER REF_IMG_BioIconNot + +uchar map_notes_on = TRUE; +extern uchar fullscrn_vitals; +extern uchar fullscrn_icons; + +struct _field_data { + uchar *var; + uchar fullscrn; + int qvar; +} checkbox_fields[] = { + {&fullscrn_vitals, TRUE, FULLSCRN_VITAL_QVAR}, + {&fullscrn_icons, TRUE, FULLSCRN_ICON_QVAR}, + {&map_notes_on, FALSE, AMAP_NOTES_QVAR}, +}; + +#define NUM_CHECKBOX_FIELDS (sizeof(checkbox_fields) / sizeof(struct _field_data)) + +void mfd_viewhelp_expose(MFD *mfd, ubyte control) { + uchar full = control & MFD_EXPOSE_FULL; + if (control == 0) // MFD is drawing stuff + { + // Do unexpose stuff here. + } + if (control & MFD_EXPOSE) // Time to draw stuff + { + int i; + ubyte bits = 0; + // clear update rects + mfd_clear_rects(); + // set up canvas + gr_push_canvas(pmfd_canvas); + ss_safe_set_cliprect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + // Clear the canvas by drawing the background bitmap + if (!full_game_3d) + ss_bitmap(&mfd_background, 0, 0); + // gr_bitmap(&mfd_background, 0, 0); + + // figure out where to start, and what tracks are active. + for (i = 0; i < NUM_CHECKBOX_FIELDS; i++) { + struct _field_data *fd = &checkbox_fields[i]; + + if (*fd->var) { + bits |= 1 << i; + } + } + full = full || bits != LAST_ON_BITS(mfd->id) || hud_color_bank != LAST_HUD_COLOR(mfd->id); + if (full) { + for (i = 0; i < NUM_CHECKBOX_FIELDS; i++) { + ubyte clr = ITEM_COLOR; + short w, h; + char buf[50]; + short x, y; + + x = LEFT_MARGIN; + y = TOP_MARGIN + BARRY_HGT * i / NUM_CHECKBOX_FIELDS; + draw_raw_resource_bm(BOOL_FIELD_BMAP(i), x, y); + if (!(bits & (1 << i))) + draw_raw_resource_bm(BAR_SINISTER, x, y); + mfd_add_rect(x, y, x + BARRY_WID, y + BARRY_HGT); + x += BUTTON_WID + LEFT_MARGIN; + get_string(BOOL_FIELD_STRING(i), buf, sizeof(buf)); + gr_set_font(ResLock(MFD_FONT)); + gr_string_size(buf, &w, &h); + y += (BUTTON_HGT - h) / 2; + if (checkbox_fields[i].fullscrn && !full_game_3d) + clr = DULL_ITEM_COLOR; + mfd_draw_string(buf, x, y, clr, TRUE); + ResUnlock(MFD_FONT); + } + mfd_draw_string(get_temp_string(REF_STR_HudColorsTitle), COLORS_X, COLOR_TITLE_Y, ITEM_COLOR, TRUE); + for (i = 0; i < HUD_COLOR_BANKS; i++) { + short x = COLORS_X + (COLORS_WID - COLORS_BWID) * i / (HUD_COLOR_BANKS - 1); + gr_set_fcolor(hud_colors[i][2]); + ss_rect(x, COLORS_Y, x + COLORS_BWID, COLORS_Y + COLORS_BHGT); + gr_set_fcolor(hud_colors[i][0]); + ss_rect(x + 2, COLORS_Y + 2, x + COLORS_BWID - 2, COLORS_Y + COLORS_BHGT - 2); + if (i == hud_color_bank) { + gr_set_fcolor(ITEM_COLOR); + ss_box(x - 1, COLORS_Y - 1, x + COLORS_BWID + 1, COLORS_Y + COLORS_BHGT + 1); + } + } + } + LAST_ON_BITS(mfd->id) = bits; + LAST_HUD_COLOR(mfd->id) = hud_color_bank; + + // on a full expose, make sure to draw everything + + if (full) + mfd_add_rect(0, 0, MFD_VIEW_WID, MFD_VIEW_HGT); + + // Pop the canvas + gr_pop_canvas(); + // Now that we've popped the canvas, we can send the + // updated mfd to screen + mfd_update_rects(mfd); + } +} + +// -------- +// HANDLERS +// -------- + +uchar mfd_viewhelp_button_handler(MFD *m, LGPoint bttn, uiEvent *ev, void *data) { + int track = -1; + int i = bttn.y; + if (!(ev->subtype & (MOUSE_LDOWN | UI_MOUSE_LDOUBLE))) + return FALSE; + *checkbox_fields[i].var = !*checkbox_fields[i].var; + QUESTVAR_SET(checkbox_fields[i].qvar, *checkbox_fields[i].var); + string_message_info((*checkbox_fields[i].var) ? BOOL_FIELD_ON_MSG(i) : BOOL_FIELD_OFF_MSG(i)); + mfd_notify_func(MFD_VIEWHELP_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); + return TRUE; +} + +uchar mfd_viewhelp_color_handler(MFD *m, LGPoint bttn, uiEvent *ev, void *data) { + if (!(ev->subtype & (MOUSE_LDOWN | UI_MOUSE_LDOUBLE))) + return FALSE; + hud_color_bank = bttn.x; + QUESTVAR_SET(HUDCOLOR_QVAR, hud_color_bank); + mfd_notify_func(MFD_VIEWHELP_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); + return TRUE; +} + +errtype install_color_handler(MFD_Func *f) { + errtype err; + LGPoint bsize = {COLORS_BWID, COLORS_BHGT}; + LGPoint bdims = {HUD_COLOR_BANKS, 1}; + LGRect r = {{COLORS_X, COLORS_Y}, {COLORS_X + COLORS_WID, COLORS_Y + COLORS_HGT}}; + + err = MFDBttnArrayInit(&f->handlers[f->handler_count++], &r, bdims, bsize, mfd_viewhelp_color_handler, NULL); + return err; +} + +errtype mfd_viewhelp_init(MFD_Func *f) { + errtype err; + LGPoint bsize = {BARRY_WID, BUTTON_HGT}; + LGPoint bdims = {1, NUM_CHECKBOX_FIELDS}; + LGRect r = {{LEFT_MARGIN, TOP_MARGIN}, {LEFT_MARGIN + BARRY_WID, TOP_MARGIN + BARRY_HGT}}; + err = MFDBttnArrayInit(&f->handlers[0], &r, bdims, bsize, mfd_viewhelp_button_handler, NULL); + if (err != OK) + return err; + f->handler_count = 1; + return install_color_handler(f); +} diff --git a/engine/src/GameSrc/vitals.c b/engine/src/GameSrc/vitals.c new file mode 100644 index 0000000..4d07063 --- /dev/null +++ b/engine/src/GameSrc/vitals.c @@ -0,0 +1,292 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/vitals.c $ + * $Revision: 1.10 $ + * $Author: xemu $ + * $Date: 1994/11/04 13:10:16 $ + * + */ + +#include "player.h" +#include "status.h" +#include "tools.h" +#include "gamescr.h" +#include "citres.h" +#include "gamesys.h" +#include "fullscrn.h" + +#include "otrip.h" + +#include "gr2ss.h" + +// Defines +#define NUM_BIO_TRACKS 8 + +#define GAMESCR_BIO 0 +#define GAMESCR_BIO_X 5 +#define GAMESCR_BIO_Y 1 +//#define GAMESCR_BIO_WIDTH 149 +#define GAMESCR_BIO_WIDTH 131 +#define GAMESCR_BIO_HEIGHT 17 + +#define DIFF_BIO 1 +#define DIFF_BIO_X 6 +#define DIFF_BIO_Y 181 +#define DIFF_BIO_WIDTH 307 +#define DIFF_BIO_HEIGHT 17 + +#define STATUS_CHI_AMP 8 + +#define STATUS_BIO_X curr_bio_x +#define STATUS_BIO_Y curr_bio_y +#define STATUS_BIO_WIDTH curr_bio_w +#define STATUS_BIO_HEIGHT curr_bio_h + +#define STATUS_START_OFFSET 0 +#define STATUS_BIO_Y_DELTA 1 +#define MAX_BIO_LENGTH 307 +#define STATUS_BIO_LENGTH (STATUS_BIO_WIDTH - STATUS_START_OFFSET) +#define STATUS_BIO_TAIL 30 +#define STATUS_BIO_PEAK (STATUS_BIO_HEIGHT - 3) // 3 because of zany art size +#define STATUS_BIO_X_BASE (STATUS_BIO_X + STATUS_START_OFFSET) +#define STATUS_BIO_Y_BASE (STATUS_BIO_Y + STATUS_BIO_HEIGHT - STATUS_BIO_Y_DELTA - 2) + +#define SPIKE_THRESHOLD 4 +#define COLOR_CHANGES 6 +#define COLOR_LENGTH (STATUS_BIO_TAIL / COLOR_CHANGES) + +#define MAX_TAIL_LENGTH 5 +#define NO_HEIGHT 0x1f +#define INVALID_HEIGHT 0xE0 + +#define COLOR_BIO_MASK 0xE0 // Top Three bits signify depth of color +#define HEIGHT_BIO_MASK 0x1f // Bottom Five bits signify height +#define BIT6 0x20 // First bit of the color field. + +#define COLOR_BIT_SHIFT(x) ((x) << 5) + +//#define FIND_OVERLAP(x,y) (((x) - y + STATUS_BIO_LENGTH) % STATUS_BIO_LENGTH) + +#define STATUS_VITALS_X 184 +#define STATUS_VITALS_Y 0 +#define STATUS_VITALS_WIDTH 130 +#define STATUS_VITALS_HEIGHT 17 + +#define STATUS_VITALS_X_BASE (STATUS_VITALS_X + 4) +#define STATUS_VITALS_Y_TOP (STATUS_VITALS_Y + 1) +#define STATUS_VITALS_Y_BOTTOM (STATUS_VITALS_Y + 11) +#define STATUS_VITALS_H 8 +#define STATUS_VITALS_W (STATUS_VITALS_WIDTH - 9) + +#define STATUS_X 4 +#define STATUS_Y 1 +#define STATUS_HEIGHT 20 +#define STATUS_WIDTH 312 + +#define GAMESCR_BIO_REF REF_IMG_bmBiorhythm +#define DIFF_BIO_REF REF_IMG_bmDiffBio +#define STATUS_RESID curr_bio_ref +#define STATUS_RES_VITALSID REF_IMG_bmVitals +#define STATUS_RES_HEALTH_ID REF_IMG_bmVitalInnardsTop +#define STATUS_RES_ENERGY_ID REF_IMG_bmVitalInnardsBottom + +// Special Status Biorhythm variables + +#define NO_SPIKE 0x01 +#define SPIKE_NOISE 0x02 + +errtype draw_status_arrow(int x_coord, int y); +void draw_status_bar(ushort x0, ushort x1, ushort cutoff, ushort y); + +// =========================================================================== +// ======================= * UPPER RIGHT HAND CORNER STUFF * ================= +// ======================= * STARTS HERE * ================= +// =========================================================================== + +// --------------------------------------------------------------------------- +// status_vitals_init() +// +// Initially draws the background art for the vitals display, and loads in +// appropriate bitmaps so we don't hit disk randomly. + +#define NUM_STATUS_ARROWS 4 +#define STATUS_ANGLE_SIZE 5 +grs_bitmap status_arrows[NUM_STATUS_ARROWS]; + +void status_vitals_init() { + // Draw the background map + // draw_res_bm(STATUS_RES_VITALSID, STATUS_VITALS_X, STATUS_VITALS_Y); + + // Draw the innards + draw_res_bm(STATUS_RES_HEALTH_ID, STATUS_VITALS_X_BASE, STATUS_VITALS_Y_TOP); + draw_res_bm(STATUS_RES_ENERGY_ID, STATUS_VITALS_X_BASE, STATUS_VITALS_Y_BOTTOM); + // draw_hires_resource_bm(STATUS_RES_HEALTH_ID, 372, 3); + // draw_hires_resource_bm(STATUS_RES_ENERGY_ID, 372, 27); + return; +} + +void status_vitals_start() { + // load in our bitmaps! + for (int i = 0; i < NUM_STATUS_ARROWS; i++) + simple_load_res_bitmap(&status_arrows[i], REF_IMG_bmStatusAngle1 + i); +} + +void status_vitals_end() { + int i; + for (i = 0; i < NUM_STATUS_ARROWS; i++) + free(status_arrows[i].bits); +} +#define VITALS_MAX 23 + + // --------------------------------------------------------------------------- + // status_vitals_update() + // + // This routine is called whenever the energy shield and health bar graphs + // in the upper right hand corner of the screen need to be changed. + // + +#define STATUS_ICON_X 307 + +errtype status_vitals_update(uchar Full_Redraw) { + static short last_health_x = 0; + static short last_energy_x = 0; + grs_bitmap *icon_bmp; + extern uchar full_game_3d; + Ref ref; + + short health_value, energy_value, health_x, energy_x; + ushort minx, maxx; + // static long last_time=0L; + // long delta; + + if (global_fullmap->cyber) + health_value = player_struct.cspace_hp; + else + health_value = player_struct.hit_points; + + if (health_value < 0) + health_value = 0; + + energy_value = player_struct.energy; + + // So the scale is 0-VITALS_MAX, which is # of angles to draw + health_x = lg_max(0, ((health_value)*VITALS_MAX + PLAYER_MAX_HP - 1) / PLAYER_MAX_HP); + energy_x = lg_max(0, (energy_value * VITALS_MAX + MAX_ENERGY - 1) / MAX_ENERGY); + // mprintf("health_x = %d, energy_x = %d\n",health_x,energy_x); + + if (Full_Redraw) { + if (health_x != 0) + last_health_x = 0; + if (energy_x != 0) + last_energy_x = 0; + } + + if (health_x != last_health_x) { + minx = lg_min(health_x, last_health_x); + if (Full_Redraw) + maxx = VITALS_MAX; + else + maxx = lg_max(health_x, last_health_x); + + draw_status_bar(minx, maxx, health_x, STATUS_VITALS_Y_TOP); + ref = ((global_fullmap->cyber) ? REF_IMG_bmCyberIcon1 : REF_IMG_bmHealthIcon1) + (health_x / 8); + icon_bmp = lock_bitmap_from_ref(ref); + ss_bitmap(icon_bmp, STATUS_ICON_X, STATUS_VITALS_Y_TOP); + // gr_bitmap(icon_bmp, SCONV_X(STATUS_ICON_X), SCONV_Y(STATUS_VITALS_Y_TOP)); + RefUnlock(ref); + + last_health_x = health_x; + } + + if (!(full_game_3d && global_fullmap->cyber)) { + if (energy_x != last_energy_x) { + + minx = lg_min(energy_x, last_energy_x); + if (Full_Redraw) + maxx = VITALS_MAX; + else + maxx = lg_max(energy_x, last_energy_x); + + draw_status_bar(minx, maxx, energy_x, STATUS_VITALS_Y_BOTTOM + 1); + ref = REF_IMG_bmEnergyIcon1 + (energy_x / 8); + icon_bmp = lock_bitmap_from_ref(ref); + ss_bitmap(icon_bmp, STATUS_ICON_X, STATUS_VITALS_Y_BOTTOM); + // gr_bitmap(icon_bmp, SCONV_X(STATUS_ICON_X), SCONV_Y(STATUS_VITALS_Y_BOTTOM)); + RefUnlock(ref); + + last_energy_x = energy_x; + } + } + + return (OK); +} + +// --------------------------------------------------------------------------- +// draw_status_arrow(int x_coord, int y) +// +// Draws a status arrow at the appropriate location, using the +// right bitmap for that location. A negative x_coord means to "undraw" +// an angle at that coordinate. +// NOTE: x_coord in in angle units, not pixels! y is still in pixels +errtype draw_status_arrow(int x_coord, int y) { + int index; + if (x_coord < 0) { + index = 3; + x_coord = ~x_coord; + } else if (x_coord <= 7) + index = 0; + else if (x_coord <= 15) + index = 1; + else + index = 2; + ss_bitmap(&status_arrows[index], STATUS_VITALS_X_BASE + (x_coord * STATUS_ANGLE_SIZE), y); + // gr_bitmap(&status_arrows[index], + // SCONV_X(STATUS_VITALS_X_BASE + (x_coord * STATUS_ANGLE_SIZE)), + // SCONV_Y(y)); + return (OK); +} + +// --------------------------------------------------------------------------- +// draw_status_bar() +// +// Draws a series of status angles at the specified height, within the specified coordinate +// range, color shaded as appropriate. + +void draw_status_bar(ushort x0, ushort x1, ushort cutoff, ushort y) { + int i; + LGRect r; + + r.ul = MakePoint(STATUS_VITALS_X_BASE + (x0 * STATUS_ANGLE_SIZE), y); + r.lr = MakePoint(STATUS_VITALS_X_BASE + (x1 * STATUS_ANGLE_SIZE), y + status_arrows[0].h); + + uiHideMouse(&r); + // mprintf ("draw_bar x0=%d x1=%d cutoff = %d\n",x0,x1,cutoff); + // Do the drawing + for (i = x0; i < cutoff; i++) + draw_status_arrow(i, y); + + // Do the erasing + if (!full_game_3d) + for (i = cutoff; i < x1; i++) + draw_status_arrow(~i, y); + uiShowMouse(&r); + + return; +} diff --git a/engine/src/GameSrc/vmail.c b/engine/src/GameSrc/vmail.c new file mode 100644 index 0000000..6f6632c --- /dev/null +++ b/engine/src/GameSrc/vmail.c @@ -0,0 +1,489 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/vmail.c $ + * $Revision: 1.31 $ + * $Author: xemu $ + * $Date: 1994/11/01 09:17:43 $ + * + */ + +#include "Shock.h" + +#include "anim.h" +#include "email.h" +#include "game_screen.h" +#include "vmail.h" +#include "input.h" +#include "invent.h" +#include "mainloop.h" +#include "gameloop.h" +#include "tools.h" +#include "gametime.h" +#include "sfxlist.h" +#include "musicai.h" +#include "player.h" +#include "sdl_events.h" +#include "statics.h" +#include "fullscrn.h" +#include "render.h" +#include "gr2ss.h" +#include "criterr.h" + + +//#define LOTS_O_SPEW +uchar vmail_wait_for_input = TRUE; + +//#define CONTINUOUS_VMAIL_TEST + +ActAnim *main_anim; + +#define NUM_VMAIL 6 +#define INTRO_VMAIL (NUM_VMAIL+1) + +byte current_vmail = -1; +extern LGCursor vmail_cursor; + +Ref vmail_frame_anim[NUM_VMAIL] = { + RES_FRAMES_shield, + RES_FRAMES_grove, + RES_FRAMES_bridge, + RES_FRAMES_laser1, + RES_FRAMES_status, + RES_FRAMES_explode1 +}; + +Ref vmail_res[NUM_VMAIL] = { + RES_shield, + RES_grove, + RES_bridge, + RES_laser1, + RES_status, + RES_explode1 +}; + +ubyte vmail_len[NUM_VMAIL] = { + 1, // shield + 1, // grove + 1, // bridge + 2, // laser + 1, // status + 5, // explode +}; + +#define MAX_VMAIL_SIZE 100000 + +// -------------------------------------------------------------------- +// +// + +extern grs_canvas *anim_offscreen; +uchar copied_background = FALSE; +grs_bitmap *vmail_background = NULL; + +#pragma disable_message(202) +void vmail_intro(LGRect *area, ubyte flags) +{ + if (flags & BEFORE_ANIM_BITMAP) + { + if (vmail_background) + gr_bitmap(vmail_background, 0, 0); + } +} +#pragma enable_message(202) + +// -------------------------------------------------------------------- +// +// + +#pragma disable_message(202) +void vmail_anim_end(ActAnim *paa, AnimCode ancode, AnimCodeData *pdata) +{ +#ifdef PLAYTEST + if (current_vmail == -1) + { + Warning(("Trying to end vmail, with no current vmail!\n")); + } +#endif + current_vmail = -1; +} +#pragma enable_message(202) + +// -------------------------------------------------------------------- +// +// + +#pragma disable_message(202) +void vmail_start_anim_end(ActAnim *paa, AnimCode ancode, AnimCodeData *pdata) +{ +#ifdef PLAYTEST + if (current_vmail == -1) + { + Warning(("Trying to end vmail, with no current vmail!\n")); + } +#endif + current_vmail = -1; +} +#pragma enable_message(202) + +#define EARLY_EXIT ((errtype)200) + +// --------------------------------------------- +// +// play_vmail_intro() +// + +#define MOVIE_BUFFER_SIZE (512 * 1024) + +errtype play_vmail_intro(uchar use_texture_buffer) +{ + LGPoint animloc = {VINTRO_X, VINTRO_Y}; + uchar *p, *useBuffer; + int bsize; + short w,h; + + DEBUG("Playing vmail intro"); + + main_anim = AnimPlayRegion(REF_ANIM_vintro, mainview_region, animloc, 0, vmail_intro); + if (main_anim == NULL) + return(ERR_NOEFFECT); + + if (use_texture_buffer) + { + AnimSetDataBufferSafe(main_anim, tmap_static_mem,sizeof(tmap_static_mem)); + AnimPreloadFrames(main_anim, REF_ANIM_vintro); + } + + // let's slork up memory!!!! + w = VINTRO_W; + h = VINTRO_H; + { + useBuffer = frameBuffer; + bsize = sizeof(frameBuffer); + } + if ((w * h) + sizeof(grs_bitmap) > bsize) + critical_error(CRITERR_MEM|8); + p = useBuffer+bsize-(w*h); + vmail_background = (grs_bitmap *) (p - sizeof(grs_bitmap)); + + gr_init_bm(vmail_background, p, BMT_FLAT8, 0, w,h); + uiHideMouse(NULL); +#ifdef SVGA_SUPPORT + if (convert_use_mode) + { + grs_canvas tempcanv; + gr_make_canvas(vmail_background,&tempcanv); + gr_push_canvas(&tempcanv); + gr_clear(1); + gr_pop_canvas(); + } + else +#endif + gr_get_bitmap(vmail_background, VINTRO_X, VINTRO_Y); + uiShowMouse(NULL); + + AnimSetNotify(main_anim, NULL, ANCODE_KILL, vmail_start_anim_end); + current_vmail = INTRO_VMAIL; + play_digi_fx(SFX_VMAIL, 1); + +#ifdef LOTS_O_SPEW + mprintf("*PLAY INTRO*"); +#endif + while (current_vmail != -1) + { + AnimRecur(); + tight_loop(TRUE); + } +#ifdef LOTS_O_SPEW + mprintf("*DONE INTRO*"); +#endif + vmail_background = NULL; + + return(OK); +} + +// -------------------------------------------------------------------- +// +// play_vmail() +// + +#pragma disable_message(202) +errtype play_vmail(byte vmail_no) +{ + LGPoint animloc = {VINTRO_X, VINTRO_Y}; + errtype intro_error; + int vmail_animfile_num = 0; + uchar early_exit = FALSE; + uchar preload_animation= TRUE; + uchar use_texture_buffer = FALSE; + int len = vmail_len[vmail_no]; + int i; + //MemStat data; + + // let's extern + + DEBUG("Playing vmail %i", vmail_no); + + // the more I look at this procedure - the more I think + // art - what were you thinking + extern uiSlab fullscreen_slab; + extern uiSlab main_slab; + extern uchar game_paused; + extern uchar checking_mouse_button_emulation; + extern short old_invent_page; + + // make sure we don't have a current vmail, and we're given a valid vmail num + if ((current_vmail != -1) || (vmail_no < 0) || (vmail_no >= NUM_VMAIL)) + return(ERR_NOEFFECT); + + if (full_game_3d) + render_run(); + + // spew the appropriate text for vmail - full screen needs a draw! + suspend_game_time(); + time_passes = FALSE; + checking_mouse_button_emulation = game_paused = TRUE; + + // open the res file + vmail_animfile_num = ResOpenFile("res/data/vidmail.res"); + if (vmail_animfile_num < 0) + return(ERR_FOPEN); + + uiPushSlabCursor(&fullscreen_slab, &vmail_cursor); + uiPushSlabCursor(&main_slab, &vmail_cursor); + + //MemStats(&data); + //use_texture_buffer = (data.free.sizeMax < MAX_VMAIL_SIZE); + +#ifdef LOTS_O_SPEW + mprintf("\nBUFFER:(%d)\n", use_texture_buffer); +#endif + + // if we're not using the texture buffer - then we can probably + // preload the animations + if (!use_texture_buffer) + { + uchar cant_preload_all = FALSE; + + // load the intro in first! before checking for preloading + if (ResLock(RES_FRAMES_vintro) == NULL) + use_texture_buffer = TRUE; + else + { + for (i=0;i no pause between the two + // if it fails on the lock - then say you can't preload! + if(ResLock(vmail_frame_anim[vmail_no]+i) == NULL) + { + cant_preload_all = TRUE; + break; + } + } + // if we failed our preloading for whatever reason + // let's unlock it all - drop it so that it doesn't stay + // in memory + if (cant_preload_all) + { + int j; + preload_animation = FALSE; + for (j=0; j. + +*/ +/* + * $Source: r:/prj/cit/src/RCS/wares.c $ + * $Revision: 1.109 $ + * $Author: dc $ + * $Date: 1994/11/22 15:59:26 $ + * + */ + +#include + +#include "wares.h" +#include "hud.h" +#include "player.h" +#include "email.h" +#include "gamerend.h" +#include "gamesys.h" +#include "mfdgames.h" +#include "sideicon.h" +#include "newmfd.h" +#include "cybstrng.h" +#include "gamestrn.h" +#include "textmaps.h" +#include "frparams.h" +#include "FrUtils.h" +#include "objsim.h" +#include "otrip.h" +#include "mainloop.h" +#include "gameloop.h" +#include "musicai.h" +#include "sfxlist.h" +#include "objbit.h" +#include "objprop.h" +#include "plotware.h" +#include "target.h" +#include "tools.h" +#include "faketime.h" +#include "view360.h" +#include "weapons.h" +#include "map.h" +#include "physics.h" +#include "softdef.h" +#include "cyber.h" +#include "damage.h" + +//---------------- +// Internal Prototypes +//---------------- +bool check_game(void); + +// ------ +// Globals +// ------- + +// forward decls for arrays at end of file +extern WARE HardWare[NUM_HARDWAREZ]; +extern WARE Combat_SoftWare[NUM_COMBAT_SOFTS]; +extern WARE Defense_SoftWare[NUM_DEFENSE_SOFTS]; +extern WARE Misc_SoftWare[NUM_MISC_SOFTS]; + +#define MAX_VERSIONS 5 +extern short energy_cost_vec[NUM_HARDWAREZ][MAX_VERSIONS]; + +long ware_base_triples[NUM_WARE_TYPES] = { + MAKETRIP(CLASS_HARDWARE, 0, 0), + MAKETRIP(CLASS_SOFTWARE, SOFTWARE_SUBCLASS_OFFENSE, 0), + MAKETRIP(CLASS_SOFTWARE, SOFTWARE_SUBCLASS_DEFENSE, 0), + MAKETRIP(CLASS_SOFTWARE, SOFTWARE_SUBCLASS_ONESHOT, 0), +}; + +// The existence of this array is a crime. I should be shot. +ubyte waretype2invtype[] = { + MFD_INV_HARDWARE, + MFD_INV_SOFT_COMBAT, + MFD_INV_SOFT_DEFENSE, + MFD_INV_SOFT_MISC, +}; + +#define IDX_OF_TYPE(type, trip) (OPTRIP(trip) - OPTRIP(ware_base_triples[type])) + +#define PASSIVE_WARE_FLAG 1 + +// EXTERNALS +// ========= +// ------------------------------------------------------- +// get_ware_triple() converts our stupid representation for +// a ware triple into the standard one. + +int get_ware_triple(int waretype, int num) { return nth_after_triple(ware_base_triples[waretype], num); } + +// --------------------------------------------------------------------------- +// get_ware_name() +// +// Returns the name of a ware to the inventory system. + +char *get_ware_name(int waretype, int num, char *buf, int sz) { + get_object_short_name(nth_after_triple(ware_base_triples[waretype], num), buf, sz); + return buf; +} + +uchar is_passive_hardware(int n) { + ushort cflags = (ObjProps[OPTRIP(MAKETRIP(CLASS_HARDWARE, 0, 0)) + n].flags & CLASS_FLAGS) >> CLASS_FLAGS_SHF; + return (cflags & PASSIVE_WARE_FLAG); +} + +bool is_oneshot_misc_software(int n) { return ((n < NUM_ONESHOT_SOFTWARE)); } + +// INTERNALS +// ========= + +int energy_cost(int warenum) { + uchar version = player_struct.hardwarez[warenum]; + if (version == 0) + return 0; + if (warenum == CPTRIP(LANTERN_HARD_TRIPLE)) + version = LAMP_SETTING(player_struct.hardwarez_status[warenum]) + 1; + if (warenum == CPTRIP(SHIELD_HARD_TRIPLE)) + version = LAMP_SETTING(player_struct.hardwarez_status[warenum]) + 1; + if (warenum == CPTRIP(MOTION_HARD_TRIPLE) && motionware_mode == MOTION_SKATES) + version = MOTION_SKATES; + if (warenum == CPTRIP(JET_HARD_TRIPLE)) + return 0; + return energy_cost_vec[warenum][version - 1]; +} + +// --------------------------------------------------------------------------- +// use_ware() +// +// Called from the UI/Inventory, this routine figures out what is being used +// and what function to call. Turns things on or off as appropriate. + +void use_ware(int waretype, int num) { + ubyte *player_wares, *player_status; + WARE *wares; + int n, ecost; + int ware_sfx = SFX_NONE, hnd; + + // int i; + // ubyte invtype; + if ((!global_fullmap->cyber != (waretype == 0)) // boolean equality, yum. + && !(waretype == WARE_SOFT_MISC && num == 4) // special games ware hack + && !(waretype == WARE_HARD && num == HARDWARE_FULLSCREEN)) + return; + get_ware_pointers(waretype, &player_wares, &player_status, &wares, &n); + if ((player_wares[num] == 0) && (!(WareActive(player_status[num])))) { + return; // don't turn on a ware we don't have, only turn off one we're discarding + } + + // Hey, can we even use this kind of ware right now? + if (wares[num].check != NULL) + if (!wares[num].check()) + return; + + // check to see if we have enough power + if (waretype == WARE_HARD && !WareActive(player_status[num]) && player_struct.energy < (energy_cost(num) + 4) / 5) { + string_message_info(REF_STR_WareNoPower); + return; + } + player_status[num] ^= WARE_ON; + + if (wares[num].sideicon != SI_NONE) + side_icon_expose(wares[num].sideicon); + + if (!WareActive(player_status[num])) { // we're turning a ware off + + // note that the energy_cost function may use state which is + // dependent on the ware being on to figure out the correct + // cost (e.g., motionware). + if (waretype == WARE_HARD) + ecost = energy_cost(num); + + if (wares[num].turnoff) + wares[num].turnoff(TRUE, TRUE); + switch (num) { + case HARDWARE_360: + case HARDWARE_SHIELD: + case HARDWARE_EMAIL: + break; + case HARDWARE_GOGGLE_INFRARED: + ware_sfx = SFX_VIDEO_DOWN; + break; + default: + ware_sfx = SFX_HUDFROB; + break; + } + } else { // we're turning a ware on + + // Turn on the durned thing + if (wares[num].turnon) + wares[num].turnon(TRUE, TRUE); + + // note that the energy_cost function may use state which is + // dependent on the ware being on to figure out the correct + // cost (e.g., motionware). + if (waretype == WARE_HARD) + ecost = energy_cost(num); + + if ((waretype == WARE_HARD) && (num >= FIRST_GOGGLE_WARE) && (num <= LAST_GOGGLE_WARE)) { + // Play the goggle sound effect + ware_sfx = SFX_GOGGLE; + } else { + if (num != HARDWARE_SHIELD) { + ware_sfx = SFX_HUDFROB; + } + } + + // // keep the invtype around in case we want to let mfd know about it + // if (waretype == WARE_HARD) invtype = MFD_INV_HARDWARE; + // else if (waretype == WARE_SOFT_COMBAT) invtype = MFD_INV_SOFT_COMBAT; + // else if (waretype == WARE_SOFT_DEFENSE) invtype = MFD_INV_SOFT_DEFENSE; + // else invtype = MFD_INV_SOFT_MISC; + } + if (ware_sfx != SFX_NONE) { + extern char secret_global_pan; + int ci_idx = wares[num].sideicon; + // secret_global_pan=(ci_idx==SI_NONE)?SND_DEF_PAN:(ci_idx<5)?5:122; + hnd = play_digi_fx(ware_sfx, 1); + // secret_global_pan=SND_DEF_PAN; + } + if (waretype == WARE_HARD) { + short newe = player_struct.energy_spend; + if (WareActive(player_status[num])) + newe = lg_min(newe + ecost, MAX_ENERGY); + else + newe = lg_max(newe - ecost, 0); + set_player_energy_spend((ubyte)newe); + } + if (_current_loop <= FULLSCREEN_LOOP) + chg_set_flg(INVENTORY_UPDATE); + mfd_notify_func(NOTIFY_ANY_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, TRUE); +} + +// Hey, we're closing down a game. return to normalcy. +void hardware_closedown(uchar visible) { + for (uint16_t i = 0; i < NUM_HARDWAREZ; i++) { + // if (i != HARDWARE_FULLSCREEN) + if (WareActive(player_struct.hardwarez_status[i])) + if (HardWare[i].turnoff != NULL) + HardWare[i].turnoff(visible, FALSE); + } +} + +void hardware_startup(uchar visible) { + for (uint16_t i = 0; i < NUM_HARDWAREZ; i++) { + // if (i != HARDWARE_FULLSCREEN) + if (WareActive(player_struct.hardwarez_status[i])) + if (HardWare[i].turnon != NULL) + HardWare[i].turnon(visible, FALSE); + } +} + +void hardware_power_outage(void) { + for (uint16_t i = 0; i < NUM_HARDWAREZ; i++) { + if (energy_cost(i) > 0 && WareActive(player_struct.hardwarez_status[i])) + use_ware(WARE_HARD, i); + } + if (WareActive(player_struct.hardwarez_status[CPTRIP(JET_HARD_TRIPLE)])) + use_ware(WARE_HARD, CPTRIP(JET_HARD_TRIPLE)); +} + +// --------------------------------------------------------------------------- +// get_ware_pointers() +// +// Sets a number of pointers to point at the appropriate ware structures +// for a given type. + +void get_ware_pointers(int type, ubyte **player_wares, ubyte **player_status, WARE **wares, int *n) { + if (type == WARE_HARD) { + *n = NUM_HARDWAREZ; + *player_wares = player_struct.hardwarez; + *player_status = player_struct.hardwarez_status; + *wares = HardWare; + } + + else if (type == WARE_SOFT_COMBAT) { + *n = NUM_COMBAT_SOFTS; + *player_wares = player_struct.softs.combat; + *player_status = player_struct.softs_status.combat; + *wares = Combat_SoftWare; + } else if (type == WARE_SOFT_DEFENSE) { + *n = NUM_DEFENSE_SOFTS; + *player_wares = player_struct.softs.defense; + *player_status = player_struct.softs_status.defense; + *wares = Defense_SoftWare; + } else { + *n = NUM_MISC_SOFTS; + *player_wares = player_struct.softs.misc; + *player_status = player_struct.softs_status.misc; + *wares = Misc_SoftWare; + } + +} + +// -------------------------------------------------- +// +// get_player_ware_version returns the version number +// of a ware in the player's inventory. zero means +// the player doesn't have it. + +int get_player_ware_version(int type, int n) { + WARE *Pwares; + ubyte *pver; + ubyte *pstat; + int num; + get_ware_pointers(type, &pver, &pstat, &Pwares, &num); + return pver[n]; +} + +// --------------------------------------------------------------------------- +// hw_hotkey_callback() +// +// The callback for the hardware buttons when triggered throug hotkeys + +uchar hw_hotkey_callback(ushort keycode, uint32_t context, intptr_t data) { + int wareType = WARE_HARD; + int wareNum = (int) data; + if (get_player_ware_version(wareType, wareNum)) { + use_ware(wareType, wareNum); + } + + return TRUE; +} + +// --------------------------------------------------------------------------- +// wares_update() +// +// Called from the main loop, this routine cycles through all active wares +// and sees if any need attention. + +void wares_update() { + ubyte *player_status, *player_wares; + WARE *wares; + int i, j, n; + + if ((player_struct.game_time - player_struct.last_ware_update) >= WARE_UPDATE_FREQ) { + + player_struct.last_ware_update = player_struct.game_time; + + // Iterate through all types of wares... + + // NOTE: At some point, we may want to differentiate here between + // wares that get updated in the cyberloop as opposed to the + // real world. For now, we leave it all mashed together. + + for (i = 0; i < NUM_WARE_TYPES; i++) { + + get_ware_pointers(i, &player_wares, &player_status, &wares, &n); + + // Now we know what type of ware we're looking at. + // Look at all wares of this type. + + for (j = 0; j < n; j++) { + + // Is it active? If so... + if (WareActive(player_status[j])) { + + // Does it have a continually active effect? + if (wares[j].effect) + wares[j].effect(); + } + } + + // We're ready to look at the next type of ware. + } + } + for (j = 0; j < NUM_HARDWAREZ; j++) + if (player_struct.hardwarez_status[j] & WARE_FLASH) { + side_icon_expose(HardWare[j].sideicon); + } + + } + +// --------------------------------------------------------------------------- +// wares_init() +// +// Sets the static values for all wares. + +void wares_init() { } + +// CALLBACKS +// ========= + +// --------------------------------------------------------------------------- +// wares_dummy_func() +// +// Temporary dummy function for all wares callbacks. + +// void wares_dummy_func() +//{ +// return; +//} + +// ---------- +// * BIO WARE +// ---------- +void bioware_turnon(uchar visible, uchar real_start); +void bioware_turnoff(uchar visible, uchar real_stop); +void bioware_effect(void); + +// --------------------------------------------------------------------------- +// bioware_turnon() +// +// Let the MFD system know that the bioware is active, and take over +// the appropriate info MFD + +void bioware_turnon(uchar visible, uchar real_s) { + if (visible) { + mfd_notify_func(MFD_BIOWARE_FUNC, MFD_INFO_SLOT, TRUE, MFD_FLASH, TRUE); + int32_t i = mfd_grab_func(MFD_BIOWARE_FUNC, MFD_INFO_SLOT); + mfd_change_slot(i, MFD_INFO_SLOT); + } + +} + +// --------------------------------------------------------------------------- +// bioware_turnoff() +// +// Let the MFD system know that the bioware is deactivated, and toss it off +// the info slot, replacing it with a blank. + +void bioware_turnoff(uchar visible, uchar real_stop) { + if (real_stop && player_struct.mfd_all_slots[MFD_INFO_SLOT] == MFD_BIOWARE_FUNC) + mfd_notify_func(MFD_EMPTY_FUNC, MFD_INFO_SLOT, TRUE, MFD_EMPTY, TRUE); +} + +// --------------------------------------------------- +// bioware_effect() +// +// updates the mfd. + +void bioware_effect(void) { mfd_notify_func(MFD_BIOWARE_FUNC, MFD_INFO_SLOT, FALSE, MFD_ACTIVE, FALSE); } + +// --------------- +// * INFRARED WARE +// --------------- +void infrared_turnon(uchar visible, uchar real_start); +void infrared_turnoff(uchar visible, uchar real_start); + +extern char curr_clut_table; +// --------------------------------------------------------------------------- +// infrared_turnon() +// +// Turns on the infrared ware +void infrared_turnon(uchar visible, uchar real_s) { + if (visible) { + gr_set_light_tab(bw_shading_table); + curr_clut_table = 1; + chg_set_flg(_current_3d_flag); + hud_set(HUD_INFRARED); + } +} + +// --------------------------------------------------------------------------- +// infrared_turnoff() +// +// Turns off the infrared ware + +void infrared_turnoff(uchar visible, uchar real_s) { + if (visible) { + gr_set_light_tab(shading_table); + chg_set_flg(_current_3d_flag); + curr_clut_table = 0; + + hud_unset(HUD_INFRARED); + } +} + +// -------------------- +// * TARGETING WARE +// -------------------- +void targeting_turnon(uchar visible, uchar real_start); +void targeting_turnoff(uchar visible, uchar real_start); + +// --------------------------------------------------------------------------- +// targeting_turnon() +// +// Turn on the targeting ware +void targeting_turnon(uchar visible, uchar real_start) { + + player_struct.hardwarez_status[CPTRIP(TARG_GOG_TRIPLE)] &= ~WARE_ON; + if (visible && real_start) { + if (player_struct.curr_target == OBJ_NULL) + select_closest_target(); + mfd_change_slot(mfd_grab_func(MFD_TARGET_FUNC, MFD_TARGET_SLOT), MFD_TARGET_SLOT); + } +} + +// --------------------------------------------------------------------------- +// targeting_turnoff() +// +// Turn off the targeting ware + +void targeting_turnoff(uchar visible, uchar real_s) { } + +// ---------------- +// LANTERN WARE +// --------------- + +struct _lampspec { + int rad1; + int base1; + int rad2; + int base2; + fix slope; + fix yint; +} lamp_specs[] = { + {0, 10, 5, 0, -2 * FIX_UNIT, 12 * FIX_UNIT}, + {1, 20, 6, 0, -4 * FIX_UNIT, 24 * FIX_UNIT}, + {1, 18, 7, 0, -3 * FIX_UNIT, 21 * FIX_UNIT}, + {1, 14, 8, 0, -2 * FIX_UNIT, 16 * FIX_UNIT} +}; + +// other oldest lowest value +// { 0,15,5,0,-3*FIX_UNIT,18*FIX_UNIT}, +// old lowest value (old 0) +// { 0,8,4,0,-2*FIX_UNIT,8*FIX_UNIT}, +// is level 3 above ever used? + +extern uchar muzzle_fire_light; + +void lamp_set_vals(void) { lamp_set_vals_with_offset(0); } + +#define OFF_SHF 3 +void lamp_set_vals_with_offset(byte offset) { + int n = IDX_OF_TYPE(WARE_HARD, LANTERN_HARD_TRIPLE), s; + struct _lampspec *lspec; + + s = (muzzle_fire_light) ? LAMP_SETTING(player_struct.light_value) : LAMP_SETTING(player_struct.hardwarez_status[n]); + lspec = &lamp_specs[s]; + + _frp.lighting.yint = lspec->yint + (offset << (16 - OFF_SHF)); + _frp.lighting.slope = lspec->slope; + _frp.lighting.rad[0] = (uchar)lspec->rad1; + _frp.lighting.base[0] = + (uchar)((lspec->base1 + (offset >> OFF_SHF) > 0) ? (lspec->base1 + (offset >> OFF_SHF)) : 0); + if (offset != 0) { + fix slope_based_mod; + slope_based_mod = fix_div((offset << (16 - OFF_SHF)), -lspec->slope); + _frp.lighting.rad[1] = (uchar)(lspec->rad2 + (slope_based_mod >> 16)); + // slope_based_mod=fix_mul((offset<<(16-OFF_SHF)),-lspec->slope); + _frp.lighting.yint += -lspec->slope; + _frp.lighting.rad[0]++; + } else + _frp.lighting.rad[1] = (uchar)lspec->rad2; + _frp.lighting.base[1] = (uchar)lspec->base2; + + chg_set_flg(_current_3d_flag); + // Warning(("New parms %x %x, %x %x, line %x %x from %x %x\n", + // _frp.lighting.rad[0], _frp.lighting.base[0], + // _frp.lighting.rad[1], _frp.lighting.base[1], + // _frp.lighting.yint, _frp.lighting.slope,offset,s)); +} + +void lamp_turnon(uchar visible, uchar real_s) { + lamp_set_vals(); + if (visible) { + _frp_light_bits_set(LIGHT_BITS_CAM); + mfd_notify_func(MFD_LANTERN_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + } +} + +void lamp_change_setting(byte offset) { + lamp_set_vals_with_offset(offset); + _frp_light_bits_set(LIGHT_BITS_CAM); +} + +void lamp_turnoff(uchar visible, uchar real_stop) { + if (visible) { + _frp_light_bits_clear(LIGHT_BITS_CAM); + chg_set_flg(_current_3d_flag); + if (real_stop) + mfd_notify_func(MFD_LANTERN_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + } +} + +uchar lantern_change_setting_hkey(ushort key, uint32_t context, intptr_t data) { + int n = CPTRIP(LANTERN_HARD_TRIPLE); + int v = player_struct.hardwarez[n]; + uint32_t s = player_struct.hardwarez_status[n]; + uchar on = s & WARE_ON; + void mfd_lantern_setting(int setting); + + s = LAMP_SETTING(s); + if (s == 0 && on) { + use_ware(WARE_HARD, n); + mfd_notify_func(MFD_LANTERN_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + return TRUE; + } + + s = (s + v - 1) % v; // decrement current setting + mfd_lantern_setting(s); + + if (!on) + use_ware(WARE_HARD, n); + mfd_notify_func(MFD_LANTERN_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + + return TRUE; +} + +//-------------------------- +// SHIELD WARE +//-------------------------- + +#define SHIELD_IDX (CPTRIP(SHIELD_HARD_TRIPLE)) + +ubyte shield_absorb_rates[] = {20, 40, 75, 75}; +ubyte shield_thresholds[] = {0, 10, 15, 30}; + +void shield_set_absorb(void) { + ubyte s = player_struct.hardwarez_status[SHIELD_IDX]; + if (s & WARE_ON) { + player_struct.shield_absorb_rate = shield_absorb_rates[LAMP_SETTING(s)]; + player_struct.shield_threshold = shield_thresholds[LAMP_SETTING(s)]; + } else { + player_struct.shield_absorb_rate = 0; + player_struct.shield_threshold = 0; + } +} + +void shield_toggle(uchar visible, uchar real) { + ubyte s = player_struct.hardwarez_status[SHIELD_IDX]; + if (real) { + if (s & WARE_ON) { + set_shield_raisage(TRUE); + play_digi_fx(SFX_SHIELD_UP, 1); + } else { + set_shield_raisage(FALSE); + play_digi_fx(SFX_SHIELD_DOWN, 1); + } + mfd_notify_func(MFD_SHIELD_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + } + shield_set_absorb(); +} + +uchar shield_change_setting_hkey(ushort key, uint32_t context, intptr_t data) { + int n = CPTRIP(SHIELD_HARD_TRIPLE); + int v = player_struct.hardwarez[n]; + uint32_t s = player_struct.hardwarez_status[n]; + uchar on = s & WARE_ON; + void mfd_shield_setting(int setting); + + // version 4 has only one setting. + if (v == 4) + v = 1; + + s = LAMP_SETTING(s); + if (s == 0 && on) { + use_ware(WARE_HARD, n); + mfd_notify_func(MFD_SHIELD_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + return TRUE; + } + + s = (s + v - 1) % v; // decrement current setting + mfd_shield_setting(s); + + if (!on) + use_ware(WARE_HARD, n); + mfd_notify_func(MFD_SHIELD_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, FALSE); + + return TRUE; +} + +//---------------- +// NAV WARE +//---------------- +void nav_turnon(uchar visible, uchar real_start); +void nav_turnoff(uchar visible, uchar real_start); + +void nav_turnon(uchar visible, uchar real_s) { + if (visible) + hud_set(HUD_COMPASS); +} + +void nav_turnoff(uchar visible, uchar real_s) { + if (visible) { + hud_unset(HUD_COMPASS); + } +} + +//---------------------- +// MOTION WARE +//---------------------- +void motionware_update(uchar visible, uchar real, uchar on); +void motionware_turnon(uchar visible, uchar real); +void motionware_turnoff(uchar visible, uchar real); + +ubyte motionware_mode = MOTION_INACTIVE; + +#define MOTION_SETTING LAMP_SETTING + +void motionware_update(uchar visible, uchar real, uchar on) { + ubyte s = player_struct.hardwarez_status[CPTRIP(MOTION_HARD_TRIPLE)]; + if (on) + motionware_mode = MOTION_SETTING(s) + 1; + else + motionware_mode = MOTION_INACTIVE; + if (visible) { + Pelvis elvis; + mfd_notify_func(MFD_MOTION_FUNC, MFD_ITEM_SLOT, FALSE, MFD_ACTIVE, TRUE); + EDMS_get_pelvis_parameters(PLAYER_PHYSICS, &elvis); + if (motionware_mode == MOTION_SKATES) { + if (!global_fullmap->cyber) { + elvis.cyber_space = PELVIS_MODE_SKATES; + } + } else { + if (!global_fullmap->cyber) { + elvis.cyber_space = PELVIS_MODE_NORMAL; + } + } + EDMS_set_pelvis_parameters(PLAYER_PHYSICS, &elvis); + } +} + +void motionware_turnon(uchar visible, uchar real) { motionware_update(visible, real, TRUE); } + +void motionware_turnoff(uchar visible, uchar real) { motionware_update(visible, real, FALSE); } + + +static short jumpjet_controls[] = {-25, -50, -75}; +static fix jumpjet_thrust_scales[] = {FIX_UNIT / 64, FIX_UNIT / 32, FIX_UNIT / 16}; + +uchar jumpjets_active = FALSE; + +// modifies z control based on jumpject ware. +void activate_jumpjets(fix *xcntl, fix *ycntl, fix *zcntl) { + int ecost; + short edrain; + + ubyte n = CPTRIP(JET_HARD_TRIPLE); + ubyte v = player_struct.hardwarez[n]; + ubyte s = player_struct.hardwarez_status[n]; + + jumpjets_active = FALSE; + if ((s & WARE_ON) == 0 || player_struct.energy == 0) + return; + ecost = energy_cost_vec[n][v - 1] * player_struct.deltat + player_struct.jumpjet_energy_fraction; + player_struct.jumpjet_energy_fraction = ecost % APPROX_CIT_CYCLE_HZ; + ecost /= APPROX_CIT_CYCLE_HZ; + edrain = drain_energy(ecost); + *zcntl = fix_make(jumpjet_controls[v - 1], 0); + if (edrain < ecost) + *zcntl = (*zcntl) * edrain / ecost; + *ycntl = fix_mul(*ycntl, jumpjet_thrust_scales[v - 1]); + *xcntl = 0; + jumpjets_active = TRUE; +} + +//----------------------- +// FULLSCREEN WARE +//----------------------- +void fullscreen_turnon(uchar visible, uchar real_start); +void fullscreen_turnoff(uchar visible, uchar real_start); +extern bool DoubleSize; + +void fullscreen_turnon(uchar visible, uchar real_s) { + if (visible) { + _new_mode = FULLSCREEN_LOOP; + chg_set_flg(GL_CHG_LOOP); + } +} + +void fullscreen_turnoff(uchar visible, uchar real_s) { + if (visible) { + _new_mode = GAME_LOOP; + chg_set_flg(GL_CHG_LOOP); + } +} + +//----------------------- +// CYBERSPACE ONESHOTS +//----------------------- + +void do_turbo_stuff(uchar from_drug) { + if (cspace_effect_times[CS_TURBO_EFF] == 0) { + if (from_drug) { + hud_set(HUD_TURBO); + chg_set_flg(INVENTORY_UPDATE); + } + } +} + +void turbo_turnon(uchar visible, uchar real_start) { + ulong hammer_time = cspace_effect_durations[CS_TURBO_EFF]; + do_turbo_stuff(visible); + if (real_start) { + play_digi_fx(SFX_TURBO, 1); + player_struct.softs.misc[SOFTWARE_TURBO]--; + cspace_effect_times[CS_TURBO_EFF] = player_struct.game_time + hammer_time; + } +} + +void turbo_turnoff(uchar visible, uchar real_s) { + if (visible) { + hud_unset(HUD_TURBO); + } + cspace_effect_times[CS_TURBO_EFF] = 0; +} + +void fakeid_turnon(uchar visible, uchar real_start) { + if (!(player_struct.hud_modes & HUD_FAKEID) && visible) { + if (real_start) { + player_struct.softs.misc[SOFTWARE_FAKEID]--; + play_digi_fx(SFX_FAKEID, 1); + } + hud_set(HUD_FAKEID); + chg_set_flg(INVENTORY_UPDATE); + } +} + +void decoy_turnon(uchar visible, uchar real_start) { + if (real_start) { + if (cspace_decoy_obj != OBJ_NULL) + decoy_turnoff(TRUE, TRUE); + cspace_decoy_obj = obj_create_base(TARGET_TRIPLE); + if (cspace_decoy_obj != OBJ_NULL) { + obj_move_to(cspace_decoy_obj, &objs[PLAYER_OBJ].loc, FALSE); + cspace_effect_times[CS_DECOY_EFF] = player_struct.game_time + cspace_effect_durations[CS_DECOY_EFF]; + player_struct.softs.misc[SOFTWARE_DECOY]--; + play_digi_fx(SFX_DECOY, 1); + hud_set(HUD_DECOY); + chg_set_flg(INVENTORY_UPDATE); + } + } +} + +void decoy_turnoff(uchar visible, uchar real_stop) { + if (visible) { + hud_unset(HUD_DECOY); + } + cspace_effect_times[CS_DECOY_EFF] = 0; + if (real_stop) { + if (cspace_decoy_obj != OBJ_NULL) + ADD_DESTROYED_OBJECT(cspace_decoy_obj); + } + cspace_decoy_obj = OBJ_NULL; +} + +void recall_turnon(uchar visible, uchar real_start) { + if (visible && real_start) { + player_struct.softs.misc[SOFTWARE_RECALL]--; + obj_move_to(PLAYER_OBJ, &recall_objloc, TRUE); + chg_set_flg(INVENTORY_UPDATE); + play_digi_fx(SFX_RECALL, 1); + } +} + +// ================= +// THE STATIC ARRAYS + +WARE HardWare[NUM_HARDWAREZ] = { + //"infrared" + {WARE_FLAGS_NONE, SI_SIXTH, infrared_turnon, NULL, infrared_turnoff, NULL}, + //"target info" + {WARE_FLAGS_NONE, SI_NONE, targeting_turnon, NULL, targeting_turnoff, NULL}, + //"360 view" + {WARE_FLAGS_NONE, SI_THIRD, view360_turnon, NULL, view360_turnoff, view360_check}, + //"aim" + {WARE_FLAGS_NONE, SI_NONE, NULL, NULL, NULL, NULL}, + //"HUD" + {WARE_FLAGS_NONE, SI_NONE, NULL, NULL, NULL, NULL}, + //"bioscan" + {WARE_FLAGS_NONE, SI_FIRST, bioware_turnon, bioware_effect, bioware_turnoff, NULL}, + //"nav unit" + {WARE_FLAGS_NONE, SI_SEVENTH, nav_turnon, NULL, nav_turnoff, NULL}, + //"shield" + {WARE_FLAGS_NONE, SI_FIFTH, shield_toggle, NULL, shield_toggle, NULL}, + //"data reader" + {WARE_FLAGS_NONE, SI_EIGHTH, email_turnon, NULL, email_turnoff, NULL}, + //"lantern" + {WARE_FLAGS_NONE, SI_FOURTH, lamp_turnon, NULL, lamp_turnoff, NULL}, + //"fullscreen" + {WARE_FLAGS_NONE, SI_SECOND, fullscreen_turnon, NULL, fullscreen_turnoff, NULL}, + //"enviro-suit" + {WARE_FLAGS_NONE, SI_NONE, NULL, NULL, NULL, NULL}, + //"motion" + {WARE_FLAGS_NONE, SI_NINTH, motionware_turnon, NULL, motionware_turnoff, NULL}, + //"skates" + {WARE_FLAGS_NONE, SI_TENTH, NULL, NULL, NULL, NULL}, + //"status" + {WARE_FLAGS_NONE, SI_NONE, plotware_turnon, NULL, NULL, NULL}, +}; + +// Except for jumpjets, these costs are in points per +// minute of use. + +short energy_cost_vec[NUM_HARDWAREZ][MAX_VERSIONS] = { + //"infrared" + {50, 50, 50}, + //"target info" + {0}, + //"360 view" + {9, 9, 9}, + //"aim" + {0}, + //"HUD" + {0}, + //"bioscan" + {1}, + //"nav unit" + {0}, + //"shield" + {24, 60, 105, 30}, + //"data reader" + {0}, + //"lantern" + // { 15, 25, 30, }, + {10, 25, 30}, + //"robo comm" + {0}, + //"enviro-suit" + {0}, + //"motion" + {0, 40}, + //"jumpjets" + // these are in points per second of thrust + {25, 30, 35}, + //"status" + {0}, +}; + +bool check_game(void) { return (!global_fullmap->cyber); } + +WARE Combat_SoftWare[NUM_COMBAT_SOFTS]; +WARE Defense_SoftWare[NUM_DEFENSE_SOFTS]; +WARE Misc_SoftWare[NUM_MISC_SOFTS] = { + {WARE_FLAGS_NONE, SI_NONE, turbo_turnon, NULL, NULL, NULL}, + {WARE_FLAGS_NONE, SI_NONE, fakeid_turnon, NULL, NULL, NULL}, + {WARE_FLAGS_NONE, SI_NONE, decoy_turnon, NULL, decoy_turnoff, NULL}, + {WARE_FLAGS_NONE, SI_NONE, recall_turnon, NULL, NULL, NULL}, + {WARE_FLAGS_NONE, SI_NONE, mfd_games_turnon, NULL, mfd_games_turnoff, check_game}, +}; diff --git a/engine/src/GameSrc/weapons.c b/engine/src/GameSrc/weapons.c new file mode 100644 index 0000000..5074d01 --- /dev/null +++ b/engine/src/GameSrc/weapons.c @@ -0,0 +1,1500 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/weapons.c $ + * $Revision: 1.186 $ + * $Author: xemu $ + * $Date: 1994/11/23 22:50:30 $ + * + */ + +#define __WEAPONS_SRC + +#include + +#include "weapons.h" +#include "damage.h" +#include "hand.h" +#include "hudobj.h" +#include "objclass.h" +#include "objprop.h" +#include "objwpn.h" +#include "player.h" +#include "sdl_events.h" +#include "gameloop.h" +#include "objsim.h" +#include "gamestrn.h" +#include "newmfd.h" +#include "mfdfunc.h" +#include "musicai.h" +#include "rendtool.h" +#include "combat.h" +#include "sfxlist.h" +#include "tools.h" +#include "effect.h" +#include "schedule.h" +#include "otrip.h" +#include "mainloop.h" +#include "game_screen.h" +#include "fullscrn.h" +#include "physics.h" +#include "physunit.h" +#include "cybrnd.h" +#include "softdef.h" +#include "colors.h" +#include "hud.h" +#include "cybstrng.h" +#include "frtypes.h" +#include "doorparm.h" +#include "gr2ss.h" +#ifdef STEREO_SUPPORT +#include +#include +#endif + +#define MIN_ENERGY_WPN_THRESHOLD 20 +#define FATIGUE_ACCURACY_RATIO 400 + +extern bool DoubleSize; + +// char ammo_type_letters[] = "stnths mphssb "; + +uchar muzzle_fire_light; +short mouse_attack_x = -1; +short mouse_attack_y = -1; + +//---------------- +// Internal Prototypes +//---------------- +void weapon_properties(int triple, ubyte *damage_modifier, ubyte *offense); +ObjID do_effect_fix(ObjID owner, ubyte effect, ubyte start, Combat_Pt effect_point, short location); +ObjID do_wall_hit(Combat_Pt *hit_point, Combat_Pt pt, int triple, short mouse_x, short mouse_y, uchar do_effect); +uchar player_fire_handtohand(LGPoint *pos, ubyte slot, ObjID *what_hit, int gun_triple); +uchar decrease_ammo(ubyte slot, int shots); +uchar player_fire_projectile(LGPoint *pos, LGRegion *r, ubyte slot, int gun_triple); +uchar weapon_energy_drain(weapon_slot *ws, ubyte charge, ubyte max_charge); +uchar player_fire_energy(LGPoint *pos, ubyte slot, int gun_triple); +uchar player_fire_slow_projectile_weapon(LGPoint *pos, ubyte slot, int gun_triple); +uchar player_fire_energy_proj(LGPoint *pos, ubyte slot, int gun_triple); +uchar fire_player_software(LGPoint *pos, LGRegion *r, uchar pull); +void check_temperature(weapon_slot *ws, uchar clear); +uchar reload_current_weapon(void); +uchar reload_weapon_hotkey(ushort keycode, uint32_t context, intptr_t data); + +// ------------------------------------------------- +// does_weapon_overload() +// +uchar does_weapon_overload(int type, int subtype) { + switch (type) { + case (GUN_SUBCLASS_BEAM): + return (TRUE); + break; + case (GUN_SUBCLASS_PISTOL): + case (GUN_SUBCLASS_AUTO): + case (GUN_SUBCLASS_SPECIAL): + case (GUN_SUBCLASS_HANDTOHAND): + case (GUN_SUBCLASS_BEAMPROJ): + default: + return (FALSE); + break; + } +} + +// ----------------------------------------------------------------- +// get_weapon_name() +// + +char *get_weapon_name(int type, int subtype, char *buf) { + char name[50]; + + if ((type >= MAX_WEAPON_TYPE) || (subtype >= MAX_WEAPON_SUBTYPE)) { + return (NULL); + } + + get_object_short_name(MAKETRIP(CLASS_GUN, type, subtype), name, 50); + strcpy(buf, name); + return buf; +} + +// -------------------------------------------------------------- +// get_weapon_long_name() +// + +char *get_weapon_long_name(int type, int subtype, char *buf) { + char name[50]; + + if ((type >= MAX_WEAPON_TYPE) || (subtype >= MAX_WEAPON_SUBTYPE)) { + return (NULL); + } + + get_object_long_name(MAKETRIP(CLASS_GUN, type, subtype), name, 50); + strcpy(buf, name); + + return buf; +} + +// --------------------------------------------------------------------- +// weapon_properties() +// + +void weapon_properties(int triple, ubyte *damage_modifier, ubyte *offense) { + int wpn_class = TRIP2CL(triple); + + switch (wpn_class) { + case (CLASS_GUN): // Beam weapon is the only gun with damage type + *damage_modifier = BeamGunProps[SCTRIP(triple)].damage_modifier; + *offense = BeamGunProps[SCTRIP(triple)].offense_value; + break; + case (CLASS_PHYSICS): + *damage_modifier = AmmoProps[CPTRIP(triple)].damage_modifier; + *offense = AmmoProps[CPTRIP(triple)].offense_value; + break; + case (CLASS_GRENADE): + *damage_modifier = GrenadeProps[CPTRIP(triple)].damage_modifier; + *offense = GrenadeProps[CPTRIP(triple)].offense_value; + break; + } +} + +// ------------------------------------------------------------------------- +// set_beam_weapon_max_charge() +// + +void set_beam_weapon_max_charge(ubyte index, ubyte max_charge) { + if (max_charge < MIN_ENERGY_USE) + max_charge = MIN_ENERGY_USE; + player_struct.weapons[index].setting = max_charge; + mfd_notify_func(MFD_WEAPON_FUNC, MFD_WEAPON_SLOT, FALSE, MFD_ACTIVE, TRUE); + + return; +} + +// ------------------------------------------------------- +// do_effect_fix() +// + +ObjID do_effect_fix(ObjID owner, ubyte effect, ubyte start, Combat_Pt effect_point, short location) { + ObjLoc loc; + + loc.x = obj_coord_from_fix(effect_point.x); + loc.y = obj_coord_from_fix(effect_point.y); + loc.z = obj_height_from_fix(effect_point.z); + return (do_special_effect_location(owner, effect, start, &loc, location)); + return OBJ_NULL; +} + +// --------------------------------------------------------------- +// +// do_wall_hit() +// + +ObjID do_wall_hit(Combat_Pt *hit_point, Combat_Pt pt, int triple, short mouse_x, short mouse_y, uchar do_effect) { + ubyte effect = 0; + ObjID id = OBJ_NULL; + ObjID efft; + + // you know this should all be changed, so change it when + // fr_get_at_raw_real is done + id = fr_get_at_raw(_current_fr_context, mouse_x, mouse_y, FALSE, FALSE); + if (id < 0) + id = OBJ_NULL; + else { + ObjID obj_trans; + + obj_trans = fr_get_at_raw(_current_fr_context, mouse_x, mouse_y, FALSE, TRUE); + + if (objs[id].obclass == CLASS_DOOR) { + if (!DOOR_REALLY_CLOSED(id)) + id = obj_trans; + } else + id = obj_trans; + + if (id < 0) + id = OBJ_NULL; + // else if (ObjProps[OPNUM(target)].render_type!=1) // don't do this if the object isn't a 3d model + // else if(objs[id].class==CLASS_CRITTER) // let edms raycast deal with hitting critters + else if (objs[id].info.ph >= 0) // physics object can be dealt with the edms raycast system + id = OBJ_NULL; + } + if (id == OBJ_NULL) { + switch (TRIP2CL(triple)) { + case (CLASS_GUN): // Beam weapon is the only gun with damage type + effect = (TRIP2SC(triple) == GUN_SUBCLASS_HANDTOHAND) ? 0 : BEAM_HIT_WALL; + break; + case (CLASS_AMMO): + effect = BULL_HIT_WALL; + break; + case (CLASS_GRENADE): + effect = 0; + break; + } + // make sure that we should do an effect, and the hit point is in the world + if (do_effect && effect && (hit_point->x > 0) && (hit_point->y > 0) && (hit_point->z > 0)) { + efft = do_effect_fix(OBJ_NULL, effect, 0xFF, *hit_point, 0); + + // set the special beam weapon effect voodoo + if (efft && (TRIP2CL(triple) == CLASS_GUN)) { + if (TRIP2SC(triple) == GUN_SUBCLASS_BEAM) { + extern ObjID beam_effect_id; + beam_effect_id = efft; + hudobj_set_id(beam_effect_id, TRUE); + } + } + } + } + + return (id); +} + +#define NO_KNOCKBACK_MASS fix_make(0, 0x2000) +#define NO_KNOCKBACK_SPEED fix_make(1, 0) + +#define HAND2HANDX (SCREEN_VIEW_X + (SCREEN_VIEW_WIDTH / 2)) +#define HAND2HANDY (SCREEN_VIEW_Y + (SCREEN_VIEW_HEIGHT / 2)) +#define FULLHAND2HANDY (FULL_VIEW_Y + ((FULL_VIEW_HEIGHT * 2) / 3)) + //#define HAND2HANDY2 (SCREEN_VIEW_Y+SCREEN_VIEW_HEIGHT-3) + +#define HITLOCS 3 + +#define HAND_OFFSET (fix_make(0, 0x4000)) +#define HAND_Z_OFFSET (fix_make(0, 0x3000)) + +#define HAND_X_DELTA 15 + +// Point hand_locations[HITLOCS] = {{HAND2HANDX-25,HAND2HANDY2},{HAND2HANDX+25,HAND2HANDY2}, +// {HAND2HANDX-25, HAND2HANDY}, {HAND2HANDX+25, HAND2HANDY}}; + +// --------------------------------------------------------------------------- +// player_fire_handtohand() +// + +uchar player_fire_handtohand(LGPoint *p, ubyte slot, ObjID *what_hit, int gun_triple) { + fix attack_mass; + fix range; + + State new_state; // used to get physics state + + Combat_Pt view_pt; // used to calculate where to shot from + Combat_Pt body_pt; + + Combat_Pt vector; + Combat_Pt origin; + Combat_Pt save_origin; + Combat_Pt hit_point; + + ObjID target; + LGPoint hand_pos; + extern ubyte handart_count; + byte i; + uchar dead; + uchar hit_wall = FALSE; + + *what_hit = OBJ_NULL; + + // let's get the range of the weapon + range = fix_make(HandtohandGunProps[SCTRIP(gun_triple)].attack_range, 0); + + // get the viewpoint of the player + // EDMS_get_pelvic_viewpoint(objs[PLAYER_OBJ].info.ph, &new_state); + get_phys_state(objs[PLAYER_OBJ].info.ph, &new_state, PLAYER_OBJ); + view_pt.x = new_state.X; + view_pt.y = new_state.Y; + view_pt.z = new_state.Z; + + // get the body point of the player + EDMS_get_state(objs[PLAYER_OBJ].info.ph, &new_state); + body_pt.x = new_state.X; + body_pt.y = new_state.Y; + body_pt.z = new_state.Z; + + // find the actual place to fire from + origin.x = (view_pt.x + body_pt.x) / 2; + origin.y = (view_pt.y + body_pt.y) / 2; + origin.z = (view_pt.z + body_pt.z) / 2; + save_origin = origin; + + // let's iterate through the three attempts we're going to do + for (i = (HITLOCS - 1); i >= 0; i--) { + // restore the origin of the attack - this is because + // raycast changes the origin + origin = save_origin; + + // find the firing vector + if (i) + hand_pos.x = (i % 2) ? (HAND2HANDX + HAND_X_DELTA) : (HAND2HANDX - HAND_X_DELTA); + else + hand_pos.x = HAND2HANDX; + + hand_pos.y = (full_game_3d) ? FULLHAND2HANDY : HAND2HANDY; + if (DoubleSize) + hand_pos.y = SCONV_Y(hand_pos.y) >> 1; + else + ss_point_convert(&(hand_pos.x), &(hand_pos.y), FALSE); + find_fire_vector(&hand_pos, &vector); + + // shift the origin point for the last attack which is right + // in front of you + if (!i) + origin.z = view_pt.z; + + // do the actual raycast + target = ray_cast_vector(PLAYER_OBJ, &origin, vector, NO_KNOCKBACK_MASS, RAYCAST_ATTACK_SIZE, + NO_KNOCKBACK_SPEED, range); + + // check if we hit something on the wall + if (target == OBJ_NULL) { + target = do_wall_hit(&origin, vector, gun_triple, hand_pos.x, hand_pos.y, (i == 0)); + + // check the distance! + if (target != OBJ_NULL) { + fix dist; + fix temp; + temp = fix_from_obj_coord(objs[target].loc.x) - body_pt.x; + dist = fix_mul(temp, temp); + temp = fix_from_obj_coord(objs[target].loc.y) - body_pt.y; + dist += fix_mul(temp, temp); + temp = fix_from_obj_height(target) - body_pt.z; + dist += fix_mul(temp, temp); + if (dist > fix_mul(range, range)) + target = OBJ_NULL; + } + } + + // if we've either hit the wall or we've hit an object - remember we've hit something here + if (((origin.x > 0) && (origin.y > 0)) || (target != OBJ_NULL)) { + hit_point = origin; + + if (target != OBJ_NULL) + break; // exit out of loop! + else + hit_wall = TRUE; + } + } + + // so we've hit an object - let's do it damage + if (target != OBJ_NULL) { + ubyte val = HandtohandGunProps[SCTRIP(gun_triple)].energy_use; + int use; + + if (val && !player_struct.energy) { + use = 0; + message_info(get_temp_string(REF_STR_NoEnergyWeapon)); + } else + use = (val) ? ((drain_energy(val) * 100L) / val) : 100; + + player_struct.num_hits++; + dead = player_attack_object(target, gun_triple, use, origin); + *what_hit = target; + } + + // okay - if we've hit an object - let's do a raycast, do simulate the effect of hitting an object + if (hit_wall || (target != OBJ_NULL)) { + // use the original origin + origin = save_origin; + + // let's simulate now! + attack_mass = fix_make(HandtohandGunProps[SCTRIP(gun_triple)].attack_mass, 0) * 20; + ray_cast_vector(PLAYER_OBJ, &origin, vector, attack_mass, RAYCAST_ATTACK_SIZE, + fix_make(HandtohandGunProps[SCTRIP(gun_triple)].attack_speed, 0), + fix_make(HandtohandGunProps[SCTRIP(gun_triple)].attack_range, 0)); + + // see - if we hit an object - the hitting of the object will do an effect + // so if we hit a wall - let's do the DARN sound effect + if (target == OBJ_NULL) { + do_effect_fix(PLAYER_OBJ, IMPACT, 0xFF, hit_point, 0); + + switch (gun_triple) { + case BATON_TRIPLE: + play_digi_fx(SFX_GUN_PIPE_HIT_METAL, 1); + break; + case LASERAPIER_TRIPLE: + play_digi_fx(SFX_GUN_LASEREPEE_HIT, 1); + break; + } + } + } else { + switch (gun_triple) { + case BATON_TRIPLE: + play_digi_fx(SFX_GUN_PIPE_MISS, 1); + break; + case LASERAPIER_TRIPLE: + play_digi_fx(SFX_GUN_LASEREPEE_MISS, 1); + break; + } + } + + handart_count &= (~0x80); + return (TRUE); +} + +// ----------------------------------------------------------------- +// decrease_ammo() +// +// returns FALSE if we don't have as many shots as we want to remove. +// else removes them and returns TRUE. + +uchar decrease_ammo(ubyte slot, int shots) { + if (player_struct.weapons[slot].ammo == 0) { + weapon_mfd_for_reload(); + return FALSE; + } + + player_struct.weapons[slot].ammo -= shots; + + if (player_struct.weapons[slot].ammo == 0) + weapon_mfd_for_reload(); + + return TRUE; +} + +// --------------------------------------------------------------------------- +// player_fire_projectile +// +// decrements the current_magazine - returns false iff there is no more +// ammo - (make click noise, if this is true???) +// we need to set something in the "bullet" so that we know the "owner" +// so we can keep track of hits by the player. + +//#define BRIGHT_LIGHT_FLASH 50 + +uchar player_fire_projectile(LGPoint *pos, LGRegion *r, ubyte slot, int gun_triple) { + int ammo_triple; + int ammo_subclass; + fix bullet_mass; + Combat_Pt vector; + Combat_Pt origin; + ObjID target; + State new_state; + + ammo_subclass = AMMOTYPE_SUBCLASS(GunProps[CPTRIP(gun_triple)].useable_ammo_type); + ammo_triple = MAKETRIP(CLASS_AMMO, ammo_subclass, player_struct.weapons[slot].ammo_type); + + // decrement the ammo count - unless we're out of ammo + if (!decrease_ammo(slot, 1)) + return FALSE; + + // If we finished a cartridge, throw it away + // if (player_struct.weapons[slot].ammo == 0) + // player_struct.weapons[slot].ammo_type = EMPTY_WEAPON_SLOT; + + get_phys_state(objs[PLAYER_OBJ].info.ph, &new_state, PLAYER_OBJ); + origin.x = new_state.X; + origin.y = new_state.Y; + origin.z = new_state.Z; + + find_fire_vector(pos, &vector); + bullet_mass = fix_make(AmmoProps[CPTRIP(ammo_triple)].bullet_mass, 0) * 30; + + target = ray_cast_vector(PLAYER_OBJ, &origin, vector, bullet_mass, RAYCAST_ATTACK_SIZE, + fix_make(AmmoProps[CPTRIP(ammo_triple)].bullet_speed, 0), + fix_make(AmmoProps[CPTRIP(ammo_triple)].range, 0)); + + // check if we hit something on the wall + if (target == OBJ_NULL) + target = do_wall_hit(&origin, vector, ammo_triple, pos->x, pos->y, TRUE); + + if (target != OBJ_NULL) { + player_struct.num_hits++; + player_attack_object(target, ammo_triple, 100, origin); + } + + // Modify cursor position because of ammo's recoil (Done after shot is fired) + randomize_cursor_pos(pos, r, + AmmoProps[CPTRIP(ammo_triple)].recoil_force + player_struct.fatigue / FATIGUE_ACCURACY_RATIO); + + mfd_notify_func(MFD_WEAPON_FUNC, MFD_WEAPON_SLOT, FALSE, MFD_ACTIVE, FALSE); + chg_set_flg(INVENTORY_UPDATE); + return TRUE; +} + +ubyte energy_expulsion = 0; +uchar overload_beam = FALSE; + +// ---------------------------------------------------------- +// weapon_energy_drain() +// +// deals with heat/settings/charge of an energy weapon +// + +uchar weapon_energy_drain(weapon_slot *ws, ubyte charge, ubyte max_charge) { + ubyte energy_used; + + if (ws->heat >= OVERHEAT_THRESHOLD) { + message_info(get_temp_string(REF_STR_GunTooHot)); + return (FALSE); + } + if (player_struct.energy < MIN_ENERGY_WPN_THRESHOLD) { + message_info(get_temp_string(REF_STR_NoEnergyFireWeapon)); + return (FALSE); + } + + // find out which is smaller, the charge setting, or the amount of "coolness" in the weapon + charge = lg_min(charge, (MAX_HEAT - ws->heat)); + if (OVERLOAD_VALUE(ws->setting)) // if we are overloaded - double it! + { + overload_beam = TRUE; + charge = (MAX_HEAT * 2); + } else + overload_beam = FALSE; + + energy_used = (ubyte)((((int)max_charge) * charge) / 100); + + // energy_expulsion is used for lighting values + energy_expulsion = charge; + drain_energy(energy_used); + + // use up remaining energy if energy_used was more than energy left, and then + // find out what the setting would have been with the left over energy + // add the heat from the shot to the weapon + if ((charge / 2) > (0xFF - ws->heat)) { + charge = (0xFF - ws->heat) * 2; + ws->heat = 0xFF; + } else + ws->heat += (charge / 2); + + return (TRUE); +} + +// ------------------------------------------------------------------------------- +// player_fire_energy() +// +// Fires the energy weapon in the specified slot returns true +// iff the weapon actually got a shot off. + +uchar player_fire_energy(LGPoint *pos, ubyte slot, int gun_triple) { + Combat_Pt vector; + Combat_Pt origin; + ObjID target; + fix bullet_mass; + State new_state; + weapon_slot *ws = &player_struct.weapons[slot]; + + // get the charge setting + ubyte charge = OVERLOAD_VALUE(ws->setting) ? (MAX_HEAT) : ws->setting; + + if (!weapon_energy_drain(ws, charge, BeamGunProps[SCTRIP(gun_triple)].max_charge)) + return FALSE; + + chg_set_flg(VITALS_UPDATE); + + // do the ray cast here + get_phys_state(objs[PLAYER_OBJ].info.ph, &new_state, PLAYER_OBJ); + origin.x = new_state.X; + origin.y = new_state.Y; + origin.z = new_state.Z; + + find_fire_vector(pos, &vector); + bullet_mass = fix_make(BeamGunProps[SCTRIP(gun_triple)].attack_mass, 0) * 30; + + target = ray_cast_vector(PLAYER_OBJ, &origin, vector, bullet_mass, RAYCAST_ATTACK_SIZE, + fix_make(BeamGunProps[SCTRIP(gun_triple)].attack_speed, 0), + fix_make(BeamGunProps[SCTRIP(gun_triple)].attack_range, 0)); + + // did we hit something??? + if (target == OBJ_NULL) + target = do_wall_hit(&origin, vector, gun_triple, pos->x, pos->y, TRUE); + + if (target != OBJ_NULL) { + player_struct.num_hits++; + player_attack_object(target, gun_triple, charge, origin); + } + + // deal with overload + OVERLOAD_RESET(ws->setting); + + mfd_force_update(); // need to do this because of OVERLOAD + chg_set_flg(INVENTORY_UPDATE); + + return (TRUE); +} + +#define SLOW_PROJECTILE_DURATION 1000 +#define SLOW_PROJECTILE_GRAVITY fix_make(0, 0x0C00) +#define SLOW_PROJ_SPEED 6 +#define PROJ_RAYCAST_RANGE fix_make(0, 0x6000) + +// --------------------------------------------------------------------------- +// player_fire_slow_projectile +// + +uchar player_fire_slow_projectile(int proj_triple, int fire_triple, fix proj_mass, fix fire_spd, ubyte proj_speed, + LGPoint *pos); + +uchar player_fire_slow_projectile_weapon(LGPoint *pos, ubyte slot, int gun_triple) { + ubyte proj_speed = SpecialGunProps[SCTRIP(gun_triple)].speed; + + // decrement the ammo count - unless we're out of ammo + decrease_ammo(slot, 1); + return (player_fire_slow_projectile(SpecialGunProps[SCTRIP(gun_triple)].proj_triple, gun_triple, + fix_make(SpecialGunProps[SCTRIP(gun_triple)].attack_mass, 0) * 20, + fix_make(SpecialGunProps[SCTRIP(gun_triple)].attack_speed, 0), proj_speed, + pos)); +} + +// here we actually generate the slow projectile and send it on it's way +// note that this actually takes in the relevant low-level data, so that +// the source of the shot need not be a Weapon(tm) but can be a software, etc... -- Rob + +extern ubyte old_head, old_pitch; + +uchar player_fire_slow_projectile(int proj_triple, int fire_triple, fix proj_mass, fix fire_spd, ubyte proj_speed, + LGPoint *pos) { + Combat_Pt vector; + Combat_Pt origin; + State new_state; + ObjID proj_id; + ObjSpecID osid; + ObjLoc loc; + Robot da_robot; + LGPoint new_pos; + physics_handle obj_ph; + + fix dist; + ubyte head; + + proj_id = obj_create_base(proj_triple); + if (proj_id == OBJ_NULL) { + return (OK); + } + osid = objs[proj_id].specID; + + objPhysicss[osid].owner = PLAYER_OBJ; + objPhysicss[osid].bullet_triple = fire_triple; + objPhysicss[osid].duration = player_struct.game_time + SLOW_PROJECTILE_DURATION; + objPhysicss[osid].p3.x = objPhysicss[osid].p3.y = 0; + + new_pos = *pos; + new_pos.y -= 4; + if (global_fullmap->cyber) { + // shift zero to middle of screen + new_pos.x -= + (((fauxrend_context *)_current_fr_context)->xtop + (((fauxrend_context *)_current_fr_context)->xwid >> 1)); + new_pos.y -= + (((fauxrend_context *)_current_fr_context)->ytop + (((fauxrend_context *)_current_fr_context)->ywid >> 1)); + + // shrink + new_pos.x = (new_pos.x * 3) / 4; + new_pos.y = (new_pos.y * 3) / 4; + + // shift back + new_pos.x += + (((fauxrend_context *)_current_fr_context)->xtop + (((fauxrend_context *)_current_fr_context)->xwid >> 1)); + new_pos.y += + (((fauxrend_context *)_current_fr_context)->ytop + (((fauxrend_context *)_current_fr_context)->ywid >> 1)); + } + + find_fire_vector(&new_pos, &vector); + + get_phys_state(objs[PLAYER_OBJ].info.ph, &new_state, PLAYER_OBJ); + // do the raycasting to have the kickback effect + if (fire_spd && !global_fullmap->cyber) { + origin.x = new_state.X; + origin.y = new_state.Y; + origin.z = new_state.Z; + ray_cast_vector(PLAYER_OBJ, &origin, vector, proj_mass, RAYCAST_ATTACK_SIZE, fire_spd, PROJ_RAYCAST_RANGE); + } + + // Add projectile object here and send it on its way. + loc = objs[PLAYER_OBJ].loc; + loc.x = obj_coord_from_fix(new_state.X); + loc.y = obj_coord_from_fix(new_state.Y); + loc.z = obj_height_from_fix(new_state.Z); + + // let's get the heading of the shot + head = obj_angle_from_fixang(fix_atan2(vector.y, vector.x)); + loc.h = (ubyte)((320L - head) % 256); + + // let's get the pitch + dist = fix_fast_pyth_dist(vector.x, vector.y); + loc.p = obj_angle_from_fixang(fix_atan2(vector.z, dist)); + + // for slow projectiles - we don't care about the bank + loc.b = 0; + + // let's move it a little in front of the player + if (global_fullmap->cyber) { + new_pos.x = + (((fauxrend_context *)_current_fr_context)->xwid >> 1) + ((fauxrend_context *)_current_fr_context)->xtop; + new_pos.y = + (((fauxrend_context *)_current_fr_context)->ywid >> 1) + ((fauxrend_context *)_current_fr_context)->ytop; + find_fire_vector(&new_pos, &origin); + loc.x += (obj_coord_from_fix(origin.x) / 2); + loc.y += (obj_coord_from_fix(origin.y) / 2); + loc.z += (obj_height_from_fix(origin.z) / 2); + } + + // get state gets the velocity - this from shamu the pirate! + EDMS_get_state(objs[PLAYER_OBJ].info.ph, &new_state); + + vector.x = (vector.x * proj_speed) + (obj_coord_from_fix(new_state.X_dot) * PHYSICS_RADIUS_UNIT) * 2; + vector.y = (vector.y * proj_speed) + (obj_coord_from_fix(new_state.Y_dot) * PHYSICS_RADIUS_UNIT) * 2; + vector.z = (vector.z * proj_speed) + (obj_height_from_fix(new_state.Z_dot) * PHYSICS_RADIUS_UNIT) * 2; + + obj_move_to_vel(proj_id, &loc, TRUE, vector.x, vector.y, vector.z); + apply_gravity_to_one_object(proj_id, SLOW_PROJECTILE_GRAVITY); + + obj_ph = objs[proj_id].info.ph; + + // Spew(DSRC_PHYSICS_Collisions, ("We are ignoring collisions between ph: %d and ph: + // %d\n",objs[PLAYER_OBJ].info.ph, obj_ph)); + EDMS_ignore_collisions(objs[PLAYER_OBJ].info.ph, obj_ph); + + // make it wall proof + EDMS_get_robot_parameters(obj_ph, &da_robot); + da_robot.cyber_space = -1; + EDMS_set_robot_parameters(obj_ph, &da_robot); + + mfd_force_update(); // due to overload + chg_set_flg(INVENTORY_UPDATE); + + return TRUE; +} + + // ----------------------------------------------------------------------------- + // player_fire_energy_proj() + // + +#define OVERLOAD_EXPLODE 5 +#define PROJ_X_OFFSET 20 +#define PROJ_Y_OFFSET 20 + +uchar player_fire_energy_proj(LGPoint *pos, ubyte slot, int gun_triple) { + weapon_slot *ws = &player_struct.weapons[slot]; + ubyte charge = OVERLOAD_VALUE(ws->setting) ? (MAX_HEAT) : ws->setting; + int subclass = SCTRIP(gun_triple); + + if (!weapon_energy_drain(ws, charge, BeamprojGunProps[SCTRIP(gun_triple)].max_charge)) + return FALSE; + + // the TRIPLE of the projectile will determine the "power" + player_fire_slow_projectile(BeamprojGunProps[subclass].proj_triple, MAKETRIP(charge, 0, ws->subtype), + (fix_make(BeamprojGunProps[subclass].attack_mass, 0) * 20), + fix_make(BeamprojGunProps[subclass].attack_speed, 0), + BeamprojGunProps[SCTRIP(gun_triple)].speed, pos); + + // deal with feedback of energy usage + mfd_force_update(); + chg_set_flg(INVENTORY_UPDATE); + + return TRUE; +} + +// used to decide when handart should return to screen +ulong next_fire_time = 0; + +// it decides how much to light up the world - this +// is so automatic weapons don't keep the same brightness +byte gun_fire_offset = 0; + +// --------------------------------- +// fire_player_weapon() +// +// pos - point on viewscreen (not converted to -100/+100 map coords) +// pull - whether the gun was just pulled. + +#define AUTOFIRE_SPEED 1500 +#define SOFTWARE_SPEW_FIRE_RATE 60 + +ulong software_fire_remainder = 0; + +char cspace_digi_fxs[] = {SFX_DRILL, SFX_DATASTORM, SFX_NONE, SFX_DISC, SFX_PULSER, SFX_NONE, SFX_NONE}; +int cspace_slow_projs[] = {DRILLSLOW_TRIPLE, SPEWSLOW_TRIPLE, 0, DISCSLOW_TRIPLE, CYBERSLOW_TRIPLE, 0, 0}; + +uchar fire_player_software(LGPoint *pos, LGRegion *reg, uchar pull) { + int soft = player_struct.actives[ACTIVE_COMBAT_SOFT]; + uchar retval; + char shots = 1; + + if (!player_struct.softs.combat[soft]) + return FALSE; + if (!pull) + return FALSE; + + player_struct.last_fire = player_struct.game_time; + + // Note fullwise our clever (or not so clever) usage of weapon_triple as the + // level of the ware firing the projectile. + play_digi_fx(cspace_digi_fxs[soft], 1); + switch (soft) { + case SOFTWARE_DRILL: + case SOFTWARE_PULSER: + case SOFTWARE_DISC: + case SOFTWARE_SPEW: + retval = + player_fire_slow_projectile(cspace_slow_projs[soft], player_struct.softs.combat[soft], 0, FIX_UNIT, 3, pos); + break; + } + return retval; +} + +ulong weapon_fire_remainder = 0; +uchar handart_flash = FALSE; + +// --------------------------------------------------------------------------- +// fire_player_weapon() +// + +char weapon_combat_fxs[] = { + SFX_GUN_MINIPISTOL, SFX_GUN_DARTPISTOL, SFX_GUN_MAGNUM, SFX_GUN_ASSAULT, + SFX_GUN_RIOT, SFX_GUN_FLECHETTE, SFX_GUN_SKORPION, SFX_GUN_MAGPULSE, + SFX_GUN_RAILGUN, SFX_GUN_PIPE_MISS, SFX_GUN_LASEREPEE_MISS, SFX_GUN_PHASER, + SFX_GUN_BLASTER, SFX_GUN_IONBEAM, SFX_GUN_STUNGUN, SFX_GUN_PLASMA, +}; + +ObjID damage_sound_id; +char damage_sound_fx = -1; + +#define CYBER_FIRE_WAIT 60 +#define MIN_AUTO_SHOT 3 +#define AUTO_FIRE_CLICK_WAIT 60 +#define CURSOR_WAIT 60 +#define MAX_AUTO_FIRE 8 + +uchar fire_player_weapon(LGPoint *pos, LGRegion *r, uchar pull) { + int w = player_struct.actives[ACTIVE_WEAPON]; + int shots = 1; + int i; + int gun_triple = current_weapon_trip(); + short deltax, deltay; + uchar handart_ok = TRUE; + LGPoint realpos = *pos; + LGPoint cp; + LGRect rc; + ObjID hit_obj = OBJ_NULL; + extern uchar game_paused; // prevent firing when time ain't running + extern uchar time_passes; + + if (player_struct.dead || game_paused || !time_passes) + return (FALSE); + + if (player_struct.weapons[w].type == EMPTY_WEAPON_SLOT) return FALSE; + + region_abs_rect(r, r->r, &rc); + if (DoubleSize) + rc.lr.y = SCONV_Y(rc.lr.y) >> 1; + cp = realpos; + if (!DoubleSize) + ss_mouse_convert(&(cp.x), &(cp.y), TRUE); +#ifdef STEREO_SUPPORT + if (convert_use_mode == 5) { + switch (i6d_device) { + case I6D_VFX1: + realpos.x = realpos.x << 1; + break; + } + } +#endif + if (!RECT_TEST_PT(&rc, cp)) { + //realpos.x = r->abs_x + RectWidth(r->r) / 2; + //realpos.y = r->abs_y + RectHeight(r->r) / 2; + + // ui_mouse_put_xy(realpos.x,realpos.y); + // Heck we know this is the first time you're firing. + pull = TRUE; + } + + if (global_fullmap->cyber) { + if ((CYBER_FIRE_WAIT + player_struct.last_fire) > player_struct.game_time) + return FALSE; + player_struct.last_fire = player_struct.game_time; + return (fire_player_software(&realpos, r, pull)); + } + + if ((player_struct.weapons[w].type == GUN_SUBCLASS_AUTO) && !player_struct.weapons[w].ammo) + weapon_mfd_for_reload(); + + // if we didn't just pull the gun and we have an automatic weapon or + // if we are out of ammo for an automatic weapon - snap back to normal cursor + if ((!pull && (player_struct.weapons[w].type != GUN_SUBCLASS_AUTO)) || + ((player_struct.weapons[w].type == GUN_SUBCLASS_AUTO) && !player_struct.weapons[w].ammo)) { + extern uchar fire_slam; + + if (fire_slam && (player_struct.last_fire + CURSOR_WAIT < player_struct.game_time)) { + extern uiSlab fullscreen_slab; + extern uiSlab main_slab; + + if (full_game_3d) + uiPopSlabCursor(&fullscreen_slab); + else + uiPopSlabCursor(&main_slab); + fire_slam = FALSE; + } + return FALSE; + } + + // Don't fire if we're out of ammo, and we're notfiring a beam weapon or hand-to-hand + if ((player_struct.weapons[w].type != GUN_SUBCLASS_BEAM) && + (player_struct.weapons[w].type != GUN_SUBCLASS_HANDTOHAND) && + (player_struct.weapons[w].type != GUN_SUBCLASS_BEAMPROJ) && (player_struct.weapons[w].ammo == 0)) { + weapon_mfd_for_reload(); + return FALSE; + } + + // if the trigger is being held down, fire as many shots + // as time has passed. (But not more shots than we actually have + // in our clip!) + + if (player_struct.weapons[w].type == GUN_SUBCLASS_AUTO) { + if (!pull) { + ulong deltat = + (player_struct.game_time - player_struct.last_fire) * player_struct.fire_rate + weapon_fire_remainder; + + if ((player_struct.auto_fire_click + AUTO_FIRE_CLICK_WAIT) > player_struct.game_time) + return FALSE; + + shots = deltat / AUTOFIRE_SPEED; + + shots = lg_min(lg_min(shots, player_struct.weapons[w].ammo), MAX_AUTO_FIRE); + weapon_fire_remainder = deltat % AUTOFIRE_SPEED; + if (shots) + gun_fire_offset = RndRange(&effect_rnd, 0, 30) - 15; + } else { + // make sure we're not just clicking very fast + if ((player_struct.auto_fire_click + AUTO_FIRE_CLICK_WAIT) > player_struct.game_time) + return FALSE; + // clear the weapon remainder - if we just pulled the trigger + weapon_fire_remainder = 0; + shots = MIN_AUTO_SHOT; + player_struct.auto_fire_click = player_struct.game_time; + } + + next_fire_time = player_struct.game_time; + } else { + if (((GunProps[CPTRIP(gun_triple)].fire_rate * 10) + player_struct.last_fire) > player_struct.game_time) + return FALSE; + next_fire_time = (10) + player_struct.game_time; + gun_fire_offset = 0; + } + if (shots) + player_struct.last_fire = player_struct.game_time; + else + return FALSE; + + deltax = mouse_attack_x = realpos.x; + deltay = mouse_attack_y = realpos.y; + switch (player_struct.weapons[w].type) { + case (GUN_SUBCLASS_PISTOL): + handart_ok = player_fire_projectile(&realpos, r, w, gun_triple); + break; + case (GUN_SUBCLASS_AUTO): + for (i = 0; i < shots; i++) { + if (!player_fire_projectile(&realpos, r, w, gun_triple)) + break; + } + break; + case (GUN_SUBCLASS_SPECIAL): + handart_ok = player_fire_slow_projectile_weapon(&realpos, w, gun_triple); + // do slow projectile + break; + case (GUN_SUBCLASS_HANDTOHAND): + handart_ok = player_fire_handtohand(&realpos, w, &hit_obj, gun_triple); + break; + case (GUN_SUBCLASS_BEAM): + handart_ok = player_fire_energy(&realpos, w, gun_triple); + break; + case (GUN_SUBCLASS_BEAMPROJ): + handart_ok = player_fire_energy_proj(&realpos, w, gun_triple); + break; + } + if (handart_ok) { + int trip; + handart_show = 2; + handart_fire = FALSE; + handart_flash = TRUE; + + if (_current_loop <= FULLSCREEN_LOOP) { + chg_set_flg(INVENTORY_UPDATE); + mfd_notify_func(MFD_WEAPON_FUNC, MFD_WEAPON_SLOT, FALSE, MFD_ACTIVE, FALSE); + } + + player_struct.rounds_fired += shots; + + // Play some sound effects!! + trip = MAKETRIP(CLASS_GUN, player_struct.weapons[w].type, player_struct.weapons[w].subtype); + switch (trip) { + case BATON_TRIPLE: + if (hit_obj != OBJ_NULL) { + if ((objs[hit_obj].obclass == CLASS_CRITTER) && (objs[hit_obj].subclass == CRITTER_SUBCLASS_MUTANT)) + play_digi_fx(SFX_GUN_PIPE_HIT_MEAT, 1); + else + play_digi_fx(SFX_GUN_PIPE_HIT_METAL, 1); + } + break; + case LASERAPIER_TRIPLE: + if (hit_obj != OBJ_NULL) + play_digi_fx(SFX_GUN_LASEREPEE_HIT, 1); + break; + default: + play_digi_fx(weapon_combat_fxs[CPTRIP(trip)], 1); + break; + } + if (damage_sound_fx != -1) { + play_digi_fx_obj(damage_sound_fx, 1, damage_sound_id); + } + damage_sound_fx = -1; + } + return (handart_ok); +} + +#ifdef SELFRUN +// ---------------------------------------------- +// demo_fire_weapon() +// + +void demo_fire_weapon(short x, short y) { + LGPoint aimpos; + + aimpos.x = x; + aimpos.y = y; + ui_mouse_put_xy(x, y); + fire_player_weapon(&aimpos, _current_view, TRUE); +} +#endif + +// --------------------------------------------------------------- +// get_available_ammo_type() +// + +void get_available_ammo_type(int guntype, int gun_subtype, int *num_ammo_types, ubyte *bitflag, int *ammo_subclass) { + int i; + int subclass; + int type; + int triple; + int count = 0; + + if (guntype == GUN_SUBCLASS_BEAM || guntype == GUN_SUBCLASS_HANDTOHAND) { + *num_ammo_types = 0; + return; + } + + triple = MAKETRIP(CLASS_GUN, guntype, gun_subtype); + type = GunProps[CPTRIP(triple)].useable_ammo_type; + subclass = *ammo_subclass = AMMOTYPE_SUBCLASS(type); + + type = AMMOTYPE_TYPE(type); + for (i = 0; i < 3; i++, type >>= 1) { + if (type & 0x01) { + triple = MAKETRIP(CLASS_AMMO, subclass, i); + if (player_struct.cartridges[CPTRIP(triple)] != 0 || player_struct.partial_clip[CPTRIP(triple)] > 0) + bitflag[count++] = i; + } + } + *num_ammo_types = count; +} + +// -------------------------------------------------------------------------------------- +// change_ammo_type() +// + +uchar change_ammo_type(ubyte ammo_type) { + weapon_slot *ws; + int subclass; + int type; + int triple; + uchar changed = FALSE; + + ws = &player_struct.weapons[player_struct.actives[ACTIVE_WEAPON]]; + triple = MAKETRIP(CLASS_GUN, ws->type, ws->subtype); + type = GunProps[CPTRIP(triple)].useable_ammo_type; + subclass = AMMOTYPE_SUBCLASS(type); + + // This needs to be fixed for the new object regime + triple = MAKETRIP(CLASS_AMMO, subclass, ammo_type); + if (player_struct.cartridges[CPTRIP(triple)] != 0) { + // Decrement the cartridge count + player_struct.cartridges[CPTRIP(triple)]--; + ws->ammo = AmmoProps[CPTRIP(triple)].cartridge_size; + changed = TRUE; + } else if (player_struct.partial_clip[CPTRIP(triple)] != 0) { + ws->ammo = player_struct.partial_clip[CPTRIP(triple)]; + player_struct.partial_clip[CPTRIP(triple)] = 0; + changed = TRUE; + } + if (changed) { + ws->ammo_type = ammo_type; + chg_set_flg(INVENTORY_UPDATE); + switch (triple) { + case PRAMMO_TRIPLE: + case MRAMMO_TRIPLE: + case HNAMMO_TRIPLE: + case SPLAMMO_TRIPLE: + play_digi_fx(SFX_RELOAD_2, 1); + break; + default: + play_digi_fx(SFX_RELOAD_1, 1); + break; + } + mfd_notify_func(MFD_WEAPON_FUNC, MFD_WEAPON_SLOT, FALSE, MFD_ACTIVE, TRUE); + return TRUE; + } else + return FALSE; +} + +//-------------------------------------------------------------------- +// unload_current_weapon() +// +// removes all ammo and stuffs it in a partial clip. + +void unload_current_weapon(void) { + weapon_slot *ws; + int subclass; + int type; + int triple; + int clipsize; + + ws = &player_struct.weapons[player_struct.actives[ACTIVE_WEAPON]]; + triple = MAKETRIP(CLASS_GUN, ws->type, ws->subtype); + type = GunProps[CPTRIP(triple)].useable_ammo_type; + subclass = AMMOTYPE_SUBCLASS(type); + triple = MAKETRIP(CLASS_AMMO, subclass, ws->ammo_type); + clipsize = AmmoProps[CPTRIP(triple)].cartridge_size; + player_struct.partial_clip[CPTRIP(triple)] += ws->ammo; + + if (clipsize <= 0) + return; + while (player_struct.partial_clip[CPTRIP(triple)] >= clipsize) { + player_struct.cartridges[CPTRIP(triple)]++; + player_struct.partial_clip[CPTRIP(triple)] -= clipsize; + } + chg_set_flg(INVENTORY_UPDATE); + ws->ammo = 0; +} + + // ---------------------------------------- + // cool_off_beam_weapons() + // + // Cool off beam weapons, duh + +#define WEAPON_COOL_OFF_TIME 25 +#define HEAT_FUDGE 2 + +uchar temp_critical = FALSE; + +void check_temperature(weapon_slot *ws, uchar clear) { + if (clear) { + if (temp_critical) + hud_unset(HUD_BEAMHOT); + temp_critical = FALSE; + } + if ((ws->type != GUN_SUBCLASS_BEAM) && (ws->type != GUN_SUBCLASS_BEAMPROJ)) + return; + + if (!OVERLOAD_VALUE(ws->setting) && ((ws->heat + (ws->setting / 2)) > (OVERHEAT_THRESHOLD + HEAT_FUDGE))) { + if (!temp_critical) + hud_set(HUD_BEAMHOT); + temp_critical = TRUE; + } else { + if (temp_critical) + hud_unset(HUD_BEAMHOT); + temp_critical = FALSE; + } +} + +void cool_off_beam_weapons(void) { + static long running_dt; + int i; + weapon_slot *ws; + + running_dt += player_struct.deltat; + while (running_dt > WEAPON_COOL_OFF_TIME) { + running_dt = + lg_max(0, running_dt - WEAPON_COOL_OFF_TIME); // must be non frame-rate dependant, even if goofily so. + + for (i = 0; i < NUM_WEAPON_SLOTS; i++) { + ws = &player_struct.weapons[i]; + + if ((ws->type == GUN_SUBCLASS_BEAM) || (ws->type == GUN_SUBCLASS_BEAMPROJ)) { + if (ws->heat > 0) { + ws->heat--; + + chg_set_flg(INVENTORY_UPDATE); + if (player_struct.actives[ACTIVE_WEAPON] == i) { + mfd_notify_func(MFD_WEAPON_FUNC, MFD_WEAPON_SLOT, FALSE, MFD_ACTIVE, TRUE); + check_temperature(ws, FALSE); + } + } + } + } + } +} + +// --------------------------------------------------------------------------- +// randomize_cursor_pos() +// +// Jerks the cursor around randomly, to a degree determined by +// a percentage which is from 0->100% + +void randomize_cursor_pos(LGPoint *cpos, LGRegion *reg, ubyte p) +{ + if (p < 2) return; + + LGRect r = *(reg->r); +#ifdef SVGA_SUPPORT + ss_mouse_convert(&(r.ul.x), &(r.ul.y), FALSE); + ss_mouse_convert(&(r.lr.x), &(r.lr.y), FALSE); +#endif + + short x, y, dx, dy; + + mouse_get_xy(&x, &y); + + dx = (rand() % (p + 1)) - (p / 2); + dy = (rand() % (p + 1)) - (p / 2); + + x += dx; + y += dy; + + if (x >= r.ul.x && x < r.lr.x && + y >= r.ul.y && y < r.lr.y) + { + mouse_put_xy(x, y); + + set_mouse_chaos(dx, dy); + } +} + + // --------------------------------------------------------------------------- + // drain_energy() + // + // takes a request for energy from the central reservoir. Returns how + // much energy was actually drained and thus given to the requesting function + +#define ENERGY_VAR_RATE 50 + +ubyte drain_energy(ubyte e) { + extern int bio_energy_var; + ubyte ret; + + if (e > player_struct.energy) { + ret = player_struct.energy; + player_struct.energy = 0; + } else { + ret = e; + player_struct.energy -= e; + } + chg_set_flg(VITALS_UPDATE); + if (ret > 0) + bio_energy_var += ret * ENERGY_VAR_RATE; + + return ret; +} + +// returns the triple of the player's currently selected weapon. +// returns -1 if no weapon is currently selected. + +int current_weapon_trip() { + weapon_slot *ws; + int slot; + + slot = player_struct.actives[ACTIVE_WEAPON]; + if (slot == EMPTY_WEAPON_SLOT) + return -1; + + ws = &player_struct.weapons[slot]; + + return (MAKETRIP(CLASS_GUN, ws->type, ws->subtype)); +} + + // CHANGING CURSOR STUFF + // + +#define NUM_MOTION_CURSORS 15 +short cursor_color_offset = RED_BASE + 4; +extern grs_bitmap motion_cursor_bitmaps[NUM_MOTION_CURSORS]; + +ubyte weapon_colors[NUM_SC_GUN] = {RED_BASE + 4, // pistol + GREEN_BASE, // auto + 0x4A, // special - yellow + 0x40, // hand-to-hand - brown + BLUE_BASE + 4, // beam + TURQUOISE_BASE + 3}; // beam proj + +extern ubyte handart_count; + +// ----------------------------------------------------------------------------- +// SetMotionCursorsColorForWeapon() +// +// modifies motion cursor bitmap color according to specified weapon +// + +void SetMotionCursorsColorForWeapon(int w) +{ + if (global_fullmap->cyber) return; + + short new_offset = RED_BASE + 4; + + if (w >= 0 && w < NUM_WEAPON_SLOTS) + { + weapon_slot *ws = &player_struct.weapons[w]; + + if (ws->type != EMPTY_WEAPON_SLOT) + new_offset = weapon_colors[ws->type]; + } + + if (new_offset != cursor_color_offset) + { + for (int i = 0; i < NUM_MOTION_CURSORS; i++) + { + uchar *bits = motion_cursor_bitmaps[i].bits; + if (bits != NULL) + { + int size = motion_cursor_bitmaps[i].w * motion_cursor_bitmaps[i].h; + for (int j = 0; j < size; j++, bits++) + { + if (*bits && !(*bits & 1)) + *bits += (new_offset - cursor_color_offset); + } + } + } + + cursor_color_offset = new_offset; + } +} + +// ----------------------------------------------------------------------------- +// SetMotionCursorsColorForActiveWeapon() +// +// modifies motion cursor bitmap color according to currently active weapon +// + +// called at end of: +// weapons_add_func() invent.c +// weapon_drop_func() invent.c +// fullscreen_start() fullscrn.c +// screen_start() screen.c + +void SetMotionCursorsColorForActiveWeapon(void) +{ + if (global_fullmap->cyber) return; + + int w = player_struct.actives[ACTIVE_WEAPON]; + SetMotionCursorsColorForWeapon(w); +} + +// ----------------------------------------------------------------------------- +// change_selected_weapon() +// +// deals with changing cursor color +// + +void change_selected_weapon(int new_weapon) { + + if (global_fullmap->cyber) + return; + + SetMotionCursorsColorForWeapon(new_weapon); + + weapon_slot *ws = &player_struct.weapons[new_weapon]; + check_temperature(ws, TRUE); + if ((ws->type == GUN_SUBCLASS_BEAM) || (ws->type == GUN_SUBCLASS_BEAMPROJ)) { + check_temperature(ws, FALSE); + } + player_struct.auto_fire_click = 0; + + // reset the handart count + handart_show = 1; + reset_handart_count(new_weapon); +} + +uchar gun_takes_ammo(int guntrip, int ammotrip) { + int type; + + type = GunProps[CPTRIP(guntrip)].useable_ammo_type; + return (AMMOTYPE_TYPE(type) != 0 && TRIP2SC(ammotrip) == AMMOTYPE_SUBCLASS(type)); +} + +uchar reload_current_weapon(void) { + weapon_slot *ws = &player_struct.weapons[player_struct.actives[ACTIVE_WEAPON]]; + int ammosc; + ubyte ammo_types[3]; + int num_types; + int i; + + // unload_current_weapon(); + // if (change_ammo_type(ws->ammo_type)) + // return TRUE; + get_available_ammo_type(ws->type, ws->subtype, &num_types, ammo_types, &ammosc); + for (i = num_types - 1; i >= 0; i--) + if (change_ammo_type(ammo_types[i])) + return TRUE; + return FALSE; +} + +uchar reload_weapon_hotkey(ushort key, uint32_t context, intptr_t data) { + int differ = (int)data; + weapon_slot *ws = &player_struct.weapons[player_struct.actives[ACTIVE_WEAPON]]; + int ammosc; + ubyte ammo_types[3]; + int num_types; + int i, cur; + + // Don't attempt to reload a weapon that doesn't use ammo! + if ((ws->type == GUN_SUBCLASS_BEAM) || + (ws->type == GUN_SUBCLASS_HANDTOHAND)) { + return FALSE; // nice try + } + + if (!differ) { + // attempts to reload the current weapon with the same ammo type it + // held before, but if that fails will reload with anything available. + unload_current_weapon(); + mfd_notify_func(MFD_WEAPON_FUNC, MFD_WEAPON_SLOT, FALSE, MFD_ACTIVE, TRUE); + if (change_ammo_type(ws->ammo_type)) { + return TRUE; + } + return (reload_current_weapon()); + } else { + unload_current_weapon(); + mfd_notify_func(MFD_WEAPON_FUNC, MFD_WEAPON_SLOT, FALSE, MFD_ACTIVE, TRUE); + get_available_ammo_type(ws->type, ws->subtype, &num_types, ammo_types, &ammosc); + // find index of current ammo type + for (cur = 0; cur < num_types - 1; cur++) + if (ammo_types[cur] == ws->ammo_type) + break; + // try available ammo types, current one last. Note that if our + // current type is not avaiable, then cur will be num_types, or + // zero mode num_types, so this will try all numbers modulo num_types + // in the order 1, ... ,num_types-1,0 + for (i = 1; i < num_types; i++) { + if (change_ammo_type(ammo_types[(cur + i) % num_types])) { + return TRUE; + } + } + } + return FALSE; +} + +uchar ready_to_draw_handart(void) { + ubyte active = player_struct.actives[ACTIVE_WEAPON]; + uchar val = FALSE; + + switch (player_struct.weapons[active].type) { + case (GUN_SUBCLASS_HANDTOHAND): + val = TRUE; + break; + case (GUN_SUBCLASS_BEAM): + case (GUN_SUBCLASS_BEAMPROJ): + val = (player_struct.weapons[active].heat < OVERHEAT_THRESHOLD); + break; + default: + val = (player_struct.game_time >= next_fire_time); + break; + } + return (val); +} diff --git a/engine/src/GameSrc/wrapper.c b/engine/src/GameSrc/wrapper.c new file mode 100644 index 0000000..9036976 --- /dev/null +++ b/engine/src/GameSrc/wrapper.c @@ -0,0 +1,2329 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/RCS/wrapper.c $ + * $Revision: 1.146 $ + * $Author: dc $ + * $Date: 1994/11/28 06:40:50 $ + */ + +#include + +#include "newmfd.h" +#include "wrapper.h" +#include "tools.h" +#include "invent.h" +#include "invpages.h" +#include "gamescr.h" +#include "mainloop.h" +#include "hkeyfunc.h" +#include "gamewrap.h" +#include "colors.h" +#include "cybstrng.h" +#include "fullscrn.h" +#include "render.h" +#include "gametime.h" +#include "musicai.h" +#include "input.h" +#include "gamestrn.h" +#include "mfdext.h" +#include "miscqvar.h" +#include "cit2d.h" +#include "rendtool.h" +#include "sideicon.h" +#include "sndcall.h" +#include "sfxlist.h" +#include "criterr.h" +#include "gr2ss.h" +#include "player.h" +#include "popups.h" +#include "olhext.h" +#include "Xmi.h" +#include "Prefs.h" + +#include "OpenGL.h" + + +#ifdef AUDIOLOGS +#include "audiolog.h" +#endif + +#include "mfdart.h" // for the slider bar + +#include "MacTune.h" + +#define LOAD_BUTTON 0 +#define SAVE_BUTTON 1 +#define AUDIO_BUTTON 2 +#define INPUT_BUTTON 3 +#define OPTIONS_BUTTON 4 +#define VIDEO_BUTTON 5 +#define RETURN_BUTTON 6 +#define QUIT_BUTTON 7 +#define AUDIO_OPT_BUTTON 8 +#define SCREENMODE_BUTTON 9 +#define HEAD_RECENTER_BUTTON 10 +#define HEADSET_BUTTON 11 + +#define MOUSE_DOWN (MOUSE_LDOWN | MOUSE_RDOWN | UI_MOUSE_LDOUBLE) +#define MOUSE_UP (MOUSE_LUP | MOUSE_RUP) +#define MOUSE_LEFT (MOUSE_LDOWN | UI_MOUSE_LDOUBLE) +#define MOUSE_WHEEL (MOUSE_WHEELUP | MOUSE_WHEELDN) + +#define STATUS_X 4 +#define STATUS_Y 1 +#define STATUS_HEIGHT 20 +#define STATUS_WIDTH 312 + +LGCursor option_cursor; +grs_bitmap option_cursor_bmap; + +extern LGRegion *inventory_region; +int wrap_id = -1, wrapper_wid, wrap_key_id; +uchar clear_panel = TRUE, wrapper_panel_on = FALSE; +grs_font *opt_font; +uchar olh_temp; +static bool digi_gain = true; // enable sfx volume slider +errtype (*wrapper_cb)(int num_clicked); +errtype (*slot_callback)(int num_clicked); +static uchar cursor_loaded = FALSE; +#if defined(VFX1_SUPPORT) || defined(CTM_SUPPORT) +uchar headset_track = TRUE; +#define HEADSET_FOV_MIN 30 +#define HEADSET_FOV_MAX 180 +// these 3 should all be initialized for real elsewhere... +int inp6d_real_fov = 60; +int hack_headset_fov = 30; +#endif +int inp6d_curr_fov = 60; + +errtype music_slots(); +errtype wrapper_do_save(); +errtype wrapper_panel_close(uchar clear_message); +errtype do_savegame_guts(uchar slot); +void quit_verify_pushbutton_handler(uchar butid); +uchar quit_verify_slorker(uchar butid); +void save_verify_pushbutton_handler(uchar butid); +uchar save_verify_slorker(uchar butid); +void free_options_cursor(void); + +void input_screen_init(void); +void joystick_screen_init(void); +void sound_screen_init(void); +void soundopt_screen_init(void); +void video_screen_init(void); + +uint multi_get_curval(uchar type, void *p); +void multi_set_curval(uchar type, void *p, uint val, void *deal); + +extern LGCursor slider_cursor; +extern grs_bitmap slider_cursor_bmap; +extern char which_lang; + +void options_screen_init(void); +void wrapper_init(void); +void load_screen_init(void); +void save_screen_init(void); + +void draw_button(uchar butid); + +#define SLOTNAME_HEIGHT 6 +#define PANEL_MARGIN_Y 3 +#define WRAPPER_PANEL_HEIGHT (INVENTORY_PANEL_HEIGHT - 2 * PANEL_MARGIN_Y) + +#define OPTIONS_FONT RES_tinyTechFont + +errtype (*verify_callback)(int num_clicked) = NULL; +char savegame_verify; +char comments[NUM_SAVE_SLOTS + 1][SAVE_COMMENT_LEN]; +uchar pause_game_func(ushort keycode, uint32_t context, intptr_t data); +uchar really_quit_key_func(ushort keycode, uint32_t context, intptr_t data); + +// separate mouse region for regular-screen and fullscreen. +#define NUM_MOUSEREGION_SCREENS 2 +LGRegion options_mouseregion[NUM_MOUSEREGION_SCREENS]; +uchar free_mouseregion = 0; + +char save_game_name[] = "savgam00.dat"; + +extern grs_canvas *pinv_canvas; +extern grs_canvas inv_norm_canvas; +extern grs_canvas inv_fullscrn_canvas; +extern grs_canvas inv_view360_canvas; + +#define FULL_BACK_X (GAME_MESSAGE_X - INVENTORY_PANEL_X) +#define FULL_BACK_Y (GAME_MESSAGE_Y - INVENTORY_PANEL_Y) + +#define BUTTON_COLOR GREEN_BASE + 2 +#define BUTTON_SHADOW 7 + +// SLIDER WIDGETS: +// structure for slider widget. The slider has a pointer to a uchar, +// ushort, or uint, which it sets to a value in the range [0,maxval]. +// It recalculates this value based on interpolation from the actual +// size of the slider. The function dealfunc (if not NULL) is called +// when the value changes, and is passed the new value. The value is +// updated continuously if smooth==TRUE; otherwise it is updated upon +// mouse-up. +// +typedef struct { + uchar color; + uchar bvalcol; + uchar sliderpos; + uchar active; + Ref descrip; + uint maxval; + uchar baseval; + uchar type; + uchar smooth; + void *curval; + void *dealfunc; +} opt_slider_state; + +// PUSHBUTTON WIDGETS: +// the simplest widgets. Calls pushfunc, passing in its own button ID, +// upon mouse left-click upon it, or on a keyboard event corresponding +// to keyeq. +// +typedef struct { + uchar keyeq; + Ref descrip; + uchar fcolor; + uchar shadow; + void (*pushfunc)(uchar butid); +} opt_pushbutton_state; + +// MULTI_STATE WIDGETS: +// these are much like pushbuttons, but also have a pointer to a uchar, +// ushort, or uint, which takes on a value in the range [0,num_opts-1]. +// The button is labelled both with its description string (descrip) and +// with a string offset from optbase by an amount equal to the current +// value of its associated variable. Whenever its value changes, it calls +// dealfunc, and message-lines a string offset from feedbackbase by an +// amount equal to its current value. +// +typedef struct { + uchar keyeq; + uchar type; + uchar num_opts; + Ref optbase; + Ref descrip; + Ref feedbackbase; + void *curval; + void *dealfunc; +} opt_multi_state; + +// TEXT WIDGET +// nothing but a piece of text, folks. No handler, simple draw func. +// +typedef struct { + Ref descrip; + uchar color; +} opt_text_state; + +// TEXTLIST WIDGET +// used for editing and selecting (and responding to the editing and +// selecting of) a list of text strings. You provide a block of text, +// which is assumed to be a 2-D array of chars (you inform the widget +// of the dimension of the subarrays). The widget may either be edit- +// allowing or not. If not, then it calls its dealfunc whenever a +// text string is selected (by mouse-clicking on it or by using the +// keyboard to move the highlight to it and hitting ENTER). If the +// strings are editable, it calls its dealfunc only when you are done +// selecting and editing one. A mask may be provided of what entries +// on the list are valid candidates for selection, and a string resource +// is given to display in the place of uninitialized selections. Different +// colors are provided for selectable text, currently selected text, +// and non-selectable text. Note that the user is responsible for +// providing space for one more line of text than the widget uses, for +// the purposes of saving string information. +// +typedef struct { + char *text; + uchar numblocks; + uchar blocksiz; + + char currstring; + char index; + uchar modified; + + uchar editable; + ushort editmask; + ushort selectmask; + ushort initmask; + Ref invalidstr; + + Ref selectprompt; + + uchar validcol; + uchar selectcol; + uchar invalidcol; + + void (*dealfunc)(uchar butid, uchar index); +} opt_textlist_state; + +// SLORKER WIDGET +// used to implement default actions in the keyboard interface to options +// screens, slorker widgets respond to no mouse events, but will respond +// to any keyboard events which actually reach them by calling their function +// with their button id as an argument. Thus, any keypress which is not +// handled by another gadget is taken by the slorker. +// +typedef uchar (*slorker)(uchar butid); + +typedef struct { + LGRect rect; + union { + opt_slider_state slider_st; + opt_pushbutton_state pushbutton_st; + opt_text_state text_st; + opt_multi_state multi_st; + opt_textlist_state textlist_st; + slorker sl; + } user; + ulong evmask; + void (*drawfunc)(uchar butid); + uchar (*handler)(uiEvent *ev, uchar butid); +} opt_button; + +void verify_screen_init(void (*verify)(uchar butid), slorker slork); +// void verify_screen_init(void (*verify)(uchar butid), void (*slork)(uchar butid)); + +#define OPT_SLIDER_BAR REF_IMG_BeamSetting + +#define MAX_OPTION_BUTTONS 12 +#define BR(i) (OButtons[i].rect) + +#ifdef STATIC_BUTTON_STORE +opt_button OButtons[MAX_OPTION_BUTTONS]; +#else +extern grs_canvas _offscreen_mfd; +opt_button *OButtons; +uchar fv; +#endif + +#define OPTIONS_COLOR RED_BROWN_BASE + 4 + +// decides on a "standard" width for our widgets based on column count +// of current screen. Our desire is that uniform widgets of this size +// should have certain margins between them independent of column count. +#define CONSTANT_MARGINS + +#ifdef HALF_BUTTON_MARGINS +#define widget_width(t, m) (2 * INVENTORY_PANEL_WIDTH / (3 * (t) + 1)) +#define widget_x(c, t, m) ((3 * (t) + 1) * INVENTORY_PANEL_WIDTH / (3 * (t) + 1)) +#endif +#ifdef CONSTANT_MARGINS +#define widget_width(t, m) ((INVENTORY_PANEL_WIDTH - ((m) * ((t) + 1))) / (t)) +#define widget_x(c, t, m) ((m) * ((c) + 1) + widget_width(t, m) * (c)) +#endif + +// override get_temp_string() to support hard-coded custom strings without +// providing an actual resource file + +#define MIDI_OUT_STR_SIZE 1024 +static char MIDI_STR_BUFFER[MIDI_OUT_STR_SIZE]; + +static char *_get_temp_string(int num) { + switch (num) { + case REF_STR_Renderer: return "Renderer"; + case REF_STR_Software: return "Software"; + case REF_STR_OpenGL: return "OpenGL"; + + case REF_STR_TextFilt: return "Tex Filter"; + case REF_STR_TFUnfil: return "Unfiltered"; + case REF_STR_TFBilin: return "Bilinear"; + + case REF_STR_MousLook: return "Mouselook"; + case REF_STR_MousNorm: return "Normal"; + case REF_STR_MousInv: return "Inverted"; + + case REF_STR_Seqer: return "Midi Player"; + case REF_STR_ADLMIDI: return "ADLMIDI"; + case REF_STR_NativeMI: return "Native MIDI"; +#ifdef USE_FLUIDSYNTH + case REF_STR_FluidSyn: return "FluidSynth"; +#endif + + case REF_STR_MidiOut: return "Midi Output"; + } + + if (num >= REF_STR_MidiOutX && num <= (REF_STR_MidiOutX | 0x0fffffff)) + { + const unsigned int midiOutputIndex = (unsigned int)num - REF_STR_MidiOutX; + MIDI_STR_BUFFER[0] = '\0'; + GetOutputNameXMI(midiOutputIndex, &MIDI_STR_BUFFER[0], MIDI_OUT_STR_SIZE); + return &MIDI_STR_BUFFER[0]; + } + + return get_temp_string(num); +} + +#define get_temp_string _get_temp_string + +//#ifdef NOT_YET // + +void draw_button(uchar butid) { + if (OButtons[butid].drawfunc) { +#ifdef SVGA_SUPPORT + uchar old_over; + old_over = gr2ss_override; + gr2ss_override = OVERRIDE_ALL; +#endif + uiHideMouse(NULL); + gr_push_canvas(&inv_norm_canvas); + gr_set_font(opt_font); + OButtons[butid].drawfunc(butid); + gr_pop_canvas(); + uiShowMouse(NULL); +#ifdef GR2SS_OVERRIDE + gr2ss_override = old_over; +#endif + } +} + +void wrapper_draw_background(short ulx, short uly, short lrx, short lry) { + short cx1, cx2, cy1, cy2; + extern grs_bitmap inv_backgnd; + short a1, a2, a3, a4; + +#ifdef SVGA_SUPPORT + uchar old_over; + old_over = gr2ss_override; + gr2ss_override = OVERRIDE_ALL; +#endif + // draw background behind the slider. + STORE_CLIP(cx1, cy1, cx2, cy2); + ss_safe_set_cliprect(ulx, uly, lrx, lry); + if (full_game_3d) { + // gr_bitmap(&inv_view360_canvas.bm,FULL_BACK_X,FULL_BACK_Y); + gr_get_cliprect(&a1, &a2, &a3, &a4); + ss_noscale_bitmap(&inv_view360_canvas.bm, FULL_BACK_X, FULL_BACK_Y); + } else + ss_bitmap(&inv_backgnd, 0, 0); + RESTORE_CLIP(cx1, cy1, cx2, cy2); +#ifdef SVGA_SUPPORT + gr2ss_override = old_over; +#endif +} + +void slider_draw_func(uchar butid) { + opt_slider_state *st = &(OButtons[butid].user.slider_st); + short w, h, sw; + char *title; + +#ifdef SVGA_SUPPORT + uchar old_over; + old_over = gr2ss_override; + gr2ss_override = OVERRIDE_ALL; +#endif + + sw = res_bm_width(OPT_SLIDER_BAR); + gr_set_fcolor(st->color); + title = get_temp_string(st->descrip); + gr_string_size(title, &w, &h); + + // draw background behind the slider + wrapper_draw_background(BR(butid).ul.x - sw / 2, BR(butid).ul.y - h, BR(butid).lr.x + sw / 2, BR(butid).lr.y); + draw_shadowed_string(title, BR(butid).ul.x, BR(butid).ul.y - h, full_game_3d); + + gr_set_fcolor(st->bvalcol); + ss_vline(BR(butid).ul.x + st->baseval, BR(butid).ul.y, BR(butid).lr.y - 1); + + gr_set_fcolor(st->color); + ss_box(BR(butid).ul.x, BR(butid).ul.y, BR(butid).lr.x, BR(butid).lr.y); + + if (!(st->active)) + draw_raw_resource_bm(OPT_SLIDER_BAR, BR(butid).ul.x + st->sliderpos + 1 - sw / 2, BR(butid).ul.y); + +#ifdef SVGA_SUPPORT + gr2ss_override = old_over; +#endif +} + +void slider_deal(uchar butid, uchar deal) { + opt_slider_state *st = &(OButtons[butid].user.slider_st); + uint val; + + deal = deal || st->smooth; + + val = (st->sliderpos * (st->maxval + 1)) / (BR(butid).lr.x - BR(butid).ul.x - 3); + if (val > st->maxval) val = st->maxval; + + multi_set_curval(st->type, st->curval, val, deal ? st->dealfunc : NULL); +} + +// +// every time you find yourself, +// you lose a little bit of me, from within +// + +uchar slider_handler(uiEvent *ev, uchar butid) { + opt_slider_state *st = &(OButtons[butid].user.slider_st); + + switch (ev->type) { + case UI_EVENT_MOUSE_MOVE: + if (ev->mouse_data.buttons) { + st->sliderpos = ev->pos.x - BR(butid).ul.x; + slider_deal(butid, TRUE); + draw_button(butid); + } + break; + case UI_EVENT_MOUSE: + if (ev->mouse_data.action & MOUSE_WHEELUP) { + st->sliderpos = st->sliderpos <= 5 ? 0 : st->sliderpos - 5; + } else if (ev->mouse_data.action & MOUSE_WHEELDN) { + uchar max = BR(butid).lr.x - BR(butid).ul.x - 3; + st->sliderpos = lg_min(st->sliderpos + 5, max); + } else { + st->sliderpos = ev->pos.x - BR(butid).ul.x; + } + slider_deal(butid, TRUE); + draw_button(butid); + return TRUE; + default: + break; + } + return FALSE; +} + +void slider_init(uchar butid, Ref descrip, uchar type, uchar smooth, void *var, uint maxval, uchar baseval, + void *dealfunc, LGRect *r) { + opt_slider_state *st = &OButtons[butid].user.slider_st; + uint val; + + if (maxval) + { + val = ((r->lr.x - r->ul.x - 3) * multi_get_curval(type, var)) / maxval; + } + else + { + // just put it in the middle + val = (r->lr.x - r->ul.x - 3) / 2; + } + + st->color = BUTTON_COLOR; + st->bvalcol = GREEN_YELLOW_BASE + 1; + st->sliderpos = val; + st->baseval = baseval; + st->maxval = maxval; + st->active = FALSE; + st->descrip = descrip; + st->type = type; + // note that in these settings, we don't care what size of + // variable we're dealing with, 'cause we secretly know that + // all pointers are represented the same and we don't + // have to actually dereference these. + st->dealfunc = dealfunc; + st->curval = var; + st->smooth = smooth; + + OButtons[butid].evmask = UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE; + OButtons[butid].drawfunc = slider_draw_func; + OButtons[butid].handler = slider_handler; + OButtons[butid].rect = *r; +} + +void pushbutton_draw_func(uchar butid) { + char *btext; + short w, h; + opt_pushbutton_state *st = &OButtons[butid].user.pushbutton_st; + + w = BR(butid).lr.x - BR(butid).ul.x; + h = BR(butid).lr.y - BR(butid).ul.y; + + btext = get_temp_string(st->descrip); + gr_string_wrap(btext, BR(butid).lr.x - BR(butid).ul.x - 3); + text_button(btext, BR(butid).ul.x, BR(butid).ul.y, st->fcolor, st->shadow, -w, -h); + gr_font_string_unwrap(btext); +} + +uchar pushbutton_handler(uiEvent *ev, uchar butid) { + if (((ev->type == UI_EVENT_MOUSE) && (ev->subtype & MOUSE_DOWN)) || + ((ev->type == UI_EVENT_KBD_COOKED) && + ((ev->cooked_key_data.code & 0xFF) == OButtons[butid].user.pushbutton_st.keyeq))) { + OButtons[butid].user.pushbutton_st.pushfunc(butid); + return TRUE; + } + return FALSE; +} + +void pushbutton_init(uchar butid, uchar keyeq, Ref descrip, void (*pushfunc)(uchar butid), LGRect *r) { + opt_pushbutton_state *st = &OButtons[butid].user.pushbutton_st; + + OButtons[butid].rect = *r; + OButtons[butid].evmask = UI_EVENT_MOUSE | UI_EVENT_KBD_COOKED; + OButtons[butid].drawfunc = pushbutton_draw_func; + OButtons[butid].handler = pushbutton_handler; + st->fcolor = BUTTON_COLOR; + st->shadow = BUTTON_SHADOW; + st->keyeq = keyeq; + st->descrip = descrip; + st->pushfunc = pushfunc; +} + +void dim_pushbutton(uchar butid) { + opt_pushbutton_state *st = &OButtons[butid].user.pushbutton_st; + OButtons[butid].evmask = 0; + st->fcolor += 4; + st->shadow -= 3; +} + +void bright_pushbutton(uchar butid) { + opt_pushbutton_state *st = &OButtons[butid].user.pushbutton_st; + OButtons[butid].evmask = 0; + st->fcolor -= 2; + st->shadow += 2; +} + +// text widget +void text_draw_func(uchar butid) { + opt_text_state *st = &OButtons[butid].user.text_st; + char *s = get_temp_string(st->descrip); + + gr_string_wrap(s, BR(butid).lr.x - BR(butid).ul.x); + gr_set_fcolor(st->color); + draw_shadowed_string(s, BR(butid).ul.x, BR(butid).ul.y, full_game_3d); + gr_font_string_unwrap(s); +} + +void textwidget_init(uchar butid, uchar color, Ref descrip, LGRect *r) { + opt_text_state *st = &OButtons[butid].user.text_st; + + OButtons[butid].rect = *r; + st->descrip = descrip; + st->color = color; + OButtons[butid].drawfunc = text_draw_func; + OButtons[butid].handler = NULL; + OButtons[butid].evmask = 0; +} + +// a keywidget is just like a pushbutton, but invisible. +// +void keywidget_init(uchar butid, uchar keyeq, void (*pushfunc)(uchar butid)) { + opt_pushbutton_state *st = &OButtons[butid].user.pushbutton_st; + + OButtons[butid].evmask = UI_EVENT_KBD_COOKED; + OButtons[butid].drawfunc = NULL; + OButtons[butid].handler = pushbutton_handler; + st->keyeq = keyeq; + st->pushfunc = pushfunc; +} + +// gets the current "value" of a multi-option widget, whatever size +// thing that may be. +uint multi_get_curval(uchar type, void *p) { + uint val = 0; + + switch (type) { + case sizeof(uchar): + val = *((uchar *)p); + break; + case sizeof(ushort): + val = *((ushort *)p); + break; + case sizeof(uint): + val = *((uint *)p); + break; + } + return val; +} + +// sets the current value pointed to by a multi-option widget. +void multi_set_curval(uchar type, void *p, uint val, void *deal) { + switch (type) { + case sizeof(uchar): + *((uchar *)p) = (uchar)val; + if (deal) + ((void (*)(uchar))deal)((uchar)val); + break; + case sizeof(ushort): + *((ushort *)p) = (ushort)val; + if (deal) + ((void (*)(ushort))deal)((ushort)val); + break; + case sizeof(uint): + *((uint *)p) = (uint)val; + if (deal) + ((void (*)(uint))deal)((uint)val); + break; + } +} + +void multi_draw_func(uchar butid) { + char *btext; + short w, h, x, y; + uint val = 0; + opt_multi_state *st = &OButtons[butid].user.multi_st; + + gr_set_fcolor(BUTTON_COLOR); + ss_rect(BR(butid).ul.x, BR(butid).ul.y, BR(butid).lr.x, BR(butid).lr.y); + gr_set_fcolor(BUTTON_COLOR + BUTTON_SHADOW); + ss_rect(BR(butid).ul.x + 1, BR(butid).ul.y + 1, BR(butid).lr.x - 1, BR(butid).lr.y - 1); + gr_set_fcolor(BUTTON_COLOR); + x = (BR(butid).lr.x + BR(butid).ul.x) / 2; + y = (BR(butid).lr.y + BR(butid).ul.y) / 2; + btext = get_temp_string(st->descrip); + gr_string_size(btext, &w, &h); + ss_string(btext, x - w / 2, y - h); + val = multi_get_curval(st->type, st->curval); + btext = get_temp_string(st->optbase + val); + gr_string_size(btext, &w, &h); + ss_string(btext, x - w / 2, y); +} + +uchar multi_handler(uiEvent *ev, uchar butid) { + uint val = 0, delta = 0; + opt_multi_state *st = &OButtons[butid].user.multi_st; + + if (ev->type == UI_EVENT_MOUSE) { + if (ev->subtype & MOUSE_LEFT) + delta = 1; + else if (ev->subtype & MOUSE_RDOWN) + delta = st->num_opts - 1; + } else if (ev->type == UI_EVENT_KBD_COOKED) { + short code = ev->cooked_key_data.code; + if (tolower(code & 0xFF) == st->keyeq) { + if (isupper(code & 0xFF)) + delta = st->num_opts - 1; + else + delta = 1; + } + } + + if (delta) { + val = multi_get_curval(st->type, st->curval); + val = (val + delta) % (st->num_opts); + multi_set_curval(st->type, st->curval, val, st->dealfunc); + draw_button(butid); + if (st->feedbackbase) { + string_message_info(st->feedbackbase + val); + } + return TRUE; + } + return FALSE; +} + +void multi_init(uchar butid, uchar key, Ref descrip, Ref optbase, Ref feedbase, uchar type, void *var, uchar num_opts, + void *dealfunc, LGRect *r) { + opt_multi_state *st = &OButtons[butid].user.multi_st; + + OButtons[butid].rect = *r; + OButtons[butid].drawfunc = multi_draw_func; + OButtons[butid].handler = multi_handler; + OButtons[butid].evmask = UI_EVENT_MOUSE | UI_EVENT_KBD_COOKED; + st->descrip = descrip; + st->optbase = optbase; + st->feedbackbase = feedbase; + st->type = type; + st->keyeq = key; + st->num_opts = num_opts; + // note that in these settings, we don't care what size of + // variable we're dealing with, 'cause we secretly know that + // all pointers are represented the same and we don't + // have to actually dereference these. + st->dealfunc = dealfunc; + st->curval = var; +} + +#pragma disable_message(202) +uchar keyslork_handler(uiEvent *ev, uchar butid) { + slorker *slork = &OButtons[butid].user.sl; + + return ((*slork)(butid)); +} +#pragma enable_message(202) + +void slork_init(uchar butid, slorker slork) { + LG_memset(&OButtons[butid].rect, 0, sizeof(LGRect)); + OButtons[butid].user.sl = slork; + OButtons[butid].evmask = UI_EVENT_KBD_COOKED; + OButtons[butid].drawfunc = NULL; + OButtons[butid].handler = keyslork_handler; +} + +char *textlist_string(opt_textlist_state *st, int ind) { return (st->text + ind * (st->blocksiz)); } + +void textlist_draw_line(opt_textlist_state *st, int line, uchar butid) { + short w, h; + LGRect scrrect; + LGRect r; + char *s; + uchar col; +#ifdef SVGA_SUPPORT + uchar old_over; +#endif + + scrrect = BR(butid); + scrrect.ul.x += INVENTORY_PANEL_X; + scrrect.ul.y += INVENTORY_PANEL_Y; + scrrect.lr.x += INVENTORY_PANEL_X; + scrrect.lr.y += INVENTORY_PANEL_Y; + + if (((1 << line) & (st->initmask)) || (line == st->currstring && st->index >= 0)) + s = textlist_string(st, line); + else + s = get_temp_string(st->invalidstr); + + if (line == st->currstring) + col = st->selectcol; + else if (st->selectmask & (1 << line)) + col = st->validcol; + else + col = st->invalidcol; + gr_push_canvas(&inv_norm_canvas); + gr_set_fcolor(col); + + gr_set_font(opt_font); + gr_string_size(s, &w, &h); + r.ul.x = BR(butid).ul.x; + r.ul.y = BR(butid).ul.y + h * line; + r.lr.x = BR(butid).lr.x; + r.lr.y = r.ul.y + h; + + uiHideMouse(&scrrect); +#ifdef SVGA_SUPPORT + old_over = gr2ss_override; + gr2ss_override = OVERRIDE_ALL; +#endif + wrapper_draw_background(r.ul.x, r.ul.y, r.lr.x, r.lr.y); + draw_shadowed_string(s, r.ul.x, r.ul.y, full_game_3d); +#ifdef SVGA_SUPPORT + gr2ss_override = old_over; +#endif + uiShowMouse(&scrrect); + gr_pop_canvas(); +} + +void textlist_draw_func(uchar butid) { + int i; + opt_textlist_state *st = &OButtons[butid].user.textlist_st; + + for (i = 0; i < st->numblocks; i++) { + textlist_draw_line(st, i, butid); + } +} + +void textlist_cleanup(opt_textlist_state *st) { + if (st->editable && st->currstring >= 0 && st->index >= 0) { + strcpy(textlist_string(st, st->currstring), textlist_string(st, st->numblocks)); + st->index = -1; + } +} + +#ifdef WE_USED_THIS +void textlist_edit_line(opt_textlist_state *st, uchar butid, uchar line, uchar end) { + char *s, *bak; + char tmp; + + gr_push_canvas(&inv_norm_canvas); + s = textlist_string(st, line); + bak = textlist_string(st, st->numblocks); + tmp = st->currstring; + st->currstring = line; + if (tmp >= 0) { + strcpy(textlist_string(st, tmp), bak); + textlist_draw_line(st, tmp, butid); + } + strcpy(bak, s); + st->index = end ? strlen(s) : 0; + s[0] = '\0'; + textlist_draw_line(st, line, butid); + gr_pop_canvas(); +} +#endif + +void textlist_select_line(opt_textlist_state *st, uchar butid, uchar line, uchar deal) { + char tmp; + + gr_push_canvas(&inv_norm_canvas); + tmp = st->currstring; + st->currstring = line; + st->index = -1; + if (tmp >= 0) + textlist_draw_line(st, tmp, butid); + textlist_draw_line(st, line, butid); + gr_pop_canvas(); + if (deal) + st->dealfunc(butid, line); +} + +uchar textlist_handler(uiEvent *ev, uchar butid) { + uchar line; + opt_textlist_state *st = &OButtons[butid].user.textlist_st; + + if ((ev->type == UI_EVENT_MOUSE) && (ev->subtype & MOUSE_DOWN)) { + short w, h; + + gr_set_font(opt_font); + gr_char_size('X', &w, &h); + + line = (ev->pos.y - BR(butid).ul.y) / h; + + if (st->editable && (st->editmask & (1 << line))) { + // this is how you would do this if you wanted right-click to select + // a line w/ confirm, which would be the right thing to do, instead + // of confirm without selection, which is what Harvey at Origin wants. + // + // if(st->selectprompt) + // textlist_select_line(st,butid,line,FALSE); + // textlist_select_line(st,butid,line,(ev->subtype&MOUSE_RDOWN)!=0); + // + if (ev->subtype & MOUSE_RDOWN) { + if (st->currstring >= 0) + st->dealfunc(butid, st->currstring); + } else if (!st->modified) { + string_message_info(st->selectprompt); + if (st->selectprompt) + textlist_select_line(st, butid, line, FALSE); + } + } else if (st->selectmask & (1 << line)) { + textlist_select_line(st, butid, line, TRUE); + } + return TRUE; + } else if (ev->type == UI_EVENT_KBD_COOKED) { + short code = ev->cooked_key_data.code; + char k = code & 0xFF; + uint keycode = code & ~KB_FLAG_DOWN; + uchar special = ((code & KB_FLAG_SPECIAL) != 0); + char *s; + char upness = 0; + char cur = st->currstring; + + // explicitly do not deal with alt-x, but leave + // it to more capable hands. + if (keycode == (KB_FLAG_ALT | 'x')) + return FALSE; + if (cur >= 0) + s = textlist_string(st, cur); + if (st->editable && cur >= 0 && !special && kb_isprint(keycode)) { + if (st->index < 0) { + strcpy(textlist_string(st, st->numblocks), textlist_string(st, st->currstring)); + st->index = 0; + } + if (st->index + 1 < st->blocksiz) { + s[st->index] = k; + st->index++; + s[st->index] = '\0'; + textlist_draw_line(st, cur, butid); + } + st->modified = TRUE; + return TRUE; + } + switch (keycode) { + case KEY_BS: + if (st->editable && cur >= 0) { + if (st->index < 0) { + strcpy(textlist_string(st, st->numblocks), textlist_string(st, st->currstring)); + st->index = strlen(s); + } + if (st->index > 0) + st->index--; + s[st->index] = '\0'; + textlist_draw_line(st, cur, butid); + } + break; + case KEY_UP: + upness = st->numblocks - 1; + break; + case KEY_DOWN: + upness = 1; + break; + case KEY_ENTER: + if (st->currstring >= 0) { + st->dealfunc(butid, cur); + return TRUE; + } + break; + case KEY_ESC: + // on ESC, clean up but pass the event through. + textlist_cleanup(st); + wrapper_panel_close(TRUE); + return FALSE; + } + if (upness != 0) { + char newstring; + uchar safety = 0; + + newstring = cur; + if (newstring < 0) + newstring = (upness == 1) ? st->numblocks - 1 : 0; + do { + newstring = (newstring + upness) % st->numblocks; + safety++; + } while (safety < st->numblocks && !((1 << newstring) & st->selectmask)); + if (safety >= st->numblocks) + newstring = cur; + if (newstring != cur) { + textlist_cleanup(st); + st->currstring = newstring; + if (cur >= 0 && cur < st->numblocks) + textlist_draw_line(st, cur, butid); + textlist_draw_line(st, newstring, butid); + } + } + return TRUE; + } + return TRUE; +} + +void textlist_init(uchar butid, char *text, uchar numblocks, uchar blocksiz, uchar editable, ushort editmask, + ushort selectmask, ushort initmask, Ref invalidstr, uchar validcol, uchar selectcol, + uchar invalidcol, Ref selectprompt, void (*dealfunc)(uchar butid, uchar index), LGRect *r) { + opt_textlist_state *st = &OButtons[butid].user.textlist_st; + + if (r == NULL) { + BR(butid).ul.x = 2; + BR(butid).ul.y = 2; + BR(butid).lr.x = INVENTORY_PANEL_WIDTH; + BR(butid).lr.y = INVENTORY_PANEL_HEIGHT; + } else + OButtons[butid].rect = *r; + OButtons[butid].drawfunc = textlist_draw_func; + OButtons[butid].handler = textlist_handler; + OButtons[butid].evmask = UI_EVENT_MOUSE | UI_EVENT_KBD_COOKED; + st->text = text; + st->numblocks = numblocks; + st->blocksiz = blocksiz; + st->editable = editable; + st->editmask = editmask; + st->selectmask = selectmask; + st->initmask = initmask; + st->invalidstr = invalidstr; + st->validcol = validcol; + st->selectcol = selectcol; + st->invalidcol = invalidcol; + st->dealfunc = dealfunc; + st->selectprompt = selectprompt; + + st->currstring = -1; + st->index = -1; + st->modified = FALSE; +} + +// One, true mouse handler for all options panel mouse events. +// checks all options panel widgets which enclose point of mouse +// event to see if they want to deal with it. +// +#pragma disable_message(202) +uchar opanel_mouse_handler(uiEvent *ev, LGRegion *r, intptr_t user_data) { + int b; + uiEvent mev = *ev; + + if (!(ev->type & (UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE))) + return FALSE; + if (ev->type == UI_EVENT_MOUSE && !(ev->subtype & (MOUSE_DOWN | MOUSE_UP | MOUSE_WHEEL))) + return FALSE; + + mev.pos.x -= inventory_region->r->ul.x; + mev.pos.y -= inventory_region->r->ul.y; + + for (b = 0; b < MAX_OPTION_BUTTONS; b++) { + if (RECT_TEST_PT(&BR(b), mev.pos) && (ev->type & OButtons[b].evmask)) { + if (OButtons[b].handler && OButtons[b].handler((uiEvent *)(&mev), b)) + return TRUE; + } + } + return TRUE; +} + +// One, true keyboard handler for all options mode events. +// checks all options panel widgets to see if they want to deal. +// +uchar opanel_kb_handler(uiEvent *ev, LGRegion *r, intptr_t user_data) { + int b; + short code = ev->cooked_key_data.code; + + if (!(code & KB_FLAG_DOWN)) + return TRUE; + + for (b = 0; b < MAX_OPTION_BUTTONS; b++) { + if ((ev->type & OButtons[b].evmask) && OButtons[b].handler && OButtons[b].handler(ev, b)) + return TRUE; + } + // if no-one else has hooked KEY_ESC, it defaults to closing + // the wrapper panel. + // + if ((code & 0xFF) == KEY_ESC) + wrapper_panel_close(TRUE); + return TRUE; +} +#pragma enable_message(202) + +void clear_obuttons() { + uiCursorStack *cs; + extern uiSlab *uiCurrentSlab; + + uiGetSlabCursorStack(uiCurrentSlab, &cs); + uiPopCursorEvery(cs, &slider_cursor); + mouse_unconstrain(); + LG_memset(OButtons, 0, MAX_OPTION_BUTTONS * sizeof(opt_button)); +} + +void opanel_redraw(uchar back) { + extern grs_bitmap inv_backgnd; + int but; + LGRect r = {{INVENTORY_PANEL_X, INVENTORY_PANEL_Y}, + {INVENTORY_PANEL_X + INVENTORY_PANEL_WIDTH, INVENTORY_PANEL_Y + INVENTORY_PANEL_HEIGHT}}; +#ifdef SVGA_SUPPORT + uchar old_over = gr2ss_override; + gr2ss_override = OVERRIDE_ALL; // Since we are really going straight to screen in our heart of hearts +#endif + if (!full_game_3d) + inventory_clear(); + gr_push_canvas(&inv_norm_canvas); + uiHideMouse(NULL); + gr_set_font(opt_font); + if (back) { + if (full_game_3d) + ss_noscale_bitmap(&inv_view360_canvas.bm, FULL_BACK_X, FULL_BACK_Y); + else + ss_bitmap(&inv_backgnd, 0, 0); + } + + for (but = 0; but < MAX_OPTION_BUTTONS; but++) { + if (OButtons[but].drawfunc) { + OButtons[but].drawfunc(but); + } + } + uiShowMouse(&r); + gr_pop_canvas(); +#ifdef SVGA_SUPPORT + gr2ss_override = old_over; +#endif +} + +// fills in the Rect r with one of the "standard" button rects, +// assuming buttons in three columns, ro rows, high enough for +// a specified number of lines of text. +// +void standard_button_rect(LGRect *r, uchar butid, uchar lines, uchar ro, uchar mar) { + short w, h; + char i = butid; + + gr_set_font(opt_font); + gr_string_size("X", &w, &h); + + h *= lines; + + r->ul.x = widget_x(i % 3, 3, mar); + r->lr.x = r->ul.x + widget_width(3, mar); + r->ul.y = INVENTORY_PANEL_HEIGHT * (i / 3 + 1) / (ro + 1) - h / 2; + if (ro > 2) + r->ul.y += (3 * ((i / 3) - 1)); + r->lr.y = r->ul.y + h + 2; +} + +void standard_slider_rect(LGRect *r, uchar butid, uchar ro, uchar mar) { + short sh, sw; + + standard_button_rect(r, butid, 2, ro, mar); + + sh = res_bm_height(OPT_SLIDER_BAR); + sw = res_bm_height(OPT_SLIDER_BAR); + r->ul.x += sw / 2; + r->lr.x -= sw / 2; + r->ul.y = r->lr.y - sh; +} + +errtype wrapper_panel_close(uchar clear_message) { + uiCursorStack *cs; + extern uiSlab *uiCurrentSlab; + int i; + + if (!wrapper_panel_on) + return ERR_NOEFFECT; + mouse_unconstrain(); + if (clear_message) + message_info(""); + wrapper_panel_on = FALSE; + SavePrefs(); + inventory_page = inv_last_page; + if (inventory_page < 0 && inventory_page != INV_3DVIEW_PAGE) + inventory_page = 0; + pause_game_func(0, 0, 0); + uiGetSlabCursorStack(uiCurrentSlab, &cs); + uiPopCursorEvery(cs, &slider_cursor); + uiReleaseFocus(inventory_region, UI_EVENT_KBD_COOKED | UI_EVENT_MOUSE); + uiRemoveRegionHandler(inventory_region, wrap_id); + uiRemoveRegionHandler(inventory_region, wrap_key_id); +#ifndef STATIC_BUTTON_STORE + full_visible = fv; +#endif + inventory_clear(); + inventory_draw(); +#ifdef SVGA_SUPPORT + mfd_clear_all(); +#endif + for (i = 0; i < NUM_MFDS; i++) + mfd_force_update_single(i); + ResUnlock(OPTIONS_FONT); + resume_game_time(); + return (OK); +} + +extern uchar game_paused; + +uchar can_save() { + uchar gp = game_paused; + if (global_fullmap->cyber) { + // spoof the game as not being paused so that the message won't go to the + // phantom message line in full screen mode, where it will stay only for a frame. + game_paused = FALSE; + string_message_info(REF_STR_NoCyberSave); + game_paused = gp; + return (FALSE); + } + if (input_cursor_mode == INPUT_OBJECT_CURSOR) { + string_message_info(REF_STR_CursorObjSave); + return (FALSE); + } + return (TRUE); +} + +// +// THE TOP LEVEL OPTIONS: Initialization, handler +// + +void wrapper_pushbutton_func(uchar butid) { + switch (butid) { + case LOAD_BUTTON: // Load Game +#ifdef DEMO + wrapper_panel_close(FALSE); +#else + load_screen_init(); + string_message_info(REF_STR_LoadSlot); +#endif + break; + case SAVE_BUTTON: // Save Game +#ifdef DEMO + wrapper_panel_close(FALSE); +#else + if (can_save()) { + save_screen_init(); + string_message_info(REF_STR_SaveSlot); + } else + wrapper_panel_close(FALSE); +#endif + break; + case AUDIO_BUTTON: // Audio + sound_screen_init(); + break; + case INPUT_BUTTON: // Input + input_screen_init(); + break; + case VIDEO_BUTTON: // Input + video_screen_init(); + break; +#ifdef SVGA_SUPPORT + case SCREENMODE_BUTTON: // Input + screenmode_screen_init(); + break; + case HEAD_RECENTER_BUTTON: // Input + { + // extern uchar recenter_headset(ushort keycode, uint32_t context, intptr_t data); + // recenter_headset(0,0,0); + } break; + case HEADSET_BUTTON: + // headset_screen_init(); + break; +#endif + case AUDIO_OPT_BUTTON: + soundopt_screen_init(); + break; + case OPTIONS_BUTTON: // Options + options_screen_init(); + break; + case RETURN_BUTTON: // Return + wrapper_panel_close(TRUE); + break; + case QUIT_BUTTON: // Quit + verify_screen_init(quit_verify_pushbutton_handler, quit_verify_slorker); + string_message_info(REF_STR_QuitConfirm); + break; + } + return; +} + +void wrapper_init(void) { + LGRect r; + int i; + char *keyequivs; + + keyequivs = get_temp_string(REF_STR_KeyEquivs0); + + clear_obuttons(); + for (i = 0; i < 8; i++) { + standard_button_rect(&r, i, 2, 3, 5); + pushbutton_init(i, keyequivs[i], REF_STR_WrapperText + i, wrapper_pushbutton_func, &r); + } +#ifdef DEMO + dim_pushbutton(LOAD_BUTTON); + dim_pushbutton(SAVE_BUTTON); +#endif + opanel_redraw(TRUE); +} + + // + // THE VERIFY SCREEN: Initialization, handlers + // + +#pragma disable_message(202) +void quit_verify_pushbutton_handler(uchar butid) { really_quit_key_func(0, 0, 0); } + +uchar quit_verify_slorker(uchar butid) { + wrapper_panel_close(TRUE); + return TRUE; +} + +void save_verify_pushbutton_handler(uchar butid) { do_savegame_guts(savegame_verify); } + +uchar save_verify_slorker(uchar butid) { + strcpy(comments[savegame_verify], comments[NUM_SAVE_SLOTS]); + wrapper_panel_close(TRUE); + return TRUE; +} +#pragma enable_message(202) + +void verify_screen_init(void (*verify)(uchar butid), slorker slork) { + LGRect r; + + clear_obuttons(); + + standard_button_rect(&r, 1, 2, 2, 5); + pushbutton_init(0, tolower(get_temp_string(REF_STR_VerifyText)[0]), REF_STR_VerifyText, verify, &r); + + standard_button_rect(&r, 4, 2, 2, 5); + pushbutton_init(1, tolower(get_temp_string(REF_STR_VerifyText + 1)[0]), (REF_STR_VerifyText + 1), (void (*)(uchar))slork, &r); + + slork_init(2, slork); + + opanel_redraw(TRUE); +} + +void quit_verify_init(void) { verify_screen_init(quit_verify_pushbutton_handler, quit_verify_slorker); } + +// +// THE SOUND OPTIONS SCREEN: Initialization, update funcs + +uchar curr_vol_lev = 100; +uchar curr_sfx_vol = 100; +uchar curr_alog_vol = 100; + +void recompute_music_level(ushort vol) { + // curr_vol_lev=long_sqrt(100*vol); + curr_vol_lev = QVAR_TO_VOLUME(vol); + if (vol == 0) { + music_on = FALSE; + // stop_music_func(0,0,0); + } else { + if (!music_on) { + music_on = TRUE; + // start_music_func(0,0,0); + } + // mlimbs_change_master_volume(curr_vol_lev); + } + MacTuneUpdateVolume(); +} + +void recompute_digifx_level(ushort vol) { + sfx_on = (vol != 0); + curr_sfx_vol = QVAR_TO_VOLUME(vol); + if (sfx_on) { +#ifdef DEMO + play_digi_fx(73, 1); +#else + // play a sample (if not alreay playing) + if (!digi_fx_playing(SFX_NEAR_1, NULL)) + play_digi_fx(SFX_NEAR_1, 1); + // update volume (main loop is not running at this point) + sound_frame_update(); +#endif + } else { +#ifdef AUDIOLOGS + audiolog_stop(); +#endif + stop_digi_fx(); + } +} + +#ifdef AUDIOLOGS +void recompute_audiolog_level(ushort vol) { + curr_alog_vol = QVAR_TO_VOLUME(vol); + sound_frame_update(); +} +#endif + +#pragma disable_message(202) +void digi_toggle_deal(uchar offon) { + int vol; + vol = (sfx_on) ? 100 : 0; + recompute_digifx_level(vol); + QUESTVAR_SET(SFX_VOLUME_QVAR, vol); +} + +#ifdef AUDIOLOGS +void audiolog_dealfunc(short val) { + if (!val) + audiolog_stop(); + QUESTVAR_SET(ALOG_OPT_QVAR, audiolog_setting); +} +#endif + +char hack_digi_channels = 1; + +void digichan_dealfunc(short val) { + hack_digi_channels = val; + switch (hack_digi_channels) { + case 0: + cur_digi_channels = 2; + break; + case 1: + cur_digi_channels = 4; + break; + case 2: + cur_digi_channels = 8; + break; + } + QUESTVAR_SET(DIGI_CHANNELS_QVAR, hack_digi_channels); + // snd_set_digital_channels(cur_digi_channels); +} + +static void seqer_dealfunc(short val) { +// INFO("Selected MIDI device %d", val); + gShockPrefs.soMidiOutput = 0; + ReloadDecXMI(); // Reload Midi decoder + soundopt_screen_init(); + (void)val; +} + +static void midi_output_dealfunc(short val) { +// INFO("Selected MIDI output %d", val); + ReloadDecXMI(); // Reload Midi decoder + soundopt_screen_init(); + (void)val; +} + +#pragma enable_message(202) + +#define SLIDER_OFFSET_3 0 +void soundopt_screen_init() { + LGRect r; + char retkey; + int i = 0; + + clear_obuttons(); + + standard_button_rect(&r, i, 2, 2, 5); + retkey = tolower(get_temp_string(REF_STR_AilThreeText)[0]); + multi_init(i, retkey, REF_STR_AilThreeText, REF_STR_DigiChannelState, ID_NULL, sizeof(hack_digi_channels), + &hack_digi_channels, 3, digichan_dealfunc, &r); + i++; + + standard_button_rect(&r, i, 2, 2, 5); + retkey = tolower(get_temp_string(REF_STR_AilThreeText + 1)[0]); + // multi_init(i, retkey, REF_STR_AilThreeText+1, REF_STR_StereoReverseState, NULL, + // sizeof(snd_stereo_reverse), &snd_stereo_reverse, 2, NULL, &r); + // i++; + +#ifdef AUDIOLOGS + standard_button_rect(&r, i, 2, 2, 5); + retkey = tolower(get_temp_string(REF_STR_MusicText + 3)[0]); + multi_init(i, retkey, REF_STR_MusicText + 3, REF_STR_AudiologState, ID_NULL, sizeof(audiolog_setting), + &audiolog_setting, 3, audiolog_dealfunc, &r); + i++; +#endif + + standard_button_rect(&r, i, 2, 2, 5); + multi_init(i, 'p', REF_STR_Seqer, REF_STR_ADLMIDI, ID_NULL, + sizeof(gShockPrefs.soMidiBackend), &gShockPrefs.soMidiBackend, OPT_SEQ_Max, seqer_dealfunc, &r); + i++; +/* standard button is too narrow, so use a slider instead + const unsigned int numMidiOutputs = GetOutputCountXMI(); + INFO("numMidiOutputs=%d", numMidiOutputs); + standard_button_rect(&r, i, 2, 2, 5); + multi_init(i, 'o', REF_STR_MidiOut, REF_STR_MidiOutX, ID_NULL, + sizeof(gShockPrefs.soMidiOutput), &gShockPrefs.soMidiOutput, numMidiOutputs, midi_output_dealfunc, &r); + i++; +*/ + unsigned int midiOutputCount = GetOutputCountXMI(); + if (midiOutputCount > 1) + { + standard_slider_rect(&r, i, 2, 5); + // this makes it double-wide i guess? + r.lr.x += (r.lr.x - r.ul.x); + slider_init(i, REF_STR_MidiOutX + gShockPrefs.soMidiOutput, sizeof(gShockPrefs.soMidiOutput), FALSE, &gShockPrefs.soMidiOutput, midiOutputCount - 1, + 0, midi_output_dealfunc, &r); + i++; + } + else if (midiOutputCount == 1) + { + // just show a text label + standard_button_rect(&r, i, 1, 2, 10); + textwidget_init(i, BUTTON_COLOR, REF_STR_MidiOutX, &r); + i++; + } + + standard_button_rect(&r, 5, 2, 2, 5); + retkey = tolower(get_temp_string(REF_STR_MusicText + 2)[0]); + pushbutton_init(RETURN_BUTTON, retkey, REF_STR_MusicText + 2, wrapper_pushbutton_func, &r); + + // FIXME: Cannot pass a keycode with modifier flags as uchar + keywidget_init(QUIT_BUTTON, /*KB_FLAG_ALT |*/ 'x', wrapper_pushbutton_func); + opanel_redraw(TRUE); +} + +void sound_screen_init(void) { + LGRect r; + uchar sliderbase; + char retkey; + char slider_offset = 0; +#ifdef AUDIOLOGS + slider_offset = 10; +#endif + + clear_obuttons(); + + if (music_card) { + standard_slider_rect(&r, 0, 2, 5); + // let's double the width of these things, eh? + r.lr.x += (r.lr.x - r.ul.x); + r.ul.y -= slider_offset; + r.lr.y -= slider_offset; + sliderbase = r.lr.x - r.ul.x - 2; + slider_init(0, REF_STR_MusicText, sizeof(ushort), TRUE, &player_struct.questvars[MUSIC_VOLUME_QVAR], 100, + sliderbase, recompute_music_level, &r); + } else { + standard_button_rect(&r, 0, 2, 2, 5); + r.lr.x += (r.lr.x - r.ul.x); + r.ul.y -= slider_offset / 2; + r.lr.y -= slider_offset / 2; + textwidget_init(0, BUTTON_COLOR, REF_STR_MusicFeedbackText + 2, &r); + } + + if (digi_gain) { + standard_slider_rect(&r, 3, 2, 5); + r.lr.x += (r.lr.x - r.ul.x); + r.ul.y -= slider_offset; + r.lr.y -= slider_offset; + slider_init(1, REF_STR_MusicText + 1, sizeof(ushort), FALSE, &player_struct.questvars[SFX_VOLUME_QVAR], 100, + sliderbase, recompute_digifx_level, &r); + } else { + standard_button_rect(&r, 3, 2, 2, 5); + r.ul.y -= slider_offset; + r.lr.y -= slider_offset; + multi_init(1, get_temp_string(REF_STR_MusicText + 1)[0], REF_STR_MusicText + 1, REF_STR_OffonText, + REF_STR_MusicFeedbackText + 5, sizeof(sfx_on), &sfx_on, 2, digi_toggle_deal, &r); + } + +#ifdef AUDIOLOGS + standard_slider_rect(&r, 6, 2, 5); + r.lr.x += (r.lr.x - r.ul.x); + r.ul.y -= slider_offset; + r.lr.y -= slider_offset; + slider_init(2, REF_STR_MusicText + 4, sizeof(ushort), FALSE, &player_struct.questvars[ALOG_VOLUME_QVAR], 100, + sliderbase, recompute_audiolog_level, &r); +#endif + + standard_button_rect(&r, 2, 2, 2, 5); + retkey = tolower(get_temp_string(REF_STR_AilThreeText + 2)[0]); + pushbutton_init(AUDIO_OPT_BUTTON, retkey, REF_STR_AilThreeText + 2, wrapper_pushbutton_func, &r); + + standard_button_rect(&r, 5, 2, 2, 5); + retkey = tolower(get_temp_string(REF_STR_MusicText + 2)[0]); + pushbutton_init(RETURN_BUTTON, retkey, REF_STR_MusicText + 2, wrapper_pushbutton_func, &r); + + // FIXME: Cannot pass a keycode with modifier flags as uchar + keywidget_init(QUIT_BUTTON, /*KB_FLAG_ALT |*/ 'x', wrapper_pushbutton_func); + + opanel_redraw(TRUE); +} + + // + // THE OPTIONS SCREEN: Initialization, update funcs + // + + /*void gamma_dealfunc(ushort gamma_qvar) + { + fix gamma; + + // gamma=FIX_UNIT-fix_make(0,gamma_qvar); + // gamma=fix_mul(gamma,gamma)+(FIX_UNIT/2); + gamma=QVAR_TO_GAMMA(gamma_qvar); + gr_set_gamma_pal(0,256,gamma); + }*/ + +#ifdef SVGA_SUPPORT +uchar wrapper_screenmode_hack = FALSE; +void screenmode_change(uchar new_mode) { + extern short mode_id; + mode_id = new_mode; + QUESTVAR_SET(SCREENMODE_QVAR, new_mode); + change_mode_func(0, 0, _current_loop); + wrapper_screenmode_hack = TRUE; + + INFO("Changed screen mode to %i\n", mode_id); + wrapper_panel_close(TRUE); +} +#endif + +void language_change(uchar lang) { + extern int string_res_file, mfdart_res_file; + extern char *mfdart_files[]; + extern char *language_files[]; + + ResCloseFile(string_res_file); + ResCloseFile(mfdart_res_file); + + mfdart_res_file = ResOpenFile(mfdart_files[lang]); + if (mfdart_res_file < 0) + critical_error(CRITERR_RES | 2); + + string_res_file = ResOpenFile(language_files[lang]); + if (string_res_file < 0) + critical_error(CRITERR_RES | 0); + + QUESTVAR_SET(LANGUAGE_QVAR, lang); + + // in case we got here from interpret_qvars, and thus + // haven't set this yet + which_lang = lang; + + invent_language_change(); + mfd_language_change(); + side_icon_language_change(); + // free_options_cursor(); + make_options_cursor(); +} + +void language_dealfunc(uchar lang) { + language_change(lang); + + render_run(); + opanel_redraw(FALSE); +} + +void dclick_dealfunc(ushort dclick_qvar) { + uiDoubleClickDelay = QVAR_TO_DCLICK(dclick_qvar, 0); + uiDoubleClickTime = QVAR_TO_DCLICK(dclick_qvar, 1); +} + +void joysens_dealfunc(ushort joysens_qvar) { + extern fix inpJoystickSens; + + inpJoystickSens = QVAR_TO_JOYSENS(joysens_qvar); +} + +#pragma disable_message(202) +void center_joy_go(uchar butid) { + + // recenter_joystick(0,0,0); + joystick_screen_init(); +} +#pragma enable_message(202) + +void center_joy_pushbutton_func(uchar butid) { + int i; + string_message_info(REF_STR_CenterJoyPrompt); + + // take over this button, null the other buttons + // except for RETURN and QUIT; + + for (i = 0; i < MAX_OPTION_BUTTONS; i++) { + if (i == butid) + keywidget_init(i, KEY_ENTER, center_joy_go); + else if (i != RETURN_BUTTON && i != QUIT_BUTTON) + OButtons[i].evmask = 0; + } +} + +static void renderer_dealfunc(bool unused) { + uiHideMouse(NULL); + render_run(); + if (full_game_3d) { + // update stored background bitmap and redraw menu + ss_get_bitmap(&inv_view360_canvas.bm, GAME_MESSAGE_X, GAME_MESSAGE_Y); + opanel_redraw(FALSE); + } + uiShowMouse(NULL); + // recalculate menu in case a button needs to be added or removed + video_screen_init(); + // suppress compiler warning + (void)unused; +} + +void detail_dealfunc(uchar det) { + + change_detail_level(det); + uiHideMouse(NULL); + render_run(); + if (full_game_3d) + opanel_redraw(FALSE); + uiShowMouse(NULL); +} + +void mousehand_dealfunc(ushort lefty) { + // mouse_set_lefty(lefty); +} + +#if defined(VFX1_SUPPORT) || defined(CTM_SUPPORT) +#pragma disable_message(202) +void headset_stereo_dealfunc(uchar st_on) { + extern uchar inp6d_headset; + extern uchar inp6d_stereo; + // extern uchar ui_stereo_on; + if ((inp6d_headset) && (i6d_device != I6D_ALLPRO)) { + // ui_stereo_on = inp6d_stereo; + if (!inp6d_stereo) + i6_video(I6VID_CLOSEDOWN, NULL); // this will want to be I6VID_STR_CLOSE at some point + else { + if (i6_video(I6VID_STR_START, NULL)) { + Warning(("Headset stereo startup failed!\n")); + return; + } + } + } +} + +void headset_tracking_dealfunc(uchar tr_on) { + Warning(("tracking now %d!\n", tr_on)); + return; +} + +void headset_fov_dealfunc(int hackval) { + inp6d_curr_fov = hack_headset_fov + HEADSET_FOV_MIN; + Warning(("FOV now %d!\n", inp6d_curr_fov)); + return; +} +#pragma enable_message(202) +#endif + +#pragma disable_message(202) +void olh_dealfunc(uchar olh) { + toggle_olh_func(0, 0, 0); +} +#pragma enable_message(202) + +#ifdef STEREO_SUPPORT +#define INITIAL_OCULAR_DIST fix_make(3, 0x4000) +#endif + +ushort wrap_joy_type = 0; +ushort high_joy_flags; +void joystick_type_func(ushort new_joy_type) { + extern uchar joystick_count; + // joystick_count = joy_init(high_joy_flags | new_joy_type); + // config_set_single_value("joystick",CONFIG_INT_TYPE,(config_valtype)(high_joy_flags|new_joy_type)); + joystick_screen_init(); +} + +void joystick_screen_init(void) { + LGRect r; + int i = 0; + char *keys; + extern uchar inp6d_headset; + uchar sliderbase; + + extern uchar joystick_count; + keys = get_temp_string(REF_STR_KeyEquivs6); + clear_obuttons(); + + standard_button_rect(&r, i, 2, 2, 1); + multi_init(i, keys[i], REF_STR_JoystickType, REF_STR_JoystickTypes, ID_NULL, sizeof(wrap_joy_type), + &wrap_joy_type, 4, joystick_type_func, &r); + i++; + + standard_button_rect(&r, i, 2, 2, 1); + pushbutton_init(i, keys[i], REF_STR_CenterJoy, center_joy_pushbutton_func, &r); + if (!joystick_count && !inp6d_headset) { + dim_pushbutton(i); + } + i++; + + if (joystick_count) { + standard_slider_rect(&r, i, 2, 1); + sliderbase = (r.lr.x - r.ul.x - 2) >> 1; + slider_init(i, REF_STR_JoystickSens, sizeof(ushort), FALSE, &player_struct.questvars[JOYSENS_QVAR], 256, + sliderbase, joysens_dealfunc, &r); + } + i++; + + standard_button_rect(&r, 5, 2, 2, 1); + pushbutton_init(RETURN_BUTTON, keys[i], REF_STR_OptionsText + 5, wrapper_pushbutton_func, &r); + + // FIXME: Cannot pass a keycode with modifier flags as uchar + keywidget_init(QUIT_BUTTON, /*KB_FLAG_ALT |*/ 'x', wrapper_pushbutton_func); + + opanel_redraw(TRUE); +} + +#pragma disable_message(202) +void joystick_button_func(uchar butid) { joystick_screen_init(); } +#pragma enable_message(202) + +void input_screen_init(void) { + LGRect r; + char *keys; + int i = 0; + uchar sliderbase; + extern uchar inp6d_headset; + + keys = get_temp_string(REF_STR_KeyEquivs1); + clear_obuttons(); + + standard_button_rect(&r, i, 2, 2, 1); + r.ul.x -= 1; + multi_init(i, keys[0], REF_STR_OptionsText + 0, REF_STR_OffonText, REF_STR_PopupCursFeedback, sizeof(popup_cursors), + &popup_cursors, 2, NULL, &r); + i++; + + standard_button_rect(&r, i, 2, 2, 1); + multi_init(i, keys[1], REF_STR_OptionsText + 1, REF_STR_MouseHand, REF_STR_HandFeedback, + sizeof(player_struct.questvars[MOUSEHAND_QVAR]), &player_struct.questvars[MOUSEHAND_QVAR], 2, + mousehand_dealfunc, &r); + i++; + + standard_slider_rect(&r, i, 2, 1); + r.ul.x -= 1; + sliderbase = ((r.lr.x - r.ul.x - 3) * (FIX_UNIT / 3)) / USHRT_MAX; + slider_init(i, REF_STR_DoubleClick, sizeof(ushort), FALSE, &player_struct.questvars[DCLICK_QVAR], USHRT_MAX, + sliderbase, dclick_dealfunc, &r); + i++; + + standard_button_rect(&r, i, 2, 2, 1); + r.ul.x -= 1; + pushbutton_init(i, keys[2], REF_STR_Joystick, joystick_button_func, &r); + i++; + + standard_button_rect(&r, i, 2, 2, 1); + r.ul.x -= 1; + multi_init(i, keys[3], REF_STR_MousLook, REF_STR_MousNorm, ID_NULL, + sizeof(gShockPrefs.goInvertMouseY), &gShockPrefs.goInvertMouseY, 2, NULL, &r); + i++; + + standard_button_rect(&r, 5, 2, 2, 1); + pushbutton_init(RETURN_BUTTON, keys[3], REF_STR_OptionsText + 5, wrapper_pushbutton_func, &r); + + // FIXME: Cannot pass a keycode with modifier flags as uchar + keywidget_init(QUIT_BUTTON, /*KB_FLAG_ALT |*/ 'x', wrapper_pushbutton_func); + + opanel_redraw(TRUE); +} + +//gamma param not used here; see SetSDLPalette() in Shock.c +void gamma_slider_dealfunc(ushort gamma_qvar) { + gr_set_gamma_pal(0, 256, 0); + + uiHideMouse(NULL); + render_run(); + if (full_game_3d) + opanel_redraw(FALSE); + uiShowMouse(NULL); +} + +void video_screen_init(void) { + LGRect r; + int i; + char *keys; +#ifdef SVGA_SUPPORT + extern short mode_id; +#endif + uchar sliderbase; +#ifdef STEREO_SUPPORT + extern uchar inp6d_headset; +#endif + + keys = get_temp_string(REF_STR_KeyEquivs3); + clear_obuttons(); + i = 0; + +#ifdef USE_OPENGL + // renderer + if(can_use_opengl()) { + standard_button_rect(&r, i, 2, 2, 2); + multi_init(i, 'g', REF_STR_Renderer, REF_STR_Software, ID_NULL, + sizeof(gShockPrefs.doUseOpenGL), &gShockPrefs.doUseOpenGL, 2, renderer_dealfunc, &r); + i++; + } +#endif + +#ifdef SVGA_SUPPORT + // video mode + standard_button_rect(&r, i, 2, 2, 2); + pushbutton_init(SCREENMODE_BUTTON, keys[0], REF_STR_VideoText, wrapper_pushbutton_func, &r); + i++; +#endif + + // detail level + standard_button_rect(&r, i, 2, 2, 2); + r.lr.x += 2; + multi_init(i, keys[1], REF_STR_OptionsText + 4, REF_STR_DetailLvl, REF_STR_DetailLvlFeedback, + sizeof(_fr_global_detail), &_fr_global_detail, 4, detail_dealfunc, &r); + i++; + + // gamma + standard_slider_rect(&r, i, 2, 2); + r.ul.x = r.ul.x + 1; + sliderbase = ((r.lr.x - r.ul.x - 1) * 29 / 100); + slider_init(i, REF_STR_OptionsText + 3, sizeof(ushort), TRUE, &(gShockPrefs.doGamma), 100, + sliderbase, gamma_slider_dealfunc, &r); + i++; + +#if defined(VFX1_SUPPORT) || defined(CTM_SUPPORT) + standard_button_rect(&r, i, 2, 2, 2); + pushbutton_init(HEADSET_BUTTON, keys[2], REF_STR_HeadsetText, wrapper_pushbutton_func, &r); + if (!inp6d_headset) + dim_pushbutton(HEADSET_BUTTON); + i++; +#endif + +#ifdef USE_OPENGL + // textre filter + if(can_use_opengl() && gShockPrefs.doUseOpenGL) { + standard_button_rect(&r, i, 2, 2, 2); + multi_init(i, 't', REF_STR_TextFilt, REF_STR_TFUnfil, ID_NULL, + sizeof(gShockPrefs.doTextureFilter), &gShockPrefs.doTextureFilter, 2, renderer_dealfunc, &r); + i++; + } +#endif + + // return (fixed at position 5) + standard_button_rect(&r, 5, 2, 2, 2); + pushbutton_init(RETURN_BUTTON, keys[3], REF_STR_OptionsText + 5, wrapper_pushbutton_func, &r); + + // FIXME: Cannot pass a keycode with modifier flags as uchar + keywidget_init(QUIT_BUTTON, /*KB_FLAG_ALT |*/ 'x', wrapper_pushbutton_func); + + opanel_redraw(TRUE); +} + +#if defined(VFX1_SUPPORT) || defined(CTM_SUPPORT) +void headset_screen_init(void) { + LGRect r; + int i; + char *keys; +#ifdef STEREO_SUPPORT + extern uchar inp6d_stereo; + extern int inp6d_stereo_div; +#endif + + keys = get_temp_string(REF_STR_KeyEquivs5); + + clear_obuttons(); + + i = 0; + + standard_button_rect(&r, i, 2, 2, 2); + pushbutton_init(HEAD_RECENTER_BUTTON, keys[0], REF_STR_HeadsetText + 1, wrapper_pushbutton_func, &r); + +#ifdef STEREO_SUPPORT + i++; + standard_slider_rect(&r, i, 2, 2); + r.ul.x -= 1; + slider_init(i, REF_STR_HeadsetText + 2, sizeof(inp6d_stereo_div), FALSE, &inp6d_stereo_div, fix_make(10, 0), + INITIAL_OCULAR_DIST, NULL, &r); + + i++; + standard_button_rect(&r, i, 2, 2, 2); + multi_init(i, keys[1], REF_STR_HeadsetText + 3, REF_STR_OffonText, ID_NULL, sizeof(inp6d_stereo), + &inp6d_stereo, 2, headset_stereo_dealfunc, &r); + + if (i6d_device == I6D_ALLPRO) + dim_pushbutton(i); + + i++; + standard_button_rect(&r, i, 2, 2, 2); + multi_init(i, keys[3], REF_STR_MoreHeadset + 1, REF_STR_OffonText, ID_NULL, sizeof(headset_track), + &headset_track, 2, headset_tracking_dealfunc, &r); + + i++; + standard_slider_rect(&r, i, 2, 2); + r.ul.x -= 1; + slider_init(i, REF_STR_MoreHeadset, sizeof(hack_headset_fov), FALSE, &hack_headset_fov, + HEADSET_FOV_MAX - HEADSET_FOV_MIN, inp6d_real_fov - HEADSET_FOV_MIN, headset_fov_dealfunc, &r); +#endif + + // Standard return button and other bureaucracy + standard_button_rect(&r, 5, 2, 2, 2); + pushbutton_init(RETURN_BUTTON, keys[2], REF_STR_OptionsText + 5, wrapper_pushbutton_func, &r); + keywidget_init(QUIT_BUTTON, KB_FLAG_ALT | 'x', wrapper_pushbutton_func); + opanel_redraw(TRUE); +} +#endif + +#ifdef SVGA_SUPPORT +void screenmode_screen_init(void) { + LGRect r; + int i; + char *keys; + + if (wrapper_screenmode_hack && !(can_use_opengl() && gShockPrefs.doUseOpenGL)) { + uiHideMouse(NULL); + render_run(); + uiShowMouse(NULL); + wrapper_screenmode_hack = FALSE; + } + + keys = get_temp_string(REF_STR_KeyEquivs4); + + clear_obuttons(); + + for (i = 0; i < 5; i++) { + extern short svga_mode_data[]; + uchar mode_ok = FALSE; + char j = 0; + standard_button_rect(&r, i, 2, 2, 2); + pushbutton_init(i, keys[i], REF_STR_ScreenModeText + i, screenmode_change, &r); + while ((grd_info.modes[j] != -1) && !mode_ok) { + if (grd_info.modes[j] == svga_mode_data[i]) + mode_ok = TRUE; + j++; + } + if (!mode_ok) + dim_pushbutton(i); + else if (i == convert_use_mode) + bright_pushbutton(i); + } + + standard_button_rect(&r, 5, 2, 2, 2); + pushbutton_init(RETURN_BUTTON, keys[2], REF_STR_OptionsText + 5, wrapper_pushbutton_func, &r); + + // FIXME: Cannot pass a keycode with modifier flags as uchar + keywidget_init(QUIT_BUTTON, /*KB_FLAG_ALT |*/ 'x', wrapper_pushbutton_func); + + opanel_redraw(TRUE); +} +#endif + +void options_screen_init(void) { + LGRect r; + char *keys; + int i = 0; + + keys = get_temp_string(REF_STR_KeyEquivs2); + clear_obuttons(); + + // olh_temp=(QUESTBIT_GET(OLH_QBIT)==0); + + olh_temp = olh_active; + + // okay, I admit it, we're going to tweak these "standard" + // button rects a little bit. + + standard_button_rect(&r, 0, 2, 2, 2); + r.ul.x -= 2; + multi_init(i, keys[i], REF_STR_OptionsText + 2, REF_STR_TerseText, REF_STR_TerseFeedback, + sizeof(gShockPrefs.goMsgLength), &(gShockPrefs.goMsgLength), 2, NULL, &r); + i++; + + i++; + + standard_button_rect(&r, 1, 2, 2, 2); + multi_init(i, keys[i], REF_STR_OnlineHelp, REF_STR_OffonText, ID_NULL, sizeof(olh_temp), &olh_temp, 2, + olh_dealfunc, &r); + i++; + + i++; + + standard_button_rect(&r, 2, 2, 2, 2); + multi_init(i, keys[i], REF_STR_Language, REF_STR_Languages, ID_NULL, sizeof(which_lang), &which_lang, 3, + language_dealfunc, &r); + i++; + + standard_button_rect(&r, 5, 2, 2, 2); + r.lr.x += 2; + pushbutton_init(RETURN_BUTTON, keys[i], REF_STR_OptionsText + 5, wrapper_pushbutton_func, &r); + + // FIXME: Cannot pass a keycode with modifier flags as uchar + keywidget_init(QUIT_BUTTON, /*KB_FLAG_ALT |*/ 'x', wrapper_pushbutton_func); + + opanel_redraw(TRUE); +} + +#pragma disable_message(202) +uchar wrapper_options_func(ushort keycode, uint32_t context, intptr_t data) { + wrapper_start(wrapper_init); + return (OK); +} +#pragma enable_message(202); + +// +// THE LOAD GAME SCREEN: Initialization, update funcs +// + +#pragma disable_message(202) +void load_dealfunc(uchar butid, uchar index) { + begin_wait(); + Poke_SaveName(index); + // Spew(DSRC_EDITOR_Save,("attempting to load from %s\n",save_game_name)); + + if (load_game(save_game_name) != OK) { + WARN("%s: Load game failed!", __FUNCTION__); + } else { + INFO("Game %d loaded!", index); + // Spew(DSRC_EDITOR_Restore,("Game %d loaded!\n",index)); + } + end_wait(); + // spoof_mouse_event(); + wrapper_panel_close(TRUE); +} +#pragma enable_message(202) + +void load_screen_init(void) { + extern uchar valid_save; + + clear_obuttons(); + + textlist_init(0, *comments, NUM_SAVE_SLOTS, SAVE_COMMENT_LEN, FALSE, 0, valid_save, valid_save, REF_STR_UnusedSave, + BUTTON_COLOR, WHITE, BUTTON_COLOR + 2, 0, load_dealfunc, NULL); + + // FIXME: Cannot pass a keycode with modifier flags as uchar + keywidget_init(QUIT_BUTTON, /*KB_FLAG_ALT |*/ 'x', wrapper_pushbutton_func); + + opanel_redraw(TRUE); +} + +// +// THE SAVE GAME SCREEN: Initialization, update funcs +// + +void save_dealfunc(uchar butid, uchar index) { + if (!ObjSysOkay()) { + string_message_info(REF_STR_ObjSysBad); + savegame_verify = index; + verify_screen_init(save_verify_pushbutton_handler, save_verify_slorker); + } else { + message_info(""); + do_savegame_guts(index); + } +} + +void save_screen_init(void) { + extern uchar valid_save; + + clear_obuttons(); + + textlist_init(0, *comments, NUM_SAVE_SLOTS, SAVE_COMMENT_LEN, TRUE, 0xFFFF, 0xFFFF, valid_save, REF_STR_UnusedSave, + BUTTON_COLOR, WHITE, BUTTON_COLOR + 2, REF_STR_EnterSaveString, save_dealfunc, NULL); + + // FIXME: Cannot pass a keycode with modifier flags as uchar + keywidget_init(QUIT_BUTTON, /*KB_FLAG_ALT |*/ 'x', wrapper_pushbutton_func); + + opanel_redraw(TRUE); +} + +void wrapper_start(void (*init)(void)) { + if (wrapper_panel_on) + return; + inv_last_page = inventory_page; + if (!game_paused) + pause_game_func(0, 0, 0); + if (!full_game_3d) + message_info(""); + inventory_page = -1; + wrapper_panel_on = TRUE; + suspend_game_time(); + opt_font = ResLock(OPTIONS_FONT); +#ifndef STATIC_BUTTON_STORE + OButtons = (opt_button *)(_offscreen_mfd.bm.bits); + fv = full_visible; + full_visible = 0; +#endif + render_run(); //move here to fix ghost mouse cursor + uiHideMouse(NULL); + if (full_game_3d) { +#ifdef SVGA_SUPPORT + uchar old_over = gr2ss_override; +#endif + gr_push_canvas(grd_screen_canvas); +#ifdef SVGA_SUPPORT + gr2ss_override = OVERRIDE_ALL; +#endif + ss_get_bitmap(&inv_view360_canvas.bm, GAME_MESSAGE_X, GAME_MESSAGE_Y); +#ifdef SVGA_SUPPORT + gr2ss_override = old_over; +#endif + gr_pop_canvas(); + } else + inventory_clear(); + uiShowMouse(NULL); + uiInstallRegionHandler(inventory_region, UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE, opanel_mouse_handler, 0, + &wrap_id); + uiInstallRegionHandler(inventory_region, UI_EVENT_KBD_COOKED, opanel_kb_handler, 0, &wrap_key_id); + uiGrabFocus(inventory_region, UI_EVENT_KBD_COOKED | UI_EVENT_MOUSE); + region_set_invisible(inventory_region, FALSE); + reset_input_system(); + init(); +} + +#define NEEDED_DISKSPACE 630000 +errtype check_free_diskspace(int *needed) { + /*struct diskfree_t freespace; + _dos_getdiskfree(0, &freespace); + if (freespace.avail_clusters * freespace.sectors_per_cluster * freespace.bytes_per_sector < NEEDED_DISKSPACE) + { + *needed = NEEDED_DISKSPACE - (freespace.avail_clusters * freespace.sectors_per_cluster * + freespace.bytes_per_sector); return(ERR_NOMEM); + } + *needed = 0;*/ + return (OK); +} + +errtype do_savegame_guts(uchar slot) { + extern uchar valid_save; + errtype retval = OK; + + begin_wait(); + if (!(valid_save & (1 << slot))) { + int needed; + // char buf1[128],buf2[128]; + if (check_free_diskspace(&needed) == ERR_NOMEM) { + // lg_sprintf(buf2, get_string(REF_STR_InsufficientDisk, buf1, 128), needed); + string_message_info(REF_STR_InsufficientDisk); + retval = ERR_NOMEM; + } + } + if (retval == OK) { + Poke_SaveName(slot); + if (save_game(save_game_name, comments[slot]) != OK) { + ERROR("Save game failed!"); + message_info("Game save failed!"); + // strcpy(comments[comment_mode], original_comment); + retval = ERR_NOEFFECT; + valid_save &= ~(1 << slot); + } else + // Spew(DSRC_EDITOR_Save, ("Game %d saved!\n", slot)); + if (retval == OK) + valid_save |= 1 << slot; + } + end_wait(); + // spoof_mouse_event(); + if (retval == OK) + wrapper_panel_close(TRUE); + return (retval); +} + + //#endif // NOT_YET + +#pragma disable_message(202) +uchar wrapper_region_mouse_handler(uiEvent *ev, LGRegion *r, intptr_t data) { + /*if (global_fullmap->cyber) + { + uiSetRegionDefaultCursor(r,NULL); + return FALSE; + } + else*/ + + uiSetRegionDefaultCursor(r, &option_cursor); + + if (ev->mouse_data.action & MOUSE_DOWN) { + wrapper_options_func(0, 0, TRUE); + return TRUE; + } + return FALSE; +} +#pragma enable_message(202) + +errtype make_options_cursor(void) { + char *s; + short w, h; + LGPoint hot = {0, 0}; + grs_canvas cursor_canv; + short orig_w; + extern uchar svga_options_cursor_bits[]; + uchar old_over = gr2ss_override; + gr2ss_override = OVERRIDE_ALL; + + orig_w = w = res_bm_width(REF_IMG_bmOptionCursor); + h = res_bm_height(REF_IMG_bmOptionCursor); + ss_point_convert(&w, &h, FALSE); + gr_init_bm(&option_cursor_bmap, svga_options_cursor_bits, BMT_FLAT8, BMF_TRANS, w, h); + gr_make_canvas(&option_cursor_bmap, &cursor_canv); + gr_push_canvas(&cursor_canv); + gr_clear(0); + s = get_temp_string(REF_STR_ClickForOptions); + gr_set_font(ResLock(OPTIONS_FONT)); + gr_string_wrap(s, orig_w - 3); + gr_string_size(s, &w, &h); + gr_set_fcolor(0xB8); + ss_rect(1, 1, w + 2, h + 2); + gr_set_fcolor(0xD3); + ss_string(s, 2, 1); + gr_font_string_unwrap(s); + uiMakeBitmapCursor(&option_cursor, &option_cursor_bmap, hot); + gr_pop_canvas(); + ResUnlock(OPTIONS_FONT); + cursor_loaded = TRUE; + gr2ss_override = old_over; + + return OK; +} + +/*void free_options_cursor(void) +{ +#ifndef SVGA_SUPPORT + if(cursor_loaded) + Free(option_cursor_bmap.bits); +#endif +}*/ + +errtype wrapper_create_mouse_region(LGRegion *root) { + errtype err; + int id; + LGRect r = {{0, 0}, {STATUS_X, STATUS_HEIGHT}}; + LGRegion *reg = &(options_mouseregion[free_mouseregion++]); + + err = region_create(root, reg, &r, 2, 0, REG_USER_CONTROLLED | AUTODESTROY_FLAG, NULL, NULL, NULL, NULL); + if (err != OK) + return err; + err = uiInstallRegionHandler(reg, UI_EVENT_MOUSE | UI_EVENT_MOUSE_MOVE, wrapper_region_mouse_handler, + 0, &id); + if (err != OK) + return err; + if (!cursor_loaded) { + err = make_options_cursor(); + if (err != OK) + return err; + } + uiSetRegionDefaultCursor(reg, &option_cursor); + return OK; +} + +//#ifdef NOT_YET // +#pragma disable_message(202) +uchar saveload_hotkey_func(ushort keycode, uint32_t context, intptr_t data) { +#ifdef DEMO + return (TRUE); +#else + if ((!data) && (!can_save())) + return (TRUE); + wrapper_start(data ? load_screen_init : save_screen_init); + string_message_info(data ? REF_STR_LoadSlot : REF_STR_SaveSlot); + return (TRUE); +#endif +} + +uchar demo_quit_func(ushort keycode, uint32_t context, intptr_t data) { + wrapper_start(quit_verify_init); + string_message_info(REF_STR_QuitConfirm); + return (TRUE); +} +#pragma enable_message(202) + +//#endif // NOT_YET diff --git a/engine/src/Icon%0D b/engine/src/Icon%0D new file mode 100644 index 0000000..e69de29 diff --git a/engine/src/Libraries/2D/Source/2d.h b/engine/src/Libraries/2D/Source/2d.h new file mode 100644 index 0000000..3b8bf2c --- /dev/null +++ b/engine/src/Libraries/2D/Source/2d.h @@ -0,0 +1,1650 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include "fix.h" +#ifndef __2D_H +#define __2D_H + +#include "GR/grs.h" +#include "plytyp.h" +#include "tmaps.h" + +#if defined(__cplusplus) +extern "C" { +#endif // !defined(__cplusplus) + +#pragma pack(push,2) + +typedef struct { + grs_point3d normal; + grs_point3d u_grad; + grs_point3d v_grad; +} grs_per_context; + +extern grs_sys_info grd_info; +extern grs_drvcap *grd_cap; +extern grs_drvcap grd_mode_cap; +extern void (**grd_driver_list[])(); +extern int grd_mode; +#define grd_scr_canv grd_screen_canvas +#define grd_vis_canv grd_visible_canvas +#define dr_screen grd_screen +#define dr_canvas grd_canvas +#define dr_scr_canv grd_screen_canvas +#define dr_vis_canv grd_visible_canvas +#define dr_bm grd_bm +#define dr_gc grd_gc +#define dr_ytab grd_ytab +#define dr_int_clip grd_int_clip +#define dr_fix_clip grd_fix_clip +#define dr_clip grd_clip +#define driver_func grd_driver_func +extern grs_screen *grd_screen; +extern uchar grd_default_pal[]; +extern uchar *grd_pal; +extern grs_rgb grd_default_bpal[]; +extern grs_rgb *grd_bpal; +extern uchar *grd_ipal; +extern grs_canvas *grd_screen_canvas; +extern grs_canvas *grd_visible_canvas; +extern grs_canvas *grd_canvas; +#define grd_bm (grd_canvas->bm) +#define grd_gc (grd_canvas->gc) +#define grd_ytab (grd_canvas->ytab) +#define grd_int_clip (grd_gc.clip.i) +#define grd_fix_clip (grd_gc.clip.f) +#define grd_clip (grd_int_clip) +extern void (**grd_pixel_table)(); +extern void (**grd_device_table)(); +extern void (**grd_canvas_table)(); +extern void (**grd_function_table)(); +enum { + BMT_DEVICE, + BMT_MONO, + BMT_FLAT8, + BMT_FLAT24, + BMT_RSD8, + BMT_TLUC8, + BMT_SPAN, + BMT_GEN, + BMT_TYPES +}; +#define REAL_BMT_TYPES BMT_SPAN +#define BMF_TRANS 1 +#define BMF_TLUC8 2 +extern void gr_init_bitmap + (grs_bitmap *bm, uchar *p, uchar type, ushort flags, short w, short h); +extern void gr_init_sub_bitmap + (grs_bitmap *sbm, grs_bitmap *dbm, short x, short y, short w, short h); +extern grs_bitmap *gr_alloc_bitmap + (uchar type, ushort flags, short w, short h); +#define gr_init_bm gr_init_bitmap +#define gr_init_sub_bm gr_init_sub_bitmap +#define gr_alloc_bm gr_alloc_bitmap +enum { + + GRC_PIXEL, +#define GRC_LINE GRC_PIXEL + GRC_WIRE_POLY_LINE, + GRC_DEGEN_LINE, + GRC_BITMAP, + GRC_STENCIL_BITMAP, + GRC_CLUT_BITMAP, + GRC_HFLIP_BITMAP, + GRC_CLUT_HFLIP_BITMAP, + GRC_MASK_BITMAP, + GRC_HDOUBLE_BITMAP, + GRC_VDOUBLE_BITMAP, + GRC_HVDOUBLE_BITMAP, + GRC_HDOUBLE_BLEND_BITMAP, + GRC_VDOUBLE_BLEND_BITMAP, + GRC_HVDOUBLE_BLEND_BITMAP, + GRC_SCALE, + GRC_TRANS_SCALE, + GRC_LIT_SCALE, + GRC_TRANS_LIT_SCALE, + GRC_CLUT_SCALE, + GRC_TRANS_CLUT_SCALE, + GRC_POLY, + GRC_MORE_POLY, + GRC_LIN, + GRC_TRANS_LIN, + GRC_LIT_LIN, + GRC_TRANS_LIT_LIN, + GRC_CLUT_LIN, + GRC_TRANS_CLUT_LIN, + GRC_BILIN, + GRC_TRANS_BILIN, + GRC_LIT_BILIN, + GRC_TRANS_LIT_BILIN, + GRC_CLUT_BILIN, + GRC_TRANS_CLUT_BILIN, + GRC_FLOOR, + GRC_TRANS_FLOOR, + GRC_LIT_FLOOR, + GRC_TRANS_LIT_FLOOR, + GRC_CLUT_FLOOR, + GRC_TRANS_CLUT_FLOOR, + GRC_WALL2D, + GRC_TRANS_WALL2D, + GRC_LIT_WALL2D, + GRC_TRANS_LIT_WALL2D, + GRC_CLUT_WALL2D, + GRC_TRANS_CLUT_WALL2D, + GRC_WALL1D, + GRC_TRANS_WALL1D, + GRC_LIT_WALL1D, + GRC_TRANS_LIT_WALL1D, + GRC_CLUT_WALL1D, + GRC_TRANS_CLUT_WALL1D, + GRC_PER, + GRC_TRANS_PER, + GRC_LIT_PER, + GRC_TRANS_LIT_PER, + GRC_CLUT_PER, + GRC_TRANS_CLUT_PER, + GRC_PER_VSCAN, + GRC_TRANS_PER_VSCAN, + GRC_LIT_PER_VSCAN, + GRC_TRANS_LIT_PER_VSCAN, + GRC_CLUT_PER_VSCAN, + GRC_TRANS_CLUT_PER_VSCAN, + GRD_FUNCS +}; +enum { + FILL_NORM, + FILL_CLUT, + FILL_XOR, + FILL_BLEND, + FILL_SOLID, + GRD_FILL_TYPES +}; +typedef void (*grt_function_table[GRD_FILL_TYPES][GRD_FUNCS*REAL_BMT_TYPES])(); +extern grt_function_table gen_function_table; +extern grt_function_table flat8_function_table; +extern grt_function_table flat8d_function_table; +extern grt_function_table modex_function_table; +extern grt_function_table bank8_function_table; +extern grt_function_table bank24_function_table; +enum { + GR_LINE, + GR_ILINE, + GR_HLINE, + GR_VLINE, + GR_SLINE, + GR_CLINE, + GR_WIRE_POLY_LINE, + GR_WIRE_POLY_SLINE, + GR_WIRE_POLY_CLINE, + GRD_LINE_TYPES +}; +typedef + void *grt_uline_fill; +typedef + void (*grt_uline_fill_v) (long, long, grs_vertex *, grs_vertex *); +typedef + void (*grt_uline_fill_xy) (short, short, short, long, long); +typedef + void (*grt_wire_poly_uline) (long, long, grs_vertex *, grs_vertex *); +typedef + void (*grt_wire_poly_ucline) (long, long, grs_vertex *, grs_vertex *); +typedef + grt_uline_fill grt_uline_fill_table[GRD_FILL_TYPES][GRD_LINE_TYPES]; +#define grt_wire_poly_usline grt_wire_poly_ucline; +extern grt_uline_fill *grd_uline_fill_vector; +extern grt_uline_fill_table *grd_uline_fill_table; +extern grt_uline_fill_table *grd_uline_fill_table_list[]; +extern grt_uline_fill gen_uline_fill_table[][GRD_LINE_TYPES]; +extern grt_uline_fill flat8_uline_fill_table[][GRD_LINE_TYPES]; +/* +// WH - not used, see lintab.c +extern grt_uline_fill bank8_uline_fill_table[][GRD_LINE_TYPES]; +extern grt_uline_fill bank24_uline_fill_table[][GRD_LINE_TYPES]; +extern grt_uline_fill modex_uline_fill_table[][GRD_LINE_TYPES]; +*/ +extern grt_function_table *grd_function_table_list[]; +extern grt_function_table *grd_function_fill_table; +#define gr_init_st(s,p,f) (s)->elem=(p), (s)->flags=(f) +#ifndef GRSTATE_H +#define GRSTATE_H +#define gr_push_state \ + ((int (*)())grd_pixel_table[PUSH_STATE]) +#define gr_pop_state \ + ((int (*)())grd_pixel_table[POP_STATE]) +#endif +extern int gr_init (void); +extern int gr_close (void); +#define GR_TEMP_USE_MEMSTACK +#ifdef GR_TEMP_USE_MEMSTACK +#define gr_alloc_temp temp_malloc +#define gr_free_temp temp_free +#else +extern void *gr_alloc_temp (int n); +extern void gr_free_temp (void *p); +#endif +extern grs_context grd_defgc; +extern void gr_set_canvas (grs_canvas *c); +extern int gr_push_canvas (grs_canvas *c); +extern grs_canvas *gr_pop_canvas (void); +extern void gr_make_canvas (grs_bitmap *bm, grs_canvas *c); +extern void gr_init_canvas (grs_canvas *c, uchar *p, int id, + short w, short h); +extern void gr_init_sub_canvas (grs_canvas *sc, grs_canvas *dc, + short x, short y, short w, short h); +extern grs_canvas *gr_alloc_canvas (int id, short w, short h); +extern void gr_free_canvas (grs_canvas *c); +extern grs_canvas *gr_alloc_sub_canvas (grs_canvas *c, short x, short y, + short w, short h); +extern void gr_free_sub_canvas (grs_canvas *c); +#define CLIP_NONE 0 +#define CLIP_LEFT 1 +#define CLIP_TOP 2 +#define CLIP_RIGHT 4 +#define CLIP_BOT 8 +#define CLIP_ALL 16 +extern int gr_clip_fix_code + (fix, fix); +extern int gr_clip_int_line + (short *x0, short *y0, short *x1, short *y1); +extern int gr_clip_fix_line + (long *x0, long *y0, long *x1, long *y1); +extern int gr_clip_fix_poly + (int n, fix *vlist, fix *clist); +extern int gr_clip_poly + (int n, int l, grs_vertex **vplist, grs_vertex ***pcplist); +extern int gr_clip_spoly + (int n, fix *vlist, fix *clist, fix *ilist, fix *cilist); +extern int gr_clip_fix_cpoly + (int n, fix *vlist, grs_rgb *blist, fix *clist, grs_rgb *cblist); +extern int gr_clip_rect + (short *left, short *top, short *right, short *bot); +extern int gr_clip_mono_bitmap + (grs_bitmap *bm, short *x, short *y); +extern int gr_clip_flat8_bitmap + (grs_bitmap *bm, short *x, short *y); +extern int gr_clip_flat24_bitmap + (grs_bitmap *bm, short *x, short *y); +#define gr_init_gc(c) { (c)->gc=grd_defgc; \ + (c)->gc.clip.f.right=((c)->bm.w)<<16; \ + (c)->gc.clip.f.bot=((c)->bm.h)<<16; } +#define gr_set_cliprect(l, t, r, b) \ + grd_clip.sten=NULL, \ + grd_clip.left=(l), grd_clip.top=(t), \ + grd_clip.right=(r), grd_clip.bot=(b) +#define gr_safe_set_cliprect(l, t, r, b) \ + do { \ + grd_clip.sten=NULL; \ + grd_clip.left=(((l)<0)?0:(l)); \ + grd_clip.right=(((r)>grd_bm.w)?grd_bm.w:(r)); \ + grd_clip.top=(((t)<0)?0:(t)); \ + grd_clip.bot=(((b)>grd_bm.h)?grd_bm.h:(b)); \ + } while (0) +#define gr_set_fix_cliprect(l, t, r, b) \ + grd_fix_clip.sten=NULL, \ + grd_fix_clip.left=(l), grd_fix_clip.top=(t), \ + grd_fix_clip.right=(r), grd_fix_clip.bot=(b) +#define gr_set_clipmask(t,b,mask) \ + grd_clip.top=(t), grd_clip.bot=(b), \ + grd_clip.sten = (mask), \ + gr_set_canvas (grd_canvas) +#define gr_set_fcolor(color) (grd_canvas->gc.fcolor=color) +#define gr_get_fcolor() (grd_canvas->gc.fcolor) +#define gr_set_bcolor(color) (grd_canvas->gc.bcolor=color) +#define gr_get_bcolor() (grd_canvas->gc.bcolor) +#define gr_set_font(fnt) (grd_canvas->gc.font=fnt) +#define gr_get_font() (grd_canvas->gc.font) +#define gr_set_text_attr(attr) (grd_canvas->gc.text_attr=attr) +#define gr_get_text_attr() (grd_canvas->gc.text_attr) + +// gri_set_fill_globals implementation is in PixFill.C +extern void gri_set_fill_globals(long *fill_type_ptr, long fill_type, + void (***function_table_ptr)(), void (**function_table)(), + grt_uline_fill **line_vector_ptr, grt_uline_fill *line_vector); +/*#pragma aux gri_set_fill_globals = \ + "mov [edx],eax" \ + "mov [esi],ebx" \ + "mov [edi],ecx" \ + parm [edx] [eax] [esi] [ebx] [edi] [ecx];*/ +#ifdef OPTIMAL_BUT_BROKEN +#define gr_set_fill_type(__ft) \ +do { \ + long fill_type=__ft; \ + gri_set_fill_globals(&(grd_canvas->gc.fill_type),fill_type, \ + &grd_function_table,(*grd_function_fill_table)[fill_type], \ + &grd_uline_fill_vector,(*grd_uline_fill_table)[fill_type]); \ +} while (0) +#else +#define gr_set_fill_type(type) \ +do { \ + grd_canvas->gc.fill_type=(type); \ + gr_set_canvas(grd_canvas); \ +} while (0) +#endif +#define gr_get_fill_type() (grd_canvas->gc.fill_type) +#define gr_set_fill_parm(parm) \ + (grd_canvas->gc.fill_parm=(intptr_t)(parm)) +#define gr_get_fill_parm() (grd_canvas->gc.fill_parm) +#define gr_cset_cliprect(c, l, t, r, b) \ + (c)->gc.clip.i.sten=NULL, \ + (c)->gc.clip.i.left=(l), (c)->gc.clip.i.top=t, \ + (c)->gc.clip.i.right=(r), (c)->gc.clip.i.bot=(b) +#define gr_cset_fix_cliprect(c, l, t, r, b) \ + (c)->gc.clip.i.sten->flags=NULL, \ + (c)->gc.clip.f.left=(l), (c)->gc.clip.f.top=(t), \ + (c)->gc.clip.f.right=(r), (c)->gc.clip.f.bot=(b) +#define gr_cset_clipmask(canvas,t,b,mask) \ + (canvas)->gc.clip.i.top=(t), (canvas)->gc.clip.i.bot=(b), \ + (canvas)->gc.clip.i.sten = (mask) +#define gr_cset_fcolor(canvas,color) ((canvas)->gc.fcolor=color) +#define gr_cget_fcolor(canvas) ((canvas)->gc.fcolor) +#define gr_cset_bcolor(canvas,color) ((canvas)->gc.bcolor=color) +#define gr_cget_bcolor(canvas) ((canvas)->gc.bcolor) +#define gr_cset_font(canvas,fnt) ((canvas)->gc.font=fnt) +#define gr_cget_font(canvas) ((canvas)->gc.font) +#define gr_get_cliprect(l,t,r,b) (*(l)=grd_clip.left,*(t)=grd_clip.top, \ + *(r)=grd_clip.right,*(b)=grd_clip.bot) +#define gr_get_fix_cliprect(l,t,r,b) (*(l)=grd_fix_clip.left, \ + *(t)=grd_fix_clip.top,*(r)=grd_clip.right,*(b)=grd_clip.bot) +#define gr_get_clip_l() (grd_clip.left) +#define gr_get_clip_t() (grd_clip.top) +#define gr_get_clip_r() (grd_clip.right) +#define gr_get_clip_b() (grd_clip.bot) +#define gr_get_fclip_l() (grd_fix_clip.left) +#define gr_get_fclip_t() (grd_fix_clip.top) +#define gr_get_fclip_r() (grd_fix_clip.right) +#define gr_get_fclip_b() (grd_fix_clip.bot) +#define gr_cget_cliprect(c,l,t,r,b) (\ + *(l)=(c)->gc.clip.i.left,*(t)=(c)->gc.clip.i.top,\ + *(r)=(c)->gc.clip.i.right,*(b)=(c)->gc.clip.i.bot) +#define gr_cget_fix_cliprect(l,t,r,b) (\ + *(l)=(c)->gc.clip.f.left,*(t)=(c)->gc.clip.f.top,\ + *(r)=(c)->gc.clip.f.right,*(b)=(c)->gc.clip.f.bot) +#define gr_cget_clip_l(c) ((c)->gc.clip.i.left) +#define gr_cget_clip_t(c) ((c)->gc.clip.i.top) +#define gr_cget_clip_r(c) ((c)->gc.clip.i.right) +#define gr_cget_clip_b(c) ((c)->gc.clip.i.bot) +#define gr_cget_fclip_l(c) ((c)->gc.clip.f.left) +#define gr_cget_fclip_t(c) ((c)->gc.clip.f.top) +#define gr_cget_fclip_r(c) ((c)->gc.clip.f.right) +#define gr_cget_fclip_b(c) ((c)->gc.clip.f.bot) +extern int gr_detect (grs_sys_info *info); + +enum { + GRM_320x200x8, + GRM_320x200x8X, + GRM_320x400x8, + GRM_320x240x8, + GRM_320x480x8, + GRM_640x400x8, + GRM_640x480x8, + GRM_800x600x8, + GRM_1024x768x8, + GRM_1280x1024x8, + GRM_320x200x24, + GRM_640x480x24, + GRM_800x600x24, + GRM_1024x768x24, + GRM_1280x1024x24 +}; +enum { + GRM_320X200X8, + GRM_320X200X8X, + GRM_320X400X8, + GRM_320X240X8, + GRM_320X480X8, + GRM_640X400X8, + GRM_640X480X8, + GRM_800X600X8, + GRM_1024X768X8, + GRM_1280X1024X8, + GRM_320X200X24, + GRM_640X480X24, + GRM_800X600X24, + GRM_1024X768X24, + GRM_1280X1024X24, + GRD_MODES +}; +extern grs_mode_info grd_mode_info[]; +extern int gr_set_mode (int mode, int clear); + +#define STF_MULT 1 +extern grs_screen *gr_alloc_screen (short w, short h); +extern void gr_free_screen (grs_screen *s); +extern void gr_set_screen (grs_screen *s); +typedef + void *grt_line_clip_fill; +typedef + int (*grt_line_clip_fill_v) (long, long, grs_vertex *, grs_vertex *); +typedef + int (*grt_line_clip_fill_xy) (short, short, short, long, long); +extern grt_line_clip_fill *grd_line_clip_fill_vector; +#define grd_uline_fill ((grt_uline_fill_v) (grd_uline_fill_vector[GR_LINE])) +#define grd_uiline_fill ((grt_uline_fill_v) (grd_uline_fill_vector[GR_ILINE])) +#define grd_uhline_fill ((grt_uline_fill_xy) (grd_uline_fill_vector[GR_HLINE])) +#define grd_uvline_fill ((grt_uline_fill_xy) (grd_uline_fill_vector[GR_VLINE])) +#define grd_usline_fill ((grt_uline_fill_v) (grd_uline_fill_vector[GR_SLINE])) +#define grd_ucline_fill ((grt_uline_fill_v) (grd_uline_fill_vector[GR_CLINE])) +#define grd_wire_poly_uline_fill ((grt_wire_poly_uline) (grd_uline_fill_vector[GR_WIRE_POLY_LINE])) +#define grd_wire_poly_usline_fill ((grt_wire_poly_usline) (grd_uline_fill_vector[GR_WIRE_POLY_SLINE])) +#define grd_wire_poly_ucline_fill ((grt_wire_poly_ucline) (grd_uline_fill_vector[GR_WIRE_POLY_CLINE])) +#define grd_line_clip_fill ((grt_line_clip_fill_v) (grd_line_clip_fill_vector[GR_LINE])) +#define grd_iline_clip_fill ((grt_line_clip_fill_v) (grd_line_clip_fill_vector[GR_ILINE])) +#define grd_hline_clip_fill ((grt_line_clip_fill_xy) (grd_line_clip_fill_vector[GR_HLINE])) +#define grd_vline_clip_fill ((grt_line_clip_fill_xy) (grd_line_clip_fill_vector[GR_VLINE])) +#define grd_sline_clip_fill ((grt_line_clip_fill_v) (grd_line_clip_fill_vector[GR_SLINE])) +#define grd_cline_clip_fill ((grt_line_clip_fill_v) (grd_line_clip_fill_vector[GR_CLINE])) +#define grd_wire_poly_line_clip_fill ((grt_wire_poly_uline) (grd_line_clip_fill_vector[GR_WIRE_POLY_LINE])) +#define grd_wire_poly_sline_clip_fill ((grt_wire_poly_usline) (grd_line_clip_fill_vector[GR_WIRE_POLY_SLINE])) +#define grd_wire_poly_cline_clip_fill ((grt_wire_poly_ucline) (grd_line_clip_fill_vector[GR_WIRE_POLY_CLINE])) +#define grd_pixel_fill(c, parm, x, y) gr_fill_upixel(c, x, y) +#define gr_double_h_ubitmap(bm,x,y) ((int (*)(grs_bitmap *_bm,short _x,short _y)) \ + grd_canvas_table[DOUBLE_H_DEVICE_UBITMAP+2*((bm)->type)])(bm,x,y) +#define gr_double_v_ubitmap(bm,x,y) ((int (*)(grs_bitmap *_bm,short _x,short _y)) \ + grd_canvas_table[DOUBLE_V_DEVICE_UBITMAP+2*((bm)->type)])(bm,x,y) +#define gr_double_hv_ubitmap(bm,x,y) ((int (*)(grs_bitmap *_bm,short _x,short _y)) \ + grd_canvas_table[DOUBLE_HV_DEVICE_UBITMAP+2*((bm)->type)])(bm,x,y) +#define gr_smooth_double_h_ubitmap(bm,x,y) ((int (*)(grs_bitmap *_bm,short _x,short _y)) \ + grd_canvas_table[SMOOTH_DOUBLE_H_DEVICE_UBITMAP+2*((bm)->type)])(bm,x,y) +#define gr_smooth_double_v_ubitmap(bm,x,y) ((int (*)(grs_bitmap *_bm,short _x,short _y)) \ + grd_canvas_table[SMOOTH_DOUBLE_V_DEVICE_UBITMAP+2*((bm)->type)])(bm,x,y) +#define gr_smooth_double_hv_ubitmap(bm,x,y) ((int (*)(grs_bitmap *_bm,short _x,short _y)) \ + grd_canvas_table[SMOOTH_DOUBLE_HV_DEVICE_UBITMAP+2*((bm)->type)])(bm,x,y) +#define gr_clut_lin_umap(bm,n,vpl,cl) \ + ((void (*)(grs_bitmap *_bm,int _n,grs_vertex **_vpl, uchar *_cl)) \ + grd_canvas_table[DEVICE_CLUT_LIN_UMAP+2*((bm)->type)])(bm,n,vpl,cl) +#define gr_flat8_clut_lin_umap \ + ((void (*)(grs_bitmap *bm,int n,grs_vertex **vpl)) \ + grd_canvas_table[FLAT8_CLUT_LIN_UMAP]) +#define gr_clut_lin_map(bm,n,vpl,cl) \ + ((int (*)(grs_bitmap *_bm,int _n,grs_vertex **_vpl, uchar *_cl)) \ + grd_canvas_table[DEVICE_CLUT_LIN_MAP+2*((bm)->type)])(bm,n,vpl,cl) +#define gr_flat8_clut_lin_map \ + ((void (*)(grs_bitmap *bm,int n,grs_vertex **vpl)) \ + grd_canvas_table[FLAT8_CLUT_LIN_MAP]) +#define gr_clut_hflip_ubitmap(bm,x,y,cl) \ + ((void (*)(grs_bitmap *_bm,short _x,short _y,uchar *_cl)) \ + grd_canvas_table[CLUT_HFLIP_DEVICE_UBITMAP+2*((bm)->type)])(bm,x,y,cl) +#define gr_clut_hflip_bitmap(bm,x,y,cl) \ + ((void (*)(grs_bitmap *_bm,short _x,short _y,uchar *_cl)) \ + grd_canvas_table[CLUT_HFLIP_DEVICE_BITMAP+2*((bm)->type)])(bm,x,y,cl) +#define gr_clut_hflip_flat8_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_HFLIP_FLAT8_UBITMAP]) +#define gr_clut_hflip_flat8_bitmap \ + ((void (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_HFLIP_FLAT8_BITMAP]) +#define gr_clut_floor_umap \ + ((void (*)(grs_bitmap *bm,int n,grs_vertex **vpl, uchar *cl)) \ + grd_canvas_table[FLAT8_CLUT_FLOOR_UMAP]) +#define gr_clut_wall_umap \ + ((void (*)(grs_bitmap *bm,int n,grs_vertex **vpl, uchar *cl)) \ + grd_canvas_table[FLAT8_CLUT_WALL_UMAP]) +#define gr_clut_per_umap(bm,n,vpl,cl) \ + ((void (*)(grs_bitmap *_bm,int _n,grs_vertex **_vpl, uchar *_cl)) \ + grd_canvas_table[DEVICE_CLUT_PER_UMAP+2*((bm)->type)])(bm,n,vpl,cl) +#define gr_flat8_clut_per_umap \ + ((void (*)(grs_bitmap *bm,int n,grs_vertex **vpl, uchar *cl)) \ + grd_canvas_table[FLAT8_CLUT_PER_UMAP]) +#define gr_clut_per_map(bm,n,vpl,cl) \ + ((int (*)(grs_bitmap *_bm,int _n,grs_vertex **_vpl, uchar *_cl)) \ + grd_canvas_table[DEVICE_CLUT_PER_MAP+2*((bm)->type)])(bm,n,vpl,cl) +#define gr_flat8_clut_per_map \ + ((void (*)(grs_bitmap *bm,int n,grs_vertex **vpl, uchar *cl)) \ + grd_canvas_table[FLAT8_CLUT_PER_MAP]) +#define gr_int_ucircle \ + ((void (*)(short x,short y,short r))grd_canvas_table[INT_UCIRCLE]) +#define gr_int_circle \ + ((int (*)(short x,short y,short r))grd_canvas_table[INT_CIRCLE]) +#define gr_fix_ucircle \ + ((void (*)(fix x,fix y,fix r))grd_canvas_table[FIX_UCIRCLE]) +#define gr_fix_circle \ + ((int (*)(fix x,fix y,fix r))grd_canvas_table[FIX_CIRCLE]) +#define gr_int_udisk \ + ((void (*)(short x,short y,short r))grd_canvas_table[INT_UDISK]) +#define gr_int_disk \ + ((int (*)(short x,short y,short r))grd_canvas_table[INT_DISK]) +#define gr_fix_udisk \ + ((void (*)(fix x,fix y,fix r))grd_canvas_table[FIX_UDISK]) +#define gr_fix_disk \ + ((int (*)(fix x,fix y,fix r))grd_canvas_table[FIX_DISK]) +#define gr_int_urod ((void (*)())grd_canvas_table[INT_UROD]) +#define gr_int_rod ((int (*)())grd_canvas_table[INT_ROD]) +#define gr_fix_urod ((void (*)()grd_canvas_table[FIX_UROD]) +#define gr_fix_rod ((int (*)())grd_canvas_table[FIX_ROD]) +#define gr_ubitmap(bm,x,y) \ + ((void (*)(grs_bitmap *_bm,short _x,short _y)) \ + grd_canvas_table[DRAW_DEVICE_UBITMAP+2*((bm)->type)])(bm,x,y) +#define gr_bitmap(bm,x,y) \ + ((int (*)(grs_bitmap *_bm,short _x,short _y)) \ + grd_canvas_table[DRAW_DEVICE_BITMAP+2*((bm)->type)])(bm,x,y) +#define gr_mono_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[DRAW_MONO_UBITMAP]) +#define gr_mono_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[DRAW_MONO_BITMAP]) +#define gr_flat8_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[DRAW_FLAT8_UBITMAP]) +#define gr_flat8_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[DRAW_FLAT8_BITMAP]) +#define gr_flat24_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[DRAW_FLAT24_UBITMAP]) +#define gr_flat24_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[DRAW_FLAT24_BITMAP]) +#define gr_rsd8_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[DRAW_RSD8_UBITMAP]) +#define gr_rsd8_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[DRAW_RSD8_BITMAP]) +#define gr_tluc8_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[DRAW_TLUC8_UBITMAP]) +#define gr_tluc8_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[DRAW_TLUC8_BITMAP]) +#define gr_get_ubitmap(bm,x,y) \ + ((void (*)(grs_bitmap *_bm,short _x,short _y)) \ + grd_canvas_table[GET_DEVICE_UBITMAP+2*((bm)->type)])(bm,x,y) +#define gr_get_bitmap(bm,x,y) \ + ((int (*)(grs_bitmap *_bm,short _x,short _y)) \ + grd_canvas_table[GET_DEVICE_BITMAP+2*((bm)->type)])(bm,x,y) +#define gr_get_mono_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[GET_MONO_UBITMAP]) +#define gr_get_mono_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[GET_MONO_BITMAP]) +#define gr_get_flat8_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[GET_FLAT8_UBITMAP]) +#define gr_get_flat8_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[GET_FLAT8_BITMAP]) +#define gr_get_rsd8_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[GET_RSD8_UBITMAP] +#define gr_get_rsd8_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[GET_RSD8_BITMAP]) +extern void gr_hflip_in_place(grs_bitmap *bm); +#define gr_hflip_ubitmap(bm,x,y) \ + ((void (*)(grs_bitmap *_bm,short _x,short _y)) \ + grd_canvas_table[HFLIP_DEVICE_UBITMAP+2*((bm)->type)])(bm,x,y) +#define gr_hflip_bitmap(bm,x,y) \ + ((void (*)(grs_bitmap *_bm,short _x,short _y)) \ + grd_canvas_table[HFLIP_DEVICE_BITMAP+2*((bm)->type)])(bm,x,y) +#define gr_hflip_flat8_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[HFLIP_FLAT8_UBITMAP]) +#define gr_hflip_flat8_bitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[HFLIP_FLAT8_BITMAP]) +#define gr_mask_ubitmap(bm,m,x,y) \ + ((void (*)(grs_bitmap *_bm,grs_stencil *_m,short _x,short _y)) \ + grd_canvas_table[MASK_DEVICE_UBITMAP+2*((bm)->type)])(bm,m,x,y) +#define gr_mask_bitmap(bm,m,x,y) \ + ((int (*)(grs_bitmap *_bm,grs_stencil *_m,short _x,short _y)) \ + grd_canvas_table[MASK_DEVICE_BITMAP+2*((bm)->type)])(bm,m,x,y) +#define gr_mask_mono_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[MASK_MONO_UBITMAP]) +#define gr_mask_mono_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[MASK_MONO_BITMAP]) +#define gr_mask_flat8_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[MASK_FLAT8_UBITMAP]) +#define gr_mask_flat8_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[MASK_FLAT8_BITMAP]) +#define gr_mask_rsd8_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[MASK_RSD8_UBITMAP] +#define gr_mask_rsd8_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[MASK_RSD8_BITMAP]) +extern void gr_set_malloc (void *(*malloc_func)(int bytes)); +extern void gr_set_free (void (*free_func)(void *mem)); +extern void *(*gr_malloc)(int n); +extern void (*gr_free)(void *p); +extern int gr_int_line (short x0, short y0, short x1, short y1); +#define gr_int_uline (x0,y0,x1,y1) \ +do {\ + grs_vertex giu_v0, giu_v1;\ + giu_v0.x = (x0); giu_v0.y = (y0); \ + giu_v1.x = (x1); giu_v1.y = (y1); \ + grd_uiline_fill (gr_get_fcolor(), gr_get_fill_parm(), &giu_v0, &giu_v1); \ +} while (0); +extern int gr_check_poly_y_min(int n,grs_vertex **vpl,long *h_buf); + +#define gr_ucpoly \ + ((void (*)(long c, int n,grs_vertex **vpl))grd_canvas_table[FIX_UCPOLY]) +#define gr_cpoly \ + ((int (*)(long c, int n,grs_vertex **vpl))grd_canvas_table[FIX_CPOLY]) +#define gr_init_device(info) \ + (grd_device_table[GRT_INIT_DEVICE] ?\ + ((int (*)(grs_sys_info *_info))grd_device_table[GRT_INIT_DEVICE])(info) :\ + 0) +#define gr_close_device(info) \ + (grd_device_table[GRT_CLOSE_DEVICE] ?\ + ((int (*)(grs_sys_info *_info))grd_device_table[GRT_CLOSE_DEVICE])(info) :\ + 0) +#define gr_set_screen_mode \ + ((int (*)(int mode,int clear))grd_device_table[GRT_SET_MODE]) +#define gr_get_screen_mode \ + ((int (*)(void))grd_device_table[GRT_GET_MODE]) +#define gr_set_state \ + ((int (*)(void *buf,int clear))grd_device_table[GRT_SET_STATE]) +#define gr_get_state \ + ((int (*)(void *buf,int flags))grd_device_table[GRT_GET_STATE]) +#define gr_stat_htrace \ + ((int (*)(void))grd_device_table[GRT_STAT_HTRACE]) +#define gr_stat_vtrace \ + ((int (*)(void))grd_device_table[GRT_STAT_VTRACE]) +#define gr_set_screen_pal \ + ((void (*)(int start,int n,uchar *pal_data))grd_device_table[GRT_SET_PAL]) +#define gr_get_screen_pal \ + ((void (*)(int start,int n,uchar *pal_data))grd_device_table[GRT_GET_PAL]) +#define gr_set_width \ + ((void (*)(short w))grd_device_table[GRT_SET_WIDTH]) +#define gr_get_width \ + ((short (*)(void))grd_device_table[GRT_GET_WIDTH]) +#define gr_set_focus \ + ((void (*)(short x,short y))grd_device_table[GRT_SET_FOCUS]) +#define gr_get_focus \ + ((void (*)())grd_device_table[GRT_GET_FOCUS]) +#define gr_lit_lin_umap(bm,n,vpl) \ + ((void (*)(grs_bitmap *_bm,int _n,grs_vertex **_vpl)) \ + grd_canvas_table[DEVICE_LIT_LIN_UMAP+2*((bm)->type)])(bm,n,vpl) +#define gr_flat8_lit_lin_umap \ + ((void (*)(grs_bitmap *bm,int n,grs_vertex **vpl)) \ + grd_canvas_table[FLAT8_LIT_LIN_UMAP]) +#define gr_lit_lin_map(bm,n,vpl) \ + ((int (*)(grs_bitmap *_bm,int _n,grs_vertex **_vpl)) \ + grd_canvas_table[DEVICE_LIT_LIN_MAP+2*((bm)->type)])(bm,n,vpl) +#define gr_flat8_lit_lin_map \ + ((void (*)(grs_bitmap *bm,int n,grs_vertex **vpl)) \ + grd_canvas_table[FLAT8_LIT_LIN_MAP]) +#define gr_lin_umap(bm,n,vpl) \ + ((void (*)(grs_bitmap *_bm,int _n,grs_vertex **_vpl)) \ + grd_canvas_table[DEVICE_ULMAP+2*((bm)->type)])(bm,n,vpl) +#define gr_lin_map(bm,n,vpl) \ + ((int (*)(grs_bitmap *_bm,int _n,grs_vertex **_vpl)) \ + grd_canvas_table[DEVICE_LMAP+2*((bm)->type)])(bm,n,vpl) +#define gr_flat8_lin_umap \ + ((void (*)(grs_bitmap *bm,int n,grs_vertex **vpl)) \ + grd_canvas_table[FLAT8_ULMAP]) +#define gr_flat8_lin_map \ + ((int (*)(grs_bitmap *bm,int n,grs_vertex **vpl)) \ + grd_canvas_table[FLAT8_LMAP]) +#define gr_flat24_lin_umap \ + ((void (*)(grs_bitmap *bm,int n,grs_vertex **vpl)) \ + grd_canvas_table[FLAT24_ULMAP]) +#define gr_flat24_lin_map \ + ((int (*)(grs_bitmap *bm,int n,grs_vertex **vpl)) \ + grd_canvas_table[FLAT24_LMAP]) +#define gr_tluc8_lin_umap \ + ((void (*)(grs_bitmap *bm,int n,grs_vertex **vpl)) \ + grd_canvas_table[TLUC8_ULMAP]) +#define gr_tluc8_lin_map \ + ((int (*)(grs_bitmap *bm,int n,grs_vertex **vpl)) \ + grd_canvas_table[TLUC8_LMAP]) +#define gr_lit_wall_umap \ + ((void (*)(grs_bitmap *bm,int n,grs_vertex **vpl)) \ + grd_canvas_table[FLAT8_LIT_WALL_UMAP]) +#define gr_lit_floor_umap \ + ((void (*)(grs_bitmap *bm,int n,grs_vertex **vpl)) \ + grd_canvas_table[FLAT8_LIT_FLOOR_UMAP]) +#define gr_lit_per_umap(bm,n,vpl) \ + ((void (*)(grs_bitmap *_bm,int _n,grs_vertex **_vpl)) \ + grd_canvas_table[DEVICE_LIT_PER_UMAP+2*((bm)->type)])(bm,n,vpl) +#define gr_flat8_lit_per_umap \ + ((void (*)(grs_bitmap *bm,int n,grs_vertex **vpl)) \ + grd_canvas_table[FLAT8_LIT_PER_UMAP]) +#define gr_lit_per_map(bm,n,vpl) \ + ((int (*)(grs_bitmap *_bm,int _n,grs_vertex **_vpl)) \ + grd_canvas_table[DEVICE_LIT_PER_MAP+2*((bm)->type)])(bm,n,vpl) +#define gr_flat8_lit_per_map \ + ((void (*)(grs_bitmap *bm,int n,grs_vertex **vpl)) \ + grd_canvas_table[FLAT8_LIT_PER_MAP]) +#define gr_set_upixel24 \ + ((void (*)(long color,short x,short y))grd_pixel_table[SET_UPIXEL24]) +#define gr_set_pixel24 \ + ((int (*)(long color,short x,short y))grd_pixel_table[SET_PIXEL24]) +#define gr_get_upixel24 \ + ((long (*)(short x,short y))grd_pixel_table[GET_UPIXEL24]) +#define gr_get_pixel24 \ + ((long (*)(short x,short y))grd_pixel_table[GET_PIXEL24]) +#define gr_set_upixel \ + ((void (*)(long color, short x, short y))grd_pixel_table[SET_UPIXEL8]) +#define gr_set_pixel \ + ((int (*)(long color, short x, short y))grd_pixel_table[SET_PIXEL8]) +extern int gen_fill_pixel(long color, short x, short y); +#define gr_set_upixel_interrupt \ + ((void (*)(long color, short x, short y))grd_pixel_table[SET_UPIXEL8_INTERRUPT]) +#define gr_set_pixel_interrupt \ + ((int (*)(long color, short x, short y))grd_pixel_table[SET_PIXEL8_INTERRUPT]) +#define gr_fill_upixel \ + ((void (*)(long color, short x, short y))grd_function_table[GRC_PIXEL]) +#define gr_fill_pixel gen_fill_pixel +#define gr_get_upixel \ + ((long (*)(short x, short y))grd_pixel_table[GET_UPIXEL8]) +#define gr_get_pixel \ + ((long (*)(short x, short y))grd_pixel_table[GET_PIXEL8]) +#define gr_upoly \ + ((void (*)(long c,int n,grs_vertex **vpl)) \ + grd_canvas_table[FIX_UPOLY]) +#define gr_poly \ + ((int (*)(long c,int n,grs_vertex **vpl)) \ + grd_canvas_table[FIX_POLY]) +#define gr_tluc8_upoly \ + ((void (*)(long c,int n,grs_vertex **vpl)) \ + grd_canvas_table[FIX_TLUC8_UPOLY]) +#define gr_tluc8_poly \ + ((int (*)(long c,int n,grs_vertex **vpl)) \ + grd_canvas_table[FIX_TLUC8_POLY]) +#define gr_floor_umap \ + ((void (*)(grs_bitmap *bm,int n,grs_vertex **vpl)) \ + grd_canvas_table[FLAT8_FLOOR_UMAP]) +#define gr_wall_umap \ + ((void (*)(grs_bitmap *bm,int n,grs_vertex **vpl)) \ + grd_canvas_table[FLAT8_WALL_UMAP]) +#define gr_per_umap(bm,n,vpl) \ + ((void (*)(grs_bitmap *_bm,int _n,grs_vertex **_vpl)) \ + grd_canvas_table[DEVICE_PER_UMAP+2*((bm)->type)])(bm,n,vpl) +#define gr_flat8_per_umap \ + ((void (*)(grs_bitmap *bm,int n,grs_vertex **vpl)) \ + grd_canvas_table[FLAT8_PER_UMAP]) +#define gr_per_map(bm,n,vpl) \ + ((int (*)(grs_bitmap *_bm,int _n,grs_vertex **_vpl)) \ + grd_canvas_table[DEVICE_PER_MAP+2*((bm)->type)])(bm,n,vpl) +#define gr_flat8_per_map \ + ((void (*)(grs_bitmap *bm,int n,grs_vertex **vpl)) \ + grd_canvas_table[FLAT8_PER_MAP]) +#ifndef _FL8PS_C +extern grs_per_context *grd_per_context; +#endif + +#define gr_clut_ubitmap(bm,x,y,cl) \ + ((void (*)(grs_bitmap *_bm,short _x,short _y, uchar *_cl)) \ + grd_canvas_table[CLUT_DRAW_DEVICE_UBITMAP+2*((bm)->type)])(bm,x,y,cl) +#define gr_clut_bitmap(bm,x,y,cl) \ + ((int (*)(grs_bitmap *_bm,short _x,short _y, uchar *_cl)) \ + grd_canvas_table[CLUT_DRAW_DEVICE_BITMAP+2*((bm)->type)])(bm,x,y,cl) +#define gr_mono_clut_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_DRAW_MONO_UBITMAP]) +#define gr_mono_clut_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_DRAW_MONO_BITMAP]) +#define gr_flat8_clut_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_DRAW_FLAT8_UBITMAP]) +#define gr_flat8_clut_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_DRAW_FLAT8_BITMAP]) +#define gr_flat24_clut_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_DRAW_FLAT24_UBITMAP]) +#define gr_flat24_clut_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_DRAW_FLAT24_BITMAP]) +#define gr_rsd8_clut_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_DRAW_RSD8_UBITMAP]) +#define gr_rsd8_clut_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_DRAW_RSD8_BITMAP]) +#define gr_tluc8_clut_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_DRAW_TLUC8_UBITMAP]) +#define gr_tluc8_clut_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_DRAW_TLUC8_BITMAP]) +#define gr_clear \ + ((void (*)(long color))grd_canvas_table[DRAW_CLEAR]) +#define gr_upoint \ + ((void (*)(short x,short y))grd_canvas_table[DRAW_UPOINT]) +#define gr_point \ + ((int (*)(short x,short y))grd_canvas_table[DRAW_POINT]) +#define gr_uhline(x0,y0,x1) \ +do {\ + grd_uhline_fill ((x0), (y0), (x1), gr_get_fcolor(), gr_get_fill_parm()); \ +} while (0) +extern int gen_hline (short x0, short y0, short x1); +#define gr_hline gen_hline +#define gr_uvline(x0,y0,y1) \ +do {\ + grd_uvline_fill ((x0), (y0), (y1), gr_get_fcolor(), gr_get_fill_parm()); \ +} while (0) +extern int gen_vline (short x0, short y0, short y1); +#define gr_vline gen_vline +#define gr_urect \ + ((void (*)(short x0,short y0,short x1,short y1))grd_canvas_table[DRAW_URECT]) +#define gr_rect \ + ((int (*)(short x0,short y0,short x1,short y1))grd_canvas_table[DRAW_RECT]) +#define gr_ubox \ + ((void (*)(short x0,short y0,short x1,short y1))grd_canvas_table[DRAW_UBOX]) +#define gr_box \ + ((int (*)(short x0,short y0,short x1,short y1))grd_canvas_table[DRAW_BOX]) +#define gr_fix_line gen_fix_line +extern int gen_fix_line (fix x0, fix y0, fix x1, fix y1); +#define gr_fix_uline(x0,y0,x1,y1)\ +do {\ + grs_vertex gfu_v0, gfu_v1;\ + gfu_v0.x = (x0); gfu_v0.y = (y0);\ + gfu_v1.x = (x1); gfu_v1.y = (y1);\ + grd_uline_fill (gr_get_fcolor(), gr_get_fill_parm(), &gfu_v0, &gfu_v1);\ +} while (0) +#define gr_fix_cline gen_fix_cline +extern int gen_fix_cline (fix x0, fix y0, grs_rgb c0, fix x1, fix y1, grs_rgb c1); +#define gr_fix_ucline(x0,y0,c0,x1,y1,c1) \ +do { \ + grs_vertex gfuc_v0, gfuc_v1; \ +\ + gfuc_v0.x = (x0); gfuc_v0.y = (y0); \ + gfuc_v1.x = (x1); gfuc_v1.y = (y1); \ +\ + gr_split_rgb ((c0), (uchar*) &(gfuc_v0.u), (uchar*)&(gfuc_v0.v), (uchar*)&(gfuc_v0.w)); \ + gr_split_rgb ((c1), (uchar*)&(gfuc_v1.u), (uchar*)&(gfuc_v1.v), (uchar*)&(gfuc_v1.w)); \ +\ + grd_ucline_fill (gr_get_fcolor(), gr_get_fill_parm(), &gfuc_v0, &gfuc_v1); \ +} while(0) +#define gr_fix_sline gen_fix_sline +extern int gen_fix_sline (fix x0, fix y0, fix i0, fix x1, fix y1, fix i1); +#define gr_fix_usline(x0,y0,i0,x1,y1,i1) \ +do { \ + grs_vertex gfuc_v0, gfuc_v1; \ +\ + gfuc_v0.x = (x0); gfuc_v0.y = (y0); gfuc_v0.i = (i0);\ + gfuc_v1.x = (x1); gfuc_v1.y = (y1); gfuc_v1.i = (i1);\ +\ + grd_usline_fill (gr_get_fcolor(), gr_get_fill_parm(), &gfuc_v0, &gfuc_v1); \ +} while(0) +#define gr_uline(c,v0,v1) \ + grd_uline_fill(c,gr_get_fill_parm(),v0,v1) +#define gr_usline(v0,v1) \ + grd_usline_fill(gr_get_fcolor(), gr_get_fill_parm(),v0,v1) +#define gr_ucline(v0,v1) \ + grd_ucline_fill(gr_get_fcolor(), gr_get_fill_parm(),v0,v1) +#define gr_wire_poly_uline(c,v0,v1) \ + grd_wire_poly_uline_fill(c, gr_get_fill_parm(),v0,v1) +#define gr_wire_poly_usline(v0,v1) \ + grd_wire_poly_usline_fill(gr_get_fcolor(), gr_get_fill_parm(), v0, v1) +#define gr_wire_poly_ucline(v0,v1) \ + grd_wire_poly_ucline_fill(gr_get_fcolor(), gr_get_fill_parm(), v0, v1) +#define gr_wire_poly_line(c,v0,v1) \ + grd_wire_poly_line_clip_fill(c, gr_get_fill_parm(),v0,v1) +#define gr_wire_poly_sline(v0,v1) \ + grd_wire_poly_sline_clip_fill(gr_get_fcolor(), gr_get_fill_parm(), v0, v1) +#define gr_wire_poly_cline(v0,v1) \ + grd_wire_poly_cline_clip_fill(gr_get_fcolor(), gr_get_fill_parm(), v0, v1) +#define gr_vox_rect \ + ((void (*)(fix x[4],fix y[4],fix dz[3],int near_ver,grs_bitmap *col,grs_bitmap *ht,int dotw,int doth)) \ + grd_canvas_table[VOX_RECT]) +#define gr_vox_poly \ + ((void (*)(fix x[4],fix y[4],fix dz[3],int near_ver,grs_bitmap *col,grs_bitmap *ht)) \ + grd_canvas_table[VOX_POLY]) +#define gr_vox_cpoly \ + ((void (*)(fix x[4],fix y[4],fix dz[3],int near_ver,grs_bitmap *col,grs_bitmap *ht)) \ + grd_canvas_table[VOX_CPOLY]) +#define gr_interp2_ubitmap \ + ((void (*)(grs_bitmap *bm)) grd_canvas_table[INTERP2_UBITMAP]) +#define gr_filter2_ubitmap \ + ((void (*)(grs_bitmap *bm)) grd_canvas_table[FILTER2_UBITMAP]) + +#define gr_scale_ubitmap(bm,x,y,w,h) \ + ((void (*)(grs_bitmap *_bm,short _x,short _y,short _w,short _h)) \ + grd_canvas_table[SCALE_DEVICE_UBITMAP+2*((bm)->type)]) (bm,x,y,w,h) +#define gr_scale_bitmap(bm,x,y,w,h) \ + ((int (*)(grs_bitmap *_bm,short _x,short _y,short _w,short _h)) \ + grd_canvas_table[SCALE_DEVICE_BITMAP+2*((bm)->type)]) (bm,x,y,w,h) +#define gr_clut_scale_ubitmap(bm,x,y,w,h,cl) \ + ((int (*)(grs_bitmap *_bm,short _x,short _y,short _w,short _h,uchar *_cl)) \ + grd_canvas_table[CLUT_SCALE_DEVICE_UBITMAP+2*((bm)->type)]) \ + (bm,x,y,w,h,cl) +#define gr_clut_scale_bitmap(bm,x,y,w,h,cl) \ + ((int (*)(grs_bitmap *_bm,short _x,short _y,short _w,short _h,uchar *_cl)) \ + grd_canvas_table[CLUT_SCALE_DEVICE_BITMAP+2*((bm)->type)]) \ + (bm,x,y,w,h,cl) +#define gr_roll_ubitmap grd_canvas_table[ROLL_UBITMAP]) +#define gr_roll_bitmap ((int (*)())grd_canvas_table[ROLL_BITMAP]) +#define gr_uspoly \ + ((void (*)(long c,int n,grs_vertex **vpl))grd_canvas_table[FIX_USPOLY]) +#define gr_spoly \ + ((int (*)(long c,int n,grs_vertex **vpl))grd_canvas_table[FIX_SPOLY]) +#define gr_tluc8_uspoly \ + ((void (*)(long c,int n,grs_vertex **vpl))grd_canvas_table[FIX_TLUC8_USPOLY]) +#define gr_tluc8_spoly \ + ((int (*)(long c,int n,grs_vertex **vpl))grd_canvas_table[FIX_TLUC8_SPOLY]) + +// MLA - had to change the gr_string calls because they weren't passing parms correctly +#define gr_ustring(s, x, y) \ + (((void (*)(grs_font *, char *, short, short)) grd_canvas_table[DRAW_USTRING])) ((grs_font *)gr_get_font(), s, x, y) +#define gr_string(s, x, y) \ + (((int (*)(grs_font *, char *, short, short)) grd_canvas_table[DRAW_STRING])) ((grs_font *)gr_get_font(), s, x, y) +#define gr_scale_ustring(s, x, y, w, h) \ + (((void (*)(grs_font *, char *, short, short, short, short)) grd_canvas_table[DRAW_SCALE_USTRING])) ((grs_font *)gr_get_font(), s, x, y, w, h) +#define gr_scale_string(s, x, y, w, h) \ + (((int (*)(grs_font *, char *, short, short, short, short)) grd_canvas_table[DRAW_SCALE_STRING])) ((grs_font *)gr_get_font(), s, x, y, w, h) +#define gr_uchar(s, x, y) \ + (((void (*)(grs_font *, char, short, short)) grd_canvas_table[DRAW_UCHAR])) ((grs_font *)gr_get_font(), s, x, y) +#define gr_char(s, x, y) \ + (((int (*)(grs_font *, char, short, short)) grd_canvas_table[DRAW_CHAR])) ((grs_font *)gr_get_font(), s, x, y) + + +#define gr_font_ustring \ + ((void (*)(grs_font *f,char *s,short x,short y))grd_canvas_table[DRAW_USTRING]) +#define gr_font_string \ + ((int (*)(grs_font *f,char *s,short x,short y))grd_canvas_table[DRAW_STRING]) +#define gr_font_scale_ustring \ + ((void (*)(grs_font *f,char *s,short x,short y, short w, short h))grd_canvas_table[DRAW_SCALE_USTRING]) +#define gr_font_scale_string \ + ((int (*)(grs_font *f,char *s,short x,short y, short w, short h))grd_canvas_table[DRAW_SCALE_STRING]) +#define gr_font_uchar \ + ((void (*)(grs_font *f,char c,short x,short y))grd_canvas_table[DRAW_UCHAR]) +#define gr_font_char \ + ((int (*)(grs_font *f,char c,short x,short y))grd_canvas_table[DRAW_CHAR]) +enum { + + SET_UPIXEL8, + SET_PIXEL8, + GET_UPIXEL8, + GET_PIXEL8, + SET_UPIXEL24, + SET_PIXEL24, + GET_UPIXEL24, + GET_PIXEL24, + DRAW_CLEAR, + DRAW_UPOINT, + DRAW_POINT, + SET_UPIXEL8_INTERRUPT, + SET_PIXEL8_INTERRUPT, + DRAW_UVLINE, + DRAW_VLINE, + DRAW_URECT, + DRAW_RECT, + DRAW_UBOX, + DRAW_BOX, + PUSH_STATE, + POP_STATE, + FIX_USLINE, + FIX_SLINE, + FIX_UCLINE, + FIX_CLINE, + FIX_UPOLY, + FIX_POLY, + FIX_USPOLY, + FIX_SPOLY, + FIX_UCPOLY, + FIX_CPOLY, + FIX_TLUC8_UPOLY, + FIX_TLUC8_POLY, + FIX_TLUC8_USPOLY, + FIX_TLUC8_SPOLY, + VOX_RECT, + VOX_POLY, + VOX_CPOLY, + INTERP2_UBITMAP, + FILTER2_UBITMAP, + ROLL_UBITMAP, + ROLL_BITMAP, + FLAT8_WALL_UMAP, + FLAT8_WALL_MAP, + FLAT8_LIT_WALL_UMAP, + FLAT8_LIT_WALL_MAP, + FLAT8_CLUT_WALL_UMAP, + FLAT8_CLUT_WALL_MAP, + FLAT8_FLOOR_UMAP, + FLAT8_FLOOR_MAP, + FLAT8_LIT_FLOOR_UMAP, + FLAT8_LIT_FLOOR_MAP, + FLAT8_CLUT_FLOOR_UMAP, + FLAT8_CLUT_FLOOR_MAP, + DEVICE_ULMAP, + DEVICE_LMAP, + MONO_ULMAP, + MONO_LMAP, + FLAT8_ULMAP, + FLAT8_LMAP, + FLAT24_ULMAP, + FLAT24_LMAP, + RSD_ULMAP, + RSD_LMAP, + TLUC8_ULMAP, + TLUC8_LMAP, + DEVICE_LIT_LIN_UMAP, + DEVICE_LIT_LIN_MAP, + MONO_LIT_LIN_UMAP, + MONO_LIT_LIN_MAP, + FLAT8_LIT_LIN_UMAP, + FLAT8_LIT_LIN_MAP, + FLAT24_LIT_LIN_UMAP, + FLAT24_LIT_LIN_MAP, + RSD_LIT_LIN_UMAP, + RSD_LIT_LIN_MAP, + TLUC8_LIT_LIN_UMAP, + TLUC8_LIT_LIN_MAP, + DEVICE_CLUT_LIN_UMAP, + DEVICE_CLUT_LIN_MAP, + MONO_CLUT_LIN_UMAP, + MONO_CLUT_LIN_MAP, + FLAT8_CLUT_LIN_UMAP, + FLAT8_CLUT_LIN_MAP, + FLAT24_CLUT_LIN_UMAP, + FLAT24_CLUT_LIN_MAP, + RSD_CLUT_LIN_UMAP, + RSD_CLUT_LIN_MAP, + TLUC8_CLUT_LIN_UMAP, + TLUC8_CLUT_LIN_MAP, + FLAT8_SOLID_LIN_UMAP, + FLAT8_SOLID_LIN_MAP, + DEVICE_PER_UMAP, + DEVICE_PER_MAP, + MONO_PER_UMAP, + MONO_PER_MAP, + FLAT8_PER_UMAP, + FLAT8_PER_MAP, + FLAT24_PER_UMAP, + FLAT24_PER_MAP, + RSD_PER_UMAP, + RSD_PER_MAP, + TLUC8_PER_UMAP, + TLUC8_PER_MAP, + DEVICE_LIT_PER_UMAP, + DEVICE_LIT_PER_MAP, + MONO_LIT_PER_UMAP, + MONO_LIT_PER_MAP, + FLAT8_LIT_PER_UMAP, + FLAT8_LIT_PER_MAP, + FLAT24_LIT_PER_UMAP, + FLAT24_LIT_PER_MAP, + RSD_LIT_PER_UMAP, + RSD_LIT_PER_MAP, + TLUC8_LIT_PER_UMAP, + TLUC8_LIT_PER_MAP, + DEVICE_CLUT_PER_UMAP, + DEVICE_CLUT_PER_MAP, + MONO_CLUT_PER_UMAP, + MONO_CLUT_PER_MAP, + FLAT8_CLUT_PER_UMAP, + FLAT8_CLUT_PER_MAP, + FLAT24_CLUT_PER_UMAP, + FLAT24_CLUT_PER_MAP, + RSD_CLUT_PER_UMAP, + RSD_CLUT_PER_MAP, + TLUC8_CLUT_PER_UMAP, + TLUC8_CLUT_PER_MAP, + FLAT8_SOLID_PER_UMAP, + FLAT8_SOLID_PER_MAP, + INT_UCIRCLE, + INT_CIRCLE, + FIX_UCIRCLE, + FIX_CIRCLE, + INT_UDISK, + INT_DISK, + FIX_UDISK, + FIX_DISK, + INT_UROD, + INT_ROD, + FIX_UROD, + FIX_ROD, + DRAW_DEVICE_UBITMAP, + DRAW_DEVICE_BITMAP, + DRAW_MONO_UBITMAP, + DRAW_MONO_BITMAP, + DRAW_FLAT8_UBITMAP, + DRAW_FLAT8_BITMAP, + DRAW_FLAT24_UBITMAP, + DRAW_FLAT24_BITMAP, + DRAW_RSD8_UBITMAP, + DRAW_RSD8_BITMAP, + DRAW_TLUC8_UBITMAP, + DRAW_TLUC8_BITMAP, + CLUT_DRAW_DEVICE_UBITMAP, + CLUT_DRAW_DEVICE_BITMAP, + CLUT_DRAW_MONO_UBITMAP, + CLUT_DRAW_MONO_BITMAP, + CLUT_DRAW_FLAT8_UBITMAP, + CLUT_DRAW_FLAT8_BITMAP, + CLUT_DRAW_FLAT24_UBITMAP, + CLUT_DRAW_FLAT24_BITMAP, + CLUT_DRAW_RSD8_UBITMAP, + CLUT_DRAW_RSD8_BITMAP, + CLUT_DRAW_TLUC8_UBITMAP, + CLUT_DRAW_TLUC8_BITMAP, + SOLID_RSD8_UBITMAP, + SOLID_RSD8_BITMAP, + SCALE_DEVICE_UBITMAP, + SCALE_DEVICE_BITMAP, + SCALE_MONO_UBITMAP, + SCALE_MONO_BITMAP, + SCALE_FLAT8_UBITMAP, + SCALE_FLAT8_BITMAP, + SCALE_FLAT24_UBITMAP, + SCALE_FLAT24_BITMAP, + SCALE_RSD8_UBITMAP, + SCALE_RSD8_BITMAP, + SCALE_TLUC8_UBITMAP, + SCALE_TLUC8_BITMAP, + SOLID_SCALE_RSD8_UBITMAP, + SOLID_SCALE_RSD8_BITMAP, + CLUT_SCALE_DEVICE_UBITMAP, + CLUT_SCALE_DEVICE_BITMAP, + CLUT_SCALE_MONO_UBITMAP, + CLUT_SCALE_MONO_BITMAP, + CLUT_SCALE_FLAT8_UBITMAP, + CLUT_SCALE_FLAT8_BITMAP, + CLUT_SCALE_FLAT24_UBITMAP, + CLUT_SCALE_FLAT24_BITMAP, + CLUT_SCALE_RSD8_UBITMAP, + CLUT_SCALE_RSD8_BITMAP, + CLUT_SCALE_TLUC8_UBITMAP, + CLUT_SCALE_TLUC8_BITMAP, + MASK_DEVICE_UBITMAP, + MASK_DEVICE_BITMAP, + MASK_MONO_UBITMAP, + MASK_MONO_BITMAP, + MASK_FLAT8_UBITMAP, + MASK_FLAT8_BITMAP, + MASK_FLAT24_UBITMAP, + MASK_FLAT24_BITMAP, + MASK_RSD8_UBITMAP, + MASK_RSD8_BITMAP, + MASK_TLUC8_UBITMAP, + MASK_TLUC8_BITMAP, + GET_DEVICE_UBITMAP, + GET_DEVICE_BITMAP, + GET_MONO_UBITMAP, + GET_MONO_BITMAP, + GET_FLAT8_UBITMAP, + GET_FLAT8_BITMAP, + GET_FLAT24_UBITMAP, + GET_FLAT24_BITMAP, + GET_RSD8_UBITMAP, + GET_RSD8_BITMAP, + GET_TLUC8_UBITMAP, + GET_TLUC8_BITMAP, + HFLIP_DEVICE_UBITMAP, + HFLIP_DEVICE_BITMAP, + HFLIP_MONO_UBITMAP, + HFLIP_MONO_BITMAP, + HFLIP_FLAT8_UBITMAP, + HFLIP_FLAT8_BITMAP, + HFLIP_FLAT24_UBITMAP, + HFLIP_FLAT24_BITMAP, + HFLIP_RSD8_UBITMAP, + HFLIP_RSD8_BITMAP, + HFLIP_TLUC8_UBITMAP, + HFLIP_TLUC8_BITMAP, + CLUT_HFLIP_DEVICE_UBITMAP, + CLUT_HFLIP_DEVICE_BITMAP, + CLUT_HFLIP_MONO_UBITMAP, + CLUT_HFLIP_MONO_BITMAP, + CLUT_HFLIP_FLAT8_UBITMAP, + CLUT_HFLIP_FLAT8_BITMAP, + CLUT_HFLIP_FLAT24_UBITMAP, + CLUT_HFLIP_FLAT24_BITMAP, + CLUT_HFLIP_RSD8_UBITMAP, + CLUT_HFLIP_RSD8_BITMAP, + CLUT_HFLIP_TLUC8_UBITMAP, + CLUT_HFLIP_TLUC8_BITMAP, + DOUBLE_H_DEVICE_UBITMAP, + DOUBLE_H_DEVICE_BITMAP, + DOUBLE_H_MONO_UBITMAP, + DOUBLE_H_MONO_BITMAP, + DOUBLE_H_FLAT8_UBITMAP, + DOUBLE_H_FLAT8_BITMAP, + DOUBLE_H_FLAT24_UBITMAP, + DOUBLE_H_FLAT24_BITMAP, + DOUBLE_H_RSD8_UBITMAP, + DOUBLE_H_RSD8_BITMAP, + DOUBLE_H_TLUC8_UBITMAP, + DOUBLE_H_TLUC8_BITMAP, + DOUBLE_V_DEVICE_UBITMAP, + DOUBLE_V_DEVICE_BITMAP, + DOUBLE_V_MONO_UBITMAP, + DOUBLE_V_MONO_BITMAP, + DOUBLE_V_FLAT8_UBITMAP, + DOUBLE_V_FLAT8_BITMAP, + DOUBLE_V_FLAT24_UBITMAP, + DOUBLE_V_FLAT24_BITMAP, + DOUBLE_V_RSD8_UBITMAP, + DOUBLE_V_RSD8_BITMAP, + DOUBLE_V_TLUC8_UBITMAP, + DOUBLE_V_TLUC8_BITMAP, + DOUBLE_HV_DEVICE_UBITMAP, + DOUBLE_HV_DEVICE_BITMAP, + DOUBLE_HV_MONO_UBITMAP, + DOUBLE_HV_MONO_BITMAP, + DOUBLE_HV_FLAT8_UBITMAP, + DOUBLE_HV_FLAT8_BITMAP, + DOUBLE_HV_FLAT24_UBITMAP, + DOUBLE_HV_FLAT24_BITMAP, + DOUBLE_HV_RSD8_UBITMAP, + DOUBLE_HV_RSD8_BITMAP, + DOUBLE_HV_TLUC8_UBITMAP, + DOUBLE_HV_TLUC8_BITMAP, + SMOOTH_DOUBLE_H_DEVICE_UBITMAP, + SMOOTH_DOUBLE_H_DEVICE_BITMAP, + SMOOTH_DOUBLE_H_MONO_UBITMAP, + SMOOTH_DOUBLE_H_MONO_BITMAP, + SMOOTH_DOUBLE_H_FLAT8_UBITMAP, + SMOOTH_DOUBLE_H_FLAT8_BITMAP, + SMOOTH_DOUBLE_H_FLAT24_UBITMAP, + SMOOTH_DOUBLE_H_FLAT24_BITMAP, + SMOOTH_DOUBLE_H_RSD8_UBITMAP, + SMOOTH_DOUBLE_H_RSD8_BITMAP, + SMOOTH_DOUBLE_H_TLUC8_UBITMAP, + SMOOTH_DOUBLE_H_TLUC8_BITMAP, + SMOOTH_DOUBLE_V_DEVICE_UBITMAP, + SMOOTH_DOUBLE_V_DEVICE_BITMAP, + SMOOTH_DOUBLE_V_MONO_UBITMAP, + SMOOTH_DOUBLE_V_MONO_BITMAP, + SMOOTH_DOUBLE_V_FLAT8_UBITMAP, + SMOOTH_DOUBLE_V_FLAT8_BITMAP, + SMOOTH_DOUBLE_V_FLAT24_UBITMAP, + SMOOTH_DOUBLE_V_FLAT24_BITMAP, + SMOOTH_DOUBLE_V_RSD8_UBITMAP, + SMOOTH_DOUBLE_V_RSD8_BITMAP, + SMOOTH_DOUBLE_V_TLUC8_UBITMAP, + SMOOTH_DOUBLE_V_TLUC8_BITMAP, + SMOOTH_DOUBLE_HV_DEVICE_UBITMAP, + SMOOTH_DOUBLE_HV_DEVICE_BITMAP, + SMOOTH_DOUBLE_HV_MONO_UBITMAP, + SMOOTH_DOUBLE_HV_MONO_BITMAP, + SMOOTH_DOUBLE_HV_FLAT8_UBITMAP, + SMOOTH_DOUBLE_HV_FLAT8_BITMAP, + SMOOTH_DOUBLE_HV_FLAT24_UBITMAP, + SMOOTH_DOUBLE_HV_FLAT24_BITMAP, + SMOOTH_DOUBLE_HV_RSD8_UBITMAP, + SMOOTH_DOUBLE_HV_RSD8_BITMAP, + SMOOTH_DOUBLE_HV_TLUC8_UBITMAP, + SMOOTH_DOUBLE_HV_TLUC8_BITMAP, + DRAW_USTRING, + DRAW_STRING, + DRAW_SCALE_USTRING, + DRAW_SCALE_STRING, + DRAW_UCHAR, + DRAW_CHAR, + CALC_ROW, + SUB_BITMAP, + START_FRAME, + END_FRAME, + GRD_CANVAS_FUNCS +}; +enum { + GRT_INIT_DEVICE, + GRT_CLOSE_DEVICE, + GRT_SET_MODE, + GRT_GET_MODE, + GRT_SET_STATE, + GRT_GET_STATE, + GRT_STAT_HTRACE, + GRT_STAT_VTRACE, + GRT_SET_PAL, + GRT_GET_PAL, + GRT_SET_WIDTH, + GRT_GET_WIDTH, + GRT_SET_FOCUS, + GRT_GET_FOCUS, + GRT_CANVAS_TABLE, + GRT_SPAN_TABLE, + GRD_DEVICE_FUNCS +}; +extern uchar grd_interrupt; +extern void gr_set_pal (int start, int n, uchar *pal_data); +extern void gr_set_gamma_pal (int start, int n, fix gamma); +extern void gr_get_pal (int start, int n, uchar *pal_data); +typedef struct { + ubyte ltol, wftol; + fix cltol; +} gr_per_detail_level; +enum { + GR_LOW_PER_DETAIL, + GR_MEDIUM_PER_DETAIL, + GR_HIGH_PER_DETAIL, + GR_NUM_PER_DETAIL_LEVELS +}; +extern void gr_set_per_tol(ubyte linear_tol, ubyte wall_floor_tol); +extern void gr_set_clut_lit_tol(fix clut_lit_tol); +extern void gr_set_per_detail_level(int detail_level); +extern void gr_set_per_detail_level_param + (int linear_tol, int wall_floor_tol, fix clut_lit_tol, int detail_level); +#define RGB_OK (0) +#define RGB_OUT_OF_MEMORY (-1) +#define RGB_CANT_DEALLOCATE (-2) +#define RGB_IPAL_NOT_ALLOCATED (-3) +#define gr_index_rgb(r,g,b) \ + (((r)>>19)&0x1f) | (((g)>>14)&0x3e0) | (((b)>>9)&0x7c00) +#define gr_index_lrgb(t) \ + ((((t)>>3)&0x1f) | (((t)>>6)&0x3e0) | (((t)>>9)&0x7c00)) +#define gr_bind_rgb(r,g,b) (((r)<<2)|((g)<<13)|((b)<<24)) +#define gr_index_brgb(c) \ + ((((c)>>5)&0x1f)|(((c)>>11)&0x3e0)|(((c)>>17)&0x7c00)) +extern void gr_split_rgb (grs_rgb c, uchar *r, uchar *g, uchar *b); +int gr_alloc_ipal(void); +int gr_init_ipal(void); +int gr_free_ipal(void); +#define gr_get_light_tab() (grd_screen->ltab) +#define gr_set_light_tab(p) (grd_screen->ltab=(p)) +#define gr_get_clut() (grd_screen->clut) +#define gr_set_clut(cl) (grd_screen->clut=(cl)) +#ifndef STATE_H +#define STATE_H +#define GRD_STATE_GRAPHICS_OURS 0 // in house graphics mode +#define GRD_STATE_GRAPHICS_VGA 1 // VGA or VESA graphics moed +#define GRD_STATE_BIOS_TEXT 2 // VGA text mode +#define GRD_STATE_VESA_TEXT 3 // VESA text mode +#define GRD_STATE_DEF 0 +#define GRD_STATE_PAL 1 +int gr_push_video_state (int flags); +void gr_pop_video_state (int clear); +#endif + +extern void gr_font_string_size (grs_font *font, char *string, short *width, short *height); +extern short gr_font_string_width (grs_font *font, char *string); +extern short gr_font_char_width (grs_font *font, char c); +extern void gr_font_char_size (grs_font *font, char c, short *width, short *height); +extern int gr_font_string_wrap (grs_font *pfont, char *ps, short width); +extern void gr_font_string_unwrap (char *s); +#define gr_string_size(s, w, h) gr_font_string_size ((grs_font *) gr_get_font(), s, w, h) +#define gr_string_width(s) gr_font_string_width ((grs_font *)gr_get_font(), s) +#define gr_char_width(c) gr_font_char_width ((grs_font *) gr_get_font(), c) +#define gr_char_size(c, w, h) gr_font_char_size ((grs_font *) gr_get_font(), c, w, h) +#define gr_string_wrap(string, width) gr_font_string_wrap ((grs_font *) gr_get_font(), string, width) + +extern void vga_save_mode (void); +extern void vga_rest_mode (void); +extern void vga_wait_vsync (void); +extern void vga_wait_display (void); +extern void vga_set_pal (int start, int n, uchar *pal_data); +extern void vga_get_pal (int start, int n, uchar *pal_data); +extern void gr_wire_upoly(long c,int n,grs_vertex **vpl); +extern int gr_wire_poly(long c,int n,grs_vertex **vpl); +extern void gr_wire_ucpoly(int n,grs_vertex **vpl); +extern void gr_wire_cpoly(int n,grs_vertex **vpl); +typedef struct { + grs_vertex val; + grs_vertex d; +} grs_span_vertex; +typedef struct _span { + short l, r; + struct _span *n; + union { + struct { + grs_span_vertex *lvert, *rvert; + } pgon; + struct { + uchar *pp; + fix scale; + fix start; + } bitmap; + }; +} grs_span; +enum { + GRUS_SOLID, + GRS_SOLID, + GRUS_OPAQUE8, + GRS_OPAQUE8, + GRUS_TRANS8, + GRS_TRANS8, + GRUS_OPAQUETLUC8, + GRS_OPAQUETLUC8, + GRUS_TRANSTLUC8, + GRS_TRANSTLUC8, + GRUS_SCALED_OPAQUE8, + GRS_SCALED_OPAQUE8, + GRUS_SCALED_TRANS8, + GRS_SCALED_TRANS8, + GRUS_SCALED_OPAQUETLUC8, + GRS_SCALED_OPAQUETLUC8, + GRUS_SCALED_TRANSTLUC8, + GRS_SCALED_TRANSTLUC8, + GRUS_CLUT_SCALED_OPAQUE8, + GRS_CLUT_SCALED_OPAQUE8, + GRUS_CLUT_SCALED_TRANS8, + GRS_CLUT_SCALED_TRANS8 +}; +#define gr_span(f,t,b,s) \ + ((void (*)(short top,short bot,grs_span *sp))grd_span_table[f])(t,b,s) +extern grs_span span_list[]; +extern grs_span int_span_list[]; +extern void (**grd_span_table)(); +extern void (***grd_span_table_list)(); +extern void (***grd_span_table_list_list[])(); +extern void span_upoint (short x, short y); +extern void span_point (short x, short y); +extern void span_uhline (short x0, short y0, short x1); +extern int span_hline (short x0, short y0, short x1); +extern void span_uvline (short x0, short y0, short y1); +extern int span_vline (short x0, short y0, short y1); +extern void span_urect (short left, short right, short top, short bot); +extern int span_rect (short left, short top, short right, short bot); +extern void span_flat8_ubitmap (grs_bitmap *bm, short left, short top); +extern int span_flat8_bitmap (grs_bitmap *bm, short left, short top); +extern void span_rsd8_ubitmap (grs_bitmap *bm, short left, short top); +extern int span_rsd8_bitmap (grs_bitmap *bm, short left, short top); +extern void span_mask_flat8_ubitmap + (grs_bitmap *bm, grs_stencil *sten, short left, short top); +extern int span_mask_flat8_bitmap + (grs_bitmap *bm, grs_stencil *sten, short left, short top); +extern void span_tluc8_ubitmap (grs_bitmap *bm, short left, short top); +extern int span_tluc8_bitmap (grs_bitmap *bm, short left, short top); +extern void span_flat8_clut_ubitmap (grs_bitmap *bm, short left, short top, uchar *cl); +extern void span_scaled_flat8_ubitmap(grs_bitmap *bm, short left, short top, short w, short h); +extern int span_scaled_flat8_bitmap(grs_bitmap *bm, short left, short top, short w, short h); +extern void span_rsd8_scale_ubitmap(grs_bitmap *bm, short left, short top, short w, short h); +extern int span_rsd8_scale_bitmap(grs_bitmap *bm, short left, short top, short w, short h); +extern void span_rsd8_clut_scale_ubitmap(grs_bitmap *bm, short left, short top, short w, short h, uchar *cl); +extern int span_rsd8_clut_scale_bitmap(grs_bitmap *bm, short left, short top, short w, short h, uchar *cl); +extern void span_scaled_tluc8_ubitmap(grs_bitmap *bm, short left, short top, short w, short h); +extern int span_scaled_tluc8_bitmap(grs_bitmap *bm, short left, short top, short w, short h); +extern void span_tluc8_clut_scale_ubitmap(grs_bitmap *bm, short left, short top, short w, short h, uchar *clut); +extern int span_tluc8_clut_scale_bitmap(grs_bitmap *bm, short left, short top, short w, short h, uchar *clut); +extern void span_clut_scaled_flat8_ubitmap(grs_bitmap *bm, short left, short top, short w, short h, uchar *cl); +extern int span_clut_scaled_flat8_bitmap(grs_bitmap *bm, short left, short top, short w, short h, uchar *cl); +extern void span_scaled_masked_flat8_ubitmap(grs_bitmap *bm, grs_stencil *mask, short left, short top, short w, short h); +extern int span_scaled_masked_flat8_bitmap(grs_bitmap *bm, grs_stencil *mask, short left, short top, short w, short h); +extern void span_solid_upoly(long c,int n,grs_vertex **data); +extern int span_solid_poly(long c,int n,grs_vertex **data); +extern void span_flat8_floor_umap(long c,int n,grs_vertex **data); +extern void span_flat8_lit_floor_umap(long c,int n,grs_vertex **data); +extern void span_per_umap(grs_bitmap *bm, short nverts, grs_vertex **data); +extern int span_per_map(grs_bitmap *bm, short nverts, grs_vertex **data); +extern void span_clut_per_umap(grs_bitmap *bm, short nverts, grs_vertex **data, uchar *cl); +extern int span_clut_per_map(grs_bitmap *bm, short nverts, grs_vertex **data, uchar *cl); +extern void span_lit_per_umap(grs_bitmap *bm, short nverts, grs_vertex **data); +extern int span_lit_per_map(grs_bitmap *bm, short nverts, grs_vertex **data); +extern void span_flat8_lin_umap(grs_bitmap *bm, short nverts, grs_vertex **data); +extern int span_flat8_lin_map(grs_bitmap *bm, short nverts, grs_vertex **data); +extern void span_clut_lin_umap(grs_bitmap *bm, short nverts, grs_vertex **data, uchar *cl); +extern int span_clut_lin_map(grs_bitmap *bm, short nverts, grs_vertex **data, uchar *cl); +extern void span_tluc8_lin_umap(grs_bitmap *bm, short nverts, grs_vertex **data); +extern int span_tluc8_lin_map(grs_bitmap *bm, short nverts, grs_vertex **data); +extern void span_tluc8_clut_lin_umap(grs_bitmap *bm, short nverts, grs_vertex **data, uchar *clut); +extern int span_tluc8_clut_lin_map(grs_bitmap *bm, short nverts, grs_vertex **data, uchar *clut); +extern void span_lit_lin_umap(grs_bitmap *bm, short nverts, grs_vertex **data); +extern int span_lit_lin_map(grs_bitmap *bm, short nverts, grs_vertex **data); +extern void span_uspoly(short nverts, grs_vertex **data); +extern int span_spoly(short nverts, grs_vertex **data); +extern void span_ucpoly(short nverts, grs_vertex **data); +extern int span_cpoly(short nverts, grs_vertex **data); +extern void span_tluc8_upoly(short nverts, grs_vertex **data); +extern int span_tluc8_poly(short nverts, grs_vertex **data); +extern void span_tluc8_ucpoly(short nverts, grs_vertex **data); +extern int span_tluc8_cpoly(short nverts, grs_vertex **data); +extern void span_solid_per_umap(int n, grs_vertex **vpl, int c); +extern void span_flat8_solid_lin_umap(int n, grs_vertex **vpl, int c); +extern void span_flat8_h_double_ubitmap (grs_bitmap *bm); +extern void span_flat8_v_double_ubitmap (grs_bitmap *bm); +extern void span_flat8_hv_double_ubitmap (grs_bitmap *bm); +extern void span_flat8_smooth_h_double_ubitmap (grs_bitmap *bm); +extern void span_flat8_smooth_v_double_ubitmap (grs_bitmap *bm); +extern void span_flat8_smooth_hv_double_ubitmap (grs_bitmap *bm); +extern long span_color; +extern uchar *span_clut; +extern grs_bitmap *span_texture; +enum { + GRPS_COLOR_INT, + GRPS_INTENSITY_INT, + GRPS_TLUC8_SOLID, + GRPS_TLUC8_INT, + GRPS_FLOOR, + GRPS_LIT_FLOOR, + GRPS_NON_LIN_OPAQUE8, + GRPS_NON_LIN_TRANS8, + GRPS_NON_PER_OPAQUE8, + GRPS_NON_PER_TRANS8, + GRPS_LIT_LIN_OPAQUE8, + GRPS_LIT_LIN_TRANS8, + GRPS_LIT_PER_OPAQUE8, + GRPS_LIT_PER_TRANS8, + GRPS_NON_LIN_OPAQUETLUC8, + GRPS_NON_LIN_TRANSTLUC8, + GRPS_NON_PER_OPAQUETLUC8, + GRPS_NON_PER_TRANSTLUC8, + GRPS_LIT_LIN_OPAQUETLUC8, + GRPS_LIT_LIN_TRANSTLUC8, + GRPS_LIT_PER_OPAQUETLUC8, + GRPS_LIT_PER_TRANSTLUC8, + GRPS_FUNCS +}; +extern void (**grd_polyspan_table)(); +extern void (***grd_polyspan_table_list)(); +extern void (***grd_polyspan_table_list_list[])(); +extern void span_upoly_draw(short top, short bottom, grs_span *p, int func); +extern void span_poly_draw(short top, short bottom, grs_span *p, int func); +extern int make_poly_spans(short nverts, grs_span_vertex *vlist, int *top, int *bottom); +extern void span_upoly_setup(short nverts, grs_vertex **data, int func); +extern int span_poly_setup(short nverts, grs_vertex **data, int func); +extern void span_per_upoly_setup(short nverts, grs_vertex **data, int func); +extern int span_per_poly_setup(short nverts, grs_vertex **data, int func); +#ifndef _TLUCTAB +#define _TLUCTAB +extern uchar *gr_init_translucency_table(uchar *p, fix opacity, fix purity, grs_rgb color); +extern uchar *gr_init_lit_translucency_table(uchar *p, fix opacity, fix purity, grs_rgb color, grs_rgb light); +extern uchar *gr_init_lit_translucency_tables(uchar *p, fix opacity, fix purity, grs_rgb color, int n); +extern int gr_dump_tluc8_table(uchar *buf, int nlit); +extern void gr_read_tluc8_table(uchar *buf); +#define gr_alloc_translucency_table(n) \ + ((uchar *)gr_malloc(n*256)) +#define gr_free_translucency_table(tab) (gr_free(tab)) +#define gr_make_translucency_table(op, pu, co) \ + (gr_init_translucency_table(gr_alloc_translucency_table(1), op, pu, co)) +#define gr_make_lit_translucency_table(op, pu, co, li) \ + (gr_init_translucency_table(gr_alloc_translucency_table(1), op, pu, co, li)) +#define gr_make_lit_translucency_tables(op, pu, co, lnum) \ + (gr_init_lit_translucency_tables(gr_alloc_translucency_table(lnum), op, pu, co, lnum)) +#define gr_make_tluc8_table(num, op, pu, co) \ + (tluc8tab[num]=gr_make_translucency_table(op, pu, co)) +#define gr_make_lit_tluc8_table(num, op, pu, co, li) \ + (tluc8ltab[num]=gr_make_lit_translucency_tables(op, pu, co, li), \ + gr_make_tluc8_table(num, op, pu, co)) +#define gr_alloc_tluc8_spoly_table(num) \ + (tluc8nstab=num, tluc8stab=gr_alloc_translucency_table(num)) +#define gr_init_tluc8_spoly_table(num, op, pu, co, li) \ + (gr_init_lit_translucency_table(tluc8stab+(256*num), op, pu, co, li)) +#define gr_init_tluc8_spoly_tables(num, op, pu, co, li) \ + (gr_init_lit_translucency_tables(tluc8stab+(256*num), op, pu, co, li)) +#define gr_bind_tluc8_table(num, p) (tluc8tab[num]=p) +#define gr_bind_lit_tluc8_table(num, p) (tluc8ltab[num]=p) +#define gr_bind_tluc8_spoly_table(p) (tluc8stab=p) +#endif +extern uchar *tluc8tab[256]; +extern uchar *tluc8ltab[256]; +extern uchar *tluc8stab; +extern int tluc8nstab; +#ifndef _RSDCVT_C +extern uchar *grd_unpack_buf; +extern int gr_rsd8_convert(grs_bitmap *sbm, grs_bitmap *dbm); +#endif +uchar *gr_rsd8_unpack(uchar* src, uchar *dst); + +// MLA - added these from TMapFcn, so the 3d lib can get to them without including it + + +// MLA - removed on mac +//#pragma aux gr_rsd8_unpack parm [esi] [edi] value [edi] modify [eax ecx edx esi edi] + +#define gr_set_unpack_buf(buf) grd_unpack_buf=buf +#define gr_get_unpack_buf() grd_unpack_buf +#define GR_UNPACK_RSD8_OK 0 +#define GR_UNPACK_RSD8_NOBUF 1 +#define GR_UNPACK_RSD8_NOTRSD 2 +uchar gr_free_blend(void); +uchar gr_init_blend(int log_blend_levels); +typedef struct iaaiiaia{ + void (*f)(); + struct iaaiiaia *next; + uchar flags; +} grs_func_chain; +extern short grd_pixel_index; +extern short grd_canvas_index; +extern uchar chn_flags; +#define CHN_ON 1 +#define CHN_GEN 2 +extern grs_func_chain *gr_chain_add_over(int n, void (*f)()); +extern grs_func_chain *gr_chain_add_before(int n, void (*f)(void)); +extern grs_func_chain *gr_chain_add_after(int n, void (*f)(void)); +extern void (*chain_rest())(); +extern void gr_unchain(int n); +extern void gr_rechain(int n); +extern void gr_unchain_all(); +extern void gr_rechain_all(); +#define gr_do_chain (chain_rest()) +#define gr_chaining_on() (chn_flags |= CHN_ON) +#define gr_chaining_off() (chn_flags &= ~CHN_ON) +#define gr_chaining_toggle() (chn_flags ^= CHN_ON) +#define gr_generic (chn_flags & CHN_GEN) +extern void gr_force_generic(); +extern void gr_unforce_generic(); +#define gr_toggle_generic() (gr_generic? gr_unforce_generic() : gr_force_generic()) +#define gr_start_frame ((void (*)())grd_canvas_table[START_FRAME]) +#define gr_end_frame ((void (*)())grd_canvas_table[END_FRAME]) +#define MAX_PPROF_OBJ_CNT (1<<12) +extern unsigned short *pixprof_screen; +extern uchar pixprof_setup(); +extern void pixprof_report(); +#define install_pixprof_report() (gr_chain_add_before(END_FRAME, &pixprof_report)) +#define pixprof_on() (gr_chaining_on(), gr_force_generic()) +#define pixprof_off() (gr_chaining_off(), gr_unforce_generic()) +#define pixprof_toggle() (gr_chaining_toggle(), gr_toggle_generic()) +extern void start_thing_prof(); +extern void end_thing_prof(); +extern unsigned short pixprof_objects; +extern char *fcount_names[GRD_CANVAS_FUNCS]; +extern int *fcount_table; +extern void fcount_increment(); +extern void fcount_start(); +extern void fcount_stop(); +extern void fcount_report(); +extern void fcount_install(); + +#pragma pack(pop) + +#if defined(__cplusplus) +} +#endif // !defined(__cplusplus) + +#endif /* __2D_H */ diff --git a/engine/src/Libraries/2D/Source/Clip/clip.h b/engine/src/Libraries/2D/Source/Clip/clip.h new file mode 100644 index 0000000..50763be --- /dev/null +++ b/engine/src/Libraries/2D/Source/Clip/clip.h @@ -0,0 +1,87 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/rcs/clip.h $ + * $Revision: 1.11 $ + * $Author: kaboom $ + * $Date: 1993/09/02 19:41:01 $ + * + * Prototypes for 2d clippers, constants for clipping codes. + * + * $Log: clip.h $ + * Revision 1.11 1993/09/02 19:41:01 kaboom + * Added prototype for 24-bit bitmap clipper. + * + * Revision 1.10 1993/08/10 18:48:49 kaboom + * Added prototype for gr_clip_spoly. + * + * Revision 1.9 1993/02/22 14:32:46 kaboom + * Changed name of gr_clip_int_rect() to gr_clip_rect(). Removed the + * prototypes for gr_clip_fix_rect() and gr_clip_int_poly(). + * + * Revision 1.8 1993/02/19 13:12:04 jaemz + * Added definition for cpoly clipper + * + * Revision 1.7 1993/02/04 17:09:02 kaboom + * Fixed bug in prototype for gr_clip_fix_rect. Moved clip code defines + * to here. + * + * Revision 1.6 1993/01/14 18:30:46 kaboom + * Added prototype for gr_clip_fix_poly(). Changed clip_xxx to gr_clip_xxx. + * + * Revision 1.5 1993/01/12 00:03:07 kaboom + * Added prototype for clip_int_poly(). + * + * Revision 1.4 1992/11/12 13:46:58 kaboom + * Changed flat8 bitmap clipper to not allocate a new bitmap; now it changes the + * bitmap passed in and returns the clip code. Added prototype for monochrome + * bitmap clipper. + * + * Revision 1.3 1992/10/21 16:02:32 kaboom + * Updates references to gr_xxx structures to grs_xxx. + * + * Revision 1.2 1992/10/13 17:40:12 kaboom + * Added prototypes for clip_fix_line, clip_int_rect, clip_fix_rect, and also + * for clip_int_bitmap. + * + * Revision 1.1 1992/10/09 16:51:22 kaboom + * Initial revision + */ + +#ifndef __CLIP_H +#define __CLIP_H +/* prototypes for analytic clippers. */ +extern int gr_clip_int_line (short *x0, short *y0, short *x1, short *y1); +extern int gr_clip_fix_line (long *x0, long *y0, long *x1, long *y1); +extern int gr_clip_fix_poly (int n, fix *vlist, fix *clist); +extern int gr_clip_spoly (int n, fix *vlist, fix *clist, fix *ilist, fix *cilist); +extern int gr_clip_fix_cpoly (int n, fix *vlist, grs_rgb *blist, fix *clist, grs_rgb *cblist); +extern int gr_clip_rect (short *left, short *top, short *right, short *bot); +extern int gr_clip_mono_bitmap (grs_bitmap *bm, short *x, short *y); +extern int gr_clip_flat8_bitmap (grs_bitmap *bm, short *x, short *y); +extern int gr_clip_flat24_bitmap (grs_bitmap *bm, short *x, short *y); + +/* clip codes. */ +#define CLIP_NONE 0 +#define CLIP_LEFT 1 +#define CLIP_TOP 2 +#define CLIP_RIGHT 4 +#define CLIP_BOT 8 +#define CLIP_ALL 16 +#endif /* !__CLIP_H */ diff --git a/engine/src/Libraries/2D/Source/Clip/clpclin.c b/engine/src/Libraries/2D/Source/Clip/clpclin.c new file mode 100644 index 0000000..216ff54 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Clip/clpclin.c @@ -0,0 +1,104 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/clpclin.c $ + * $Revision: 1.2 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 01:14:47 $ +*/ + +/* clip and fill colored line -- using line fill table and grs_vertex interfaces. + These will be the preferred interfaces and are called from the original + gr_xxx canvas table x,y routines. + */ + +#include +#include "clpcon.h" +#include "clpfcn.h" +#include "grrend.h" +#include "rgb.h" +#include "scrdat.h" +#include "clpltab.h" +#include "lg.h" + +#define fix_make_nof(x) fix_make(x,0x0000) + +/* The amount of copying into and out of vertex's is quite + disgusting. + + Also, becuase of the vertex interface, we've introduced a new + function call that we can't inline because it needs to return a + value. + */ + +int gri_cline_clip (grs_vertex *v0, grs_vertex *v1) +{ + int r; + fix x0, y0, x1, y1; + fix xb0, xb1, yb0, yb1; + uchar r0, g0, b0, r1, g1, b1; + fix dr, dg, db; + fix pixels, pixels_0, pixels_1; + + /* transfer out of v0, v1 -- from old gen_clin */ + x0 = v0->x; y0 = v0->y; + r0 = (uchar) (v0->u); g0 = (uchar) (v0->v); b0 = (uchar) (v0->w); + x1 = v1->x; y1 = v1->y; + r1 = (uchar) (v1->u); g1 = (uchar) (v1->v); b1 = (uchar) (v1->w); + + + xb0 = x0; xb1 = x1; yb0 = y0; yb1 = y1; + + pixels = lg_max(fix_abs(y1-y0),fix_abs(x1-x0)); + + if (pixels != 0) { + dr = fix_div(fix_make_nof(r1-r0),pixels); + dg = fix_div(fix_make_nof(g1-g0),pixels); + db = fix_div(fix_make_nof(b1-b0),pixels); + } + + r = gr_clip_fix_line (&x0, &y0, &x1, &y1); + if (r != CLIP_ALL) { + + if (((r0 != r1) || (g0 != g1) || (b0 != b1)) && + ((x0 != xb0) || (y0 != yb0) || (x1 != xb1) || (y1 != yb1))) { + + pixels_0 = lg_max(fix_abs(yb0-y0),fix_abs(xb0-x0)); /* # pixels lost */ + pixels_1 = lg_max(fix_abs(yb1-y1),fix_abs(xb1-x1)); /* for endpoints */ + + r0 += fix_int(fix_mul(dr, pixels_0)); + g0 += fix_int(fix_mul(dg, pixels_0)); + b0 += fix_int(fix_mul(db, pixels_0)); + + r1 -= fix_int(fix_mul(dr, pixels_1)); + g1 -= fix_int(fix_mul(dg, pixels_1)); + b1 -= fix_int(fix_mul(db, pixels_1)); + } + } + + /* and transfer back to v0, v1 */ + (v0->x) = x0; (v0->y) = y0; + (v0->u) = r0; (v0->v) = g0; (v0->w) = b0; + (v1->x) = x1; (v1->y) = y1; + (v1->u) = r1; (v1->v) = g1; (v1->w) = b1; + + return r; + } + + diff --git a/engine/src/Libraries/2D/Source/Clip/clpcon.h b/engine/src/Libraries/2D/Source/Clip/clpcon.h new file mode 100644 index 0000000..852e381 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Clip/clpcon.h @@ -0,0 +1,45 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/clpcon.h $ + * $Revision: 1.1 $ + * $Author: kaboom $ + * $Date: 1993/10/01 15:32:22 $ + * + * Clipping code constants. + * + * This file is part of the 2d library. + * + * $Log: clpcon.h $ + * Revision 1.1 1993/10/01 15:32:22 kaboom + * Initial revision + * + */ + +#ifndef __CLPCON +#define __CLPCON + +/* clip codes. */ +#define CLIP_NONE 0 +#define CLIP_LEFT 1 +#define CLIP_TOP 2 +#define CLIP_RIGHT 4 +#define CLIP_BOT 8 +#define CLIP_ALL 16 +#endif /* !__CLPCON */ diff --git a/engine/src/Libraries/2D/Source/Clip/clpf24.c b/engine/src/Libraries/2D/Source/Clip/clpf24.c new file mode 100644 index 0000000..52b355f --- /dev/null +++ b/engine/src/Libraries/2D/Source/Clip/clpf24.c @@ -0,0 +1,84 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/clpf24.c $ + * $Revision: 1.3 $ + * $Author: kaboom $ + * $Date: 1993/10/19 09:50:01 $ + * + * Routines for clipping flat 24 bitmaps to a rectangle. + * + * This file is part of the 2d library. + * + * $Log: clpf24.c $ + * Revision 1.3 1993/10/19 09:50:01 kaboom + * Replaced #include w; t=*y; b=t+bm->h; + if (l>grd_clip.right || rgrd_clip.bot || bw -= extra; + bm->bits += 3*extra; + *x = grd_clip.left; + code |= CLIP_LEFT; + } + if (r > grd_clip.right) { + /* off the right edge of the window. */ + bm->w -= r-grd_clip.right; + code |= CLIP_RIGHT; + } + if (t < grd_clip.top) { + /* off the top of the window. */ + extra = grd_clip.top-t; + bm->h -= extra; + bm->bits += bm->row*extra; + *y = grd_clip.top; + code |= CLIP_TOP; + } + if (b > grd_clip.bot) { + /* off the bottom of the window. */ + bm->h -= b-grd_clip.bot; + code |= CLIP_BOT; + } + + return code; +} diff --git a/engine/src/Libraries/2D/Source/Clip/clpfcn.h b/engine/src/Libraries/2D/Source/Clip/clpfcn.h new file mode 100644 index 0000000..f3492ea --- /dev/null +++ b/engine/src/Libraries/2D/Source/Clip/clpfcn.h @@ -0,0 +1,70 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/clpfcn.h $ + * $Revision: 1.3 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 00:46:51 $ + * + * Routines for clipping vertex pointer polygons to a rectangle. + * + * This file is part of the 2d library. + * + * $Log: clpfcn.h $ + * Revision 1.3 1994/06/11 00:46:51 lmfeeney + * gr_clip_fix_code is now used in mulitple 2d files, but probably + * doesn't need to be external to 2d.h (oh, well) + * + * Revision 1.2 1993/10/19 09:59:01 kaboom + * Fixed #ifndef check macro, now includes grs.h. + * + * Revision 1.1 1993/10/01 15:34:00 kaboom + * Initial revision + */ + +#ifndef __CLPFCN_H +#define __CLPFCN_H +#include "grs.h" +#include "plytyp.h" + + +/* prototypes for analytic clippers. */ +extern int gr_clip_fix_code + (fix, fix); +extern int gr_clip_int_line + (short *x0, short *y0, short *x1, short *y1); +extern int gr_clip_fix_line + (fix *x0, fix *y0, fix *x1, fix *y1); +extern int gr_clip_fix_poly + (int n, fix *vlist, fix *clist); +extern int gr_clip_poly + (int n, int l, grs_vertex **vplist, grs_vertex ***pcplist); +extern int gr_clip_spoly + (int n, fix *vlist, fix *clist, fix *ilist, fix *cilist); +extern int gr_clip_fix_cpoly + (int n, fix *vlist, grs_rgb *blist, fix *clist, grs_rgb *cblist); +extern int gr_clip_rect + (short *left, short *top, short *right, short *bot); +extern int gr_clip_mono_bitmap + (grs_bitmap *bm, short *x, short *y); +extern int gr_clip_flat8_bitmap + (grs_bitmap *bm, short *x, short *y); +extern int gr_clip_flat24_bitmap + (grs_bitmap *bm, short *x, short *y); +#endif /* !__CLPFCN_H */ diff --git a/engine/src/Libraries/2D/Source/Clip/clplin.c b/engine/src/Libraries/2D/Source/Clip/clplin.c new file mode 100644 index 0000000..0143c2d --- /dev/null +++ b/engine/src/Libraries/2D/Source/Clip/clplin.c @@ -0,0 +1,131 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/clplin.c $ + * $Revision: 1.4 $ + * $Author: kaboom $ + * $Date: 1993/10/19 09:50:06 $ + * + * Routines for clipping fixed-point lines to a rectangle. + * + * This file is part of the 2d library. + * + * $Log: clplin.c $ + * Revision 1.4 1993/10/19 09:50:06 kaboom + * Replaced #include grd_fix_clip.right-fix_make(1,0)) + code |= CLIP_RIGHT; + if (y < grd_fix_clip.top) + code |= CLIP_TOP; + else if (y > grd_fix_clip.bot-fix_make(1,0)) + code |= CLIP_BOT; + + return code; +} + +/* fixed-point Cohen-Sutherland line clipper. */ +int gr_clip_fix_line (fix *x0, fix *y0, fix *x1, fix *y1) +{ + int code0; /* clip code for (x0,y0) */ + int code1; /* code for (x1,y1) */ + int code; /* code for current point */ + fix dx; /* x distance */ + fix dy; /* y distance */ + fix *px; /* pointer to x current coordinate */ + fix *py; /* " to current y */ + + dx = *x1-*x0; + dy = *y1-*y0; + + while (1) { + /* get codes for endpoints. */ + code0 = gr_clip_fix_code (*x0, *y0); + code1 = gr_clip_fix_code (*x1, *y1); + + if (code0==0 && code1==0) /* check trivial accept */ + return CLIP_NONE; + else if ((code0&code1) != 0) /* check for trivial reject */ + return CLIP_ALL; + + /* set current code and px&py. first, for point0, then when it's + dealt with, point1. */ + if (code0 != 0) { + px = x0; + py = y0; + code = code0; + } else { + px = x1; + py = y1; + code = code1; + } + + /* check for left/right clip; compute intersection. */ + if (code & CLIP_LEFT) { + *py += fix_mul_div (dy, grd_fix_clip.left-*px, dx); + *px = grd_fix_clip.left; + } else if (code & CLIP_RIGHT) { + *py += fix_mul_div (dy, grd_fix_clip.right-fix_make(1,0)-*px, dx); + *px = grd_fix_clip.right-fix_make(1,0); + } + /* check for top/bottom clip; compute intersection. */ + if (code & CLIP_TOP) { + *px += fix_mul_div (dx, grd_fix_clip.top-*py, dy); + *py = grd_fix_clip.top; + } else if (code & CLIP_BOT) { + *px += fix_mul_div (dx, grd_fix_clip.bot-fix_make(1,0)-*py, dy); + *py = grd_fix_clip.bot-fix_make(1,0); + } + } +} diff --git a/engine/src/Libraries/2D/Source/Clip/clplin2.c b/engine/src/Libraries/2D/Source/Clip/clplin2.c new file mode 100644 index 0000000..79b75b6 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Clip/clplin2.c @@ -0,0 +1,93 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/clplin2.c $ + * $Revision: 1.1 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 01:17:48 $ +*/ + +#include +#include "grs.h" +#include "clpcon.h" +#include "clpfcn.h" +#include "grrect.h" +#include "clpltab.h" + +/* The amount of copying into and out of vertex's is quite + disgusting. + + Also, becuase of the vertex interface, we've introduced a new + function call that we can't inline because it needs to return a + value. + */ + +int gri_line_clip (grs_vertex *v0, grs_vertex *v1) +{ + int code0; /* clip code for (x0,y0) */ + int code1; /* code for (x1,y1) */ + int code; /* code for current point */ + fix dx; /* x distance */ + fix dy; /* y distance */ + fix *px; /* pointer to x current coordinate */ + fix *py; /* pointer to y current coordinate */ + + dx = v1->x - v0->x; + dy = v1->y - v0->y; + + while (1) { + /* get codes for endpoints. */ + code0 = gr_clip_fix_code (v0->x, v0->y); + code1 = gr_clip_fix_code (v1->x, v1->y); + + if (code0==0 && code1==0) /* check trivial accept */ + return CLIP_NONE; + else if ((code0&code1) != 0) /* check for trivial reject */ + return CLIP_ALL; + + /* set current code and px&py. first, for point0, then when it's + dealt with, point1. */ + if (code0 != 0) { + px = &(v0->x); + py = &(v0->y); + code = code0; + } else { + px = &(v1->x); + py = &(v1->y); + code = code1; + } + + /* check for left/right clip; compute intersection. */ + if (code & CLIP_LEFT) { + *py += fix_mul_div (dy, grd_fix_clip.left-*px, dx); + *px = grd_fix_clip.left; + } else if (code & CLIP_RIGHT) { + *py += fix_mul_div (dy, grd_fix_clip.right-fix_make(1,0)-*px, dx); + *px = grd_fix_clip.right-fix_make(1,0); + } + /* check for top/bottom clip; compute intersection. */ + if (code & CLIP_TOP) { + *px += fix_mul_div (dx, grd_fix_clip.top-*py, dy); + *py = grd_fix_clip.top; + } else if (code & CLIP_BOT) { + *px += fix_mul_div (dx, grd_fix_clip.bot-fix_make(1,0)-*py, dy); + *py = grd_fix_clip.bot-fix_make(1,0); + } + } +} diff --git a/engine/src/Libraries/2D/Source/Clip/clpltab.c b/engine/src/Libraries/2D/Source/Clip/clpltab.c new file mode 100644 index 0000000..ff2c0fe --- /dev/null +++ b/engine/src/Libraries/2D/Source/Clip/clpltab.c @@ -0,0 +1,44 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/clpltab.c $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/08/04 09:54:59 $ + */ + +#include "clpltyp.h" +#include "clpltab.h" +#include "grnull.h" +#include "line.h" + +grt_line_clip_fill grd_line_clip_fill_table [GRD_LINE_TYPES] = +{ + gri_line_clip_fill, + gri_iline_clip_fill, + gri_hline_clip_fill, + gri_vline_clip_fill, + gri_sline_clip_fill, + gri_cline_clip_fill, + gri_wire_poly_line_clip_fill, + gr_null, + gri_wire_poly_cline_clip_fill +}; + +grt_line_clip_fill * grd_line_clip_fill_vector = grd_line_clip_fill_table; diff --git a/engine/src/Libraries/2D/Source/Clip/clpltab.h b/engine/src/Libraries/2D/Source/Clip/clpltab.h new file mode 100644 index 0000000..e543d3f --- /dev/null +++ b/engine/src/Libraries/2D/Source/Clip/clpltab.h @@ -0,0 +1,69 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/clpltab.h $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/08/04 09:45:09 $ + */ + +/* External defintions for clipped line drawers and line clippers. + + In general, there would be a table of clipped line drawers. Each + clipped line drawer would call the appropriate line clipper and + unclipped line drawer in whatever way it needed to for the kind of + clip. + + NB: The vlin drawer can't easily call a clipper, since it is + compiled to be called from an interrupt. It inlines the routine. + The hlin drawer also inlines it's clipper, since the fn call + overhead is significant. + + (A stecil clipped line drawer might interleave clip and unclipped + line draw calls, for example.) +*/ + +#ifndef __CLPLTAB_H +#define __CLPLTAB_H + +#include "plytyp.h" + +/* functions living in the vector */ + +extern int gri_line_clip_fill (long, long, grs_vertex *, grs_vertex *); +extern int gri_iline_clip_fill (long, long, grs_vertex *, grs_vertex *); +extern int gri_cline_clip_fill (long c, long parm, grs_vertex *v0, grs_vertex *v1); +extern int gri_sline_clip_fill (long c, long parm, grs_vertex *v0, grs_vertex *v1); +extern int gri_hline_clip_fill (short, short, short, long, long); +extern int gri_vline_clip_fill (short, short, short, long, long); + +extern int gri_wire_poly_line_clip_fill (long c, long parm, grs_vertex *v0, grs_vertex *v1); +extern int gri_wire_poly_sline_clip_fill (long c, long parm, grs_vertex *v0, grs_vertex *v1); +extern int gri_wire_poly_cline_clip_fill (long c, long parm, grs_vertex *v0, grs_vertex *v1); + +/* actual clippers */ +extern int gri_line_clip (grs_vertex *, grs_vertex *); +extern int gri_cline_clip (grs_vertex *, grs_vertex *); +extern int gri_sline_clip (grs_vertex *, grs_vertex *); + +/* these are implemented, but are not used */ +extern int gri_hline_clip (short *, short *, short *); +extern int gri_vline_clip (short *, short *, short *); + +#endif diff --git a/engine/src/Libraries/2D/Source/Clip/clpltyp.h b/engine/src/Libraries/2D/Source/Clip/clpltyp.h new file mode 100644 index 0000000..62bb29f --- /dev/null +++ b/engine/src/Libraries/2D/Source/Clip/clpltyp.h @@ -0,0 +1,42 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/clpltyp.h $ + * $Revision: 1.1 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 01:10:35 $ + */ + +#ifndef __CLPLTYPE_H +#define __CLPLTYPE_H + +#include "plytyp.h" + +typedef + void *grt_line_clip_fill; + +typedef + int (*grt_line_clip_fill_v) (long, long, grs_vertex *, grs_vertex *); + +typedef + int (*grt_line_clip_fill_xy) (short, short, short, long, long); + +extern grt_line_clip_fill *grd_line_clip_fill_vector; + +#endif diff --git a/engine/src/Libraries/2D/Source/Clip/clpmono.c b/engine/src/Libraries/2D/Source/Clip/clpmono.c new file mode 100644 index 0000000..d086c60 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Clip/clpmono.c @@ -0,0 +1,91 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/clpmono.c $ + * $Revision: 1.4 $ + * $Author: kaboom $ + * $Date: 1993/10/19 09:50:10 $ + * + * Routines for clipping monochrome bitmaps to a rectangle. + * + * This file is part of the 2d library. + * + * $Log: clpmono.c $ + * Revision 1.4 1993/10/19 09:50:10 kaboom + * Replaced #include w; t=*y; b=t+bm->h; + if (r<=grd_clip.left || l>=grd_clip.right || + b<=grd_clip.top || t>=grd_clip.bot) + /* bitmap is completely clipped. */ + return CLIP_ALL; + + if (l < grd_clip.left) { + /* bitmap is off the left edge of the window. */ + extra = grd_clip.left-l; + bm->w -= extra; + bm->bits += extra/8; + if ((bm->align+=extra%8) > 7) { + bm->align -= 8; + bm->bits++; + } + *x = grd_clip.left; + code |= CLIP_LEFT; + } + if (r > grd_clip.right) { + /* off the right edge of the window. */ + bm->w -= *x+bm->w-grd_clip.right; + code |= CLIP_RIGHT; + } + if (t < grd_clip.top) { + /* off the top of the window. */ + extra = grd_clip.top-t; + bm->h -= extra; + bm->bits += bm->row*extra; + *y = grd_clip.top; + code |= CLIP_TOP; + } + if (b > grd_clip.bot) { + /* off the bottom of the window. */ + bm->h -= b-grd_clip.bot; + code |= CLIP_BOT; + } + + return code; +} diff --git a/engine/src/Libraries/2D/Source/Clip/clpply.c b/engine/src/Libraries/2D/Source/Clip/clpply.c new file mode 100644 index 0000000..b0aaa72 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Clip/clpply.c @@ -0,0 +1,251 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/clpply.c $ + * $Revision: 1.4 $ + * $Author: kevin $ + * $Date: 1994/08/10 01:32:39 $ + * + * Routines for clipping a polygon to a rectangle. + * + * This file is part of the 2d library. + * + * $Log: clpply.c $ + * Revision 1.4 1994/08/10 01:32:39 kevin + * fixed typo. + * + * Revision 1.3 1993/10/19 09:50:11 kaboom + * Replaced #include tplist */ + v0 = ((fix **)vpl)[n-1]; + for (i=j=0; i= grd_canvas->gc.clip.f.left) { + /* start point is inside half plane */ + if (v1[0] >= grd_canvas->gc.clip.f.left) + /* both points inside half plane. output end point. */ + tplist[j++] = v1; + else { + /* edge exits half plane. output intersection. */ + num = grd_canvas->gc.clip.f.left-v0[0]; + den = v1[0]-v0[0]; + tlist[new_v] = grd_canvas->gc.clip.f.left; + tlist[new_v+1] = v0[1]+fix_mul_div(v1[1]-v0[1], num, den); + for (k=2; k= grd_canvas->gc.clip.f.left) { + /* edge enters half plane. output intersection and end point. */ + if (v1[0] != grd_canvas->gc.clip.f.left) { + num = grd_canvas->gc.clip.f.left-v0[0]; + den = v1[0]-v0[0]; + tlist[new_v] = grd_canvas->gc.clip.f.left; + tlist[new_v+1] = v0[1]+fix_mul_div(v1[1]-v0[1], num, den); + for (k=2; kcplist */ + v0 = tplist[j-1]; + for (n=j, i=j=0; i= grd_canvas->gc.clip.f.top) { + /* start point is inside half plane */ + if (v1[1] >= grd_canvas->gc.clip.f.top) + /* both points inside half plane. output end point. */ + cplist[j++] = v1; + else { + /* edge exits half plane. output intersection. */ + num = grd_canvas->gc.clip.f.top-v0[1]; + den = v1[1]-v0[1]; + tlist[new_v] = v0[0]+fix_mul_div(v1[0]-v0[0], num, den); + tlist[new_v+1] = grd_canvas->gc.clip.f.top; + for (k=2; k= grd_canvas->gc.clip.f.top) { + /* edge enters half plane. output intersection and end point. */ + if (v1[1] != grd_canvas->gc.clip.f.top) { + num = grd_canvas->gc.clip.f.top-v0[1]; + den = v1[1]-v0[1]; + tlist[new_v] = v0[0]+fix_mul_div(v1[0]-v0[0], num, den); + tlist[new_v+1] = grd_canvas->gc.clip.f.top; + for (k=2; ktplist */ + v0 = cplist[j-1]; + for (n=j, i=j=0; igc.clip.f.right) { + /* start point is inside half plane */ + if (v1[0] <= grd_canvas->gc.clip.f.right) + /* both points inside half plane. output end point. */ + tplist[j++] = v1; + else { + /* edge exits half plane. output intersection. */ + num = grd_canvas->gc.clip.f.right-v0[0]; + den = v1[0]-v0[0]; + tlist[new_v] = grd_canvas->gc.clip.f.right; + tlist[new_v+1] = v0[1]+fix_mul_div(v1[1]-v0[1], num, den); + for (k=2; kgc.clip.f.right) { + /* edge enters half plane. output intersection and end point. */ + if (v1[0] != grd_canvas->gc.clip.f.right) { + num = grd_canvas->gc.clip.f.right-v0[0]; + den = v1[0]-v0[0]; + tlist[new_v] = grd_canvas->gc.clip.f.right; + tlist[new_v+1] = v0[1]+fix_mul_div(v1[1]-v0[1], num, den); + for (k=2; kcplist */ + v0 = tplist[j-1]; + for (n=j, i=j=0; igc.clip.f.bot) { + /* start point is inside half plane */ + if (v1[1] <= grd_canvas->gc.clip.f.bot) + /* both points inside half plane. output end point. */ + cplist[j++] = v1; + else { + /* edge exits half plane. output intersection. */ + num = grd_canvas->gc.clip.f.bot-v0[1]; + den = v1[1]-v0[1]; + tlist[new_v] = v0[0]+fix_mul_div(v1[0]-v0[0], num, den); + tlist[new_v+1] = grd_canvas->gc.clip.f.bot; + for (k=2; kgc.clip.f.bot) { + /* edge enters half plane. output intersection and end point. */ + if (v1[1] != grd_canvas->gc.clip.f.bot) { + num = grd_canvas->gc.clip.f.bot-v0[1]; + den = v1[1]-v0[1]; + tlist[new_v] = v0[0]+fix_mul_div(v1[0]-v0[0], num, den); + tlist[new_v+1] = grd_canvas->gc.clip.f.bot; + for (k=2; k. + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/clppoly.c $ + * $Revision: 1.4 $ + * $Author: kaboom $ + * $Date: 1993/10/19 09:50:13 $ + * + * Routines for clipping fixed-point polygons to a rectangle. + * + * This file is part of the 2d library. + * + * $Log: clppoly.c $ + * Revision 1.4 1993/10/19 09:50:13 kaboom + * Replaced #include tlist */ + x0 = vlist[2*(n-1)]; + y0 = vlist[2*(n-1)+1]; + for (i=j=0; i= grd_fix_clip.left) { + /* start point is inside half plane */ + if (x1 >= grd_fix_clip.left) { + /* both points inside half plane. output end point. */ + tlist[2*j] = x1; + tlist[2*j+1] = y1; + j++; + } else { + /* edge exits half plane. output intersection. */ + fix dx = x1-x0; + fix dy = y1-y0; + + tlist[2*j] = grd_fix_clip.left; + tlist[2*j+1] = y0 + fix_mul_div (dy,(grd_fix_clip.left-x0),dx); + j++; + } + } else { + /* start point is outside half plane. */ + if (x1 >= grd_fix_clip.left) { + /* edge enters half plane. output intersection and end point. */ + fix dx = x1-x0; + fix dy = y1-y0; + + if (x1 != grd_fix_clip.left) { + tlist[2*j] = grd_fix_clip.left; + tlist[2*j+1] = y0 + fix_mul_div (dy,(grd_fix_clip.left-x0),dx); + j++; + } + tlist[2*j] = x1; + tlist[2*j+1] = y1; + j++; + } else + /* both points outside, eliminate edge. */ + ; + } + x0=x1; y0=y1; + } + + /* clip top edge from tlist->clist */ + x0 = tlist[2*(j-1)]; + y0 = tlist[2*(j-1)+1]; + for (n=j, i=j=0; i= grd_fix_clip.top) { + /* start point is inside half plane */ + if (y1 >= grd_fix_clip.top) { + /* both points inside half plane. output end point. */ + clist[2*j] = x1; + clist[2*j+1] = y1; + j++; + } else { + /* edge exits half plane. output intersection. */ + fix dx = x1-x0; + fix dy = y1-y0; + + clist[2*j] = x0 + fix_mul_div (dx,(grd_fix_clip.top-y0),dy); + clist[2*j+1] = grd_fix_clip.top; + j++; + } + } else { + /* start point is outside half plane. */ + if (y1 >= grd_fix_clip.top) { + /* edge enters half plane. output intersection and end point. */ + fix dx = x1-x0; + fix dy = y1-y0; + + if (y1 != grd_fix_clip.top) { + clist[2*j] = x0 + fix_mul_div (dx,(grd_fix_clip.top-y0),dy); + clist[2*j+1] = grd_fix_clip.top; + j++; + } + clist[2*j] = x1; + clist[2*j+1] = y1; + j++; + } else + /* both points outside, eliminate edge. */ + ; + } + x0=x1; y0=y1; + } + + /* clip right edge from clist->tlist */ + x0 = clist[2*(j-1)]; + y0 = clist[2*(j-1)+1]; + for (n=j, i=j=0; iclist */ + x0 = tlist[2*(j-1)]; + y0 = tlist[2*(j-1)+1]; + for (n=j, i=j=0; i. + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/clprect.c $ + * $Revision: 1.4 $ + * $Author: kaboom $ + * $Date: 1993/10/19 09:50:14 $ + * + * Routines for clipping rectangles to a rectangle. + * + * This file is part of the 2d library. + * + * $Log: clprect.c $ + * Revision 1.4 1993/10/19 09:50:14 kaboom + * Replaced #include =grd_clip.right || + *bot<=grd_clip.top || *top>=grd_clip.bot) + /* rect is completely clipped. */ + return CLIP_ALL; + + if (*left < grd_clip.left) { + /* rect is off the left edge of the window. */ + *left = grd_clip.left; + code |= CLIP_LEFT; + } + if (*right > grd_clip.right) { + /* off the right edge of the window. */ + *right = grd_clip.right; + code |= CLIP_RIGHT; + } + if (*top < grd_clip.top) { + /* off the top of the window. */ + *top = grd_clip.top; + code |= CLIP_TOP; + } + if (*bot > grd_clip.bot) { + /* off the bottom of the window. */ + *bot = grd_clip.bot; + code |= CLIP_BOT; + } + + return code; +} diff --git a/engine/src/Libraries/2D/Source/Clip/clpslin.c b/engine/src/Libraries/2D/Source/Clip/clpslin.c new file mode 100644 index 0000000..5af138b --- /dev/null +++ b/engine/src/Libraries/2D/Source/Clip/clpslin.c @@ -0,0 +1,89 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/clpslin.c $ + * $Revision: 1.2 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 01:13:24 $ +*/ + +/* clip and fill shaded line -- using line fill table and grs_vertex interfaces. + These will be the preferred interfaces and are called from the original + gr_xxx canvas table x,y routines. + */ + +#include "clpcon.h" +#include "clpfcn.h" +#include "clpltab.h" +#include "lg.h" + +/* The amount of copying into and out of vertex's is quite + disgusting. + + Also, becuase of the vertex interface, we've introduced a new + function call that we can't inline because it needs to return a + value. + */ + +int gri_sline_clip (grs_vertex *v0, grs_vertex *v1) +{ + int r; + + fix x0, y0, x1, y1; + fix xb0, xb1, yb0, yb1; + fix i0, i1; + fix di; + fix pixels, pixels_0, pixels_1; + + /* transfer back to x,y -- stolen from old gen_slin */ + + x0 = v0->x; y0 = v0->y; i0 = v0->i; + x1 = v1->x; y1 = v1->y; i1 = v1->i; + + xb0 = x0; xb1 = x1; yb0 = y0; yb1 = y1; + + pixels = lg_max(fix_abs(y1-y0),fix_abs(x1-x0)); + + if (pixels != 0) di = fix_div(i1-i0,pixels); + + r = gr_clip_fix_line (&x0, &y0, &x1, &y1); + + if (r != CLIP_ALL) { + + // If x,y changed and we need to clip intensities, + + if ((i0 != i1) && + ((x0 != xb0) || (y0 != yb0) || (x1 != xb1) || (y1 != yb1))) { + + pixels_0 = lg_max(fix_abs(yb0-y0),fix_abs(xb0-x0)); // # pixels lost + pixels_1 = lg_max(fix_abs(yb1-y1),fix_abs(xb1-x1)); // for endpoints + + i0 += fix_mul(di, pixels_0); + i1 -= fix_mul(di, pixels_1); + + } + } + + /* and transfer back to v0, v1 */ + (v0->x) = x0; (v0->y) = y0; (v0->i) = i0; + (v1->x) = x1; (v1->y) = y1; (v1->i) = i1; + + return r; +} + diff --git a/engine/src/Libraries/2D/Source/Flat8/FL8OPL.c b/engine/src/Libraries/2D/Source/Flat8/FL8OPL.c new file mode 100644 index 0000000..0d673ad --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/FL8OPL.c @@ -0,0 +1,282 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8opl.c $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/08/16 12:57:20 $ + * + * full perspective texture mapper. + * scanline processors. + * + */ + +#include "cnvdat.h" +#include "pertyp.h" +#include "plytyp.h" + +// prototypes +void gri_opaque_per_umap_hscan_scanline(grs_per_info *pi, grs_bitmap *bm); +void gri_opaque_per_umap_vscan_scanline(grs_per_info *pi, grs_bitmap *bm); +void gri_opaque_per_umap_hscan_init(grs_bitmap *bm, grs_per_setup *ps); +void gri_opaque_per_umap_vscan_init(grs_bitmap *bm, grs_per_setup *ps); + +void gri_opaque_per_umap_hscan_scanline(grs_per_info *pi, grs_bitmap *bm) { + + // make SURE these come out in registers + register int x, k, y_cint; + register uchar *p; + register int gr_row, l_u_mask, l_v_mask, l_v_shift; + register fix l_du, l_dv, l_y_fix, l_scan_slope, test, l_u, l_v; + register uchar *bm_bits; + register fix l_dl, l_dt; + + int l_x; + + gr_row = grd_bm.row; + bm_bits = bm->bits; + l_u = pi->u; + l_v = pi->v; + l_scan_slope = pi->scan_slope; + l_v_shift = pi->v_shift; + l_v_mask = pi->v_mask; + l_u_mask = pi->u_mask; + l_du = pi->du; + l_dv = pi->dv; + l_y_fix = pi->y_fix; + l_x = pi->x; + + l_y_fix = l_x * l_scan_slope + fix_make(pi->yp, 0xffff); + l_u = pi->u0 + fix_div(pi->unum, pi->denom); + l_v = pi->v0 + fix_div(pi->vnum, pi->denom); + l_du = fix_div(pi->dunum, pi->denom); + l_dv = fix_div(pi->dvnum, pi->denom); + l_u += l_x * l_du; + l_v += l_x * l_dv; + + y_cint = fix_int(l_y_fix); + if (l_scan_slope < 0) + gr_row = -gr_row; + p = grd_bm.bits + l_x + (y_cint * grd_bm.row); + if (l_x < pi->xl) { + l_dl = pi->dyl; + l_dt = pi->dtl; + test = l_x * pi->dyl - y_cint * pi->dxl + pi->cl; + x = pi->xl - l_x; + l_x = pi->xl; + for (; x > 0; x--) { + if (test <= 0) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + *p = bm_bits[k]; // gr_fill_upixel(bm_bits[k],l_x,y_cint); + } + k = y_cint; + y_cint = fix_int(l_y_fix += l_scan_slope); + + if (k != y_cint) { + p += gr_row; + test += l_dt; + } else + test += l_dl; + + p++; + l_u += l_du; + l_v += l_dv; + } + } + + if (l_x < pi->xr0) { + x = pi->xr0 - l_x; + l_x = pi->xr0; + for (; x > 0; x--) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + *(p++) = bm_bits[k]; // gr_fill_upixel(bm_bits[k],l_x,y_cint); + k = y_cint; + y_cint = fix_int(l_y_fix += l_scan_slope); + + if (k != y_cint) + p += gr_row; + + l_u += l_du; + l_v += l_dv; + } + } + + if (l_x < pi->xr) { + l_dl = pi->dyr; + test = l_x * l_dl - y_cint * pi->dxr + pi->cr; + p = grd_bm.bits + l_x + y_cint * grd_bm.row; + x = pi->xr - l_x; + l_dt = pi->dtr; + l_x = pi->xr; + for (; x > 0; x--) { + if (test >= 0) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + *p = bm_bits[k]; // gr_fill_upixel(bm_bits[k],l_x,y_cint); + } + k = y_cint; + y_cint = fix_int(l_y_fix += l_scan_slope); + + if (k != y_cint) { + p += gr_row; + test += l_dt; + } else + test += l_dl; + + p++; + l_u += l_du; + l_v += l_dv; + } + } + + pi->y_fix = l_y_fix; + pi->x = l_x; + pi->u = l_u; + pi->v = l_v; + pi->du = l_du; + pi->dv = l_dv; +} + +void gri_opaque_per_umap_vscan_scanline(grs_per_info *pi, grs_bitmap *bm) { + int x_cint; + + // locals used to speed PPC code + fix l_dxr, l_x_fix, l_u, l_v, l_du, l_dv, l_scan_slope, l_dtl, l_dxl, l_dyl, l_dtr, l_dyr; + int l_yl, l_yr0, l_yr, l_y, l_u_mask, l_v_mask, l_v_shift; + int gr_row, temp_x; + uchar *bm_bits; + uchar *p; + + gr_row = grd_bm.row; + bm_bits = bm->bits; + l_dxr = pi->dxr; + l_x_fix = pi->x_fix; + l_y = pi->y; + l_yr = pi->yr; + l_yr0 = pi->yr0; + l_yl = pi->yl; + l_dyr = pi->dyr; + l_dtr = pi->dtr; + l_dyl = pi->dyl; + l_dxl = pi->dxl; + l_dtl = pi->dtl; + l_scan_slope = pi->scan_slope; + l_v_shift = pi->v_shift; + l_v_mask = pi->v_mask; + l_u_mask = pi->u_mask; + l_u = pi->u; + l_v = pi->v; + l_du = pi->du; + l_dv = pi->dv; + + l_x_fix = l_y * l_scan_slope + fix_make(pi->xp, 0xffff); + + l_u = pi->u0 + fix_div(pi->unum, pi->denom); + l_v = pi->v0 + fix_div(pi->vnum, pi->denom); + l_du = fix_div(pi->dunum, pi->denom); + l_dv = fix_div(pi->dvnum, pi->denom); + l_u += l_y * l_du; + l_v += l_y * l_dv; + + x_cint = fix_int(l_x_fix); + p = grd_bm.bits + x_cint + l_y * gr_row; + if (l_y < l_yl) { + fix test = l_y * l_dxl - x_cint * l_dyl + pi->cl; + for (; l_y < l_yl; l_y++) { + if (test <= 0) { + int k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + *p = bm_bits[k]; // gr_fill_upixel(bm_bits[k],x_cint,l_y); + } + temp_x = x_cint; + x_cint = fix_int(l_x_fix += l_scan_slope); + + if (temp_x != x_cint) { + test += l_dtl; + p -= (temp_x - x_cint); + } else + test += l_dxl; + + p += gr_row; + l_u += l_du; + l_v += l_dv; + } + } + + for (; l_y < l_yr0; l_y++) { + int k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + *p = bm_bits[k]; // gr_fill_upixel(bm_bits[k],x_cint,l_y); + + temp_x = x_cint; + x_cint = fix_int(l_x_fix += l_scan_slope); + if (temp_x != x_cint) + p -= (temp_x - x_cint); + + p += gr_row; + l_u += l_du; + l_v += l_dv; + } + + if (l_y < l_yr) { + fix test = l_y * l_dxr - x_cint * l_dyr + pi->cr; + p = grd_bm.bits + x_cint + l_y * gr_row; + for (; l_y < l_yr; l_y++) { + if (test >= 0) { + int k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + *p = bm_bits[k]; // gr_fill_upixel(bm_bits[k],x_cint,l_y); + } + + temp_x = x_cint; + x_cint = fix_int(l_x_fix += l_scan_slope); + if (temp_x != x_cint) { + test += l_dtr; + p -= (temp_x - x_cint); + } else + test += l_dxr; + + p += gr_row; + l_u += l_du; + l_v += l_dv; + } + } + + pi->x_fix = l_x_fix; + pi->y = l_y; + pi->u = l_u; + pi->v = l_v; + pi->du = l_du; + pi->dv = l_dv; +} + +extern void gri_per_umap_hscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps); +extern void gri_per_umap_vscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps); + +void gri_opaque_per_umap_hscan_init(grs_bitmap *bm, grs_per_setup *ps) { + ps->shell_func = (void (*)())gri_per_umap_hscan; + ps->scanline_func = (void (*)())gri_opaque_per_umap_hscan_scanline; +} + +void gri_opaque_per_umap_vscan_init(grs_bitmap *bm, grs_per_setup *ps) { + ps->shell_func = (void (*)())gri_per_umap_vscan; + ps->scanline_func = (void (*)())gri_opaque_per_umap_vscan_scanline; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/Fl8F.c b/engine/src/Libraries/2D/Source/Flat8/Fl8F.c new file mode 100644 index 0000000..04b35f1 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/Fl8F.c @@ -0,0 +1,280 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/FL8F.c $ + * $Revision: 1.4 $ + * $Author: kevin $ + * $Date: 1994/08/16 12:50:13 $ + * + * Routines to floor texture map a flat8 bitmap to a generic canvas. + * + * This file is part of the 2d library. + * + */ + +#include "cnvdat.h" +#include "fl8tf.h" +#include "fl8tmapdv.h" +#include "gente.h" +#include "poly.h" +#include "tmapint.h" +#include "vtab.h" + +int gri_floor_umap_loop(grs_tmap_loop_info *tli); + +int gri_floor_umap_loop(grs_tmap_loop_info *tli) { + fix u, v, du, dv, dx, d; + + // locals used to store copies of tli-> stuff, so its in registers on the PPC + uchar t_wlog; + uint32_t t_mask; + int x, k; + uchar *t_bits; + uchar *p_dest; + fix inv; + uchar *t_clut; + uchar temp_pix; + int32_t *t_vtab; + +#if InvDiv + inv = fix_div(fix_make(1, 0), tli->w); + u = fix_mul_asm_safe(tli->left.u, inv); + du = fix_mul_asm_safe(tli->right.u, inv) - u; + v = fix_mul_asm_safe(tli->left.v, inv); + dv = fix_mul_asm_safe(tli->right.v, inv) - v; +#else + u = fix_div(tli->left.u, tli->w); + du = fix_div(tli->right.u, tli->w) - u; + v = fix_div(tli->left.v, tli->w); + dv = fix_div(tli->right.v, tli->w) - v; +#endif + + dx = tli->right.x - tli->left.x; + + t_clut = tli->clut; + t_mask = tli->mask; + t_wlog = tli->bm.wlog; + t_vtab = tli->vtab; + t_bits = tli->bm.bits; + + // handle PowerPC loop + do { + if ((d = fix_ceil(tli->right.x) - fix_ceil(tli->left.x)) > 0) { + d = fix_ceil(tli->left.x) - tli->left.x; + +#if InvDiv + inv = fix_div(fix_make(1, 0), dx); + du = fix_mul_asm_safe(du, inv); + dv = fix_mul_asm_safe(dv, inv); +#else + du = fix_div(du, dx); + dv = fix_div(dv, dx); +#endif + + u += fix_mul(du, d); + v += fix_mul(dv, d); + + // copy out tli-> stuff into locals + p_dest = grd_bm.bits + (grd_bm.row * tli->y) + fix_cint(tli->left.x); + x = fix_cint(tli->right.x) - fix_cint(tli->left.x); + + switch (tli->bm.hlog) { + case GRL_OPAQUE: + for (; x > 0; x--) { + k = t_vtab[fix_fint(v)] + fix_fint(u); + *(p_dest++) = t_bits[k]; // gr_fill_upixel(t_bits[k],x,t_y); + u += du; + v += dv; + } + break; + case GRL_TRANS: + for (; x > 0; x--) { + k = t_vtab[fix_fint(v)] + fix_fint(u); + temp_pix = t_bits[k]; + if (temp_pix != 0) + *p_dest = temp_pix; + // gr_fill_upixel(t_bits[k],x,t_y); + p_dest++; + u += du; + v += dv; + } + break; + case GRL_OPAQUE | GRL_LOG2: + for (; x > 0; x--) { + k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + *(p_dest++) = t_bits[k]; + // gr_fill_upixel(t_bits[k],x,t_y); + u += du; + v += dv; + } + break; + case GRL_TRANS | GRL_LOG2: + for (; x > 0; x--) { + k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + temp_pix = t_bits[k]; + if (temp_pix != 0) + *p_dest = temp_pix; + // gr_fill_upixel(t_bits[k],x,t_y); + p_dest++; + u += du; + v += dv; + } + break; + case GRL_OPAQUE | GRL_CLUT: + for (; x > 0; x--) { + k = t_vtab[fix_fint(v)] + fix_fint(u); + *(p_dest++) = t_clut[t_bits[k]]; + // gr_fill_upixel(tli->clut[t_bits[k]],x,t_y); + u += du; + v += dv; + } + break; + case GRL_TRANS | GRL_CLUT: + for (; x > 0; x--) { + k = t_vtab[fix_fint(v)] + fix_fint(u); + k = t_bits[k]; + if (k != 0) + *p_dest = t_clut[k]; + // gr_fill_upixel(tli->clut[k],x,t_y); + p_dest++; + u += du; + v += dv; + } + break; + case GRL_OPAQUE | GRL_LOG2 | GRL_CLUT: + while (((intptr_t)p_dest & 3) != 0) { + k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + *(p_dest++) = t_clut[t_bits[k]]; + // gr_fill_upixel(tli->clut[t_bits[k]],x,t_y); + u += du; + v += dv; + x--; + } + + while (x > 0) { + k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + inv = t_clut[t_bits[k]]; + // gr_fill_upixel(tli->clut[t_bits[k]],x,t_y); + u += du; + v += dv; + *p_dest = inv; + p_dest++; + x--; + } + + for (; x > 0; x--) { + k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + *(p_dest++) = t_clut[t_bits[k]]; + // gr_fill_upixel(tli->clut[t_bits[k]],x,t_y); + u += du; + v += dv; + } + break; + case GRL_TRANS | GRL_LOG2 | GRL_CLUT: + for (; x > 0; x--) { + k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + k = t_bits[k]; + if (k != 0) + *p_dest = t_clut[k]; // gr_fill_upixel(tli->clut[k],x,t_y); + p_dest++; + u += du; + v += dv; + } + } + } else if (d < 0) + return TRUE; /* punt this tmap */ + + tli->w += tli->dw; + +#if InvDiv + inv = fix_div(fix_make(1, 0), tli->w); + u = fix_mul_asm_safe((tli->left.u += tli->left.du), inv); + tli->right.u += tli->right.du; + du = fix_mul_asm_safe(tli->right.u, inv) - u; + v = fix_mul_asm_safe((tli->left.v += tli->left.dv), inv); + tli->right.v += tli->right.dv; + dv = fix_mul_asm_safe(tli->right.v, inv) - v; +#else + u = fix_div((tli->left.u += tli->left.du), tli->w); + tli->right.u += tli->right.du; + du = fix_div(tli->right.u, tli->w) - u; + v = fix_div((tli->left.v += tli->left.dv), tli->w); + tli->right.v += tli->right.dv; + dv = fix_div(tli->right.v, tli->w) - v; +#endif + + tli->left.x += tli->left.dx; + tli->right.x += tli->right.dx; + dx = tli->right.x - tli->left.x; + tli->y++; + } while (--(tli->n) > 0); + return FALSE; /* tmap OK */ +} + +void gri_trans_floor_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_TRANS | GRL_LOG2; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_TRANS; + } + tli->loop_func = (void (*)())gri_floor_umap_loop; + tli->left_edge_func = (void (*)())gri_uvwx_edge; + tli->right_edge_func = (void (*)())gri_uvwx_edge; +} + +void gri_opaque_floor_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_OPAQUE | GRL_LOG2; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_OPAQUE; + } + tli->loop_func = (void (*)())gri_floor_umap_loop; + tli->left_edge_func = (void (*)())gri_uvwx_edge; + tli->right_edge_func = (void (*)())gri_uvwx_edge; +} + +void gri_trans_clut_floor_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_TRANS | GRL_LOG2 | GRL_CLUT; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_TRANS | GRL_CLUT; + } + tli->loop_func = (void (*)())gri_floor_umap_loop; + tli->left_edge_func = (void (*)())gri_uvwx_edge; + tli->right_edge_func = (void (*)())gri_uvwx_edge; +} + +void gri_opaque_clut_floor_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_OPAQUE | GRL_LOG2 | GRL_CLUT; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_OPAQUE | GRL_CLUT; + } + tli->loop_func = (void (*)())gri_floor_umap_loop; + tli->left_edge_func = (void (*)())gri_uvwx_edge; + tli->right_edge_func = (void (*)())gri_uvwx_edge; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8bl.c b/engine/src/Libraries/2D/Source/Flat8/fl8bl.c new file mode 100644 index 0000000..1d4554f --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8bl.c @@ -0,0 +1,60 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8bl.c $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/09/08 01:12:51 $ + * + * Generic routines for texture mapping rsd bitmaps. + * + * This file is part of the 2d library. + * + */ + +#include "bitmap.h" +#include "grs.h" +#include "ifcn.h" +#include "lg.h" +#include "rsdunpck.h" +#include "tmapint.h" +#include "tmaptab.h" + +extern void flat8_flat8_smooth_hv_double_ubitmap(grs_bitmap *src, grs_bitmap *dst); +void gri_trans_blend_clut_lin_umap_init(grs_tmap_loop_info *ti); + +void gri_trans_blend_clut_lin_umap_init(grs_tmap_loop_info *ti) { + if (grd_unpack_buf != NULL) { + grs_bitmap tbm; + + tbm.row = 2 * ti->bm.w; + if (ti->bm.bits == grd_unpack_buf) + tbm.bits = ti->bm.bits + ti->bm.row * ti->bm.h; + else + tbm.bits = grd_unpack_buf; + + flat8_flat8_smooth_hv_double_ubitmap(&(ti->bm), &tbm); + ti->n = BMT_FLAT8 * GRD_FUNCS + GRC_TRANS_CLUT_BILIN; + ti->bm.bits = tbm.bits; + ti->bm.row = tbm.row; + ti->bm.w *= 2; + ti->bm.h *= 2; + ((void (*)(grs_tmap_loop_info *))(grd_tmap_init_table[ti->n]))(ti); + } +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8bldbl.c b/engine/src/Libraries/2D/Source/Flat8/fl8bldbl.c new file mode 100644 index 0000000..02495dc --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8bldbl.c @@ -0,0 +1,265 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8bldbl.c $ + * $Revision: 1.3 $ + * $Author: kevin $ + * $Date: 1994/12/01 14:59:38 $ + * + * $Log: fl8bldbl.c $ + * Revision 1.3 1994/12/01 14:59:38 kevin + * Added sub/bitmap blending routines. + * + * Revision 1.2 1994/09/08 00:01:07 kevin + * removed smooth_hv_doubler (replaced by asm version). + * + * Revision 1.1 1994/03/14 17:51:09 kevin + * Initial revision + * + */ + +#include "blncon.h" +#include "blndat.h" +#include "blnfcn.h" +#include "cnvdat.h" +#include "grs.h" +#include "lg.h" +#include + +void flat8_flat8_v_double_ubitmap(grs_bitmap *bm) { + int i, j, bpv, row, b_row; /* loop controls, bottom pixel value */ + uchar *src = bm->bits, *dst = grd_bm.bits, *src_nxt, *dst_nxt; + int dst_skip = grd_bm.row - bm->w, src_skip = bm->row - bm->w; + uchar *local_grd_half_blend; + + local_grd_half_blend = grd_half_blend; + row = grd_bm.row; + b_row = bm->row; + + LG_memcpy(dst, src, bm->w); /* first copy the top line */ + for (i = 0; i < bm->h - 1; i++) /* for each row source, 2 destination */ + { + src_nxt = src + b_row; /* next line of source */ + dst += row; /* interpolated row */ + dst_nxt = dst + row; /* next clone row */ + for (j = 0; j < bm->w; j++) /* all pixels in vertical clone */ + { + *dst++ = local_grd_half_blend[((bpv = *src_nxt++) << 8) | (*src++)]; + *dst_nxt++ = bpv; /* is this faster than another memcpy? in asm probably? */ + } + dst += dst_skip; + src += src_skip; /* get to the next line */ + } +#ifdef FULL_FILL + memset(dst + row, 0, bm->w); +#endif +} + +#define QSB_SIZE 4 +#define LOG_QSB_SIZE 2 +#define D_ROW 8 * QSB_SIZE + +uchar *grd_last_sub_bm; +uchar grd_sub_bm_buffer[D_ROW * D_ROW]; + +void gri_flat8_hv_quadruple_sub_bitmap(grs_bitmap *src_bm, grs_bitmap *dst_bm, int u, int v); +void gri_flat8_hv_quadruple_sub_bitmap(grs_bitmap *src_bm, grs_bitmap *dst_bm, int u, int v) { + int i, j, full_h_blend; + uchar *src, *dst; + + if (grd_log_blend_levels != 2) { + gr_free_blend(); + gr_init_blend(2); + } + + /* initialize destination bitmap parameters. */ + dst_bm->bits = grd_sub_bm_buffer; + dst_bm->h = D_ROW; + dst_bm->row = dst_bm->w = D_ROW; + dst_bm->hlog = dst_bm->wlog = LOG_QSB_SIZE + 3; + + /* get pointer to sub bitmap bits */ + src = src_bm->bits + u + src_bm->row * v; + + /* If we just did this bitmap, no need to do it again! */ + if (src == grd_last_sub_bm) + return; + grd_last_sub_bm = src; + + /* Fill in the middle of the destination bitmap */ + dst = dst_bm->bits + (D_ROW + 1) * (D_ROW / 4); + + if (v + QSB_SIZE < src_bm->h) + full_h_blend = 1; + else + full_h_blend = 0; + + /* First horizontally blend the source bitmap into every fourth destination + * bitmap row. */ + for (i = 0; i < QSB_SIZE + full_h_blend; i++) { + for (j = 0; j < QSB_SIZE; j++) { + if (u + j + 1 >= src_bm->w) { + /* if we're at the right edge of the source bitmap, just copy the right + * pixel.*/ + dst[0] = dst[1] = dst[2] = dst[3] = src[j]; + dst += 4; + } else { + int k = (src[j + 1]) | (src[j] << 8); + dst[0] = src[j]; + dst[1] = grd_blend[k]; + dst[2] = grd_blend[k + GR_BLEND_TABLE_SIZE]; + dst[3] = grd_blend[k + 2 * GR_BLEND_TABLE_SIZE]; + dst += 4; + } + } + dst += 4 * (D_ROW - QSB_SIZE); + src += src_bm->row; + } + + /* Now verticaly blend the destination colums. */ + dst = dst_bm->bits + (D_ROW + 1) * (D_ROW / 4); + for (i = 0; i < QSB_SIZE + full_h_blend - 1; i++) { + for (j = 0; j < 4 * QSB_SIZE; j++) { + int k = (dst[j + 4 * D_ROW]) | (dst[j] << 8); + dst[j + D_ROW] = grd_blend[k]; + dst[j + 2 * D_ROW] = grd_blend[k + GR_BLEND_TABLE_SIZE]; + dst[j + 3 * D_ROW] = grd_blend[k + 2 * GR_BLEND_TABLE_SIZE]; + } + dst += 4 * D_ROW; + } + + /* if we're at the bottom edge of the source bitmap, just copy the bottom row + * 3 times. */ + if (full_h_blend == 0) { + LG_memcpy(dst + D_ROW, dst, 4 * QSB_SIZE); + LG_memcpy(dst + 2 * D_ROW, dst, 4 * QSB_SIZE); + LG_memcpy(dst + 3 * D_ROW, dst, 4 * QSB_SIZE); + } + + /* copy the top row to fill out the top of the dest. */ + dst = dst_bm->bits + (D_ROW + 1) * (D_ROW / 4); + for (i = 0; i < D_ROW / 4; i++) { + LG_memcpy(dst - D_ROW, dst, 4 * QSB_SIZE); + dst -= D_ROW; + } + /* copy the bottom row to fill out the bottom of the dest. */ + dst = dst_bm->bits + (3 * D_ROW + 1) * (D_ROW / 4); + for (i = 0; i < D_ROW / 4; i++) { + LG_memcpy(dst, dst - D_ROW, 4 * QSB_SIZE); + dst += D_ROW; + } + /* copy the right and left colums to fill out the right and left edges. */ + dst = dst_bm->bits; + for (i = 0; i < D_ROW; i++) { + memset(dst, dst[D_ROW / 4], D_ROW / 4); + memset(dst + 3 * D_ROW / 4, dst[(3 * D_ROW / 4) - 1], D_ROW / 4); + dst += D_ROW; + } +} + +#define DSB_SIZE 8 +#define LOG_DSB_SIZE 3 + +void gri_flat8_hv_double_sub_bitmap(grs_bitmap *src_bm, grs_bitmap *dst_bm, int u, int v); +void gri_flat8_hv_double_sub_bitmap(grs_bitmap *src_bm, grs_bitmap *dst_bm, int u, int v) { + int i, j, full_h_blend; + uchar *src, *dst; + + if (grd_log_blend_levels != 2) { + gr_free_blend(); + gr_init_blend(2); + } + + /* initialize destination bitmap parameters. */ + dst_bm->bits = grd_sub_bm_buffer; + dst_bm->h = D_ROW; + dst_bm->row = dst_bm->w = D_ROW; + dst_bm->hlog = dst_bm->wlog = LOG_DSB_SIZE + 2; + + /* get pointer to sub bitmap bits */ + src = src_bm->bits + u + src_bm->row * v; + + /* If we just did this bitmap, no need to do it again! */ + if (src == grd_last_sub_bm) + return; + grd_last_sub_bm = src; + + /* Fill in the middle of the destination bitmap */ + dst = dst_bm->bits + (D_ROW + 1) * (D_ROW / 4); + + if (v + DSB_SIZE < src_bm->h) + full_h_blend = 1; + else + full_h_blend = 0; + + /* First horizontally blend the source bitmap into every other destination + * bitmap row. */ + for (i = 0; i < DSB_SIZE + full_h_blend; i++) { + for (j = 0; j < DSB_SIZE; j++) { + if (u + j + 1 >= src_bm->w) { + /* if we're at the right edge of the source bitmap, just copy the right + * pixel.*/ + dst[0] = dst[1] = src[j]; + dst += 2; + } else { + int k = (src[j + 1]) | (src[j] << 8); + dst[0] = src[j]; + dst[1] = grd_half_blend[k]; + dst += 2; + } + } + dst += 2 * (D_ROW - DSB_SIZE); + src += src_bm->row; + } + + /* Now verticaly blend the destination colums. */ + dst = dst_bm->bits + (D_ROW + 1) * (D_ROW / 4); + for (i = 0; i < DSB_SIZE + full_h_blend - 1; i++) { + for (j = 0; j < 2 * DSB_SIZE; j++) { + int k = (dst[j + 2 * D_ROW]) | (dst[j] << 8); + dst[j + D_ROW] = grd_half_blend[k]; + } + dst += 2 * D_ROW; + } + + /* if we're at the bottom edge of the source bitmap, just copy the bottom row + * 3 times. */ + if (full_h_blend == 0) + LG_memcpy(dst + D_ROW, dst, 4 * QSB_SIZE); + + // all this is unnecessary if we're just doing linear maps. + // /* copy the top row to fill out the top of the dest. */ + // dst=dst_bm->bits+(D_ROW+1)*(D_ROW/4); + // for (i=0;ibits+(3*D_ROW+1)*(D_ROW/4); + // for (i=0;ibits; for (i=0;i. + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/fl8chfl8.c $ + * $Revision: 1.4 $ + * $Author: kevin $ + * $Date: 1993/10/26 02:23:49 $ + * $Log: fl8chfl8.c $ + * Revision 1.4 1993/10/26 02:23:49 kevin + * Use default clut if passed cl=NULL. + * + * Revision 1.3 1993/10/19 09:50:17 kaboom + * Replaced #include h; + src = bm->bits; + dst = grd_bm.bits + y * grd_bm.row + x + bm->w - 1; + while (h--) { + w = bm->w; + while (w--) + *dst-- = cl[*src++]; + src += bm->row - bm->w; + dst += grd_bm.row + bm->w; + } +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8clear.c b/engine/src/Libraries/2D/Source/Flat8/fl8clear.c new file mode 100644 index 0000000..cd23536 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8clear.c @@ -0,0 +1,110 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/fl8clear.c $ + * $Revision: 1.3 $ + * $Author: kaboom $ + * $Date: 1993/10/19 09:50:18 $ + * + * Routines for clearing a flat 8 canvas. + * + * This file is part of the 2d library. + * + * $Log: fl8clear.c $ + * Revision 1.3 1993/10/19 09:50:18 kaboom + * Replaced #include "grd.h" with new headers split from grd.h. + * + * Revision 1.2 1993/10/08 01:15:08 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.1 1993/02/16 14:14:00 kaboom + * Initial revision + */ + +#include "cnvdat.h" +#include "lg.h" +#include + +/* clear a flat8 canvas. */ +void flat8_clear(long color) { + + uchar *p; + int h; + int w; + int row; + ushort short_val; + double double_stack, doub_vl; + uint firstbytes, middoubles, lastbytes, fb, md, lb; + uchar *dst; + double *dst_doub; + uint temp; + + color &= 0x00ff; + p = grd_bm.bits; + h = grd_bm.h; + w = grd_bm.w; + row = grd_bm.row; + + if (w >= 16) // only do doubles if at least two of them (16 bytes) + { + // get a 64 bit version of color in doub_vl + short_val = (uchar)color | color << 8; + color = (int)short_val | ((int)short_val) << 16; + *(int *)(&double_stack) = color; + *((int *)(&double_stack) + 1) = color; + doub_vl = double_stack; + + lastbytes = w; + firstbytes = (intptr_t)p & 3; + if (firstbytes != 0) // check for boundary problems + lastbytes -= firstbytes; + + middoubles = lastbytes >> 3; + lastbytes -= middoubles << 3; + } else { + lastbytes = w; + middoubles = 0; + } + + fb = firstbytes, md = middoubles, lb = lastbytes; + while (h--) { + // MLA - inlined this code + memset(p, color, w); + /*{ + firstbytes = fb,middoubles = md,lastbytes = lb; + dst = p; + + if (middoubles) + { + // first get to a 4 byte boundary + while (firstbytes--) *(dst++) = color; + dst_doub = (double *) dst; + + // now do doubles + while (middoubles--) *(dst_doub++) = doub_vl; + dst = (uchar *) dst_doub; + } + + // do remaining bytes + while (lastbytes--) *(dst++) = color; + }*/ + + p += row; + } +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8clin.c b/engine/src/Libraries/2D/Source/Flat8/fl8clin.c new file mode 100644 index 0000000..38fc8f1 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8clin.c @@ -0,0 +1,105 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8clin.c $ + * $Revision: 1.7 $ + * $Author: kevin $ + * $Date: 1994/10/17 14:59:57 $ + * + * Routine to draw an rgb shaded line to a flat 8 canvas. + * + * This file is part of the 2d library. + * + * $Log: fl8clin.c $ + * Revision 1.7 1994/10/17 14:59:57 kevin + * Use palette macros in preparation for switch to palette globals. + * + * Revision 1.6 1994/06/11 01:24:08 lmfeeney + * guts of the routine moved to fl8{c,s}lin.h, per fill type + * line drawers are created by defining macros and including + * this file + * + * Revision 1.5 1994/05/06 18:18:38 lmfeeney + * rewritten for greater accuracy and speed + * + * Revision 1.4 1994/05/01 05:34:38 lmfeeney + * rewritten using simple dda algorithm (+ bit twiddle hack) for greater + * speed (20/30%) and improvement for e.g. diagonal lines + * + * Revision 1.3 1993/10/19 09:50:19 kaboom + * Replaced #include + +// MLA #pragma off (unreferenced) + +#define fix_make_nof(x) fix_make(x, 0x0000) +#define macro_get_ipal(r, g, b) (long)((r >> 19) & 0x1f) | ((g >> 14) & 0x3e0) | ((b >> 9) & 0x7c00) + +#undef macro_plot_rgb +#define macro_plot_rgb(x, p, i) \ + do { \ + p[x] = grd_ipal[i]; \ + } while (0) + +void gri_flat8_ucline_norm(long c, long parm, grs_vertex *v0, grs_vertex *v1) { +#include "fl8clin.h" +} + +#undef macro_plot_rgb +#define macro_plot_rgb(x, p, i) \ + do { \ + p[x] = (long)(((uchar *)parm)[(grd_ipal[i])]); \ + } while (0) + +void gri_flat8_ucline_clut(long c, long parm, grs_vertex *v0, grs_vertex *v1) { +#include "fl8clin.h" +} + +#undef macro_plot_rgb +#define macro_plot_rgb(x, p, i) \ + do { \ + p[x] = p[x] ^ (grd_ipal[i]); \ + } while (0) + +void gri_flat8_ucline_xor(long c, long parm, grs_vertex *v0, grs_vertex *v1) { +#include "fl8clin.h" +} + +/* punt */ +#undef macro_plot_rgb +#define macro_plot_rgb(x, p, i) \ + do { \ + p[x] = (long)(((uchar *)parm)[(grd_ipal[i])]); \ + } while (0) + +void gri_flat8_ucline_blend(long c, long parm, grs_vertex *v0, grs_vertex *v1) { +#include "fl8clin.h" +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8clin.h b/engine/src/Libraries/2D/Source/Flat8/fl8clin.h new file mode 100644 index 0000000..735a48b --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8clin.h @@ -0,0 +1,322 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/fl8clin.h $ + * $Revision: 1.1 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 00:49:17 $ + */ + +/* This file is an UNCOMPILABLE code fragment */ + +/* Draw a gouraud-shaded line as specified by endpoint rgb value... + weird precision bugs abound due to precision errors + + 5/94 These have been (completely?) corrected. See test/clintest.c + + See also note.txt for argument for correctness and precision safety + of current algorithm. +*/ + +/* NB: directionality policy -- reversible + Lines are drawn in order of increasing x or increasing y, + for lines that have greater x or y extent, repsectively. + This is done for all lines, including horiz., vert., and + 45' lines (increasing x). +*/ + +/* NB: endpoint policy -- inclusive + + The left and top endpoints are inclusive, i.e. 'trunc'-ed. The + right and bottom endpoints exclude the ceiling. This is + calculated by subtracting epsilon (i.e. 1/65536) from its + fixed-point representation, then trunc'ing. + + This makes sense if you note that open interval on right + < ceil (x) + is the same as + <= trunc (x - e) + + This means that it might not be necessary to go through the ugliness + of swapping endpoints, but I'm not convinced that there aren't + precision problems involved. Since wire-poly's overdraw, it's + really necessary to ensure that the same points are always drawn. +*/ + +fix x0, y0, x1, y1; +fix dx, dy; /* deltas in x and y */ +fix t; /* tmp */ + +uchar r0, g0, b0, r1, g1, b1; /* rgb values of endpt colors */ +fix r, g, b; /* current intensities */ +fix dr, dg, db; /* deltas for each of rgb */ +long i; /* color index */ + +uchar *p; /* ptr into canvas */ + +x0 = v0->x; +y0 = v0->y; +x1 = v1->x; +y1 = v1->y; + +r0 = (uchar)(v0->u); +g0 = (uchar)(v0->v); +b0 = (uchar)(v0->w); +r1 = (uchar)(v1->u); +g1 = (uchar)(v1->v); +b1 = (uchar)(v1->w); + +/* set endpoints + note that this cannot go negative or change octant, since the == + case is excluded */ + +if (x0 < x1) { + x1 -= 1; /* e.g. - epsilon */ +} else if (x0 > x1) { + x0 -= 1; +} + +if (y0 < y1) { + y1 -= 1; +} else if (y0 > y1) { + y0 -= 1; +} + +dx = fix_trunc(x1) - fix_trunc(x0); /* x extent in pixels, (macro is flakey) */ +dx = fix_abs(dx); +dy = fix_trunc(y1) - fix_trunc(y0); /* y extent in pixels */ +dy = fix_abs(dy); + +if (dx == 0 && dy == 0) + return; + +/* three cases: absolute value dx < = > dy + + along the longer dimension, the fixpoint x0 (or y0) is treated + as an int + + the points are swapped if needed and the rgb initial and deltas + are calculated accordingly + + there are two or three sub-cases - a horizontal or vertical line, + and the dx or dy being added or subtracted. dx and dy + are kept as absolute values and +/- is managed in + two separate inner loops if it is a y change, since you need to + manage the canvas pointer + + if y is being changed by 'dy' and x is being incremented, do a + FunkyBitCheck (TM) to see whether the integer part of y has changed + and if it has, resetting the the canvas pointer to the next row + + if x is being changed by 'dx' and y is being incremented, just + add or subtract row to increment y in the canvas + + the endpoints are walked inclusively in all cases, see above + + 45' degree lines are explicitly special cased -- because it + all runs as integers, but it's probably not frequent enough + to justify the check + + */ + +if (dx > dy) { + + x0 = fix_int(x0); + x1 = fix_int(x1); + + if (x0 < x1) { + r = fix_make(r0, 0); + g = fix_make(g0, 0); + b = fix_make(b0, 0); + dr = fix_div(fix_make_nof(r1 - r0), dx); + dg = fix_div(fix_make_nof(g1 - g0), dx); + db = fix_div(fix_make_nof(b1 - b0), dx); + + p = grd_bm.bits + grd_bm.row * (fix_int(y0)); /* set canvas ptr */ + + } else { + t = x0; + x0 = x1; + x1 = t; + t = y0; + y0 = y1; + y1 = t; + + r = fix_make(r1, 0); + g = fix_make(g1, 0); + b = fix_make(b1, 0); + dr = fix_div(fix_make_nof(r0 - r1), dx); + dg = fix_div(fix_make_nof(g0 - g1), dx); + db = fix_div(fix_make_nof(b0 - b1), dx); + + p = grd_bm.bits + grd_bm.row * (fix_int(y0)); + } + + if ((fix_int(y0)) == (fix_int(y1))) { + while (x0 <= x1) { + i = macro_get_ipal(r, g, b); + macro_plot_rgb(x0, p, i); + x0++; + r += dr; + g += dg; + b += db; + } + } else if (y0 < y1) { + dy = fix_div((y1 - y0), dx); + while (x0 <= x1) { + i = macro_get_ipal(r, g, b); + macro_plot_rgb(x0, p, i); + x0++; + y0 += dy; + p += (grd_bm.row & (-(fix_frac(y0) < dy))); + r += dr; + g += dg; + b += db; + } + } else { + dy = fix_div((y0 - y1), dx); + while (x0 <= x1) { + i = macro_get_ipal(r, g, b); + macro_plot_rgb(x0, p, i); + x0++; + p -= (grd_bm.row & (-(fix_frac(y0) < dy))); + y0 -= dy; + r += dr; + g += dg; + b += db; + } + } +} + +else if (dy > dx) { + + y0 = fix_int(y0); + y1 = fix_int(y1); + + if (y0 < y1) { + r = fix_make(r0, 0); + g = fix_make(g0, 0); + b = fix_make(b0, 0); + dr = fix_div(fix_make_nof(r1 - r0), dy); + dg = fix_div(fix_make_nof(g1 - g0), dy); + db = fix_div(fix_make_nof(b1 - b0), dy); + + p = grd_bm.bits + grd_bm.row * y0; + + } else { + t = x0; + x0 = x1; + x1 = t; + t = y0; + y0 = y1; + y1 = t; + + r = fix_make(r1, 0); + g = fix_make(g1, 0); + b = fix_make(b1, 0); + dr = fix_div(fix_make_nof(r0 - r1), dy); + dg = fix_div(fix_make_nof(g0 - g1), dy); + db = fix_div(fix_make_nof(b0 - b1), dy); + + p = grd_bm.bits + grd_bm.row * y0; + } + + if ((fix_int(x0)) == (fix_int(x1))) { + x0 = fix_int(x0); + while (y0 <= y1) { + i = macro_get_ipal(r, g, b); + macro_plot_rgb(x0, p, i); + y0++; + p += grd_bm.row; + r += dr; + g += dg; + b += db; + } + } else { + dx = fix_div((x1 - x0), dy); + while (y0 <= y1) { + i = macro_get_ipal(r, g, b); + macro_plot_rgb(fix_fint(x0), p, i); + x0 += dx; + y0++; + p += grd_bm.row; + r += dr; + g += dg; + b += db; + } + } +} else { /* dy == dx, walk the x axis, all integers */ + + x0 = fix_int(x0); + x1 = fix_int(x1); + y0 = fix_int(y0); + y1 = fix_int(y1); + + if (x0 < x1) { + r = fix_make(r0, 0); + g = fix_make(g0, 0); + b = fix_make(b0, 0); + dr = fix_div(fix_make_nof(r1 - r0), dx); + dg = fix_div(fix_make_nof(g1 - g0), dx); + db = fix_div(fix_make_nof(b1 - b0), dx); + + p = grd_bm.bits + grd_bm.row * y0; /* set canvas ptr */ + + } else { + t = x0; + x0 = x1; + x1 = t; + t = y0; + y0 = y1; + y1 = t; + + r = fix_make(r1, 0); + g = fix_make(g1, 0); + b = fix_make(b1, 0); + dr = fix_div(fix_make_nof(r0 - r1), dx); + dg = fix_div(fix_make_nof(g0 - g1), dx); + db = fix_div(fix_make_nof(b0 - b1), dx); + + p = grd_bm.bits + grd_bm.row * y0; + } + + if (y0 < y1) { + while (y0 <= y1) { + i = macro_get_ipal(r, g, b); + macro_plot_rgb(x0, p, i); + x0++; + y0++; + p += grd_bm.row; + r += dr; + g += dg; + b += db; + } + } else { + while (y0 >= y1) { + i = macro_get_ipal(r, g, b); + macro_plot_rgb(x0, p, i); + x0++; + y0--; + p -= grd_bm.row; + r += dr; + g += dg; + b += db; + } + } +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8cnv.c b/engine/src/Libraries/2D/Source/Flat8/fl8cnv.c new file mode 100644 index 0000000..c6a63e8 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8cnv.c @@ -0,0 +1,397 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8cnv.c $ + * $Revision: 1.78 $ + * $Author: kevin $ + * $Date: 1994/11/12 02:21:59 $ + * + * General-purpose routines for drawing into straight-8 bitmaps. + * This file is part of the 2d library. + * + */ + +#include "flat8.h" +#include "general.h" +#include "grnull.h" +#include "icanvas.h" + +typedef void (*ptr_type)(); + +void (*flat8_canvas_table[GRD_CANVAS_FUNCS])() = { + (ptr_type)flat8_set_upixel, /* 8-bit pixel set/get */ + (ptr_type)gen_set_pixel, + (ptr_type)flat8_get_upixel, + (ptr_type)flat8_get_pixel, + + (ptr_type)flat8_set_upixel24, /* 24-bit pixel set/get */ + (ptr_type)flat8_set_pixel24, + (ptr_type)flat8_get_upixel24, + (ptr_type)flat8_get_pixel24, + + (ptr_type)flat8_clear, /* integral, straight primitives */ + (ptr_type)temp_upoint, + (ptr_type)temp_point, + (ptr_type)flat8_set_upixel, /* 8-bit pixel set/get */ + (ptr_type)gen_set_pixel_interrupt, + gr_null, + gr_null, + (ptr_type)gen_urect, + (ptr_type)gen_rect, + (ptr_type)gen_ubox, + (ptr_type)gen_box, + + gr_null, /* fixed-point rendering primitives */ + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + (ptr_type)temp_upoly, + (ptr_type)temp_poly, + (ptr_type)temp_uspoly, + (ptr_type)temp_spoly, + (ptr_type)temp_ucpoly, + (ptr_type)temp_cpoly, + (ptr_type)temp_utpoly, + (ptr_type)temp_tpoly, + (ptr_type)temp_ustpoly, + (ptr_type)temp_stpoly, + + (ptr_type)gen_vox_rect, + (ptr_type)gen_vox_poly, + (ptr_type)gen_vox_cpoly, + (ptr_type)flat8_interp2_ubitmap, + (ptr_type)flat8_filter2_ubitmap, + gr_not_imp, // (ptr_type) gen_roll_ubitmap, MLA - not used? + gr_not_imp, // (ptr_type) gen_roll_bitmap, MLA - not used? + + (ptr_type)temp_wall_umap, + gr_null, + (ptr_type)temp_lit_wall_umap, + gr_null, + (ptr_type)temp_clut_wall_umap, + gr_null, + + (ptr_type)temp_floor_umap, + gr_null, + (ptr_type)temp_lit_floor_umap, + gr_null, + (ptr_type)temp_clut_floor_umap, + gr_null, + + gr_null, /* linear texture mappers */ + gr_null, + gr_null, + gr_null, + (ptr_type)temp_lin_umap, + (ptr_type)temp_lin_map, + (ptr_type)temp_lin_umap, + (ptr_type)temp_lin_map, + (ptr_type)temp_lin_umap, + (ptr_type)temp_lin_map, + (ptr_type)temp_lin_umap, + (ptr_type)temp_lin_map, + + gr_null, /* lit linear texture mappers */ + gr_null, + gr_null, + gr_null, + (ptr_type)temp_lit_lin_umap, + (ptr_type)temp_lit_lin_map, + gr_null, + gr_null, + (ptr_type)temp_lit_lin_umap, + (ptr_type)temp_lit_lin_map, + gr_null, + gr_null, + + gr_null, /* clut linear texture mappers */ + gr_null, + gr_null, + gr_null, + (ptr_type)temp_clut_lin_umap, + (ptr_type)temp_clut_lin_map, + gr_null, + gr_null, + (ptr_type)temp_clut_lin_umap, + (ptr_type)temp_clut_lin_map, + (ptr_type)temp_clut_lin_umap, + (ptr_type)temp_clut_lin_map, + + gr_null, /* solid linear mapper */ + gr_null, + + gr_null, /* perspective texture mappers */ + gr_null, + gr_null, + gr_null, + (ptr_type)temp_per_umap, + (ptr_type)temp_per_map, + gr_null, + gr_null, + (ptr_type)temp_per_umap, + (ptr_type)temp_per_map, + gr_null, + gr_null, + + gr_null, /* lit perspective texture mappers */ + gr_null, + gr_null, + gr_null, + (ptr_type)temp_lit_per_umap, + gr_null, + gr_null, + gr_null, + (ptr_type)temp_lit_per_umap, + gr_null, + gr_null, + gr_null, + + gr_null, /* clut perspective texture mappers */ + gr_null, + gr_null, + gr_null, + (ptr_type)temp_clut_per_umap, + (ptr_type)temp_clut_per_map, + gr_null, + gr_null, + (ptr_type)temp_clut_per_umap, + (ptr_type)temp_clut_per_map, + gr_null, + gr_null, + + gr_null, /* solid perspective mapper */ + gr_null, + + (ptr_type)gen_int_ucircle, /* curves, should change to fixed-point */ + (ptr_type)gen_int_circle, + (ptr_type)gen_fix_ucircle, + (ptr_type)gen_fix_circle, + (ptr_type)gen_int_udisk, + (ptr_type)gen_int_disk, + (ptr_type)gen_fix_udisk, + (ptr_type)gen_fix_disk, + (ptr_type)gen_int_urod, + (ptr_type)gen_int_rod, + (ptr_type)gen_fix_urod, + (ptr_type)gen_fix_rod, + + // MLA - added these two for the device functions + (ptr_type)flat8_flat8_ubitmap, /* bitmap drawing functions. */ + (ptr_type)gen_flat8_bitmap, + (ptr_type)flat8_mono_ubitmap, + (ptr_type)gen_mono_bitmap, + (ptr_type)temp_flat8_ubitmap, + (ptr_type)gen_flat8_bitmap, + (ptr_type)gen_flat24_ubitmap, + (ptr_type)gen_flat24_bitmap, + (ptr_type)temp_rsd8_ubitmap, + (ptr_type)temp_rsd8_bitmap, + (ptr_type)temp_tluc8_ubitmap, + (ptr_type)gen_tluc8_bitmap, + + gr_null, /* bitmap drawing functions through a clut. */ + gr_null, + gr_null, + gr_null, + (ptr_type)temp_flat8_clut_ubitmap, + (ptr_type)gen_flat8_clut_bitmap, + gr_null, + gr_null, + (ptr_type)unpack_rsd8_clut_ubitmap, + (ptr_type)unpack_rsd8_clut_bitmap, + gr_null, + gr_null, + + gr_null, /* rsd8 solid bitmap functions. No longer used. */ + gr_null, + + gr_null, /* bitmap scale functions. */ + gr_null, + (ptr_type)flat8_mono_scale_ubitmap, + (ptr_type)flat8_mono_scale_bitmap, + (ptr_type)temp_scale_umap, + (ptr_type)temp_scale_map, + gr_null, + gr_null, + (ptr_type)temp_scale_umap, + (ptr_type)temp_scale_map, + (ptr_type)temp_scale_umap, + (ptr_type)temp_scale_map, + + gr_null, /* rsd8 solid scale functions. No longer used. */ + gr_null, + + gr_null, /* clut scale functions. */ + gr_null, + gr_null, + gr_null, + (ptr_type)temp_clut_scale_umap, + (ptr_type)temp_clut_scale_map, + gr_null, + gr_null, + (ptr_type)temp_clut_scale_umap, + (ptr_type)temp_clut_scale_map, + (ptr_type)temp_clut_scale_umap, + (ptr_type)temp_clut_scale_map, + + gr_null, /* bitmap mask draw functions. */ + gr_null, + gr_null, + gr_null, + (ptr_type)flat8_flat8_ubitmap, + (ptr_type)temp_flat8_mask_bitmap, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + + gr_null, /* bitmap get functions. */ + gr_null, + gr_null, + gr_null, + (ptr_type)flat8_get_flat8_ubitmap, + (ptr_type)gen_get_flat8_bitmap, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + + gr_null, /* bitmap horizontal flip functions */ + gr_null, + gr_null, + gr_null, + (ptr_type)flat8_hflip_flat8_ubitmap, + (ptr_type)gen_hflip_flat8_bitmap, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + + gr_null, /* bitmap clut horizontal flip functions */ + gr_null, + gr_null, + gr_null, + (ptr_type)flat8_clut_hflip_flat8_ubitmap, + (ptr_type)gen_clut_hflip_flat8_bitmap, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + + gr_null, /* bitmap horizontal doubling. */ + gr_null, + gr_null, + gr_null, + (ptr_type)flat8_flat8_h_double_ubitmap, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + + gr_null, /* bitmap vertical doubling. */ + gr_null, + gr_null, + gr_null, + (ptr_type)flat8_flat8_v_double_ubitmap, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + + gr_null, /* bitmap horizontal and vertical doubling. */ + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + + gr_null, /* bitmap smooth horizontal doubling. */ + gr_null, + gr_null, + gr_null, + (ptr_type)flat8_flat8_smooth_h_double_ubitmap, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + + gr_null, /* bitmap smooth vertical doubling. */ + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + + gr_null, /* bitmap smooth horizontal and vertical doubling. */ + gr_null, + gr_null, + gr_null, + (ptr_type)flat8_flat8_smooth_hv_double_ubitmap, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + + (ptr_type)gen_font_ustring, /* text/font functions. */ + (ptr_type)gen_font_string, + (ptr_type)gen_font_scale_ustring, + (ptr_type)gen_font_scale_string, + (ptr_type)gen_font_uchar, + (ptr_type)gen_font_char, + + (ptr_type)flat8_calc_row, /* utility functions. */ + (ptr_type)flat8_sub_bitmap, + + gr_null, /* placeholders for primitiveless chains */ + gr_null, +}; diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8cop.c b/engine/src/Libraries/2D/Source/Flat8/fl8cop.c new file mode 100644 index 0000000..43f64bf --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8cop.c @@ -0,0 +1,298 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/gencop.c $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/08/16 12:56:46 $ + * + * full perspective texture mapper. + * scanline processors. + * + */ + +#include "cnvdat.h" +#include "fl8tmapdv.h" +#include "pertyp.h" +#include "plytyp.h" + +// prototypes +void gri_opaque_clut_per_umap_hscan_scanline(grs_per_info *pi, grs_bitmap *bm); +void gri_opaque_clut_per_umap_vscan_scanline(grs_per_info *pi, grs_bitmap *bm); +void gri_opaque_clut_per_umap_hscan_init(grs_bitmap *bm, grs_per_setup *ps); +void gri_opaque_clut_per_umap_vscan_init(grs_bitmap *bm, grs_per_setup *ps); + +void gri_opaque_clut_per_umap_hscan_scanline(grs_per_info *pi, grs_bitmap *bm) { + register int k, y_cint; + uchar *p; + + // locals used to speed PPC code + fix l_u, l_v, l_du, l_dv, l_y_fix, l_scan_slope, l_dtl, l_dxl, l_dyl, l_dtr, l_dyr; + int l_x, l_xl, l_xr, l_xr0, l_u_mask, l_v_mask, l_v_shift; + int gr_row, temp_y; + uchar *bm_bits; + uchar *t_clut; + + t_clut = pi->clut; + gr_row = grd_bm.row; + bm_bits = bm->bits; + l_dyr = pi->dyr; + l_dtr = pi->dtr; + l_dyl = pi->dyl; + l_dxl = pi->dxl; + l_dtl = pi->dtl; + l_scan_slope = pi->scan_slope; + l_y_fix = pi->y_fix; + l_v_shift = pi->v_shift; + l_v_mask = pi->v_mask; + l_u_mask = pi->u_mask; + l_xr0 = pi->xr0; + l_x = pi->x; + l_xl = pi->xl; + l_xr = pi->xr; + l_u = pi->u; + l_v = pi->v; + l_du = pi->du; + l_dv = pi->dv; + + l_y_fix = l_x * l_scan_slope + fix_make(pi->yp, 0xffff); + +#if InvDiv + k = fix_div(fix_make(1, 0), pi->denom); + l_u = pi->u0 + fix_mul_asm_safe(pi->unum, k); + l_v = pi->v0 + fix_mul_asm_safe(pi->vnum, k); + l_du = fix_mul_asm_safe(pi->dunum, k); + l_dv = fix_mul_asm_safe(pi->dvnum, k); +#else + l_u = pi->u0 + fix_div(pi->unum, pi->denom); + l_v = pi->v0 + fix_div(pi->vnum, pi->denom); + l_du = fix_div(pi->dunum, pi->denom); + l_dv = fix_div(pi->dvnum, pi->denom); +#endif + + l_u += l_x * l_du; + l_v += l_x * l_dv; + + y_cint = fix_int(l_y_fix); + + if (l_scan_slope < 0) + gr_row = -gr_row; + + p = grd_bm.bits + l_x + y_cint * grd_bm.row; + if (l_x < l_xl) { + fix test = l_x * l_dyl - y_cint * l_dxl + pi->cl; + for (; l_x < l_xl; l_x++) { + if (test <= 0) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + *p = t_clut[bm_bits[k]]; // gr_fill_upixel(t_clut[bm_bits[k]],l_x,y_cint); + } + temp_y = y_cint; + y_cint = fix_int(l_y_fix += l_scan_slope); + + if (temp_y != y_cint) { + p += gr_row; + test += l_dtl; + } else + test += l_dyl; + + p++; + l_u += l_du; + l_v += l_dv; + } + } + + for (; l_x < l_xr0; l_x++) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + *(p++) = t_clut[bm_bits[k]]; // gr_fill_upixel(t_clut[bm_bits[k]],l_x,y_cint); + temp_y = y_cint; + y_cint = fix_int(l_y_fix += l_scan_slope); + if (temp_y != y_cint) + p += gr_row; + + l_u += l_du; + l_v += l_dv; + } + + if (l_x < l_xr) { + fix test = l_x * l_dyr - y_cint * pi->dxr + pi->cr; + p = grd_bm.bits + l_x + y_cint * grd_bm.row; + for (; l_x < l_xr; l_x++) { + if (test >= 0) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + *p = t_clut[bm_bits[k]]; // gr_fill_upixel(t_clut[bm_bits[k]],l_x,y_cint); + } + temp_y = y_cint; + y_cint = fix_int(l_y_fix += l_scan_slope); + if (temp_y != y_cint) { + p += gr_row; + test += l_dtr; + } else + test += l_dyr; + + p++; + l_u += l_du; + l_v += l_dv; + } + } + + pi->y_fix = l_y_fix; + pi->x = l_x; + pi->u = l_u; + pi->v = l_v; + pi->du = l_du; + pi->dv = l_dv; +} + +void gri_opaque_clut_per_umap_vscan_scanline(grs_per_info *pi, grs_bitmap *bm) { + register int k, x_cint; + + // locals used to speed PPC code + fix l_dxr, l_x_fix, l_u, l_v, l_du, l_dv, l_scan_slope, l_dtl, l_dxl, l_dyl, l_dtr, l_dyr; + int l_yl, l_yr0, l_yr, l_y, l_u_mask, l_v_mask, l_v_shift; + int gr_row, temp_x; + uchar *bm_bits; + uchar *p; + uchar *t_clut; + + t_clut = pi->clut; + gr_row = grd_bm.row; + bm_bits = bm->bits; + l_dxr = pi->dxr; + l_x_fix = pi->x_fix; + l_y = pi->y; + l_yr = pi->yr; + l_yr0 = pi->yr0; + l_yl = pi->yl; + l_dyr = pi->dyr; + l_dtr = pi->dtr; + l_dyl = pi->dyl; + l_dxl = pi->dxl; + l_dtl = pi->dtl; + l_scan_slope = pi->scan_slope; + l_v_shift = pi->v_shift; + l_v_mask = pi->v_mask; + l_u_mask = pi->u_mask; + l_u = pi->u; + l_v = pi->v; + l_du = pi->du; + l_dv = pi->dv; + + l_x_fix = l_y * l_scan_slope + fix_make(pi->xp, 0xffff); + +#if InvDiv + k = fix_div(fix_make(1, 0), pi->denom); + l_u = pi->u0 + fix_mul_asm_safe(pi->unum, k); + l_v = pi->v0 + fix_mul_asm_safe(pi->vnum, k); + l_du = fix_mul_asm_safe(pi->dunum, k); + l_dv = fix_mul_asm_safe(pi->dvnum, k); +#else + l_u = pi->u0 + fix_div(pi->unum, pi->denom); + l_v = pi->v0 + fix_div(pi->vnum, pi->denom); + l_du = fix_div(pi->dunum, pi->denom); + l_dv = fix_div(pi->dvnum, pi->denom); +#endif + l_u += l_y * l_du; + l_v += l_y * l_dv; + + x_cint = fix_int(l_x_fix); + p = grd_bm.bits + x_cint + l_y * gr_row; + if (l_y < l_yl) { + fix test = l_y * l_dxl - x_cint * l_dyl + pi->cl; + for (; l_y < l_yl; l_y++) { + if (test <= 0) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + *p = t_clut[bm_bits[k]]; // gr_fill_upixel(t_clut[bm_bits[k]],x_cint,l_y); + } + temp_x = x_cint; + x_cint = fix_int(l_x_fix += l_scan_slope); + if (temp_x != x_cint) { + test += l_dtl; + p -= (temp_x - x_cint); + } else + test += l_dxl; + + p += gr_row; + l_u += l_du; + l_v += l_dv; + } + } + + for (; l_y < l_yr0; l_y++) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + *p = t_clut[bm_bits[k]]; // gr_fill_upixel(t_clut[bm_bits[k]],x_cint,l_y); + + temp_x = x_cint; + x_cint = fix_int(l_x_fix += l_scan_slope); + if (temp_x != x_cint) + p -= (temp_x - x_cint); + + p += gr_row; + l_u += l_du; + l_v += l_dv; + } + + if (l_y < l_yr) { + fix test = l_y * l_dxr - x_cint * l_dyr + pi->cr; + p = grd_bm.bits + x_cint + l_y * gr_row; + for (; l_y < l_yr; l_y++) { + if (test >= 0) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + *p = t_clut[bm_bits[k]]; // gr_fill_upixel(t_clut[bm_bits[k]],x_cint,l_y); + } + + temp_x = x_cint; + x_cint = fix_int(l_x_fix += l_scan_slope); + if (temp_x != x_cint) { + test += l_dtr; + p -= (temp_x - x_cint); + } else + test += l_dxr; + + p += gr_row; + l_u += l_du; + l_v += l_dv; + } + } + + pi->x_fix = l_x_fix; + pi->y = l_y; + pi->u = l_u; + pi->v = l_v; + pi->du = l_du; + pi->dv = l_dv; +} + +extern void gri_per_umap_hscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps); +extern void gri_per_umap_vscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps); + +void gri_opaque_clut_per_umap_hscan_init(grs_bitmap *bm, grs_per_setup *ps) { + ps->shell_func = (void (*)())gri_per_umap_hscan; + ps->scanline_func = (void (*)())gri_opaque_clut_per_umap_hscan_scanline; +} + +void gri_opaque_clut_per_umap_vscan_init(grs_bitmap *bm, grs_per_setup *ps) { + ps->shell_func = (void (*)())gri_per_umap_vscan; + ps->scanline_func = (void (*)())gri_opaque_clut_per_umap_vscan_scanline; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8cply.c b/engine/src/Libraries/2D/Source/Flat8/fl8cply.c new file mode 100644 index 0000000..47b25c2 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8cply.c @@ -0,0 +1,112 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8cply.c $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/10/17 14:59:58 $ + * + * Routines for drawing flat shaded polygons onto a flat 8 canvas. + * + * This file is part of the 2d library. + */ + +#include "cnvdat.h" +#include "fix.h" +#include "gente.h" +#include "poly.h" +#include "rgb.h" +#include "scrdat.h" +#include "tmapint.h" + +// prototypes +int gri_cpoly_loop(grs_tmap_loop_info *ti); +void gri_cpoly_init(grs_tmap_loop_info *ti); +void gri_clut_cpoly_init(grs_tmap_loop_info *ti); + +int gri_cpoly_loop(grs_tmap_loop_info *ti) { + int x, d; + fix dx, frac; + fix r, g, b, dr, dg, db; + + do { + dx = ti->right.x - ti->left.x; + frac = fix_ceil(ti->left.x) - ti->left.x; + + r = ti->left.u; + dr = fix_div(ti->right.u - r, dx); + r += fix_mul(frac, dr); + + g = ti->left.v; + dg = fix_div(ti->right.v - g, dx); + g += fix_mul(frac, dg); + + b = ti->left.i; + db = fix_div(ti->right.i - b, dx); + b += fix_mul(frac, db); + + if ((d = fix_cint(ti->right.x) - fix_cint(ti->left.x)) > 0) { + switch (ti->bm.hlog) { + case GRL_OPAQUE: + for (x = fix_cint(ti->left.x); x < fix_cint(ti->right.x); x++) { + int j = gr_index_rgb(r, g, b); + ti->d[x] = grd_ipal[j]; + r += dr, g += dg, b += db; + } + break; + case GRL_CLUT: + for (x = fix_cint(ti->left.x); x < fix_cint(ti->right.x); x++) { + int j = gr_index_rgb(r, g, b); + ti->d[x] = ti->clut[grd_ipal[j]]; + r += dr, g += dg, b += db; + } + break; + } + } else if (d < 0) { + return TRUE; + } + /* update span extrema and destination. */ + ti->left.x += ti->left.dx; + ti->right.x += ti->right.dx; + ti->left.u += ti->left.du; + ti->right.u += ti->right.du; + ti->left.v += ti->left.dv; + ti->right.v += ti->right.dv; + ti->left.i += ti->left.di; + ti->right.i += ti->right.di; + ti->d += grd_bm.row; + } while ((--(ti->n)) > 0); + return FALSE; +} + +void gri_cpoly_init(grs_tmap_loop_info *ti) { + ti->bm.hlog = GRL_OPAQUE; + ti->d = ti->y * grd_bm.row + grd_bm.bits; + ti->loop_func = (void (*)())gri_cpoly_loop; + ti->top_edge_func = (void (*)())gri_rgbx_edge; + ti->bot_edge_func = (void (*)())gri_rgbx_edge; +} + +void gri_clut_cpoly_init(grs_tmap_loop_info *ti) { + ti->bm.hlog = GRL_CLUT; + ti->d = ti->y * grd_bm.row + grd_bm.bits; + ti->loop_func = (void (*)())gri_cpoly_loop; + ti->top_edge_func = (void (*)())gri_rgbx_edge; + ti->bot_edge_func = (void (*)())gri_rgbx_edge; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8ctp.c b/engine/src/Libraries/2D/Source/Flat8/fl8ctp.c new file mode 100644 index 0000000..ab709e8 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8ctp.c @@ -0,0 +1,312 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/FL8CTP.c $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/08/16 12:57:23 $ + * + * full perspective texture mapper. + * scanline processors. + * + */ + +#include "cnvdat.h" +#include "fl8tmapdv.h" +#include "pertyp.h" +#include "plytyp.h" + +// prototypes +void gri_trans_clut_per_umap_hscan_scanline(grs_per_info *pi, grs_bitmap *bm); +void gri_trans_clut_per_umap_vscan_scanline(grs_per_info *pi, grs_bitmap *bm); +void gri_trans_clut_per_umap_hscan_init(grs_bitmap *bm, grs_per_setup *ps); +void gri_trans_clut_per_umap_vscan_init(grs_bitmap *bm, grs_per_setup *ps); + +void gri_trans_clut_per_umap_hscan_scanline(grs_per_info *pi, grs_bitmap *bm) { + register int k, y_cint; + uchar *p; + + // ltc_als used to speed PPC code + fix l_u, l_v, l_du, l_dv, l_y_fix, l_scan_slope, l_dtl, l_dxl, l_dyl, l_dtr, l_dyr; + int l_x, l_xl, l_xr, l_xr0, l_u_mask, l_v_mask, l_v_shift; + int gr_row, temp_y; + uchar *bm_bits; + uchar *t_clut, temp_pix; + + t_clut = pi->clut; + gr_row = grd_bm.row; + bm_bits = bm->bits; + l_dyr = pi->dyr; + l_dtr = pi->dtr; + l_dyl = pi->dyl; + l_dxl = pi->dxl; + l_dtl = pi->dtl; + l_scan_slope = pi->scan_slope; + l_y_fix = pi->y_fix; + l_v_shift = pi->v_shift; + l_v_mask = pi->v_mask; + l_u_mask = pi->u_mask; + l_xr0 = pi->xr0; + l_x = pi->x; + l_xl = pi->xl; + l_xr = pi->xr; + l_u = pi->u; + l_v = pi->v; + l_du = pi->du; + l_dv = pi->dv; + + l_y_fix = l_x * l_scan_slope + fix_make(pi->yp, 0xffff); + +#if InvDiv + k = fix_div(fix_make(1, 0), pi->denom); + l_u = pi->u0 + fix_mul_asm_safe(pi->unum, k); + l_v = pi->v0 + fix_mul_asm_safe(pi->vnum, k); + l_du = fix_mul_asm_safe(pi->dunum, k); + l_dv = fix_mul_asm_safe(pi->dvnum, k); +#else + l_u = pi->u0 + fix_div(pi->unum, pi->denom); + l_v = pi->v0 + fix_div(pi->vnum, pi->denom); + l_du = fix_div(pi->dunum, pi->denom); + l_dv = fix_div(pi->dvnum, pi->denom); +#endif + + l_u += l_x * l_du; + l_v += l_x * l_dv; + + y_cint = fix_int(l_y_fix); + if (l_scan_slope < 0) + gr_row = -gr_row; + + p = grd_bm.bits + l_x + y_cint * grd_bm.row; + if (l_x < l_xl) { + fix test = l_x * l_dyl - y_cint * l_dxl + pi->cl; + for (; l_x < l_xl; l_x++) { + if (test <= 0) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + temp_pix = bm_bits[k]; + if (temp_pix != 0) + *p = t_clut[temp_pix]; // gr_fill_upixel(t_clut[bm_bits[k]],l_x,y_cint); + } + temp_y = y_cint; + y_cint = fix_int(l_y_fix += l_scan_slope); + + if (temp_y != y_cint) { + p += gr_row; + test += l_dtl; + } else + test += l_dyl; + + p++; + l_u += l_du; + l_v += l_dv; + } + } + + for (; l_x < l_xr0; l_x++) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + temp_pix = bm_bits[k]; + if (temp_pix != 0) + *p = t_clut[temp_pix]; // gr_fill_upixel(t_clut[bm_bits[k]],l_x,y_cint); + temp_y = y_cint; + y_cint = fix_int(l_y_fix += l_scan_slope); + if (temp_y != y_cint) + p += gr_row; + + p++; + l_u += l_du; + l_v += l_dv; + } + + if (l_x < l_xr) { + fix test = l_x * l_dyr - y_cint * pi->dxr + pi->cr; + p = grd_bm.bits + l_x + y_cint * grd_bm.row; + for (; l_x < l_xr; l_x++) { + if (test >= 0) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + temp_pix = bm_bits[k]; + if (temp_pix != 0) + *p = t_clut[temp_pix]; // gr_fill_upixel(t_clut[bm_bits[k]],l_x,y_cint); + } + temp_y = y_cint; + y_cint = fix_int(l_y_fix += l_scan_slope); + + if (temp_y != y_cint) { + p += gr_row; + test += l_dtr; + } else + test += l_dyr; + + p++; + l_u += l_du; + l_v += l_dv; + } + } + + pi->y_fix = l_y_fix; + pi->x = l_x; + pi->u = l_u; + pi->v = l_v; + pi->du = l_du; + pi->dv = l_dv; +} + +void gri_trans_clut_per_umap_vscan_scanline(grs_per_info *pi, grs_bitmap *bm) { + register int k, x_cint; + + // locals used to speed PPC code + fix l_dxr, l_x_fix, l_u, l_v, l_du, l_dv, l_scan_slope, l_dtl, l_dxl, l_dyl, l_dtr, l_dyr; + int l_yl, l_yr0, l_yr, l_y, l_u_mask, l_v_mask, l_v_shift; + int gr_row, temp_x; + uchar *bm_bits; + uchar *p; + uchar *t_clut, temp_pix; + + t_clut = pi->clut; + gr_row = grd_bm.row; + bm_bits = bm->bits; + l_dxr = pi->dxr; + l_x_fix = pi->x_fix; + l_y = pi->y; + l_yr = pi->yr; + l_yr0 = pi->yr0; + l_yl = pi->yl; + l_dyr = pi->dyr; + l_dtr = pi->dtr; + l_dyl = pi->dyl; + l_dxl = pi->dxl; + l_dtl = pi->dtl; + l_scan_slope = pi->scan_slope; + l_v_shift = pi->v_shift; + l_v_mask = pi->v_mask; + l_u_mask = pi->u_mask; + l_u = pi->u; + l_v = pi->v; + l_du = pi->du; + l_dv = pi->dv; + + l_x_fix = l_y * l_scan_slope + fix_make(pi->xp, 0xffff); + +#if InvDiv + k = fix_div(fix_make(1, 0), pi->denom); + l_u = pi->u0 + fix_mul_asm_safe(pi->unum, k); + l_v = pi->v0 + fix_mul_asm_safe(pi->vnum, k); + l_du = fix_mul_asm_safe(pi->dunum, k); + l_dv = fix_mul_asm_safe(pi->dvnum, k); +#else + l_u = pi->u0 + fix_div(pi->unum, pi->denom); + l_v = pi->v0 + fix_div(pi->vnum, pi->denom); + l_du = fix_div(pi->dunum, pi->denom); + l_dv = fix_div(pi->dvnum, pi->denom); +#endif + + l_u += l_y * l_du; + l_v += l_y * l_dv; + + x_cint = fix_int(l_x_fix); + p = grd_bm.bits + x_cint + l_y * gr_row; + if (l_y < l_yl) { + fix test = l_y * l_dxl - x_cint * l_dyl + pi->cl; + for (; l_y < l_yl; l_y++) { + if (test <= 0) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + temp_pix = bm_bits[k]; + if (temp_pix != 0) + *p = t_clut[temp_pix]; // gr_fill_upixel(t_clut[bm_bits[k]],x_cint,l_y); + } + temp_x = x_cint; + x_cint = fix_int(l_x_fix += l_scan_slope); + if (temp_x != x_cint) { + test += l_dtl; + p -= (temp_x - x_cint); + } else + test += l_dxl; + + p += gr_row; + l_u += l_du; + l_v += l_dv; + } + } + + for (; l_y < l_yr0; l_y++) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + temp_pix = bm_bits[k]; + if (temp_pix != 0) + *p = t_clut[temp_pix]; // gr_fill_upixel(t_clut[bm_bits[k]],x_cint,l_y); + + temp_x = x_cint; + x_cint = fix_int(l_x_fix += l_scan_slope); + if (temp_x != x_cint) + p -= (temp_x - x_cint); + + p += gr_row; + l_u += l_du; + l_v += l_dv; + } + + if (l_y < l_yr) { + fix test = l_y * l_dxr - x_cint * l_dyr + pi->cr; + p = grd_bm.bits + x_cint + l_y * gr_row; + for (; l_y < l_yr; l_y++) { + if (test >= 0) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + temp_pix = bm_bits[k]; + if (temp_pix != 0) + *p = t_clut[temp_pix]; // gr_fill_upixel(t_clut[bm_bits[k]],x_cint,l_y); + } + + temp_x = x_cint; + x_cint = fix_int(l_x_fix += l_scan_slope); + if (temp_x != x_cint) { + test += l_dtr; + p -= (temp_x - x_cint); + } else + test += l_dxr; + + p += gr_row; + l_u += l_du; + l_v += l_dv; + } + } + + pi->x_fix = l_x_fix; + pi->y = l_y; + pi->u = l_u; + pi->v = l_v; + pi->du = l_du; + pi->dv = l_dv; +} + +extern void gri_per_umap_hscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps); +extern void gri_per_umap_vscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps); + +void gri_trans_clut_per_umap_hscan_init(grs_bitmap *bm, grs_per_setup *ps) { + ps->shell_func = (void (*)())gri_per_umap_hscan; + ps->scanline_func = (void (*)())gri_trans_clut_per_umap_hscan_scanline; +} + +void gri_trans_clut_per_umap_vscan_init(grs_bitmap *bm, grs_per_setup *ps) { + ps->shell_func = (void (*)())gri_per_umap_vscan; + ps->scanline_func = (void (*)())gri_trans_clut_per_umap_vscan_scanline; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8dbl.c b/engine/src/Libraries/2D/Source/Flat8/fl8dbl.c new file mode 100644 index 0000000..8a568b6 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8dbl.c @@ -0,0 +1,176 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// +// $Source: r:/prj/lib/src/2d/RCS/fl8dbl.asm $ +// $Revision: 1.3 $ +// $Author: kevin $ +// $Date: 1994/09/08 00:00:18 $ +// +// Bitmap doubling primitives. +// + +#include + +#include "blndat.h" +#include "grs.h" +#include "lg.h" + +// ------------------------------------------------------------------------ +// PowerPC routines +// ------------------------------------------------------------------------ +// ======================================================================== +void flat8_flat8_h_double_ubitmap(grs_bitmap *bm) { + DEBUG("%s: call mark", __FUNCTION__); + /* int h,v,endh,endv; + uchar *src=bm->bits, *dst=grd_bm.bits; + long srcAdd,dstAdd; + uchar temp; + + srcAdd = bm->row-bm->w; + dstAdd = grd_bm.row - (bm->w<<1); + endh = bm->w; + endv = bm->h; + + for (v=0; vbits, *dst = dstb->bits; + long srcAdd, dstAdd; + ushort curpix, tempshort; + uchar *local_grd_half_blend; + + local_grd_half_blend = grd_half_blend; + if (!local_grd_half_blend) + return; + + srcAdd = (srcb->row - srcb->w) - 1; + dstAdd = dstb->row - (srcb->w << 1); + endh = srcb->w - 1; + endv = srcb->h; + + for (v = 0; v < endv; v++) { + curpix = *(short *)src; + src += 2; + for (h = 0; h < endh; h++) { + tempshort = curpix & 0xff00; + tempshort |= local_grd_half_blend[curpix]; + *(ushort *)dst = tempshort; + dst += 2; + curpix = (curpix << 8) | *(src++); + } + + // double last pixel + curpix >>= 8; + *(dst++) = curpix; + *(dst++) = curpix; + + src += srcAdd; + dst += dstAdd; + } +} + +// ======================================================================== +// src = eax, dest = edx +void flat8_flat8_smooth_hv_double_ubitmap(grs_bitmap *src, grs_bitmap *dst) { + int tempH, tempW, temp, savetemp; + uchar *srcPtr, *dstPtr; + uchar *shvd_read_row1, *shvd_write, *shvd_read_row2, *shvd_read_blend; + ushort tempc; + + dstPtr = dst->bits; + srcPtr = src->bits; + + // HAX HAX HAX no smooth doubling for now! + for (int y = 0; y < src->h; y++) { + for (int x = 0; x < src->w; x++) { + *(dstPtr) = *srcPtr; + *(dstPtr + 1) = *srcPtr; + *(dstPtr + src->w * 2) = *srcPtr; + *((dstPtr + src->w * 2) + 1) = *srcPtr; + dstPtr += 2; + srcPtr++; + } + dstPtr += src->w * 2; + } + + return; + +/* // WH - unused code + dst->row <<= 1; + flat8_flat8_smooth_h_double_ubitmap(src, dst); + + dst->row = tempW = dst->row >> 1; + dstPtr = dst->bits; + + tempH = src->h - 1; + temp = src->w << 1; + dstPtr += temp; + temp = -temp; + + shvd_read_row1 = dstPtr; + dstPtr += tempW; + shvd_write = dstPtr - 1; + dstPtr += tempW; + shvd_read_row2 = dstPtr; + shvd_read_blend = grd_half_blend; + savetemp = temp; + + do { + do { + tempc = shvd_read_row1[temp]; + tempc |= ((ushort)shvd_read_row2[temp]) << 8; + temp++; + + shvd_write[temp] = shvd_read_blend[tempc]; + } while (temp != 0); + + if (--tempH == 0) + break; + + shvd_read_row1 = dstPtr; + dstPtr += tempW; + shvd_write = dstPtr - 1; + dstPtr += tempW; + shvd_read_row2 = dstPtr; + temp = savetemp; + } while (true); + + // do last row + srcPtr = dstPtr + savetemp; + dstPtr += tempW + savetemp; + savetemp = -savetemp; + + for (; savetemp > 0; savetemp--) + *(dstPtr++) = *(srcPtr++); +*/ +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8fl8.c b/engine/src/Libraries/2D/Source/Flat8/fl8fl8.c new file mode 100644 index 0000000..db2e67f --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8fl8.c @@ -0,0 +1,86 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/fl8fl8.c $ + * $Revision: 1.5 $ + * $Author: kaboom $ + * $Date: 1993/10/19 09:50:21 $ + * + * Routines for drawing flat 8 bitmaps into a flat 8 canvas. + * + * This file is part of the 2d library. + * + * $Log: fl8fl8.c $ + * Revision 1.5 1993/10/19 09:50:21 kaboom + * Replaced #include + +void flat8_flat8_ubitmap(grs_bitmap *bm, short x, short y) { + uchar *m_src; + uchar *m_dst; + int w = bm->w; + int h = bm->h; + int i; + int brow, grow; + + brow = bm->row; + grow = grd_bm.row; + + m_src = bm->bits; + m_dst = grd_bm.bits + grow * y + x; + + if (bm->flags & BMF_TRANS) + while (h--) { + for (i = 0; i < w; i++) + if (m_src[i] != 0) + m_dst[i] = m_src[i]; + m_src += brow; + m_dst += grow; + } + else + while (h--) { + memmove(m_dst, m_src, w); + + m_src += brow; + m_dst += grow; + } +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8fl8c.c b/engine/src/Libraries/2D/Source/Flat8/fl8fl8c.c new file mode 100644 index 0000000..36e49ca --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8fl8c.c @@ -0,0 +1,71 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8fl8c.c $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/10/04 18:45:42 $ + * + * Routines for drawing flat8 bitmaps into a flat8 canvas through a clut. + * + * This file is part of the 2d library. + * + * $Log: fl8fl8c.c $ + * Revision 1.2 1994/10/04 18:45:42 kevin + * added clut fill mode specific function. Renamed old proc. + * + * Revision 1.1 1994/03/15 13:15:51 kevin + * Initial revision + * + */ + +#include "bitmap.h" +#include "cnvdat.h" +#include "fl8tf.h" + +void gri_flat8_fill_clut_ubitmap(grs_bitmap *bm, short x, short y) { + gri_flat8_clut_ubitmap(bm, x, y, (uchar *)(grd_gc.fill_parm)); +} + +void gri_flat8_clut_ubitmap(grs_bitmap *bm, short x, short y, uchar *cl) { + uchar *src, *dst, *srcf; + short w = bm->w; + short h = bm->h; + int ds = bm->row - w; + int dd = grd_bm.row - w; + + src = bm->bits; + dst = grd_bm.bits + grd_bm.row * y + x; + + if (bm->flags & BMF_TRANS) + while (h--) { + for (srcf = src + w; src < srcf; src++, dst++) + if ((*src) != 0) + *dst = cl[*src]; + src += ds; + dst += dd; + } + else + while (h--) { + for (srcf = src + w; src < srcf; src++, dst++) + *dst = cl[*src]; + src += ds; + dst += dd; + } +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8fl8m.c b/engine/src/Libraries/2D/Source/Flat8/fl8fl8m.c new file mode 100644 index 0000000..ed089f2 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8fl8m.c @@ -0,0 +1,152 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8fl8m.c $ + * $Revision: 1.3 $ + * $Author: kevin $ + * $Date: 1994/10/04 18:46:16 $ + * + * Routines for masking flat 8 bitmaps into a flat 8 canvas + * through a stencil. + * + * This file is part of the 2d library. + * + * $Log: fl8fl8m.c $ + * Revision 1.3 1994/10/04 18:46:16 kevin + * added clut fill mode specific function. Renamed old proc. + * + * Revision 1.2 1994/08/16 18:29:35 kevin + * fixed several bugs. + * + * Revision 1.1 1994/08/16 13:14:22 kevin + * Initial revision + * + */ + +#include "bitmap.h" +#include "clip.h" +#include "cnvdat.h" +#include "fl8tf.h" +#include "grs.h" +#include "lg.h" +#include + +extern int gen_flat8_bitmap(grs_bitmap *bm, short x, short y); +int gri_flat8_mask_bitmap(grs_bitmap *bm, short x, short y, grs_stencil *sten) { + if (sten == NULL) + return gen_flat8_bitmap(bm, x, y); + else { + int yf = y + bm->h; + uchar *dst; + uchar *src = bm->bits - x; + if (yf > grd_clip.bot) + yf = grd_clip.bot; + if (y < grd_clip.top) { + src += (grd_clip.top - y) * bm->row; + y = grd_clip.top; + } + dst = grd_bm.bits + y * grd_bm.row; + for (; y < yf; y++) { + int xi = x, xf = x + bm->w; + grs_sten_elem *s; +#ifdef NEW_STENCILS + s = &(sten[y]); +#else + s = &((sten->elem)[y]); +#endif + for (; s->r <= xi;) { + s = s->n; + if (s == NULL) + break; + } + if (s != NULL) { + if (s->l > xi) + xi = s->l; + if (s->r < xf) + xf = s->r; + if (xf > xi) { + if (bm->flags & BMF_TRANS) { + int i; + for (i = xi; i < xf; i++) + if (src[i]) + dst[i] = src[i]; + } else { + LG_memmove(dst + xi, src + xi, xf - xi); + } + } + } + src += bm->row; + dst += grd_bm.row; + } + } + return CLIP_NONE; /* actually, who knows? */ +} + +extern int gen_flat8_clut_bitmap(grs_bitmap *bm, short x, short y, uchar *clut); +int gri_flat8_mask_fill_clut_bitmap(grs_bitmap *bm, short x, short y, grs_stencil *sten) { + uchar *clut = (uchar *)(grd_gc.fill_parm); + if (sten == NULL) + return gen_flat8_clut_bitmap(bm, x, y, clut); + else { + int yf = y + bm->h; + uchar *dst; + uchar *src = bm->bits - x; + if (yf > grd_clip.bot) + yf = grd_clip.bot; + if (y < grd_clip.top) { + src += (grd_clip.top - y) * bm->row; + y = grd_clip.top; + } + dst = grd_bm.bits + y * grd_bm.row; + for (; y < yf; y++) { + int xi = x, xf = x + bm->w; + grs_sten_elem *s; +#ifdef NEW_STENCILS + s = &(sten[y]); +#else + s = &((sten->elem)[y]); +#endif + for (; s->r <= xi;) { + s = s->n; + if (s == NULL) + break; + } + if (s != NULL) { + if (s->l > xi) + xi = s->l; + if (s->r < xf) + xf = s->r; + if (xf > xi) { + int i; + if (bm->flags & BMF_TRANS) { + for (i = xi; i < xf; i++) + if (src[i]) + dst[i] = clut[src[i]]; + } else { + for (i = xi; i < xf; i++) + dst[i] = clut[src[i]]; + } + } + } + src += bm->row; + dst += grd_bm.row; + } + } + return CLIP_NONE; /* actually, who knows? */ +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8fltr2.c b/engine/src/Libraries/2D/Source/Flat8/fl8fltr2.c new file mode 100644 index 0000000..4101623 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8fltr2.c @@ -0,0 +1,90 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/fl8fltr2.c $ + * $Revision: 1.3 $ + * $Author: kaboom $ + * $Date: 1993/10/19 09:52:36 $ + * + * Routine for scaling down by 2 with filtering onto a flat8 canvas + * + * This file is part of the 2d library. + * + * $Log: fl8fltr2.c $ + * Revision 1.3 1993/10/19 09:52:36 kaboom + * Replaced #include bits; + + ds = 2 * bm->row - bm->w; + dd = grd_bm.row - grd_bm.w; + + /* variables + a,src,dst,bm->row,grd_ipal,grd_bpal */ + + /* Cycle through rows and do horizontal strips */ + for (j = bm->h; j > 0; j -= 2) { + for (i = bm->w; i > 0; i -= 2) { + a = (grd_bpal[*src] >> 2) & 0x3fc7f8ff; + src++; + a += (grd_bpal[*src] >> 2) & 0x3fc7f8ff; + src += bm->row; + a += (grd_bpal[*src] >> 2) & 0x3fc7f8ff; + src--; + a += (grd_bpal[*src] >> 2) & 0x3fc7f8ff; + src -= bm->row; + src += 2; + + c = grd_ipal; + a = a >> 5; + c += a & 0x1f; + a = a >> 6; + c += a & 0x3e0; + a = a >> 6; + c += a & 0x7c00; + + *dst = *c; + dst++; + } + src += ds; + dst += dd; + } +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8ft.c b/engine/src/Libraries/2D/Source/Flat8/fl8ft.c new file mode 100644 index 0000000..a6a49b5 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8ft.c @@ -0,0 +1,1637 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8ft.c $ + * $Revision: 1.1 $ + * $Author: kevin $ + * $Date: 1994/08/16 13:18:21 $ + * + * Gruesome function tables. + * flat8 canvas. + * + * This file is part of the 2d library. + */ + +#include "bitmap.h" +#include "fill.h" +#include "fl8tf.h" +#include "gentf.h" +#include "grnull.h" +#include "ifcn.h" + +typedef void (*ptr_type)(); + +void (*flat8_function_table[GRD_FILL_TYPES][GRD_FUNCS * REAL_BMT_TYPES])() = { + {/* normal fill type - from fl8nft.h */ + /* FILL_NORM */ + /* BMT_DEVICE */ + (ptr_type)flat8_set_upixel, /* pixel primitve */ + gr_null, /* reserved wire poly line */ + gr_null, /* solid hline */ + + gr_null, /* bitmap blitter */ + gr_null, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + gr_null, /* Scalers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_poly_init, /* solid polygon primitives */ + gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Bilinear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Floor */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall2d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall1d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -hscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -vscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + /* BMT_MONO */ + gr_null, /* solid line */ + gr_null, /* solid wire poly line */ + gr_null, /* gouraud hline */ + + gr_null, /* bitmap blitter */ + gr_null, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + gr_null, /* Scalers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_spoly_init, /* gouraud polygon primitives */ + gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Linear */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Floor */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall2d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall1d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -hscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -vscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + /* BMT_FLAT8 */ + gr_null, /* gouraud line */ + gr_null, /* gouraud wire poly line */ + gr_null, /* rgb hline */ + + (ptr_type)flat8_flat8_ubitmap, /* bitmap blitter */ + (ptr_type)gri_flat8_mask_bitmap, /* stencil clipped bitmap blitter */ + (ptr_type)gri_flat8_clut_ubitmap, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + (ptr_type)gri_opaque_scale_umap_init, /* Scalers */ + (ptr_type)gri_trans_scale_umap_init, gr_null, gr_null, (ptr_type)gri_opaque_clut_scale_umap_init, + (ptr_type)gri_trans_clut_scale_umap_init, + + (ptr_type)gri_cpoly_init, /* RGB polygon primitives */ + (ptr_type)gri_trans_blend_clut_lin_umap_init, + + (ptr_type)gr_not_imp, //### MLA- not supposed to be used (ptr_type) + // gri_opaque_true_lin_umap_init, /* Linear mappers + //*/ + gr_null, gr_null, gr_null, + (ptr_type)gr_not_imp, //### MLA- not supposed to be used (ptr_type) + // gri_opaque_clut_true_lin_umap_init, + gr_null, + + (ptr_type)gri_opaque_lin_umap_init, /* Bilinear */ + (ptr_type)gri_trans_lin_umap_init, (ptr_type)gri_opaque_lit_lin_umap_init, (ptr_type)gri_trans_lit_lin_umap_init, + (ptr_type)gri_opaque_clut_lin_umap_init, (ptr_type)gri_trans_clut_lin_umap_init, + + (ptr_type)gri_opaque_floor_umap_init, /* Floor */ + (ptr_type)gri_trans_floor_umap_init, (ptr_type)gri_opaque_lit_floor_umap_init, + (ptr_type)gri_trans_lit_floor_umap_init, (ptr_type)gri_opaque_clut_floor_umap_init, + (ptr_type)gri_trans_clut_floor_umap_init, + + (ptr_type)gri_opaque_wall_umap_init, /* Wall2d */ + (ptr_type)gri_trans_wall_umap_init, (ptr_type)gri_opaque_lit_wall_umap_init, + (ptr_type)gri_trans_lit_wall_umap_init, (ptr_type)gri_opaque_clut_wall_umap_init, + (ptr_type)gri_trans_clut_wall_umap_init, + + (ptr_type)gri_opaque_wall_umap_init, /* Wall1d */ + (ptr_type)gri_trans_wall_umap_init, (ptr_type)gri_opaque_lit_wall1d_umap_init, + (ptr_type)gri_trans_lit_wall_umap_init, (ptr_type)gri_opaque_clut_wall1d_umap_init, + (ptr_type)gri_trans_clut_wall_umap_init, + + (ptr_type)gri_opaque_per_umap_hscan_init, /* Perspective -hscan */ + (ptr_type)gri_trans_per_umap_hscan_init, (ptr_type)gri_opaque_lit_per_umap_hscan_init, + (ptr_type)gri_trans_lit_per_umap_hscan_init, (ptr_type)gri_opaque_clut_per_umap_hscan_init, + (ptr_type)gri_trans_clut_per_umap_hscan_init, + + (ptr_type)gri_opaque_per_umap_vscan_init, /* Perspective -vscan */ + (ptr_type)gri_trans_per_umap_vscan_init, (ptr_type)gri_opaque_lit_per_umap_vscan_init, + (ptr_type)gri_trans_lit_per_umap_vscan_init, (ptr_type)gri_opaque_clut_per_umap_vscan_init, + (ptr_type)gri_trans_clut_per_umap_vscan_init, + + /* BMT_FLAT24 */ + gr_null, /* rgb line */ + gr_null, /* rgb wire poly line */ + gr_null, /* solid vline */ + + gr_null, /* bitmap blitter */ + gr_null, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + gr_null, /* Scalers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_tpoly_init, /* translucent polygon primitives */ + gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Linear */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Floor */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall2d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall1d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -hscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -vscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + /* BMT_RSD8 */ + gr_null, /* translucent line */ + gr_null, /* translucent wire poly line */ + gr_null, /* gouraud vline */ + + (ptr_type)gri_flat8_rsd8_ubitmap, /* bitmap blitter */ + (ptr_type)gri_flat8_rsd8_bitmap, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + (ptr_type)rsd8_tm_init, /* Scalers */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)gri_stpoly_init, /* shaded translucent polygon primitives */ + (ptr_type)rsd8_tm_init, + + (ptr_type)rsd8_tm_init, /* Linear mappers */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)rsd8_tm_init, /* Linear */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)rsd8_tm_init, /* Floor */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)rsd8_tm_init, /* Wall2d */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)rsd8_tm_init, /* Wall1d */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)rsd8_pm_init, /* Perspective -hscan */ + (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, + (ptr_type)rsd8_pm_init, + + (ptr_type)rsd8_pm_init, /* Perspective -vscan */ + (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, + (ptr_type)rsd8_pm_init, + + /* BMT_TLUC8 */ + gr_null, /* shaded translucent line */ + gr_null, /* shaded translucent wire poly line */ + gr_null, /* rgb vline */ + + (ptr_type)flat8_tluc8_ubitmap, /* bitmap blitter */ + gr_null, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + (ptr_type)gri_tluc8_opaque_scale_umap_init, /* Scalers */ + (ptr_type)gri_tluc8_trans_scale_umap_init, gr_null, gr_null, (ptr_type)gri_tluc8_opaque_clut_scale_umap_init, + (ptr_type)gri_tluc8_trans_clut_scale_umap_init, + + gr_null, gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_tluc8_opaque_lin_umap_init, /* Linear */ + (ptr_type)gri_tluc8_trans_lin_umap_init, gr_null, gr_null, (ptr_type)gri_tluc8_opaque_clut_lin_umap_init, + (ptr_type)gri_tluc8_trans_clut_lin_umap_init, + + gr_null, /* Floor */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall2d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall1d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -hscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -vscan */ + gr_null, gr_null, gr_null, gr_null, gr_null + + }, + {/* clut fill type - from fl8cft.h */ + /* FILL_NORM */ + /* BMT_DEVICE */ + (ptr_type)flat8_clut_set_upixel, /* pixel primitve */ + gr_null, /* reserved wire poly line */ + gr_null, /* solid hline */ + + gr_null, /* bitmap blitter */ + gr_null, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + gr_null, /* Scalers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_clut_poly_init, /* solid polygon primitives */ + gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Bilinear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Floor */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall2d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall1d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -hscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -vscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + /* BMT_MONO */ + gr_null, /* solid line */ + gr_null, /* solid wire poly line */ + gr_null, /* gouraud hline */ + + gr_null, /* bitmap blitter */ + gr_null, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + gr_null, /* Scalers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_clut_spoly_init, /* gouraud polygon primitives */ + gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Linear */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Floor */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall2d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall1d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -hscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -vscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + /* BMT_FLAT8 */ + gr_null, /* gouraud line */ + gr_null, /* gouraud wire poly line */ + gr_null, /* rgb hline */ + + (ptr_type)gri_flat8_fill_clut_ubitmap, /* bitmap blitter */ + (ptr_type)gri_flat8_mask_fill_clut_bitmap, /* stencil clipped bitmap blitter */ + (ptr_type)gri_flat8_fill_clut_ubitmap, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + (ptr_type)gri_opaque_clut_scale_umap_init, /* Scalers */ + (ptr_type)gri_trans_clut_scale_umap_init, (ptr_type)gri_opaque_clut_scale_umap_init, + (ptr_type)gri_trans_clut_scale_umap_init, (ptr_type)gri_opaque_clut_scale_umap_init, + (ptr_type)gri_trans_clut_scale_umap_init, + + (ptr_type)gri_clut_cpoly_init, /* RGB polygon primitives */ + gr_null, + + (ptr_type)gr_not_imp, + //### MLA- not supposed to be used + // gri_opaque_clut_true_lin_umap_init, /* Linear mappers */ + gr_null, gr_null, gr_null, (ptr_type)gr_not_imp, + //### MLA- not supposed to be used + // gri_opaque_clut_true_lin_umap_init, + gr_null, + + (ptr_type)gri_opaque_clut_lin_umap_init, /* Bilinear */ + (ptr_type)gri_trans_clut_lin_umap_init, (ptr_type)gri_opaque_clut_lin_umap_init, + (ptr_type)gri_trans_clut_lin_umap_init, (ptr_type)gri_opaque_clut_lin_umap_init, + (ptr_type)gri_trans_clut_lin_umap_init, + + (ptr_type)gri_opaque_clut_floor_umap_init, /* Floor */ + (ptr_type)gri_trans_clut_floor_umap_init, (ptr_type)gri_opaque_clut_floor_umap_init, + (ptr_type)gri_trans_clut_floor_umap_init, (ptr_type)gri_opaque_clut_floor_umap_init, + (ptr_type)gri_trans_clut_floor_umap_init, + + (ptr_type)gri_opaque_clut_wall_umap_init, /* Wall2d */ + (ptr_type)gri_trans_clut_wall_umap_init, (ptr_type)gri_opaque_clut_wall_umap_init, + (ptr_type)gri_trans_clut_wall_umap_init, (ptr_type)gri_opaque_clut_wall_umap_init, + (ptr_type)gri_trans_clut_wall_umap_init, + + (ptr_type)gri_opaque_clut_wall1d_umap_init, /* Wall1d */ + (ptr_type)gri_trans_clut_wall_umap_init, (ptr_type)gri_opaque_clut_wall1d_umap_init, + (ptr_type)gri_trans_clut_wall_umap_init, (ptr_type)gri_opaque_clut_wall1d_umap_init, + (ptr_type)gri_trans_clut_wall_umap_init, + + (ptr_type)gri_opaque_clut_per_umap_hscan_init, /* Perspective -hscan */ + (ptr_type)gri_trans_clut_per_umap_hscan_init, (ptr_type)gri_opaque_clut_per_umap_hscan_init, + (ptr_type)gri_trans_clut_per_umap_hscan_init, (ptr_type)gri_opaque_clut_per_umap_hscan_init, + (ptr_type)gri_trans_clut_per_umap_hscan_init, + + (ptr_type)gri_opaque_clut_per_umap_vscan_init, /* Perspective -vscan */ + (ptr_type)gri_trans_clut_per_umap_vscan_init, (ptr_type)gri_opaque_clut_per_umap_vscan_init, + (ptr_type)gri_trans_clut_per_umap_vscan_init, (ptr_type)gri_opaque_clut_per_umap_vscan_init, + (ptr_type)gri_trans_clut_per_umap_vscan_init, + + /* BMT_FLAT24 */ + gr_null, /* rgb line */ + gr_null, /* rgb wire poly line */ + gr_null, /* solid vline */ + + gr_null, /* bitmap bclutter */ + gr_null, /* stencil clipped bitmap bclutter */ + gr_null, /* clut bitmap bclutter */ + + gr_null, /* hflipped bitmap bclutter */ + gr_null, /* clut hflipped bitmap bclutter */ + + gr_null, /* masked bitmap bclutter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + gr_null, /* Scalers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_clut_tpoly_init, /* translucent polygon primitives */ + gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Linear */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Floor */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall2d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall1d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -hscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -vscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + /* BMT_RSD8 */ + gr_null, /* translucent line */ + gr_null, /* translucent wire poly line */ + gr_null, /* gouraud vline */ + + (ptr_type)gri_gen_rsd8_ubitmap, /* bitmap blitter */ + (ptr_type)gri_gen_rsd8_bitmap, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap bclutter */ + + gr_null, /* hflipped bitmap bclutter */ + gr_null, /* clut hflipped bitmap bclutter */ + + gr_null, /* masked bitmap bclutter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + (ptr_type)rsd8_tm_init, /* Scalers */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)gri_clut_stpoly_init, /* shaded translucent polygon primitives */ + gr_null, + + (ptr_type)rsd8_tm_init, /* Linear mappers */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)rsd8_tm_init, /* Linear */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)rsd8_tm_init, /* Floor */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)rsd8_tm_init, /* Wall2d */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)rsd8_tm_init, /* Wall1d */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)rsd8_pm_init, /* Perspective -hscan */ + (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, + (ptr_type)rsd8_pm_init, + + (ptr_type)rsd8_pm_init, /* Perspective -vscan */ + (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, + (ptr_type)rsd8_pm_init, + + /* BMT_TLUC8 */ + gr_null, /* shaded translucent line */ + gr_null, /* shaded translucent wire poly line */ + gr_null, /* rgb vline */ + + gr_null, /* bitmap bclutter */ + gr_null, /* stencil clipped bitmap bclutter */ + gr_null, /* clut bitmap bclutter */ + + gr_null, /* hflipped bitmap bclutter */ + gr_null, /* clut hflipped bitmap bclutter */ + + gr_null, /* masked bitmap bclutter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + (ptr_type)gri_tluc8_opaque_clut_scale_umap_init, /* Scalers */ + (ptr_type)gri_tluc8_trans_clut_scale_umap_init, (ptr_type)gri_tluc8_opaque_clut_scale_umap_init, + (ptr_type)gri_tluc8_trans_clut_scale_umap_init, (ptr_type)gri_tluc8_opaque_clut_scale_umap_init, + (ptr_type)gri_tluc8_trans_clut_scale_umap_init, + + gr_null, gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_tluc8_opaque_clut_lin_umap_init, /* Linear */ + (ptr_type)gri_tluc8_trans_clut_lin_umap_init, (ptr_type)gri_tluc8_opaque_clut_lin_umap_init, + (ptr_type)gri_tluc8_trans_clut_lin_umap_init, (ptr_type)gri_tluc8_opaque_clut_lin_umap_init, + (ptr_type)gri_tluc8_trans_clut_lin_umap_init, + + gr_null, /* Floor */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall2d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall1d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -hscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -vscan */ + gr_null, gr_null, gr_null, gr_null, gr_null + + }, + {/* xor fill type - from fl8xft.h */ + /* FILL_NORM */ + /* BMT_DEVICE */ + (ptr_type)flat8_xor_set_upixel, /* pixel primitve */ + gr_null, /* reserved wire poly line */ + gr_null, /* solid hline */ + + gr_null, /* bitmap blitter */ + gr_null, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + gr_null, /* Scalers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_poly_init, /* solid polygon primitives */ + gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Bilinear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Floor */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall2d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall1d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -hscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -vscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + /* BMT_MONO */ + gr_null, /* solid line */ + gr_null, /* solid wire poly line */ + gr_null, /* gouraud hline */ + + gr_null, /* bitmap blitter */ + gr_null, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + gr_null, /* Scalers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_spoly_init, /* gouraud polygon primitives */ + gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Linear */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Floor */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall2d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall1d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -hscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -vscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + /* BMT_FLAT8 */ + gr_null, /* gouraud line */ + gr_null, /* gouraud wire poly line */ + gr_null, /* rgb hline */ + + gr_null, /* bitmap blitter */ + gr_null, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + (ptr_type)gri_opaque_scale_umap_init, /* Scalers */ + (ptr_type)gri_trans_scale_umap_init, gr_null, gr_null, (ptr_type)gri_opaque_clut_scale_umap_init, + (ptr_type)gri_trans_clut_scale_umap_init, + + (ptr_type)gri_cpoly_init, /* RGB polygon primitives */ + gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_opaque_lin_umap_init, /* Linear */ + (ptr_type)gri_trans_lin_umap_init, (ptr_type)gri_opaque_lit_lin_umap_init, (ptr_type)gri_trans_lit_lin_umap_init, + (ptr_type)gri_opaque_clut_lin_umap_init, (ptr_type)gri_trans_clut_lin_umap_init, + + (ptr_type)gri_opaque_floor_umap_init, /* Floor */ + (ptr_type)gri_trans_floor_umap_init, (ptr_type)gri_opaque_lit_floor_umap_init, + (ptr_type)gri_trans_lit_floor_umap_init, (ptr_type)gri_opaque_clut_floor_umap_init, + (ptr_type)gri_trans_clut_floor_umap_init, + + (ptr_type)gri_opaque_wall_umap_init, /* Wall2d */ + (ptr_type)gri_trans_wall_umap_init, (ptr_type)gri_opaque_lit_wall_umap_init, + (ptr_type)gri_trans_lit_wall_umap_init, (ptr_type)gri_opaque_clut_wall_umap_init, + (ptr_type)gri_trans_clut_wall_umap_init, + + (ptr_type)gri_opaque_wall_umap_init, /* Wall1d */ + (ptr_type)gri_trans_wall_umap_init, (ptr_type)gri_opaque_lit_wall_umap_init, + (ptr_type)gri_trans_lit_wall_umap_init, (ptr_type)gri_opaque_clut_wall_umap_init, + (ptr_type)gri_trans_clut_wall_umap_init, + + (ptr_type)gri_opaque_per_umap_hscan_init, /* Perspective -hscan */ + (ptr_type)gri_trans_per_umap_hscan_init, (ptr_type)gri_opaque_lit_per_umap_hscan_init, + (ptr_type)gri_trans_lit_per_umap_hscan_init, (ptr_type)gri_opaque_clut_per_umap_hscan_init, + (ptr_type)gri_trans_clut_per_umap_hscan_init, + + (ptr_type)gri_opaque_per_umap_vscan_init, /* Perspective -vscan */ + (ptr_type)gri_trans_per_umap_vscan_init, (ptr_type)gri_opaque_lit_per_umap_vscan_init, + (ptr_type)gri_trans_lit_per_umap_vscan_init, (ptr_type)gri_opaque_clut_per_umap_vscan_init, + (ptr_type)gri_trans_clut_per_umap_vscan_init, + + /* BMT_FLAT24 */ + gr_null, /* rgb line */ + gr_null, /* rgb wire poly line */ + gr_null, /* solid vline */ + + gr_null, /* bitmap blitter */ + gr_null, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + gr_null, /* Scalers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_tpoly_init, /* translucent polygon primitives */ + gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Linear */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Floor */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall2d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall1d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -hscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -vscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + /* BMT_RSD8 */ + gr_null, /* translucent line */ + gr_null, /* translucent wire poly line */ + gr_null, /* gouraud vline */ + + (ptr_type)gri_gen_rsd8_ubitmap, /* bitmap blitter */ + (ptr_type)gri_gen_rsd8_bitmap, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + (ptr_type)rsd8_tm_init, /* Scalers */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)gri_stpoly_init, /* shaded translucent polygon primitives */ + gr_null, + + (ptr_type)rsd8_tm_init, /* Linear mappers */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)rsd8_tm_init, /* Linear */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)rsd8_tm_init, /* Floor */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)rsd8_tm_init, /* Wall2d */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)rsd8_tm_init, /* Wall1d */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)rsd8_pm_init, /* Perspective -hscan */ + (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, + (ptr_type)rsd8_pm_init, + + (ptr_type)rsd8_pm_init, /* Perspective -vscan */ + (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, + (ptr_type)rsd8_pm_init, + + /* BMT_TLUC8 */ + gr_null, /* shaded translucent line */ + gr_null, /* shaded translucent wire poly line */ + gr_null, /* rgb vline */ + + gr_null, /* bitmap blitter */ + gr_null, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + (ptr_type)gri_tluc8_opaque_scale_umap_init, /* Scalers */ + (ptr_type)gri_tluc8_trans_scale_umap_init, gr_null, gr_null, (ptr_type)gri_tluc8_opaque_clut_scale_umap_init, + (ptr_type)gri_tluc8_trans_clut_scale_umap_init, + + gr_null, gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_tluc8_opaque_lin_umap_init, /* Linear */ + (ptr_type)gri_tluc8_trans_lin_umap_init, gr_null, gr_null, (ptr_type)gri_tluc8_opaque_clut_lin_umap_init, + (ptr_type)gri_tluc8_trans_clut_lin_umap_init, + + gr_null, /* Floor */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall2d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall1d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -hscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -vscan */ + gr_null, gr_null, gr_null, gr_null, gr_null}, + {/* blend fill type - from fl8bft.h */ + /* FILL_NORM */ + /* BMT_DEVICE */ + (ptr_type)flat8_blend_set_upixel, /* pixel primitve */ + gr_null, /* reserved wire poly line */ + gr_null, /* solid hline */ + + gr_null, /* bitmap blitter */ + gr_null, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + gr_null, /* Scalers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_poly_init, /* solid polygon primitives */ + gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Bilinear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Floor */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall2d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall1d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -hscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -vscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + /* BMT_MONO */ + gr_null, /* solid line */ + gr_null, /* solid wire poly line */ + gr_null, /* gouraud hline */ + + gr_null, /* bitmap blitter */ + gr_null, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + gr_null, /* Scalers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_spoly_init, /* gouraud polygon primitives */ + gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Linear */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Floor */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall2d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall1d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -hscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -vscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + /* BMT_FLAT8 */ + gr_null, /* gouraud line */ + gr_null, /* gouraud wire poly line */ + gr_null, /* rgb hline */ + + (ptr_type)flat8_flat8_ubitmap, /* bitmap blitter */ + (ptr_type)gri_flat8_mask_bitmap, /* stencil clipped bitmap blitter */ + (ptr_type)gri_flat8_clut_ubitmap, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + (ptr_type)gri_opaque_scale_umap_init, /* Scalers */ + (ptr_type)gri_trans_scale_umap_init, gr_null, gr_null, (ptr_type)gri_opaque_clut_scale_umap_init, + (ptr_type)gri_trans_clut_scale_umap_init, + + (ptr_type)gri_cpoly_init, /* RGB polygon primitives */ + gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_opaque_lin_umap_init, /* Linear */ + (ptr_type)gri_trans_lin_umap_init, (ptr_type)gri_opaque_lit_lin_umap_init, (ptr_type)gri_trans_lit_lin_umap_init, + (ptr_type)gri_opaque_clut_lin_umap_init, (ptr_type)gri_trans_clut_lin_umap_init, + + (ptr_type)gri_opaque_floor_umap_init, /* Floor */ + (ptr_type)gri_trans_floor_umap_init, (ptr_type)gri_opaque_lit_floor_umap_init, + (ptr_type)gri_trans_lit_floor_umap_init, (ptr_type)gri_opaque_clut_floor_umap_init, + (ptr_type)gri_trans_clut_floor_umap_init, + + (ptr_type)gri_opaque_wall_umap_init, /* Wall2d */ + (ptr_type)gri_trans_wall_umap_init, (ptr_type)gri_opaque_lit_wall_umap_init, + (ptr_type)gri_trans_lit_wall_umap_init, (ptr_type)gri_opaque_clut_wall_umap_init, + (ptr_type)gri_trans_clut_wall_umap_init, + + (ptr_type)gri_opaque_wall_umap_init, /* Wall1d */ + (ptr_type)gri_trans_wall_umap_init, (ptr_type)gri_opaque_lit_wall_umap_init, + (ptr_type)gri_trans_lit_wall_umap_init, (ptr_type)gri_opaque_clut_wall_umap_init, + (ptr_type)gri_trans_clut_wall_umap_init, + + (ptr_type)gri_opaque_per_umap_hscan_init, /* Perspective -hscan */ + (ptr_type)gri_trans_per_umap_hscan_init, (ptr_type)gri_opaque_lit_per_umap_hscan_init, + (ptr_type)gri_trans_lit_per_umap_hscan_init, (ptr_type)gri_opaque_clut_per_umap_hscan_init, + (ptr_type)gri_trans_clut_per_umap_hscan_init, + + (ptr_type)gri_opaque_per_umap_vscan_init, /* Perspective -vscan */ + (ptr_type)gri_trans_per_umap_vscan_init, (ptr_type)gri_opaque_lit_per_umap_vscan_init, + (ptr_type)gri_trans_lit_per_umap_vscan_init, (ptr_type)gri_opaque_clut_per_umap_vscan_init, + (ptr_type)gri_trans_clut_per_umap_vscan_init, + + /* BMT_FLAT24 */ + gr_null, /* rgb line */ + gr_null, /* rgb wire poly line */ + gr_null, /* solid vline */ + + gr_null, /* bitmap blitter */ + gr_null, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + gr_null, /* Scalers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_tpoly_init, /* translucent polygon primitives */ + gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Linear */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Floor */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall2d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall1d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -hscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -vscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + /* BMT_RSD8 */ + gr_null, /* translucent line */ + gr_null, /* translucent wire poly line */ + gr_null, /* gouraud vline */ + + (ptr_type)gri_gen_rsd8_ubitmap, /* bitmap blitter */ + (ptr_type)gri_gen_rsd8_bitmap, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + (ptr_type)rsd8_tm_init, /* Scalers */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)gri_stpoly_init, /* shaded translucent polygon primitives */ + gr_null, + + (ptr_type)rsd8_tm_init, /* Linear mappers */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)rsd8_tm_init, /* Linear */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)rsd8_tm_init, /* Floor */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)rsd8_tm_init, /* Wall2d */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)rsd8_tm_init, /* Wall1d */ + (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, (ptr_type)rsd8_tm_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)rsd8_pm_init, /* Perspective -hscan */ + (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, + (ptr_type)rsd8_pm_init, + + (ptr_type)rsd8_pm_init, /* Perspective -vscan */ + (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, (ptr_type)rsd8_pm_init, + (ptr_type)rsd8_pm_init, + + /* BMT_TLUC8 */ + gr_null, /* shaded translucent line */ + gr_null, /* shaded translucent wire poly line */ + gr_null, /* rgb vline */ + + gr_null, /* bitmap blitter */ + gr_null, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + (ptr_type)gri_tluc8_opaque_scale_umap_init, /* Scalers */ + (ptr_type)gri_tluc8_trans_scale_umap_init, gr_null, gr_null, (ptr_type)gri_tluc8_opaque_clut_scale_umap_init, + (ptr_type)gri_tluc8_trans_clut_scale_umap_init, + + gr_null, gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_tluc8_opaque_lin_umap_init, /* Linear */ + (ptr_type)gri_tluc8_trans_lin_umap_init, gr_null, gr_null, (ptr_type)gri_tluc8_opaque_clut_lin_umap_init, + (ptr_type)gri_tluc8_trans_clut_lin_umap_init, + + gr_null, /* Floor */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall2d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall1d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -hscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -vscan */ + gr_null, gr_null, gr_null, gr_null, gr_null}, + {/* solid fill type - from fl8sft.h */ + /* FILL_SOLID */ + /* BMT_DEVICE */ + (ptr_type)flat8_solid_set_upixel, /* pixel primitve */ + gr_null, /* reserved wire poly line */ + gr_null, /* solid hline */ + + gr_null, /* bitmap blitter */ + gr_null, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + gr_null, /* Scalers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_solid_poly_init, /* solid polygon primitives */ + gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Bilinear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Floor */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall2d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall1d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -hscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -vscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + /* BMT_MONO */ + gr_null, /* solid line */ + gr_null, /* solid wire poly line */ + gr_null, /* gouraud hline */ + + gr_null, /* bitmap blitter */ + gr_null, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + gr_null, /* Scalers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_solid_poly_init, /* gouraud polygon primitives */ + gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Linear */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Floor */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall2d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall1d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -hscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -vscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + /* BMT_FLAT8 */ + gr_null, /* gouraud line */ + gr_null, /* gouraud wire poly line */ + gr_null, /* rgb hline */ + + gr_null, /* bitmap blitter */ + gr_null, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + (ptr_type)gri_opaque_solid_scale_umap_init, /* Scalers */ + (ptr_type)gri_trans_solid_scale_umap_init, (ptr_type)gri_opaque_solid_scale_umap_init, + (ptr_type)gri_trans_solid_scale_umap_init, (ptr_type)gri_opaque_solid_scale_umap_init, + (ptr_type)gri_trans_solid_scale_umap_init, + + (ptr_type)gri_solid_poly_init, /* RGB polygon primitives */ + gr_null, + + (ptr_type)gri_solid_poly_init, /* Linear mappers */ + gr_null, (ptr_type)gri_solid_poly_init, gr_null, (ptr_type)gri_solid_poly_init, gr_null, + + (ptr_type)gri_solid_poly_init, /* Bilinear */ + (ptr_type)gri_trans_solid_lin_umap_init, (ptr_type)gri_solid_poly_init, (ptr_type)gri_trans_solid_lin_umap_init, + (ptr_type)gri_solid_poly_init, (ptr_type)gri_trans_solid_lin_umap_init, + + (ptr_type)gri_solid_poly_init, /* Floor */ + (ptr_type)gri_trans_solid_floor_umap_init, (ptr_type)gri_solid_poly_init, + (ptr_type)gri_trans_solid_floor_umap_init, (ptr_type)gri_solid_poly_init, + (ptr_type)gri_trans_solid_floor_umap_init, + + (ptr_type)gri_solid_poly_init, /* Wall2d */ + (ptr_type)gri_trans_solid_wall_umap_init, (ptr_type)gri_solid_poly_init, (ptr_type)gri_trans_solid_wall_umap_init, + (ptr_type)gri_solid_poly_init, (ptr_type)gri_trans_solid_wall_umap_init, + + (ptr_type)gri_solid_poly_init, /* Wall1d */ + (ptr_type)gri_trans_solid_wall_umap_init, (ptr_type)gri_solid_poly_init, (ptr_type)gri_trans_solid_wall_umap_init, + (ptr_type)gri_solid_poly_init, (ptr_type)gri_trans_solid_wall_umap_init, + + (ptr_type)gri_solid_poly_init, /* Perspective -hscan */ + (ptr_type)gri_trans_solid_per_umap_hscan_init, (ptr_type)gri_solid_poly_init, + (ptr_type)gri_trans_solid_per_umap_hscan_init, (ptr_type)gri_solid_poly_init, + (ptr_type)gri_trans_solid_per_umap_hscan_init, + + (ptr_type)gri_solid_poly_init, /* Perspective -vscan */ + (ptr_type)gri_trans_solid_per_umap_vscan_init, (ptr_type)gri_solid_poly_init, + (ptr_type)gri_trans_solid_per_umap_vscan_init, (ptr_type)gri_solid_poly_init, + (ptr_type)gri_trans_solid_per_umap_vscan_init, + + /* BMT_FLAT24 */ + gr_null, /* rgb line */ + gr_null, /* rgb wire poly line */ + gr_null, /* solid vline */ + + gr_null, /* bitmap blitter */ + gr_null, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + gr_null, /* Scalers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_solid_poly_init, /* translucent polygon primitives */ + gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Linear */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Floor */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall2d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall1d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -hscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -vscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + /* BMT_RSD8 */ + gr_null, /* translucent line */ + gr_null, /* translucent wire poly line */ + gr_null, /* gouraud vline */ + + (ptr_type)gri_gen_rsd8_ubitmap, /* bitmap blitter */ + (ptr_type)gri_gen_rsd8_bitmap, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + (ptr_type)gri_opaque_solid_scale_umap_init, (ptr_type)rsd8_tm_init, (ptr_type)gri_opaque_solid_scale_umap_init, + (ptr_type)rsd8_tm_init, (ptr_type)gri_opaque_solid_scale_umap_init, (ptr_type)rsd8_tm_init, + + (ptr_type)gri_solid_poly_init, /* shaded translucent polygon primitives */ + gr_null, + + (ptr_type)gri_solid_poly_init, /* Linear mappers */ + (ptr_type)rsd8_tm_init, (ptr_type)gri_solid_poly_init, (ptr_type)rsd8_tm_init, (ptr_type)gri_solid_poly_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)gri_solid_poly_init, /* Linear */ + (ptr_type)rsd8_tm_init, (ptr_type)gri_solid_poly_init, (ptr_type)rsd8_tm_init, (ptr_type)gri_solid_poly_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)gri_solid_poly_init, /* Floor */ + (ptr_type)rsd8_tm_init, (ptr_type)gri_solid_poly_init, (ptr_type)rsd8_tm_init, (ptr_type)gri_solid_poly_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)gri_solid_poly_init, /* Wall2d */ + (ptr_type)rsd8_tm_init, (ptr_type)gri_solid_poly_init, (ptr_type)rsd8_tm_init, (ptr_type)gri_solid_poly_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)gri_solid_poly_init, /* Wall1d */ + (ptr_type)rsd8_tm_init, (ptr_type)gri_solid_poly_init, (ptr_type)rsd8_tm_init, (ptr_type)gri_solid_poly_init, + (ptr_type)rsd8_tm_init, + + (ptr_type)gri_solid_poly_init, /* Perspective -hscan */ + (ptr_type)rsd8_pm_init, (ptr_type)gri_solid_poly_init, (ptr_type)rsd8_pm_init, (ptr_type)gri_solid_poly_init, + (ptr_type)rsd8_pm_init, + + (ptr_type)gri_solid_poly_init, /* Perspective -vscan */ + (ptr_type)rsd8_pm_init, (ptr_type)gri_solid_poly_init, (ptr_type)rsd8_pm_init, (ptr_type)gri_solid_poly_init, + (ptr_type)rsd8_pm_init, + + /* BMT_TLUC8 */ + gr_null, /* shaded translucent line */ + gr_null, /* shaded translucent wire poly line */ + gr_null, /* rgb vline */ + + gr_null, /* bitmap blitter */ + gr_null, /* stencil clipped bitmap blitter */ + gr_null, /* clut bitmap blitter */ + + gr_null, /* hflipped bitmap blitter */ + gr_null, /* clut hflipped bitmap blitter */ + + gr_null, /* masked bitmap blitter */ + + gr_null, /* horizontal bitmap doubler */ + gr_null, /* vertical bitmap doubler */ + gr_null, /* horizontal and vertical bitmap doubler */ + + gr_null, /* blended horizontal bitmap doubler */ + gr_null, /* blended vertical bitmap doubler */ + gr_null, /* blended horizontal and vertical bitmap doubler */ + + (ptr_type)gri_opaque_solid_scale_umap_init, /* Scalers */ + (ptr_type)gri_trans_solid_scale_umap_init, (ptr_type)gri_opaque_solid_scale_umap_init, + (ptr_type)gri_trans_solid_scale_umap_init, (ptr_type)gri_opaque_solid_scale_umap_init, + (ptr_type)gri_trans_solid_scale_umap_init, + + gr_null, gr_null, + + gr_null, /* Linear mappers */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + (ptr_type)gri_solid_poly_init, /* Linear */ + (ptr_type)gri_trans_solid_lin_umap_init, (ptr_type)gri_solid_poly_init, (ptr_type)gri_trans_solid_lin_umap_init, + (ptr_type)gri_solid_poly_init, (ptr_type)gri_trans_solid_lin_umap_init, + + gr_null, /* Floor */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall2d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Wall1d */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -hscan */ + gr_null, gr_null, gr_null, gr_null, gr_null, + + gr_null, /* Perspective -vscan */ + gr_null, gr_null, gr_null, gr_null, gr_null}}; diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8g24.c b/engine/src/Libraries/2D/Source/Flat8/fl8g24.c new file mode 100644 index 0000000..cc7c336 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8g24.c @@ -0,0 +1,71 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8g24.c $ + * $Revision: 1.4 $ + * $Author: kevin $ + * $Date: 1994/10/17 14:59:58 $ + * + * Routines for reading 24-bit pixels from a flat 8 canvas. + * + * This file is part of the 2d library. + * + * $Log: fl8g24.c $ + * Revision 1.4 1994/10/17 14:59:58 kevin + * Use palette macros in preparation for switch to palette globals. + * + * Revision 1.3 1993/10/19 09:50:22 kaboom + * Replaced #include bm.bits + grd_canvas->bm.row * y + x; + i = *p; + r = (long *)(grd_pal + 3 * i); + return *r & 0x00ffffff; +} + +/* set a clipped pixel in bank-switched memory. return the clip code. */ +long flat8_get_pixel24(short x, short y) { + uchar *p; + long *r; + int i; + + if (x < grd_clip.left || x >= grd_clip.right || y < grd_clip.top || y >= grd_clip.bot) + return CLIP_ALL; + p = grd_canvas->bm.bits + grd_canvas->bm.row * y + x; + i = *p; + r = (long *)(grd_pal + 3 * i); + return *r & 0x00ffffff; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8gfl8.c b/engine/src/Libraries/2D/Source/Flat8/fl8gfl8.c new file mode 100644 index 0000000..78a1645 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8gfl8.c @@ -0,0 +1,59 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/fl8gfl8.c $ + * $Revision: 1.3 $ + * $Author: kaboom $ + * $Date: 1993/10/19 09:50:23 $ + * + * Routines for reading flat 8 bitmaps from a flat 8 canvas. + * + * This file is part of the 2d library. + * + * $Log: fl8gfl8.c $ + * Revision 1.3 1993/10/19 09:50:23 kaboom + * Replaced #include + +void flat8_get_flat8_ubitmap(grs_bitmap *bm, short x, short y) { + uchar *src; + uchar *dst; + short h = bm->h; + short w = bm->w; + ushort brow = bm->row; + ushort grow = grd_bm.row; + + src = grd_bm.bits + grow * y + x; + dst = bm->bits; + while (h--) { + LG_memmove(dst, src, w); + src += grow; + dst += brow; + } +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8gpix.c b/engine/src/Libraries/2D/Source/Flat8/fl8gpix.c new file mode 100644 index 0000000..61b696d --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8gpix.c @@ -0,0 +1,60 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/fl8gpix.c $ + * $Revision: 1.3 $ + * $Author: kaboom $ + * $Date: 1993/10/19 09:50:32 $ + * + * Routines for reading pixels from a flat 8 canvas. + * + * This file is part of the 2d library. + * + * $Log: fl8gpix.c $ + * Revision 1.3 1993/10/19 09:50:32 kaboom + * Replaced #include = grd_clip.right || y < grd_clip.top || y >= grd_clip.bot) + return -1; + + p = grd_bm.bits + grd_bm.row * y + x; + return (long)*p; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8hfl8.c b/engine/src/Libraries/2D/Source/Flat8/fl8hfl8.c new file mode 100644 index 0000000..11c4444 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8hfl8.c @@ -0,0 +1,63 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/fl8hfl8.c $ + * $Revision: 1.3 $ + * $Author: kaboom $ + * $Date: 1993/10/19 09:50:33 $ + * + * flat 8 bitmap horizontal flip routine. + * + * This file is part of the 2d library. + * + * $Log: fl8hfl8.c $ + * Revision 1.3 1993/10/19 09:50:33 kaboom + * Replaced #include row; + ushort grow = grd_bm.row; + short bw = bm->w; + + h = bm->h; + src = bm->bits; + dst = grd_bm.bits + y * grow + x + bw - 1; + while (h--) { + w = bw; + while (w--) + *dst-- = *src++; + src += brow - bw; + dst += grow + bw; + } +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8hlin.c b/engine/src/Libraries/2D/Source/Flat8/fl8hlin.c new file mode 100644 index 0000000..ba83f25 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8hlin.c @@ -0,0 +1,124 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8hlin.c $ + * $Revision: 1.8 $ + * $Author: lmfeeney $ + * $Date: 1994/08/12 01:09:33 $ + * + * Routines for horizontal drawing lines into a flat 8 canvas. + * + * This file is part of the 2d library. + * + * $Log: fl8hlin.c $ + * Revision 1.8 1994/08/12 01:09:33 lmfeeney + * get fill/solid from right place + * + * Revision 1.7 1994/06/14 00:04:22 lmfeeney + * fixed stupid error in xor lines + * + * Revision 1.6 1994/06/11 01:46:09 lmfeeney + * unclipped flat8 line drawers routines for each fill type + * canvas values as parameters + * + * Revision 1.5 1993/10/19 09:50:34 kaboom + * Replaced #include + +/* draw an unclipped horizontal line with integral coordinates. */ + +// MLA #pragma off (unreferenced) + +/* clut should be in _ns everywhere, it doesn't need its own function */ + +void gri_flat8_uhline_ns(short x0, short y0, short x1, long c, long parm) { + uchar *p; + short t; + + if (x0 > x1) { + t = x0; + x0 = x1; + x1 = t; + } + if (gr_get_fill_type() == FILL_SOLID) + c = (uchar)parm; + p = grd_bm.bits + y0 * grd_bm.row + x0; + memset(p, c, x1 - x0 + 1); +} + +void gri_flat8_uhline_clut(short x0, short y0, short x1, long c, long parm) { + uchar *p; + short t; + + if (x0 > x1) { + t = x0; + x0 = x1; + x1 = t; + } + + c = (long)(((uchar *)parm)[c]); + p = grd_bm.bits + y0 * grd_bm.row + x0; + memset(p, c, x1 - x0 + 1); +} + +void gri_flat8_uhline_xor(short x0, short y0, short x1, long c, long parm) { + uchar *p; + short t; + + if (x0 > x1) { + t = x0; + x0 = x1; + x1 = t; + } + + for (p = grd_bm.bits + y0 * grd_bm.row + x0; x0 <= x1; p++, x0++) + *p = *p ^ c; +} + +/* punt */ +void gri_flat8_uhline_blend(short x0, short y0, short x1, long c, long parm) { + uchar *p; + short t; + + if (x0 > x1) { + t = x0; + x0 = x1; + x1 = t; + } + p = grd_bm.bits + y0 * grd_bm.row + x0; + memset(p, c, x1 - x0 + 1); +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8lf.c b/engine/src/Libraries/2D/Source/Flat8/fl8lf.c new file mode 100644 index 0000000..2f718ca --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8lf.c @@ -0,0 +1,200 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/genlf.c $ + * $Revision: 1.3 $ + * $Author: kevin $ + * $Date: 1994/08/16 12:50:14 $ + * + * Routines to floor texture map a flat8 bitmap to a generic canvas. + * + * This file is part of the 2d library. + * + */ + +#include "cnvdat.h" +#include "fl8tf.h" +#include "fl8tmapdv.h" +#include "gente.h" +#include "poly.h" +#include "scrmac.h" +#include "tmapint.h" +#include "vtab.h" + +int gri_lit_floor_umap_loop(grs_tmap_loop_info *tli); + +int gri_lit_floor_umap_loop(grs_tmap_loop_info *tli) { + +#if InvDiv + fix inv = fix_div(fix_make(1, 0), tli->w); + fix u = fix_mul_asm_safe(tli->left.u, inv); + fix du = fix_mul_asm_safe(tli->right.u, inv) - u; + fix v = fix_mul_asm_safe(tli->left.v, inv); + fix dv = fix_mul_asm_safe(tli->right.v, inv) - v; + fix i = fix_mul_asm_safe(tli->left.i, inv); + fix di = fix_mul_asm_safe(tli->right.i, inv) - i; +#else + fix u = fix_div(tli->left.u, tli->w); + fix du = fix_div(tli->right.u, tli->w) - u; + fix v = fix_div(tli->left.v, tli->w); + fix dv = fix_div(tli->right.v, tli->w) - v; + fix i = fix_div(tli->left.i, tli->w); + fix di = fix_div(tli->right.i, tli->w) - i; +#endif + + ulong t_mask = tli->mask; + uchar t_wlog = tli->bm.wlog; + uchar *g_ltab = grd_screen->ltab; + int32_t *t_vtab = tli->vtab; + uchar *t_bits = tli->bm.bits; + + do { + fix dx = tli->right.x - tli->left.x; + if (dx > 0) + { + +#if InvDiv + inv = fix_div(fix_make(1, 0) << 8, dx); + di = fix_mul_asm_safe_light(di, inv); + inv >>= 8; + du = fix_mul_asm_safe(du, inv); + dv = fix_mul_asm_safe(dv, inv); +#else + du = fix_div(du, dx); + dv = fix_div(dv, dx); + di = fix_div(di, dx); +#endif + + fix d = fix_ceil(tli->left.x) - tli->left.x; + u += fix_mul(du, d); + v += fix_mul(dv, d); + i += fix_mul(di, d); + + uchar *p_dest = grd_bm.bits + (grd_bm.row * tli->y) + fix_cint(tli->left.x); + + int x = fix_cint(tli->right.x) - fix_cint(tli->left.x); + + switch (tli->bm.hlog) { + case GRL_OPAQUE: + for (; x > 0; x--) { + int k = t_vtab[fix_fint(v)] + fix_fint(u); + *(p_dest++) = g_ltab[t_bits[k] + fix_light(i)]; + // gr_fill_upixel(g_ltab[t_bits[k]+fix_light(i)],x,t_y); + } + break; + + case GRL_TRANS: + for (; x > 0; x--) { + int k = t_vtab[fix_fint(v)] + fix_fint(u); + k = t_bits[k]; + if (k != 0) *p_dest = g_ltab[k + fix_light(i)]; + // gr_fill_upixel(g_ltab[k+fix_light(i)],x,t_y); + p_dest++; + u += du; + v += dv; + i += di; + } + break; + + case GRL_OPAQUE|GRL_LOG2: + for (; x > 0; x--) { + int k = ((fix_fint(v)< 0; x--) { + int k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + k = t_bits[k]; + if (k != 0) *p_dest = g_ltab[k + fix_light(i)]; + // gr_fill_upixel(g_ltab[k+fix_light(i)],x,t_y); + p_dest++; + u += du; + v += dv; + i += di; + } + break; + } + } + + tli->w += tli->dw; + +#if InvDiv + inv = fix_div(fix_make(1, 0), tli->w); + u = fix_mul_asm_safe((tli->left.u += tli->left.du), inv); + tli->right.u += tli->right.du; + du = fix_mul_asm_safe(tli->right.u, inv) - u; + v = fix_mul_asm_safe((tli->left.v += tli->left.dv), inv); + tli->right.v += tli->right.dv; + dv = fix_mul_asm_safe(tli->right.v, inv) - v; + i = fix_mul_asm_safe((tli->left.i += tli->left.di), inv); + tli->right.i += tli->right.di; + di = fix_mul_asm_safe(tli->right.i, inv) - i; +#else + u = fix_div((tli->left.u += tli->left.du), tli->w); + tli->right.u += tli->right.du; + du = fix_div(tli->right.u, tli->w) - u; + v = fix_div((tli->left.v += tli->left.dv), tli->w); + tli->right.v += tli->right.dv; + dv = fix_div(tli->right.v, tli->w) - v; + i = fix_div((tli->left.i += tli->left.di), tli->w); + tli->right.i += tli->right.di; + di = fix_div(tli->right.i, tli->w) - i; +#endif + + tli->left.x += tli->left.dx; + tli->right.x += tli->right.dx; + + tli->y++; + + } while (--(tli->n) > 0); + + return FALSE; // tmap OK +} + +void gri_trans_lit_floor_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_TRANS | GRL_LOG2; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_TRANS; + } + tli->loop_func = (void (*)())gri_lit_floor_umap_loop; + tli->left_edge_func = (void (*)())gri_uviwx_edge; + tli->right_edge_func = (void (*)())gri_uviwx_edge; +} + +void gri_opaque_lit_floor_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_OPAQUE | GRL_LOG2; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_OPAQUE; + } + tli->loop_func = (void (*)())gri_lit_floor_umap_loop; + tli->left_edge_func = (void (*)())gri_uviwx_edge; + tli->right_edge_func = (void (*)())gri_uviwx_edge; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8lin.c b/engine/src/Libraries/2D/Source/Flat8/fl8lin.c new file mode 100644 index 0000000..9c47677 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8lin.c @@ -0,0 +1,241 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8lin.c $ + * $Revision: 1.11 $ + * $Author: lmfeeney $ + * $Date: 1994/08/12 01:09:59 $ + * + * Routines for drawing fixed-point lines onto a flat 8 canvas. + * + * This file is part of the 2d library. + * + * $Log: fl8lin.c $ + * Revision 1.11 1994/08/12 01:09:59 lmfeeney + * get fill/solid parm from right place + * + * Revision 1.10 1994/06/11 01:22:44 lmfeeney + * guts of the routine moved to fl8{c,s}lin.h, per fill type + * line drawers are created by defining macros and including + * this file + * + * Revision 1.9 1994/05/06 18:18:58 lmfeeney + * rewritten for greater accuracy + * + * Revision 1.8 1993/12/15 11:25:22 kaboom + * Fixed up problems with not including endpoints and to match up with + * new polygon scanner. + * + * Revision 1.7 1993/10/19 09:50:35 kaboom + * Replaced #include "grd.h" with new headers split from grd.h. + * + * Revision 1.6 1993/10/08 01:15:18 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.5 1993/06/23 04:56:07 kaboom + * Put in checks for single-pixel horizontal and vertical lines. + * + * Revision 1.4 1993/06/22 15:14:38 kaboom + * Now checks to see if final span is empty. + * + * Revision 1.3 1993/06/16 18:15:24 kaboom + * Removed foolish mprintfs accidentally left in. + * + * Revision 1.2 1993/06/16 01:59:53 kaboom + * Fixed gradual precision error. Last span now explicitly set to x1 + * instead of adding m_inv. + * + * Revision 1.1 1993/03/02 20:33:50 kaboom + * Initial revision + */ + +#include "cnvdat.h" +#include "ctxmac.h" +#include "fill.h" +#include "fix.h" +#include "lg.h" +#include "plytyp.h" +#include "scrdat.h" +#include + +/* This particular mess implements the fix_uline for each of the + five fill types. The main driver (essentially the code in the + the 'original' fl8lin.c) is now a code fragment in fl8lin.h + + For each function, four macros are (re)defined - flat8_pixel_fill_xf + and flat8_pixel_fill_xi - which set the pixel value in the case of + x fix-point and x known to be integer, and flat8_pixel_fill_row, which + allows us to retain the speed hack for a (nearly) horizontal line. For + fill types which are indep of the pixel value (norm, solid, clut), the + color is set only once in flat8_pixel_fill_init. + + Note that each macro is referenced 7 times (the line drawer has + lots of dx <=> dy type cases). This makes blend come out huge. + + None of these macros take arguments, instead they rely on secret + gnosis of the variable names in fl8lin.h +*/ + +/* not all line fill functions use all their parameters */ +// MLA #pragma off (unreferenced) + +/* same for norm, solid and clut */ + +#undef flat8_pixel_fill_xf +#define flat8_pixel_fill_xf \ + do { \ + p[fix_fint(x0)] = c; \ + } while (0) + +#undef flat8_pixel_fill_xi +#define flat8_pixel_fill_xi \ + do { \ + p[x0] = c; \ + } while (0) + +#undef flat8_pixel_fill_row +#define flat8_pixel_fill_row \ + do { \ + LG_memset(p + x0, c, x1 - x0 + 1); \ + } while (0) + +#undef flat8_pixel_fill_init +#define flat8_pixel_fill_init \ + do { \ + if (gr_get_fill_type() == FILL_SOLID) \ + c = (uchar)parm; \ + } while (0) + +/* norm */ + +void gri_flat8_uline_ns(long c, long parm, grs_vertex *v0, grs_vertex *v1) { +#include "fl8lin.h" +} + + /* clut */ + +#undef flat8_pixel_fill_init +#define flat8_pixel_fill_init \ + do { \ + c = (long)(((uchar *)parm)[c]); \ + } while (0) + +void gri_flat8_uline_clut(long c, long parm, grs_vertex *v0, grs_vertex *v1) { +#include "fl8lin.h" +} + + /* xor */ + +#undef flat8_pixel_fill_xf +#define flat8_pixel_fill_xf \ + do { \ + p[fix_fint(x0)] = c ^ p[fix_fint(x0)]; \ + } while (0) + +#undef flat8_pixel_fill_xi +#define flat8_pixel_fill_xi \ + do { \ + p[x0] = c ^ p[x0]; \ + } while (0) + +#undef flat8_pixel_fill_row +#define flat8_pixel_fill_row \ + do { \ + while (x0 < x1) { \ + flat8_pixel_fill_xi; \ + x0++; \ + } \ + } while (0) + +void gri_flat8_uline_xor(long c, long parm, grs_vertex *v0, grs_vertex *v1) { +#include "fl8lin.h" +} + + /* blend -- maybe we should just swallow the function call */ + +#define QMASK 0x3fc7f8ff +/* convert red in a glomped rgb to a fixed point */ +#define rtof(b) (((b)&0x3ff) << 12) +/* convert green in a glomped rgb to a fixed point */ +#define gtof(b) (((b)&0x1ff800) << 1) +/* convert blue in a glomped rgb to a fixed point */ +#define btof(b) (((b)&0xffc00000) >> 10) + +#undef flat8_pixel_fill_xf +#define flat8_pixel_fill_xf \ + do { \ + uchar *k; \ + grs_rgb prev; \ + grs_rgb lg_new; \ + fix r1, g1, b1; \ + \ + prev = grd_bpal[p[fix_fint(x0)]]; \ + lg_new = grd_bpal[c]; \ + \ + r1 = fix_mul(rtof(lg_new), (fix)parm) + fix_mul(rtof(prev), FIX_UNIT - (fix)parm); \ + g1 = fix_mul(gtof(lg_new), (fix)parm) + fix_mul(gtof(prev), FIX_UNIT - (fix)parm); \ + b1 = fix_mul(btof(lg_new), (fix)parm) + fix_mul(btof(prev), FIX_UNIT - (fix)parm); \ + \ + k = grd_ipal; \ + k += (r1 >> 17) & 0x1f; \ + k += (g1 >> 12) & 0x3e0; \ + k += (b1 >> 7) & 0x7c00; \ + p[fix_fint(x0)] = *k; \ + } while (0) + +#undef flat8_pixel_fill_xi +#define flat8_pixel_fill_xi \ + do { \ + uchar *k; \ + grs_rgb prev; \ + grs_rgb lg_new; \ + fix r1, g1, b1; \ + \ + prev = grd_bpal[p[x0]]; \ + lg_new = grd_bpal[c]; \ + \ + r1 = fix_mul(rtof(lg_new), (fix)parm) + fix_mul(rtof(prev), FIX_UNIT - (fix)parm); \ + g1 = fix_mul(gtof(lg_new), (fix)parm) + fix_mul(gtof(prev), FIX_UNIT - (fix)parm); \ + b1 = fix_mul(btof(lg_new), (fix)parm) + fix_mul(btof(prev), FIX_UNIT - (fix)parm); \ + \ + k = grd_ipal; \ + k += (r1 >> 17) & 0x1f; \ + k += (g1 >> 12) & 0x3e0; \ + k += (b1 >> 7) & 0x7c00; \ + p[x0] = *k; \ + } while (0) + +#undef flat8_pixel_fill_row +#define flat8_pixel_fill_row \ + do { \ + while (x0 < x1) { \ + flat8_pixel_fill_xi; \ + x0++; \ + } \ + } while (0) + +#undef flat8_pixel_fill_init +#define flat8_pixel_fill_init \ + do { \ + ; \ + } while (0) + +void gri_flat8_uline_blend(long c, long parm, grs_vertex *v0, grs_vertex *v1) { +#include "fl8lin.h" +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8lin.h b/engine/src/Libraries/2D/Source/Flat8/fl8lin.h new file mode 100644 index 0000000..6cd02af --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8lin.h @@ -0,0 +1,194 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/fl8lin.h $ + * $Revision: 1.1 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 00:51:50 $ + */ + +/* this was originally the guts of flat8_fix_uline */ + +fix x0, y0, x1, y1; +fix dx, dy; /* deltas in x and y */ +fix t; /* temporary fix */ + +uchar *p; /* pointer into canvas */ + +x0 = v0->x; +y0 = v0->y; +x1 = v1->x; +y1 = v1->y; + +/* set endpoints + note that this cannot go negative or change octant, since the == + case is excluded */ + +if (x0 < x1) { + x1 -= 1; /* e.g. - epsilon */ +} else if (x0 > x1) { + x0 -= 1; +} + +if (y0 < y1) { + y1 -= 1; +} else if (y0 > y1) { + y0 -= 1; +} + +dx = fix_trunc(x1) - fix_trunc(x0); /* x extent in pixels, (macro is flakey) */ +dx = fix_abs(dx); +dy = fix_trunc(y1) - fix_trunc(y0); /* y extent in pixels */ +dy = fix_abs(dy); + +if (dx == 0 && dy == 0) + return; + +flat8_pixel_fill_init; + +/* three cases: absolute value dx < = > dy + + along the longer dimension, the fixpoint x0 (or y0) is treated + as an int + + the points are swapped if needed and the rgb initial and deltas + are calculated accordingly + + there are two or three sub-cases - a horizontal or vertical line, + and the dx or dy being added or subtracted. dx and dy + are kept as absolute values and +/- is managed in + two separate inner loops if it is a y change, since you need to + manage the canvas pointer + + if y is being changed by 'dy' and x is being incremented, do a + FunkyBitCheck (TM) to see whether the integer part of y has changed + and if it has, resetting the the canvas pointer to the next row + + if x is being changed by 'dx' and y is being incremented, just + add or subtract row to increment y in the canvas + + the endpoints are walked inclusively in all cases, see above + + 45' degree lines are explicitly special cased -- because it + all runs as integers, but it's probably not frequent enough + to justify the check + + */ + +if (dx > dy) { + + x0 = fix_int(x0); + x1 = fix_int(x1); + + if (x0 > x1) { + t = x0; + x0 = x1; + x1 = t; + t = y0; + y0 = y1; + y1 = t; + } + p = grd_bm.bits + grd_bm.row * (fix_int(y0)); + + if ((fix_int(y0)) == (fix_int(y1))) { + flat8_pixel_fill_row; + } else if (y0 < y1) { + dy = fix_div((y1 - y0), dx); + while (x0 <= x1) { + flat8_pixel_fill_xi; + x0++; + y0 += dy; + p += (grd_bm.row & (-(fix_frac(y0) < dy))); + } + } else { + dy = fix_div((y0 - y1), dx); + while (x0 <= x1) { + flat8_pixel_fill_xi; + x0++; + p -= (grd_bm.row & (-(fix_frac(y0) < dy))); + y0 -= dy; + } + } +} + +else if (dy > dx) { + + y0 = fix_int(y0); + y1 = fix_int(y1); + + if (y0 > y1) { + t = x0; + x0 = x1; + x1 = t; + t = y0; + y0 = y1; + y1 = t; + } + + p = grd_bm.bits + grd_bm.row * y0; + + if ((fix_int(x0)) == (fix_int(x1))) { + x0 = fix_int(x0); + while (y0 <= y1) { + flat8_pixel_fill_xi; + y0++; + p += grd_bm.row; + } + } else { + dx = fix_div((x1 - x0), dy); + while (y0 <= y1) { + flat8_pixel_fill_xf; + x0 += dx; + y0++; + p += grd_bm.row; + } + } +} else { /* dy == dx, walk the x axis, all integers */ + + x0 = fix_int(x0); + x1 = fix_int(x1); + y0 = fix_int(y0); + y1 = fix_int(y1); + + if (x0 > x1) { + t = x0; + x0 = x1; + x1 = t; + t = y0; + y0 = y1; + y1 = t; + } + p = grd_bm.bits + grd_bm.row * y0; + + if (y0 < y1) { + while (y0 <= y1) { + flat8_pixel_fill_xi; + x0++; + y0++; + p += grd_bm.row; + } + } else { + while (y0 >= y1) { + flat8_pixel_fill_xi; + x0++; + y0--; + p -= grd_bm.row; + } + } +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8ll.c b/engine/src/Libraries/2D/Source/Flat8/fl8ll.c new file mode 100644 index 0000000..6b3083c --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8ll.c @@ -0,0 +1,333 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/genll.c $ + * $Revision: 1.3 $ + * $Author: kevin $ + * $Date: 1994/08/16 12:50:11 $ + * + * Routines to linearly texture map a flat8 bitmap to a generic canvas. + * + * This file is part of the 2d library. + * + */ + +#include "cnvdat.h" +#include "fl8tf.h" +#include "fl8tmapdv.h" +#include "gente.h" +#include "poly.h" +#include "scrdat.h" +#include "tmapint.h" +#include "vtab.h" + +int gri_lit_lin_umap_loop(grs_tmap_loop_info *tli); + +// PPC specific optimized routines +/*extern "C" +{ +int Handle_Lit_Lin_Loop_PPC(fix u, fix v, fix du, fix dv, fix dx, + grs_tmap_loop_info +*tli, uchar *start_pdest, uchar *t_bits, long gr_row, fix i, fix di, uchar *g_ltab, uchar t_wlog, ulong t_mask); + +int Handle_TLit_Lin_Loop2_PPC(fix u, fix v, fix du, fix dv, fix dx, + grs_tmap_loop_info *tli, uchar *start_pdest, uchar *t_bits, long gr_row, + fix i, fix di, uchar *g_ltab, uchar t_wlog, ulong t_mask); +}*/ + +int Handle_Lit_Lin_Loop_C(fix u, fix v, fix du, fix dv, fix dx, grs_tmap_loop_info *tli, uchar *start_pdest, + uchar *t_bits, long gr_row, fix i, fix di, uchar *g_ltab, uchar t_wlog, ulong t_mask) { + int x, t_xl, t_xr, inv; + uchar *p_dest; + + tli->y += tli->n; + + do { + if ((x = fix_ceil(tli->right.x) - fix_ceil(tli->left.x)) > 0) { + x = fix_div(fix_make(1, 0) << 8, dx); + di = fix_mul_asm_safe_light(di, x); + x >>= 8; + du = fix_mul_asm_safe(du, x); + dv = fix_mul_asm_safe(dv, x); + + x = fix_ceil(tli->left.x) - tli->left.x; + u += fix_mul(du, x); + v += fix_mul(dv, x); + i += fix_mul(di, x); + + // copy out tli-> stuff into locals + t_xl = fix_cint(tli->left.x); + t_xr = fix_cint(tli->right.x); + p_dest = start_pdest + t_xl; + x = t_xr - t_xl; + + for (; x > 0; x--) { + *(p_dest++) = g_ltab[t_bits[((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask] + fix_light(i)]; + u += du; + v += dv; + i += di; + } + } else if (x < 0) + return TRUE; // punt this tmap + + u = (tli->left.u += tli->left.du); + tli->right.u += tli->right.du; + du = tli->right.u - u; + v = (tli->left.v += tli->left.dv); + tli->right.v += tli->right.dv; + dv = tli->right.v - v; + i = (tli->left.i += tli->left.di); + tli->right.i += tli->right.di; + di = tli->right.i - i; + tli->left.x += tli->left.dx; + tli->right.x += tli->right.dx; + dx = tli->right.x - tli->left.x; + start_pdest += gr_row; + } while (--(tli->n) > 0); + return FALSE; // tmap OK +} + +int Handle_TLit_Lin_Loop2_C(fix u, fix v, fix du, fix dv, fix dx, grs_tmap_loop_info *tli, uchar *start_pdest, + uchar *t_bits, long gr_row, fix i, fix di, uchar *g_ltab, uchar t_wlog, ulong t_mask) { + int x, k; + uchar *p_dest; + int t_xl, t_xr; + int lx, rx; + + lx = tli->left.x; + rx = tli->right.x; + + tli->y += tli->n; + do { + if ((x = fix_ceil(rx) - fix_ceil(lx)) > 0) { + x = fix_ceil(lx) - lx; + + k = fix_div(fix_make(1, 0) << 8, dx); + di = fix_mul_asm_safe_light(di, k); + k >>= 8; + du = fix_mul_asm_safe(du, k); + dv = fix_mul_asm_safe(dv, k); + + u += fix_mul(du, x); + v += fix_mul(dv, x); + i += fix_mul(di, x); + + // copy out tli-> stuff into locals + t_xl = fix_cint(lx); + t_xr = fix_cint(rx); + p_dest = start_pdest + t_xl; + x = t_xr - t_xl; + + for (; x > 0; x--) { + // assume pixel in transparent first + k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + k = t_bits[k]; + if (k != 0) // not transparent, move to assuming opaque + *p_dest = g_ltab[k + fix_light(i)]; + + p_dest++; + u += du; + v += dv; + i += di; + } + } else if (x < 0) + return TRUE; // punt this tmap + + u = (tli->left.u += tli->left.du); + tli->right.u += tli->right.du; + du = tli->right.u - u; + v = (tli->left.v += tli->left.dv); + tli->right.v += tli->right.dv; + dv = tli->right.v - v; + i = (tli->left.i += tli->left.di); + tli->right.i += tli->right.di; + di = tli->right.i - i; + lx += tli->left.dx; + rx += tli->right.dx; + dx = rx - lx; + start_pdest += gr_row; + } while (--(tli->n) > 0); + + tli->left.x = lx; + tli->right.x = rx; + + return FALSE; // tmap OK +} + +int gri_lit_lin_umap_loop(grs_tmap_loop_info *tli) { + fix u, v, i, du, dv, di, dx, d; + + // locals used to store copies of tli-> stuff, so its in registers on the PPC + register int x, k; + int t_xl, t_xr, inv; + int32_t *t_vtab; + uchar *t_bits; + uchar *p_dest; + uchar temp_pix; + uchar t_wlog; + ulong t_mask; + uchar *g_ltab; + long gr_row; + uchar *start_pdest; + + u = tli->left.u; + du = tli->right.u - u; + v = tli->left.v; + dv = tli->right.v - v; + i = tli->left.i; + di = tli->right.i - i; + dx = tli->right.x - tli->left.x; + + t_vtab = tli->vtab; + t_mask = tli->mask; + t_wlog = tli->bm.wlog; + g_ltab = grd_screen->ltab; + + t_bits = tli->bm.bits; + gr_row = grd_bm.row; + start_pdest = grd_bm.bits + (gr_row * (tli->y)); + + // handle optimized cases first + if (tli->bm.hlog == (GRL_OPAQUE | GRL_LOG2)) + return ( + Handle_Lit_Lin_Loop_C(u, v, du, dv, dx, tli, start_pdest, t_bits, gr_row, i, di, g_ltab, t_wlog, t_mask)); + if (tli->bm.hlog == (GRL_TRANS | GRL_LOG2)) + return ( + Handle_TLit_Lin_Loop2_C(u, v, du, dv, dx, tli, start_pdest, t_bits, gr_row, i, di, g_ltab, t_wlog, t_mask)); + + do { + if ((d = fix_ceil(tli->right.x) - fix_ceil(tli->left.x)) > 0) { + d = fix_ceil(tli->left.x) - tli->left.x; + +#if InvDiv + k = fix_div(fix_make(1, 0) << 8, dx); + di = fix_mul_asm_safe_light(di, k); + k >>= 8; + du = fix_mul_asm_safe(du, k); + dv = fix_mul_asm_safe(dv, k); +#else + du = fix_div(du, dx); + dv = fix_div(dv, dx); + di = fix_div(di, dx); +#endif + + u += fix_mul(du, d); + v += fix_mul(dv, d); + i += fix_mul(di, d); + + // copy out tli-> stuff into locals + t_xl = fix_cint(tli->left.x); + t_xr = fix_cint(tli->right.x); + p_dest = start_pdest + t_xl; + x = t_xr - t_xl; + + switch (tli->bm.hlog) { + case GRL_OPAQUE: + for (; x > 0; x--) { + k = t_vtab[fix_fint(v)] + fix_fint(u); + *(p_dest++) = + g_ltab[t_bits[k] + fix_light(i)]; // gr_fill_upixel(g_ltab[t_bits[k]+fix_light(i)],x,t_y); + u += du; + v += dv; + i += di; + } + break; + case GRL_TRANS: + for (; x > 0; x--) { + k = t_vtab[fix_fint(v)] + fix_fint(u); + k = t_bits[k]; + if (k != 0) + *p_dest = g_ltab[k + fix_light(i)]; // gr_fill_upixel(g_ltab[k+fix_light(i)],x,t_y); + p_dest++; + u += du; + v += dv; + i += di; + } + break; + // handled in special case code + case GRL_OPAQUE | GRL_LOG2: + for (; x > 0; x--) { + k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + *(p_dest++) = + g_ltab[t_bits[k] + fix_light(i)]; // gr_fill_upixel(g_ltab[t_bits[k]+fix_light(i)],x,t_y); + u += du; + v += dv; + i += di; + } + break; + case GRL_TRANS | GRL_LOG2: + for (; x > 0; x--) { + k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + k = t_bits[k]; + if (k != 0) + *p_dest = g_ltab[k + fix_light(i)]; // gr_fill_upixel(g_ltab[k+fix_light(i)],x,t_y); + p_dest++; + u += du; + v += dv; + i += di; + } + break; + } + } else if (d < 0) + return TRUE; /* punt this tmap */ + + u = (tli->left.u += tli->left.du); + tli->right.u += tli->right.du; + du = tli->right.u - u; + v = (tli->left.v += tli->left.dv); + tli->right.v += tli->right.dv; + dv = tli->right.v - v; + i = (tli->left.i += tli->left.di); + tli->right.i += tli->right.di; + di = tli->right.i - i; + tli->left.x += tli->left.dx; + tli->right.x += tli->right.dx; + dx = tli->right.x - tli->left.x; + tli->y++; + start_pdest += gr_row; + } while (--(tli->n) > 0); + return FALSE; /* tmap OK */ +} + +void gri_trans_lit_lin_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_TRANS | GRL_LOG2; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_TRANS; + } + tli->loop_func = (void (*)())gri_lit_lin_umap_loop; + tli->right_edge_func = (void (*)())gri_uvix_edge; + tli->left_edge_func = (void (*)())gri_uvix_edge; +} + +void gri_opaque_lit_lin_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_OPAQUE | GRL_LOG2; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_OPAQUE; + } + + tli->loop_func = (void (*)())gri_lit_lin_umap_loop; + tli->right_edge_func = (void (*)())gri_uvix_edge; + tli->left_edge_func = (void (*)())gri_uvix_edge; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8lnop.c b/engine/src/Libraries/2D/Source/Flat8/fl8lnop.c new file mode 100644 index 0000000..52ae518 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8lnop.c @@ -0,0 +1,300 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/genl.c $ + * $Revision: 1.4 $ + * $Author: kevin $ + * $Date: 1994/08/16 12:48:26 $ + * + * Routines to linearly texture map a flat8 bitmap to a generic canvas. + * + * This file is part of the 2d library. + * + */ + +#include "cnvdat.h" +#include "fl8tf.h" +#include "fl8tmapdv.h" +#include "gente.h" +#include "poly.h" +#include "tmapint.h" +#include "vtab.h" + +// prototypes +int gri_lin_umap_loop(grs_tmap_loop_info *tli); + +/*extern "C" +{ +int Handle_LinClut_Loop_PPC(fix u, fix v, fix du, fix dv, fix dx, + grs_tmap_loop_info +*tli, uchar *start_pdest, uchar *t_bits, int32_t gr_row, uchar *t_clut, uchar t_wlog, uint32_t t_mask); +}*/ + +int Handle_LinClut_Loop_C(fix u, fix v, fix du, fix dv, fix dx, grs_tmap_loop_info *tli, uchar *start_pdest, + uchar *t_bits, int32_t gr_row, uchar *t_clut, uchar t_wlog, uint32_t t_mask) { + register int x, k; + uchar *p_dest; + register fix rx, lx; + + rx = tli->right.x; + lx = tli->left.x; + tli->y += tli->n; + + do { + if ((x = fix_ceil(rx) - fix_ceil(lx)) > 0) { + x = fix_ceil(lx) - lx; + + k = fix_div(fix_make(1, 0), dx); + du = fix_mul_asm_safe(du, k); + dv = fix_mul_asm_safe(dv, k); + + u += fix_mul(du, x); + v += fix_mul(dv, x); + + // copy out tli-> stuff into locals + p_dest = start_pdest + fix_cint(lx); + x = fix_cint(rx) - fix_cint(lx); + + for (; x > 0; x--) { + *(p_dest++) = t_clut[t_bits[((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask]]; + u += du; + v += dv; + } + } else if (x < 0) + return TRUE; // punt this tmap + + u = (tli->left.u += tli->left.du); + tli->right.u += tli->right.du; + du = tli->right.u - u; + v = (tli->left.v += tli->left.dv); + tli->right.v += tli->right.dv; + dv = tli->right.v - v; + lx += tli->left.dx; + rx += tli->right.dx; + dx = rx - lx; + start_pdest += gr_row; + } while (--(tli->n) > 0); + + tli->right.x = rx; + tli->left.x = lx; + + return FALSE; // tmap OK +} + +int gri_lin_umap_loop(grs_tmap_loop_info *tli) { + fix u, v, du, dv, dx, d; + + // locals used to store copies of tli-> stuff, so its in registers on the PPC + register int x, k; + uchar *p_dest; + uchar temp_pix; + int32_t *t_vtab; + uchar *t_bits; + uchar *t_clut; + uchar t_wlog; + uint32_t t_mask; + int32_t gr_row; + uchar *start_pdest; + int32_t inv; + + u = tli->left.u; + du = tli->right.u - u; + v = tli->left.v; + dv = tli->right.v - v; + dx = tli->right.x - tli->left.x; + + t_vtab = tli->vtab; + t_clut = tli->clut; + t_mask = tli->mask; + t_wlog = tli->bm.wlog; + + t_bits = tli->bm.bits; + gr_row = grd_bm.row; + start_pdest = grd_bm.bits + (gr_row * (tli->y)); + + // handle PowerPC loop + if (tli->bm.hlog == (GRL_OPAQUE | GRL_LOG2 | GRL_CLUT)) + return (Handle_LinClut_Loop_C(u, v, du, dv, dx, tli, start_pdest, t_bits, gr_row, t_clut, t_wlog, t_mask)); + + do { + if ((d = fix_ceil(tli->right.x) - fix_ceil(tli->left.x)) > 0) { + d = fix_ceil(tli->left.x) - tli->left.x; + +#if InvDiv + k = fix_div(fix_make(1, 0), dx); + du = fix_mul_asm_safe(du, k); + dv = fix_mul_asm_safe(dv, k); +#else + du = fix_div(du, dx); + dv = fix_div(dv, dx); +#endif + u += fix_mul(du, d); + v += fix_mul(dv, d); + + // copy out tli-> stuff into locals + p_dest = start_pdest + fix_cint(tli->left.x); + x = fix_cint(tli->right.x) - fix_cint(tli->left.x); + + switch (tli->bm.hlog) { + case GRL_OPAQUE: + for (; x > 0 && v >= 0; x--) { + k = t_vtab[fix_fint(v)] + fix_fint(u); + *(p_dest++) = t_bits[k]; // gr_fill_upixel(t_bits[k],x,y); + u += du; + v += dv; + } + break; + case GRL_TRANS: + for (; x > 0 && v >= 0; x--) { + k = t_vtab[fix_fint(v)] + fix_fint(u); + temp_pix = t_bits[k]; + if (temp_pix != 0) + *p_dest = temp_pix; // gr_fill_upixel(t_bits[k],x,y); + p_dest++; + u += du; + v += dv; + } + break; + case GRL_OPAQUE | GRL_LOG2: + for (; x > 0 && v >= 0; x--) { + k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + *(p_dest++) = t_bits[k]; // gr_fill_upixel(t_bits[k],x,y); + u += du; + v += dv; + } + break; + case GRL_TRANS | GRL_LOG2: + for (; x > 0 && v >= 0; x--) { + k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + temp_pix = t_bits[k]; + if (temp_pix != 0) + *p_dest = temp_pix; // gr_fill_upixel(t_bits[k],x,y); + p_dest++; + u += du; + v += dv; + } + break; + case GRL_OPAQUE | GRL_CLUT: + for (; x > 0 && v >= 0; x--) { + k = t_vtab[fix_fint(v)] + fix_fint(u); + *(p_dest++) = t_clut[t_bits[k]]; // gr_fill_upixel(tli->clut[t_bits[k]],x,y); + u += du; + v += dv; + } + break; + case GRL_TRANS | GRL_CLUT: + for (; x > 0 && v >= 0; x--) { + k = t_vtab[fix_fint(v)] + fix_fint(u); + k = t_bits[k]; + if (k != 0) + *p_dest = t_clut[k]; // gr_fill_upixel(tli->clut[k],x,y); + p_dest++; + u += du; + v += dv; + } + break; + // handled in special case now + /* case GRL_OPAQUE|GRL_LOG2|GRL_CLUT: + for (; x>0; x--) { + k=((fix_fint(v)<clut[t_bits[k]],x,y); u+=du; v+=dv; + } + break;*/ + case GRL_TRANS | GRL_LOG2 | GRL_CLUT: + for (; x > 0 && v >= 0; x--) { + k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + k = t_bits[k]; + if (k != 0) + *p_dest = t_clut[k]; // gr_fill_upixel(tli->clut[k],x,y); + p_dest++; + u += du; + v += dv; + } + break; + } + } else if (d < 0) + return TRUE; /* punt this tmap */ + + u = (tli->left.u += tli->left.du); + tli->right.u += tli->right.du; + du = tli->right.u - u; + v = (tli->left.v += tli->left.dv); + tli->right.v += tli->right.dv; + dv = tli->right.v - v; + tli->left.x += tli->left.dx; + tli->right.x += tli->right.dx; + dx = tli->right.x - tli->left.x; + tli->y++; + start_pdest += gr_row; + } while (--(tli->n) > 0); + return FALSE; /* tmap OK */ +} + +void gri_trans_lin_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_TRANS | GRL_LOG2; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_TRANS; + } + tli->loop_func = (void (*)())gri_lin_umap_loop; + tli->right_edge_func = (void (*)())gri_uvx_edge; + tli->left_edge_func = (void (*)())gri_uvx_edge; +} + +void gri_opaque_lin_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_OPAQUE | GRL_LOG2; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_OPAQUE; + } + tli->loop_func = (void (*)())gri_lin_umap_loop; + tli->right_edge_func = (void (*)())gri_uvx_edge; + tli->left_edge_func = (void (*)())gri_uvx_edge; +} + +void gri_trans_clut_lin_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_TRANS | GRL_LOG2 | GRL_CLUT; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_TRANS | GRL_CLUT; + } + tli->loop_func = (void (*)())gri_lin_umap_loop; + tli->right_edge_func = (void (*)())gri_uvx_edge; + tli->left_edge_func = (void (*)())gri_uvx_edge; +} + +void gri_opaque_clut_lin_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_OPAQUE | GRL_LOG2 | GRL_CLUT; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_OPAQUE | GRL_CLUT; + } + tli->loop_func = (void (*)())gri_lin_umap_loop; + tli->right_edge_func = (void (*)())gri_uvx_edge; + tli->left_edge_func = (void (*)())gri_uvx_edge; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8lop.c b/engine/src/Libraries/2D/Source/Flat8/fl8lop.c new file mode 100644 index 0000000..8926697 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8lop.c @@ -0,0 +1,379 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8lop.c $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/08/16 12:57:18 $ + * + * lit full perspective texture mapper. + * scanline processors. + * + */ + +#include "cnvdat.h" +#include "fl8tmapdv.h" +#include "pertyp.h" +#include "plytyp.h" +#include "scrdat.h" +#include "tmapint.h" + +// prototypes +void gri_opaque_lit_per_umap_hscan_scanline(grs_per_info *pi, grs_bitmap *bm); +void gri_opaque_lit_per_umap_vscan_scanline(grs_per_info *pi, grs_bitmap *bm); +void gri_opaque_lit_per_umap_hscan_init(grs_bitmap *bm, grs_per_setup *ps); +void gri_opaque_lit_per_umap_vscan_init(grs_bitmap *bm, grs_per_setup *ps); + +void opaque_lit_per_hscan_Loop_C(int dx, fix l_du, fix l_dv, fix *pl_u, fix *pl_v, uchar **pp, fix *pl_y_fix, + int *py_cint, fix l_di, fix *pl_i, int l_u_mask, int l_v_shift, int l_v_mask, + fix l_scan_slope, uchar *bm_bits, int gr_row, uchar *ltab) { + fix l_u, l_v, l_y_fix, l_i; + int k, y_cint; + uchar *p; + + l_u = *pl_u; + l_v = *pl_v; + l_y_fix = *pl_y_fix; + l_i = *pl_i; + p = *pp; + y_cint = *py_cint; + + for (; dx > 0; dx--) { + k = ((l_u >> 16) & l_u_mask) + ((l_v >> l_v_shift) & l_v_mask); + k = bm_bits[k]; + *(p++) = ltab[(fix_light(l_i)) + k]; + + k = y_cint; + y_cint = fix_int(l_y_fix += l_scan_slope); + if (k != y_cint) + p += gr_row; + + l_u += l_du; + l_v += l_dv; + l_i += l_di; + } + + *pl_u = l_u; + *pl_v = l_v; + *pl_y_fix = l_y_fix; + *pl_i = l_i; + *pp = p; + *py_cint = y_cint; +} + +void gri_opaque_lit_per_umap_hscan_scanline(grs_per_info *pi, grs_bitmap *bm) { + uchar *ltab = grd_screen->ltab; + int l_x, y_cint, l_u_mask, l_v_mask, l_v_shift; + fix k, l_u, l_v, l_du, l_dv, l_i, l_di, l_y_fix, l_scan_slope; + int gr_row; + uchar *bm_bits, *p; + + // locals used to speed PPC code + fix test, l_dtl, l_dxl, l_dyl, l_dtr, l_dyr; + int l_xl, l_xr, l_xr0; + + gr_row = grd_bm.row; + bm_bits = bm->bits; + l_xr0 = pi->xr0; + l_x = pi->x; + l_di = pi->di; + l_i = pi->i; + l_dyr = pi->dyr; + l_dtr = pi->dtr; + l_dyl = pi->dyl; + l_dxl = pi->dxl; + l_dtl = pi->dtl; + l_scan_slope = pi->scan_slope; + l_y_fix = pi->y_fix; + l_v_shift = pi->v_shift; + l_v_mask = pi->v_mask; + l_u_mask = pi->u_mask; + l_xl = pi->xl; + l_xr = pi->xr; + l_u = pi->u; + l_v = pi->v; + l_du = pi->du; + l_dv = pi->dv; + + l_y_fix = l_x * l_scan_slope + fix_make(pi->yp, 0xffff); + + if (l_scan_slope < 0) + gr_row = -gr_row; + +#if InvDiv + k = fix_div(fix_make(1, 0), pi->denom); + l_u = pi->u0 + fix_mul_asm_safe(pi->unum, k); + l_v = pi->v0 + fix_mul_asm_safe(pi->vnum, k); + l_du = fix_mul_asm_safe(pi->dunum, k); + l_dv = fix_mul_asm_safe(pi->dvnum, k); +#else + l_u = pi->u0 + fix_div(pi->unum, pi->denom); + l_v = pi->v0 + fix_div(pi->vnum, pi->denom); + l_du = fix_div(pi->dunum, pi->denom); + l_dv = fix_div(pi->dvnum, pi->denom); +#endif + + l_u += l_x * l_du; + l_v += l_x * l_dv; + + y_cint = fix_int(l_y_fix); + p = grd_bm.bits + l_x + y_cint * grd_bm.row; + if (l_x < l_xl) { + test = l_x * l_dyl - y_cint * l_dxl + pi->cl; + for (; l_x < l_xl; l_x++) { + if (test <= 0) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + k = bm_bits[k]; + *p = ltab[(fix_light(l_i)) + k]; // gr_fill_upixel(ltab[(fix_int(l_i)<<8)+k],l_x,y_cint); + } + + k = y_cint; + y_cint = fix_int(l_y_fix += l_scan_slope); + if (k != y_cint) { + p += gr_row; + test += l_dtl; + } else + test += l_dyl; + + p++; + l_u += l_du; + l_v += l_dv; + l_i += l_di; + } + } + + if (l_x < l_xr0) { + opaque_lit_per_hscan_Loop_C(l_xr0 - l_x, l_du, l_dv, &l_u, &l_v, &p, &l_y_fix, &y_cint, l_di, &l_i, l_u_mask, + l_v_shift, l_v_mask, l_scan_slope, bm_bits, gr_row, ltab); + l_x = l_xr0; + } + + if (l_x < l_xr) { + test = l_x * l_dyr - y_cint * pi->dxr + pi->cr; + p = grd_bm.bits + l_x + y_cint * grd_bm.row; + for (; l_x < l_xr; l_x++) { + if (test >= 0) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + k = bm_bits[k]; + *p = ltab[(fix_light(l_i)) + k]; + // gr_fill_upixel(ltab[(fix_int(l_i)<<8)+k],l_x,y_cint); + } + + k = y_cint; + y_cint = fix_int(l_y_fix += l_scan_slope); + if (k != y_cint) { + p += gr_row; + test += l_dtr; + } else + test += l_dyr; + + p++; + l_u += l_du; + l_v += l_dv; + l_i += l_di; + } + } + + pi->y_fix = l_y_fix; + pi->x = l_x; + pi->u = l_u; + pi->v = l_v; + pi->du = l_du; + pi->dv = l_dv; + pi->di = l_di; + pi->i = l_i; +} + +void opaque_lit_per_vscan_Loop_C(int dy, fix l_du, fix l_dv, fix *pl_u, fix *pl_v, uchar **pp, fix *pl_x_fix, + int *px_cint, fix l_di, fix *pl_i, int l_u_mask, int l_v_shift, int l_v_mask, + fix l_scan_slope, uchar *bm_bits, int gr_row, uchar *ltab) { + fix l_u, l_v, l_x_fix, l_i; + int k, x_cint; + uchar *p; + + l_u = *pl_u; + l_v = *pl_v; + l_x_fix = *pl_x_fix; + l_i = *pl_i; + p = *pp; + x_cint = *px_cint; + + for (; dy > 0; dy--) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + k = bm_bits[k]; + *p = ltab[(fix_light(l_i)) + k]; + + k = x_cint; + x_cint = fix_int(l_x_fix += l_scan_slope); + if (k != x_cint) + p -= (k - x_cint); + + p += gr_row; + l_u += l_du; + l_v += l_dv; + l_i += l_di; + } + + *pl_u = l_u; + *pl_v = l_v; + *pl_x_fix = l_x_fix; + *pl_i = l_i; + *pp = p; + *px_cint = x_cint; +} + +void gri_opaque_lit_per_umap_vscan_scanline(grs_per_info *pi, grs_bitmap *bm) { + int k, x_cint; + uchar *ltab = grd_screen->ltab; + + // locals used to speed PPC code + fix l_dxr, l_x_fix, l_u, l_v, l_du, l_dv, l_scan_slope, l_dtl, l_dxl, l_dyl, l_dtr, l_dyr, l_i, l_di; + int l_yl, l_yr0, l_yr, l_y, l_u_mask, l_v_mask, l_v_shift; + int gr_row, temp_x; + uchar *bm_bits; + uchar *p; + + gr_row = grd_bm.row; + bm_bits = bm->bits; + l_di = pi->di; + l_i = pi->i; + l_dxr = pi->dxr; + l_x_fix = pi->x_fix; + l_y = pi->y; + l_yr = pi->yr; + l_yr0 = pi->yr0; + l_yl = pi->yl; + l_dyr = pi->dyr; + l_dtr = pi->dtr; + l_dyl = pi->dyl; + l_dxl = pi->dxl; + l_dtl = pi->dtl; + l_scan_slope = pi->scan_slope; + l_v_shift = pi->v_shift; + l_v_mask = pi->v_mask; + l_u_mask = pi->u_mask; + l_u = pi->u; + l_v = pi->v; + l_du = pi->du; + l_dv = pi->dv; + + l_x_fix = l_y * l_scan_slope + fix_make(pi->xp, 0xffff); + +#if InvDiv + k = fix_div(fix_make(1, 0), pi->denom); + l_u = pi->u0 + fix_mul_asm_safe(pi->unum, k); + l_v = pi->v0 + fix_mul_asm_safe(pi->vnum, k); + l_du = fix_mul_asm_safe(pi->dunum, k); + l_dv = fix_mul_asm_safe(pi->dvnum, k); +#else + l_u = pi->u0 + fix_div(pi->unum, pi->denom); + l_v = pi->v0 + fix_div(pi->vnum, pi->denom); + l_du = fix_div(pi->dunum, pi->denom); + l_dv = fix_div(pi->dvnum, pi->denom); +#endif + l_u += l_y * l_du; + l_v += l_y * l_dv; + + x_cint = fix_int(l_x_fix); + p = grd_bm.bits + x_cint + l_y * gr_row; + if (l_y < l_yl) { + fix test = l_y * l_dxl - x_cint * l_dyl + pi->cl; + for (; l_y < l_yl; l_y++) { + if (test <= 0) { + int k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + k = bm_bits[k]; + *p = ltab[(fix_light(l_i)) + k]; // gr_fill_upixel(ltab[(fix_int(l_i)<<8)+k],x_cint,l_y); + } + + temp_x = x_cint; + x_cint = fix_int(l_x_fix += l_scan_slope); + if (temp_x != x_cint) { + test += l_dtl; + p -= temp_x - x_cint; + } else { + test += l_dxl; + x_cint = fix_int(l_x_fix); + } + + p += gr_row; + l_u += l_du; + l_v += l_dv; + l_i += l_di; + } + } + + if (l_y < l_yr0) { + opaque_lit_per_vscan_Loop_C(l_yr0 - l_y, l_du, l_dv, &l_u, &l_v, &p, &l_x_fix, &x_cint, l_di, &l_i, l_u_mask, + l_v_shift, l_v_mask, l_scan_slope, bm_bits, gr_row, ltab); + l_y = l_yr0; + } + + if (l_y < l_yr) { + fix test = l_y * l_dxr - x_cint * l_dyr + pi->cr; + p = grd_bm.bits + x_cint + l_y * gr_row; + for (; l_y < l_yr; l_y++) { + if (test >= 0) { + int k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + k = bm_bits[k]; + *p = ltab[(fix_light(l_i)) + k]; // gr_fill_upixel(ltab[(fix_int(l_i)<<8)+k],x_cint,l_y); + } + + temp_x = x_cint; + x_cint = fix_int(l_x_fix += l_scan_slope); + if (temp_x != x_cint) { + test += l_dtr; + p -= temp_x - x_cint; + } else { + test += l_dxr; + x_cint = fix_int(l_x_fix); + } + p += gr_row; + l_u += l_du; + l_v += l_dv; + l_i += l_di; + } + } + + pi->x_fix = l_x_fix; + pi->y = l_y; + pi->u = l_u; + pi->v = l_v; + pi->du = l_du; + pi->dv = l_dv; + pi->di = l_di; + pi->i = l_i; +} + +extern void gri_lit_per_umap_hscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps); +extern void gri_lit_per_umap_vscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps); + +void gri_opaque_lit_per_umap_hscan_init(grs_bitmap *bm, grs_per_setup *ps) { + ps->shell_func = (void (*)())gri_lit_per_umap_hscan; + ps->scanline_func = (void (*)())gri_opaque_lit_per_umap_hscan_scanline; +} + +void gri_opaque_lit_per_umap_vscan_init(grs_bitmap *bm, grs_per_setup *ps) { + ps->shell_func = (void (*)())gri_lit_per_umap_vscan; + ps->scanline_func = (void (*)())gri_opaque_lit_per_umap_vscan_scanline; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8lp.c b/engine/src/Libraries/2D/Source/Flat8/fl8lp.c new file mode 100644 index 0000000..eadfcd1 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8lp.c @@ -0,0 +1,639 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8lp.c $ + * $Revision: 1.23 $ + * $Author: kevin $ + * $Date: 1994/11/02 19:39:45 $ + * + * lit full perspective texture mapper. + * + */ + +// ************************************************************************************ +// ************************************************************************************ +// +// MLA - don't think we need to optimize this, its in C on the PC too +// +// ************************************************************************************ +// ************************************************************************************ + +#include "cnvdat.h" +#include "pertyp.h" +#include "plytyp.h" +#define safe_fix_cint(x) ((fix_frac(x) == 0) ? (fix_int(x)) : (fix_int(x) + 1)) +#define fix_16_20(a) ((a) >> 4) + +/************************************************************** +Routines to scan polygon. hscan=standard horizontal scanlines. +vscan=vertical scanlines. +**************************************************************/ + +// prototypes +void gri_lit_per_umap_hscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps); +void gri_lit_per_umap_vscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps); + +void gri_lit_per_umap_hscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps) { + grs_per_info pi; + fix y_prime[10]; + fix yp_left, yp_right; + fix x_left, x_right; + fix y_left, y_right; + fix dx_left, dx_right; + fix i_left, i_right; + fix di_left, di_right; + int yp_min, yp_max, yp_next; + int x_min, x_max, xr_min, xr_max, xl_min, xl_max; + int n_min, n_left, n_right; + int j; + + pi.scale = grd_bm.w; + pi.scan_slope = ps->scan_slope; + pi.dp = ps->dp; + pi.clut = ps->clut; + if (fix_abs(pi.scan_slope) > FIX_UNIT) + return; + + if (bm->row != 1 << (bm->wlog)) + return; + if (bm->h != 1 << (bm->hlog)) + return; + pi.u_mask = bm->row - 1; + pi.v_mask = (bm->h - 1) << bm->wlog; + pi.v_shift = 16 - bm->wlog; + + yp_min = yp_max = fix_cint(y_prime[n_min = 0] = vpl[0]->y - fix_mul(vpl[0]->x, ps->scan_slope)); + for (j = 1; j < n; j++) { + pi.yp = fix_cint(y_prime[j] = vpl[j]->y - fix_mul(vpl[j]->x, ps->scan_slope)); + if (pi.yp < yp_min) { + yp_min = pi.yp; + n_min = j; + } + if (pi.yp > yp_max) + yp_max = pi.yp; + } + if (yp_max == yp_min) + return; + pi.denom = fix_16_20(ps->c + fix_mul(ps->b, y_prime[0])); + pi.u0 = + vpl[0]->u - fix_div(fix_mul(vpl[0]->x, ps->alpha_u) + fix_mul(vpl[0]->y, ps->beta_u) + ps->gamma_u, pi.denom); + pi.v0 = + vpl[0]->v - fix_div(fix_mul(vpl[0]->x, ps->alpha_v) + fix_mul(vpl[0]->y, ps->beta_v) + ps->gamma_v, pi.denom); + + n_left = n_right = n_min; + pi.yp = yp_min; + while (fix_cint(y_prime[(n_left + n - 1) % n]) == pi.yp) + n_left = (n_left + n - 1) % n; + while (fix_cint(y_prime[(n_right + 1) % n]) == pi.yp) + n_right = (n_right + 1) % n; + + pi.yp--; + pi.denom = fix_16_20(ps->c + pi.yp * ps->b); + pi.unum = ps->gamma_u + pi.yp * ps->beta_u; + pi.dunum = ps->alpha_u + fix_mul(ps->scan_slope, ps->beta_u); + pi.vnum = ps->gamma_v + pi.yp * ps->beta_v; + pi.dvnum = ps->alpha_v + fix_mul(ps->scan_slope, ps->beta_v); + + if (n_right != n_left) { + + pi.dxl = (vpl[n_right]->x - vpl[n_left]->x) / pi.scale; + pi.dyl = (vpl[n_right]->y - vpl[n_left]->y) / pi.scale; + pi.x = fix_cint(vpl[n_left]->x); + pi.xl = fix_cint(vpl[n_right]->x); + if (pi.scan_slope > 0) { + x_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.bot - 1) - pi.yp, 1), pi.scan_slope)); + x_min = fix_int(fix_div(fix_make((grd_int_clip.top - 1) - pi.yp, 1), pi.scan_slope)) + 1; + } else { + x_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.top - 1) - pi.yp, 1), pi.scan_slope)); + x_min = fix_int(fix_div(fix_make((grd_int_clip.bot - 1) - pi.yp, 1), pi.scan_slope)) + 1; + } + if (pi.x < x_min) + pi.x = x_min; + if (pi.xl > x_max) + pi.xl = x_max; + pi.xr0 = pi.xr = pi.xl; + pi.i = vpl[n_left]->i; + if (pi.xr - pi.x) + pi.di = (vpl[n_right]->i - pi.i) / (pi.xr - pi.x); + else { + pi.di = 0x7fffffff; + if (vpl[n_right]->i - pi.i < 0) + pi.di = -pi.di; + } + + pi.cl = fix_mul(pi.dxl, vpl[n_left]->y) - fix_mul(pi.dyl, vpl[n_left]->x); + if (pi.x * pi.dyl - fix_mul(fix_make(pi.yp - 1, 0) + pi.x * pi.scan_slope, pi.dxl) + pi.cl < 0) { + pi.dyl = -pi.dyl; + pi.dxl = -pi.dxl; + pi.cl = -pi.cl; + } + if (pi.scan_slope > 0) + pi.dtl = pi.dyl - pi.dxl; + else + pi.dtl = pi.dyl + pi.dxl; + + ((void (*)(grs_per_info *, grs_bitmap *))(ps->scanline_func))(&pi, bm); + + pi.denom += fix_16_20(ps->b), pi.unum += ps->beta_u, pi.vnum += ps->beta_v; + yp_min--; /* first line already done */ + } + pi.yp++; + while (pi.yp < yp_max) { + /* check left edge */ + if (fix_cint(y_prime[n_left]) <= pi.yp) { + int n_prev, dyp; + fix d; + do { + if (fix_cint(y_prime[n_left]) == pi.yp) + n_prev = n_left; + if (--n_left < 0) + n_left = n - 1; + } while (fix_cint(y_prime[n_left]) <= pi.yp); + yp_left = y_prime[n_prev]; + x_left = vpl[n_prev]->x; + y_left = vpl[n_prev]->y; + i_left = vpl[n_prev]->i; + dyp = fix_cint(y_prime[n_left]) - fix_cint(yp_left); + di_left = (vpl[n_left]->i - i_left) / dyp; + xl_min = fix_cint(x_left); + xl_max = fix_cint(vpl[n_left]->x); + pi.dxl = (vpl[n_left]->x - x_left) / pi.scale; + pi.dyl = (vpl[n_left]->y - y_left) / pi.scale; + pi.cl = fix_mul(pi.dxl, y_left) - fix_mul(pi.dyl, x_left); + d = pi.dyl - fix_mul(pi.dxl, pi.scan_slope); + dx_left = fix_div(pi.dxl, d); + x_left = fix_div(fix_mul(pi.dyl, x_left) + fix_mul(pi.dxl, fix_ceil(yp_left) - y_left), d); + if (xl_max < xl_min) { + fix foo = xl_min; + xl_min = xl_max; + xl_max = foo; + } + if (fix_mul(vpl[n_left]->x - FIX_UNIT, pi.dyl) - fix_mul(vpl[n_left]->y - pi.scan_slope, pi.dxl) + pi.cl < + 0) { + pi.dyl = -pi.dyl; + pi.dxl = -pi.dxl; + pi.cl = -pi.cl; + } + if (pi.scan_slope > 0) + pi.dtl = pi.dyl - pi.dxl; + else + pi.dtl = pi.dyl + pi.dxl; + yp_left = y_prime[n_left]; + } + + /* check right edge */ + if (fix_cint(y_prime[n_right]) <= pi.yp) { + int n_prev, dyp; + fix d; + do { + if (fix_cint(y_prime[n_right]) == pi.yp) + n_prev = n_right; + if (++n_right == n) + n_right = 0; + } while (fix_cint(y_prime[n_right]) <= pi.yp); + yp_right = y_prime[n_prev]; + x_right = vpl[n_prev]->x; + y_right = vpl[n_prev]->y; + i_right = vpl[n_prev]->i; + dyp = fix_cint(y_prime[n_right]) - fix_cint(yp_right); + di_right = (vpl[n_right]->i - i_right) / dyp; + xr_min = fix_cint(x_right); + xr_max = fix_cint(vpl[n_right]->x); + pi.dxr = (vpl[n_right]->x - x_right) / pi.scale; + pi.dyr = (vpl[n_right]->y - y_right) / pi.scale; + pi.cr = fix_mul(pi.dxr, y_right) - fix_mul(pi.dyr, x_right); + d = pi.dyr - fix_mul(pi.dxr, pi.scan_slope); + dx_right = fix_div(pi.dxr, d); + x_right = fix_div(fix_mul(pi.dyr, x_right) + fix_mul(pi.dxr, fix_ceil(yp_right) - y_right), d); + if (xr_max < xr_min) { + fix foo = xr_min; + xr_min = xr_max; + xr_max = foo; + } + if (fix_mul(vpl[n_right]->x - FIX_UNIT, pi.dyr) - fix_mul(vpl[n_right]->y - pi.scan_slope, pi.dxr) + pi.cr < + 0) { + pi.dyr = -pi.dyr; + pi.dxr = -pi.dxr; + pi.cr = -pi.cr; + } + if (pi.scan_slope > 0) + pi.dtr = pi.dyr - pi.dxr; + else + pi.dtr = pi.dyr + pi.dxr; + yp_right = y_prime[n_right]; + } + yp_next = (yp_right < yp_left) ? fix_cint(yp_right) : fix_cint(yp_left); + + /* do 0th scanline if at yp_min */ + if (pi.yp == yp_min) { + x_left -= dx_left; + x_right -= dx_right; + pi.yp--; + } + for (; pi.yp < yp_next; pi.yp++) { + if ((pi.yp + 1 == yp_max) && (n_left != n_right)) { + pi.dxl = (vpl[n_right]->x - vpl[n_left]->x) / pi.scale; + pi.dyl = (vpl[n_right]->y - vpl[n_left]->y) / pi.scale; + pi.x = fix_cint(vpl[n_left]->x); + pi.xl = fix_cint(vpl[n_right]->x); + if (pi.scan_slope > 0) { + x_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.bot - 1) - pi.yp, 1), pi.scan_slope)); + x_min = fix_int(fix_div(fix_make((grd_int_clip.top - 1) - pi.yp, 1), pi.scan_slope)) + 1; + } else { + x_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.top - 1) - pi.yp, 1), pi.scan_slope)); + x_min = fix_int(fix_div(fix_make((grd_int_clip.bot - 1) - pi.yp, 1), pi.scan_slope)) + 1; + } + if (pi.x < x_min) + pi.x = x_min; + if (pi.xl > x_max) + pi.xl = x_max; + pi.xr0 = pi.xr = pi.xl; + pi.i = i_left; + if (pi.xr - pi.x) + pi.di = (i_right - pi.i) / (pi.xr - pi.x); + else { + pi.di = 0x7fffffff; + if (i_right - pi.i < 0) + pi.di = -pi.di; + } + + pi.cl = fix_mul(pi.dxl, vpl[n_left]->y) - fix_mul(pi.dyl, vpl[n_left]->x); + if (pi.x * pi.dyl - fix_mul(fix_make(pi.yp + 1, 0) + pi.x * pi.scan_slope, pi.dxl) + pi.cl < 0) { + pi.dyl = -pi.dyl; + pi.dxl = -pi.dxl; + pi.cl = -pi.cl; + } + if (pi.scan_slope > 0) + pi.dtl = pi.dyl - pi.dxl; + else + pi.dtl = pi.dyl + pi.dxl; + + ((void (*)(grs_per_info *, grs_bitmap *))(ps->scanline_func))(&pi, bm); + + pi.yp = yp_max; + break; + } + + if (dx_left > 0) { + pi.x = fix_fint(x_left); + pi.xl = fix_cint(x_left + dx_left); + } else { + pi.x = fix_fint(x_left + dx_left); + pi.xl = fix_cint(x_left); + } + if (dx_right > 0) { + pi.xr0 = fix_fint(x_right); + pi.xr = fix_cint(x_right + dx_right); + } else { + pi.xr0 = fix_fint(x_right + dx_right); + pi.xr = fix_cint(x_right); + } + if (pi.scan_slope > 0) { + x_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.bot - 1) - pi.yp, 1), pi.scan_slope)); + x_min = fix_int(fix_div(fix_make((grd_int_clip.top - 1) - pi.yp, 1), pi.scan_slope)) + 1; + } else { + x_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.top - 1) - pi.yp, 1), pi.scan_slope)); + x_min = fix_int(fix_div(fix_make((grd_int_clip.bot - 1) - pi.yp, 1), pi.scan_slope)) + 1; + } + if (xl_min > x_min) + x_min = xl_min; + if (xr_max < x_max) + x_max = xr_max; + if (pi.xr > x_max) + pi.xr = x_max; + if (pi.xr0 > x_max) + pi.xr0 = x_max; + if (pi.xl > pi.xr0) + pi.xl = pi.xr0; + if (pi.x < x_min) + pi.x = x_min; + pi.i = i_left; + if (pi.xr - pi.x) + pi.di = (i_right - pi.i) / (pi.xr - pi.x); + else { + pi.di = 0x7fffffff; + if (i_right - pi.i < 0) + pi.di = -pi.di; + } + + ((void (*)(grs_per_info *, grs_bitmap *))(ps->scanline_func))(&pi, bm); + + if (pi.yp >= yp_min) { + i_left += di_left, i_right += di_right; + } + x_left += dx_left, x_right += dx_right; + pi.denom += fix_16_20(ps->b), pi.unum += ps->beta_u, pi.vnum += ps->beta_v; + } + } +} + +void gri_lit_per_umap_vscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps) { + grs_per_info pi; + fix x_prime[10]; + fix xp_top, xp_bot; + fix x_top, x_bot; + fix y_top, y_bot; + fix dy_top, dy_bot; + fix i_top, i_bot; + fix di_top, di_bot; + int xp_min, xp_max, xp_next; + int y_min, y_max, yr_min, yr_max, yl_min, yl_max; + int n_min, n_top, n_bot; + int j; + + pi.scale = grd_bm.w; + pi.scan_slope = ps->scan_slope; + pi.dp = ps->dp; + pi.clut = ps->clut; + if (fix_abs(pi.scan_slope) >= FIX_UNIT) + return; + + if (bm->row != 1 << (bm->wlog)) + return; + if (bm->h != 1 << (bm->hlog)) + return; + pi.u_mask = bm->row - 1; + pi.v_mask = (bm->h - 1) << bm->wlog; + pi.v_shift = 16 - bm->wlog; + + xp_min = xp_max = fix_cint(x_prime[n_min = 0] = vpl[0]->x - fix_mul(vpl[0]->y, ps->scan_slope)); + for (j = 1; j < n; j++) { + pi.xp = fix_cint(x_prime[j] = vpl[j]->x - fix_mul(vpl[j]->y, ps->scan_slope)); + if (pi.xp < xp_min) { + xp_min = pi.xp; + n_min = j; + } + if (pi.xp > xp_max) + xp_max = pi.xp; + } + if (xp_max == xp_min) + return; + pi.denom = fix_16_20(ps->c + fix_mul(ps->a, x_prime[0])); + pi.u0 = + vpl[0]->u - fix_div(fix_mul(vpl[0]->x, ps->alpha_u) + fix_mul(vpl[0]->y, ps->beta_u) + ps->gamma_u, pi.denom); + pi.v0 = + vpl[0]->v - fix_div(fix_mul(vpl[0]->x, ps->alpha_v) + fix_mul(vpl[0]->y, ps->beta_v) + ps->gamma_v, pi.denom); + + n_top = n_bot = n_min; + pi.xp = xp_min; + while (fix_cint(x_prime[(n_top + 1) % n]) == pi.xp) + n_top = (n_top + 1) % n; + while (fix_cint(x_prime[(n_bot + n - 1) % n]) == pi.xp) + n_bot = (n_bot + n - 1) % n; + + pi.xp--; + pi.denom = fix_16_20(ps->c + pi.xp * ps->a); + pi.unum = ps->gamma_u + pi.xp * ps->alpha_u; + pi.dunum = ps->beta_u + fix_mul(ps->scan_slope, ps->alpha_u); + pi.vnum = ps->gamma_v + pi.xp * ps->alpha_v; + pi.dvnum = ps->beta_v + fix_mul(ps->scan_slope, ps->alpha_v); + + if (n_bot != n_top) { + + pi.dxl = (vpl[n_bot]->x - vpl[n_top]->x) / pi.scale; + pi.dyl = (vpl[n_bot]->y - vpl[n_top]->y) / pi.scale; + pi.y = fix_cint(vpl[n_top]->y); + pi.yl = fix_cint(vpl[n_bot]->y); + if (pi.scan_slope > 0) { + y_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.right - 1) - pi.xp, 1), pi.scan_slope)); + y_min = fix_int(fix_div(fix_make((grd_int_clip.left - 1) - pi.xp, 1), pi.scan_slope)) + 1; + } else { + y_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.left - 1) - pi.xp, 1), pi.scan_slope)); + y_min = fix_int(fix_div(fix_make((grd_int_clip.right - 1) - pi.xp, 1), pi.scan_slope)) + 1; + } + if (pi.y < y_min) + pi.y = y_min; + if (pi.yl > y_max) + pi.yl = y_max; + pi.yr0 = pi.yr = pi.yl; + pi.i = vpl[n_top]->i; + if (pi.yr - pi.y) + pi.di = (vpl[n_bot]->i - pi.i) / (pi.yr - pi.y); + else { + pi.di = 0x7fffffff; + if (vpl[n_bot]->i - pi.i < 0) + pi.di = -pi.di; + } + + pi.cl = -fix_mul(pi.dxl, vpl[n_top]->y) + fix_mul(pi.dyl, vpl[n_top]->x); + if (pi.y * pi.dxl - fix_mul(fix_make(pi.xp - 1, 0) + pi.y * pi.scan_slope, pi.dyl) + pi.cl < 0) { + pi.dyl = -pi.dyl; + pi.dxl = -pi.dxl; + pi.cl = -pi.cl; + } + if (pi.scan_slope > 0) + pi.dtl = pi.dxl - pi.dyl; + else + pi.dtl = pi.dyl + pi.dxl; + + ((void (*)(grs_per_info *, grs_bitmap *))(ps->scanline_func))(&pi, bm); + + pi.denom += fix_16_20(ps->a), pi.unum += ps->alpha_u, pi.vnum += ps->alpha_v; + xp_min--; /* first line already done */ + } + pi.xp++; + while (pi.xp < xp_max) { + /* check top edge */ + if (fix_cint(x_prime[n_top]) <= pi.xp) { + int n_prev, dxp; + fix d; + do { + if (fix_cint(x_prime[n_top]) == pi.xp) + n_prev = n_top; + if (++n_top == n) + n_top = 0; + } while (fix_cint(x_prime[n_top]) <= pi.xp); + xp_top = x_prime[n_prev]; + x_top = vpl[n_prev]->x; + y_top = vpl[n_prev]->y; + i_top = vpl[n_prev]->i; + dxp = fix_cint(x_prime[n_top]) - fix_cint(xp_top); + di_top = (vpl[n_top]->i - i_top) / dxp; + yl_min = fix_cint(y_top); + yl_max = fix_cint(vpl[n_top]->y); + pi.dxl = (vpl[n_top]->x - x_top) / pi.scale; + pi.dyl = (vpl[n_top]->y - y_top) / pi.scale; + pi.cl = -fix_mul(pi.dxl, y_top) + fix_mul(pi.dyl, x_top); + d = pi.dxl - fix_mul(pi.dyl, pi.scan_slope); + dy_top = fix_div(pi.dyl, d); + y_top = fix_div(fix_mul(pi.dxl, y_top) + fix_mul(pi.dyl, fix_ceil(xp_top) - x_top), d); + if (yl_max < yl_min) { + fix foo = yl_min; + yl_min = yl_max; + yl_max = foo; + } + if (fix_mul(vpl[n_top]->y - FIX_UNIT, pi.dxl) - fix_mul(vpl[n_top]->x - pi.scan_slope, pi.dyl) + pi.cl < + 0) { + pi.dyl = -pi.dyl; + pi.dxl = -pi.dxl; + pi.cl = -pi.cl; + } + if (pi.scan_slope > 0) + pi.dtl = pi.dxl - pi.dyl; + else + pi.dtl = pi.dyl + pi.dxl; + xp_top = x_prime[n_top]; + } + + /* check bot edge */ + if (fix_cint(x_prime[n_bot]) <= pi.xp) { + int n_prev, dxp; + fix d; + do { + if (fix_cint(x_prime[n_bot]) == pi.xp) + n_prev = n_bot; + if (--n_bot < 0) + n_bot = n - 1; + } while (fix_cint(x_prime[n_bot]) <= pi.xp); + xp_bot = x_prime[n_prev]; + x_bot = vpl[n_prev]->x; + y_bot = vpl[n_prev]->y; + i_bot = vpl[n_prev]->i; + dxp = fix_cint(x_prime[n_bot]) - fix_cint(xp_bot); + di_bot = (vpl[n_bot]->i - i_bot) / dxp; + yr_min = fix_cint(y_bot); + yr_max = fix_cint(vpl[n_bot]->y); + pi.dxr = (vpl[n_bot]->x - x_bot) / pi.scale; + pi.dyr = (vpl[n_bot]->y - y_bot) / pi.scale; + pi.cr = -fix_mul(pi.dxr, y_bot) + fix_mul(pi.dyr, x_bot); + d = pi.dxr - fix_mul(pi.dyr, pi.scan_slope); + dy_bot = fix_div(pi.dyr, d); + y_bot = fix_div(fix_mul(pi.dxr, y_bot) + fix_mul(pi.dyr, fix_ceil(xp_bot) - x_bot), d); + if (yr_max < yr_min) { + fix foo = yr_min; + yr_min = yr_max; + yr_max = foo; + } + if (fix_mul(vpl[n_bot]->y - FIX_UNIT, pi.dxr) - fix_mul(vpl[n_bot]->x - pi.scan_slope, pi.dyr) + pi.cr < + 0) { + pi.dyr = -pi.dyr; + pi.dxr = -pi.dxr; + pi.cr = -pi.cr; + } + if (pi.scan_slope > 0) + pi.dtr = pi.dxr - pi.dyr; + else + pi.dtr = pi.dyr + pi.dxr; + xp_bot = x_prime[n_bot]; + } + xp_next = (xp_bot < xp_top) ? fix_cint(xp_bot) : fix_cint(xp_top); + + /* do 0th scanline if at xp_min */ + if (pi.xp == xp_min) { + y_top -= dy_top; + y_bot -= dy_bot; + pi.xp--; + } + for (; pi.xp < xp_next; pi.xp++) { + if ((pi.xp + 1 == xp_max) && (n_top != n_bot)) { + pi.dxl = (vpl[n_bot]->x - vpl[n_top]->x) / pi.scale; + pi.dyl = (vpl[n_bot]->y - vpl[n_top]->y) / pi.scale; + pi.y = fix_cint(vpl[n_top]->y); + pi.yl = fix_cint(vpl[n_bot]->y); + if (pi.scan_slope > 0) { + y_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.right - 1) - pi.xp, 1), pi.scan_slope)); + y_min = fix_int(fix_div(fix_make((grd_int_clip.left - 1) - pi.xp, 1), pi.scan_slope)) + 1; + } else { + y_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.left - 1) - pi.xp, 1), pi.scan_slope)); + y_min = fix_int(fix_div(fix_make((grd_int_clip.right - 1) - pi.xp, 1), pi.scan_slope)) + 1; + } + if (pi.y < y_min) + pi.y = y_min; + if (pi.yl > y_max) + pi.yl = y_max; + pi.yr0 = pi.yr = pi.yl; + pi.i = i_top; + if (pi.yr - pi.y) + pi.di = (i_bot - pi.i) / (pi.yr - pi.y); + else { + pi.di = 0x7fffffff; + if (i_bot - pi.i < 0) + pi.di = -pi.di; + } + + pi.cl = -fix_mul(pi.dxl, vpl[n_top]->y) + fix_mul(pi.dyl, vpl[n_top]->x); + if (pi.y * pi.dxl - fix_mul(fix_make(pi.xp + 1, 0) + pi.y * pi.scan_slope, pi.dyl) + pi.cl < 0) { + pi.dyl = -pi.dyl; + pi.dxl = -pi.dxl; + pi.cl = -pi.cl; + } + if (pi.scan_slope > 0) + pi.dtl = pi.dxl - pi.dyl; + else + pi.dtl = pi.dyl + pi.dxl; + + ((void (*)(grs_per_info *, grs_bitmap *))(ps->scanline_func))(&pi, bm); + + pi.xp = xp_max; + break; + } + + if (dy_top > 0) { + pi.y = fix_fint(y_top); + pi.yl = fix_cint(y_top + dy_top); + } else { + pi.y = fix_fint(y_top + dy_top); + pi.yl = fix_cint(y_top); + } + if (dy_bot > 0) { + pi.yr0 = fix_fint(y_bot); + pi.yr = fix_cint(y_bot + dy_bot); + } else { + pi.yr0 = fix_fint(y_bot + dy_bot); + pi.yr = fix_cint(y_bot); + } + if (pi.scan_slope > 0) { + y_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.right - 1) - pi.xp, 1), pi.scan_slope)); + y_min = fix_int(fix_div(fix_make((grd_int_clip.left - 1) - pi.xp, 1), pi.scan_slope)) + 1; + } else { + y_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.left - 1) - pi.xp, 1), pi.scan_slope)); + y_min = fix_int(fix_div(fix_make((grd_int_clip.right - 1) - pi.xp, 1), pi.scan_slope)) + 1; + } + if (yl_min > y_min) + y_min = yl_min; + if (yr_max < y_max) + y_max = yr_max; + if (pi.yr > y_max) + pi.yr = y_max; + if (pi.yr0 > y_max) + pi.yr0 = y_max; + if (pi.yl > pi.yr0) + pi.yl = pi.yr0; + if (pi.y < y_min) + pi.y = y_min; + pi.i = i_top; + if (pi.yr - pi.y) + pi.di = (i_bot - pi.i) / (pi.yr - pi.y); + else { + pi.di = 0x7fffffff; + if (i_bot - pi.i < 0) + pi.di = -pi.di; + } + + ((void (*)(grs_per_info *, grs_bitmap *))(ps->scanline_func))(&pi, bm); + + if (pi.xp >= xp_min) { + i_top += di_top, i_bot += di_bot; + } + + y_top += dy_top, y_bot += dy_bot; + pi.denom += fix_16_20(ps->a), pi.unum += ps->alpha_u, pi.vnum += ps->alpha_v; + } + } +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8ltp.c b/engine/src/Libraries/2D/Source/Flat8/fl8ltp.c new file mode 100644 index 0000000..166d9a6 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8ltp.c @@ -0,0 +1,329 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/FL8ltp.c $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/08/16 12:57:21 $ + * + * lit full perspective texture mapper. + * scanline processors. + * + */ + +#include "cnvdat.h" +#include "fl8tmapdv.h" +#include "pertyp.h" +#include "plytyp.h" +#include "scrdat.h" +#include "tmapint.h" + +// prototypes +void gri_trans_lit_per_umap_hscan_scanline(grs_per_info *pi, grs_bitmap *bm); +void gri_trans_lit_per_umap_vscan_scanline(grs_per_info *pi, grs_bitmap *bm); +void gri_trans_lit_per_umap_hscan_init(grs_bitmap *bm, grs_per_setup *ps); +void gri_trans_lit_per_umap_vscan_init(grs_bitmap *bm, grs_per_setup *ps); + +void gri_trans_lit_per_umap_hscan_scanline(grs_per_info *pi, grs_bitmap *bm) { + register int k, y_cint; + uchar *ltab = grd_screen->ltab; + + // locals used to speed PPC code + fix test; + fix l_u, l_v, l_du, l_dv, l_y_fix, l_scan_slope, l_dtl, l_dxl, l_dyl, l_dtr, l_dyr, l_i, l_di; + int l_x, l_xl, l_xr, l_xr0, l_u_mask, l_v_mask, l_v_shift; + int gr_row, temp_y; + uchar *bm_bits, *p; + + gr_row = grd_bm.row; + bm_bits = bm->bits; + l_di = pi->di; + l_i = pi->i; + l_dyr = pi->dyr; + l_dtr = pi->dtr; + l_dyl = pi->dyl; + l_dxl = pi->dxl; + l_dtl = pi->dtl; + l_scan_slope = pi->scan_slope; + l_y_fix = pi->y_fix; + l_v_shift = pi->v_shift; + l_v_mask = pi->v_mask; + l_u_mask = pi->u_mask; + l_xr0 = pi->xr0; + l_x = pi->x; + l_xl = pi->xl; + l_xr = pi->xr; + l_u = pi->u; + l_v = pi->v; + l_du = pi->du; + l_dv = pi->dv; + + l_y_fix = l_x * l_scan_slope + fix_make(pi->yp, 0xffff); + +#if InvDiv + k = fix_div(fix_make(1, 0), pi->denom); + l_u = pi->u0 + fix_mul_asm_safe(pi->unum, k); + l_v = pi->v0 + fix_mul_asm_safe(pi->vnum, k); + l_du = fix_mul_asm_safe(pi->dunum, k); + l_dv = fix_mul_asm_safe(pi->dvnum, k); +#else + l_u = pi->u0 + fix_div(pi->unum, pi->denom); + l_v = pi->v0 + fix_div(pi->vnum, pi->denom); + l_du = fix_div(pi->dunum, pi->denom); + l_dv = fix_div(pi->dvnum, pi->denom); +#endif + + l_u += l_x * l_du; + l_v += l_x * l_dv; + + y_cint = fix_int(l_y_fix); + if (l_scan_slope < 0) + gr_row = -gr_row; + + p = grd_bm.bits + l_x + y_cint * grd_bm.row; + if (l_x < l_xl) { + test = l_x * l_dyl - y_cint * l_dxl + pi->cl; + for (; l_x < l_xl; l_x++) { + if (test <= 0) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + k = bm_bits[k]; + if (k) + *p = ltab[(fix_light(l_i)) + k]; // gr_fill_upixel(ltab[(fix_int(l_i)<<8)+k],l_x,y_cint); + } + + temp_y = y_cint; + y_cint = fix_int(l_y_fix += l_scan_slope); + if (temp_y != y_cint) { + p += gr_row; + test += l_dtl; + } else + test += l_dyl; + + p++; + l_u += l_du; + l_v += l_dv; + l_i += l_di; + } + } + + for (; l_x < l_xr0; l_x++) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + k = bm_bits[k]; + if (k) + *p = ltab[(fix_light(l_i)) + k]; // gr_fill_upixel(ltab[(fix_int(l_i)<<8)+k],l_x,y_cint); + + temp_y = y_cint; + y_cint = fix_int(l_y_fix += l_scan_slope); + if (temp_y != y_cint) + p += gr_row; + + p++; + l_u += l_du; + l_v += l_dv; + l_i += l_di; + } + + if (l_x < l_xr) { + test = l_x * l_dyr - y_cint * pi->dxr + pi->cr; + p = grd_bm.bits + l_x + y_cint * grd_bm.row; + for (; l_x < l_xr; l_x++) { + if (test >= 0) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + k = bm_bits[k]; + if (k) + *p = ltab[(fix_light(l_i)) + k]; // gr_fill_upixel(ltab[(fix_int(l_i)<<8)+k],l_x,y_cint); + } + + temp_y = y_cint; + y_cint = fix_int(l_y_fix += l_scan_slope); + if (temp_y != y_cint) { + p += gr_row; + test += l_dtr; + } else + test += l_dyr; + + p++; + l_u += l_du; + l_v += l_dv; + l_i += l_di; + } + } + + pi->y_fix = l_y_fix; + pi->x = l_x; + pi->u = l_u; + pi->v = l_v; + pi->du = l_du; + pi->dv = l_dv; + pi->di = l_di; + pi->i = l_i; +} + +void gri_trans_lit_per_umap_vscan_scanline(grs_per_info *pi, grs_bitmap *bm) { + register int k, x_cint; + uchar *ltab = grd_screen->ltab; + + // locals used to speed PPC code + fix test; + fix l_dxr, l_x_fix, l_u, l_v, l_du, l_dv, l_scan_slope, l_dtl, l_dxl, l_dyl, l_dtr, l_dyr, l_i, l_di; + int l_yl, l_yr0, l_yr, l_y, l_u_mask, l_v_mask, l_v_shift; + int gr_row, temp_x; + uchar *bm_bits; + uchar *p; + + gr_row = grd_bm.row; + bm_bits = bm->bits; + l_di = pi->di; + l_i = pi->i; + l_dxr = pi->dxr; + l_x_fix = pi->x_fix; + l_y = pi->y; + l_yr = pi->yr; + l_yr0 = pi->yr0; + l_yl = pi->yl; + l_dyr = pi->dyr; + l_dtr = pi->dtr; + l_dyl = pi->dyl; + l_dxl = pi->dxl; + l_dtl = pi->dtl; + l_scan_slope = pi->scan_slope; + l_v_shift = pi->v_shift; + l_v_mask = pi->v_mask; + l_u_mask = pi->u_mask; + l_u = pi->u; + l_v = pi->v; + l_du = pi->du; + l_dv = pi->dv; + + l_x_fix = l_y * l_scan_slope + fix_make(pi->xp, 0xffff); + +#if InvDiv + k = fix_div(fix_make(1, 0), pi->denom); + l_u = pi->u0 + fix_mul_asm_safe(pi->unum, k); + l_v = pi->v0 + fix_mul_asm_safe(pi->vnum, k); + l_du = fix_mul_asm_safe(pi->dunum, k); + l_dv = fix_mul_asm_safe(pi->dvnum, k); +#else + l_u = pi->u0 + fix_div(pi->unum, pi->denom); + l_v = pi->v0 + fix_div(pi->vnum, pi->denom); + l_du = fix_div(pi->dunum, pi->denom); + l_dv = fix_div(pi->dvnum, pi->denom); +#endif + + l_u += l_y * l_du; + l_v += l_y * l_dv; + + x_cint = fix_int(l_x_fix); + p = grd_bm.bits + x_cint + l_y * gr_row; + if (l_y < l_yl) { + test = l_y * l_dxl - x_cint * l_dyl + pi->cl; + for (; l_y < l_yl; l_y++) { + if (test <= 0) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + k = bm_bits[k]; + if (k) + *p = ltab[(fix_light(l_i)) + k]; // gr_fill_upixel(ltab[(fix_int(l_i)<<8)+k],x_cint,l_y); + } + + temp_x = x_cint; + x_cint = fix_int(l_x_fix += l_scan_slope); + if (temp_x != x_cint) { + test += l_dtl; + p -= (temp_x - x_cint); + } else + test += l_dxl; + + p += gr_row; + l_u += l_du; + l_v += l_dv; + l_i += l_di; + } + } + + for (; l_y < l_yr0; l_y++) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + k = bm_bits[k]; + if (k) + *p = ltab[(fix_light(l_i)) + k]; // gr_fill_upixel(ltab[(fix_int(l_i)<<8)+k],x_cint,l_y); + + temp_x = x_cint; + x_cint = fix_int(l_x_fix += l_scan_slope); + if (temp_x != x_cint) + p -= (temp_x - x_cint); + + p += gr_row; + l_u += l_du; + l_v += l_dv; + l_i += l_di; + } + + if (l_y < l_yr) { + test = l_y * l_dxr - x_cint * l_dyr + pi->cr; + p = grd_bm.bits + x_cint + l_y * gr_row; + for (; l_y < l_yr; l_y++) { + if (test >= 0) { + k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + k = bm_bits[k]; + if (k) + *p = ltab[(fix_light(l_i)) + k]; // gr_fill_upixel(ltab[(fix_int(l_i)<<8)+k],x_cint,l_y); + } + + temp_x = x_cint; + x_cint = fix_int(l_x_fix += l_scan_slope); + if (temp_x != x_cint) { + test += l_dtr; + p -= (temp_x - x_cint); + } else + test += l_dxr; + + p += gr_row; + l_u += l_du; + l_v += l_dv; + l_i += l_di; + } + } + + pi->x_fix = l_x_fix; + pi->y = l_y; + pi->u = l_u; + pi->v = l_v; + pi->du = l_du; + pi->dv = l_dv; + pi->di = l_di; + pi->i = l_i; +} + +extern void gri_lit_per_umap_hscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps); +extern void gri_lit_per_umap_vscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps); + +void gri_trans_lit_per_umap_hscan_init(grs_bitmap *bm, grs_per_setup *ps) { + ps->shell_func = (void (*)())gri_lit_per_umap_hscan; + ps->scanline_func = (void (*)())gri_trans_lit_per_umap_hscan_scanline; +} + +void gri_trans_lit_per_umap_vscan_init(grs_bitmap *bm, grs_per_setup *ps) { + ps->shell_func = (void (*)())gri_lit_per_umap_vscan; + ps->scanline_func = (void (*)())gri_trans_lit_per_umap_vscan_scanline; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8lw.c b/engine/src/Libraries/2D/Source/Flat8/fl8lw.c new file mode 100644 index 0000000..7c2c3e2 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8lw.c @@ -0,0 +1,381 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8lw.c $ + * $Revision: 1.4 $ + * $Author: kevin $ + * $Date: 1994/08/16 12:50:16 $ + * + * Routines to wall floor map a flat8 bitmap to a generic canvas. + * + * This file is part of the 2d library. + * + */ + +#include "cnvdat.h" +#include "fl8tf.h" +#include "fl8tmapdv.h" +#include "gente.h" +#include "poly.h" +#include "scrmac.h" +#include "tmapint.h" +#include "vtab.h" + +int gri_lit_wall_umap_loop(grs_tmap_loop_info *tli); +int gri_lit_wall_umap_loop_1D(grs_tmap_loop_info *tli); + +int gri_lit_wall_umap_loop(grs_tmap_loop_info *tli) { + fix u, v, i, du, dv, di, dy, d; + + // locals used to store copies of tli-> stuff, so its in registers on the PPC + int k, y; + uint32_t t_mask; + uint32_t t_wlog; + uchar *t_bits; + uchar *p_dest; + int32_t gr_row; + uchar *g_ltab; + fix inv_dy; + int32_t *t_vtab; + +#if InvDiv + inv_dy = fix_div(fix_make(1, 0), tli->w); + u = fix_mul_asm_safe(tli->left.u, inv_dy); + du = fix_mul_asm_safe(tli->right.u, inv_dy) - u; + v = fix_mul_asm_safe(tli->left.v, inv_dy); + dv = fix_mul_asm_safe(tli->right.v, inv_dy) - v; + i = fix_mul_asm_safe(tli->left.i, inv_dy); + di = fix_mul_asm_safe(tli->right.i, inv_dy) - i; + if (di >= -256 && di <= 256) + i += 1024; +#else + u = fix_div(tli->left.u, tli->w); + du = fix_div(tli->right.u, tli->w) - u; + v = fix_div(tli->left.v, tli->w); + dv = fix_div(tli->right.v, tli->w) - v; + i = fix_div(tli->left.i, tli->w); + di = fix_div(tli->right.i, tli->w) - i; + if (di >= -256 && di <= 256) + i += 1024; +#endif + + dy = tli->right.y - tli->left.y; + + t_mask = tli->mask; + t_wlog = tli->bm.wlog; + g_ltab = grd_screen->ltab; + t_vtab = tli->vtab; + t_bits = tli->bm.bits; + gr_row = grd_bm.row; + + do { + if ((d = fix_ceil(tli->right.y) - fix_ceil(tli->left.y)) > 0) { + d = fix_ceil(tli->left.y) - tli->left.y; + +#if InvDiv + inv_dy = fix_div(fix_make(1, 0) << 8, dy); + di = fix_mul_asm_safe_light(di, inv_dy); + inv_dy >>= 8; + du = fix_mul_asm_safe(du, inv_dy); + dv = fix_mul_asm_safe(dv, inv_dy); +#else + du = fix_div(du, dy); + dv = fix_div(dv, dy); + di = fix_div(di, dy); +#endif + u += fix_mul(du, d); + v += fix_mul(dv, d); + i += fix_mul(di, d); + + y = fix_cint(tli->right.y) - fix_cint(tli->left.y); + p_dest = grd_bm.bits + (gr_row * fix_cint(tli->left.y)) + tli->x; + + switch (tli->bm.hlog) { + case GRL_OPAQUE: + for (; y > 0; y--) { + k = t_vtab[fix_fint(v)] + fix_fint(u); + *p_dest = g_ltab[t_bits[k] + fix_light(i)]; // gr_fill_upixel(g_ltab[t_bits[k]+fix_light(i)],t_x,y); + p_dest += gr_row; + u += du; + v += dv; + i += di; + } + break; + case GRL_TRANS: + for (; y > 0; y--) { + k = t_vtab[fix_fint(v)] + fix_fint(u); + k = t_bits[k]; + if (k != 0) + *p_dest = g_ltab[k + fix_light(i)]; // gr_fill_upixel(g_ltab[k+fix_light(i)],t_x,y); + p_dest += gr_row; + u += du; + v += dv; + i += di; + } + break; + case GRL_OPAQUE | GRL_LOG2: + for (; y > 0; y--) { + k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + *p_dest = g_ltab[t_bits[k] + fix_light(i)]; + p_dest += gr_row; + u += du; + v += dv; + i += di; + } + break; + case GRL_TRANS | GRL_LOG2: + for (; y > 0; y--) { + k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + k = t_bits[k]; + if (k != 0) + *p_dest = g_ltab[k + fix_light(i)]; // gr_fill_upixel(g_ltab[k+fix_light(i)],t_x,y); + p_dest += gr_row; + u += du; + v += dv; + i += di; + } + break; + } + } else if (d < 0) + return TRUE; /* punt this tmap */ + + tli->w += tli->dw; + + // figure out new left u & v & i + inv_dy = 0; + k = tli->left.u + tli->left.du; + y = tli->left.v + tli->left.dv; + tli->left.i += tli->left.di; + +#if InvDiv + inv_dy = fix_div(fix_make(1, 0), tli->w); + u = fix_mul_asm_safe(k, inv_dy); + v = fix_mul_asm_safe(y, inv_dy); + i = fix_mul_asm_safe(tli->left.i, inv_dy); + if (di >= -256 && di <= 256) + i += 1024; +#else + u = fix_div(k, tli->w); + v = fix_div(y, tli->w); + i = fix_div(tli->left.i, tli->w); + if (di >= -256 && di <= 256) + i += 1024; +#endif + + tli->left.u = k; + tli->left.v = y; + + // figure out new right u & v & i + k = tli->right.u + tli->right.du; + y = tli->right.v + tli->right.dv; + tli->right.i += tli->right.di; + +#if InvDiv + du = fix_mul_asm_safe(k, inv_dy) - u; + dv = fix_mul_asm_safe(y, inv_dy) - v; + di = fix_mul_asm_safe(tli->right.i, inv_dy) - i; +#else + du = fix_div(k, tli->w) - u; + dv = fix_div(y, tli->w) - v; + di = fix_div(tli->right.i, tli->w) - i; +#endif + tli->right.u = k; + tli->right.v = y; + + tli->left.y += tli->left.dy; + tli->right.y += tli->right.dy; + dy = tli->right.y - tli->left.y; + tli->x++; + } while (--(tli->n) > 0); + + return FALSE; /* tmap OK */ +} + +void gri_trans_lit_wall_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_TRANS | GRL_LOG2; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_TRANS; + } + tli->loop_func = (void (*)())gri_lit_wall_umap_loop; + tli->left_edge_func = (void (*)())gri_uviwy_edge; + tli->right_edge_func = (void (*)())gri_uviwy_edge; +} + +void gri_opaque_lit_wall_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_OPAQUE | GRL_LOG2; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_OPAQUE; + } + tli->loop_func = (void (*)())gri_lit_wall_umap_loop; + tli->left_edge_func = (void (*)())gri_uviwy_edge; + tli->right_edge_func = (void (*)())gri_uviwy_edge; +} + +/*extern "C" +{ +extern int HandleWallLitLoop1D_PPC(grs_tmap_loop_info *tli, + fix u, fix v, fix i, fix dv, fix di, fix dy, + uchar *g_ltab, int32_t *t_vtab, uchar *o_bits, + int32_t gr_row, uint32_t t_mask, uint32_t t_wlog); +}*/ + +int HandleWallLitLoop1D_C(grs_tmap_loop_info *tli, fix u, fix v, fix i, fix dv, fix di, fix dy, uchar *g_ltab, + uchar *o_bits, int32_t gr_row, uint32_t t_mask, uint32_t t_wlog) { + fix d, inv_dy; + register fix lefty, righty; + int32_t k, y; + uchar *t_bits; + uchar *p_dest; + + lefty = tli->left.y; + righty = tli->right.y; + do { + if ((d = fix_ceil(righty) - fix_ceil(lefty)) > 0) { + d = fix_ceil(lefty) - lefty; + + inv_dy = fix_div(fix_make(1, 0) << 8, dy); + dv = fix_mul_asm_safe(dv, inv_dy >> 8); + di = fix_mul_asm_safe_light(di, inv_dy); + + v += fix_mul(dv, d); + i += fix_mul(di, d); + + if (di >= -256 && di <= 256) + i += 256; + + y = fix_cint(righty) - fix_cint(lefty); + p_dest = grd_bm.bits + (gr_row * fix_cint(lefty)) + tli->x; + t_bits = o_bits + fix_fint(u); + + // inner loop + for (; y > 0; y--) { + k = (fix_fint(v) << t_wlog) & t_mask; + *p_dest = g_ltab[t_bits[k] + fix_light(i)]; + p_dest += gr_row; + v += dv; + i += di; + } + + } else if (d < 0) + return TRUE; // punt this tmap + + tli->w += tli->dw; + + // figure out new left u & v & i + k = tli->left.u + tli->left.du; + y = tli->left.v + tli->left.dv; + tli->left.i += tli->left.di; + + inv_dy = fix_div(fix_make(1, 0), tli->w); + u = fix_mul_asm_safe(k, inv_dy); + v = fix_mul_asm_safe(y, inv_dy); + i = fix_mul_asm_safe(tli->left.i, inv_dy); + + tli->left.u = k; + tli->left.v = y; + + // figure out new right u & v & i + k = tli->right.u + tli->right.du; + y = tli->right.v + tli->right.dv; + tli->right.i += tli->right.di; + + dv = fix_mul_asm_safe(y, inv_dy) - v; + di = fix_mul_asm_safe(tli->right.i, inv_dy) - i; + if (di >= -256 && di <= 256) + i += 1024; + + tli->right.u = k; + tli->right.v = y; + + lefty += tli->left.dy; + righty += tli->right.dy; + dy = righty - lefty; + tli->x++; + } while (--(tli->n) > 0); + + tli->left.y = lefty; + tli->right.y = righty; + + return FALSE; // tmap OK +} + +// ================================================================================== +// Wall_1D versions of routines +int gri_lit_wall_umap_loop_1D(grs_tmap_loop_info *tli) { + fix u, v, i, dv, di, dy; + + // locals used to store copies of tli-> stuff, so its in registers on the PPC + int k, y; + uint32_t t_mask; + uint32_t t_wlog; + int32_t gr_row; + uchar *g_ltab; + uchar *o_bits; + fix inv_dy; + +#if InvDiv + inv_dy = fix_div(fix_make(1, 0), tli->w); + u = fix_mul_asm_safe(tli->left.u, inv_dy); + v = fix_mul_asm_safe(tli->left.v, inv_dy); + dv = fix_mul_asm_safe(tli->right.v, inv_dy) - v; + i = fix_mul_asm_safe(tli->left.i, inv_dy); + di = fix_mul_asm_safe(tli->right.i, inv_dy) - i; + if (di >= -256 && di <= 256) + i += 512; +#else + u = fix_div(tli->left.u, tli->w); + v = fix_div(tli->left.v, tli->w); + dv = fix_div(tli->right.v, tli->w) - v; + i = fix_div(tli->left.i, tli->w); + di = fix_div(tli->right.i, tli->w) - i; + if (di >= -256 && di <= 256) + i += 512; +#endif + + dy = tli->right.y - tli->left.y; + + t_mask = tli->mask; + t_wlog = tli->bm.wlog; + g_ltab = grd_screen->ltab; + o_bits = tli->bm.bits; + gr_row = grd_bm.row; + + return HandleWallLitLoop1D_C(tli, u, v, i, dv, di, dy, g_ltab, o_bits, gr_row, t_mask, t_wlog); +} + +void gri_opaque_lit_wall1d_umap_init(grs_tmap_loop_info *tli) { + // Wall1D is always log2 + /* if ((tli->bm.row==(1<bm.wlog)) && + (tli->bm.h==(1<bm.hlog))) {*/ + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_OPAQUE | GRL_LOG2; + /* } else { + tli->vtab=gr_make_vtab(&(tli->bm)); + tli->bm.hlog=GRL_OPAQUE; + }*/ + tli->loop_func = (void (*)())gri_lit_wall_umap_loop_1D; + tli->left_edge_func = (void (*)())gri_uviwy_edge; + tli->right_edge_func = (void (*)())gri_uviwy_edge; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8mono.c b/engine/src/Libraries/2D/Source/Flat8/fl8mono.c new file mode 100644 index 0000000..c2d051e --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8mono.c @@ -0,0 +1,105 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/fl8mono.c $ + * $Revision: 1.3 $ + * $Author: kaboom $ + * $Date: 1993/10/19 09:50:39 $ + * + * Routines for drawing monochrome bitmaps into a flat 8 canvas. + * + * This file is part of the 2d library. + * + * $Log: fl8mono.c $ + * Revision 1.3 1993/10/19 09:50:39 kaboom + * Replaced #include h; + p_row = bm->bits; + p_dst = grd_bm.bits + y * grd_bm.row + x; + + if (bm->flags & BMF_TRANS) { + /* transparent bitmap; draw 1's as fcolor, don't draw 0's. */ + while (h-- > 0) { + /* set up scanline. */ + bit = bm->align; + p_src = p_row; + w = bm->w; + + while (w-- > 0) { + /* do current scanline. */ + if (*p_src & bitmask[bit]) + *p_dst++ = grd_gc.fcolor; + else + p_dst++; + if (++bit > 7) { + bit = 0; + p_src++; + } + } + p_dst += grd_bm.row - bm->w; + p_row += bm->row; + } + } else { + /* opaque bitmap; draw 1's as fcolor, 0's as bcolor. */ + while (h-- > 0) { + bit = bm->align; + p_src = p_row; + w = bm->w; + + while (w-- > 0) { + if (*p_src & bitmask[bit]) + *p_dst++ = grd_gc.fcolor; + else + *p_dst++ = grd_gc.bcolor; + if (++bit > 7) { + bit = 0; + p_src++; + } + } + p_dst += grd_bm.row - bm->w; + p_row += bm->row; + } + } +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8mscl.c b/engine/src/Libraries/2D/Source/Flat8/fl8mscl.c new file mode 100644 index 0000000..737f9e7 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8mscl.c @@ -0,0 +1,183 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* $Source: n:/project/lib/src/2d/RCS/fl8mscl.c $ + * $Revision: 1.1 $ + * $Author: lmfeeney $ + * $Date: 1994/04/09 00:18:42 $ + */ + +#include "bit.h" +#include "bitmap.h" +#include "clpcon.h" +#include "cnvdat.h" + +void flat8_mono_scale_ubitmap(grs_bitmap *bm, short x, short y, short w, short h) { + fix x_scale; /* x scale factor */ + fix y_scale; /* y scale factor */ + fix x_src; /* fractional x in source bitmap */ + fix y_src; /* y */ + uchar *p_src; /* pointer into source bitmap */ + uchar *p_dst; /* pointer into destination bitmap */ + int index, bit; /* calculate indexes into char and bitmap */ + int i; + + /* if either width or height is to be 0, no problem. */ + if (w == 0 || h == 0) + return; + + x_scale = (bm->w << 16) / w; + y_scale = (bm->h << 16) / h; + + y_src = y_scale >> 1; + p_dst = grd_bm.bits + y * grd_bm.row + x; + + if (bm->flags & BMF_TRANS) + while (h-- > 0) { + p_src = bm->bits + fix_int(y_src) * bm->row; + x_src = (bm->align << 16) + (x_scale >> 1); + + for (i = 0; i < w; i++) { + index = x_src >> (16 + 3); + bit = (x_src >> 16) & 0x0007; + + if (p_src[index] & bitmask[bit]) + *p_dst++ = grd_gc.fcolor; + else + p_dst++; + + x_src += x_scale; + } + p_dst += grd_bm.row - w; + y_src += y_scale; + } + else + while (h-- > 0) { + p_src = bm->bits + fix_int(y_src) * bm->row; + x_src = (bm->align << 16) + (x_scale >> 4); + + for (i = 0; i < w; i++) { + index = x_src >> (16 + 3); + bit = (x_src >> 16) & 0x0007; + + if (p_src[index] & bitmask[bit]) + *p_dst++ = grd_gc.fcolor; + else + *p_dst++ = grd_gc.bcolor; + + x_src += x_scale; + } + + p_dst += grd_bm.row - w; + y_src += y_scale; + } +} + +int flat8_mono_scale_bitmap(grs_bitmap *bm, short x, short y, short w, short h) { + fix x_left; + fix x_scale; /* x scale factor */ + fix y_scale; /* y scale factor */ + fix x_src; /* fractional x in source bitmap */ + fix y_src; /* y */ + uchar *p_src; /* pointer into source bitmap */ + uchar *p_dst; + int code; + int index = 0, bit = 0; /* char, bit in char in bitmask */ + int i; + + /* if either width or height is to be 0, no problem. */ + if (w == 0 || h == 0) + return CLIP_ALL; + + /* check for trivial reject clip. */ + if (x > grd_clip.right || x + w <= grd_clip.left || y > grd_clip.bot || y + h <= grd_clip.top) + return CLIP_ALL; + + x_scale = (bm->w << 16) / w; + y_scale = (bm->h << 16) / h; + x_left = y_src = 0; + code = CLIP_NONE; + + if (x < grd_clip.left) { + x_left = x_scale * (grd_clip.left - x); + w -= grd_clip.left - x; + x = grd_clip.left; + code |= CLIP_LEFT; + } + if (x + w > grd_clip.right) { + w = grd_clip.right - x; + code |= CLIP_RIGHT; + } + if (y < grd_clip.top) { + y_src = y_scale * (grd_clip.top - y); + h -= grd_clip.top - y; + y = grd_clip.top; + code |= CLIP_TOP; + } + if (y + h > grd_clip.bot) { + h = grd_clip.bot - y; + code |= CLIP_BOT; + } + + x_left += (bm->align << 16); + x_left += (x_scale >> 1); + y_src += y_scale >> 1; + p_dst = grd_bm.bits + y * grd_bm.row + x; + + if (bm->flags & BMF_TRANS) + while (h-- > 0) { + p_src = bm->bits + fix_int(y_src) * bm->row; + x_src = x_left; + + for (i = w; i > 0; i--) { + index = x_src >> (16 + 3); + bit = (x_src >> 16) & 0x0007; + + if (p_src[index] & bitmask[bit]) + *p_dst++ = grd_gc.fcolor; + else + p_dst++; + + x_src += x_scale; + } + + p_dst += grd_bm.row - w; + y_src += y_scale; + } + else + + while (h-- > 0) { + p_src = bm->bits + fix_int(y_src) * bm->row; + x_src = x_left; + + for (i = w; i > 0; i--) { + + if (p_src[index] & bitmask[bit]) + *p_dst++ = grd_gc.fcolor; + else + *p_dst++ = grd_gc.bcolor; + + x_src += x_scale; + } + + p_dst += grd_bm.row - w; + y_src += y_scale; + } + + return code; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8nl.c b/engine/src/Libraries/2D/Source/Flat8/fl8nl.c new file mode 100644 index 0000000..b1f00ff --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8nl.c @@ -0,0 +1,237 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8nl.c $ + * $Revision: 1.3 $ + * $Author: kevin $ + * $Date: 1994/08/16 13:12:41 $ + * + * Routines to linearly texture map a flat8 bitmap to a generic canvas. + * + * This file is part of the 2d library. + * + */ + +#include "cnvdat.h" +#include "fl8tf.h" +#include "fl8tmapdv.h" +#include "gente.h" +#include "plytyp.h" +#include "poly.h" +#include "tlucdat.h" +#include "tmapint.h" +#include "vtab.h" + +// prototypes +int gri_tluc8_lin_umap_loop(grs_tmap_loop_info *tli); + +int gri_tluc8_lin_umap_loop(grs_tmap_loop_info *tli) +{ + fix u = tli->left.u, du = tli->right.u - u; + fix v = tli->left.v, dv = tli->right.v - v; + int32_t *t_vtab = tli->vtab; + uchar *t_bits = tli->bm.bits; + uchar *t_clut = tli->clut; + uint32_t t_mask = tli->mask; + uchar t_wlog = tli->bm.wlog; + uchar temp_pix; + + while (tli->n) + { + fix dx = tli->right.x - tli->left.x; + if (dx <= 0) return TRUE; //might divide by zero below; punt this tmap + + uchar *p = tli->d + fix_cint(tli->left.x); + uchar *p_final = tli->d + fix_cint(tli->right.x); + + du = fix_div(du, dx); + dv = fix_div(dv, dx); + + switch (tli->bm.hlog) + { + case GRL_OPAQUE: + for (; p < p_final; p++) + { + int k = t_vtab[fix_fint(v)] + fix_fint(u); + k = t_bits[k]; + if (tluc8tab[k] != NULL) *p = tluc8tab[k][*p]; else *p = k; + u += du; + v += dv; + } + break; + + case GRL_TRANS: + for (; p < p_final; p++) + { + int k = t_vtab[fix_fint(v)] + fix_fint(u); + k = t_bits[k]; + if (k != 0) { + if (tluc8tab[k] != NULL) *p = tluc8tab[k][*p]; else *p = k; + } + u += du; + v += dv; + } + break; + + case GRL_OPAQUE | GRL_LOG2: + for (; p < p_final; p++) + { + int k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + k = t_bits[k]; + if (tluc8tab[k] != NULL) *p = tluc8tab[k][*p]; else *p = k; + u += du; + v += dv; + } + break; + + case GRL_TRANS | GRL_LOG2: + for (; p < p_final; p++) + { + int k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + k = t_bits[k]; + if (k != 0) { + if (tluc8tab[k] != NULL) *p = tluc8tab[k][*p]; else *p = k; + } + u += du; + v += dv; + } + break; + + case GRL_OPAQUE | GRL_CLUT: + for (; p < p_final; p++) + { + int k = t_vtab[fix_fint(v)] + fix_fint(u); + k = t_bits[k]; + if (tluc8tab[k] != NULL) *p = t_clut[tluc8tab[k][*p]]; else *p = t_clut[k]; + u += du; + v += dv; + } + break; + + case GRL_TRANS | GRL_CLUT: + for (; p < p_final; p++) + { + int k = t_vtab[fix_fint(v)] + fix_fint(u); + k = t_bits[k]; + if (k != 0) { + if (tluc8tab[k] != NULL) *p = t_clut[tluc8tab[k][*p]]; else *p = t_clut[k]; + } + u += du; + v += dv; + } + break; + + case GRL_OPAQUE | GRL_LOG2 | GRL_CLUT: + for (; p < p_final; p++) + { + int k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + k = t_bits[k]; + if (tluc8tab[k] != NULL) *p = t_clut[tluc8tab[k][*p]]; else *p = t_clut[k]; + u += du; + v += dv; + } + break; + + case GRL_TRANS | GRL_LOG2 | GRL_CLUT: + for (; p < p_final; p++) + { + int k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + k = t_bits[k]; + if (k != 0) { + if (tluc8tab[k] != NULL) *p = t_clut[tluc8tab[k][*p]]; else *p = t_clut[k]; + } + u += du; + v += dv; + } + break; + } + + u = (tli->left.u += tli->left.du); + tli->right.u += tli->right.du; + du = tli->right.u - u; + + v = (tli->left.v += tli->left.dv); + tli->right.v += tli->right.dv; + dv = tli->right.v - v; + + tli->left.x += tli->left.dx; + tli->right.x += tli->right.dx; + + tli->d += grd_bm.row; + tli->n --; + } + + return FALSE; //tmap OK +} + +void gri_tluc8_trans_lin_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_TRANS | GRL_LOG2; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_TRANS; + } + tli->d = grd_bm.bits + grd_bm.row * tli->y; + tli->loop_func = (void (*)())gri_tluc8_lin_umap_loop; + tli->right_edge_func = (void (*)())gri_uvx_edge; + tli->left_edge_func = (void (*)())gri_uvx_edge; +} + +void gri_tluc8_opaque_lin_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_OPAQUE | GRL_LOG2; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_OPAQUE; + } + tli->d = grd_bm.bits + grd_bm.row * tli->y; + tli->loop_func = (void (*)())gri_tluc8_lin_umap_loop; + tli->right_edge_func = (void (*)())gri_uvx_edge; + tli->left_edge_func = (void (*)())gri_uvx_edge; +} + +void gri_tluc8_trans_clut_lin_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_TRANS | GRL_LOG2 | GRL_CLUT; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_TRANS | GRL_CLUT; + } + tli->d = grd_bm.bits + grd_bm.row * tli->y; + tli->loop_func = (void (*)())gri_tluc8_lin_umap_loop; + tli->right_edge_func = (void (*)())gri_uvx_edge; + tli->left_edge_func = (void (*)())gri_uvx_edge; +} + +void gri_tluc8_opaque_clut_lin_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_OPAQUE | GRL_LOG2 | GRL_CLUT; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_OPAQUE | GRL_CLUT; + } + tli->d = grd_bm.bits + grd_bm.row * tli->y; + tli->loop_func = (void (*)())gri_tluc8_lin_umap_loop; + tli->right_edge_func = (void (*)())gri_uvx_edge; + tli->left_edge_func = (void (*)())gri_uvx_edge; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8ns.c b/engine/src/Libraries/2D/Source/Flat8/fl8ns.c new file mode 100644 index 0000000..29c240d --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8ns.c @@ -0,0 +1,139 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8ns.c $ + * $Revision: 1.1 $ + * $Author: kevin $ + * $Date: 1994/08/16 13:13:12 $ + * + * Routines to scale a flat8 bitmap to a generic canvas. + * + * This file is part of the 2d library. + * + */ + +#include "cnvdat.h" +#include "fl8tf.h" +#include "gente.h" +#include "grnull.h" +#include "poly.h" +#include "tlucdat.h" +#include "tmapint.h" + +// prototypes +int gri_tluc8_scale_umap_loop(grs_tmap_loop_info *tli); + +int gri_tluc8_scale_umap_loop(grs_tmap_loop_info *tli) { + fix u, ul, du; + uchar *pl, *pr; + + pl = tli->d + fix_cint(tli->left.x); + pr = tli->d + fix_cint(tli->right.x); + if (pr <= pl) + return TRUE; + ul = tli->left.u; + du = fix_div(tli->right.u - ul, tli->right.x - tli->left.x); + ul += fix_mul(du, fix_ceil(tli->left.x) - tli->left.x); + do { + uchar *p_dst, k; + uchar *p_src = tli->bm.bits + tli->bm.row * fix_int(tli->left.v); + switch (tli->bm.hlog) { + case GRL_OPAQUE: + for (p_dst = pl, u = ul; p_dst < pr; p_dst++) { + k = p_src[fix_fint(u)]; + if (tluc8tab[k] != NULL) + *p_dst = tluc8tab[k][*p_dst]; + else + *p_dst = k; + u += du; + } + break; + case GRL_TRANS: + for (p_dst = pl, u = ul; p_dst < pr; p_dst++) { + k = p_src[fix_fint(u)]; + if (k != 0) { + if (tluc8tab[k] != NULL) + *p_dst = tluc8tab[k][*p_dst]; + else + *p_dst = k; + } + u += du; + } + break; + case GRL_OPAQUE | GRL_CLUT: + for (p_dst = pl, u = ul; p_dst < pr; p_dst++) { + k = p_src[fix_fint(u)]; + if (tluc8tab[k] != NULL) + *p_dst = tli->clut[tluc8tab[k][*p_dst]]; + else + *p_dst = tli->clut[k]; + u += du; + } + break; + case GRL_TRANS | GRL_CLUT: + for (p_dst = pl, u = ul; p_dst < pr; p_dst++) { + k = p_src[fix_fint(u)]; + if (k != 0) { + if (tluc8tab[k] != NULL) + *p_dst = tli->clut[tluc8tab[k][*p_dst]]; + else + *p_dst = tli->clut[k]; + } + u += du; + } + break; + } + tli->left.v += tli->left.dv; + pl += grd_bm.row; + pr += grd_bm.row; + } while (--(tli->n) > 0); + return FALSE; /* tmap OK */ +} + +void gri_tluc8_trans_scale_umap_init(grs_tmap_loop_info *tli) { + tli->bm.hlog = GRL_TRANS; + tli->d = grd_bm.bits + grd_bm.row * tli->y; + tli->loop_func = (void (*)())gri_tluc8_scale_umap_loop; + tli->right_edge_func = gr_null; + tli->left_edge_func = (void (*)())gri_scale_edge; +} + +void gri_tluc8_opaque_scale_umap_init(grs_tmap_loop_info *tli) { + tli->bm.hlog = GRL_OPAQUE; + tli->d = grd_bm.bits + grd_bm.row * tli->y; + tli->loop_func = (void (*)())gri_tluc8_scale_umap_loop; + tli->right_edge_func = gr_null; + tli->left_edge_func = (void (*)())gri_scale_edge; +} + +void gri_tluc8_trans_clut_scale_umap_init(grs_tmap_loop_info *tli) { + tli->bm.hlog = GRL_TRANS | GRL_CLUT; + tli->d = grd_bm.bits + grd_bm.row * tli->y; + tli->loop_func = (void (*)())gri_tluc8_scale_umap_loop; + tli->right_edge_func = gr_null; + tli->left_edge_func = (void (*)())gri_scale_edge; +} + +void gri_tluc8_opaque_clut_scale_umap_init(grs_tmap_loop_info *tli) { + tli->bm.hlog = GRL_OPAQUE | GRL_CLUT; + tli->d = grd_bm.bits + grd_bm.row * tli->y; + tli->loop_func = (void (*)())gri_tluc8_scale_umap_loop; + tli->right_edge_func = gr_null; + tli->left_edge_func = (void (*)())gri_scale_edge; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8ntrp2.c b/engine/src/Libraries/2D/Source/Flat8/fl8ntrp2.c new file mode 100644 index 0000000..8821ea1 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8ntrp2.c @@ -0,0 +1,118 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/fl8ntrp2.c $ + * $Revision: 1.4 $ + * $Author: kaboom $ + * $Date: 1993/10/19 09:54:21 $ + * + * Routine for scaling up by 2 with interpolation onto a gen canvas + * + * This file is part of the 2d library. + * + * $Log: fl8ntrp2.c $ + * Revision 1.4 1993/10/19 09:54:21 kaboom + * Replaced #include row - bm->w; + + dst = grd_bm.bits; + src = bm->bits; + + for (j = bm->h; j > 0; j--) { + for (i = bm->w; i > 0; i--) { + + a = *src; + src++; + b = *src; + + *dst = a; + dst++; + + /* load them with the rgb values */ + a = grd_bpal[a]; + b = grd_bpal[b]; + a = (a >> 1) + (b >> 1); + c = grd_ipal; + a = a >> 5; + c += a & 0x1f; + a = a >> 6; + c += a & 0x3e0; + a = a >> 6; + c += a & 0x7c00; + + *dst = *c; + dst++; + } + dst += dd; + src += ds; + } + + /* Cycle through rows and fill in missing strips */ + src = grd_bm.bits; + for (i = (bm->w) * 2 - 1; i >= 0; i--) { + src = grd_bm.bits + i; + for (j = bm->h; j > 1; j--) { + a = *src; + src += 2 * grd_bm.row; + b = *src; + src -= grd_bm.row; + + a = grd_bpal[a]; + b = grd_bpal[b]; + a = (a >> 1) + (b >> 1); + c = grd_ipal; + a = a >> 5; + c += a & 0x1f; + a = a >> 6; + c += a & 0x3e0; + a = a >> 6; + c += a & 0x7c00; + *src = *c; + src += grd_bm.row; + } + src += grd_bm.row; + *src = 0; + } +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8p.c b/engine/src/Libraries/2D/Source/Flat8/fl8p.c new file mode 100644 index 0000000..952a5ae --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8p.c @@ -0,0 +1,571 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8p.c $ + * $Revision: 1.15 $ + * $Author: kevin $ + * $Date: 1994/11/02 19:38:32 $ + * + * full perspective texture mapper. + * + */ + +// ************************************************************************************ +// ************************************************************************************ +// +// MLA - don't think we need to optimize this, its in C on the PC too +// +// ************************************************************************************ +// ************************************************************************************ + +#include "cnvdat.h" +#include "pertyp.h" +#include "plytyp.h" +#define safe_fix_cint(x) ((fix_frac(x) == 0) ? (fix_int(x)) : (fix_int(x) + 1)) +#define fix_16_20(a) ((a) >> 4) + +// prototypes +void gri_per_umap_hscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps); +void gri_per_umap_vscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps); + +/************************************************************** +Routines to scan polygon. hscan=standard horizontal scanlines. +vscan=vertical scanlines. +**************************************************************/ + +void gri_per_umap_hscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps) { + grs_per_info pi; + fix y_prime[10]; + fix yp_left, yp_right; + fix x_left, x_right; + fix y_left, y_right; + fix dx_left, dx_right; + int yp_min, yp_max, yp_next; + int x_min, x_max, xr_min, xr_max, xl_min, xl_max; + int n_min, n_left, n_right; + int j; + + pi.scale = grd_bm.w; + pi.scan_slope = ps->scan_slope; + pi.dp = ps->dp; + pi.clut = ps->clut; + if (fix_abs(pi.scan_slope) > FIX_UNIT) + return; + + if (bm->row != 1 << (bm->wlog)) + return; + if (bm->h != 1 << (bm->hlog)) + return; + pi.u_mask = bm->row - 1; + pi.v_mask = (bm->h - 1) << bm->wlog; + pi.v_shift = 16 - bm->wlog; + + n_min = 0; + y_prime[n_min] = vpl[n_min]->y - fix_mul(vpl[n_min]->x, ps->scan_slope); + yp_min = yp_max = fix_cint(y_prime[n_min]); + + for (j = 1; j < n; j++) { + y_prime[j] = vpl[j]->y - fix_mul(vpl[j]->x, ps->scan_slope); + pi.yp = fix_cint(y_prime[j]); + if (pi.yp < yp_min) { + yp_min = pi.yp; + n_min = j; + } + if (pi.yp > yp_max) + yp_max = pi.yp; + } + + if (yp_max == yp_min) + return; + + pi.denom = fix_16_20(ps->c + fix_mul(ps->b, y_prime[0])); + pi.u0 = + vpl[0]->u - fix_div(fix_mul(vpl[0]->x, ps->alpha_u) + fix_mul(vpl[0]->y, ps->beta_u) + ps->gamma_u, pi.denom); + pi.v0 = + vpl[0]->v - fix_div(fix_mul(vpl[0]->x, ps->alpha_v) + fix_mul(vpl[0]->y, ps->beta_v) + ps->gamma_v, pi.denom); + + n_left = n_right = n_min; + pi.yp = yp_min; + while (fix_cint(y_prime[(n_left + n - 1) % n]) == pi.yp) + n_left = (n_left + n - 1) % n; + while (fix_cint(y_prime[(n_right + 1) % n]) == pi.yp) + n_right = (n_right + 1) % n; + + pi.yp--; + pi.denom = fix_16_20(ps->c + pi.yp * ps->b); + pi.unum = ps->gamma_u + ps->beta_u * pi.yp; + pi.dunum = ps->alpha_u + fix_mul(ps->scan_slope, ps->beta_u); + pi.vnum = ps->gamma_v + ps->beta_v * pi.yp; + pi.dvnum = ps->alpha_v + fix_mul(ps->scan_slope, ps->beta_v); + + if (n_right != n_left) { + + pi.dxl = (vpl[n_right]->x - vpl[n_left]->x) / pi.scale; + pi.dyl = (vpl[n_right]->y - vpl[n_left]->y) / pi.scale; + pi.x = fix_cint(vpl[n_left]->x); + pi.xl = fix_cint(vpl[n_right]->x); + if (pi.scan_slope > 0) { + x_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.bot - 1) - pi.yp, 1), pi.scan_slope)); + x_min = fix_int(fix_div(fix_make((grd_int_clip.top - 1) - pi.yp, 1), pi.scan_slope)) + 1; + } else { + x_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.top - 1) - pi.yp, 1), pi.scan_slope)); + x_min = fix_int(fix_div(fix_make((grd_int_clip.bot - 1) - pi.yp, 1), pi.scan_slope)) + 1; + } + if (pi.x < x_min) + pi.x = x_min; + if (pi.xl > x_max) + pi.xl = x_max; + pi.xr0 = pi.xr = pi.xl; + pi.cl = fix_mul(pi.dxl, vpl[n_left]->y) - fix_mul(pi.dyl, vpl[n_left]->x); + if (pi.x * pi.dyl - fix_mul(fix_make(pi.yp - 1, 0) + pi.x * pi.scan_slope, pi.dxl) + pi.cl < 0) { + pi.dyl = -pi.dyl; + pi.dxl = -pi.dxl; + pi.cl = -pi.cl; + } + if (pi.scan_slope > 0) + pi.dtl = pi.dyl - pi.dxl; + else + pi.dtl = pi.dyl + pi.dxl; + + ((void (*)(grs_per_info *, grs_bitmap *))(ps->scanline_func))(&pi, bm); + + pi.denom += fix_16_20(ps->b), pi.unum += ps->beta_u, pi.vnum += ps->beta_v; + yp_min--; /* first line already done */ + } + pi.yp++; + while (pi.yp < yp_max) { + /* check left edge */ + if (fix_cint(y_prime[n_left]) <= pi.yp) { + int n_prev; + fix d; + do { + if (fix_cint(y_prime[n_left]) == pi.yp) + n_prev = n_left; + if (--n_left < 0) + n_left = n - 1; + } while (fix_cint(y_prime[n_left]) <= pi.yp); + yp_left = y_prime[n_prev]; + x_left = vpl[n_prev]->x; + y_left = vpl[n_prev]->y; + xl_min = fix_cint(x_left); + xl_max = fix_cint(vpl[n_left]->x); + pi.dxl = (vpl[n_left]->x - x_left) / pi.scale; + pi.dyl = (vpl[n_left]->y - y_left) / pi.scale; + pi.cl = fix_mul(pi.dxl, y_left) - fix_mul(pi.dyl, x_left); + d = pi.dyl - fix_mul(pi.dxl, pi.scan_slope); + dx_left = fix_div(pi.dxl, d); + x_left = fix_div(fix_mul(pi.dyl, x_left) + fix_mul(pi.dxl, fix_ceil(yp_left) - y_left), d); + if (xl_max < xl_min) { + fix foo = xl_min; + xl_min = xl_max; + xl_max = foo; + } + if (fix_mul(vpl[n_left]->x - FIX_UNIT, pi.dyl) - fix_mul(vpl[n_left]->y - pi.scan_slope, pi.dxl) + pi.cl < + 0) { + pi.dyl = -pi.dyl; + pi.dxl = -pi.dxl; + pi.cl = -pi.cl; + } + if (pi.scan_slope > 0) + pi.dtl = pi.dyl - pi.dxl; + else + pi.dtl = pi.dyl + pi.dxl; + yp_left = y_prime[n_left]; + } + + /* check right edge */ + if (fix_cint(y_prime[n_right]) <= pi.yp) { + int n_prev; + fix d; + do { + if (fix_cint(y_prime[n_right]) == pi.yp) + n_prev = n_right; + if (++n_right == n) + n_right = 0; + } while (fix_cint(y_prime[n_right]) <= pi.yp); + yp_right = y_prime[n_prev]; + x_right = vpl[n_prev]->x; + y_right = vpl[n_prev]->y; + xr_min = fix_cint(x_right); + xr_max = fix_cint(vpl[n_right]->x); + pi.dxr = (vpl[n_right]->x - x_right) / pi.scale; + pi.dyr = (vpl[n_right]->y - y_right) / pi.scale; + pi.cr = fix_mul(pi.dxr, y_right) - fix_mul(pi.dyr, x_right); + d = pi.dyr - fix_mul(pi.dxr, pi.scan_slope); + dx_right = fix_div(pi.dxr, d); + x_right = fix_div(fix_mul(pi.dyr, x_right) + fix_mul(pi.dxr, fix_ceil(yp_right) - y_right), d); + if (xr_max < xr_min) { + fix foo = xr_min; + xr_min = xr_max; + xr_max = foo; + } + if (fix_mul(vpl[n_right]->x - FIX_UNIT, pi.dyr) - fix_mul(vpl[n_right]->y - pi.scan_slope, pi.dxr) + pi.cr < + 0) { + pi.dyr = -pi.dyr; + pi.dxr = -pi.dxr; + pi.cr = -pi.cr; + } + if (pi.scan_slope > 0) + pi.dtr = pi.dyr - pi.dxr; + else + pi.dtr = pi.dyr + pi.dxr; + yp_right = y_prime[n_right]; + } + yp_next = (yp_right < yp_left) ? fix_cint(yp_right) : fix_cint(yp_left); + + /* do 0th scanline if at yp_min */ + if (pi.yp == yp_min) { + x_left -= dx_left; + x_right -= dx_right; + pi.yp--; + } + for (; pi.yp < yp_next; pi.yp++) { + if ((pi.yp + 1 == yp_max) && (n_left != n_right)) { + pi.dxl = (vpl[n_right]->x - vpl[n_left]->x) / pi.scale; + pi.dyl = (vpl[n_right]->y - vpl[n_left]->y) / pi.scale; + pi.x = fix_cint(vpl[n_left]->x); + pi.xl = fix_cint(vpl[n_right]->x); + if (pi.scan_slope > 0) { + x_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.bot - 1) - pi.yp, 1), pi.scan_slope)); + x_min = fix_int(fix_div(fix_make((grd_int_clip.top - 1) - pi.yp, 1), pi.scan_slope)) + 1; + } else { + x_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.top - 1) - pi.yp, 1), pi.scan_slope)); + x_min = fix_int(fix_div(fix_make((grd_int_clip.bot - 1) - pi.yp, 1), pi.scan_slope)) + 1; + } + if (pi.x < x_min) + pi.x = x_min; + if (pi.xl > x_max) + pi.xl = x_max; + pi.xr0 = pi.xr = pi.xl; + pi.cl = fix_mul(pi.dxl, vpl[n_left]->y) - fix_mul(pi.dyl, vpl[n_left]->x); + if (pi.x * pi.dyl - fix_mul(fix_make(pi.yp + 1, 0) + pi.x * pi.scan_slope, pi.dxl) + pi.cl < 0) { + pi.dyl = -pi.dyl; + pi.dxl = -pi.dxl; + pi.cl = -pi.cl; + } + if (pi.scan_slope > 0) + pi.dtl = pi.dyl - pi.dxl; + else + pi.dtl = pi.dyl + pi.dxl; + + ((void (*)(grs_per_info *, grs_bitmap *))(ps->scanline_func))(&pi, bm); + + pi.yp = yp_max; + break; + } + + if (dx_left > 0) { + pi.x = fix_fint(x_left); + pi.xl = fix_cint(x_left + dx_left); + } else { + pi.x = fix_fint(x_left + dx_left); + pi.xl = fix_cint(x_left); + } + if (dx_right > 0) { + pi.xr0 = fix_fint(x_right); + pi.xr = fix_cint(x_right + dx_right); + } else { + pi.xr0 = fix_fint(x_right + dx_right); + pi.xr = fix_cint(x_right); + } + if (pi.scan_slope > 0) { + x_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.bot - 1) - pi.yp, 1), pi.scan_slope)); + x_min = fix_int(fix_div(fix_make((grd_int_clip.top - 1) - pi.yp, 1), pi.scan_slope)) + 1; + } else { + x_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.top - 1) - pi.yp, 1), pi.scan_slope)); + x_min = fix_int(fix_div(fix_make((grd_int_clip.bot - 1) - pi.yp, 1), pi.scan_slope)) + 1; + } + if (xl_min > x_min) + x_min = xl_min; + if (xr_max < x_max) + x_max = xr_max; + if (pi.xr > x_max) + pi.xr = x_max; + if (pi.xr0 > x_max) + pi.xr0 = x_max; + if (pi.xl > pi.xr0) + pi.xl = pi.xr0; + if (pi.x < x_min) + pi.x = x_min; + + ((void (*)(grs_per_info *, grs_bitmap *))(ps->scanline_func))(&pi, bm); + + x_left += dx_left, x_right += dx_right; + pi.denom += fix_16_20(ps->b), pi.unum += ps->beta_u, pi.vnum += ps->beta_v; + } + } +} + +void gri_per_umap_vscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps) { + grs_per_info pi; + fix x_prime[10]; + fix xp_top, xp_bot; + fix x_top, x_bot; + fix y_top, y_bot; + fix dy_top, dy_bot; + int xp_min, xp_max, xp_next; + int y_min, y_max, yr_min, yr_max, yl_min, yl_max; + int n_min, n_top, n_bot; + int j; + + pi.scale = grd_bm.w; + pi.scan_slope = ps->scan_slope; + pi.dp = ps->dp; + pi.clut = ps->clut; + if (fix_abs(pi.scan_slope) >= FIX_UNIT) + return; + + if (bm->row != 1 << (bm->wlog)) + return; + if (bm->h != 1 << (bm->hlog)) + return; + pi.u_mask = bm->row - 1; + pi.v_mask = (bm->h - 1) << bm->wlog; + pi.v_shift = 16 - bm->wlog; + + xp_min = xp_max = fix_cint(x_prime[n_min = 0] = vpl[0]->x - fix_mul(vpl[0]->y, ps->scan_slope)); + for (j = 1; j < n; j++) { + pi.xp = fix_cint(x_prime[j] = vpl[j]->x - fix_mul(vpl[j]->y, ps->scan_slope)); + if (pi.xp < xp_min) { + xp_min = pi.xp; + n_min = j; + } + if (pi.xp > xp_max) + xp_max = pi.xp; + } + if (xp_max == xp_min) + return; + pi.denom = fix_16_20(ps->c + fix_mul(ps->a, x_prime[0])); + pi.u0 = + vpl[0]->u - fix_div(fix_mul(vpl[0]->x, ps->alpha_u) + fix_mul(vpl[0]->y, ps->beta_u) + ps->gamma_u, pi.denom); + pi.v0 = + vpl[0]->v - fix_div(fix_mul(vpl[0]->x, ps->alpha_v) + fix_mul(vpl[0]->y, ps->beta_v) + ps->gamma_v, pi.denom); + + n_top = n_bot = n_min; + pi.xp = xp_min; + while (fix_cint(x_prime[(n_top + 1) % n]) == pi.xp) + n_top = (n_top + 1) % n; + while (fix_cint(x_prime[(n_bot + n - 1) % n]) == pi.xp) + n_bot = (n_bot + n - 1) % n; + + pi.xp--; + pi.denom = fix_16_20(ps->c + pi.xp * ps->a); + pi.unum = ps->gamma_u + ps->alpha_u * pi.xp; + pi.dunum = ps->beta_u + fix_mul(ps->scan_slope, ps->alpha_u); + pi.vnum = ps->gamma_v + ps->alpha_v * pi.xp; + pi.dvnum = ps->beta_v + fix_mul(ps->scan_slope, ps->alpha_v); + + if (n_bot != n_top) { + + pi.dxl = (vpl[n_bot]->x - vpl[n_top]->x) / pi.scale; + pi.dyl = (vpl[n_bot]->y - vpl[n_top]->y) / pi.scale; + pi.y = fix_cint(vpl[n_top]->y); + pi.yl = fix_cint(vpl[n_bot]->y); + if (pi.scan_slope > 0) { + y_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.right - 1) - pi.xp, 1), pi.scan_slope)); + y_min = fix_int(fix_div(fix_make((grd_int_clip.left - 1) - pi.xp, 1), pi.scan_slope)) + 1; + } else { + y_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.left - 1) - pi.xp, 1), pi.scan_slope)); + y_min = fix_int(fix_div(fix_make((grd_int_clip.right - 1) - pi.xp, 1), pi.scan_slope)) + 1; + } + if (pi.y < y_min) + pi.y = y_min; + if (pi.yl > y_max) + pi.yl = y_max; + pi.yr0 = pi.yr = pi.yl; + + pi.cl = -fix_mul(pi.dxl, vpl[n_top]->y) + fix_mul(pi.dyl, vpl[n_top]->x); + if (pi.y * pi.dxl - fix_mul(fix_make(pi.xp - 1, 0) + pi.y * pi.scan_slope, pi.dyl) + pi.cl < 0) { + pi.dyl = -pi.dyl; + pi.dxl = -pi.dxl; + pi.cl = -pi.cl; + } + if (pi.scan_slope > 0) + pi.dtl = pi.dxl - pi.dyl; + else + pi.dtl = pi.dyl + pi.dxl; + + ((void (*)(grs_per_info *, grs_bitmap *))(ps->scanline_func))(&pi, bm); + + pi.denom += fix_16_20(ps->a), pi.unum += ps->alpha_u, pi.vnum += ps->alpha_v; + xp_min--; /* first line already done */ + } + pi.xp++; + while (pi.xp < xp_max) { + /* check top edge */ + if (fix_cint(x_prime[n_top]) <= pi.xp) { + int n_prev; + fix d; + do { + if (fix_cint(x_prime[n_top]) == pi.xp) + n_prev = n_top; + if (++n_top == n) + n_top = 0; + } while (fix_cint(x_prime[n_top]) <= pi.xp); + xp_top = x_prime[n_prev]; + x_top = vpl[n_prev]->x; + y_top = vpl[n_prev]->y; + yl_min = fix_cint(y_top); + yl_max = fix_cint(vpl[n_top]->y); + pi.dxl = (vpl[n_top]->x - x_top) / pi.scale; + pi.dyl = (vpl[n_top]->y - y_top) / pi.scale; + pi.cl = -fix_mul(pi.dxl, y_top) + fix_mul(pi.dyl, x_top); + d = pi.dxl - fix_mul(pi.dyl, pi.scan_slope); + dy_top = fix_div(pi.dyl, d); + y_top = fix_div(fix_mul(pi.dxl, y_top) + fix_mul(pi.dyl, fix_ceil(xp_top) - x_top), d); + if (yl_max < yl_min) { + fix foo = yl_min; + yl_min = yl_max; + yl_max = foo; + } + if (fix_mul(vpl[n_top]->y - FIX_UNIT, pi.dxl) - fix_mul(vpl[n_top]->x - pi.scan_slope, pi.dyl) + pi.cl < + 0) { + pi.dyl = -pi.dyl; + pi.dxl = -pi.dxl; + pi.cl = -pi.cl; + } + if (pi.scan_slope > 0) + pi.dtl = pi.dxl - pi.dyl; + else + pi.dtl = pi.dyl + pi.dxl; + xp_top = x_prime[n_top]; + } + + /* check bot edge */ + if (fix_cint(x_prime[n_bot]) <= pi.xp) { + int n_prev; + fix d; + do { + if (fix_cint(x_prime[n_bot]) == pi.xp) + n_prev = n_bot; + if (--n_bot < 0) + n_bot = n - 1; + } while (fix_cint(x_prime[n_bot]) <= pi.xp); + xp_bot = x_prime[n_prev]; + x_bot = vpl[n_prev]->x; + y_bot = vpl[n_prev]->y; + yr_min = fix_cint(y_bot); + yr_max = fix_cint(vpl[n_bot]->y); + pi.dxr = (vpl[n_bot]->x - x_bot) / pi.scale; + pi.dyr = (vpl[n_bot]->y - y_bot) / pi.scale; + pi.cr = -fix_mul(pi.dxr, y_bot) + fix_mul(pi.dyr, x_bot); + d = pi.dxr - fix_mul(pi.dyr, pi.scan_slope); + dy_bot = fix_div(pi.dyr, d); + y_bot = fix_div(fix_mul(pi.dxr, y_bot) + fix_mul(pi.dyr, fix_ceil(xp_bot) - x_bot), d); + if (yr_max < yr_min) { + fix foo = yr_min; + yr_min = yr_max; + yr_max = foo; + } + if (fix_mul(vpl[n_bot]->y - FIX_UNIT, pi.dxr) - fix_mul(vpl[n_bot]->x - pi.scan_slope, pi.dyr) + pi.cr < + 0) { + pi.dyr = -pi.dyr; + pi.dxr = -pi.dxr; + pi.cr = -pi.cr; + } + if (pi.scan_slope > 0) + pi.dtr = pi.dxr - pi.dyr; + else + pi.dtr = pi.dyr + pi.dxr; + xp_bot = x_prime[n_bot]; + } + xp_next = (xp_bot < xp_top) ? fix_cint(xp_bot) : fix_cint(xp_top); + + /* do 0th scanline if at xp_min */ + if (pi.xp == xp_min) { + y_top -= dy_top; + y_bot -= dy_bot; + pi.xp--; + } + for (; pi.xp < xp_next; pi.xp++) { + if ((pi.xp + 1 == xp_max) && (n_top != n_bot)) { + pi.dxl = (vpl[n_bot]->x - vpl[n_top]->x) / pi.scale; + pi.dyl = (vpl[n_bot]->y - vpl[n_top]->y) / pi.scale; + pi.y = fix_cint(vpl[n_top]->y); + pi.yl = fix_cint(vpl[n_bot]->y); + if (pi.scan_slope > 0) { + y_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.right - 1) - pi.xp, 1), pi.scan_slope)); + y_min = fix_int(fix_div(fix_make((grd_int_clip.left - 1) - pi.xp, 1), pi.scan_slope)) + 1; + } else { + y_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.left - 1) - pi.xp, 1), pi.scan_slope)); + y_min = fix_int(fix_div(fix_make((grd_int_clip.right - 1) - pi.xp, 1), pi.scan_slope)) + 1; + } + if (pi.y < y_min) + pi.y = y_min; + if (pi.yl > y_max) + pi.yl = y_max; + pi.yr0 = pi.yr = pi.yl; + pi.cl = -fix_mul(pi.dxl, vpl[n_top]->y) + fix_mul(pi.dyl, vpl[n_top]->x); + if (pi.y * pi.dxl - fix_mul(fix_make(pi.xp + 1, 0) + pi.y * pi.scan_slope, pi.dyl) + pi.cl < 0) { + pi.dyl = -pi.dyl; + pi.dxl = -pi.dxl; + pi.cl = -pi.cl; + } + if (pi.scan_slope > 0) + pi.dtl = pi.dxl - pi.dyl; + else + pi.dtl = pi.dyl + pi.dxl; + + ((void (*)(grs_per_info *, grs_bitmap *))(ps->scanline_func))(&pi, bm); + + pi.xp = xp_max; + break; + } + + if (dy_top > 0) { + pi.y = fix_fint(y_top); + pi.yl = fix_cint(y_top + dy_top); + } else { + pi.y = fix_fint(y_top + dy_top); + pi.yl = fix_cint(y_top); + } + if (dy_bot > 0) { + pi.yr0 = fix_fint(y_bot); + pi.yr = fix_cint(y_bot + dy_bot); + } else { + pi.yr0 = fix_fint(y_bot + dy_bot); + pi.yr = fix_cint(y_bot); + } + if (pi.scan_slope > 0) { + y_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.right - 1) - pi.xp, 1), pi.scan_slope)); + y_min = fix_int(fix_div(fix_make((grd_int_clip.left - 1) - pi.xp, 1), pi.scan_slope)) + 1; + } else { + y_max = safe_fix_cint(fix_div(fix_make((grd_int_clip.left - 1) - pi.xp, 1), pi.scan_slope)); + y_min = fix_int(fix_div(fix_make((grd_int_clip.right - 1) - pi.xp, 1), pi.scan_slope)) + 1; + } + if (yl_min > y_min) + y_min = yl_min; + if (yr_max < y_max) + y_max = yr_max; + if (pi.yr > y_max) + pi.yr = y_max; + if (pi.yr0 > y_max) + pi.yr0 = y_max; + if (pi.yl > pi.yr0) + pi.yl = pi.yr0; + if (pi.y < y_min) + pi.y = y_min; + + ((void (*)(grs_per_info *, grs_bitmap *))(ps->scanline_func))(&pi, bm); + + y_top += dy_top, y_bot += dy_bot; + pi.denom += fix_16_20(ps->a), pi.unum += ps->alpha_u, pi.vnum += ps->alpha_v; + } + } +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8p.h b/engine/src/Libraries/2D/Source/Flat8/fl8p.h new file mode 100644 index 0000000..0967022 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8p.h @@ -0,0 +1,95 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8p.h $ + * $Revision: 1.11 $ + * $Author: kevin $ + * $Date: 1994/11/02 19:39:49 $ + * + * Miscellaneous stuff needed by the flat8 perspective mappers. + * Includes 3 and 12 bit integer fixed point macros. + * + * This file is part of the 2d library. + * + * $Log: fl8p.h $ + * Revision 1.11 1994/11/02 19:39:49 kevin + * Use 20 bit integer fixed point for bitmap coord intermediates to avoid + * overflows in svga. + * + * Revision 1.10 1994/07/18 17:04:27 kevin + * removed unnecessary includes. + * + * Revision 1.9 1994/01/16 12:04:44 kevin + * Added clut lighting tolerance global declaration. + * + * Revision 1.8 1994/01/03 22:26:11 kevin + * Added declaration for global grd_per_setup. + * + * Revision 1.7 1993/12/17 01:00:57 kevin + * Added egregious error constants. + * + * Revision 1.6 1993/12/15 02:49:06 kevin + * Added wlog and hlog to context. + * + * Revision 1.5 1993/12/14 22:31:14 kevin + * Moved declaration of grd_per_context to + * grpm.h so everyone can use it. + * + * Revision 1.4 1993/12/08 23:44:52 kevin + * Added vtab to per_setup structure for non/power/of/2 support. + * + * Revision 1.3 1993/12/04 17:28:28 kevin + * Added clut field to per_setup structure. + * + * Revision 1.2 1993/12/04 12:33:01 kevin + * Added new structures, new fixed point primitives. + * + * Revision 1.1 1993/11/18 23:42:36 kevin + * Initial revision + * + */ + +#ifndef __FL8P_H +#define __FL8P_H + +#define ACENT (grd_bm.w >> 1) +#define BCENT (grd_bm.h >> 1) +#define SCALE 0x200 + +#define GR_PER_CODE_OK 0 +#define GR_PER_CODE_MEMERR 1 +#define GR_PER_CODE_BADPLANE 2 +#define GR_PER_CODE_BADDENOM 3 +#define GR_PER_CODE_BADINDEX 4 +#define GR_PER_CODE_LIN 5 +#define GR_PER_CODE_WALL 6 +#define GR_PER_CODE_FLOOR 7 +#define GR_PER_CODE_BIGSLOPE 8 +#define GR_PER_CODE_SMALLSLOPE 9 + +#define HS_TS_ERR 10 +#define HS_TB_ERR 11 +#define VS_TS_ERR 12 +#define VS_TB_ERR 13 + +extern ubyte flat8_per_ltol; +extern ubyte flat8_per_wftol; +extern fix gr_clut_lit_tol; + +#endif diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8p24.c b/engine/src/Libraries/2D/Source/Flat8/fl8p24.c new file mode 100644 index 0000000..2d367a5 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8p24.c @@ -0,0 +1,59 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8p24.c $ + * $Revision: 1.5 $ + * $Author: kevin $ + * $Date: 1994/10/17 14:59:59 $ + * + * Routines for drawing 24-bit pixels onto a flat 8 canvas. + * + * This file is part of the 2d library. + */ + +#include "clpcon.h" +#include "cnvdat.h" +#include "rgb.h" +#include "scrdat.h" + +/* set an unclipped pixel in bank-switched memory. draws 8-8-8 long + rgb c at (x,y). */ +void flat8_set_upixel24(long c, short x, short y) { + uchar *p; + int i; + + i = gr_index_lrgb(c); + p = grd_bm.bits + grd_bm.row * y + x; + *p = grd_ipal[i]; +} + +/* set a clipped pixel in bank-switched memory. draws an 8-8-8 long + rgb c at (x,y). return the clip code. */ +int flat8_set_pixel24(long c, short x, short y) { + uchar *p; + int i; + + if (x < grd_clip.left || x > grd_clip.right || y < grd_clip.top || y > grd_clip.bot) + return CLIP_ALL; + i = gr_index_lrgb(c); + p = grd_canvas->bm.bits + grd_canvas->bm.row * y + x; + *p = grd_ipal[i]; + + return CLIP_NONE; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8pix.c b/engine/src/Libraries/2D/Source/Flat8/fl8pix.c new file mode 100644 index 0000000..ae93150 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8pix.c @@ -0,0 +1,86 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8pix.c $ + * $Revision: 1.5 $ + * $Author: kevin $ + * $Date: 1994/08/16 13:14:01 $ + * + * Routines for drawing pixels into a flat 8 canvas. + * + * This file is part of the 2d library. + * + * $Log: fl8pix.c $ + * Revision 1.5 1994/08/16 13:14:01 kevin + * Added pixel primitives for all fill modes. + * + * Revision 1.4 1993/10/19 09:50:49 kaboom + * Replaced #include "grd.h" with new headers split from grd.h. + * + * Revision 1.3 1993/10/01 16:00:45 kaboom + * Converted to include clpcon.h instead of clip.h + * + * Revision 1.2 1993/05/16 00:33:22 kaboom + * Fixed clipped case to handle new padded clipping rectangle. + * + * Revision 1.1 1993/02/16 14:16:16 kaboom + * Initial revision + */ + +#include "blend.h" +#include "cnvdat.h" +#include "fl8tf.h" + +/* draws an unclipped pixel of the given color at (x, y) on the canvas. */ +void flat8_set_upixel(long color, short x, short y) { + uchar *p; + + p = grd_bm.bits + grd_bm.row * y + x; + *p = color; +} + +void flat8_clut_set_upixel(long color, short x, short y) { + uchar *p; + + p = grd_bm.bits + grd_bm.row * y + x; + *p = ((uchar *)grd_gc.fill_parm)[color]; +} + +void flat8_xor_set_upixel(long color, short x, short y) { + uchar *p; + + p = grd_bm.bits + grd_bm.row * y + x; + *p = color ^ *p; +} + +void flat8_blend_set_upixel(long color, short x, short y) { + uchar *p; + + p = grd_bm.bits + grd_bm.row * y + x; + *p = gr_blend(color, *p, grd_gc.fill_parm); +} + +// MLA #pragma off (unreferenced) +void flat8_solid_set_upixel(long color, short x, short y) { + uchar *p; + + p = grd_bm.bits + grd_bm.row * y + x; + *p = (uchar)grd_gc.fill_parm; +} +// MLA #pragma on (unreferenced) diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8ply.c b/engine/src/Libraries/2D/Source/Flat8/fl8ply.c new file mode 100644 index 0000000..2b513b9 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8ply.c @@ -0,0 +1,144 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8ply.c $ + * $Revision: 1.1 $ + * $Author: kevin $ + * $Date: 1994/08/16 13:11:09 $ + * + * Routines for drawing flat shaded polygons onto a flat 8 canvas. + * + * This file is part of the 2d library. + */ + +#include "cnvdat.h" +#include "gente.h" +#include "lg.h" +#include "poly.h" +#include "tlucdat.h" +#include "tmapint.h" +#include +#include + +// prototypes +int gri_poly_loop(grs_tmap_loop_info *ti); +void gri_solid_poly_init(grs_tmap_loop_info *ti); +void gri_poly_init(grs_tmap_loop_info *ti); +void gri_clut_poly_init(grs_tmap_loop_info *ti); +void gri_tpoly_init(grs_tmap_loop_info *ti); +void gri_clut_tpoly_init(grs_tmap_loop_info *ti); + +int gri_poly_loop(grs_tmap_loop_info *ti) { + int d; + uchar c = (uchar)(intptr_t)(ti->bm.bits); /* actually, fill_parm */ + uchar *bm_bits = ti->bm.bits; + uchar *ti_d = ti->d; + fix ti_left_x = ti->left.x; + fix ti_right_x = ti->right.x; + uchar *ti_clut = ti->clut; + uchar ti_hlog = ti->bm.hlog; + ushort grow = grd_bm.row; + fix ti_left_dx = ti->left.dx; + fix ti_right_dx = ti->right.dx; + + do { + if ((d = fix_cint(ti_right_x) - fix_cint(ti_left_x)) > 0) { + int x; + + switch (ti_hlog) { + case GRL_OPAQUE: + LG_memset(ti_d + fix_cint(ti_left_x), c, d); + break; + case GRL_TLUC8: + for (x = fix_cint(ti_left_x); x < fix_cint(ti_right_x); x++) + ti_d[x] = bm_bits[ti_d[x]]; + break; + case GRL_CLUT | GRL_TLUC8: + for (x = fix_cint(ti_left_x); x < fix_cint(ti_right_x); x++) + ti_d[x] = ti_clut[bm_bits[ti_d[x]]]; + break; + } + } else if (d < 0) { + return TRUE; + } + /* update span extrema and destination. */ + ti_d += grow; + ti_left_x += ti_left_dx; + ti_right_x += ti_right_dx; + } while ((--(ti->n)) > 0); + + ti->d = ti_d; + ti->right.x = ti_right_x; + ti->left.x = ti_left_x; + return FALSE; +} + +void gri_solid_poly_init(grs_tmap_loop_info *ti) { + ti->bm.bits = ti->clut; /* set fill_parm */ + ti->bm.hlog = GRL_OPAQUE; + ti->d = ti->y * grd_bm.row + grd_bm.bits; + ti->loop_func = (void (*)())gri_poly_loop; + ti->top_edge_func = (void (*)())gri_x_edge; + ti->bot_edge_func = (void (*)())gri_x_edge; +} + +void gri_poly_init(grs_tmap_loop_info *ti) { + ti->bm.hlog = GRL_OPAQUE; + ti->d = ti->y * grd_bm.row + grd_bm.bits; + ti->loop_func = (void (*)())gri_poly_loop; + ti->top_edge_func = (void (*)())gri_x_edge; + ti->bot_edge_func = (void (*)())gri_x_edge; +} + +void gri_clut_poly_init(grs_tmap_loop_info *ti) { + ti->bm.bits = (uchar *)(intptr_t)ti->clut[(intptr_t)ti->bm.bits]; + ti->bm.hlog = GRL_OPAQUE; + ti->d = ti->y * grd_bm.row + grd_bm.bits; + ti->loop_func = (void (*)())gri_poly_loop; + ti->top_edge_func = (void (*)())gri_x_edge; + ti->bot_edge_func = (void (*)())gri_x_edge; +} + +void gri_tpoly_init(grs_tmap_loop_info *ti) { + if (tluc8tab[(intptr_t)(ti->bm.bits)] != NULL) { + ti->bm.bits = tluc8tab[(intptr_t)ti->bm.bits]; + ti->bm.hlog = GRL_TLUC8; + } else { + ti->bm.hlog = GRL_OPAQUE; + } + ti->d = ti->y * grd_bm.row + grd_bm.bits; + ti->loop_func = (void (*)())gri_poly_loop; + ti->top_edge_func = (void (*)())gri_x_edge; + ti->bot_edge_func = (void (*)())gri_x_edge; +} + +void gri_clut_tpoly_init(grs_tmap_loop_info *ti) { + // DebugString("gri_clut_tpoly_init"); + if (tluc8tab[(intptr_t)(ti->bm.bits)] != NULL) { + ti->bm.bits = tluc8tab[(intptr_t)ti->bm.bits]; + ti->bm.hlog = GRL_TLUC8 | GRL_CLUT; + } else { + ti->bm.bits = (uchar *)(intptr_t)ti->clut[(intptr_t)ti->bm.bits]; + ti->bm.hlog = GRL_OPAQUE; + } + ti->d = ti->y * grd_bm.row + grd_bm.bits; + ti->loop_func = (void (*)())gri_poly_loop; + ti->top_edge_func = (void (*)())gri_x_edge; + ti->bot_edge_func = (void (*)())gri_x_edge; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8pnt.c b/engine/src/Libraries/2D/Source/Flat8/fl8pnt.c new file mode 100644 index 0000000..1da5e45 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8pnt.c @@ -0,0 +1,62 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/fl8pnt.c $ + * $Revision: 1.4 $ + * $Author: kaboom $ + * $Date: 1993/10/19 09:50:51 $ + * + * Routines for drawing points into a flat 8 canvas. + * + * This file is part of the 2d library. + * + * $Log: fl8pnt.c $ + * Revision 1.4 1993/10/19 09:50:51 kaboom + * Replaced #include = grd_clip.right || y < grd_clip.top || y >= grd_clip.bot) + return CLIP_ALL; + + p = grd_bm.bits + grd_bm.row * y + x; + *p = grd_gc.fcolor; + return CLIP_NONE; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8rect.c b/engine/src/Libraries/2D/Source/Flat8/fl8rect.c new file mode 100644 index 0000000..d44d68a --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8rect.c @@ -0,0 +1,61 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/fl8rect.c $ + * $Revision: 1.4 $ + * $Author: kaboom $ + * $Date: 1993/10/19 09:50:53 $ + * + * Routines for drawing rectangles into a flat 8 canvas. + * + * This file is part of the 2d library. + * + * $Log: fl8rect.c $ + * Revision 1.4 1993/10/19 09:50:53 kaboom + * Replaced #include + +void flat8_urect(short left, short top, short right, short bot) { + uchar *p; + int w, h; + int grow = grd_bm.row; + long fcolor = grd_gc.fcolor; + + p = grd_bm.bits + top * grow + left; + w = right - left; + h = bot - top; + + while (h-- > 0) { + LG_memset(p, fcolor, w); + p += grow; + } +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8row.c b/engine/src/Libraries/2D/Source/Flat8/fl8row.c new file mode 100644 index 0000000..e6cc50d --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8row.c @@ -0,0 +1,35 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/fl8row.c $ + * $Revision: 1.1 $ + * $Author: kaboom $ + * $Date: 1993/04/29 18:46:46 $ + * + * Row bytes calculator for flat 8 bitmaps. + * + * This file is part of the 2d library. + * + * $Log: fl8row.c $ + * Revision 1.1 1993/04/29 18:46:46 kaboom + * Initial revision + * + */ + +short flat8_calc_row(short w) { return w; } diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8rsd8.c b/engine/src/Libraries/2D/Source/Flat8/fl8rsd8.c new file mode 100644 index 0000000..625741e --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8rsd8.c @@ -0,0 +1,264 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8rsd8.c $ + * $Revision: 1.6 $ + * $Author: kevin $ + * $Date: 1994/10/27 18:26:56 $ + * + * Routines for drawing rsd bitmaps into a flat 8 canvas. + * + * This file is part of the 2d library. + * + * $Log: fl8rsd8.c $ + * Revision 1.6 1994/10/27 18:26:56 kevin + * fixed gr_rsd8_blit prototype. + * + * Revision 1.5 1994/10/25 15:13:14 kevin + * Renamed funcs, use fast rsd blitter when possible. + * + * Revision 1.4 1993/10/19 09:50:54 kaboom + * Replaced #include + +// prototypes +void gr_rsd8_blit(uchar *rsd_src, uchar *dst, int grd_bm_row, int bm_w); + +//### MLA- not supposed to be used (PC code is in RSDBLT.ASM) +void gr_rsd8_blit(uchar *rsd_src, uchar *dst, int grd_bm_row, int bm_w) { + DEBUG("%s: ask mark", __FUNCTION__); +} + +void gri_flat8_rsd8_ubitmap(grs_bitmap *bm, short x, short y) { + /* uchar *p_dst; + uchar *rsd_src; // rsd source buffer + + rsd_src = bm->bits; + p_dst = grd_bm.bits + grd_bm.row*y + x; + gr_rsd8_blit(rsd_src,p_dst,grd_bm.row,bm->w);*/ + unpack_rsd8_ubitmap(bm, x, y); +} + +int gri_flat8_rsd8_bitmap(grs_bitmap *bm, short x_left, short y_top) { + short x, y; /* current destination position */ + short x_right, y_bot; /* opposite edges of bitmap */ + short x_off, y_off; /* x,y offset for clip */ + ulong start_byte; /* byte to start drawing */ + ulong cur_byte; /* current position within rsd */ + uchar *p_dst; + uchar *rsd_src; /* rsd source buffer */ + short rsd_code; /* last rsd opcode */ + short rsd_count; /* count for last opcode */ + short op_count; /* operational count */ + int code; /* clip code to return */ + + rsd_src = bm->bits; + x = x_left; + y = y_top; + x_off = y_off = cur_byte = rsd_count = 0; + x_right = x_left + bm->w; + y_bot = y_top + bm->h; + + /* clip bitmap to rectangular clipping window. */ + if (x_left > grd_clip.right || x_right <= grd_clip.left || y_top > grd_clip.bot || y_bot <= grd_clip.top) + /* completely clipped, forget it. */ + return CLIP_ALL; + + code = CLIP_NONE; + if (x_left < grd_clip.left) { + /* clipped off left edge. */ + x_off = grd_clip.left - x_left; + x = grd_clip.left; + code |= CLIP_LEFT; + } + if (y < grd_clip.top) { + /* clipped off top edge. */ + y_off = grd_clip.top - y_top; + y = grd_clip.top; + code |= CLIP_TOP; + } + if (x_right >= grd_clip.right) { + /* clipped off right edge. */ + x_right = grd_clip.right; + code |= CLIP_RIGHT; + } + if (y_bot > grd_clip.bot) { + /* clipped off bottom edge. */ + y_bot = grd_clip.bot; + code |= CLIP_BOT; + } + + if (code == CLIP_NONE) { + gr_ubitmap(bm, x_left, y_top); + return CLIP_NONE; + } + + if (y_off > 0 || x_off > 0) { + /* been clipped of left and/or top, so we need to skip from beginning + of rsd buffer to be at x_off,y_off within rsd bitmap. */ + start_byte = y_off * bm->row + x_off; + while (cur_byte < start_byte) { + if (rsd_count == 0) + /* no pending opcodes, get a new one. */ + RSD_GET_TOKEN(); + if (cur_byte + rsd_count <= start_byte) { + /* current code doesn't hit start_byte yet, so skip all of it. */ + switch (rsd_code) { + case RSD_RUN: + /* advance past 1 byte of run color. */ + rsd_src++; + break; + case RSD_SKIP: + break; + default: /* RSD_DUMP */ + /* advance past rsd_count bytes of dump pixel data. */ + rsd_src += rsd_count; + break; + } + cur_byte += rsd_count; + rsd_count = 0; + } else { + /* current code goes past start_byte, so skip only enough to get + to start_byte. */ + op_count = start_byte - cur_byte; + switch (rsd_code) { + case RSD_RUN: + break; + case RSD_SKIP: + break; + default: /* RSD_DUMP */ + rsd_src += op_count; + break; + } + cur_byte += op_count; + rsd_count -= op_count; + } + } + } + + p_dst = grd_bm.bits + y * grd_bm.row + x; + + /* process each scanline in two chunks. the first is the clipped section + from the right edge, wrapping around to to the left. the second is the + unclipped area in the middle. */ + while (y < y_bot) { + /* clipped section. */ + while (x < x_left + x_off) { + if (rsd_count == 0) + RSD_GET_TOKEN(); + if (x + rsd_count <= x_left + x_off) { + switch (rsd_code) { + case RSD_RUN: + rsd_src++; + break; + case RSD_SKIP: + break; + default: /* RSD_DUMP */ + rsd_src += rsd_count; + break; + } + x += rsd_count; + rsd_count = 0; + } else { + op_count = x_left + x_off - x; + switch (rsd_code) { + case RSD_RUN: + break; + case RSD_SKIP: + break; + default: /* RSD_DUMP */ + rsd_src += op_count; + break; + } + rsd_count -= op_count; + x += op_count; + } + } + + /* section to draw. */ + while (x < x_right) { + if (rsd_count == 0) + RSD_GET_TOKEN(); + if (x + rsd_count <= x_right) { + switch (rsd_code) { + case RSD_RUN: + LG_memset(p_dst, *rsd_src, rsd_count); + rsd_src++; + break; + case RSD_SKIP: + break; + default: /* RSD_DUMP */ + LG_memcpy(p_dst, rsd_src, rsd_count); + rsd_src += rsd_count; + break; + } + x += rsd_count; + p_dst += rsd_count; + rsd_count = 0; + } else { + op_count = x_right - x; + switch (rsd_code) { + case RSD_RUN: + LG_memset(p_dst, *rsd_src, op_count); + break; + case RSD_SKIP: + break; + default: /* RSD_DUMP */ + LG_memcpy(p_dst, rsd_src, op_count); + rsd_src += op_count; + break; + } + x += op_count; + p_dst += op_count; + rsd_count -= op_count; + } + } + + /* reset x to be beginning of line and set y to next scanline. */ + x -= bm->w; + p_dst += grd_bm.row - (x_right - x_left) + x_off; + y++; + } +rsd_done: + return code; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8s.c b/engine/src/Libraries/2D/Source/Flat8/fl8s.c new file mode 100644 index 0000000..857d5eb --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8s.c @@ -0,0 +1,197 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// +// $Source: r:/prj/lib/src/2d/RCS/fl8s.asm $ +// $Revision: 1.1 $ +// $Author: kevin $ +// $Date: 1994/08/16 12:34:32 $ +// +// Inner loops of scaling and clut scaling primitives. +// +// This file is part of the 2d library. +// + +#include "cnvdat.h" +#include "gente.h" +#include "grnull.h" +#include "poly.h" +#include "tmapint.h" + +// globals +long ADD_DEST_OFF; +long ADD_DV_FRAC_OFF; +long AND_BM_ROW_OFF; +long ADD_SRC_OFF; +long SET_REPS_OFF; +long SET_OFFSET_OFF; +long JMP_LOOP_MIDDLE_OFF; + +#define unroll_num 4 +#define unroll_log 2 + +// externs +extern int gri_poly_loop(grs_tmap_loop_info *ti); + +// internal prototypes +int gri_scale_umap_loop_PPC(grs_tmap_loop_info *tli); +int gri_scale_umap_loop_68K(grs_tmap_loop_info *tli); + +// This file contains the scalers for both 68K and PowerPC +// First the routines that are generic to both, then the PowerPC routines, then +// 68K + +// ------------------------------------------------------------------------ +// Generic (68K & PowerPC) routines +// ------------------------------------------------------------------------ + +// ======================================================================== +// opaque solid polygon scaler +int gri_opaque_solid_scale_umap_init(grs_tmap_loop_info *info, grs_vertex **vert) { + info->left_edge_func = (void (*)())gri_scale_edge; + info->right_edge_func = (void (*)())gr_null; + info->bm.hlog = 0; + info->bm.bits = info->clut; + info->loop_func = (void (*)())gri_poly_loop; + info->d = ((uchar *)((long)grd_canvas->bm.row * (long)info->y)); + info->d += (long)grd_canvas->bm.bits; + return (0); +} + +// ------------------------------------------------------------------------ +// PowerPC routines +// ------------------------------------------------------------------------ +// ======================================================================== +// transparent solid polygon scaler +int gri_trans_solid_scale_umap_init(grs_tmap_loop_info *tli, grs_vertex **vert) { + tli->bm.hlog = GRL_TRANS | GRL_SOLID; + tli->loop_func = (void (*)())gri_scale_umap_loop_PPC; + tli->right_edge_func = gr_null; + tli->left_edge_func = (void (*)())gri_scale_edge; + return (0); +} + +// ======================================================================== +// transparent bitmap scaler +int gri_trans_scale_umap_init(grs_tmap_loop_info *tli, grs_vertex **vert) { + tli->bm.hlog = GRL_TRANS; + tli->loop_func = (void (*)())gri_scale_umap_loop_PPC; + tli->right_edge_func = gr_null; + tli->left_edge_func = (void (*)())gri_scale_edge; + return (0); +} + +// ======================================================================== +// opaque bitmap scaler +int gri_opaque_scale_umap_init(grs_tmap_loop_info *tli) { + tli->bm.hlog = GRL_OPAQUE; + tli->loop_func = (void (*)())gri_scale_umap_loop_PPC; + tli->right_edge_func = gr_null; + tli->left_edge_func = (void (*)())gri_scale_edge; + return (0); +} + +// ======================================================================== +// transparent clut bitmap scaler +int gri_trans_clut_scale_umap_init(grs_tmap_loop_info *tli) { + tli->bm.hlog = GRL_TRANS | GRL_CLUT; + tli->loop_func = (void (*)())gri_scale_umap_loop_PPC; + tli->right_edge_func = gr_null; + tli->left_edge_func = (void (*)())gri_scale_edge; + return (0); +} + +// ======================================================================== +// opaque clut bitmap scaler +int gri_opaque_clut_scale_umap_init(grs_tmap_loop_info *tli) { + tli->bm.hlog = GRL_OPAQUE | GRL_CLUT; + tli->loop_func = (void (*)())gri_scale_umap_loop_PPC; + tli->right_edge_func = gr_null; + tli->left_edge_func = (void (*)())gri_scale_edge; + return (0); +} + +// ======================================================================== +// main inside loop for PPC scalers +int gri_scale_umap_loop_PPC(grs_tmap_loop_info *tli) { + fix u, ul, du; + int x; + uchar k; + fix xl, xr, dx, d; + uchar *p_src, *p_dest; + + xl = fix_cint(tli->left.x); + xr = fix_cint(tli->right.x); + if (xr <= xl) + return TRUE; + ul = tli->left.u; + dx = tli->right.x - tli->left.x; + du = fix_div(tli->right.u - ul, dx); + d = fix_ceil(tli->left.x) - tli->left.x; + ul += fix_mul(du, d); + + do { + p_src = tli->bm.bits + tli->bm.row * fix_int(tli->left.v); + p_dest = grd_bm.bits + (grd_bm.row * tli->y) + xl; + switch (tli->bm.hlog) { + case GRL_OPAQUE: + for (x = xl, u = ul; x < xr; x++) { + *(p_dest++) = p_src[fix_fint(u)]; // gr_fill_upixel(k,x,tli->y); + u += du; + } + break; + case GRL_TRANS: + for (x = xl, u = ul; x < xr; x++) { + k = p_src[fix_fint(u)]; + if (k != 0) + *p_dest = k; // gr_fill_upixel(k,x,tli->y); + u += du; + p_dest++; + } + break; + case GRL_OPAQUE | GRL_CLUT: + for (x = xl, u = ul; x < xr; x++) { + *(p_dest++) = tli->clut[p_src[fix_fint(u)]]; // gr_fill_upixel(tli->clut[k],x,tli->y); + u += du; + } + break; + case GRL_TRANS | GRL_CLUT: + for (x = xl, u = ul; x < xr; x++) { + k = p_src[fix_fint(u)]; + if (k != 0) + *p_dest = tli->clut[k]; // gr_fill_upixel(tli->clut[k],x,tli->y); + u += du; + p_dest++; + } + break; + case GRL_TRANS | GRL_SOLID: + for (x = xl, u = ul; x < xr; x++) { + k = p_src[fix_fint(u)]; + if (k != 0) + *p_dest = tli->solid; // gr_fill_upixel((uchar )(tli->clut),x,tli->y); + u += du; + p_dest++; + } + break; + } + tli->left.v += tli->left.dv; + tli->y++; + } while (--(tli->n) > 0); + + return FALSE; /* tmap OK */ +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8slin.c b/engine/src/Libraries/2D/Source/Flat8/fl8slin.c new file mode 100644 index 0000000..9d46930 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8slin.c @@ -0,0 +1,95 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/fl8slin.c $ + * $Revision: 1.5 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 01:24:10 $ + * + * Routine to draw a gouraud shaded line to a flat 8 canvas. + * + * This file is part of the 2d library. + * + * $Log: fl8slin.c $ + * Revision 1.5 1994/06/11 01:24:10 lmfeeney + * guts of the routine moved to fl8{c,s}lin.h, per fill type + * line drawers are created by defining macros and including + * this file + * + * Revision 1.4 1994/05/06 18:19:13 lmfeeney + * rewritten for greater accuracy and speed + * + * Revision 1.3 1993/10/19 09:50:58 kaboom + * Replaced #include + +/* not all fill routines use all parms */ +// MLA #pragma off (unreferenced) + +#undef macro_plot_i +#define macro_plot_i(x, p, i) \ + do { \ + p[x] = i; \ + } while (0) + +void gri_flat8_usline_norm(long c, long parm, grs_vertex *v0, grs_vertex *v1) { +#include "fl8slin.h" +} + +#undef macro_plot_i +#define macro_plot_i(x, p, i) \ + do { \ + p[x] = (long)(((uchar *)parm)[i]); \ + } while (0) + +void gri_flat8_usline_clut(long c, long parm, grs_vertex *v0, grs_vertex *v1) { +#include "fl8slin.h" +} + +#undef macro_plot_i +#define macro_plot_i(x, p, i) \ + do { \ + p[x] = p[x] ^ i; \ + } while (0) + +void gri_flat8_usline_xor(long c, long parm, grs_vertex *v0, grs_vertex *v1) { +#include "fl8slin.h" +} + + /* punt */ + +#undef macro_plot_i +#define macro_plot_i(x, p, i) \ + do { \ + p[x] = i; \ + } while (0) + +void gri_flat8_usline_blend(long c, long parm, grs_vertex *v0, grs_vertex *v1) { +#include "fl8slin.h" +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8slin.h b/engine/src/Libraries/2D/Source/Flat8/fl8slin.h new file mode 100644 index 0000000..257edc9 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8slin.h @@ -0,0 +1,265 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/fl8slin.h $ + * $Revision: 1.1 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 00:50:38 $ + * + * Routine to draw a gouraud shaded line to a flat 8 canvas. + * + * This file is part of the 2d library. + * + * $Log: fl8slin.h $ + * Revision 1.1 1994/06/11 00:50:38 lmfeeney + * Initial revision + * + * Revision 1.4 1994/05/06 18:19:13 lmfeeney + * rewritten for greater accuracy and speed + * + * Revision 1.3 1993/10/19 09:50:58 kaboom + * Replaced #include with new headers split from grd.h. + * + * Revision 1.2 1993/10/01 16:01:20 kaboom + * Pared down includes to reduce dependencies. + * + * Revision 1.1 1993/07/01 22:12:15 spaz + * Initial revision + */ + +/* Draw a goroud-shaded line, specified by indices into the palette. + be warned, weird precision bugs abound (check out test programs + in /project/lib/src/2d/test). + + These have been mostly corrected. See also note.txt for correctness + arguement of new algorithm +*/ + +/* NB: directionality policy -- reversible + Lines are drawn in order of increasing x or increasing y, + for lines that have greater x or y extent, repsectively. + This is done for all lines, including horiz., vert., and + 45' lines (increasing x). +*/ + +/* NB: endpoint policy -- inclusive The left and top endpoints are + 'trunc'-ed. The right and bottom endpoints exclude the ceiling, + or equivlalently, include the pixel containing the line. This is + calculated by subtracting epsilon (i.e. 1/65536) from its + fixed-point representation, then trunc'ing. + + This makes sense if you note that open interval right + < ceil (x) + is the same as + <= trunc (x - e) +*/ + +fix x0, y0, x1, y1; +fix dx, dy; /* deltas in x and y */ +fix t; /* temporary fix */ + +fix i0, i1; +fix di; /* delta intensity */ + +uchar *p; /* pointer into the canvas */ + +x0 = v0->x; +y0 = v0->y; +x1 = v1->x; +y1 = v1->y; + +i0 = (fix)v0->i; +i1 = (fix)v1->i; + +/* set endpoints + note that this cannot go negative or change octant, since the == + case is excluded */ + +if (x0 < x1) { + x1 -= 1; /* e.g. - epsilon */ +} else if (x0 > x1) { + x0 -= 1; +} + +if (y0 < y1) { + y1 -= 1; +} else if (y0 > y1) { + y0 -= 1; +} + +dx = fix_trunc(x1) - fix_trunc(x0); /* x extent in pixels, (macro is flakey) */ +dx = fix_abs(dx); +dy = fix_trunc(y1) - fix_trunc(y0); /* y extent in pixels */ +dy = fix_abs(dy); + +if (dx == 0 && dy == 0) + return; + +/* three cases: absolute value dx < = > dy + + along the longer dimension, the fixpoint x0 (or y0) is treated + as an int + + the points are swapped if needed and the rgb initial and deltas + are calculated accordingly + + there are two or three sub-cases - a horizontal or vertical line, + and the dx or dy being added or subtracted. dx and dy + are kept as absolute values and +/- is managed in the + two separate inner loops if it's a dy, since you also have to + manage the canvas pointer + + if y is being changed by 'dy' and x is being incremented, do a + FunkyBitCheck (TM) to see whether the integer part of y has changed + and if it has, resetting the the canvas pointer to the next row + + if x is being changed by 'dx' and y is being incremented, just + add or subtract row to increment y in the canvas + + the endpoints are walked inclusively in all cases, see above + + 45' degree lines are explicitly special cased -- because it + all runs as integers, but it's probably not frequent enough + to justify the check + + */ + +if (dx > dy) { + + x0 = fix_int(x0); + x1 = fix_int(x1); + + if (x0 > x1) { + t = x0; + x0 = x1; + x1 = t; + t = y0; + y0 = y1; + y1 = t; + t = i0; + i0 = i1; + i1 = t; + } + + p = grd_bm.bits + grd_bm.row * (fix_int(y0)); + di = fix_div((i1 - i0), dx); + + if ((fix_int(y0)) == (fix_int(y1))) { + while (x0 <= x1) { + macro_plot_i(x0, p, fix_fint(i0)); + x0++; + i0 += di; + } + } else if (y0 < y1) { + dy = fix_div((y1 - y0), dx); + while (x0 <= x1) { + macro_plot_i(x0, p, fix_fint(i0)); + x0++; + y0 += dy; + p += (grd_bm.row & (-(fix_frac(y0) < dy))); + i0 += di; + } + } else { + dy = fix_div((y0 - y1), dx); + while (x0 <= x1) { + macro_plot_i(x0, p, fix_fint(i0)); + x0++; + p -= (grd_bm.row & (-(fix_frac(y0) < dy))); + y0 -= dy; + i0 += di; + } + } +} else if (dy > dx) { + + y0 = fix_int(y0); + y1 = fix_int(y1); + + if (y0 > y1) { + t = x0; + x0 = x1; + x1 = t; + t = y0; + y0 = y1; + y1 = t; + t = i0; + i0 = i1; + i1 = t; + } + + p = grd_bm.bits + grd_bm.row * y0; + di = fix_div((i1 - i0), dy); + + if ((fix_int(x0)) == (fix_int(x1))) { + x0 = fix_int(x0); + while (y0 <= y1) { + macro_plot_i(x0, p, fix_fint(i0)); + y0++; + p += grd_bm.row; + i0 += di; + } + } else { + dx = fix_div((x1 - x0), dy); + while (y0 <= y1) { + macro_plot_i(fix_fint(x0), p, fix_fint(i0)); + x0 += dx; + y0++; + p += grd_bm.row; + i0 += di; + } + } +} else { /* dy == dx, walk the x axis, all integers */ + + x0 = fix_int(x0); + x1 = fix_int(x1); + y0 = fix_int(y0); + y1 = fix_int(y1); + + if (x0 > x1) { + t = x0; + x0 = x1; + x1 = t; + t = y0; + y0 = y1; + y1 = t; + t = i0; + i0 = i1; + i1 = t; + } + + p = grd_bm.bits + grd_bm.row * y0; /* set canvas ptr */ + di = fix_div((i1 - i0), dx); + + if (y0 < y1) { + while (y0 <= y1) { + macro_plot_i(x0, p, fix_fint(i0)); + x0++; + y0++; + p += grd_bm.row; + i0 += di; + } + } else { + while (y0 >= y1) { + macro_plot_i(x0, p, fix_fint(i0)); + x0++; + y0--; + p -= grd_bm.row; + i0 += di; + } + } +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8sply.c b/engine/src/Libraries/2D/Source/Flat8/fl8sply.c new file mode 100644 index 0000000..9c94e8d --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8sply.c @@ -0,0 +1,145 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8sply.c $ + * $Revision: 1.1 $ + * $Author: kevin $ + * $Date: 1994/08/16 13:11:44 $ + * + * Routines for drawing flat shaded polygons onto a flat 8 canvas. + * + * This file is part of the 2d library. + */ + +#include "cnvdat.h" +#include "gente.h" +#include "poly.h" +#include "tlucdat.h" +#include "tmapint.h" + +// prototypes +int gri_spoly_loop(grs_tmap_loop_info *ti); +void gri_spoly_init(grs_tmap_loop_info *ti); +void gri_clut_spoly_init(grs_tmap_loop_info *ti); +void gri_stpoly_init(grs_tmap_loop_info *ti); +void gri_clut_stpoly_init(grs_tmap_loop_info *ti); + +int gri_spoly_loop(grs_tmap_loop_info *ti) { + int d; + fix i, di; + fix xl, xr; + uchar *ti_d; + fix ti_li, ti_ri; + + xl = ti->left.x; + xr = ti->right.x; + ti_li = ti->left.i; + ti_ri = ti->right.i; + ti_d = ti->d; + + do { + i = ti_li; + di = fix_div(ti_ri - i, xr - xl); + i += fix_mul(fix_ceil(xl) - xl, di); + + if ((d = fix_cint(xr) - fix_cint(xl)) > 0) { + switch (ti->bm.hlog) { + int x; + case GRL_OPAQUE: + for (x = fix_cint(xl); x < fix_cint(xr); x++) { + ti_d[x] = fix_fint(i); + i += di; + } + break; + case GRL_CLUT: + for (x = fix_cint(xl); x < fix_cint(xr); x++) { + ti_d[x] = ti->clut[fix_fint(i)]; + i += di; + } + break; + case GRL_TLUC8: + for (x = fix_cint(xl); x < fix_cint(xr); x++) { + ti_d[x] = tluc8stab[fix_light(i) + ti_d[x]]; + i += di; + } + break; + case GRL_CLUT | GRL_TLUC8: + for (x = fix_cint(xl); x < fix_cint(xr); x++) { + ti_d[x] = ti->clut[tluc8stab[fix_light(i) + ti_d[x]]]; + i += di; + } + break; + } + } else if (d < 0) { + return TRUE; + } + /* update span extrema and destination. */ + ti_d += grd_bm.row; + xl += ti->left.dx; + xr += ti->right.dx; + ti_li += ti->left.di; + ti_ri += ti->right.di; + } while ((--(ti->n)) > 0); + + ti->d = ti_d; + ti->left.x = xl; + ti->right.x = xr; + ti->left.i = ti_li; + ti->right.i = ti_ri; + + return FALSE; +} + +void gri_spoly_init(grs_tmap_loop_info *ti) { + ti->bm.hlog = GRL_OPAQUE; + ti->d = ti->y * grd_bm.row + grd_bm.bits; + ti->loop_func = (void (*)())gri_spoly_loop; + ti->top_edge_func = (void (*)())gri_ix_edge; + ti->bot_edge_func = (void (*)())gri_ix_edge; +} + +void gri_clut_spoly_init(grs_tmap_loop_info *ti) { + ti->bm.hlog = GRL_CLUT; + ti->d = ti->y * grd_bm.row + grd_bm.bits; + ti->loop_func = (void (*)())gri_spoly_loop; + ti->top_edge_func = (void (*)())gri_ix_edge; + ti->bot_edge_func = (void (*)())gri_ix_edge; +} + +void gri_stpoly_init(grs_tmap_loop_info *ti) { + if (tluc8stab != NULL) + ti->bm.hlog = GRL_TLUC8; + else + ti->bm.hlog = GRL_OPAQUE; + ti->d = ti->y * grd_bm.row + grd_bm.bits; + ti->loop_func = (void (*)())gri_spoly_loop; + ti->top_edge_func = (void (*)())gri_ix_edge; + ti->bot_edge_func = (void (*)())gri_ix_edge; +} + +void gri_clut_stpoly_init(grs_tmap_loop_info *ti) { + if (tluc8stab != NULL) + ti->bm.hlog = GRL_CLUT | GRL_TLUC8; + else + ti->bm.hlog = GRL_CLUT; + ti->d = ti->y * grd_bm.row + grd_bm.bits; + ti->loop_func = (void (*)())gri_spoly_loop; + ti->top_edge_func = (void (*)())gri_ix_edge; + ti->bot_edge_func = (void (*)())gri_ix_edge; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8sub.c b/engine/src/Libraries/2D/Source/Flat8/fl8sub.c new file mode 100644 index 0000000..35aded3 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8sub.c @@ -0,0 +1,45 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/fl8sub.c $ + * $Revision: 1.2 $ + * $Author: kaboom $ + * $Date: 1993/10/08 01:15:35 $ + * + * Sub bitmap routine for flat 8 bitmaps. + * + * This file is part of the 2d library. + * + * $Log: fl8sub.c $ + * Revision 1.2 1993/10/08 01:15:35 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.1 1993/04/29 18:46:54 kaboom + * Initial revision + * + */ + +#include "grs.h" + +grs_bitmap *flat8_sub_bitmap(grs_bitmap *bm, short x, short y, short w, short h) { + bm->bits += y * bm->row + x; + bm->w = w; + bm->h = h; + return bm; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8tf.h b/engine/src/Libraries/2D/Source/Flat8/fl8tf.h new file mode 100644 index 0000000..f82cf85 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8tf.h @@ -0,0 +1,140 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8tf.h $ + * $Revision: 1.14 $ + * $Author: kevin $ + * $Date: 1994/10/25 16:51:42 $ + * + * Table functions. + * Flat8 canvas. + * + * This file is part of the 2d library. + * + */ + +#include "pertyp.h" +#include "tmapint.h" + +/* pixel primitives */ +extern void flat8_set_upixel(long color, short x, short y); +extern void flat8_clut_set_upixel(long color, short x, short y); +extern void flat8_xor_set_upixel(long color, short x, short y); +extern void flat8_blend_set_upixel(long color, short x, short y); +extern void flat8_solid_set_upixel(long color, short x, short y); + +/* blit primitives */ +extern void flat8_flat8_ubitmap(grs_bitmap *bm, short x, short y); + +extern void flat8_tluc8_ubitmap(grs_bitmap *bm, short x, short y); +extern int gri_flat8_mask_bitmap(grs_bitmap *bm, short x, short y, grs_stencil *sten); +extern void gri_flat8_clut_ubitmap(grs_bitmap *bm, short x, short y, uchar *cl); +extern int gri_flat8_mask_fill_clut_bitmap(grs_bitmap *bm, short x, short y, grs_stencil *sten); +extern void gri_flat8_fill_clut_ubitmap(grs_bitmap *bm, short x, short y); +extern void gri_flat8_rsd8_ubitmap(grs_bitmap *bm, short x, short y); +extern int gri_flat8_rsd8_bitmap(grs_bitmap *bm, short x_left, short y_top); + +/* inner loop initializers */ +/* normal fill: */ +extern void gri_trans_blend_clut_lin_umap_init(grs_tmap_loop_info *ti); +extern void gri_opaque_true_lin_umap_init(); +extern void gri_opaque_clut_true_lin_umap_init(); + +extern void gri_opaque_lin_umap_init(grs_tmap_loop_info *tli); +extern void gri_trans_lin_umap_init(grs_tmap_loop_info *tli); +extern void gri_opaque_lit_lin_umap_init(grs_tmap_loop_info *tli); +extern void gri_trans_lit_lin_umap_init(grs_tmap_loop_info *tli); +extern void gri_opaque_clut_lin_umap_init(grs_tmap_loop_info *tli); +extern void gri_trans_clut_lin_umap_init(grs_tmap_loop_info *tli); + +extern void gri_opaque_floor_umap_init(grs_tmap_loop_info *tli); +extern void gri_trans_floor_umap_init(grs_tmap_loop_info *tli); +extern void gri_opaque_lit_floor_umap_init(grs_tmap_loop_info *tli); +extern void gri_trans_lit_floor_umap_init(grs_tmap_loop_info *tli); +extern void gri_opaque_clut_floor_umap_init(grs_tmap_loop_info *tli); +extern void gri_trans_clut_floor_umap_init(grs_tmap_loop_info *tli); + +extern void gri_opaque_wall_umap_init(grs_tmap_loop_info *tli); +extern void gri_trans_wall_umap_init(grs_tmap_loop_info *tli); +extern void gri_opaque_lit_wall_umap_init(grs_tmap_loop_info *tli); +extern void gri_trans_lit_wall_umap_init(grs_tmap_loop_info *tli); +extern void gri_opaque_clut_wall_umap_init(grs_tmap_loop_info *tli); +extern void gri_trans_clut_wall_umap_init(grs_tmap_loop_info *tli); + +extern void gri_opaque_wall1d_umap_init(grs_tmap_loop_info *tli); +extern void gri_trans_wall1d_umap_init(grs_tmap_loop_info *tli); +extern void gri_opaque_clut_wall1d_umap_init(grs_tmap_loop_info *tli); +extern void gri_trans_lit_wall1d_umap_init(grs_tmap_loop_info *tli); +extern void gri_opaque_lit_wall1d_umap_init(grs_tmap_loop_info *tli); +extern void gri_trans_clut_wall1d_umap_init(grs_tmap_loop_info *tli); + +extern void gri_opaque_per_umap_hscan_init(grs_bitmap *bm, grs_per_setup *ps); +extern void gri_trans_per_umap_hscan_init(grs_bitmap *bm, grs_per_setup *ps); +extern void gri_opaque_per_umap_vscan_init(grs_bitmap *bm, grs_per_setup *ps); +extern void gri_trans_per_umap_vscan_init(grs_bitmap *bm, grs_per_setup *ps); +extern void gri_opaque_lit_per_umap_hscan_init(grs_bitmap *bm, grs_per_setup *ps); +extern void gri_trans_lit_per_umap_hscan_init(grs_bitmap *bm, grs_per_setup *ps); + +extern void gri_opaque_lit_per_umap_vscan_init(grs_bitmap *bm, grs_per_setup *ps); +extern void gri_trans_lit_per_umap_vscan_init(grs_bitmap *bm, grs_per_setup *ps); +extern void gri_opaque_clut_per_umap_hscan_init(grs_bitmap *bm, grs_per_setup *ps); +extern void gri_trans_clut_per_umap_hscan_init(grs_bitmap *bm, grs_per_setup *ps); +extern void gri_opaque_clut_per_umap_vscan_init(grs_bitmap *bm, grs_per_setup *ps); +extern void gri_trans_clut_per_umap_vscan_init(grs_bitmap *bm, grs_per_setup *ps); + +extern int gri_opaque_scale_umap_init(grs_tmap_loop_info *tli); +extern int gri_trans_scale_umap_init(grs_tmap_loop_info *tli, grs_vertex **vert); +extern int gri_opaque_clut_scale_umap_init(grs_tmap_loop_info *tli); +extern int gri_trans_clut_scale_umap_init(grs_tmap_loop_info *tli); + +extern void gri_poly_init(grs_tmap_loop_info *ti); +extern void gri_spoly_init(grs_tmap_loop_info *ti); +extern void gri_cpoly_init(grs_tmap_loop_info *ti); +extern void gri_tpoly_init(grs_tmap_loop_info *ti); +extern void gri_stpoly_init(grs_tmap_loop_info *ti); + +/* clut fill: */ +extern void gri_clut_poly_init(grs_tmap_loop_info *ti); +extern void gri_clut_spoly_init(grs_tmap_loop_info *ti); +extern void gri_clut_cpoly_init(grs_tmap_loop_info *ti); +extern void gri_clut_tpoly_init(grs_tmap_loop_info *ti); +extern void gri_clut_stpoly_init(grs_tmap_loop_info *ti); + +/* solid fill: */ +extern void gri_solid_poly_init(grs_tmap_loop_info *ti); +extern void gri_trans_solid_lin_umap_init(grs_tmap_loop_info *ti); +extern void gri_trans_solid_floor_umap_init(grs_tmap_loop_info *ti); +extern void gri_trans_solid_wall_umap_init(grs_tmap_loop_info *ti); +extern void gri_trans_solid_per_umap_hscan_init(grs_bitmap *bm, grs_per_setup *ps); +extern void gri_trans_solid_per_umap_vscan_init(grs_bitmap *bm, grs_per_setup *ps); +extern int gri_opaque_solid_scale_umap_init(grs_tmap_loop_info *info, grs_vertex **vert); +extern int gri_trans_solid_scale_umap_init(grs_tmap_loop_info *tli, grs_vertex **vert); + +/* translucent bitmaps */ +extern void gri_tluc8_opaque_lin_umap_init(grs_tmap_loop_info *tli); +extern void gri_tluc8_trans_lin_umap_init(grs_tmap_loop_info *tli); +extern void gri_tluc8_opaque_lit_lin_umap_init(); +extern void gri_tluc8_trans_lit_lin_umap_init(); +extern void gri_tluc8_opaque_clut_lin_umap_init(grs_tmap_loop_info *tli); +extern void gri_tluc8_trans_clut_lin_umap_init(grs_tmap_loop_info *tli); + +extern void gri_tluc8_opaque_scale_umap_init(grs_tmap_loop_info *tli); +extern void gri_tluc8_trans_scale_umap_init(grs_tmap_loop_info *tli); +extern void gri_tluc8_opaque_clut_scale_umap_init(grs_tmap_loop_info *tli); +extern void gri_tluc8_trans_clut_scale_umap_init(grs_tmap_loop_info *tli); diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8tl8.c b/engine/src/Libraries/2D/Source/Flat8/fl8tl8.c new file mode 100644 index 0000000..71925c0 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8tl8.c @@ -0,0 +1,79 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/fl8tl8.c $ + * $Revision: 1.2 $ + * $Author: baf $ + * $Date: 1994/01/14 12:40:30 $ + * + * Routines for drawing flat 8 bitmaps into a flat 8 canvas. + * + * This file is part of the 2d library. + * + * $Log: fl8tl8.c $ + * Revision 1.2 1994/01/14 12:40:30 baf + * Lit translucency reform + * + * Revision 1.1 1993/12/01 21:17:31 baf + * Initial revision + * + */ + +#include "bitmap.h" +#include "cnvdat.h" +#include "fl8tf.h" +#include "tluctab.h" +#include + +void flat8_tluc8_ubitmap(grs_bitmap *bm, short x, short y) { + uchar *src; + uchar *dst; + long w = bm->w; + long h = bm->h; + long i; + long grow = grd_bm.row; + long brow = bm->row; + + src = bm->bits; + dst = grd_bm.bits + grow * y + x; + + if (bm->flags & BMF_TRANS) + while (h--) { + for (i = 0; i < w; i++) + if (src[i] != 0) { + if (tluc8tab[src[i]] == NULL) + dst[i] = src[i]; + else + dst[i] = tluc8tab[src[i]][dst[i]]; + } + src += brow; + dst += grow; + } + else + while (h--) { + for (i = 0; i < w; i++) { + if (tluc8tab[src[i]] == NULL) + dst[i] = src[i]; + else + dst[i] = tluc8tab[src[i]][dst[i]]; + } + src += brow; + dst += grow; + } +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8tmapdv.h b/engine/src/Libraries/2D/Source/Flat8/fl8tmapdv.h new file mode 100644 index 0000000..1437801 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8tmapdv.h @@ -0,0 +1,33 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifdef __cplusplus +extern "C" { +#endif +extern fix fix_mul_asm_safe(fix a, fix b); +#ifdef __cplusplus +} +#endif + +// InvDIv = 0 to use divs in mappers, !=0 to use inverse multiplies +#define InvDiv 1 + +// this macro does a safe divide on a light, which we can shift up for more +// precision +#define fix_mul_asm_safe_light(a, b) ((fix_mul_asm_safe(a, b) + 0x00FF) >> 8) diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8tpl.c b/engine/src/Libraries/2D/Source/Flat8/fl8tpl.c new file mode 100644 index 0000000..834c59f --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8tpl.c @@ -0,0 +1,305 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8tp.c $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/08/16 12:57:22 $ + * + * full perspective texture mapper. + * scanline processors. + * + */ + +#include "cnvdat.h" +#include "fl8tmapdv.h" +#include "pertyp.h" +#include "plytyp.h" + +// prototypes +void gri_trans_per_umap_hscan_scanline(grs_per_info *pi, grs_bitmap *bm); +void gri_trans_per_umap_vscan_scanline(grs_per_info *pi, grs_bitmap *bm); +void gri_trans_per_umap_hscan_init(grs_bitmap *bm, grs_per_setup *ps); +void gri_trans_per_umap_vscan_init(grs_bitmap *bm, grs_per_setup *ps); + +void gri_trans_per_umap_hscan_scanline(grs_per_info *pi, grs_bitmap *bm) { + register int k, y_cint; + uchar *p, temp_pix; + + // locals used to speed PPC code + fix l_u, l_v, l_du, l_dv, l_y_fix, l_scan_slope, l_dtl, l_dxl, l_dyl, l_dtr, l_dyr; + int l_x, l_xl, l_xr, l_xr0, l_u_mask, l_v_mask, l_v_shift; + int gr_row, temp_y; + uchar *bm_bits; + + gr_row = grd_bm.row; + bm_bits = bm->bits; + l_dyr = pi->dyr; + l_dtr = pi->dtr; + l_dyl = pi->dyl; + l_dxl = pi->dxl; + l_dtl = pi->dtl; + l_scan_slope = pi->scan_slope; + l_y_fix = pi->y_fix; + l_v_shift = pi->v_shift; + l_v_mask = pi->v_mask; + l_u_mask = pi->u_mask; + l_xr0 = pi->xr0; + l_x = pi->x; + l_xl = pi->xl; + l_xr = pi->xr; + l_u = pi->u; + l_v = pi->v; + l_du = pi->du; + l_dv = pi->dv; + + l_y_fix = l_x * l_scan_slope + fix_make(pi->yp, 0xffff); + +#if InvDiv + k = fix_div(fix_make(1, 0), pi->denom); + l_u = pi->u0 + fix_mul_asm_safe(pi->unum, k); + l_v = pi->v0 + fix_mul_asm_safe(pi->vnum, k); + l_du = fix_mul_asm_safe(pi->dunum, k); + l_dv = fix_mul_asm_safe(pi->dvnum, k); +#else + l_u = pi->u0 + fix_div(pi->unum, pi->denom); + l_v = pi->v0 + fix_div(pi->vnum, pi->denom); + l_du = fix_div(pi->dunum, pi->denom); + l_dv = fix_div(pi->dvnum, pi->denom); +#endif + + l_u += l_x * l_du; + l_v += l_x * l_dv; + + y_cint = fix_int(l_y_fix); + if (l_scan_slope < 0) + gr_row = -gr_row; + p = grd_bm.bits + l_x + y_cint * grd_bm.row; + if (l_x < l_xl) { + fix test = l_x * l_dyl - y_cint * l_dxl + pi->cl; + for (; l_x < l_xl; l_x++) { + if (test <= 0) { + int k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + temp_pix = bm_bits[k]; + if (temp_pix != 0) + *p = temp_pix; // gr_fill_upixel(bm_bits[k],l_x,y_cint); + } + temp_y = y_cint; + y_cint = fix_int(l_y_fix += l_scan_slope); + if (temp_y != y_cint) { + p += gr_row; + test += l_dtl; + } else + test += l_dyl; + + p++; + l_u += l_du; + l_v += l_dv; + } + } + + for (; l_x < l_xr0; l_x++) { + int k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + temp_pix = bm_bits[k]; + if (temp_pix != 0) + *p = temp_pix; // gr_fill_upixel(bm_bits[k],l_x,y_cint); + temp_y = y_cint; + y_cint = fix_int(l_y_fix += l_scan_slope); + if (temp_y != y_cint) + p += gr_row; + + p++; + l_u += l_du; + l_v += l_dv; + } + + if (l_x < l_xr) { + fix test = l_x * l_dyr - y_cint * pi->dxr + pi->cr; + p = grd_bm.bits + l_x + y_cint * grd_bm.row; + for (; l_x < l_xr; l_x++) { + if (test >= 0) { + int k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + temp_pix = bm_bits[k]; + if (temp_pix != 0) + *p = temp_pix; // gr_fill_upixel(bm_bits[k],l_x,y_cint); + } + temp_y = y_cint; + y_cint = fix_int(l_y_fix += l_scan_slope); + if (temp_y != y_cint) { + p += gr_row; + test += l_dtr; + } else + test += l_dyr; + + p++; + l_u += l_du; + l_v += l_dv; + } + } + + pi->y_fix = l_y_fix; + pi->x = l_x; + pi->u = l_u; + pi->v = l_v; + pi->du = l_du; + pi->dv = l_dv; +} + +void gri_trans_per_umap_vscan_scanline(grs_per_info *pi, grs_bitmap *bm) { + register int k, x_cint; + + // locals used to speed PPC code + fix l_dxr, l_x_fix, l_u, l_v, l_du, l_dv, l_scan_slope, l_dtl, l_dxl, l_dyl, l_dtr, l_dyr; + int l_yl, l_yr0, l_yr, l_y, l_u_mask, l_v_mask, l_v_shift; + int gr_row, temp_x; + uchar *bm_bits; + uchar *p, temp_pix; + + gr_row = grd_bm.row; + bm_bits = bm->bits; + l_dxr = pi->dxr; + l_x_fix = pi->x_fix; + l_y = pi->y; + l_yr = pi->yr; + l_yr0 = pi->yr0; + l_yl = pi->yl; + l_dyr = pi->dyr; + l_dtr = pi->dtr; + l_dyl = pi->dyl; + l_dxl = pi->dxl; + l_dtl = pi->dtl; + l_scan_slope = pi->scan_slope; + l_v_shift = pi->v_shift; + l_v_mask = pi->v_mask; + l_u_mask = pi->u_mask; + l_u = pi->u; + l_v = pi->v; + l_du = pi->du; + l_dv = pi->dv; + + l_x_fix = l_y * l_scan_slope + fix_make(pi->xp, 0xffff); + +#if InvDiv + k = fix_div(fix_make(1, 0), pi->denom); + l_u = pi->u0 + fix_mul_asm_safe(pi->unum, k); + l_v = pi->v0 + fix_mul_asm_safe(pi->vnum, k); + l_du = fix_mul_asm_safe(pi->dunum, k); + l_dv = fix_mul_asm_safe(pi->dvnum, k); +#else + l_u = pi->u0 + fix_div(pi->unum, pi->denom); + l_v = pi->v0 + fix_div(pi->vnum, pi->denom); + l_du = fix_div(pi->dunum, pi->denom); + l_dv = fix_div(pi->dvnum, pi->denom); +#endif + + l_u += l_y * l_du; + l_v += l_y * l_dv; + + x_cint = fix_int(l_x_fix); + p = grd_bm.bits + x_cint + l_y * gr_row; + if (l_y < l_yl) { + fix test = l_y * l_dxl - x_cint * l_dyl + pi->cl; + for (; l_y < l_yl; l_y++) { + if (test <= 0) { + int k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + temp_pix = bm_bits[k]; + if (temp_pix != 0) + *p = temp_pix; // gr_fill_upixel(bm_bits[k],x_cint,l_y); + } + temp_x = x_cint; + x_cint = fix_int(l_x_fix += l_scan_slope); + if (temp_x != x_cint) { + test += l_dtl; + p -= (temp_x - x_cint); + } else + test += l_dxl; + + p += gr_row; + l_u += l_du; + l_v += l_dv; + } + } + + for (; l_y < l_yr0; l_y++) { + int k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + temp_pix = bm_bits[k]; + if (temp_pix != 0) + *p = temp_pix; // gr_fill_upixel(bm_bits[k],x_cint,l_y); + + temp_x = x_cint; + x_cint = fix_int(l_x_fix += l_scan_slope); + if (temp_x != x_cint) + p -= (temp_x - x_cint); + + p += gr_row; + l_u += l_du; + l_v += l_dv; + } + + if (l_y < l_yr) { + fix test = l_y * l_dxr - x_cint * l_dyr + pi->cr; + p = grd_bm.bits + x_cint + l_y * gr_row; + for (; l_y < l_yr; l_y++) { + if (test >= 0) { + int k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + temp_pix = bm_bits[k]; + if (temp_pix != 0) + *p = temp_pix; // gr_fill_upixel(bm_bits[k],x_cint,l_y); + } + + temp_x = x_cint; + x_cint = fix_int(l_x_fix += l_scan_slope); + if (temp_x != x_cint) { + test += l_dtr; + p -= (temp_x - x_cint); + } else + test += l_dxr; + + p += gr_row; + l_u += l_du; + l_v += l_dv; + } + } + + pi->x_fix = l_x_fix; + pi->y = l_y; + pi->u = l_u; + pi->v = l_v; + pi->du = l_du; + pi->dv = l_dv; +} + +extern void gri_per_umap_hscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps); +extern void gri_per_umap_vscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps); + +void gri_trans_per_umap_hscan_init(grs_bitmap *bm, grs_per_setup *ps) { + ps->shell_func = (void (*)())gri_per_umap_hscan; + ps->scanline_func = (void (*)())gri_trans_per_umap_hscan_scanline; +} + +void gri_trans_per_umap_vscan_init(grs_bitmap *bm, grs_per_setup *ps) { + ps->shell_func = (void (*)())gri_per_umap_vscan; + ps->scanline_func = (void (*)())gri_trans_per_umap_vscan_scanline; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8tsmap.c b/engine/src/Libraries/2D/Source/Flat8/fl8tsmap.c new file mode 100644 index 0000000..6b7b35a --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8tsmap.c @@ -0,0 +1,562 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Implementation solid transparent mappers +// +// This file is part of the 2d library. +// + +#include "cnvdat.h" +#include "fl8tf.h" +#include "gente.h" +#include "poly.h" +#include "tmapint.h" +#include "vtab.h" + +// prototypes +int gri_trans_solid_lin_umap_loop(grs_tmap_loop_info *tli); +int gri_trans_solid_floor_umap_loop(grs_tmap_loop_info *tli); +int gri_solid_wall_umap_loop(grs_tmap_loop_info *tli); +void gri_trans_solid_per_umap_hscan_scanline(grs_per_info *pi, grs_bitmap *bm); +void gri_trans_solid_per_umap_vscan_scanline(grs_per_info *pi, grs_bitmap *bm); + +int gri_trans_solid_lin_umap_loop(grs_tmap_loop_info *tli) { + fix u, v, du, dv, dx, d; + + // locals used to store copies of tli-> stuff, so its in registers on the PPC + int x; + uchar solid_color; + int t_xl, t_xr; + uchar *p_dest; + int32_t *t_vtab; + uchar *t_bits; + uchar *t_clut; + uchar t_wlog; + uint32_t t_mask; + int32_t gr_row; + uchar *start_pdest; + + solid_color = tli->solid; + u = tli->left.u; + du = tli->right.u - u; + v = tli->left.v; + dv = tli->right.v - v; + dx = tli->right.x - tli->left.x; + + t_vtab = tli->vtab; + t_mask = tli->mask; + t_wlog = tli->bm.wlog; + + t_bits = tli->bm.bits; + gr_row = grd_bm.row; + start_pdest = grd_bm.bits + (gr_row * (tli->y)); + + do { + if ((d = fix_ceil(tli->right.x) - fix_ceil(tli->left.x)) > 0) { + d = fix_ceil(tli->left.x) - tli->left.x; + du = fix_div(du, dx); + dv = fix_div(dv, dx); + u += fix_mul(du, d); + v += fix_mul(dv, d); + + // copy out tli-> stuff into locals + t_xl = fix_cint(tli->left.x); + t_xr = fix_cint(tli->right.x); + p_dest = start_pdest + t_xl; + + if (tli->bm.hlog == GRL_TRANS) { + for (x = t_xl; x < t_xr; x++) { + if (t_bits[t_vtab[fix_fint(v)] + fix_fint(u)]) + *p_dest = solid_color; // gr_fill_upixel(t_bits[k],x,y); + p_dest++; + u += du; + v += dv; + } + } else { + for (x = t_xl; x < t_xr; x++) { + if (t_bits[((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask]) + *p_dest = solid_color; // gr_fill_upixel(t_bits[k],x,y); + p_dest++; + u += du; + v += dv; + } + } + } else if (d < 0) + return TRUE; /* punt this tmap */ + + u = (tli->left.u += tli->left.du); + tli->right.u += tli->right.du; + du = tli->right.u - u; + v = (tli->left.v += tli->left.dv); + tli->right.v += tli->right.dv; + dv = tli->right.v - v; + tli->left.x += tli->left.dx; + tli->right.x += tli->right.dx; + dx = tli->right.x - tli->left.x; + tli->y++; + start_pdest += gr_row; + } while (--(tli->n) > 0); + + return FALSE; /* tmap OK */ +} + +void gri_trans_solid_lin_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_TRANS | GRL_LOG2; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_TRANS; + } + tli->loop_func = (void (*)())gri_trans_solid_lin_umap_loop; + tli->right_edge_func = (void (*)())gri_uvx_edge; + tli->left_edge_func = (void (*)())gri_uvx_edge; +} + +int gri_trans_solid_floor_umap_loop(grs_tmap_loop_info *tli) { + fix u, v, du, dv, dx, d; + uchar solid_color; + int x; + // locals used to store copies of tli-> stuff, so its in registers on the PPC + int t_xl, t_xr, t_y, gr_row; + int32_t *t_vtab; + uchar *t_bits; + uchar *p_dest; + uchar temp_pix; + uchar t_wlog; + uint32_t t_mask; + + solid_color = tli->solid; + u = fix_div(tli->left.u, tli->w); + du = fix_div(tli->right.u, tli->w) - u; + v = fix_div(tli->left.v, tli->w); + dv = fix_div(tli->right.v, tli->w) - v; + dx = tli->right.x - tli->left.x; + + t_mask = tli->mask; + t_wlog = tli->bm.wlog; + t_vtab = tli->vtab; + t_bits = tli->bm.bits; + gr_row = grd_bm.row; + + do { + if ((d = fix_ceil(tli->right.x) - fix_ceil(tli->left.x)) > 0) { + d = fix_ceil(tli->left.x) - tli->left.x; + du = fix_div(du, dx); + u += fix_mul(du, d); + dv = fix_div(dv, dx); + v += fix_mul(dv, d); + + // copy out tli-> stuff into locals + t_xl = fix_cint(tli->left.x); + t_xr = fix_cint(tli->right.x); + t_y = tli->y; + p_dest = grd_bm.bits + (grd_bm.row * t_y) + t_xl; + + if (tli->bm.hlog == GRL_TRANS) { + for (x = t_xl; x < t_xr; x++) { + int k = t_vtab[fix_fint(v)] + fix_fint(u); + if (t_bits[k]) + *p_dest = solid_color; // gr_fill_upixel(t_bits[k],x,t_y); + p_dest++; + u += du; + v += dv; + } + } else { + for (x = t_xl; x < t_xr; x++) { + int k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + if (t_bits[k]) + *p_dest = solid_color; // gr_fill_upixel(t_bits[k],x,t_y); + p_dest++; + u += du; + v += dv; + } + } + } else if (d < 0) + return TRUE; /* punt this tmap */ + tli->w += tli->dw; + u = fix_div((tli->left.u += tli->left.du), tli->w); + tli->right.u += tli->right.du; + du = fix_div(tli->right.u, tli->w) - u; + v = fix_div((tli->left.v += tli->left.dv), tli->w); + tli->right.v += tli->right.dv; + dv = fix_div(tli->right.v, tli->w) - v; + tli->left.x += tli->left.dx; + tli->right.x += tli->right.dx; + dx = tli->right.x - tli->left.x; + tli->y++; + } while (--(tli->n) > 0); + return FALSE; /* tmap OK */ +} + +void gri_trans_solid_floor_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_TRANS | GRL_LOG2; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_TRANS; + } + tli->loop_func = (void (*)())gri_trans_solid_floor_umap_loop; + tli->left_edge_func = (void (*)())gri_uvwx_edge; + tli->right_edge_func = (void (*)())gri_uvwx_edge; +} + +int gri_solid_wall_umap_loop(grs_tmap_loop_info *tli) { + fix u, v, du, dv, dy, d; + uchar solid_color; + + // locals used to store copies of tli-> stuff, so its in registers on the PPC + int t_yl, t_yr; + int32_t *t_vtab; + uchar *t_bits; + uchar *p_dest; + uchar *t_clut; + uchar t_wlog; + uint32_t t_mask; + int32_t gr_row; + int y; + + solid_color = tli->solid; + u = fix_div(tli->left.u, tli->w); + du = fix_div(tli->right.u, tli->w) - u; + v = fix_div(tli->left.v, tli->w); + dv = fix_div(tli->right.v, tli->w) - v; + dy = tli->right.y - tli->left.y; + + t_bits = tli->bm.bits; + t_vtab = tli->vtab; + t_mask = tli->mask; + t_wlog = tli->bm.wlog; + + gr_row = grd_bm.row; + + // handle PowerPC loop + do { + if ((d = fix_ceil(tli->right.y) - fix_ceil(tli->left.y)) > 0) { + + d = fix_ceil(tli->left.y) - tli->left.y; + du = fix_div(du, dy); + dv = fix_div(dv, dy); + u += fix_mul(du, d); + v += fix_mul(dv, d); + + t_yl = fix_cint(tli->left.y); + t_yr = fix_cint(tli->right.y); + p_dest = grd_bm.bits + (gr_row * t_yl) + tli->x; + + if (tli->bm.hlog == GRL_TRANS) { + for (y = t_yl; y < t_yr; y++) { + int k = t_vtab[fix_fint(v)] + fix_fint(u); + if (t_bits[k]) + *p_dest = solid_color; // gr_fill_upixel(t_bits[k],t_x,y); + p_dest += gr_row; + u += du; + v += dv; + } + } else { + for (y = t_yl; y < t_yr; y++) { + int k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + if (t_bits[k]) + *p_dest = solid_color; // gr_fill_upixel(t_bits[k],t_x,y); + p_dest += gr_row; + u += du; + v += dv; + } + } + } else if (d < 0) + return TRUE; /* punt this tmap */ + + tli->w += tli->dw; + u = fix_div((tli->left.u += tli->left.du), tli->w); + tli->right.u += tli->right.du; + du = fix_div(tli->right.u, tli->w) - u; + v = fix_div((tli->left.v += tli->left.dv), tli->w); + tli->right.v += tli->right.dv; + dv = fix_div(tli->right.v, tli->w) - v; + tli->left.y += tli->left.dy; + tli->right.y += tli->right.dy; + dy = tli->right.y - tli->left.y; + tli->x++; + + } while (--(tli->n) > 0); + + return FALSE; +} + +void gri_trans_solid_wall_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_TRANS | GRL_LOG2; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_TRANS; + } + tli->loop_func = (void (*)())gri_solid_wall_umap_loop; + tli->left_edge_func = (void (*)())gri_uvwy_edge; + tli->right_edge_func = (void (*)())gri_uvwy_edge; +} + +void gri_trans_solid_per_umap_hscan_scanline(grs_per_info *pi, grs_bitmap *bm) { + int y_cint; + uchar *p; + uchar solid_color; + + // locals used to speed PPC code + fix l_u, l_v, l_du, l_dv, l_y_fix, l_scan_slope, l_dtl, l_dxl, l_dyl, l_dtr, l_dyr; + int l_x, l_xl, l_xr, l_xr0, l_u_mask, l_v_mask, l_v_shift; + int gr_row, temp_y; + uchar *bm_bits; + + solid_color = pi->fill_parm; + gr_row = grd_bm.row; + bm_bits = bm->bits; + l_dyr = pi->dyr; + l_dtr = pi->dtr; + l_dyl = pi->dyl; + l_dxl = pi->dxl; + l_dtl = pi->dtl; + l_scan_slope = pi->scan_slope; + l_y_fix = pi->y_fix; + l_v_shift = pi->v_shift; + l_v_mask = pi->v_mask; + l_u_mask = pi->u_mask; + l_xr0 = pi->xr0; + l_x = pi->x; + l_xl = pi->xl; + l_xr = pi->xr; + l_u = pi->u; + l_v = pi->v; + l_du = pi->du; + l_dv = pi->dv; + + l_y_fix = l_x * l_scan_slope + fix_make(pi->yp, 0xffff); + l_u = pi->u0 + fix_div(pi->unum, pi->denom); + l_v = pi->v0 + fix_div(pi->vnum, pi->denom); + l_du = fix_div(pi->dunum, pi->denom); + l_dv = fix_div(pi->dvnum, pi->denom); + l_u += l_x * l_du; + l_v += l_x * l_dv; + + y_cint = fix_int(l_y_fix); + if (l_scan_slope < 0) + gr_row = -gr_row; + + p = grd_bm.bits + l_x + y_cint * grd_bm.row; + if (l_x < l_xl) { + fix test = l_x * l_dyl - y_cint * l_dxl + pi->cl; + for (; l_x < l_xl; l_x++) { + if (test <= 0) { + int k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + if (bm_bits[k]) + *p = solid_color; // gr_fill_upixel(bm_bits[k],l_x,y_cint); + } + temp_y = y_cint; + y_cint = fix_int(l_y_fix += l_scan_slope); + if (temp_y != y_cint) { + test += l_dtl; + p += gr_row; + } else + test += l_dyl; + + p++; + l_u += l_du; + l_v += l_dv; + } + } + + for (; l_x < l_xr0; l_x++) { + int k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + if (bm_bits[k]) + *p = solid_color; // gr_fill_upixel(bm_bits[k],l_x,y_cint); + temp_y = y_cint; + y_cint = fix_int(l_y_fix += l_scan_slope); + if (temp_y != y_cint) // y_cint=fix_int((l_y_fix+=l_scan_slope)); + { + temp_y -= y_cint; + p += gr_row; + } + + p++; + l_u += l_du; + l_v += l_dv; + } + + if (l_x < l_xr) { + fix test = l_x * l_dyr - y_cint * pi->dxr + pi->cr; + p = grd_bm.bits + l_x + y_cint * grd_bm.row; + for (; l_x < l_xr; l_x++) { + if (test >= 0) { + int k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + if (bm_bits[k]) + *p = solid_color; // gr_fill_upixel(bm_bits[k],l_x,y_cint); + } + temp_y = y_cint; + y_cint = fix_int(l_y_fix += l_scan_slope); + if (temp_y != y_cint) { + test += l_dtr; + p += gr_row; + } else + test += l_dyr; + + p++; + l_u += l_du; + l_v += l_dv; + } + } + + pi->y_fix = l_y_fix; + pi->x = l_x; + pi->u = l_u; + pi->v = l_v; + pi->du = l_du; + pi->dv = l_dv; +} + +void gri_trans_solid_per_umap_vscan_scanline(grs_per_info *pi, grs_bitmap *bm) { + int x_cint; + uchar solid_color; + + // locals used to speed PPC code + fix l_dxr, l_x_fix, l_u, l_v, l_du, l_dv, l_scan_slope, l_dtl, l_dxl, l_dyl, l_dtr, l_dyr; + int l_yl, l_yr0, l_yr, l_y, l_u_mask, l_v_mask, l_v_shift; + int gr_row, temp_x; + uchar *bm_bits; + uchar *p; + + solid_color = pi->fill_parm; + gr_row = grd_bm.row; + bm_bits = bm->bits; + l_dxr = pi->dxr; + l_x_fix = pi->x_fix; + l_y = pi->y; + l_yr = pi->yr; + l_yr0 = pi->yr0; + l_yl = pi->yl; + l_dyr = pi->dyr; + l_dtr = pi->dtr; + l_dyl = pi->dyl; + l_dxl = pi->dxl; + l_dtl = pi->dtl; + l_scan_slope = pi->scan_slope; + l_v_shift = pi->v_shift; + l_v_mask = pi->v_mask; + l_u_mask = pi->u_mask; + l_u = pi->u; + l_v = pi->v; + l_du = pi->du; + l_dv = pi->dv; + + l_x_fix = l_y * l_scan_slope + fix_make(pi->xp, 0xffff); + + l_u = pi->u0 + fix_div(pi->unum, pi->denom); + l_v = pi->v0 + fix_div(pi->vnum, pi->denom); + l_du = fix_div(pi->dunum, pi->denom); + l_dv = fix_div(pi->dvnum, pi->denom); + l_u += l_y * l_du; + l_v += l_y * l_dv; + + x_cint = fix_int(l_x_fix); + p = grd_bm.bits + x_cint + l_y * gr_row; + if (l_y < l_yl) { + fix test = l_y * l_dxl - x_cint * l_dyl + pi->cl; + for (; l_y < l_yl; l_y++) { + if (test <= 0) { + int k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + if (bm_bits[k]) + *p = solid_color; // gr_fill_upixel(bm_bits[k],x_cint,l_y); + } + temp_x = x_cint; + x_cint = fix_int(l_x_fix += l_scan_slope); + if (temp_x != x_cint) { + test += l_dtl; + p -= (temp_x - x_cint); + } else + test += l_dxl; + + p += gr_row; + l_u += l_du; + l_v += l_dv; + } + } + + for (; l_y < l_yr0; l_y++) { + int k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + if (bm_bits[k]) + *p = solid_color; // gr_fill_upixel(bm_bits[k],x_cint,l_y); + + temp_x = x_cint; + x_cint = fix_int(l_x_fix += l_scan_slope); + if (temp_x != x_cint) + p -= (temp_x - x_cint); + + p += gr_row; + l_u += l_du; + l_v += l_dv; + } + + if (l_y < l_yr) { + fix test = l_y * l_dxr - x_cint * l_dyr + pi->cr; + p = grd_bm.bits + x_cint + l_y * gr_row; + for (; l_y < l_yr; l_y++) { + if (test >= 0) { + int k = (l_u >> 16) & l_u_mask; + k += (l_v >> l_v_shift) & l_v_mask; + if (bm_bits[k]) + *p = solid_color; // gr_fill_upixel(bm_bits[k],x_cint,l_y); + } + + temp_x = x_cint; + x_cint = fix_int(l_x_fix += l_scan_slope); + if (temp_x != x_cint) { + test += l_dtr; + p -= (temp_x - x_cint); + } else + test += l_dxr; + + p += gr_row; + l_u += l_du; + l_v += l_dv; + } + } + + pi->x_fix = l_x_fix; + pi->y = l_y; + pi->u = l_u; + pi->v = l_v; + pi->du = l_du; + pi->dv = l_dv; +} + +extern void gri_per_umap_hscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps); +extern void gri_per_umap_vscan(grs_bitmap *bm, int n, grs_vertex **vpl, grs_per_setup *ps); + +void gri_trans_solid_per_umap_hscan_init(grs_bitmap *bm, grs_per_setup *ps) { + ps->shell_func = (void (*)())gri_per_umap_hscan; + ps->scanline_func = (void (*)())gri_trans_solid_per_umap_hscan_scanline; +} + +void gri_trans_solid_per_umap_vscan_init(grs_bitmap *bm, grs_per_setup *ps) { + ps->shell_func = (void (*)())gri_per_umap_vscan; + ps->scanline_func = (void (*)())gri_trans_solid_per_umap_vscan_scanline; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8vlin.c b/engine/src/Libraries/2D/Source/Flat8/fl8vlin.c new file mode 100644 index 0000000..e562dd3 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8vlin.c @@ -0,0 +1,134 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8vlin.c $ + * $Revision: 1.7 $ + * $Author: lmfeeney $ + * $Date: 1994/08/12 01:10:20 $ + * + * Routines for drawing vertical lines into a flat 8 canvas. + * + * This file is part of the 2d library. + * + * $Log: fl8vlin.c $ + * Revision 1.7 1994/08/12 01:10:20 lmfeeney + * get fill/solid parm from right place + * + * Revision 1.6 1994/06/11 01:44:53 lmfeeney + * unclipped flat8 line drawer routines for each fill type + * added canvas values as parameters + * + * Revision 1.5 1993/10/19 09:51:05 kaboom + * Replaced #include y1) { + t = y0; + y0 = y1; + y1 = t; + } + if (gr_get_fill_type() == FILL_SOLID) + c = (uchar)parm; + + p = grd_bm.bits + y0 * grow + x0; + for (; y0 <= y1; y0++) { + *p = c; + p += grow; + } +} + +void gri_flat8_uvline_clut(short x0, short y0, short y1, long c, long parm) { + uchar *p; + short t; + int grow = grd_bm.row; + + if (y0 > y1) { + t = y0; + y0 = y1; + y1 = t; + } + c = (long)(((uchar *)parm)[c]); + p = grd_bm.bits + y0 * grow + x0; + for (; y0 <= y1; y0++) { + *p = c; + p += grow; + } +} + +void gri_flat8_uvline_xor(short x0, short y0, short y1, long c, long parm) { + uchar *p; + short t; + int grow = grd_bm.row; + + if (y0 > y1) { + t = y0; + y0 = y1; + y1 = t; + } + p = grd_bm.bits + y0 * grow + x0; + for (; y0 <= y1; y0++) { + *p = *p ^ c; + p += grow; + } +} + +/* punt */ +void gri_flat8_uvline_blend(short x0, short y0, short y1, long c, long parm) { + uchar *p; + short t; + int grow = grd_bm.row; + + if (y0 > y1) { + t = y0; + y0 = y1; + y1 = t; + } + p = grd_bm.bits + y0 * grow + x0; + for (; y0 <= y1; y0++) { + *p = c; + p += grow; + } +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8w.c b/engine/src/Libraries/2D/Source/Flat8/fl8w.c new file mode 100644 index 0000000..dc3df5e --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8w.c @@ -0,0 +1,407 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/genw.c $ + * $Revision: 1.4 $ + * $Author: kevin $ + * $Date: 1994/08/16 12:50:15 $ + * + * Routines to floor texture map a flat8 bitmap to a generic canvas. + * + * This file is part of the 2d library. + * + */ + +#include "cnvdat.h" +#include "fl8tf.h" +#include "fl8tmapdv.h" +#include "gente.h" +#include "poly.h" +#include "tmapint.h" +#include "vtab.h" + +#include + +int gri_wall_umap_loop(grs_tmap_loop_info *tli); +int gri_wall_umap_loop_1D(grs_tmap_loop_info *tli); + +int gri_wall_umap_loop(grs_tmap_loop_info *tli) { + fix u, v, du, dv, dy, d; + + // locals used to store copies of tli-> stuff, so its in registers on the PPC + int k, y; + uchar t_wlog; + uint32_t t_mask; + int32_t *t_vtab; + uchar *t_bits; + uchar *p_dest; + fix inv_dy; + uchar temp_pix; + uchar *t_clut; + int32_t gr_row; + +#if InvDiv + inv_dy = fix_div(fix_make(1, 0), tli->w); + u = fix_mul_asm_safe(tli->left.u, inv_dy); + du = fix_mul_asm_safe(tli->right.u, inv_dy) - u; + v = fix_mul_asm_safe(tli->left.v, inv_dy); + dv = fix_mul_asm_safe(tli->right.v, inv_dy) - v; +#else + u = fix_div(tli->left.u, tli->w); + du = fix_div(tli->right.u, tli->w) - u; + v = fix_div(tli->left.v, tli->w); + dv = fix_div(tli->right.v, tli->w) - v; +#endif + + dy = tli->right.y - tli->left.y; + + t_vtab = tli->vtab; + t_bits = tli->bm.bits; + + t_clut = tli->clut; + t_mask = tli->mask; + t_wlog = tli->bm.wlog; + + gr_row = grd_bm.row; + + do { + if ((d = fix_ceil(tli->right.y) - fix_ceil(tli->left.y)) > 0) { + + d = fix_ceil(tli->left.y) - tli->left.y; + +#if InvDiv + inv_dy = fix_div(fix_make(1, 0), dy); + du = fix_mul_asm_safe(du, inv_dy); + dv = fix_mul_asm_safe(dv, inv_dy); +#else + du = fix_div(du, dy); + dv = fix_div(dv, dy); +#endif + u += fix_mul(du, d); + v += fix_mul(dv, d); + + p_dest = grd_bm.bits + (gr_row * fix_cint(tli->left.y)) + tli->x; + y = fix_cint(tli->right.y) - fix_cint(tli->left.y); + + switch (tli->bm.hlog) { + case GRL_OPAQUE: + for (; y > 0; y--) { + k = t_vtab[fix_fint(v)] + fix_fint(u); + *p_dest = t_bits[k]; // gr_fill_upixel(t_bits[k],t_x,y); + p_dest += gr_row; + u += du; + v += dv; + } + break; + case GRL_TRANS: + for (; y > 0; y--) { + temp_pix = t_bits[t_vtab[fix_fint(v)] + fix_fint(u)]; + if (temp_pix != 0) + *p_dest = temp_pix; // gr_fill_upixel(t_bits[k],t_x,y); + p_dest += gr_row; + u += du; + v += dv; + } + break; + case GRL_OPAQUE | GRL_LOG2: + for (; y > 0; y--) { + *p_dest = + t_bits[((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask]; // gr_fill_upixel(t_bits[k],t_x,y); + p_dest += gr_row; + u += du; + v += dv; + } + break; + case GRL_TRANS | GRL_LOG2: + for (; y > 0; y--) { + temp_pix = t_bits[((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask]; + if (temp_pix != 0) + *p_dest = temp_pix; // gr_fill_upixel(t_bits[k],t_x,y); + p_dest += gr_row; + u += du; + v += dv; + } + break; + case GRL_OPAQUE | GRL_CLUT: + for (; y > 0; y--) { + *p_dest = + t_clut[t_bits[t_vtab[fix_fint(v)] + fix_fint(u)]]; // gr_fill_upixel(t_clut[t_bits[k]],t_x,y); + p_dest += gr_row; + u += du; + v += dv; + } + break; + case GRL_TRANS | GRL_CLUT: + for (; y > 0; y--) { + k = t_vtab[fix_fint(v)] + fix_fint(u); + k = t_bits[k]; + if (k != 0) + *p_dest = t_clut[k]; // gr_fill_upixel(t_clut[k],t_x,y); + p_dest += gr_row; + u += du; + v += dv; + } + break; + case GRL_OPAQUE | GRL_LOG2 | GRL_CLUT: + for (; y > 0; y--) { + *p_dest = t_clut[t_bits[((fix_fint(v) << t_wlog) + fix_fint(u)) & + t_mask]]; // gr_fill_upixel(t_clut[t_bits[k]],t_x,y); + p_dest += gr_row; + u += du; + v += dv; + } + break; + case GRL_TRANS | GRL_LOG2 | GRL_CLUT: + for (; y > 0; y--) { + k = ((fix_fint(v) << t_wlog) + fix_fint(u)) & t_mask; + k = t_bits[k]; + if (k != 0) + *p_dest = t_clut[k]; // gr_fill_upixel(t_clut[k],t_x,y); + p_dest += gr_row; + u += du; + v += dv; + } + break; + } + } else if (d < 0) + return TRUE; /* punt this tmap */ + + tli->w += tli->dw; + + // figure out new left u & v & i + inv_dy = 0; + k = tli->left.u + tli->left.du; + y = tli->left.v + tli->left.dv; + +#if InvDiv + inv_dy = fix_div(fix_make(1, 0), tli->w); + u = fix_mul_asm_safe(k, inv_dy); + v = fix_mul_asm_safe(y, inv_dy); +#else + u = fix_div(k, tli->w); + v = fix_div(y, tli->w); +#endif + + tli->left.u = k; + tli->left.v = y; + + // figure out new right u & v & i + k = tli->right.u + tli->right.du; + y = tli->right.v + tli->right.dv; + +#if InvDiv + du = fix_mul_asm_safe(k, inv_dy) - u; + dv = fix_mul_asm_safe(y, inv_dy) - v; +#else + du = fix_div(k, tli->w) - u; + dv = fix_div(y, tli->w) - v; +#endif + + tli->right.u = k; + tli->right.v = y; + + tli->left.y += tli->left.dy; + tli->right.y += tli->right.dy; + dy = tli->right.y - tli->left.y; + tli->x++; + + } while (--(tli->n) > 0); + + return false; +} + +void gri_trans_wall_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_TRANS | GRL_LOG2; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_TRANS; + } + tli->loop_func = (void (*)())gri_wall_umap_loop; + tli->left_edge_func = (void (*)())gri_uvwy_edge; + tli->right_edge_func = (void (*)())gri_uvwy_edge; +} + +void gri_opaque_wall_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_OPAQUE | GRL_LOG2; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_OPAQUE; + } + tli->loop_func = (void (*)())gri_wall_umap_loop; + tli->left_edge_func = (void (*)())gri_uvwy_edge; + tli->right_edge_func = (void (*)())gri_uvwy_edge; +} + +void gri_trans_clut_wall_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_TRANS | GRL_LOG2 | GRL_CLUT; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_TRANS | GRL_CLUT; + } + tli->loop_func = (void (*)())gri_wall_umap_loop; + tli->left_edge_func = (void (*)())gri_uvwy_edge; + tli->right_edge_func = (void (*)())gri_uvwy_edge; +} + +void gri_opaque_clut_wall_umap_init(grs_tmap_loop_info *tli) { + if ((tli->bm.row == (1 << tli->bm.wlog)) && (tli->bm.h == (1 << tli->bm.hlog))) { + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_OPAQUE | GRL_LOG2 | GRL_CLUT; + } else { + tli->vtab = gr_make_vtab(&(tli->bm)); + tli->bm.hlog = GRL_OPAQUE | GRL_CLUT; + } + tli->loop_func = (void (*)())gri_wall_umap_loop; + tli->left_edge_func = (void (*)())gri_uvwy_edge; + tli->right_edge_func = (void (*)())gri_uvwy_edge; +} + +/*extern "C" +{ +extern int HandleWallLoop1D_PPC(grs_tmap_loop_info *tli, + fix u, fix v, fix dv, fix dy, + uchar *t_clut, int32_t *t_vtab, uchar *o_bits, + int32_t gr_row, uint32_t t_mask, uint32_t t_wlog); +}*/ + +int HandleWallLoop1D_C(grs_tmap_loop_info *tli, fix u, fix v, fix dv, fix dy, uchar *t_clut, int32_t *t_vtab, + uchar *o_bits, int32_t gr_row, uint32_t t_mask, uint32_t t_wlog) { + register int k, y; + register fix inv_dy; + register uchar *grd_bits, *p_dest, *t_bits; + register fix ry, ly; + + ry = tli->right.y; + ly = tli->left.y; + + grd_bits = grd_bm.bits + tli->x; + tli->x += tli->n; + do { + if ((k = fix_ceil(ry) - fix_ceil(ly)) > 0) { + + k = fix_ceil(ly) - ly; + + dv = fix_div(dv, dy); + v += fix_mul(dv, k); + + p_dest = grd_bits + (gr_row * fix_cint(ly)); + y = fix_cint(ry) - fix_cint(ly); + t_bits = o_bits + fix_fint(u); + for (; y > 0; y--) { + k = ((fix_fint(v) << t_wlog)) & t_mask; + *p_dest = t_clut[t_bits[k]]; // gr_fill_upixel(t_clut[t_bits[k]],t_x,y); + v += dv; + p_dest += gr_row; + } + } else if (k < 0) + return TRUE; // punt this tmap + + tli->w += tli->dw; + + // figure out new left u & v + k = tli->left.u + tli->left.du; + y = tli->left.v + tli->left.dv; + + inv_dy = fix_div(fix_make(1, 0), tli->w); + u = fix_mul_asm_safe(k, inv_dy); + v = fix_mul_asm_safe(y, inv_dy); + + tli->left.u = k; + tli->left.v = y; + + // figure out new right u & v + tli->right.u += tli->right.du; + y = tli->right.v + tli->right.dv; + dv = fix_mul_asm_safe(y, inv_dy) - v; + tli->right.v = y; + + ly += tli->left.dy; + ry += tli->right.dy; + dy = ry - ly; + grd_bits++; + } while (--(tli->n) > 0); + + tli->right.y = ry; + tli->left.y = ly; + + return false; +} + +// ================================================================== +// 1D versions +int gri_wall_umap_loop_1D(grs_tmap_loop_info *tli) { + fix u, v, dv, dy, d; + + // locals used to store copies of tli-> stuff, so its in registers on the PPC + int k, y; + uchar t_wlog; + uint32_t t_mask; + int32_t *t_vtab; + uchar *t_bits, *o_bits; + uchar *p_dest; + fix inv_dy; + uchar temp_pix; + uchar *t_clut; + int32_t gr_row; + +#if InvDiv + inv_dy = fix_div(fix_make(1, 0), tli->w); + u = fix_mul_asm_safe(tli->left.u, inv_dy); + v = fix_mul_asm_safe(tli->left.v, inv_dy); + dv = fix_mul_asm_safe(tli->right.v, inv_dy) - v; +#else + u = fix_div(tli->left.u, tli->w); + v = fix_div(tli->left.v, tli->w); + dv = fix_div(tli->right.v, tli->w) - v; +#endif + + dy = tli->right.y - tli->left.y; + + t_vtab = tli->vtab; + o_bits = tli->bm.bits; + + t_clut = tli->clut; + t_mask = tli->mask; + t_wlog = tli->bm.wlog; + + gr_row = grd_bm.row; + + return HandleWallLoop1D_C(tli, u, v, dv, dy, t_clut, t_vtab, o_bits, gr_row, t_mask, t_wlog); +} + +void gri_opaque_clut_wall1d_umap_init(grs_tmap_loop_info *tli) { + // MLA - Wall1d is always log2 + /* if ((tli->bm.row==(1<bm.wlog)) && + (tli->bm.h==(1<bm.hlog))) {*/ + tli->mask = (1 << (tli->bm.hlog + tli->bm.wlog)) - 1; + tli->bm.hlog = GRL_OPAQUE | GRL_LOG2 | GRL_CLUT; + /* } else { + tli->vtab=gr_make_vtab(&(tli->bm)); + tli->bm.hlog=GRL_OPAQUE|GRL_CLUT; + }*/ + tli->loop_func = (void (*)())gri_wall_umap_loop_1D; + tli->left_edge_func = (void (*)())gri_uvwy_edge; + tli->right_edge_func = (void (*)())gri_uvwy_edge; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8wclin.c b/engine/src/Libraries/2D/Source/Flat8/fl8wclin.c new file mode 100644 index 0000000..ae8ffd0 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8wclin.c @@ -0,0 +1,87 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8wclin.c $ + * $Revision: 1.3 $ + * $Author: kevin $ + * $Date: 1994/10/17 15:00:00 $ + * + * Routines to draw wire polys. + * + * This file is part of the 2d library. + * + */ + +#include "cnvdat.h" +#include "plytyp.h" +#include "scrdat.h" + +#define gr_get_ipal_index(r, g, b) (long)((((r) >> 19) & 0x1f) | (((g) >> 14) & 0x3e0) | (((b) >> 9) & 0x7c00)) +#define do_hline_inc_x \ + do { \ + c = grd_ipal[gr_get_ipal_index(r0, g0, b0)]; \ + p[x] = c; \ + r0 += dr, g0 += dg, b0 += db; \ + x++; \ + } while (x < x_new) +#define do_hline_dec_x \ + if (x == x_new) { \ + c = grd_ipal[gr_get_ipal_index(r0, g0, b0)]; \ + p[x] = c; \ + r0 += dr, g0 += dg, b0 += db; \ + } else \ + do { \ + x--; \ + c = grd_ipal[gr_get_ipal_index(r0, g0, b0)]; \ + p[x] = c; \ + r0 += dr, g0 += dg, b0 += db; \ + } while (x > x_new) + +// MLA #pragma off (unreferenced) +void gri_flat8_wire_poly_ucline_norm(long c, long parm, grs_vertex *v0, grs_vertex *v1) { +#include "fl8wclin.h" +} + // MLA #pragma on (unreferenced) + +#undef do_hline_inc_x +#define do_hline_inc_x \ + do { \ + c = grd_ipal[gr_get_ipal_index(r0, g0, b0)]; \ + p[x] = clut[c]; \ + r0 += dr, g0 += dg, b0 += db; \ + x++; \ + } while (x < x_new) +#undef do_hline_dec_x +#define do_hline_dec_x \ + if (x == x_new) { \ + c = grd_ipal[gr_get_ipal_index(r0, g0, b0)]; \ + p[x] = clut[c]; \ + r0 += dr, g0 += dg, b0 += db; \ + } else \ + do { \ + x--; \ + c = grd_ipal[gr_get_ipal_index(r0, g0, b0)]; \ + p[x] = clut[c]; \ + r0 += dr, g0 += dg, b0 += db; \ + } while (x > x_new) + +void gri_flat8_wire_poly_ucline_clut(long c, long parm, grs_vertex *v0, grs_vertex *v1) { + uchar *clut = (uchar *)parm; +#include "fl8wclin.h" +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8wclin.h b/engine/src/Libraries/2D/Source/Flat8/fl8wclin.h new file mode 100644 index 0000000..02d0a0a --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8wclin.h @@ -0,0 +1,116 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8wclin.h $ + * $Revision: 1.1 $ + * $Author: kevin $ + * $Date: 1994/08/04 09:58:31 $ + * + * Main shell for drawing wire poly rgb lines. + * + * This file is part of the 2d library. + * + */ + +/* don't try to use this other than in fl8wclin.c */ + +int d, y, y_max, x, x_new; +fix x0, y0, r0, b0, g0; +fix x1, y1, r1, b1, g1; +fix dr, dg, db, x_fix, dx; +uchar *p; + +if (v1->y > v0->y) { + y = fix_cint(v0->y); + y_max = fix_cint(v1->y); +} else { + y = fix_cint(v1->y); + y_max = fix_cint(v0->y); +} +p = grd_bm.bits + y * grd_bm.row; + +/* horizontal? */ +if (y_max - y <= 1) { + if (v1->x > v0->x) { + x_new = fix_cint(v1->x), x = fix_cint(v0->x); + r0 = fix_make(v0->u, 0), g0 = fix_make(v0->v, 0), b0 = fix_make(v0->w, 0); + r1 = fix_make(v1->u, 0), g1 = fix_make(v1->v, 0), b1 = fix_make(v1->w, 0); + } else { + x_new = fix_cint(v0->x), x = fix_cint(v1->x); + r0 = fix_make(v1->u, 0), g0 = fix_make(v1->v, 0), b0 = fix_make(v1->w, 0); + r1 = fix_make(v0->u, 0), g1 = fix_make(v0->v, 0), b1 = fix_make(v0->w, 0); + } + d = x_new - x; + if (d > 0) { + dr = (r1 - r0) / d; + dg = (g1 - g0) / d; + db = (b1 - b0) / d; + do_hline_inc_x; + } + return; +} + +/* not horizontal */ +if (v1->y > v0->y) { + y0 = v0->y, x0 = v0->x; + r0 = fix_make(v0->u, 0), g0 = fix_make(v0->v, 0), b0 = fix_make(v0->w, 0); + y1 = v1->y, x1 = v1->x; + r1 = fix_make(v1->u, 0), g1 = fix_make(v1->v, 0), b1 = fix_make(v1->w, 0); +} else { + y1 = v0->y, x1 = v0->x; + r1 = fix_make(v0->u, 0), g1 = fix_make(v0->v, 0), b1 = fix_make(v0->w, 0); + y0 = v1->y, x0 = v1->x; + r0 = fix_make(v1->u, 0), g0 = fix_make(v1->v, 0), b0 = fix_make(v1->w, 0); +} +dx = fix_div(x1 - x0, y1 - y0); +if (fix_abs(dx) > FIX_UNIT) { + d = fix_cint(x1) - fix_cint(x0); + if (d < 0) + d = -d; +} else { + d = y_max - y; +} +dr = (r1 - r0) / d; +dg = (g1 - g0) / d; +db = (b1 - b0) / d; +x = fix_cint(x0); +x_fix = x0 + fix_mul(fix_ceil(y0) - y0, dx); +x_new = fix_cint(x_fix); + +/* draw line */ +if (dx >= 0) { + do_hline_inc_x; + do { + x = x_new; + x_new = fix_cint(x_fix += dx); + do_hline_inc_x; + p += grd_bm.row; + } while ((++y) < y_max - 1); + x = x_new; + x_new = fix_cint(x1); + do_hline_inc_x; +} else { + do { + do_hline_dec_x; + x_new = fix_cint(x_fix += dx); + p += grd_bm.row; + } while ((++y) < y_max - 1); + x_new = fix_cint(x1); + do_hline_dec_x; +} diff --git a/engine/src/Libraries/2D/Source/Flat8/fl8wlin.c b/engine/src/Libraries/2D/Source/Flat8/fl8wlin.c new file mode 100644 index 0000000..c941693 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/fl8wlin.c @@ -0,0 +1,116 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fl8wlin.c $ + * $Revision: 1.1 $ + * $Author: kevin $ + * $Date: 1994/08/04 09:53:09 $ + * + * Routines to draw wire polys. + * + * This file is part of the 2d library. + * + */ + +#include "cnvdat.h" +#include "ctxmac.h" +#include "fill.h" +#include "plytyp.h" + +#define gr_get_ipal_index(r, g, b) (long)((((r) >> 19) & 0x1f) | (((g) >> 14) & 0x3e0) | (((b) >> 9) & 0x7c00)) +#define do_hline_inc_x \ + do { \ + p[x] = c; \ + x++; \ + } while (x < x_new) +#define do_hline_dec_x \ + if (x == x_new) { \ + p[x] = c; \ + } else \ + do { \ + x--; \ + p[x] = c; \ + } while (x > x_new) + +void gri_flat8_wire_poly_uline(long c, long parm, grs_vertex *v0, grs_vertex *v1) { + int y, y_max, x, x_new; + fix x0, y0; + fix x1, y1; + fix x_fix, dx; + uchar *p; + + if (gr_get_fill_type() == FILL_SOLID) + c = (uchar)parm; + else if (gr_get_fill_type() == FILL_CLUT) + c = ((uchar *)parm)[c]; + if (v1->y > v0->y) { + y = fix_cint(v0->y); + y_max = fix_cint(v1->y); + } else { + y = fix_cint(v1->y); + y_max = fix_cint(v0->y); + } + p = grd_bm.bits + y * grd_bm.row; + + /* horizontal? */ + if (y_max - y <= 1) { + if (v1->x > v0->x) { + x_new = fix_cint(v1->x), x = fix_cint(v0->x); + } else { + x_new = fix_cint(v0->x), x = fix_cint(v1->x); + } + do_hline_inc_x; + return; + } + + /* not horizontal */ + if (v1->y > v0->y) { + y0 = v0->y, x0 = v0->x; + y1 = v1->y, x1 = v1->x; + } else { + y1 = v0->y, x1 = v0->x; + y0 = v1->y, x0 = v1->x; + } + dx = fix_div(x1 - x0, y1 - y0); + x = fix_cint(x0); + x_fix = x0 + fix_mul(fix_ceil(y0) - y0, dx); + x_new = fix_cint(x_fix); + + /* draw line */ + if (dx >= 0) { + do_hline_inc_x; + do { + x = x_new; + x_new = fix_cint(x_fix += dx); + do_hline_inc_x; + p += grd_bm.row; + } while ((++y) < y_max - 1); + x = x_new; + x_new = fix_cint(x1); + do_hline_inc_x; + } else { + do { + do_hline_dec_x; + x_new = fix_cint(x_fix += dx); + p += grd_bm.row; + } while ((++y) < y_max - 1); + x_new = fix_cint(x1); + do_hline_dec_x; + } +} diff --git a/engine/src/Libraries/2D/Source/Flat8/flat8.h b/engine/src/Libraries/2D/Source/Flat8/flat8.h new file mode 100644 index 0000000..28e7d94 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Flat8/flat8.h @@ -0,0 +1,306 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/flat8.h $ + * $Revision: 1.50 $ + * $Author: kevin $ + * $Date: 1994/08/16 15:31:34 $ + * + * Prototypes for routines for drawing into flat8 bitmaps. + * + * This file is part of the 2d library. + * + * $Log: flat8.h $ + * Revision 1.50 1994/08/16 15:31:34 kevin + * removed obsolete scaler function declarations. + * + * Revision 1.49 1994/07/18 17:05:21 kevin + * Moved temp_ functions to general.h. + * + * Revision 1.48 1994/04/09 07:19:32 lmfeeney + * added routines for scaled mono bitmaps + * + * Revision 1.47 1994/03/29 17:31:21 kevin + * Added doofy floor and wall mapper chaining primitives. + * + * Revision 1.46 1994/03/15 13:07:34 kevin + * Added clut_bitmap procedures. + * + * Revision 1.45 1994/03/14 17:58:22 kevin + * Added declarations for bitmap doubling routines. + * + * Revision 1.44 1994/02/26 22:50:39 kevin + * Fixed declarations for texture mapping functions. + * + * Revision 1.43 1994/02/24 22:22:33 baf + * Added tluc8 clut scaler. + * + * Revision 1.42 1994/02/14 20:38:35 baf + * Added dummy parameter to cpoly and spoly routines, for uniformity needed by + * 3D. + * + * Revision 1.41 1994/01/17 22:13:16 baf + * Redid tluc8 spolys (again). + * + * Revision 1.40 1994/01/13 12:18:47 kevin + * Added new scaling primitives. + * + * Revision 1.39 1994/01/05 04:30:45 kevin + * new lit linear mapper. + * + * Revision 1.38 1994/01/03 23:32:03 kevin + * Added prototypes for new temorary chaining primitives. + * + * Revision 1.37 1993/12/30 11:04:28 baf + * non/span solid filled polygons + * + * Revision 1.36 1993/12/28 22:04:29 baf + * Added solid RSD stuff + * + * Revision 1.35 1993/12/04 17:29:57 kevin + * Added clut_per_umap declaration. + * + * Revision 1.34 1993/12/04 12:14:19 kevin + * Added declarations for clut wall and floor mappers. + * .. + * + * Revision 1.33 1993/12/01 21:18:56 baf + * Added some tluc8 stuff. + * + * Revision 1.32 1993/11/24 01:49:05 kevin + * Added declarations for wall and floor texture mapping primitives. + * + * Revision 1.31 1993/11/18 23:31:25 kevin + * Changed flat8 perspective mapper names in honor of installing + * working versions of eric's spiffy algorithm. + * + * Revision 1.30 1993/11/10 22:46:55 kevin + * Added declaration for flat8 clut scaler. + * + * Revision 1.29 1993/10/26 02:09:10 kevin + * Added prototypes for rsd bitmap scaling and clut-scaling primitives. + * + * Revision 1.28 1993/10/20 15:20:29 kaboom + * Updated prototypes for spoly routines. + * + * Revision 1.27 1993/10/19 09:56:04 kaboom + * Updated names of polygon routines. + * + * Revision 1.26 1993/10/08 00:37:46 kevin + * Added declaration for clut linear mapper optimized for flat8 canvasses. + * + * Revision 1.25 1993/10/06 13:28:30 kevin + * Added clut version of hflip routine. + * + * Revision 1.24 1993/10/02 01:58:13 kaboom + * Put accidentally deleted prototype for flat8_lin_lit_utmap() back in. + * + * Revision 1.23 1993/10/01 16:02:12 kaboom + * Updated names for linear and perspective mappers. + * + * Revision 1.22 1993/09/08 21:48:34 kaboom + * Added prototype for flat8_lin_lit_utmap(). + * + * Revision 1.21 1993/09/07 17:45:18 kaboom + * Renamed flat8_lin_umap to flat8_flat8_lin_umap(). + * + * Revision 1.20 1993/09/02 20:04:44 kaboom + * Added prototypes for 24-bit pixel routines. + * + * Revision 1.19 1993/08/10 19:06:30 kaboom + * Added prototype for flat8_lit_utmap. + * + * Revision 1.18 1993/08/05 20:07:09 jaemz + * Added fl8ntrp2 and fl8fltr2 + * + * Revision 1.17 1993/07/01 22:11:05 spaz + * Added prototypes for fl8clin, fl8slin + * + * Revision 1.16 1993/06/14 14:09:38 kaboom + * Added prototypes for new lin_{u}map and lin_lit_lin_{u}map routines. + * + * Revision 1.15 1993/06/06 15:10:56 kaboom + * Added prototype for flat8_hflip_flat8_ubitmap(). + * + * Revision 1.14 1993/05/03 13:50:16 kaboom + * Moved declarations for span rendering routines to different file. + * + * Revision 1.13 1993/03/29 18:29:54 kaboom + * Removed convex_ from polygon scanner names. + * + * Revision 1.12 1993/03/02 19:44:55 kaboom + * Took out prototype for flat8_int_uline(). + * + * Revision 1.11 1993/02/26 17:51:53 kaboom + * Added prototype for flat8_fix_convex_upoly. + * + * Revision 1.10 1993/02/25 12:58:54 kaboom + * Added prototypes for flat 8 Gouraud shaders. + * + * Revision 1.9 1993/02/24 11:02:47 kaboom + * Added prototypes for more span functions. + * + * Revision 1.8 1993/02/22 20:31:28 kaboom + * Added prototypes for flat 8 span routines. + * + * Revision 1.7 1993/02/16 14:30:33 kaboom + * Added prototype for flat8_urect(). + * + * Revision 1.6 1993/01/07 21:04:21 kaboom + * Moved declaration for flat8_func here. + * + * Revision 1.5 1992/12/30 15:11:51 kaboom + * Added prototypes for flat8_calc_vram() and flat8_calc_row(). + * + * Revision 1.4 1992/12/14 18:12:45 kaboom + * Added prototype for flat8_sub_bm(). + * + * Revision 1.3 1992/12/11 13:19:29 kaboom + * Added prototype for flat8_get_flat8_ubitmap. + * + * Revision 1.2 1992/11/19 09:15:16 kaboom + * Corrected typo---flat8_upoint occurred twice. + * + * Revision 1.1 1992/11/19 02:34:30 kaboom + * Initial revision + */ + +#ifndef __FLAT8_H +#define __FLAT8_H +#include "grs.h" +#include "plytyp.h" +#include "tmapint.h" + +/* 8-bit pixel prototypes. */ +extern void flat8_set_upixel(long color, short x, short y); +extern int flat8_set_pixel(long color, short x, short y); +extern long flat8_get_upixel(short x, short y); +extern long flat8_get_pixel(short x, short y); + +/* 24-bit pixel prototypes. */ +extern void flat8_set_upixel24(long color, short x, short y); +extern int flat8_set_pixel24(long color, short x, short y); +extern long flat8_get_upixel24(short x, short y); +extern long flat8_get_pixel24(short x, short y); + +/* straight, rectangular-type primitives. */ +extern void flat8_clear(long color); +extern void flat8_upoint(short x, short y); +extern int flat8_point(short x, short y); +extern void flat8_uhline(short x0, short y0, short x1); +extern void flat8_uvline(short x0, short y0, short y1); +extern void flat8_urect(short left, short top, short right, short bot); + +/* fixed-point rendering-type primitives. */ +extern void flat8_fix_uline(fix x0, fix y0, fix x1, fix y1); +extern void flat8_fix_usline(fix x0, fix y0, fix i0, fix x1, fix y1, fix i1); +extern void flat8_fix_ucline(fix x0, fix y0, grs_rgb c0, fix x1, fix y1, grs_rgb c1); +extern void flat8_upoly(long c, int n, grs_vertex **vpl); +extern void flat8_uspoly(long c, int n, grs_vertex **vpl); +extern void flat8_ucpoly(long c, int n, grs_vertex **vpl); +extern void flat8_interp2_ubitmap(grs_bitmap *bm); +extern void flat8_filter2_ubitmap(grs_bitmap *bm); +extern void flat8_tluc8_upoly(long c, int n, grs_vertex **vpl); +extern void flat8_tluc8_uspoly(long c, int n, grs_vertex **vpl); + +extern void flat8_flat8_lin_umap(grs_bitmap *bm, int n, grs_vertex **vpl); +extern void flat8_lit_lin_umap(grs_bitmap *bm, int n, grs_vertex **vpl); +extern void flat8_clut_lin_umap(grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); +extern void flat8_flat8_solid_lin_umap(grs_bitmap *bm, int n, grs_vertex **vpl, int c); + +extern void flat8_flat8_wall_umap(grs_bitmap *bm, int n, grs_vertex **vpl); +extern void flat8_flat8_lit_wall_umap(grs_bitmap *bm, int n, grs_vertex **vpl); +extern void flat8_flat8_clut_wall_umap(grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); + +extern void flat8_flat8_floor_umap(grs_bitmap *bm, int n, grs_vertex **vpl); +extern void flat8_flat8_lit_floor_umap(grs_bitmap *bm, int n, grs_vertex **vpl); +extern void flat8_flat8_clut_floor_umap(grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); + +extern int flat8_flat8_per_umap(grs_bitmap *bm, int n, grs_vertex **vpl); +extern int flat8_flat8_lit_per_umap(grs_bitmap *bm, int n, grs_vertex **vpl); +extern int flat8_flat8_clut_per_umap(grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); +extern int flat8_flat8_solid_per_umap(grs_bitmap *bm, int n, grs_vertex **vpl, int c); + +extern void flat8_lin_lit_utmap(int n, fix *vlist, grs_bitmap *bm, fix *m, fix *l); + +extern void flat8_scale_ubitmap(grs_bitmap *bm, short x, short y, short w, short h); +extern int flat8_scale_bitmap(grs_bitmap *bm, short x, short y, short w, short h); + +extern void flat8_mono_scale_ubitmap(grs_bitmap *bm, short x, short y, short w, short h); +extern int flat8_mono_scale_bitmap(grs_bitmap *bm, short x, short y, short w, short h); + +extern void flat8_rsd8_scale_ubitmap(grs_bitmap *bm, short x, short y, short w, short h); +extern int flat8_rsd8_scale_bitmap(grs_bitmap *bm, short x, short y, short w, short h); + +extern void flat8_flat8_clut_scale_ubitmap(grs_bitmap *bm, short x, short y, short w, short h, uchar *cl); + +extern void flat8_rsd8_clut_scale_ubitmap(grs_bitmap *bm, short x, short y, short w, short h, uchar *cl); +extern int flat8_rsd8_clut_scale_bitmap(grs_bitmap *bm, short x, short y, short w, short h, uchar *cl); + +extern void flat8_rsd8_scale_solid_ubitmap(grs_bitmap *bm, short x, short y, short w, short h, int c); +extern int flat8_rsd8_scale_solid_bitmap(grs_bitmap *bm, short x, short y, short w, short h, int c); + +extern void flat8_tluc8_scale_ubitmap(grs_bitmap *bm, short x, short y, short w, short h); +extern int flat8_tluc8_scale_bitmap(grs_bitmap *bm, short x, short y, short w, short h); + +extern void flat8_tluc8_clut_scale_ubitmap(grs_bitmap *bm, short x, short y, short w, short h, uchar *cl); +extern int flat8_tluc8_clut_scale_bitmap(grs_bitmap *bm, short x, short y, short w, short h, uchar *cl); + +// internal scaler/mapper prototypes +int gri_opaque_solid_scale_umap_init(grs_tmap_loop_info *info, grs_vertex **vert); +int gri_trans_scale_umap_init(grs_tmap_loop_info *, grs_vertex **); +int gri_trans_solid_scale_umap_init(grs_tmap_loop_info *info, grs_vertex **vert); +int gri_opaque_scale_umap_init(grs_tmap_loop_info *tli); +int gri_trans_clut_scale_umap_init(grs_tmap_loop_info *tli); +int gri_opaque_clut_scale_umap_init(grs_tmap_loop_info *tli); + +/* bitmap drawing functions. */ +extern void flat8_mono_ubitmap(grs_bitmap *bm, short x, short y); +extern void flat8_flat8_ubitmap(grs_bitmap *bm, short x, short y); +extern void flat8_rsd8_ubitmap(grs_bitmap *bm, short x, short y); +extern void flat8_tluc8_ubitmap(grs_bitmap *bm, short x, short y); +extern int flat8_rsd8_bitmap(grs_bitmap *bm, short x, short y); + +extern void flat8_flat8_clut_ubitmap(grs_bitmap *bm, short x, short y, uchar *cl); + +extern void flat8_rsd8_solid_ubitmap(grs_bitmap *bm, short x, short y, int c); +extern int flat8_rsd8_solid_bitmap(grs_bitmap *bm, short x, short y, int c); + +/* bitmap get routines. */ +extern void flat8_get_flat8_ubitmap(grs_bitmap *bm, short x, short y); + +/* bitmap horizontal flip routines. */ +extern void flat8_hflip_flat8_ubitmap(grs_bitmap *bm, short x, short y); + +/* bitmap color lookup table horizontal flip routines. */ +extern void flat8_clut_hflip_flat8_ubitmap(grs_bitmap *bm, short x, short y, uchar *cl); + +/* device-specific routines. */ +extern short flat8_calc_row(short w); +extern grs_bitmap *flat8_sub_bitmap(grs_bitmap *bm, short x, short y, short w, short h); + +/* bitmap doubling routines. */ +extern void flat8_flat8_h_double_ubitmap(grs_bitmap *bm); +extern void flat8_flat8_smooth_h_double_ubitmap(grs_bitmap *srcb, grs_bitmap *dst); +extern void flat8_flat8_smooth_hv_double_ubitmap(grs_bitmap *src, grs_bitmap *dst); + +extern void flat8_flat8_v_double_ubitmap(grs_bitmap *bm); +extern void flat8_flat8_hv_double_ubitmap(grs_bitmap *bm); +extern void flat8_flat8_smooth_v_double_ubitmap(grs_bitmap *bm); +#endif /* !__FLAT8_H */ diff --git a/engine/src/Libraries/2D/Source/GR/grbm.h b/engine/src/Libraries/2D/Source/GR/grbm.h new file mode 100644 index 0000000..a28bee2 --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grbm.h @@ -0,0 +1,53 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/grbm.h $ + * $Revision: 1.4 $ + * $Author: kaboom $ + * $Date: 1993/10/19 10:15:22 $ + * + * Dispatch macros for utility functions. + * + * This file is part of the 2d library. + * + * $Log: grbm.h $ + * Revision 1.4 1993/10/19 10:15:22 kaboom + * Now includes tabdat.h. + * + * Revision 1.3 1993/10/08 01:15:55 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.2 1993/06/03 15:10:15 kaboom + * Now uses the grd_pixel_table to call bitmap utility functions. + * + * Revision 1.1 1993/04/29 18:35:01 kaboom + * Initial revision + */ + +#ifndef __GRBM_H +#define __GRBM_H +#include "icanvas.h" +#include "tabdat.h" + +#define gr_calc_row(w) \ + ((short (*)(short _w)) grd_pixel_table[CALC_ROW])(w) +#define gr_sub_bitmap(bm, x, y, w, h) \ + ((grs_bitmap *(*)(grs_bitmap *_bm,short _x,short _y,short _w,short _h)) \ + grd_pixel_table[SUB_BITMAP])(bm, x, y, w, h) +#endif /* !__GRBM_H */ diff --git a/engine/src/Libraries/2D/Source/GR/grcbm.h b/engine/src/Libraries/2D/Source/GR/grcbm.h new file mode 100644 index 0000000..2b1d30e --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grcbm.h @@ -0,0 +1,92 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/grcbm.h $ + * $Revision: 1.1 $ + * $Author: kevin $ + * $Date: 1994/03/15 13:09:57 $ + * + * Dispatch macros for bitmap draw functions. + * + * This file is part of the 2d library. + * + * $Log: grcbm.h $ + * Revision 1.1 1994/03/15 13:09:57 kevin + * Initial revision + * + * Revision 1.5 1993/12/01 21:27:36 baf + * Added macros for translucent/8 bitmaps. + * + * Revision 1.4 1993/10/19 10:15:22 kaboom + * Now includes tabdat.h. + * + * Revision 1.3 1993/10/08 01:15:59 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.2 1993/09/02 20:10:45 kaboom + * Added macros for flat 24 bitmap routines. + * + * Revision 1.1 1993/04/29 18:35:50 kaboom + * Initial revision + */ + +#ifndef __GRCBM_H +#define __GRCBM_H +#include "icanvas.h" +#include "tabdat.h" + +/* bitmap draw routines. */ +#define gr_clut_ubitmap(bm,x,y,cl) \ + ((void (*)(grs_bitmap *_bm,short _x,short _y, uchar *_cl)) \ + grd_canvas_table[CLUT_DRAW_DEVICE_UBITMAP+2*((bm)->type)])(bm,x,y,cl) +#define gr_clut_bitmap(bm,x,y,cl) \ + ((int (*)(grs_bitmap *_bm,short _x,short _y, uchar *_cl)) \ + grd_canvas_table[CLUT_DRAW_DEVICE_BITMAP+2*((bm)->type)])(bm,x,y,cl) + +#define gr_mono_clut_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_DRAW_MONO_UBITMAP]) +#define gr_mono_clut_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_DRAW_MONO_BITMAP]) +#define gr_flat8_clut_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_DRAW_FLAT8_UBITMAP]) +#define gr_flat8_clut_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_DRAW_FLAT8_BITMAP]) +#define gr_flat24_clut_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_DRAW_FLAT24_UBITMAP]) +#define gr_flat24_clut_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_DRAW_FLAT24_BITMAP]) +#define gr_rsd8_clut_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_DRAW_RSD8_UBITMAP]) +#define gr_rsd8_clut_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_DRAW_RSD8_BITMAP]) +#define gr_tluc8_clut_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_DRAW_TLUC8_UBITMAP]) +#define gr_tluc8_clut_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_DRAW_TLUC8_BITMAP]) +#endif /* !__GRDBM_H */ diff --git a/engine/src/Libraries/2D/Source/GR/grclhbm.h b/engine/src/Libraries/2D/Source/GR/grclhbm.h new file mode 100644 index 0000000..f04bffe --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grclhbm.h @@ -0,0 +1,54 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/grclhbm.h $ + * $Revision: 1.2 $ + * $Author: kaboom $ + * $Date: 1993/10/08 01:15:56 $ + * Dispatch macros for clut horizontal bitmap flip routines. + * + * This file is part of the 2d library. + * + * $Log: grclhbm.h $ + * Revision 1.2 1993/10/08 01:15:56 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.1 1993/10/06 13:50:54 kevin + * Initial revision + * + */ + +#ifndef __GRCLHBM_H +#define __GRCLHBM_H +#include "icanvas.h" + +#define gr_clut_hflip_ubitmap(bm,x,y,cl) \ + ((void (*)(grs_bitmap *_bm,short _x,short _y,uchar *_cl)) \ + grd_canvas_table[CLUT_HFLIP_DEVICE_UBITMAP+2*((bm)->type)])(bm,x,y,cl) +#define gr_clut_hflip_bitmap(bm,x,y,cl) \ + ((void (*)(grs_bitmap *_bm,short _x,short _y,uchar *_cl)) \ + grd_canvas_table[CLUT_HFLIP_DEVICE_BITMAP+2*((bm)->type)])(bm,x,y,cl) + +#define gr_clut_hflip_flat8_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_HFLIP_FLAT8_UBITMAP]) +#define gr_clut_hflip_flat8_bitmap \ + ((void (*)(grs_bitmap *bm,short x,short y,uchar *cl)) \ + grd_canvas_table[CLUT_HFLIP_FLAT8_BITMAP]) +#endif /* !__GRCLHBM_H */ diff --git a/engine/src/Libraries/2D/Source/GR/grcply.h b/engine/src/Libraries/2D/Source/GR/grcply.h new file mode 100644 index 0000000..d9e4c5e --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grcply.h @@ -0,0 +1,55 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/grcply.h $ + * $Revision: 1.4 $ + * $Author: baf $ + * $Date: 1994/02/14 20:38:39 $ + * + * Dispatch macros for color shaded polygon routines. + * + * This file is part of the 2d library. + * + * $Log: grcply.h $ + * Revision 1.4 1994/02/14 20:38:39 baf + * Added dummy parameter to cpoly and spoly routines, for uniformity needed by 3D. + * + * Revision 1.3 1993/11/29 20:27:39 baf + * General revisions and tidying/up of translucency + * and shading routines + * + * Revision 1.2 1993/11/23 17:52:45 baf + * Added tluc/8 interpolation + * + * Revision 1.1 1993/10/19 10:16:55 kaboom + * Initial revision + * + */ + +#ifndef __GRCPLY_H +#define __GRCPLY_H +#include "icanvas.h" +#include "plytyp.h" +#include "tabdat.h" + +#define gr_ucpoly \ + ((void (*)(long c, int n,grs_vertex **vpl))grd_canvas_table[FIX_UCPOLY]) +#define gr_cpoly \ + ((int (*)(long c, int n,grs_vertex **vpl))grd_canvas_table[FIX_CPOLY]) +#endif /* !__GRCPLY_H */ diff --git a/engine/src/Libraries/2D/Source/GR/grd.c b/engine/src/Libraries/2D/Source/GR/grd.c new file mode 100644 index 0000000..b03e4e7 --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grd.c @@ -0,0 +1,83 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/grd.c $ + * $Revision: 1.10 $ + * $Author: kevin $ + * $Date: 1994/12/05 21:07:24 $ + * + * Global stuff. + * + * This file is part of the 2d library. + */ + +#include "grs.h" + +/* pointer to the currently set screen. */ +grs_screen *grd_screen=NULL; + +/* pointer to palette */ +uchar grd_default_pal[768]; +uchar *grd_pal=grd_default_pal; + +/* pointer to blend palette */ +grs_rgb grd_default_bpal[1024]; +grs_rgb *grd_bpal=grd_default_bpal; + +/* pointer to inverse palette */ +uchar *grd_ipal=NULL; + +/* pointer to a canvas for the current virtual screen. */ +grs_canvas *grd_screen_canvas; + +/* pointer to a canvas for the visible sub-region of the virtual screen. */ +grs_canvas *grd_visible_canvas; + +/* pointer to currently set canvas. */ +grs_canvas *grd_canvas; + +/* info for current graphics setup. */ +grs_sys_info grd_info; + +grs_drvcap grd_mode_cap; + +/* capability info for currently set driver. */ +grs_drvcap *grd_cap = &grd_mode_cap; + +/* pointer to start of current device driver's function table. */ +void (**grd_device_table)(); + +void (**grd_pixel_table)(); + +/* pointer to start of current bitmap driver's function table for clipped + primitives. */ +void (**grd_canvas_table)(); + +/* currently active graphics mode. -1 means unrecognized mode */ +int grd_mode=-1; + +/* flag for whether we are executing in an interrupt. */ +uchar grd_interrupt=0; + +/* Function chaining globals. Set during gr_set_canvas; that's why I moved them here. */ +short grd_pixel_index, grd_canvas_index; +uchar chn_flags; + +/* Graphics capability detection function pointer. */ +int (*grd_detect_func)(grs_sys_info *info); diff --git a/engine/src/Libraries/2D/Source/GR/grd.h b/engine/src/Libraries/2D/Source/GR/grd.h new file mode 100644 index 0000000..e93903b --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grd.h @@ -0,0 +1,78 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/grd.h $ + * $Revision: 1.7 $ + * $Author: kaboom $ + * $Date: 1993/10/19 10:20:07 $ + * + * Declarations for globals. + * + * This file is part of the 2d library. + * + * $Log: grd.h $ + * Revision 1.7 1993/10/19 10:20:07 kaboom + * Moved declarations for canvas-related globals to other files. + * + * Revision 1.6 1993/10/08 01:15:58 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.5 1993/10/06 16:13:18 baf + * Added span_texture global + * + * Revision 1.4 1993/09/07 02:21:57 kaboom + * New declaration for grd_mode. + * + * Revision 1.3 1993/05/03 16:47:18 kaboom + * Added declaration for grd_pixel_table. + * + * Revision 1.2 1993/04/29 18:35:34 kaboom + * Moved some of the globals to other files. + * + * Revision 1.1 1993/02/04 17:34:19 kaboom + * Initial revision + */ + +#ifndef __GRD_H +#define __GRD_H +#include "grs.h" + +extern grs_sys_info grd_info; +extern grs_drvcap *grd_cap; +extern grs_drvcap grd_mode_cap; +extern void (**grd_driver_list[])(); +extern int grd_mode; + +/* support old-syle dr_ naming for now. */ +#define grd_scr_canv grd_screen_canvas +#define grd_vis_canv grd_visible_canvas +#define dr_screen grd_screen +#define dr_canvas grd_canvas +#define dr_scr_canv grd_screen_canvas +#define dr_vis_canv grd_visible_canvas + +#define dr_bm grd_bm +#define dr_gc grd_gc +#define dr_ytab grd_ytab +#define dr_int_clip grd_int_clip +#define dr_fix_clip grd_fix_clip +#define dr_clip grd_clip +#define driver_func grd_driver_func + +#endif /* !__GRD_H */ diff --git a/engine/src/Libraries/2D/Source/GR/grdbm.h b/engine/src/Libraries/2D/Source/GR/grdbm.h new file mode 100644 index 0000000..2c8bd8a --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grdbm.h @@ -0,0 +1,89 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/grdbm.h $ + * $Revision: 1.5 $ + * $Author: baf $ + * $Date: 1993/12/01 21:27:36 $ + * + * Dispatch macros for bitmap draw functions. + * + * This file is part of the 2d library. + * + * $Log: grdbm.h $ + * Revision 1.5 1993/12/01 21:27:36 baf + * Added macros for translucent/8 bitmaps. + * + * Revision 1.4 1993/10/19 10:15:22 kaboom + * Now includes tabdat.h. + * + * Revision 1.3 1993/10/08 01:15:59 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.2 1993/09/02 20:10:45 kaboom + * Added macros for flat 24 bitmap routines. + * + * Revision 1.1 1993/04/29 18:35:50 kaboom + * Initial revision + */ + +#ifndef __GRDBM_H +#define __GRDBM_H +#include "icanvas.h" +#include "tabdat.h" + +/* bitmap draw routines. */ +#define gr_ubitmap(bm,x,y) \ + ((void (*)(grs_bitmap *_bm,short _x,short _y)) \ + grd_canvas_table[DRAW_DEVICE_UBITMAP+2*((bm)->type)])(bm,x,y) +#define gr_bitmap(bm,x,y) \ + ((int (*)(grs_bitmap *_bm,short _x,short _y)) \ + grd_canvas_table[DRAW_DEVICE_BITMAP+2*((bm)->type)])(bm,x,y) + +#define gr_mono_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[DRAW_MONO_UBITMAP]) +#define gr_mono_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[DRAW_MONO_BITMAP]) +#define gr_flat8_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[DRAW_FLAT8_UBITMAP]) +#define gr_flat8_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[DRAW_FLAT8_BITMAP]) +#define gr_flat24_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[DRAW_FLAT24_UBITMAP]) +#define gr_flat24_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[DRAW_FLAT24_BITMAP]) +#define gr_rsd8_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[DRAW_RSD8_UBITMAP]) +#define gr_rsd8_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[DRAW_RSD8_BITMAP]) +#define gr_tluc8_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[DRAW_TLUC8_UBITMAP]) +#define gr_tluc8_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[DRAW_TLUC8_BITMAP]) +#endif /* !__GRDBM_H */ diff --git a/engine/src/Libraries/2D/Source/GR/grdev.h b/engine/src/Libraries/2D/Source/GR/grdev.h new file mode 100644 index 0000000..46037d8 --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grdev.h @@ -0,0 +1,86 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/grdev.h $ + * $Revision: 1.5 $ + * $Author: kaboom $ + * $Date: 1993/10/19 10:15:44 $ + * + * Macros for table driven device driver functions. + * + * This file is part of the 2d library. + * + * $Log: grdev.h $ + * Revision 1.5 1993/10/19 10:15:44 kaboom + * Now includes tabdat.h. Also put null-check for init and close device + * in here instead of requiring caller to do it. + * + * Revision 1.4 1993/10/08 01:16:00 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.3 1993/05/16 00:35:28 kaboom + * Added arguments to macros for gr_set_state() and gr_get_state(). + * + * Revision 1.2 1993/05/04 15:41:30 kaboom + * Changed names of hblank and vblank to htrace and vtrace. + * + * Revision 1.1 1993/04/29 18:36:04 kaboom + * Initial revision + */ + +#ifndef __GRDEV_H +#define __GRDEV_H +#include "grs.h" +#include "idevice.h" +#include "tabdat.h" + +/* here are the definitions for all the table driven function. */ +#define gr_init_device(info) \ + (grd_device_table[GRT_INIT_DEVICE] ?\ + ((int (*)(grs_sys_info *_info))grd_device_table[GRT_INIT_DEVICE])(info) :\ + 0) +#define gr_close_device(info) \ + (grd_device_table[GRT_CLOSE_DEVICE] ?\ + ((int (*)(grs_sys_info *_info))grd_device_table[GRT_CLOSE_DEVICE])(info) :\ + 0) +#define gr_set_screen_mode \ + ((int (*)(int mode,int clear))grd_device_table[GRT_SET_MODE]) +#define gr_get_screen_mode \ + ((int (*)(void))grd_device_table[GRT_GET_MODE]) +#define gr_set_state \ + ((int (*)(void *buf,int clear))grd_device_table[GRT_SET_STATE]) +#define gr_get_state \ + ((int (*)(void *buf,int flags))grd_device_table[GRT_GET_STATE]) +#define gr_stat_htrace \ + ((int (*)(void))grd_device_table[GRT_STAT_HTRACE]) +#define gr_stat_vtrace \ + ((int (*)(void))grd_device_table[GRT_STAT_VTRACE]) +#define gr_set_screen_pal \ + ((void (*)(int start,int n,uchar *pal_data))grd_device_table[GRT_SET_PAL]) +#define gr_get_screen_pal \ + ((void (*)(int start,int n,uchar *pal_data))grd_device_table[GRT_GET_PAL]) +#define gr_set_width \ + ((void (*)(short w))grd_device_table[GRT_SET_WIDTH]) +#define gr_get_width \ + ((short (*)(void))grd_device_table[GRT_GET_WIDTH]) +#define gr_set_focus \ + ((void (*)(short x,short y))grd_device_table[GRT_SET_FOCUS]) +#define gr_get_focus \ + ((void (*)())grd_device_table[GRT_GET_FOCUS]) +#endif /* !__GRDEV_H */ diff --git a/engine/src/Libraries/2D/Source/GR/grgbm.h b/engine/src/Libraries/2D/Source/GR/grgbm.h new file mode 100644 index 0000000..a0018fc --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grgbm.h @@ -0,0 +1,68 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/grgbm.h $ + * $Revision: 1.2 $ + * $Author: kaboom $ + * $Date: 1993/10/08 01:16:01 $ + * + * Dispatch macros for get bitmap functions. + * + * This file is part of the 2d library. + * + * $Log: grgbm.h $ + * Revision 1.2 1993/10/08 01:16:01 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.1 1993/04/29 18:36:18 kaboom + * Initial revision + * + */ + +#ifndef __GRGBM_H +#define __GRGBM_H +#include "icanvas.h" + +/* bitmap get routines. */ +#define gr_get_ubitmap(bm,x,y) \ + ((void (*)(grs_bitmap *_bm,short _x,short _y)) \ + grd_canvas_table[GET_DEVICE_UBITMAP+2*((bm)->type)])(bm,x,y) +#define gr_get_bitmap(bm,x,y) \ + ((int (*)(grs_bitmap *_bm,short _x,short _y)) \ + grd_canvas_table[GET_DEVICE_BITMAP+2*((bm)->type)])(bm,x,y) + +#define gr_get_mono_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[GET_MONO_UBITMAP]) +#define gr_get_mono_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[GET_MONO_BITMAP]) +#define gr_get_flat8_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[GET_FLAT8_UBITMAP]) +#define gr_get_flat8_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[GET_FLAT8_BITMAP]) +#define gr_get_rsd8_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[GET_RSD8_UBITMAP] +#define gr_get_rsd8_bitmap \ + ((int (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[GET_RSD8_BITMAP]) +#endif /* !__GRGBM_H */ diff --git a/engine/src/Libraries/2D/Source/GR/grhbm.h b/engine/src/Libraries/2D/Source/GR/grhbm.h new file mode 100644 index 0000000..4317d03 --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grhbm.h @@ -0,0 +1,61 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/grhbm.h $ + * $Revision: 1.3 $ + * $Author: kevin $ + * $Date: 1994/11/11 16:48:10 $ + * + * Dispatch macros for horizontal bitmap flip routines. + * + * This file is part of the 2d library. + * + * $Log: grhbm.h $ + * Revision 1.3 1994/11/11 16:48:10 kevin + * added hflip_in_place primitive. + * + * Revision 1.2 1993/10/08 01:16:02 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.1 1993/06/06 15:13:10 kaboom + * Initial revision + * + */ + +#ifndef __GRHBM_H +#define __GRHBM_H +#include "grs.h" +#include "icanvas.h" + +extern void gr_hflip_in_place(grs_bitmap *bm); + +#define gr_hflip_ubitmap(bm,x,y) \ + ((void (*)(grs_bitmap *_bm,short _x,short _y)) \ + grd_canvas_table[HFLIP_DEVICE_UBITMAP+2*((bm)->type)])(bm,x,y) +#define gr_hflip_bitmap(bm,x,y) \ + ((void (*)(grs_bitmap *_bm,short _x,short _y)) \ + grd_canvas_table[HFLIP_DEVICE_BITMAP+2*((bm)->type)])(bm,x,y) + +#define gr_hflip_flat8_ubitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[HFLIP_FLAT8_UBITMAP]) +#define gr_hflip_flat8_bitmap \ + ((void (*)(grs_bitmap *bm,short x,short y)) \ + grd_canvas_table[HFLIP_FLAT8_BITMAP]) +#endif /* !__GRHBM_H */ diff --git a/engine/src/Libraries/2D/Source/GR/grilin.c b/engine/src/Libraries/2D/Source/GR/grilin.c new file mode 100644 index 0000000..4d78b66 --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grilin.c @@ -0,0 +1,86 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/grilin.c $ + * $Revision: 1.4 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 02:29:21 $ + * + * Integral line drawers. Converts integers to fixed-point and calls + * normal line drawers. + * + * This file is part of the 2d library. + * + * $Log: grilin.c $ + * Revision 1.4 1994/06/11 02:29:21 lmfeeney + * provides both interfaces to unclipped line drawer, unclipped + * drawer moved + * + * Revision 1.3 1993/10/08 01:16:03 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.2 1993/04/29 18:49:38 kaboom + * Changed include of old gr.h to new grrend.h. + * + * Revision 1.1 1993/03/29 18:44:53 kaboom + * Initial revision + */ + +#include "fix.h" +#include "clpcon.h" +#include "plytyp.h" +#include "lintyp.h" +#include "clpltyp.h" +#include "clpltab.h" +#include "ctxmac.h" +#include "grlin.h" + +// prototypes +int gr_int_line (short x0, short y0, short x1, short y1); + + +int gr_int_line (short x0, short y0, short x1, short y1) +{ + int r; + grs_vertex v0, v1; + + v0.x = x0; v0.y = y0; /* we don't need no stinking type checking */ + v1.x = x1; v1.y = y1; + + r = grd_iline_clip_fill (gr_get_fcolor(), gr_get_fill_parm(), &v0, &v1); + + return r; +} + +int gri_iline_clip_fill (long c, long parm, grs_vertex *v0, grs_vertex *v1) +{ + int r; + grs_vertex u0, u1; + + u0.x = fix_make(v0->x, 32768); u0.y = fix_make(v0->y, 32768); + u1.x = fix_make(v1->x, 32768); u1.y = fix_make(v1->y, 32768); + + r = gri_line_clip (&u0, &u1); + + if (r != CLIP_ALL) + grd_uline_fill (c, parm, &u0, &u1); + + return r; +} + diff --git a/engine/src/Libraries/2D/Source/GR/grlin.h b/engine/src/Libraries/2D/Source/GR/grlin.h new file mode 100644 index 0000000..55fd051 --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grlin.h @@ -0,0 +1,56 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/grlin.h $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/08/04 09:46:07 $ + */ + +#ifndef __GRLIN_H +#define __GRLIN_H + +#include "plytyp.h" +#include "line.h" +#include "lintyp.h" +#include "clpltyp.h" + +#define grd_uline_fill ((grt_uline_fill_v) (grd_uline_fill_vector[GR_LINE])) +#define grd_uiline_fill ((grt_uline_fill_v) (grd_uline_fill_vector[GR_ILINE])) +#define grd_uhline_fill ((grt_uline_fill_xy) (grd_uline_fill_vector[GR_HLINE])) +#define grd_uvline_fill ((grt_uline_fill_xy) (grd_uline_fill_vector[GR_VLINE])) +#define grd_usline_fill ((grt_uline_fill_v) (grd_uline_fill_vector[GR_SLINE])) +#define grd_ucline_fill ((grt_uline_fill_v) (grd_uline_fill_vector[GR_CLINE])) +#define grd_wire_poly_uline_fill ((grt_wire_poly_uline) (grd_uline_fill_vector[GR_WIRE_POLY_LINE])) +#define grd_wire_poly_usline_fill ((grt_wire_poly_usline) (grd_uline_fill_vector[GR_WIRE_POLY_SLINE])) +#define grd_wire_poly_ucline_fill ((grt_wire_poly_ucline) (grd_uline_fill_vector[GR_WIRE_POLY_CLINE])) + +/* these should become table definitions */ + +#define grd_line_clip_fill ((grt_line_clip_fill_v) (grd_line_clip_fill_vector[GR_LINE])) +#define grd_iline_clip_fill ((grt_line_clip_fill_v) (grd_line_clip_fill_vector[GR_ILINE])) +#define grd_hline_clip_fill ((grt_line_clip_fill_xy) (grd_line_clip_fill_vector[GR_HLINE])) +#define grd_vline_clip_fill ((grt_line_clip_fill_xy) (grd_line_clip_fill_vector[GR_VLINE])) +#define grd_sline_clip_fill ((grt_line_clip_fill_v) (grd_line_clip_fill_vector[GR_SLINE])) +#define grd_cline_clip_fill ((grt_line_clip_fill_v) (grd_line_clip_fill_vector[GR_CLINE])) +#define grd_wire_poly_line_clip_fill ((grt_wire_poly_uline) (grd_line_clip_fill_vector[GR_WIRE_POLY_LINE])) +#define grd_wire_poly_sline_clip_fill ((grt_wire_poly_usline) (grd_line_clip_fill_vector[GR_WIRE_POLY_SLINE])) +#define grd_wire_poly_cline_clip_fill ((grt_wire_poly_ucline) (grd_line_clip_fill_vector[GR_WIRE_POLY_CLINE])) + +#endif diff --git a/engine/src/Libraries/2D/Source/GR/grmalloc.c b/engine/src/Libraries/2D/Source/GR/grmalloc.c new file mode 100644 index 0000000..366dc86 --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grmalloc.c @@ -0,0 +1,52 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/grmalloc.c $ + * $Revision: 1.1 $ + * $Author: kaboom $ + * $Date: 1993/02/04 17:35:34 $ + * + * 2d internal memory allocation routines. + * + * This file is part of the 2d library. + */ + +#include + +/* dynamic memory allocation/deallocation is done through the indirected + functions gr_malloc() and gr_free(). they default to malloc() and + free(). library clients can change the default with gr_set_malloc() + and gr_set_free(). */ +typedef void *(*ptr_type)(int); +typedef void (*free_type)(void *); + +void *(*gr_malloc)(int n) = (ptr_type) malloc; +void (*gr_free)(void *m) = (free_type)free; + +/* set 2d's internal function pointer to a malloc routine. */ +void gr_set_malloc (void *(*malloc_func)(int bytes)) +{ + gr_malloc = malloc_func; +} + +/* set 2d's internal function pointer to a free routine. */ +void gr_set_free (void (*free_func)(void *mem)) +{ + gr_free = free_func; +} diff --git a/engine/src/Libraries/2D/Source/GR/grmalloc.h b/engine/src/Libraries/2D/Source/GR/grmalloc.h new file mode 100644 index 0000000..e843512 --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grmalloc.h @@ -0,0 +1,38 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/grmalloc.h $ + * $Revision: 1.1 $ + * $Author: kaboom $ + * $Date: 1993/02/04 17:35:18 $ + * + * Declarations for 2d internal memory allocation routines. + * + * This file is part of the 2d library. + */ + +#ifndef __GRMALLOC_H +#define __GRMALLOC_H + +extern void gr_set_malloc (void *(*malloc_func)(int bytes)); +extern void gr_set_free (void (*free_func)(void *mem)); +extern void *(*gr_malloc)(int n); +extern void (*gr_free)(void *p); + +#endif /* !__GRMALLOC_H */ diff --git a/engine/src/Libraries/2D/Source/GR/grnull.c b/engine/src/Libraries/2D/Source/GR/grnull.c new file mode 100644 index 0000000..7fbfbac --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grnull.c @@ -0,0 +1,41 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/grnull.c $ + * $Revision: 1.2 $ + * $Author: kaboom $ + * $Date: 1993/08/20 15:05:22 $ + * + * Null-function placeholder. + * + * This file is part of the 2d library. + * + * $Log: grnull.c $ + * Revision 1.2 1993/08/20 15:05:22 kaboom + * Took out mprintf. + * + * Revision 1.1 1993/02/04 17:36:07 kaboom + * Initial revision + */ + +#include + +void gr_null (void) {} +void gr_not_imp (void) {DEBUG("%s: Graphics function not implemented", __FUNCTION__);} +void gr_not_imp_test (void) {} diff --git a/engine/src/Libraries/2D/Source/GR/grnull.h b/engine/src/Libraries/2D/Source/GR/grnull.h new file mode 100644 index 0000000..a032e98 --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grnull.h @@ -0,0 +1,42 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/grnull.h $ + * $Revision: 1.1 $ + * $Author: kaboom $ + * $Date: 1993/02/04 17:36:36 $ + * + * Null-function placeholder prototype. + * + * This file is part of the 2d library. + * + * $Log: grnull.h $ + * Revision 1.1 1993/02/04 17:36:36 kaboom + * Initial revision + * + */ + +#ifndef __GRNULL_H +#define __GRNULL_H + +extern void gr_null (void); +extern void gr_not_imp (void); +extern void gr_not_imp_test (void); + +#endif /* !__GRNULL_H */ diff --git a/engine/src/Libraries/2D/Source/GR/grp24.h b/engine/src/Libraries/2D/Source/GR/grp24.h new file mode 100644 index 0000000..6a00766 --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grp24.h @@ -0,0 +1,54 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/grp24.h $ + * $Revision: 1.3 $ + * $Author: kaboom $ + * $Date: 1993/10/19 10:18:46 $ + * + * Dispatch macros for 24-bit pixel functions. + * + * This file is part of the 2d library. + * + * $Log: grp24.h $ + * Revision 1.3 1993/10/19 10:18:46 kaboom + * Now includes tabdat.h. + * + * Revision 1.2 1993/10/08 01:16:05 kaboom + * Changed quotes in #include lines to angle brackets for Watcom. + * + * Revision 1.1 1993/09/02 20:11:35 kaboom + * Initial revision + */ + +#ifndef __GRP24_H +#define __GRP24_H +#include "icanvas.h" +#include "tabdat.h" + +#define gr_set_upixel24 \ + ((void (*)(long color,short x,short y))grd_pixel_table[SET_UPIXEL24]) +#define gr_set_pixel24 \ + ((int (*)(long color,short x,short y))grd_pixel_table[SET_PIXEL24]) +#define gr_get_upixel24 \ + ((long (*)(short x,short y))grd_pixel_table[GET_UPIXEL24]) +#define gr_get_pixel24 \ + ((long (*)(short x,short y))grd_pixel_table[GET_PIXEL24]) +#endif /* !__GRP24_H */ + diff --git a/engine/src/Libraries/2D/Source/GR/grpix.h b/engine/src/Libraries/2D/Source/GR/grpix.h new file mode 100644 index 0000000..c1234f5 --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grpix.h @@ -0,0 +1,78 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/grpix.h $ + * $Revision: 1.7 $ + * $Author: kevin $ + * $Date: 1994/11/12 02:22:57 $ + * + * Dispatch macros for pixel functions. + * + * This file is part of the 2d library. + * + * $Log: grpix.h $ + * Revision 1.7 1994/11/12 02:22:57 kevin + * added gr_set_pixel_interrupt() #define. + * + * Revision 1.6 1994/08/16 15:34:57 kevin + * Added gr_fill_upixel declaration. + * + * Revision 1.5 1993/10/19 10:18:47 kaboom + * Now includes tabdat.h. + * + * Revision 1.4 1993/10/08 01:16:06 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.3 1993/09/02 20:11:19 kaboom + * Updated index names to XXX_PIXEL8. + * + * Revision 1.2 1993/05/03 16:50:09 kaboom + * Changed pixel macros to use grd_pixel_table instead of grd_canvas_table. + * + * Revision 1.1 1993/04/29 18:36:36 kaboom + * Initial revision + */ + +#ifndef __GRPIX_H +#define __GRPIX_H +#include "icanvas.h" +#include "ifcn.h" +#include "tabdat.h" + +#define gr_set_upixel \ + ((void (*)(long color, short x, short y))grd_pixel_table[SET_UPIXEL8]) +#define gr_set_pixel \ + ((int (*)(long color, short x, short y))grd_pixel_table[SET_PIXEL8]) + +#define gr_set_upixel_interrupt \ + ((void (*)(long color, short x, short y))grd_pixel_table[SET_UPIXEL8_INTERRUPT]) +#define gr_set_pixel_interrupt \ + ((int (*)(long color, short x, short y))grd_pixel_table[SET_PIXEL8_INTERRUPT]) + +extern int gen_fill_pixel(long color, short x, short y); + +#define gr_fill_upixel \ + ((void (*)(long color, short x, short y))grd_function_table[GRC_PIXEL]) +#define gr_fill_pixel gen_fill_pixel + +#define gr_get_upixel \ + ((long (*)(short x, short y))grd_pixel_table[GET_UPIXEL8]) +#define gr_get_pixel \ + ((long (*)(short x, short y))grd_pixel_table[GET_PIXEL8]) +#endif /* !__GRPIX_H */ diff --git a/engine/src/Libraries/2D/Source/GR/grply.h b/engine/src/Libraries/2D/Source/GR/grply.h new file mode 100644 index 0000000..7029650 --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grply.h @@ -0,0 +1,56 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/grply.h $ + * $Revision: 1.2 $ + * $Author: baf $ + * $Date: 1993/11/23 17:52:47 $ + * + * Dispatch macros for solid polygon routines. + * + * This file is part of the 2d library. + * + * $Log: grply.h $ + * Revision 1.2 1993/11/23 17:52:47 baf + * Added tluc/8 interpolation + * + * Revision 1.1 1993/10/19 10:20:53 kaboom + * Initial revision + * + */ + +#ifndef __GRPLY_H +#define __GRPLY_H +#include "icanvas.h" +#include "plytyp.h" +#include "tabdat.h" + +#define gr_upoly \ + ((void (*)(long c,int n,grs_vertex **vpl)) \ + grd_canvas_table[FIX_UPOLY]) +#define gr_poly \ + ((int (*)(long c,int n,grs_vertex **vpl)) \ + grd_canvas_table[FIX_POLY]) +#define gr_tluc8_upoly \ + ((void (*)(long c,int n,grs_vertex **vpl)) \ + grd_canvas_table[FIX_TLUC8_UPOLY]) +#define gr_tluc8_poly \ + ((int (*)(long c,int n,grs_vertex **vpl)) \ + grd_canvas_table[FIX_TLUC8_POLY]) +#endif /* !__GRPLY_H */ diff --git a/engine/src/Libraries/2D/Source/GR/grrect.h b/engine/src/Libraries/2D/Source/GR/grrect.h new file mode 100644 index 0000000..ede2fd8 --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grrect.h @@ -0,0 +1,91 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/grrect.h $ + * $Revision: 1.4 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 00:36:07 $ + * + * Dispatch macros for rectangular/straight functions. + * + * This file is part of the 2d library. + * + * $Log: grrect.h $ + * Revision 1.4 1994/06/11 00:36:07 lmfeeney + * lines removed from canvas table, #defines for backward compatibility + * + * Revision 1.3 1993/10/19 10:21:35 kaboom + * Now includes tabdat.h. + * + * Revision 1.2 1993/10/08 01:16:07 kaboom + * Changed quotes in #include lines to angle brackets for Watcom. + * + * Revision 1.1 1993/04/29 18:36:43 kaboom + * Initial revision + */ + +#ifndef __GRRECT_H +#define __GRRECT_H +#include "icanvas.h" +#include "tabdat.h" +#include "ctxmac.h" +#include "grlin.h" + +#define gr_clear \ + ((void (*)(long color))grd_canvas_table[DRAW_CLEAR]) +#define gr_upoint \ + ((void (*)(short x,short y))grd_canvas_table[DRAW_UPOINT]) +#define gr_point \ + ((int (*)(short x,short y))grd_canvas_table[DRAW_POINT]) + + +/* The line routines have been removed from the canvas tables */ + +/* horizontal lines */ + +#define gr_uhline(x0,y0,x1) \ +do {\ + grd_uhline_fill ((x0), (y0), (x1), gr_get_fcolor(), gr_get_fill_parm()); \ +} while (0) + +extern int gen_hline (short x0, short y0, short x1); + +#define gr_hline gen_hline + +/* vertical lines */ + +#define gr_uvline(x0,y0,y1) \ +do {\ + grd_uvline_fill ((x0), (y0), (y1), gr_get_fcolor(), gr_get_fill_parm()); \ +} while (0) + +extern int gen_vline (short x0, short y0, short y1); + +#define gr_vline gen_vline + + +#define gr_urect \ + ((void (*)(short x0,short y0,short x1,short y1))grd_canvas_table[DRAW_URECT]) +#define gr_rect \ + ((int (*)(short x0,short y0,short x1,short y1))grd_canvas_table[DRAW_RECT]) +#define gr_ubox \ + ((void (*)(short x0,short y0,short x1,short y1))grd_canvas_table[DRAW_UBOX]) +#define gr_box \ + ((int (*)(short x0,short y0,short x1,short y1))grd_canvas_table[DRAW_BOX]) +#endif /* !__GRRECT_H */ diff --git a/engine/src/Libraries/2D/Source/GR/grrend.h b/engine/src/Libraries/2D/Source/GR/grrend.h new file mode 100644 index 0000000..318272f --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grrend.h @@ -0,0 +1,214 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/grrend.h $ + * $Revision: 1.14 $ + * $Author: kevin $ + * $Date: 1994/08/04 09:46:09 $ + * + * Dispatch macros for rendering functions. + * + * This file is part of the 2d library. + * + * $Log: grrend.h $ + * Revision 1.14 1994/08/04 09:46:09 kevin + * Added new wire poly line functionality. + * + * Revision 1.13 1994/06/11 00:35:40 lmfeeney + * lines removed from canvas table, #defines for backward compatibility + * + * Revision 1.12 1993/10/26 02:17:43 kevin + * Changed gr_scale... and gr_clut_scale... dispatch macros + * so that they may be used with any bitmap type. + * (rsd8 and flat8 types are currently supported.) + * + * Revision 1.11 1993/10/20 15:22:00 kaboom + * Took out spoly dispatch macros. + * + * Revision 1.10 1993/10/19 10:22:15 kaboom + * Moved solid and color shaded (but not intensity shaded) polygon macros + * to other files. + * + * Revision 1.9 1993/10/02 01:01:41 kaboom + * Moved texture map stuff to other files. + * + * Revision 1.8 1993/09/02 20:12:06 kaboom + * Added macros for dispatching of gr_lin_{u}map by bitmap type. Also + * put in macros for bypass 8- and 24-bit linmaps. + * + * Revision 1.7 1993/08/19 21:51:21 jaemz + * Added bitmap scale clut and voxel functions + * + * Revision 1.6 1993/08/10 19:08:12 kaboom + * Added macros for gr_lit_{u}tmap() + * + * Revision 1.5 1993/07/20 16:24:54 jaemz + * Added interp2 and filterw2 + * + * Revision 1.4 1993/06/22 19:59:50 spaz + * Added macros for goroud-shadedl ines: + * gr_fix_usline, sline, ucline, and cline. (fix only) + * + * Revision 1.3 1993/06/14 14:11:33 kaboom + * Added macros for new lin_{u}map and lin_lit_lin_{u}map routines. + * + * Revision 1.2 1993/06/01 13:51:25 kaboom + * Added macros for lit perspective tmappers. + * + * Revision 1.1 1993/04/29 18:36:58 kaboom + * Initial revision + */ + +#ifndef __GRREND_H +#define __GRREND_H + +#include "grs.h" +#include "icanvas.h" +#include "tabdat.h" + +#include "ctxmac.h" +#include "rgb.h" +#include "grlin.h" + +/* compatibility -- rah ! */ + +/* - Lines have been eliminated from the canvas table. + - The preferred interface now takes color and fill parameters and + either a grs_vertex structure or short points. + + Unclipped line routines have been inlined to convert parameters and + call the new function via the uline_fill table. + + Clipped line routines return a value and so cannot be macro inlined + this way. The canvas lookup macro is now a function which is + extern'ed by it's old 'gen' name. + + Again: the preferred interface is through the new parameter'ed functions + in the uline_fill and line_clip_fill tables. + +*/ + +/* lines */ + +#define gr_fix_line gen_fix_line +extern int gen_fix_line (fix x0, fix y0, fix x1, fix y1); + +#define gr_fix_uline(x0,y0,x1,y1)\ +do {\ + grs_vertex gfu_v0, gfu_v1;\ + gfu_v0.x = (x0); gfu_v0.y = (y0);\ + gfu_v1.x = (x1); gfu_v1.y = (y1);\ + grd_uline_fill(gr_get_fcolor(), \ + gr_get_fill_parm(), &gfu_v0, &gfu_v1);\ +} while (0) + +/* rgb shaded lines */ + +#define gr_fix_cline gen_fix_cline +extern int gen_fix_cline (fix x0, fix y0, grs_rgb c0, fix x1, fix y1, grs_rgb c1); + +#define gr_fix_ucline(x0,y0,c0,x1,y1,c1) \ +do { \ + grs_vertex gfuc_v0, gfuc_v1; \ +\ + gfuc_v0.x = (x0); gfuc_v0.y = (y0); \ + gfuc_v1.x = (x1); gfuc_v1.y = (y1); \ +\ + gr_split_rgb ((c0), (uchar*) &(gfuc_v0.u), (uchar*)&(gfuc_v0.v), (uchar*)&(gfuc_v0.w)); \ + gr_split_rgb ((c1), (uchar*)&(gfuc_v1.u), (uchar*)&(gfuc_v1.v), (uchar*)&(gfuc_v1.w)); \ +\ + grd_ucline_fill (gr_get_fcolor(), gr_get_fill_parm(), &gfuc_v0, &gfuc_v1); \ +} while(0) + + +/* i shaded lines */ + +#define gr_fix_sline gen_fix_sline +extern int gen_fix_sline (fix x0, fix y0, fix i0, fix x1, fix y1, fix i1); + +#define gr_fix_usline(x0,y0,i0,x1,y1,i1) \ +do { \ + grs_vertex gfuc_v0, gfuc_v1; \ +\ + gfuc_v0.x = (x0); gfuc_v0.y = (y0); gfuc_v0.i = (i0);\ + gfuc_v1.x = (x1); gfuc_v1.y = (y1); gfuc_v1.i = (i1);\ +\ + grd_usline_fill (gr_get_fcolor(), gr_get_fill_parm(), &gfuc_v0, &gfuc_v1); \ +} while(0) + +/* vertex lines */ + +#define gr_uline(c,v0,v1) \ + grd_uline_fill(c,gr_get_fill_parm(),v0,v1) +#define gr_usline(v0,v1) \ + grd_usline_fill(gr_get_fcolor(), gr_get_fill_parm(),v0,v1) +#define gr_ucline(v0,v1) \ + grd_ucline_fill(gr_get_fcolor(), gr_get_fill_parm(),v0,v1) + +/* wire poly lines */ + +#define gr_wire_poly_uline(c,v0,v1) \ + grd_wire_poly_uline_fill(c, gr_get_fill_parm(),v0,v1) +#define gr_wire_poly_usline(v0,v1) \ + grd_wire_poly_usline_fill(gr_get_fcolor(), gr_get_fill_parm(), v0, v1) +#define gr_wire_poly_ucline(v0,v1) \ + grd_wire_poly_ucline_fill(gr_get_fcolor(), gr_get_fill_parm(), v0, v1) + +#define gr_wire_poly_line(c,v0,v1) \ + grd_wire_poly_line_clip_fill(c, gr_get_fill_parm(),v0,v1) +#define gr_wire_poly_sline(v0,v1) \ + grd_wire_poly_sline_clip_fill(gr_get_fcolor(), gr_get_fill_parm(), v0, v1) +#define gr_wire_poly_cline(v0,v1) \ + grd_wire_poly_cline_clip_fill(gr_get_fcolor(), gr_get_fill_parm(), v0, v1) + +/* these continue to do traditional lookup's */ + +#define gr_vox_rect \ + ((void (*)(fix x[4],fix y[4],fix dz[3],int near_ver,grs_bitmap *col,grs_bitmap *ht,int dotw,int doth)) \ + grd_canvas_table[VOX_RECT]) +#define gr_vox_poly \ + ((void (*)(fix x[4],fix y[4],fix dz[3],int near_ver,grs_bitmap *col,grs_bitmap *ht)) \ + grd_canvas_table[VOX_POLY]) +#define gr_vox_cpoly \ + ((void (*)(fix x[4],fix y[4],fix dz[3],int near_ver,grs_bitmap *col,grs_bitmap *ht)) \ + grd_canvas_table[VOX_CPOLY]) +#define gr_interp2_ubitmap \ + ((void (*)(grs_bitmap *bm)) grd_canvas_table[INTERP2_UBITMAP]) +#define gr_filter2_ubitmap \ + ((void (*)(grs_bitmap *bm)) grd_canvas_table[FILTER2_UBITMAP]) + + /* These are horrific: something should be done. */ +#define gr_scale_ubitmap(bm,x,y,w,h) \ + ((void (*)(grs_bitmap *_bm,short _x,short _y,short _w,short _h)) \ + grd_canvas_table[SCALE_DEVICE_UBITMAP+2*((bm)->type)]) (bm,x,y,w,h) +#define gr_scale_bitmap(bm,x,y,w,h) \ + ((int (*)(grs_bitmap *_bm,short _x,short _y,short _w,short _h)) \ + grd_canvas_table[SCALE_DEVICE_BITMAP+2*((bm)->type)]) (bm,x,y,w,h) + +#define gr_clut_scale_ubitmap(bm,x,y,w,h,cl) \ + ((int (*)(grs_bitmap *_bm,short _x,short _y,short _w,short _h,uchar *_cl)) \ + grd_canvas_table[CLUT_SCALE_DEVICE_UBITMAP+2*((bm)->type)]) \ + (bm,x,y,w,h,cl) +#define gr_clut_scale_bitmap(bm,x,y,w,h,cl) \ + ((int (*)(grs_bitmap *_bm,short _x,short _y,short _w,short _h,uchar *_cl)) \ + grd_canvas_table[CLUT_SCALE_DEVICE_BITMAP+2*((bm)->type)]) \ + (bm,x,y,w,h,cl) +#define gr_roll_ubitmap grd_canvas_table[ROLL_UBITMAP]) +#define gr_roll_bitmap ((int (*)())grd_canvas_table[ROLL_BITMAP]) +#endif /* !__GRREND_H */ diff --git a/engine/src/Libraries/2D/Source/GR/grs.h b/engine/src/Libraries/2D/Source/GR/grs.h new file mode 100644 index 0000000..e7203f7 --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grs.h @@ -0,0 +1,248 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/grs.h $ + * $Revision: 1.13 $ + * $Author: baf $ + * $Date: 1993/12/16 00:30:50 $ + * + * Public 2D system data structures. + * + * This file is part of the 2d library. + * + * $Log: grs.h $ + * Revision 1.13 1993/12/16 00:30:50 baf + * Removed include of spntyp.h + * + * Revision 1.12 1993/12/14 22:36:29 kevin + * Took out perspective mapper context structure. Sorry + * it should never have been + * here in the first place. + * + * Revision 1.11 1993/12/04 12:42:30 kevin + * Added context structure for perspective mappers. + * + * Revision 1.10 1993/10/19 10:22:49 kaboom + * Font member of grs_context is now a grs_font *. + * + * Revision 1.9 1993/10/15 18:04:45 baf + * Moved transtab to screen + * + * Revision 1.8 1993/10/15 12:28:16 baf + * Added transluceny table to graphics context + * + * Revision 1.7 1993/10/06 16:08:56 baf + * Moved grs_span definition to spntyp.h + * + * Revision 1.6 1993/10/06 13:33:57 kevin + * Added default color lookup table to screen type. + * + * Revision 1.5 1993/07/08 23:02:53 kaboom + * Added wlog and hlog fields to grs_bitmap structure. + * + * Revision 1.4 1993/06/01 13:51:46 kaboom + * Added lighting table to screen structure. + * + * Revision 1.3 1993/04/29 18:37:08 kaboom + * Added system info structures. Pared down driver capability structure. + * + * Revision 1.1 1993/02/04 17:36:18 kaboom + * Initial revision + * + ******************************************************************** + * Log entries from old 2d.h + * Revision 1.20 1993/01/25 11:09:57 matt + * Removed structure definition for 'vector' (leaving in 'grs_vector'), + * since vector conflicted with the 3d structure of the same name. + * + * Revision 1.19 1993/01/22 19:49:02 kaboom + * Added some structures for texture mapper vertices and palette. + * + * Revision 1.18 1993/01/15 21:41:52 kaboom + * Changed grs_stencil to grs_sten_elem and made new grs_stencil that + * includes a flags word. + * + * Revision 1.12 1992/12/30 14:59:53 kaboom + * Changed from using grs_span for stencil elements to new grs_stencil + * type. + * + * Revision 1.8 1992/12/10 11:11:31 kaboom + * Moved ytab field from bitmap structure to canvas. Changed clipping + * regions from 2 structures, which are used as casts, to a union with + * fixed and int fields. + * + * Revision 1.6 1992/11/12 13:54:09 kaboom + * Added type, align and ytab fields to bitmap structure. Reduced + * bitmap flags to a short. Changed clipping region to store fixed- + * point values, and added different structures to allow their access + * as fixed-point or integer values without shifting. + * + * Revision 1.5 1992/10/21 15:58:48 kaboom + * Changed naming for gr_xxx structures to grs_xxx to avoid some name + * collisions with functions. + * + * Revision 1.3 1992/10/09 16:50:55 kaboom + * Added gr_ylrpp structure for scanline pixel-based drawing. + */ + +#ifndef __GRS_H +#define __GRS_H + +#include "fix.h" + +// FIXME pragma pack +#pragma pack(push,2) + +/* system information structure. */ +typedef struct { + uchar id_maj; /* major id---type of graphics system */ + uchar id_min; /* minor id---vendor */ + short memory; /* memory in kilobytes */ + short modes[16]; /* array of modes, ends with -1 */ +} grs_sys_info; + +/* mode information descriptor structure. */ +typedef struct { + short w; /* screen width */ + short h; /* screen height */ + uchar b; /* number of bits per pixel */ +} grs_mode_info; + +/* amazing rgb type. */ +typedef uint32_t grs_rgb; + +/* structure for bitmaps to be drawn from and to. if a bitmap is contained + within a larger bitmap, the row field tells how wide the containing bitmap + is. */ +typedef struct { + uchar *bits; /* ptr to data */ + uchar type; /* type of data in bitmap, 1-bit, 8-bit, etc */ + uchar align; /* where data really starts */ + ushort flags; /* whether compressed, transparent, etc */ + short w; /* width in pixels */ + short h; /* height */ + ushort row; /* bytes in row of containing bitmap */ + uchar wlog; /* log2 of w */ + uchar hlog; /* log2 of h */ +} grs_bitmap; + +/* stencil element for non-rectangular clipping. */ +typedef struct _sten { + short l; /* left edge of stencil */ + short r; /* right */ + struct _sten *n; /* pointer to next span in this scanline */ +} grs_sten_elem; + +/* stencil header for non-rectangular clipping. */ +typedef struct { + grs_sten_elem *elem; /* pointer to first stencil element */ + int32_t flags; /* specific stencil data. */ +} grs_stencil; + +/* structure for clipping regions. a clipping region can either be a simple + rectangle (given by left,top,right,bot) or a grs_stencil, pointed to by + sten. */ +typedef union { + struct { + grs_stencil *sten; /* pointer to stencil for nonrect clip region */ + fix left; /* current clipping rectangle */ + fix top; /* fixed-point coordinates */ + fix right; + fix bot; + } f; + struct { + grs_stencil *sten; /* pointer to stencil for nonrect clip region */ + short pad0; + short left; /* current clipping rectangle */ + short pad1; + short top; /* integral coordinates */ + short pad2; + short right; + short pad3; + short bot; + } i; +} grs_clip; + +// Font. +typedef struct { + ushort id; + char dummy1[34]; + short min; + short max; + char dummy2[32]; + int32_t cotptr; + int32_t buf; + short w; + short h; + short off_tab[1]; +} grs_font; + +// Access to fonts in resources. +#define FORMAT_FONT FORMAT_RAW + +/* structure for drawing context. the context contains data about which + color, font attributes, filling attributes, and an embedded clipping + region structure. */ +typedef struct { + int32_t fcolor; /* current drawing color */ + int32_t bcolor; /* background color */ + grs_font *font; /* font id */ + int32_t text_attr; /* attributes for text */ + int32_t fill_type; /* how to fill primitives */ + intptr_t fill_parm; /* parameter for fill */ + grs_clip clip; /* clipping region */ +} grs_context; + +/* a canvas is a bitmap drawing context. */ +typedef struct { + grs_bitmap bm; /* bitmap to draw into/read out of */ + grs_context gc; /* graphic context */ + uchar **ytab; /* pointer to an optional y table */ +} grs_canvas; + +/* a screen is a descriptor for a visible region of video memory. */ +typedef struct { + grs_bitmap bm; /* where we actually draw */ + grs_canvas *c; /* pointer to 2 system canvases */ + uchar *pal; + grs_rgb *bpal; + uchar *ipal; + uchar *ltab; + uchar ***transtab;/* table of colors under translucency */ + uchar *clut; /* default color lookup table */ + short x; /* upper left coordinates of visible */ + short y; /* region of virtual buffer */ +} grs_screen; + +/* driver capability/info structure. */ +typedef struct { + fix aspect; /* fixed point aspect ratio w/h */ + short w; /* screen width */ + short h; /* screen height */ + intptr_t *vbase; /* base video address */ +} grs_drvcap; + +/* 3d point structure for perspective mapper. */ +typedef struct { + fix x,y,z; /* 3's */ +} grs_point3d; + +#pragma pack(pop) + +#endif /* !__GRS_H */ diff --git a/engine/src/Libraries/2D/Source/GR/grstate.h b/engine/src/Libraries/2D/Source/GR/grstate.h new file mode 100644 index 0000000..dc0faf3 --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/grstate.h @@ -0,0 +1,51 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/grstate.h $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/10/19 16:22:22 $ + * + * Declarations for state push/pop. + * + * This file is part of the 2d library. + * + * $Log: grstate.h $ + * Revision 1.2 1994/10/19 16:22:22 kevin + * Switch to canvas specific state. + * + * Revision 1.1 1994/10/18 13:34:13 kevin + * Initial revision + * + * + */ + +#ifndef GRSTATE_H +#define GRSTATE_H + +#include "icanvas.h" +#include "tabdat.h" + +#define gr_push_state \ + ((int (*)())grd_pixel_table[PUSH_STATE]) +#define gr_pop_state \ + ((int (*)())grd_pixel_table[POP_STATE]) + +#endif + diff --git a/engine/src/Libraries/2D/Source/GR/gruilin.c b/engine/src/Libraries/2D/Source/GR/gruilin.c new file mode 100644 index 0000000..5d2f113 --- /dev/null +++ b/engine/src/Libraries/2D/Source/GR/gruilin.c @@ -0,0 +1,46 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/gruilin.c $ + * $Revision: 1.1 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 02:37:08 $ + */ + +#include "fix.h" +#include "plytyp.h" +#include "lintyp.h" +#include "grlin.h" + +/* This just converts to fix-point, then calls the current uline + drawer, so this OK for all canvases, fill modes, etc. In general, + you would prefer to take advantage of nice integer special cases to + avoid shifts, but this is probably overkill. +*/ + +void gri_all_uiline_fill (long c, long parm, grs_vertex *v0, grs_vertex *v1) +{ + grs_vertex u0, u1; + + u0.x = fix_make(v0->x, 32768); u0.y = fix_make(v0->y, 32768); + u1.x = fix_make(v1->x, 32768); u1.y = fix_make(v1->y, 32768); + + grd_uline_fill (c, parm, &u0, &u1); +} + diff --git a/engine/src/Libraries/2D/Source/Gen/genbox.c b/engine/src/Libraries/2D/Source/Gen/genbox.c new file mode 100644 index 0000000..9be2af7 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genbox.c @@ -0,0 +1,61 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genbox.c $ + * $Revision: 1.9 $ + * $Author: rex $ + * $Date: 1994/04/29 14:28:25 $ + * + * Generic box (unfilled rectangle) routines. + */ + +#include "clpcon.h" +#include "grrect.h" +#include "general.h" + +/* draw an unclipped, unfilled rectangle. does 2 hlines & 2 vlines. */ +void gen_ubox(short left, short top, short right, short bot) +{ + if (left<=(right-2)) + gr_uhline(left, top, right-2); + if (top<=(bot-2)) + gr_uvline(right-1, top, bot-2); + if ((left+1)<=(right-1)) + gr_uhline(left+1, bot-1, right-1); + if ((top+1)<=(bot-1)) + gr_uvline(left, top+1, bot-1); +} + +/* draw a clipped, unfilled rectangle. does 2 clipped hlines and 2 clipped + vlines. returns clip code. */ +int gen_box(short left, short top, short right, short bot) +{ + int code = CLIP_NONE; + + if (left<=(right-2)) + code |= gr_hline(left, top, right-2); + if (top<=(bot-2)) + code |= gr_vline(right-1, top, bot-2); + if ((left+1)<=(right-1)) + code |= gr_hline(left+1, bot-1, right-1); + if ((top+1)<=(bot-1)) + code |= gr_vline(left, top+1, bot-1); + + return code; +} diff --git a/engine/src/Libraries/2D/Source/Gen/genchfl8.c b/engine/src/Libraries/2D/Source/Gen/genchfl8.c new file mode 100644 index 0000000..0d3d6fa --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genchfl8.c @@ -0,0 +1,113 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genchfl8.c $ + * $Revision: 1.3 $ + * $Author: kevin $ + * $Date: 1993/10/26 02:38:07 $ + * + * Generic routines to draw a horizontally flipped flat 8 bitmap. + * + * This file is part of the 2d library. + * + * $Log: genchfl8.c $ + * Revision 1.3 1993/10/26 02:38:07 kevin + * Use default clut if passed cl=NULL. + * Clipped version now uses dispatch macro to call unclipped version. + * + * Revision 1.2 1993/10/19 09:51:06 kaboom + * Replaced #include "grd.h" with new headers split from grd.h. + * + * Revision 1.1 1993/10/06 14:40:15 kevin + * Initial revision + */ + +#include "clpcon.h" +#include "cnvdat.h" +#include "scrmac.h" +#include "grpix.h" +#include "grclhbm.h" + +/* draw an unclipped, horizontally flipped flat 8 bitmap to a + canvas. */ +void gen_clut_hflip_flat8_ubitmap(grs_bitmap *bm, short x, short y, uchar *cl) +{ + short r; /* right x coordinate */ + short b; /* bottom y coordinate */ + short cur_x; /* current x */ + uchar *src; /* pointer into source bitmap */ + + if (cl == NULL) cl=gr_get_clut(); + r = x+bm->w-1; + b = y+bm->h-1; + src = bm->bits; + for ( ; y<=b; y++, src+=bm->row-bm->w) + for (cur_x=r; cur_x>=x; cur_x--, src++) + gr_set_upixel (cl[*src], cur_x, y); +} + +/* draw a clipped, horizontally flipped flat 8bitmap to a canvas. */ +int gen_clut_hflip_flat8_bitmap (grs_bitmap *bm, short x, short y, uchar *cl) +{ + short w,h; + uchar *p; + short r; + short b; + int extra; + int code = CLIP_NONE; + + r = x+bm->w-1; + b = y+bm->h-1; + + /* save stuff that clipping changes. */ + w = bm->w; h = bm->h; p = bm->bits; + + /* first check for trivial reject. */ + if (x>=grd_clip.right || r=grd_clip.bot || bw -= grd_clip.left-x; + x = grd_clip.left; + code |= CLIP_LEFT; + } + if (r >= grd_clip.right) { /* off right */ + extra = r-grd_clip.right+1; + bm->w -= extra; + bm->bits += extra; + code |= CLIP_RIGHT; + } + if (y < grd_clip.top) { /* off top */ + extra = grd_clip.top - y; + bm->h -= extra; + bm->bits += bm->row*extra; + y = grd_clip.top; + code |= CLIP_TOP; + } + if (b >= grd_clip.bot) { /* off bottom */ + bm->h -= b-grd_clip.bot+1; + code |= CLIP_BOT; + } + gr_clut_hflip_flat8_ubitmap (bm, x, y, cl); + + /* restore bitmap to normal. */ + bm->w = w; bm->h = h; bm->bits = p; + return code; +} diff --git a/engine/src/Libraries/2D/Source/Gen/gencirc.c b/engine/src/Libraries/2D/Source/Gen/gencirc.c new file mode 100644 index 0000000..7d9d028 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/gencirc.c @@ -0,0 +1,62 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include "grd.h" +#include "genel.h" + +/* This is rather lame, uses aspect ratio to determine parameters to + * the elipse drawer. That would be fine, except that it makes the + * definition of r somewhat arbitrary, since it has to be in some unit + * in some mode. For compatibility (sort of), we say 320x200 x-pixel's. + * This leads to some ugly rounding, but the circles are good enough. + */ + +void gen_int_ucircle (short x0, short y0, short r) +{ + + fix a, b, ratio; + + /* scale from 320x200 x-pixels */ + ratio = fix_div (((grd_cap->w)<<16), (320<<16)); + a = fix_mul((r<<16), ratio); + + /* calculate equivalent b */ + b = fix_div (a,(grd_cap->aspect)); + + gr_int_uelipse (x0, y0, fix_fint(a), fix_fint(b)); + + return; +} + +int gen_int_circle (short x0, short y0, short r) +{ + int c; + + fix a, b, ratio; + + /* scale from 320x200 x-pixels */ + ratio = fix_div (((grd_cap->w)<<16), (320<<16)); + a = fix_mul((r<<16), ratio); + + /* calculate equivalent b */ + b = fix_div (a,(grd_cap->aspect)); + + c = gr_int_elipse (x0, y0, fix_fint(a), fix_fint(b)); + + return c; +} diff --git a/engine/src/Libraries/2D/Source/Gen/genclin.c b/engine/src/Libraries/2D/Source/Gen/genclin.c new file mode 100644 index 0000000..8b8e719 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genclin.c @@ -0,0 +1,101 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genclin.c $ + * $Revision: 1.8 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 02:25:04 $ + * + * Routines to draw color shaded lines. + * + * This file is part of the 2d library. + * + * $Log: genclin.c $ + * Revision 1.8 1994/06/11 02:25:04 lmfeeney + * moved unclipped drawer, now contains two versions of + * clipped line drawer, one for each i\f - call clipper + * and call unclipped line drawer + * + * Revision 1.7 1994/05/06 18:19:33 lmfeeney + * rewritten for greater accuracy and speed + * + * Revision 1.6 1993/10/19 09:51:08 kaboom + * Replaced #include "grd.h" with new headers split from grd.h. + * + * Revision 1.5 1993/10/02 01:17:18 kaboom + * Changed include of clip.h to include of clpcon.h and/or clpfcn.h. + * + * Revision 1.4 1993/07/03 22:51:39 spaz + * Bugfix; treated clipper return code spastically + * + * Revision 1.3 1993/07/01 20:32:53 spaz + * Set last pixel explicitly to i1 in h and vlines + * + * Revision 1.2 1993/06/30 00:42:26 spaz + * Changed a check for (x0>x1) to fix_int(x0)>fix_int(x1), + * because it was negating the delta uselessly for near- + * vertical lines. + * + * Revision 1.1 1993/06/22 20:14:14 spaz + * Initial revision + */ + +#include "ctxmac.h" +#include "plytyp.h" +#include "clpcon.h" +#include "clpltab.h" +#include "grlin.h" +#include "rgb.h" + +/* clip and call unclipped drawer -- returns a clip value */ + +int gen_fix_cline (fix x0, fix y0, grs_rgb c0, fix x1, fix y1, grs_rgb c1) +{ + int r; + grs_vertex v0, v1; + + v0.x = x0; v0.y = y0; + v1.x = x1; v1.y = y1; + gr_split_rgb(c0, (uchar *) &(v0.u), (uchar *) &(v0.v), (uchar *) &(v0.w)); + gr_split_rgb(c1, (uchar *) &(v1.u), (uchar *) &(v1.v), (uchar *) &(v1.w)); + + r = grd_cline_clip_fill (gr_get_fcolor(), gr_get_fill_parm(), &v0, &v1); + + return r; +} + +int gri_cline_clip_fill (long c, long parm, grs_vertex *v0, grs_vertex *v1) + +{ + int r; + grs_vertex u0, u1; + + /* save inputs (don't really need whole struct) */ + + u0 = *v0; + u1 = *v1; + + r = gri_cline_clip (&u0, &u1); + + if (r != CLIP_ALL) + grd_ucline_fill (c, parm, &u0, &u1); + + return r; +} + diff --git a/engine/src/Libraries/2D/Source/Gen/gencnv.c b/engine/src/Libraries/2D/Source/Gen/gencnv.c new file mode 100644 index 0000000..f0c09e3 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/gencnv.c @@ -0,0 +1,396 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/gencnv.c $ + * $Revision: 1.23 $ + * $Author: kevin $ + * $Date: 1994/10/25 15:13:54 $ + * + */ + +// MLA, NOTE- the general canvas table functions are left as unimplemented ptrs since the +// general canvas code isn't really used directly(at least thats what they tell me). + +#include "grs.h" +#include "grnull.h" +#include "general.h" +#include "icanvas.h" + +typedef void (*ptr_type)(); + +void (*gen_canvas_table[GRD_CANVAS_FUNCS])() = { + gr_null, /* NO generic pixel routines! */ + gr_null, + gr_null, + gr_null, + + gr_null, + gr_null, + gr_null, + gr_null, + + gr_not_imp, // (ptr_type) gen_clear, /* integral, straight primitives */ + gr_not_imp, // (ptr_type) gen_upoint, + gr_not_imp, // (ptr_type) gen_point, + gr_null, + gr_null, + gr_null, + gr_null, + (ptr_type) gen_urect, + (ptr_type) gen_rect, + (ptr_type) gen_ubox, + (ptr_type) gen_box, + + gr_null, /* fixed-point rendering primitives */ + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) temp_upoly, +gr_not_imp, // (ptr_type) temp_poly, +gr_not_imp, // (ptr_type) temp_uspoly, +gr_not_imp, // (ptr_type) temp_spoly, +gr_not_imp, // (ptr_type) temp_ucpoly, +gr_not_imp, // (ptr_type) temp_cpoly, +gr_not_imp, // (ptr_type) temp_utpoly, +gr_not_imp, // (ptr_type) temp_tpoly, +gr_not_imp, // (ptr_type) temp_ustpoly, +gr_not_imp, // (ptr_type) temp_stpoly, + +gr_not_imp, // (ptr_type) gen_vox_rect, +gr_not_imp, // (ptr_type) gen_vox_poly, +gr_not_imp, // (ptr_type) gen_vox_cpoly, +gr_not_imp, // (ptr_type) gen_interp2_ubitmap, +gr_not_imp, // (ptr_type) gen_filter2_ubitmap, +gr_not_imp, // (ptr_type) gen_roll_ubitmap, +gr_not_imp, // (ptr_type) gen_roll_bitmap, + +gr_not_imp, // (ptr_type) temp_wall_umap, + gr_null, +gr_not_imp, // (ptr_type) temp_lit_wall_umap, + gr_null, +gr_not_imp, // (ptr_type) temp_clut_wall_umap, + gr_null, + +gr_not_imp, // (ptr_type) temp_floor_umap, + gr_null, +gr_not_imp, // (ptr_type) temp_lit_floor_umap, + gr_null, +gr_not_imp, // (ptr_type) temp_clut_floor_umap, + gr_null, + + gr_null, /* linear texture mappers */ + gr_null, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) temp_lin_umap, +gr_not_imp, // (ptr_type) temp_lin_map, +gr_not_imp, // (ptr_type) temp_lin_umap, +gr_not_imp, // (ptr_type) temp_lin_map, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) temp_lin_umap, +gr_not_imp, // (ptr_type) temp_lin_map, + + gr_null, /* lit linear texture mappers */ + gr_null, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) temp_lit_lin_umap, +gr_not_imp, // (ptr_type) temp_lit_lin_map, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) temp_lit_lin_umap, +gr_not_imp, // (ptr_type) temp_lit_lin_map, + gr_null, + gr_null, + + gr_null, /* clut linear texture mappers */ + gr_null, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) temp_clut_lin_umap, +gr_not_imp, // (ptr_type) temp_clut_lin_map, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) temp_clut_lin_umap, +gr_not_imp, // (ptr_type) temp_clut_lin_map, +gr_not_imp, // (ptr_type) temp_clut_lin_umap, +gr_not_imp, // (ptr_type) temp_clut_lin_umap, + + gr_null, /* solid linear mapper */ + gr_null, + + gr_null, /* perspective texture mappers */ + gr_null, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) temp_per_umap, + gr_null, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) temp_per_umap, + gr_null, + gr_null, + gr_null, + + gr_null, /* lit perspective texture mappers */ + gr_null, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) temp_lit_per_umap, + gr_null, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) temp_lit_per_umap, + gr_null, + gr_null, + gr_null, + + gr_null, /* clut perspective texture mappers */ + gr_null, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) temp_clut_per_umap, + gr_null, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) temp_clut_per_umap, + gr_null, + gr_null, + gr_null, + + gr_null, /* solid perspective mapper */ + gr_null, + +gr_not_imp, // (ptr_type) gen_int_ucircle, /* curves, should change to fixed-point */ +gr_not_imp, // (ptr_type) gen_int_circle, +gr_not_imp, // (ptr_type) gen_fix_ucircle, +gr_not_imp, // (ptr_type) gen_fix_circle, +gr_not_imp, // (ptr_type) gen_int_udisk, +gr_not_imp, // (ptr_type) gen_int_disk, +gr_not_imp, // (ptr_type) gen_fix_udisk, +gr_not_imp, // (ptr_type) gen_fix_disk, +gr_not_imp, // (ptr_type) gen_int_urod, +gr_not_imp, // (ptr_type) gen_int_rod, +gr_not_imp, // (ptr_type) gen_fix_urod, +gr_not_imp, // (ptr_type) gen_fix_rod, + + gr_null, /* bitmap drawing functions. */ + gr_null, +gr_not_imp, // (ptr_type) gen_mono_ubitmap, +gr_not_imp, // (ptr_type) gen_mono_bitmap, +gr_not_imp, // (ptr_type) gen_flat8_ubitmap, +gr_not_imp, // (ptr_type) gen_flat8_bitmap, +gr_not_imp, // (ptr_type) gen_flat24_ubitmap, +gr_not_imp, // (ptr_type) gen_flat24_bitmap, +gr_not_imp, // (ptr_type) gri_gen_rsd8_ubitmap, +gr_not_imp, // (ptr_type) gri_gen_rsd8_bitmap, +gr_not_imp, // (ptr_type) gen_tluc8_ubitmap, +gr_not_imp, // (ptr_type) gen_tluc8_bitmap, + + gr_null, /* bitmap drawing functions through a clut. */ + gr_null, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) gen_flat8_clut_ubitmap, +gr_not_imp, // (ptr_type) gen_flat8_clut_bitmap, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) unpack_rsd8_clut_ubitmap, +gr_not_imp, // (ptr_type) unpack_rsd8_clut_bitmap, + gr_null, + gr_null, + +gr_not_imp, // (ptr_type) gen_rsd8_solid_ubitmap, +gr_not_imp, // (ptr_type) gen_rsd8_solid_bitmap, + + gr_null, /* scaled bitmap drawing functions. */ + gr_null, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) temp_scale_umap, +gr_not_imp, // (ptr_type) temp_scale_map, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) temp_scale_umap, +gr_not_imp, // (ptr_type) temp_scale_map, +gr_not_imp, // (ptr_type) temp_scale_umap, +gr_not_imp, // (ptr_type) temp_scale_map, + +gr_not_imp, // (ptr_type) gen_rsd8_scale_solid_ubitmap, +gr_not_imp, // (ptr_type) gen_rsd8_scale_solid_bitmap, + + gr_null, /* clut scale functions. */ + gr_null, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) temp_clut_scale_umap, +gr_not_imp, // (ptr_type) temp_clut_scale_map, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) temp_clut_scale_umap, +gr_not_imp, // (ptr_type) temp_clut_scale_map, +gr_not_imp, // (ptr_type) temp_clut_scale_umap, +gr_not_imp, // (ptr_type) temp_clut_scale_map, + + gr_null, /* bitmap mask draw functions. */ + gr_null, + gr_null, + gr_null, + gr_null,//span_mask_flat8_ubitmap, + gr_null,//span_mask_flat8_bitmap, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + + gr_null, /* bitmap get functions. */ + gr_null, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) gen_get_flat8_ubitmap, +gr_not_imp, // (ptr_type) gen_get_flat8_bitmap, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + + gr_null, /* bitmap horizontal flip functions */ + gr_null, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) gen_hflip_flat8_ubitmap, +gr_not_imp, // (ptr_type) gen_hflip_flat8_bitmap, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + + gr_null, /* bitmap clut horizontal flip functions */ + gr_null, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) gen_clut_hflip_flat8_ubitmap, +gr_not_imp, // (ptr_type) gen_clut_hflip_flat8_bitmap, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + + gr_null, /* bitmap horizontal doubling. */ + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + + gr_null, /* bitmap vertical doubling. */ + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + + gr_null, /* bitmap horizontal and vertical doubling. */ + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + + gr_null, /* bitmap smooth horizontal doubling. */ + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + + gr_null, /* bitmap smooth vertical doubling. */ + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + + gr_null, /* bitmap smooth horizontal and vertical doubling. */ + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + gr_null, + + (ptr_type) gen_font_ustring, /* text/font functions. */ +gr_not_imp, // (ptr_type) gen_font_string, + gr_null, + gr_null, +gr_not_imp, // (ptr_type) gen_font_uchar, +gr_not_imp, // (ptr_type) gen_font_char, + + gr_null, /* bitmap type specific functions */ + gr_null, + + gr_null, /* placeholders for primitiveless chains */ + gr_null, +}; diff --git a/engine/src/Libraries/2D/Source/Gen/gencwlin.c b/engine/src/Libraries/2D/Source/Gen/gencwlin.c new file mode 100644 index 0000000..2693260 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/gencwlin.c @@ -0,0 +1,68 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/gencwlin.c $ + * $Revision: 1.1 $ + * $Author: kevin $ + * $Date: 1994/08/04 09:53:32 $ + * + * Routines to clip and draw wire poly lines. + * + * This file is part of the 2d library. + * + */ + +#include "clpcon.h" +#include "clpltab.h" +#include "grlin.h" +#include "plytyp.h" + +int gri_wire_poly_cline_clip_fill (long c, long parm, grs_vertex *v0, grs_vertex *v1) +{ + int r; + grs_vertex u0, u1; + + /* save inputs (don't really need whole struct) */ + u0 = *v0; + u1 = *v1; + + r = gri_cline_clip (&u0, &u1); + + if (r != CLIP_ALL) + grd_wire_poly_ucline_fill (c, parm, &u0, &u1); + + return r; +} + +int gri_wire_poly_line_clip_fill (long c, long parm, grs_vertex *v0, grs_vertex *v1) +{ + int r; + grs_vertex u0, u1; + + /* save inputs (don't really need whole struct) */ + u0 = *v0; + u1 = *v1; + + r = gri_line_clip (&u0, &u1); + + if (r != CLIP_ALL) + grd_wire_poly_uline_fill (c, parm, &u0, &u1); + + return r; +} diff --git a/engine/src/Libraries/2D/Source/Gen/gendisk.c b/engine/src/Libraries/2D/Source/Gen/gendisk.c new file mode 100644 index 0000000..fbd4744 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/gendisk.c @@ -0,0 +1,84 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/gendisk.c $ + * $Revision: 1.4 $ + * $Author: lmfeeney $ + * $Date: 1994/11/21 01:22:55 $ + * + * Generic disk drawing routines. + * + * $Log: gendisk.c $ + * Revision 1.4 1994/11/21 01:22:55 lmfeeney + * rewrote to determine use aspect ratio and draw appropriate oval + * + * Revision 1.3 1993/10/08 01:15:39 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.2 1993/04/29 18:40:25 kaboom + * Changed include of gr.h to smaller more specific grxxx.h. + * + * Revision 1.1 1993/03/03 18:10:02 kaboom + * Initial revision + */ + +#include "grd.h" +#include "genov.h" + +/* This is rather lame, uses aspect ratio to determine parameters to + * the oval drawer. That would be fine, except that it makes the + * definition of r somewhat arbitrary, since it has to be in some unit + * in some mode. For compatibility (sort of), we say 320x200 x-pixel's. + * This leads to some ugly rounding, but the circles are good enough. + */ + +void gen_int_udisk (short x0, short y0, short r) +{ + fix a, b, ratio; + + /* scale from 320x200 x-pixels */ + ratio = fix_div (((grd_cap->w)<<16), (320<<16)); + a = fix_mul((r<<16), ratio); + + /* calculate equivalent b */ + b = fix_div (a,(grd_cap->aspect)); + + gr_int_uoval (x0, y0, fix_fint(a), fix_fint(b)); + + return; +} + +/* this really should return a clip code */ +void gen_int_disk (short x0, short y0, short r) +{ + int c; + + fix a, b, ratio; + + /* scale from 320x200 x-pixels */ + ratio = fix_div (((grd_cap->w)<<16), (320<<16)); + a = fix_mul((r<<16), ratio); + + /* calculate equivalent b */ + b = fix_div (a,(grd_cap->aspect)); + + c = gr_int_oval (x0, y0, fix_fint(a), fix_fint(b)); + + return; +} diff --git a/engine/src/Libraries/2D/Source/Gen/genel.c b/engine/src/Libraries/2D/Source/Gen/genel.c new file mode 100644 index 0000000..f3f4747 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genel.c @@ -0,0 +1,189 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/genel.c $ + * $Revision: 1.1 $ + * $Author: lmfeeney $ + * $Date: 1994/11/07 15:26:38 $ + */ + +#include "clpcon.h" +#include "cnvdat.h" +#include "grpix.h" +#include "fix.h" +#include "genel.h" + +/* + * x-y oriented elipse drawer, mostly from Foley and VanDam, but the clipping + * especially could be made much more efficient + * + * the elipse drawer should be canvas specific and in the tables, but it would + * be better to wait until other 2d.h changes + */ + +void gr_int_uelipse (int x0, int y0, int a, int b) +{ + int x; + int y; + + fix24 a_sq, b_sq; + fix24 d1,d2,t1,t2; + + a_sq = (a * a)<<8; + b_sq = (b * b)<<8; + + x = 0; + y = b; + + /* d1 = b_sq - a_sq*b + a_sq/4 */ + + d1 = b_sq - fix24_mul(a_sq,(b<<8)) + fix24_div(a_sq,(4<<8)); + + t1 = fix24_mul(a_sq,((y<<8)-128)); + t2 = fix24_mul(b_sq,((x+1)<<8)); + + while (t1 > t2) { + + if (d1 < 0) { + d1 += fix24_mul(b_sq,((x<<9)+(3<<8))); + x++; + } + else { + d1 += fix24_mul(b_sq,((x<<9)+(3<<8))); + d1 += fix24_mul(a_sq,(((-y)<<9)+(2<<8))); + x++; y--; + } + + gr_set_upixel(grd_gc.fcolor,x0+x,y0+y); + gr_set_upixel(grd_gc.fcolor,x0-x,y0+y); + gr_set_upixel(grd_gc.fcolor,x0+x,y0-y); + gr_set_upixel(grd_gc.fcolor,x0-x,y0-y); + + t1 = fix24_mul(a_sq,((y<<8)-128)); + t2 = fix24_mul(b_sq,((x+1)<<8)); + } + + t1 = fix24_mul(((x<<8)+128),((x<<8)+128)); + d2 = fix24_mul(t1,b_sq); + + t1 = fix24_mul(((y-1)<<8),((y-1)<<8)); + d2 += fix24_mul(t1,a_sq); + + t1 = fix24_mul(a_sq,b_sq); + d2 -= t1; + + while (y > 0) { + if (d2 < 0) { + t1 = fix24_mul(((x<<9)+(2<<8)),b_sq); + t2 = fix24_mul(((3<<8)-(y<<9)),a_sq); + d2 = d2 + t1 + t2; + x++; y--; + } + else { + t2 = fix24_mul(((3<<8)-(y<<9)),a_sq); + d2 += t2; + y--; + } + gr_set_upixel(grd_gc.fcolor,x0+x,y0+y); + gr_set_upixel(grd_gc.fcolor,x0-x,y0+y); + gr_set_upixel(grd_gc.fcolor,x0+x,y0-y); + gr_set_upixel(grd_gc.fcolor,x0-x,y0-y); + } +} + +int gr_int_elipse (int x0, int y0, int a, int b) +{ + int x; + int y; + + fix24 a_sq, b_sq; + fix24 d1,d2,t1,t2; + + /* trivial clipping */ + + if (x0+a<=grd_clip.left || x0-a>grd_clip.right || + y0+b<=grd_clip.top || y0-b>grd_clip.bot) + return CLIP_ALL; + + + a_sq = (a * a)<<8; + b_sq = (b * b)<<8; + + x = 0; + y = b; + + /* d1 = b_sq - a_sq*b + a_sq/4 */ + + d1 = b_sq - fix24_mul(a_sq,(b<<8)) + fix24_div(a_sq,(4<<8)); + + t1 = fix24_mul(a_sq,((y<<8)-128)); + t2 = fix24_mul(b_sq,((x+1)<<8)); + + while (t1 > t2) { + + if (d1 < 0) { + d1 += fix24_mul(b_sq,((x<<9)+(3<<8))); + x++; + } + else { + d1 += fix24_mul(b_sq,((x<<9)+(3<<8))); + d1 += fix24_mul(a_sq,(((-y)<<9)+(2<<8))); + x++; y--; + } + + gr_set_pixel(grd_gc.fcolor,x0+x,y0+y); + gr_set_pixel(grd_gc.fcolor,x0-x,y0+y); + gr_set_pixel(grd_gc.fcolor,x0+x,y0-y); + gr_set_pixel(grd_gc.fcolor,x0-x,y0-y); + + t1 = fix24_mul(a_sq,((y<<8)-128)); + t2 = fix24_mul(b_sq,((x+1)<<8)); + } + + t1 = fix24_mul(((x<<8)+128),((x<<8)+128)); + d2 = fix24_mul(t1,b_sq); + + t1 = fix24_mul(((y-1)<<8),((y-1)<<8)); + d2 += fix24_mul(t1,a_sq); + + t1 = fix24_mul(a_sq,b_sq); + d2 -= t1; + + while (y > 0) { + if (d2 < 0) { + t1 = fix24_mul(((x<<9)+(2<<8)),b_sq); + t2 = fix24_mul(((3<<8)-(y<<9)),a_sq); + d2 = d2 + t1 + t2; + x++; y--; + } + else { + t2 = fix24_mul(((3<<8)-(y<<9)),a_sq); + d2 += t2; + y--; + } + gr_set_pixel(grd_gc.fcolor,x0+x,y0+y); + gr_set_pixel(grd_gc.fcolor,x0-x,y0+y); + gr_set_pixel(grd_gc.fcolor,x0+x,y0-y); + gr_set_pixel(grd_gc.fcolor,x0-x,y0-y); + } + + /* could be more specific */ + return CLIP_NONE; +} + diff --git a/engine/src/Libraries/2D/Source/Gen/genel.h b/engine/src/Libraries/2D/Source/Gen/genel.h new file mode 100644 index 0000000..7aa6a4b --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genel.h @@ -0,0 +1,33 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/genel.h $ + * $Revision: 1.1 $ + * $Author: lmfeeney $ + * $Date: 1994/11/07 15:26:25 $ + */ + +/* these routines should really be in the canvas table, but that + would be a 2d.h re-compile + */ + +extern void gr_int_uelipse (int, int, int, int); +extern int gr_int_elipse (int, int, int, int); + + diff --git a/engine/src/Libraries/2D/Source/Gen/general.c b/engine/src/Libraries/2D/Source/Gen/general.c new file mode 100644 index 0000000..6b6e04a --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/general.c @@ -0,0 +1,211 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/general.c $ + * $Revision: 1.23 $ + * $Author: unknown $ + * $Date: 1993/04/08 16:25:15 $ + * + * Generic primitive drawing routines. + * + * This file is part of the 2d library. + * + * $Log: general.c $ + * Revision 1.23 1993/04/08 16:25:15 unknown + * Took out dummy functions for strings. + * + * Revision 1.22 1993/03/29 18:30:17 kaboom + * Removed dummy functions for concave polygons. + * + * Revision 1.21 1993/03/03 17:54:53 kaboom + * Removed stubs for gen_int_{u}disk(). + * + * Revision 1.20 1993/02/25 12:58:40 kaboom + * Removed stubs for Gouraud shaders. + * + * Revision 1.19 1993/02/16 15:41:38 kaboom + * Moved the rest of the primitive routines to other files. + * + * Revision 1.18 1993/02/04 17:17:24 kaboom + * Moved many functions into other files. Added functab entries for cpoly. + * + * Revision 1.14 1993/01/11 17:58:25 matt + * Added gr_int_spoly() (code in gour.c) + * + * Revision 1.13 1993/01/07 21:06:50 kaboom + * Updated references to dr_xxx to grd_xxx. Updated faux function table + * with init_driver entry. + * + * Revision 1.10 1992/12/30 15:13:40 kaboom + * Removed calc_vram() and calc_row(), reserved for drivers. Removed + * function table, reserved for drivers. + * + * Revision 1.9 1992/12/14 18:13:37 kaboom + * Changed NULL table entries to gr_null. + * + * Revision 1.8 1992/12/11 20:30:42 kaboom + * Added slots in function table for wait_display and sub_bm functions. + * + * Revision 1.4 1992/11/12 13:27:20 kaboom + * Removed gen_bitmap and gen_ubitmap. The dispatching is now done from + * the actual gr_bitmap macro. + * + * Revision 1.3 1992/10/21 15:54:04 kaboom + * Added function blanks for additional driver functions, including fixed- + * point and integral versions of many functions and general, convex and + * concave polygon functions. Updated 2d structure prefix from gr_ to be + * grs_. + * + * Revision 1.1 1992/10/10 12:00:00 kaboom + * Initial revision. + */ + +void gen_fix_ucircle (void) +{ +} + +void gen_fix_circle (void) +{ +} + +void gen_fix_udisk (void) +{ +} + +void gen_fix_disk (void) +{ +} + +void gen_int_urod (void) +{ +} + +void gen_int_rod (void) +{ +} + +void gen_fix_urod (void) +{ +} + +void gen_fix_rod (void) +{ +} + +//int gen_flat24_ubitmap (grs_bitmap *bm, short x, short y) +//void gen_flat24_ubitmap (grs_bitmap *bm, short x, short y) + +#ifdef INCLUDE_GEN_FUNC_TABLES +void (*gen_func[grd_FUNCS])() = { + gr_null, /* set_upixel */ + gr_null, /* set_pixel */ + gr_null, /* get_upixel */ + gr_null, /* get_pixel */ + + gen_clear, + gen_upoint, + gen_point, + gen_uhline, + gen_hline, + gen_uvline, + gen_vline, + gen_urect, + gen_rect, + gen_ubox, + gen_box, + + gen_fix_uline, + gen_fix_line, + gen_fix_upoly, + gen_fix_poly, + gen_fix_uspoly, + gen_fix_spoly, + gen_fix_ucpoly, + gen_fix_cpoly, + gen_fix_utmap, + gen_fix_tmap, + + gen_int_ucircle, + gen_int_circle, + gen_fix_ucircle, + gen_fix_circle, + gen_int_udisk, + gen_int_disk, + gen_fix_udisk, + gen_fix_disk, + gen_int_urod, + gen_int_rod, + gen_fix_urod, + gen_fix_rod, + + /* bitmap drawing functions. */ + gr_null, /* draw bitmap device->device */ + gr_null, + gen_mono_ubitmap, + gen_mono_bitmap, + gen_flat8_ubitmap, + gen_flat8_bitmap, + gr_null, /* draw 24 bit */ + gr_null, + gen_rsd8_ubitmap, + gen_rsd8_bitmap, + + /* bitmap get functions. */ + gr_null, /* get bitmap device->device */ + gr_null, + gr_null, /* get mono bitmap */ + gr_null, + gen_get_flat8_ubitmap, + gen_get_flat8_bitmap, + gr_null, /* get 24 bit bitmap */ + gr_null, + gr_null, /* get rsd8 bitmap */ + gr_null, + + /* bitmap transform functions. */ + gen_scale_ubitmap, + gen_scale_bitmap, + gen_roll_ubitmap, + gen_roll_bitmap, + + /* text/font functions. */ + gen_ustring, + gen_string, + + /* span drawing functions. */ + gen_solid_lr, + gen_opaque_lrpp, + gr_null, /* draw_lrii */ + gr_null, /* draw_lrcc */ + + /* device functions. */ + gr_null, /* init_driver */ + gr_null, /* init_screen */ + gr_null, /* save_mode */ + gr_null, /* rest_mode */ + gr_null, /* calc_row */ + gr_null, /* calc_vram */ + gr_null, /* wait_vsync */ + gr_null, /* wait_display */ + gr_null, /* sub_bm */ + gr_null, /* cut_screen */ + gr_null, /* set_pal */ + gr_null /* get_pal */ +}; +#endif /* INCLUDE_GEN_FUNC_TABLES */ diff --git a/engine/src/Libraries/2D/Source/Gen/general.h b/engine/src/Libraries/2D/Source/Gen/general.h new file mode 100644 index 0000000..1530ddc --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/general.h @@ -0,0 +1,501 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/general.h $ + * $Revision: 1.51 $ + * $Author: kevin $ + * $Date: 1994/11/12 02:19:47 $ + * + * Prototypes for general purpose 2d functions. + * + * This file is part of the 2d library. + * + * $Log: general.h $ + * Revision 1.51 1994/11/12 02:19:47 kevin + * Added interrupt crunchy pixel setting declarations. + * + * Revision 1.50 1994/10/25 15:11:36 kevin + * Added rsd blitters. + * + * Revision 1.49 1994/08/16 15:30:55 kevin + * Added more temp functions. + * + * Revision 1.48 1994/07/29 12:02:15 kevin + * added per_map function. + * + * Revision 1.47 1994/07/18 17:05:49 kevin + * Moved temp_ functions from flat8.h to general.h. + * + * Revision 1.46 1994/07/04 01:35:59 kevin + * Added per_umap declaration. + * + * + * Revision 1.45 1994/06/20 22:19:31 kevin + * Added temp_wall_umap. + * + * Revision 1.44 1994/05/24 23:53:22 kevin + * gen_tluc8_clut_lin_umap yay! + * + * Revision 1.43 1994/04/09 07:22:30 lmfeeney + * added routines for scaled strings + * + * Revision 1.42 1994/03/15 06:08:10 kevin + * Added clut_bitmap procedures. + * + * Revision 1.41 1994/02/14 20:38:37 baf + * Added dummy parameter to cpoly and spoly routines, for uniformity needed by 3D. + * + * Revision 1.40 1994/01/17 22:13:18 baf + * Redid tluc8 spolys (again). + * + * Revision 1.39 1993/12/30 11:04:29 baf + * non/span solid filled polygons + * + * Revision 1.38 1993/12/28 22:04:37 baf + * Added solid RSD stuff + * + * Revision 1.37 1993/12/28 19:33:26 kevin + * Added unpack and chain versions of rsd bitmap functions. + * + * Revision 1.36 1993/12/06 13:08:48 kevin + * Added declarations for rsd8 versions of texture mappers. + * + * Revision 1.35 1993/12/04 17:31:23 kevin + * Added gen_clut_per_[u]map declarations; + * Fixed gen_lit_per_map declaration; + * + * Revision 1.34 1993/12/02 14:35:26 baf + * Added generic tluc8 scaled bitmaps + * + * Revision 1.33 1993/12/01 21:18:36 baf + * Added some tluc8 stuff. + * + * Revision 1.32 1993/11/24 01:50:13 kevin + * Added declarations for wall and floor texture mapping primitives. + * + * Revision 1.31 1993/10/26 02:10:15 kevin + * Added prototypes for rsd bitmap scaling and clut-scaling primitives. + * + * Revision 1.30 1993/10/20 15:20:43 kaboom + * Updated prototypes for spoly routines. + * + * Revision 1.29 1993/10/19 09:56:05 kaboom + * Updated names of polygon routines. + * + * Revision 1.28 1993/10/06 13:30:28 kevin + * added clut versions of horizontal flip and linear mapping routines. + * + * Revision 1.27 1993/10/02 01:18:21 kaboom + * Updated names and arguments of linear and perspective mappers. + * + * Revision 1.26 1993/09/02 20:05:23 kaboom + * Added prototypes for flat 24 pixel, bitmap and linmap routines. + * + * Revision 1.25 1993/08/19 21:51:09 jaemz + * Added bitmap scale clut and voxel functions + * + * Revision 1.24 1993/08/10 19:07:11 kaboom + * Added prototypes for gen_lit_{u}tmap(). + * + * Revision 1.23 1993/07/20 16:24:52 jaemz + * Added interp2 and filterw2 + * + * Revision 1.22 1993/06/22 19:58:41 spaz + * Added prototypes for goroud-shaded lines: + * gen_fix_usline, sline, ucline, and cline. + * + * Revision 1.21 1993/06/14 14:09:55 kaboom + * Added prototypes for new lin_{u}map and lin_lit_lin_{u}map routines. + * + * Revision 1.20 1993/06/06 15:12:21 kaboom + * Added prototypes for gen_hflip_flat8_{u}bitmap. + * + * Revision 1.19 1993/06/03 15:12:20 kaboom + * Moved prototypes for span functions to another file. + * + * Revision 1.18 1993/06/01 13:49:32 kaboom + * Added prototypes for lighting perspective tmappers. + * + * Revision 1.17 1993/05/03 13:50:48 kaboom + * Removed the declarations for obsolete span rendering routines. + * + * Revision 1.16 1993/04/08 18:56:04 kaboom + * Added prototypes for gen_uchar() and gen_char(). + * + * Revision 1.15 1993/04/01 21:51:42 kaboom + * Added full arguments to polygon shader & tmap prototypes. + * + * Revision 1.14 1993/03/29 18:30:59 kaboom + * Removed prototypes for concave polygons. + * + * Revision 1.13 1993/02/24 11:04:09 kaboom + * Added prototypes for new span routines. + * + * Revision 1.12 1993/02/04 17:30:23 kaboom + * Added prototypes for gen_xxx_cpoly() functions. + * + * Revision 1.11 1993/01/22 19:51:17 kaboom + * Filled in args to gen_int_tmap() macro. + * + * Revision 1.10 1993/01/15 21:44:43 kaboom + * Added prototypes for stencilled lr drawing routines. + * + * Revision 1.9 1992/12/30 15:14:50 kaboom + * Added parameters for polygon and span rendering functions. + * + * Revision 1.8 1992/12/14 18:15:58 kaboom + * Added prototype for gen_clear(). + * + * Revision 1.7 1992/12/11 14:13:47 kaboom + * Added prototypes for gen_get_flat8_[u]bitmap. + * + * Revision 1.6 1992/12/10 11:21:22 kaboom + * Added prototypes for unclipped versions of span functions. + * + * Revision 1.5 1992/11/19 02:37:06 kaboom + * Added prototypes for unclipped versions of gen_scale_bitmap as well as + * gen_roll_bitmap. + * + * Revision 1.4 1992/11/12 13:50:11 kaboom + * Inserted arguments into prototypes for completed functions. Added prototypes + * for monochrome and rsd8 bitmap routines. + * + * Revision 1.3 1992/10/21 16:04:32 kaboom + * Changed protoypes to match new naming for integer & fixed point functions. + * + * Revision 1.2 1992/10/13 12:18:20 kaboom + * Added prototypes for fixed-point and integer versions of most 2d + * functions. + * + * Revision 1.1 1992/10/10 12:00:00 kaboom + * Initial revision. + */ + +#ifndef __GENERAL_H +#define __GENERAL_H +#include "plytyp.h" /* must fix */ + +/* the general-purpose driver has no entries for the following standard + functions: set_pixel, set_upixel, get_pixel, and get_upixel. These + functions must be defined; they are the minimal components of a driver. */ + +extern int gen_set_pixel(long color, short x, short y); +extern int gen_set_pixel_interrupt(long color, short x, short y); +extern void gen_fill_upixel(long color, short x, short y); +extern int gen_fill_pixel(long color, short x, short y); + +extern void gen_clear (long color); + +extern void gen_upoint (short x, short y); +extern int gen_point (short x, short y); +extern void gen_uhline (short x0, short y0, short x1); +extern int gen_hline (short x0, short y0, short x1); +extern void gen_uvline (short x0, short y0, short y1); +extern int gen_vline (short x0, short y0, short y1); +extern void gen_urect (short left, short top, short right, short bot); +extern int gen_rect (short left, short top, short right, short bot); +extern void gen_ubox (short left, short top, short right, short bot); +extern int gen_box (short left, short top, short right, short bot); + +extern void gen_fix_uline (fix x0, fix y0, fix x1, fix y1); +extern int gen_fix_line (fix x0, fix y0, fix x1, fix y1); +extern void gen_fix_usline(fix x0, fix y0, fix i0, fix x1, fix y1, fix i1); +extern int gen_fix_sline(fix x0, fix y0, fix i0, fix x1, fix y1, fix i1); +extern void gen_fix_ucline(fix x0, fix y0, grs_rgb c0, fix x1, fix y1, grs_rgb c1); +extern int gen_fix_cline(fix x0, fix y0, grs_rgb c0, fix x1, fix y1, grs_rgb c1); +extern void gen_upoly (long c, int n, grs_vertex **vpl); +extern int gen_poly (long c, int n, grs_vertex **vpl); +extern void gen_uspoly(long c, int n, grs_vertex **vpl); +extern int gen_spoly(long c, int n, grs_vertex **vpl); +extern void gen_tluc8_upoly (long c, int n, grs_vertex **vpl); +extern int gen_tluc8_poly (long c, int n, grs_vertex **vpl); +extern void gen_tluc8_uspoly (long c, int n, grs_vertex **vpl); +extern int gen_tluc8_spoly (long c, int n, grs_vertex **vpl); + +extern void gen_ucpoly(long c, int n, grs_vertex **vpl); +extern int gen_cpoly(long c, int n, grs_vertex **vpl); + +extern int gen_fix_cpoly (int n, fix *vlist, grs_rgb *c); +extern void gen_vox_rect(fix x[4],fix y[4],fix dz[3],int near_ver,grs_bitmap *col,grs_bitmap *ht,int dotw,int doth); +extern void gen_vox_poly(fix x[4],fix y[4],fix dz[3],int near_ver,grs_bitmap *col,grs_bitmap *ht); +extern void gen_vox_cpoly(fix x[4],fix y[4],fix dz[3],int near_ver,grs_bitmap *col,grs_bitmap *ht); +extern void gen_interp2_ubitmap(grs_bitmap *bm); +extern void gen_filter2_ubitmap(grs_bitmap *bm); +extern void gen_scale_ubitmap + (grs_bitmap *bm, short x, short y, short w, short h); +extern int gen_scale_bitmap + (grs_bitmap *bm, short x, short y, short w, short h); +extern void gen_rsd8_scale_ubitmap + (grs_bitmap *bm, short x, short y, short w, short h); +extern int gen_rsd8_scale_bitmap + (grs_bitmap *bm, short x, short y, short w, short h); +extern void unpack_rsd8_scale_ubitmap + (grs_bitmap *bm, short x, short y, short w, short h); +extern int unpack_rsd8_scale_bitmap + (grs_bitmap *bm, short x, short y, short w, short h); +extern void gen_tluc8_scale_ubitmap + (grs_bitmap *bm, short x, short y, short w, short h); +// extern gen_tluc8_scale_bitmap +// (grs_bitmap *bm, short x, short y, short w, short h); + +extern void gen_rsd8_clut_scale_ubitmap + (grs_bitmap *bm, short x, short y, short w, short h, uchar *cl); +extern int gen_rsd8_clut_scale_bitmap + (grs_bitmap *bm, short x, short y, short w, short h, uchar *cl); +extern void gen_rsd8_scale_solid_ubitmap + (grs_bitmap *bm, short x, short y, short w, short h, int c); +extern int gen_rsd8_scale_solid_bitmap + (grs_bitmap *bm, short x, short y, short w, short h, int c); +extern void unpack_rsd8_clut_scale_ubitmap + (grs_bitmap *bm, short x, short y, short w, short h, uchar *cl); +extern int unpack_rsd8_clut_scale_bitmap + (grs_bitmap *bm, short x, short y, short w, short h, uchar *cl); +extern void gen_clut_scale_ubitmap + (grs_bitmap *bm, short x, short y, short w, short h, uchar *cl); +extern void gen_clut_scale_bitmap + (grs_bitmap *bm, short x, short y, short w, short h, uchar *cl); + +extern void gen_roll_ubitmap + (grs_bitmap *bm, fix angle, short x, short y); +extern void gen_roll_bitmap (); + +extern void gen_flat8_wall_umap + (grs_bitmap *bm, int n, fix **vpl); +extern void gen_flat8_lit_wall_umap + (grs_bitmap *bm, int n, fix **vpl); + +extern void gen_flat8_floor_umap + (grs_bitmap *bm, int n, fix **vpl); +extern void gen_flat8_lit_floor_umap + (grs_bitmap *bm, int n, fix **vpl); + +extern void temp_point(short x, short y); +extern void temp_upoint(short x, short y); + +extern void temp_flat8_ubitmap (grs_bitmap *bm, int x, int y); +extern void temp_flat8_bitmap (grs_bitmap *bm, int x, int y); + +extern void temp_flat8_mask_bitmap (grs_bitmap *bm, int x, int y, grs_stencil *sten); +extern void temp_flat8_clut_ubitmap (grs_bitmap *bm, int x, int y, uchar *cl); +extern void temp_rsd8_bitmap (grs_bitmap *bm, int x, int y); +extern void temp_rsd8_ubitmap (grs_bitmap *bm, int x, int y); + +extern void temp_tluc8_ubitmap (grs_bitmap *bm, int x, int y); + +extern int temp_poly (long c, int n, grs_vertex **vpl); +extern void temp_upoly (long c, int n, grs_vertex **vpl); +extern int temp_spoly (long c, int n, grs_vertex **vpl); +extern void temp_uspoly (long c, int n, grs_vertex **vpl); +extern int temp_cpoly (long c, int n, grs_vertex **vpl); +extern void temp_ucpoly (long c, int n, grs_vertex **vpl); +extern int temp_tpoly (long c, int n, grs_vertex **vpl); +extern void temp_utpoly (long c, int n, grs_vertex **vpl); +extern int temp_stpoly (long c, int n, grs_vertex **vpl); +extern void temp_ustpoly (long c, int n, grs_vertex **vpl); + + +extern void temp_lin_umap + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern void temp_lin_map + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern void temp_lit_lin_umap + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern void temp_lit_lin_map + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern void temp_clut_lin_umap + (grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); +extern void temp_clut_lin_map + (grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); + +extern void temp_wall_umap + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern void temp_lit_wall_umap + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern void temp_clut_wall_umap + (grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); + +extern void temp_floor_umap + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern void temp_lit_floor_umap + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern void temp_clut_floor_umap + (grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); + +extern void temp_per_umap + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern void temp_per_map + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern void temp_lit_per_umap + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern void temp_clut_per_umap + (grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); +extern void temp_clut_per_map + (grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); + +extern void temp_scale_umap + (grs_bitmap *bm, short x, short y, short w, short h); +extern int temp_scale_map + (grs_bitmap *bm, short x, short y, short w, short h); +extern void temp_clut_scale_umap + (grs_bitmap *bm, short x, short y, short w, short h, uchar *cl); +extern int temp_clut_scale_map + (grs_bitmap *bm, short x, short y, short w, short h, uchar *cl); + +extern void gen_per_umap + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern int gen_per_map + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern void gen_lit_per_umap + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern int gen_lit_per_map + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern void gen_clut_per_umap + (grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); +extern int gen_clut_per_map + (grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); +extern void gen_solid_per_umap + (grs_bitmap *bm, int n, grs_vertex **vpl, int c); +extern int gen_solid_per_map + (grs_bitmap *bm, int n, grs_vertex **vpl, int c); + +extern void gen_rsd8_per_umap + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern int gen_rsd8_per_map + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern void gen_rsd8_lit_per_umap + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern int gen_rsd8_lit_per_map + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern void gen_rsd8_clut_per_umap + (grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); +extern int gen_rsd8_clut_per_map + (grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); + +extern void gen_lin_lit_utmap + (int n, fix *vlist, grs_bitmap *bm, fix *m, fix *l); +extern int gen_lin_lit_tmap + (int n, fix *vlist, grs_bitmap *bm, fix *m, fix *l); +extern void gen_bilin_lit_utmap + (int n, fix *vlist, grs_bitmap *bm, fix *m, fix *l); +extern int gen_bilin_lit_tmap + (int n, fix *vlist, grs_bitmap *bm, fix *m, fix *l); + +extern void gen_flat8_lin_umap + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern int gen_flat8_lin_map + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern void gen_flat24_lin_umap + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern int gen_flat24_lin_map + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern void gen_rsd8_lin_umap + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern int gen_rsd8_lin_map + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern void gen_tluc8_lin_umap + (grs_bitmap *bm, int n, grs_vertex **vpl); +extern int gen_tluc8_lin_map + (grs_bitmap *bm, int n, grs_vertex **vpl); + +extern void gen_lit_lin_umap + (int n, grs_bitmap *bm, grs_vertex **vpl); +extern int gen_lit_lin_map + (int n, grs_bitmap *bm, grs_vertex **vpl); + +extern void gen_clut_lin_umap + (grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); +extern int gen_clut_lin_map + (grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); +extern void gen_tluc8_clut_lin_umap + (grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); +extern void gen_flat8_solid_lin_umap + (grs_bitmap *bm, int n, grs_vertex **vpl, int c); +extern int gen_flat8_solid_lin_map + (grs_bitmap *bm, int n, grs_vertex **vpl, int c); + +extern void gen_rsd8_lit_lin_umap + (int n, grs_bitmap *bm, grs_vertex **vpl); +extern int gen_rsd8_lit_lin_map + (int n, grs_bitmap *bm, grs_vertex **vpl); + +extern void gen_rsd8_clut_lin_umap + (grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); +extern int gen_rsd8_clut_lin_map + (grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); + +extern void gen_int_ucircle (short x, short y, short r); +extern int gen_int_circle (short x0, short y0, short r); +extern void gen_fix_ucircle (void); +extern void gen_fix_circle (void); +extern void gen_int_udisk (short x0, short y0, short r); +extern void gen_int_disk (short x0, short y0, short r); +extern void gen_fix_udisk (void); +extern void gen_fix_disk (void); +extern void gen_int_urod (void); +extern void gen_int_rod (void); +extern void gen_fix_urod (void); +extern void gen_fix_rod (void); + +extern void gen_rsd8_solid_ubitmap (grs_bitmap *bm, short x, short y, int c); +extern int gen_rsd8_solid_bitmap (grs_bitmap *bm, short x, short y, int c); + +/* bitmap drawing functions. */ +extern void gen_mono_ubitmap (grs_bitmap *bm, short x, short y); +extern int gen_mono_bitmap (grs_bitmap *bm, short x, short y); +extern void gen_flat8_ubitmap (grs_bitmap *bm, short x, short y); +extern int gen_flat8_bitmap (grs_bitmap *bm, short x, short y); +extern void gen_flat24_ubitmap (grs_bitmap *bm, short x0, short y0); +extern int gen_flat24_bitmap (grs_bitmap *bm, short x0, short y0); +extern void gri_gen_rsd8_ubitmap (grs_bitmap *bm, short x, short y); +extern int gri_gen_rsd8_bitmap (grs_bitmap *bm, short x, short y); +extern void unpack_rsd8_ubitmap (grs_bitmap *bm, short x, short y); +extern int unpack_rsd8_bitmap (grs_bitmap *bm, short x, short y); +extern void gen_tluc8_ubitmap (grs_bitmap *bm, short x, short y); +extern int gen_tluc8_bitmap (grs_bitmap *bm, short x, short y); + +/* clut bitmap drawing functions. */ +extern void gen_flat8_clut_ubitmap (grs_bitmap *bm, short x, short y, uchar *clut); +extern int gen_flat8_clut_bitmap (grs_bitmap *bm, short x, short y, uchar *clut); +extern void unpack_rsd8_clut_ubitmap (grs_bitmap *bm, short x, short y, uchar *clut); +extern int unpack_rsd8_clut_bitmap (grs_bitmap *bm, short x, short y, uchar *clut); + +/* bitmap get functions. */ +extern void gen_get_flat8_ubitmap (grs_bitmap *bm, short x, short y); +extern int gen_get_flat8_bitmap (grs_bitmap *bm, short x, short y); + +/* bitmap horizontal flip routines. */ +extern void gen_hflip_flat8_ubitmap (grs_bitmap *bm, short x, short y); +extern int gen_hflip_flat8_bitmap (grs_bitmap *bm, short x, short y); + +/* bitmap color lookup table horizontal flip routines. */ +extern void gen_clut_hflip_flat8_ubitmap (grs_bitmap *bm, short x, short y, uchar *cl); +extern int gen_clut_hflip_flat8_bitmap (grs_bitmap *bm, short x, short y, uchar *cl); + +extern void gen_font_ustring (grs_font *f, char *s, short x, short y); +extern int gen_font_string (grs_font *f, char *s, short x, short y); + +extern void gen_font_scale_ustring (grs_font *f, char *s, short x, short y, short w, short h); +extern int gen_font_scale_string (grs_font *f, char *s, short x, short y, short w, short h); + +extern void gen_font_uchar (grs_font *f, char c, short x, short y); +extern int gen_font_char (grs_font *f, char c, short x, short y); + +//extern void gen_opaque_ubitmap (grs_bitmap *bm, short x, short y); + +#endif /* !__GENERAL_H */ diff --git a/engine/src/Libraries/2D/Source/Gen/genf24.c b/engine/src/Libraries/2D/Source/Gen/genf24.c new file mode 100644 index 0000000..147b06f --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genf24.c @@ -0,0 +1,94 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genf24.c $ + * $Revision: 1.3 $ + * $Author: kaboom $ + * $Date: 1993/10/02 01:17:21 $ + * + * Generic routines to draw a flat 24 bitmap. + * + * $Log: genf24.c $ + * Revision 1.3 1993/10/02 01:17:21 kaboom + * Changed include of clip.h to include of clpcon.h and/or clpfcn.h. + * + * Revision 1.2 1993/09/02 20:34:38 kaboom + * Now gen_flat24_bitmap() returns clip code. + * + * Revision 1.1 1993/09/02 20:07:31 kaboom + * Initial revision + */ + +#include "grs.h" +#include "bitmap.h" +#include "clpcon.h" +#include "clpfcn.h" +#include "grdbm.h" +#include "grp24.h" + +#if 0 +// MLA - this doesn't appear to be used anywhere +void memmove (uchar *dst, uchar *src, int n); +#pragma aux memmove = \ + "mov eax,ecx" \ + "shr ecx,2" \ + "rep movsd" \ + "mov ecx,eax" \ + "and ecx,3" \ + "rep movsb" \ + parm [edi] [esi] [ecx] \ + modify [eax ecx edi esi]; +#endif + +void gen_flat24_ubitmap (grs_bitmap *bm, short x0, short y0) +{ + short x, y; + uchar *p, *lp; + + p = bm->bits; + if (bm->flags & BMF_TRANS) + for (y=y0; yh; y++) { + lp = p; + for (x=x0; xw; x++, p+=3) + if (*((long *)p) & 0x00ffffff) + gr_set_upixel24 (*((long *)p)&0x00ffffff, x, y); + p = lp+bm->row; + } + else + for (y=y0; yh; y++) { + lp = p; + for (x=x0; xw; x++, p+=3) + gr_set_upixel24 (*((long *)p)&0x00ffffff, x, y); + p = lp+bm->row; + } +} + +int gen_flat24_bitmap (grs_bitmap *bm, short x0, short y0) +{ + int r; + short w,h; + uchar *b; + + b = bm->bits; w = bm->w; h = bm->h; + r = gr_clip_flat24_bitmap (bm, &x0, &y0); + if (r != CLIP_ALL) + gr_flat24_ubitmap (bm, x0, y0); + bm->bits = b; bm->w = w; bm->h = h; + return r; +} diff --git a/engine/src/Libraries/2D/Source/Gen/genfl8.c b/engine/src/Libraries/2D/Source/Gen/genfl8.c new file mode 100644 index 0000000..72ea233 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genfl8.c @@ -0,0 +1,142 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/genfl8.c $ + * $Revision: 1.7 $ + * $Author: kevin $ + * $Date: 1994/11/01 11:45:21 $ + * + * Generic flat 8 bitmap routines. + * + * $Log: genfl8.c $ + * Revision 1.7 1994/11/01 11:45:21 kevin + * Don't try to draw bitmaps with height or width of zero. + * + * Revision 1.6 1994/08/16 13:06:23 kevin + * gen_flat8_bitmap chains to gr_bitmap instead of gr_flat8_bitmap + * so it may now be used with translucent bitmaps as well. + * + * Revision 1.5 1993/10/19 09:51:11 kaboom + * Replaced #include "grd.h" with new headers split from grd.h. + * + * Revision 1.4 1993/10/02 01:17:22 kaboom + * Changed include of clip.h to include of clpcon.h and/or clpfcn.h. + * + * Revision 1.3 1993/06/04 10:28:04 kaboom + * Inlined clipping code so clipped bitmap can be called from an + * interrupt service routine. + * + * Revision 1.2 1993/04/29 18:40:27 kaboom + * Changed include of gr.h to smaller more specific grxxx.h. + * + * Revision 1.1 1993/02/16 15:42:37 kaboom + * Initial revision + * + ******************************************************************** + * Log from old general.c: + * + * Revision 1.9 1992/12/14 18:13:37 kaboom + * Fixed bug in gen_flat8_bitmap -- was checking result of clip before + * doing the clip. + * + * Revision 1.7 1992/12/11 13:58:09 kaboom + * Changed all the calls from gr_set_pixel to gr_set_upixel in primitives + * that have analytic clipping. Changed chain in gen_mono_bitmap() + * from general to specific (e.g., gr_ubitmap->gr_mono_ubitmap). + */ + +#include "bitmap.h" +#include "clpcon.h" +#include "cnvdat.h" +#include "grdbm.h" +#include "grpix.h" + +#include // printf() + +/* bozo flat8 bitmap drawer. */ +void gen_flat8_ubitmap (grs_bitmap *bm, short x, short y) +{ + uchar *src = bm->bits; + short right = x+bm->w; + short bot = y+bm->h; + short cur_x; + + if (bm->flags & BMF_TRANS) { + for ( ; yrow-bm->w) { + for (cur_x=x ; cur_xrow, bm->w); + for ( ; yrow-bm->w) { + for (cur_x=x ; cur_xw; h = bm->h; p = bm->bits; + + /* check for trivial reject. */ + if (x+bm->w=grd_clip.right || + y+bm->h=grd_clip.bot) { + return CLIP_ALL; + } + + /* clip & draw that sucker. */ + if (x < grd_clip.left) { /* off left edge */ + extra = grd_clip.left - x; + bm->w -= extra; + bm->bits += extra; + x = grd_clip.left; + code |= CLIP_LEFT; + } + if (x+bm->w > grd_clip.right) { /* off right edge */ + bm->w -= x+bm->w-grd_clip.right; + code |= CLIP_RIGHT; + } + if (y < grd_clip.top) { /* off top */ + extra = grd_clip.top - y; + bm->h -= extra; + bm->bits += bm->row*extra; + y = grd_clip.top; + code |= CLIP_TOP; + } + if (y+bm->h > grd_clip.bot) { /* off bottom */ + bm->h -= y+bm->h-grd_clip.bot; + code |= CLIP_BOT; + } + if ((bm->h>0)&&(bm->w>0)) + gr_ubitmap (bm, x, y); + + /* restore bitmap to normal. */ + bm->w = w; bm->h = h; bm->bits = p; + return code; +} diff --git a/engine/src/Libraries/2D/Source/Gen/genfl8c.c b/engine/src/Libraries/2D/Source/Gen/genfl8c.c new file mode 100644 index 0000000..d20314f --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genfl8c.c @@ -0,0 +1,108 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genfl8c.c $ + * $Revision: 1.1 $ + * $Author: kevin $ + * $Date: 1994/03/15 13:15:15 $ + * + * Generic flat8 clut bitmap routines. + * + * $Log: genfl8c.c $ + * Revision 1.1 1994/03/15 13:15:15 kevin + * Initial revision + * + */ + +#include "scrdat.h" +#include "bitmap.h" +#include "clpcon.h" +#include "cnvdat.h" +#include "grcbm.h" +#include "grpix.h" + +/* bozo flat8 bitmap drawer. */ +void gen_flat8_clut_ubitmap (grs_bitmap *bm, short x, short y, uchar *cl) +{ + uchar *src = bm->bits; + short right = x+bm->w; + short bot = y+bm->h; + short cur_x; + + if (cl==NULL) cl=grd_screen->clut; + if (bm->flags & BMF_TRANS) { + for ( ; yrow-bm->w) { + for (cur_x=x ; cur_xrow-bm->w) { + for (cur_x=x ; cur_xw; h = bm->h; p = bm->bits; + + /* check for trivial reject. */ + if (x+bm->w=grd_clip.right || + y+bm->h=grd_clip.bot) + return CLIP_ALL; + + /* clip & draw that sucker. */ + if (x < grd_clip.left) { /* off left edge */ + extra = grd_clip.left - x; + bm->w -= extra; + bm->bits += extra; + x = grd_clip.left; + code |= CLIP_LEFT; + } + if (x+bm->w > grd_clip.right) { /* off right edge */ + bm->w -= x+bm->w-grd_clip.right; + code |= CLIP_RIGHT; + } + if (y < grd_clip.top) { /* off top */ + extra = grd_clip.top - y; + bm->h -= extra; + bm->bits += bm->row*extra; + y = grd_clip.top; + code |= CLIP_TOP; + } + if (y+bm->h > grd_clip.bot) { /* off bottom */ + bm->h -= y+bm->h-grd_clip.bot; + code |= CLIP_BOT; + } + gr_flat8_clut_ubitmap (bm, x, y, cl); + + /* restore bitmap to normal. */ + bm->w = w; bm->h = h; bm->bits = p; + return code; +} diff --git a/engine/src/Libraries/2D/Source/Gen/gengfl8.c b/engine/src/Libraries/2D/Source/Gen/gengfl8.c new file mode 100644 index 0000000..798cf7b --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/gengfl8.c @@ -0,0 +1,126 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/gengfl8.c $ + * $Revision: 1.6 $ + * $Author: kevin $ + * $Date: 1994/10/19 17:33:13 $ + * + * Generic flat 8 bitmap capture routines. + * + * $Log: gengfl8.c $ + * Revision 1.6 1994/10/19 17:33:13 kevin + * Save and restore 2d state in case we are run in an interrupt. + * + * Revision 1.5 1993/12/15 11:26:45 kaboom + * Inlined clipping code so can be called from an interrupt. + * + * Revision 1.4 1993/10/19 09:51:13 kaboom + * Replaced #include "grd.h" with new headers split from grd.h. + * + * Revision 1.3 1993/10/02 01:17:23 kaboom + * Changed include of clip.h to include of clpcon.h and/or clpfcn.h. + * + * Revision 1.2 1993/04/29 18:40:35 kaboom + * Changed include of gr.h to smaller more specific grxxx.h. + * + * Revision 1.1 1993/02/16 15:42:44 kaboom + * Initial revision + */ + +#include "bitmap.h" +#include "clpcon.h" +#include "cnvdat.h" +#include "grgbm.h" +#include "grpix.h" +#include "grstate.h" + +/* unclipped flat8 bitmap capture. reads data from the current canvas at + (x,y) into the bitmap described by bm. the destination bitmap is always + completely filled. */ +void gen_get_flat8_ubitmap (grs_bitmap *bm, short x, short y) +{ + uchar *dst = bm->bits; + short right = x+bm->w; + short bot = y+bm->h; + short cur_x; + + gr_push_state(); + if (bm->flags & BMF_TRANS) { + for ( ; yrow-bm->w) { + for (cur_x=x ; cur_xrow-bm->w) { + for (cur_x=x ; cur_xw; h = bm->h; p = bm->bits; + + /* check for trivial reject. */ + if (x+bm->w=grd_clip.right || + y+bm->h=grd_clip.bot) + return CLIP_ALL; + + /* clip & draw that sucker. */ + if (x < grd_clip.left) { /* off left edge */ + extra = grd_clip.left - x; + bm->w -= extra; + bm->bits += extra; + x = grd_clip.left; + code |= CLIP_LEFT; + } + if (x+bm->w > grd_clip.right) { /* off right edge */ + bm->w -= x+bm->w-grd_clip.right; + code |= CLIP_RIGHT; + } + if (y < grd_clip.top) { /* off top */ + extra = grd_clip.top - y; + bm->h -= extra; + bm->bits += bm->row*extra; + y = grd_clip.top; + code |= CLIP_TOP; + } + if (y+bm->h > grd_clip.bot) { /* off bottom */ + bm->h -= y+bm->h-grd_clip.bot; + code |= CLIP_BOT; + } + gr_get_ubitmap (bm, x, y); + + /* restore bitmap to normal. */ + bm->w = w; bm->h = h; bm->bits = p; + return code; +} diff --git a/engine/src/Libraries/2D/Source/Gen/genhfl8.c b/engine/src/Libraries/2D/Source/Gen/genhfl8.c new file mode 100644 index 0000000..486671d --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genhfl8.c @@ -0,0 +1,110 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genhfl8.c $ + * $Revision: 1.3 $ + * $Author: kaboom $ + * $Date: 1993/10/19 09:51:14 $ + * + * Generic flat 8 bitmap horizontal flip routine. + * + * This file is part of the 2d library. + * + * $Log: genhfl8.c $ + * Revision 1.3 1993/10/19 09:51:14 kaboom + * Replaced #include "grd.h" with new headers split from grd.h. + * + * Revision 1.2 1993/10/02 01:17:24 kaboom + * Changed include of clip.h to include of clpcon.h and/or clpfcn.h. + * + * Revision 1.1 1993/06/06 15:09:57 kaboom + * Initial revision + */ + +#include "clpcon.h" +#include "cnvdat.h" +#include "grpix.h" +#include "grhbm.h" + +/* draw an unclipped, horizontally flipped flat 8 bitmap to a + canvas. */ +void gen_hflip_flat8_ubitmap (grs_bitmap *bm, short x, short y) +{ + short r; /* right x coordinate */ + short b; /* bottom y coordinate */ + short cur_x; /* current x */ + uchar *src; /* pointer into source bitmap */ + + r = x+bm->w-1; + b = y+bm->h-1; + src = bm->bits; + for ( ; y<=b; y++, src+=bm->row-bm->w) + for (cur_x=r; cur_x>=x; cur_x--, src++) + gr_set_upixel (*src, cur_x, y); +} + +/* draw a clipped, horizontally flipped flat 8bitmap to a canvas. */ +int gen_hflip_flat8_bitmap (grs_bitmap *bm, short x, short y) +{ + short w,h; + uchar *p; + short r; + short b; + int extra; + int code = CLIP_NONE; + + r = x+bm->w-1; + b = y+bm->h-1; + + /* save stuff that clipping changes. */ + w = bm->w; h = bm->h; p = bm->bits; + + /* first check for trivial reject. */ + if (x>=grd_clip.right || r=grd_clip.bot || bw -= grd_clip.left-x; + x = grd_clip.left; + code |= CLIP_LEFT; + } + if (r >= grd_clip.right) { /* off right */ + extra = r-grd_clip.right+1; + bm->w -= extra; + bm->bits += extra; + code |= CLIP_RIGHT; + } + if (y < grd_clip.top) { /* off top */ + extra = grd_clip.top - y; + bm->h -= extra; + bm->bits += bm->row*extra; + y = grd_clip.top; + code |= CLIP_TOP; + } + if (b >= grd_clip.bot) { /* off bottom */ + bm->h -= b-grd_clip.bot+1; + code |= CLIP_BOT; + } + gr_hflip_flat8_ubitmap (bm, x, y); + + /* restore bitmap to normal. */ + bm->w = w; bm->h = h; bm->bits = p; + return code; +} diff --git a/engine/src/Libraries/2D/Source/Gen/genhlin.c b/engine/src/Libraries/2D/Source/Gen/genhlin.c new file mode 100644 index 0000000..38610f2 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genhlin.c @@ -0,0 +1,92 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genhlin.c $ + * $Revision: 1.6 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 02:27:08 $ + * + * Generic routines for drawing horizontal lines. + * + * This file is part of the 2d library. + * + * $Log: genhlin.c $ + * Revision 1.6 1994/06/11 02:27:08 lmfeeney + * moved unclipped line drawer, two versions of clipped line drawer, + * one for each i\f - do clipping inline, then call unclipped drawer + * handles all fill types + * + * Revision 1.5 1993/10/19 09:51:14 kaboom + * Replaced #include "grd.h" with new headers split from grd.h. + * + * Revision 1.4 1993/10/02 01:17:25 kaboom + * Changed include of clip.h to include of clpcon.h and/or clpfcn.h. + * + * Revision 1.3 1993/04/29 18:40:36 kaboom + * Changed include of gr.h to smaller more specific grxxx.h. + * + * Revision 1.2 1993/03/02 19:45:58 kaboom + * Changed to not draw the rightmost pixel. + * + * Revision 1.1 1993/02/25 23:10:59 kaboom + * Initial revision + */ + +#include "ctxmac.h" +#include "clpcon.h" +#include "grlin.h" + +/* draw a clipped horizontal line with integral coordinates. returns a clip + code. */ + +int gen_hline (short x0, short y0, short x1) +{ + int r; + + r = grd_hline_clip_fill (x0, y0, x1, gr_get_fcolor(), gr_get_fill_parm()); + + return r; +} + +int gri_hline_clip_fill (short x0, short y0, short x1, long c, long parm) +{ + int r = CLIP_NONE; + short t; + + if (x0 > x1) { + t = x0; x0 = x1; x1 = t; + } + if (y0=grd_clip.bot || + x1=grd_clip.right) + return CLIP_ALL; /* forget about return values */ + + if (x0 < grd_clip.left) { + r |= CLIP_LEFT; + x0 = grd_clip.left; + } + if (x1 >= grd_clip.right) { + r |= CLIP_RIGHT; + x1 = grd_clip.right-1; + } + + if (r != CLIP_ALL) + grd_uhline_fill (x0, y0, x1, c, parm); + + return r; +} diff --git a/engine/src/Libraries/2D/Source/Gen/genlin.c b/engine/src/Libraries/2D/Source/Gen/genlin.c new file mode 100644 index 0000000..67db507 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genlin.c @@ -0,0 +1,109 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +/* + * $Source: n:/project/lib/src/2d/RCS/genlin.c $ + * $Revision: 1.13 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 02:26:12 $ + * + * Generic routines for drawing fixed-point lines. + * + * This file is part of the 2d library. + * + * $Log: genlin.c $ + * Revision 1.13 1994/06/11 02:26:12 lmfeeney + * moved unclipped drawer, now contains two versions of + * clipped line drawer, one for each i\f - call clipper + * and call unclipped line drawer + * + * Revision 1.12 1994/05/06 18:19:37 lmfeeney + * rewritten for greater accuracy + * + * Revision 1.11 1993/12/15 11:25:52 kaboom + * Fixed up problems with not including endpoints and matching up with + * new polygon scanner. + * + * Revision 1.10 1993/10/19 09:51:15 kaboom + * Replaced #include "grd.h" with new headers split from grd.h. + * + * Revision 1.9 1993/10/02 01:17:26 kaboom + * Changed include of clip.h to include of clpcon.h and/or clpfcn.h. + * + * Revision 1.8 1993/06/23 04:56:43 kaboom + * Put in checks for single-pixel horizontal and vertical lines. + * + * Revision 1.6 1993/06/22 15:14:56 kaboom + * Now checks to see if final span is empty. + * + * Revision 1.5 1993/06/16 02:00:39 kaboom + * Fixed gradual precision error. Last span now explicitly set to x1 + * instead of adding m_inv. + * + * Revision 1.4 1993/04/29 18:40:33 kaboom + * Changed include of gr.h to smaller more specific grxxx.h. + * + * Revision 1.3 1993/03/03 17:58:48 kaboom + * Fixed bugs where y was not initialized. + * + * Revision 1.2 1993/03/02 20:34:11 kaboom + * Changed algorithm to match polygon edge scanner. + * + * Revision 1.1 1993/02/25 23:10:44 kaboom + * Initial revision + */ + +#include "ctxmac.h" +#include "clpcon.h" +#include "clpltab.h" +#include "grlin.h" + +/* draw a clipped fractional-precision line, call the fixed-point line + drawer with the preferred (v0, v1, fill) interface + */ + +int gen_fix_line (fix x0, fix y0, fix x1, fix y1) +{ + int r; + grs_vertex v0, v1; + + v0.x = x0; v0.y = y0; + v1.x = x1; v1.y = y1; + + r = grd_line_clip_fill (gr_get_fcolor(), gr_get_fill_parm(), &v0, &v1); + + return r; +} + +int gri_line_clip_fill (long c, long parm, grs_vertex *v0, grs_vertex *v1) +{ + int r; + grs_vertex u0, u1; + + u0.x = v0->x; u0.y = v0->y; + u1.x = v1->x; u1.y = v1->y; + + r = gri_line_clip (&u0, &u1); + + if (r != CLIP_ALL) + grd_uline_fill (c, parm, &u0, &u1); + + return r; +} + diff --git a/engine/src/Libraries/2D/Source/Gen/genmono.c b/engine/src/Libraries/2D/Source/Gen/genmono.c new file mode 100644 index 0000000..70ba7ce --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genmono.c @@ -0,0 +1,149 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genmono.c $ + * $Revision: 1.4 $ + * $Author: kaboom $ + * $Date: 1993/10/19 09:51:20 $ + * + * Generic monochrome bitmap routines. + * + * $Log: genmono.c $ + * Revision 1.4 1993/10/19 09:51:20 kaboom + * Replaced #include "grd.h" with new headers split from grd.h. + * + * Revision 1.3 1993/10/02 01:17:28 kaboom + * Changed include of clip.h to include of clpcon.h and/or clpfcn.h. + * + * Revision 1.2 1993/04/29 18:40:38 kaboom + * Changed include of gr.h to smaller more specific grxxx.h. + * + * Revision 1.1 1993/02/16 15:42:57 kaboom + * Initial revision + * + ******************************************************************** + * Log from old general.c: + * + * Revision 1.7 1992/12/11 13:58:09 kaboom + * Changed all the calls from gr_set_pixel to gr_set_upixel in primitives + * that have analytic clipping. Changed chain in gen_mono_bitmap() + * from general to specific (e.g., gr_ubitmap->gr_mono_ubitmap). + */ + +#include "bit.h" +#include "bitmap.h" +#include "clpcon.h" +#include "clpfcn.h" +#include "cnvdat.h" +#include "grdbm.h" +#include "grpix.h" + +/* draw a monochrome bitmap with calls to gr_set_pixel for maximum device + independence and slowness. draws 1's in the source bitmap as currently + set foreground, and 0's are bacground if opaque, or not drawn if trans- + parent. */ +void gen_mono_ubitmap (grs_bitmap *bm, short x, short y) +{ + short w, h; /* working width and height */ + short dst_x; /* destination x */ + int bit; /* bit from 0-7 in source byte */ + uchar *p_row; /* pointer to current row of bitmap */ + uchar *p; /* pointer to source byte */ + + h = bm->h; + p_row = bm->bits; + + if (bm->flags & BMF_TRANS) + { + /* transparent bitmap; draw 1's as fcolor, don't draw 0's. */ + while (h-- > 0) + { + /* set up scanline. */ + bit = bm->align; + dst_x = x; + p = p_row; + w = bm->w; + + while (w-- > 0) + { + /* do current scanline. */ + if (*p & bitmask[bit]) + gr_set_pixel (grd_gc.fcolor, dst_x, y); + dst_x++; + if (++bit > 7) + { + bit = 0; + p++; + } + } + + y++; + p_row += bm->row; + } + } + else + { + /* opaque bitmap; draw 1's as fcolor, 0's as bcolor. */ + while (h-- > 0) + { + bit = bm->align; + dst_x = x; + p = p_row; + w = bm->w; + + while (w-- > 0) + { + if (*p & bitmask[bit]) + gr_set_upixel (grd_gc.fcolor, dst_x, y); + else + gr_set_upixel (grd_gc.bcolor, dst_x, y); + dst_x++; + if (++bit > 7) + { + bit = 0; + p++; + } + } + + y++; + p_row += bm->row; + } + } +} + +/* clip monochrome bitmap against cliprect and jump to unclipped drawer. */ +int gen_mono_bitmap (grs_bitmap *bm, short x, short y) +{ + short w,h; + uchar align; + uchar *p; + int code = CLIP_NONE; + + /* save stuff that clipping changes. */ + w = bm->w; h = bm->h; align = bm->align; p = bm->bits; + + /* clip & draw that sucker. */ + code = gr_clip_mono_bitmap (bm, &x, &y); + if (code != CLIP_ALL) + gr_mono_ubitmap (bm, x, y); + + /* restore bitmap to normal. */ + bm->w = w; bm->h = h; bm->align = align; bm->bits = p; + return code; +} diff --git a/engine/src/Libraries/2D/Source/Gen/genov.c b/engine/src/Libraries/2D/Source/Gen/genov.c new file mode 100644 index 0000000..a0cbc2c --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genov.c @@ -0,0 +1,183 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/genov.c $ + * $Revision: 1.1 $ + * $Author: lmfeeney $ + * $Date: 1994/11/21 01:23:46 $ + */ + +#include "clpcon.h" +#include "cnvdat.h" +#include "grrect.h" +#include "fix.h" +#include "genov.h" + + +/* + * x-y oriented filled-elipse drawer, mostly from Foley and VanDam, + * but the clipping especially could be made much more efficient + * + * the elipse drawer should be canvas specific and in the tables, but it would + * be better to wait until other 2d.h changes */ + +void gr_int_uoval (int x0, int y0, int a, int b) +{ + int x; + int y; + + fix24 a_sq, b_sq; + fix24 d1,d2,t1,t2; + + a_sq = (a * a)<<8; + b_sq = (b * b)<<8; + + x = 0; + y = b; + + /* d1 = b_sq - a_sq*b + a_sq/4 */ + + d1 = b_sq - fix24_mul(a_sq,(b<<8)) + fix24_div(a_sq,(4<<8)); + + t1 = fix24_mul(a_sq,((y<<8)-128)); + t2 = fix24_mul(b_sq,((x+1)<<8)); + + while (t1 > t2) { + + if (d1 < 0) { + d1 += fix24_mul(b_sq,((x<<9)+(3<<8))); + x++; + } + else { + d1 += fix24_mul(b_sq,((x<<9)+(3<<8))); + d1 += fix24_mul(a_sq,(((-y)<<9)+(2<<8))); + x++; y--; + } + + gr_uhline((short)(x0-x),(short)(y0+y),(short)(x0+x)); + gr_uhline((short)(x0-x),(short)(y0-y),(short)(x0+x)); + + t1 = fix24_mul(a_sq,((y<<8)-128)); + t2 = fix24_mul(b_sq,((x+1)<<8)); + } + + t1 = fix24_mul(((x<<8)+128),((x<<8)+18)); + d2 = fix24_mul(t1,b_sq); + + t1 = fix24_mul(((y-1)<<8),((y-1)<<8)); + d2 += fix24_mul(t1,a_sq); + + t1 = fix24_mul(a_sq,b_sq); + d2 -= t1; + + while (y > 0) { + if (d2 < 0) { + t1 = fix24_mul(((x<<9)+(2<<8)),b_sq); + t2 = fix24_mul(((3<<8)-(y<<9)),a_sq); + d2 = d2 + t1 + t2; + x++; y--; + } + else { + t2 = fix24_mul(((3<<8)-(y<<9)),a_sq); + d2 += t2; + y--; + } + gr_uhline((short)(x0-x),(short)(y0+y),(short)(x0+x)); + gr_uhline((short)(x0-x),(short)(y0-y),(short)(x0+x)); + } +} + + +int gr_int_oval (int x0, int y0, int a, int b) +{ + int x; + int y; + + fix24 a_sq, b_sq; + fix24 d1,d2,t1,t2; + + /* trivial clipping */ + + if (x0+a<=grd_clip.left || x0-a>grd_clip.right || + y0+b<=grd_clip.top || y0-b>grd_clip.bot) + return CLIP_ALL; + + + a_sq = (a * a)<<8; + b_sq = (b * b)<<8; + + x = 0; + y = b; + + /* d1 = b_sq - a_sq*b + a_sq/4 */ + + d1 = b_sq - fix24_mul(a_sq,(b<<8)) + fix24_div(a_sq,(4<<8)); + + t1 = fix24_mul(a_sq,((y<<8)-128)); + t2 = fix24_mul(b_sq,((x+1)<<8)); + + while (t1 > t2) { + + if (d1 < 0) { + d1 += fix24_mul(b_sq,((x<<9)+(3<<8))); + x++; + } + else { + d1 += fix24_mul(b_sq,((x<<9)+(3<<8))); + d1 += fix24_mul(a_sq,(((-y)<<9)+(2<<8))); + x++; y--; + } + + gr_hline((short)(x0-x),(short)(y0-y),(short)(x0+x)); + gr_hline((short)(x0-x),(short)(y0+y),(short)(x0+x)); + + t1 = fix24_mul(a_sq,((y<<8)-128)); + t2 = fix24_mul(b_sq,((x+1)<<8)); + } + + t1 = fix24_mul(((x<<8)+128),((x<<8)+128)); + d2 = fix24_mul(t1,b_sq); + + t1 = fix24_mul(((y-1)<<8),((y-1)<<8)); + d2 += fix24_mul(t1,a_sq); + + t1 = fix24_mul(a_sq,b_sq); + d2 -= t1; + + while (y > 0) { + if (d2 < 0) { + t1 = fix24_mul(((x<<9)+(2<<8)),b_sq); + t2 = fix24_mul(((3<<8)-(y<<9)),a_sq); + d2 = d2 + t1 + t2; + x++; y--; + } + else { + t2 = fix24_mul(((3<<8)-(y<<9)),a_sq); + d2 += t2; + y--; + } + + gr_hline((short)(x0-x),(short)(y0-y),(short)(x0+x)); + gr_hline((short)(x0-x),(short)(y0+y),(short)(x0+x)); + } + + /* could be more specific */ + return CLIP_NONE; +} + diff --git a/engine/src/Libraries/2D/Source/Gen/genov.h b/engine/src/Libraries/2D/Source/Gen/genov.h new file mode 100644 index 0000000..6ec97e3 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genov.h @@ -0,0 +1,32 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/genov.h $ + * $Revision: 1.1 $ + * $Author: lmfeeney $ + * $Date: 1994/11/21 01:24:17 $ + */ + +/* these routines should really be in the canvas table, but that + would be a 2d.h re-compile + */ + +extern void gr_int_uoval (int, int, int, int); +extern int gr_int_oval (int, int, int, int); + diff --git a/engine/src/Libraries/2D/Source/Gen/genpix.c b/engine/src/Libraries/2D/Source/Gen/genpix.c new file mode 100644 index 0000000..25f8b61 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genpix.c @@ -0,0 +1,64 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/genpix.c $ + * $Revision: 1.3 $ + * $Author: kevin $ + * $Date: 1994/11/12 02:21:29 $ + * + * Generic routines for drawing a clipped pixel. + * + * This file is part of the 2d library. + * + */ + +#include "clpcon.h" +#include "cnvdat.h" +#include "grpix.h" + +void *grd_fill_upixel_func(); + +/* understands fill type. */ +int gen_fill_pixel (long color, short x, short y) +{ + if (x=grd_clip.right || y=grd_clip.bot) + return CLIP_ALL; + + gr_fill_upixel(color,x,y); + return CLIP_NONE; +} + +/* ignores fill type. */ +int gen_set_pixel (long color, short x, short y) +{ + if (x=grd_clip.right || y=grd_clip.bot) + return CLIP_ALL; + + gr_set_upixel(color,x,y); + return CLIP_NONE; +} + +int gen_set_pixel_interrupt(long color, short x, short y) +{ + if (x=grd_clip.right || y=grd_clip.bot) + return CLIP_ALL; + + gr_set_upixel_interrupt(color,x,y); + return CLIP_NONE; +} diff --git a/engine/src/Libraries/2D/Source/Gen/genrect.c b/engine/src/Libraries/2D/Source/Gen/genrect.c new file mode 100644 index 0000000..8d377bd --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genrect.c @@ -0,0 +1,72 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genrect.c $ + * $Revision: 1.5 $ + * $Author: kaboom $ + * $Date: 1993/10/02 01:17:30 $ + * + * Generic filled rectangle routines. + * + * $Log: genrect.c $ + * Revision 1.5 1993/10/02 01:17:30 kaboom + * Changed include of clip.h to include of clpcon.h and/or clpfcn.h. + * + * Revision 1.4 1993/05/04 18:46:01 kaboom + * Changed rectangle to omit its right and bottom edges. + * + * Revision 1.3 1993/04/29 18:40:45 kaboom + * Changed include of gr.h to smaller more specific grxxx.h. + * + * Revision 1.2 1993/02/22 14:48:34 kaboom + * Changed name of gr_clip_int_rect() to gr_clip_rect(). + * + * Revision 1.1 1993/02/16 15:59:53 kaboom + * Initial revision + * + ******************************************************************** + * Log from old general.c: + * Revision 1.5 1992/11/19 02:35:32 kaboom + * Fixed bug in gen_rect which would try to draw rectangles that were + * completely clipped. + */ + +#include "clpcon.h" +#include "clpfcn.h" +#include "grrect.h" + +/* draw an unclipped, filled rectangle with edges as given. do this + by making repeated calls to the installed unclipped hline drawer. */ +void gen_urect (short left, short top, short right, short bot) +{ + while (top < bot) + gr_uhline (left, top++, right-1); +} + +/* draw a clipped, filled rectangle. clip, then chain to the installed + unclipped rectangle drawer. returns clip code. */ +int gen_rect (short left, short top, short right, short bot) +{ + int r; + + r = gr_clip_rect (&left, &top, &right, &bot); + if (r != CLIP_ALL) + gr_urect (left, top, right, bot); + return r; +} diff --git a/engine/src/Libraries/2D/Source/Gen/genrsd8.c b/engine/src/Libraries/2D/Source/Gen/genrsd8.c new file mode 100644 index 0000000..319d46c --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genrsd8.c @@ -0,0 +1,324 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/genrsd8.c $ + * $Revision: 1.7 $ + * $Author: kevin $ + * $Date: 1994/10/25 15:14:10 $ + * + * Generic rsd 8 bitmap routines. + * + * $Log: genrsd8.c $ + * Revision 1.7 1994/10/25 15:14:10 kevin + * Renamed funcs. + * + * Revision 1.6 1993/10/19 09:51:26 kaboom + * Replaced #include "grd.h" with new headers split from grd.h. + * + * Revision 1.5 1993/10/02 01:17:31 kaboom + * Changed include of clip.h to include of clpcon.h and/or clpfcn.h. + * + * Revision 1.4 1993/09/06 19:43:34 kaboom + * Fixed bug in completely clipped case---now checks for >= on right and bottom. + * + * Revision 1.3 1993/07/13 17:48:33 kaboom + * Fixed bugs in clipping case off right edge. Updated for padded clipping + * rectangle. + * + * Revision 1.2 1993/04/29 18:40:46 kaboom + * Changed include of gr.h to smaller more specific grxxx.h. + * + * Revision 1.1 1993/02/16 16:00:01 kaboom + * Initial revision + * + ******************************************************************** + * Log from old general.c: + * + * Revision 1.9 1992/12/14 18:13:37 kaboom + * Fixed bug in gen_rsd8_[u]bitmap -- + * was omitting bottom line. + * + * Revision 1.7 1992/12/11 13:58:09 kaboom + * Fixed bug in clipping off right and bottom in gen_rsd8_bitmap. + */ + +#include "bitmap.h" +#include "clpcon.h" +#include "cnvdat.h" +#include "ctxmac.h" +#include "grpix.h" +#include "grrect.h" +#include "rsd.h" +#include "general.h" + +/* draw an unclipped rsd bitmap. since rsd bitmaps have skips, the trans + field of the flags byte is ignored. */ +void gri_gen_rsd8_ubitmap (grs_bitmap *bm, short x, short y) +{ + short x_right,y_bot; /* opposite edges of bitmap */ + uchar *rsd_src; /* rsd source buffer */ + short rsd_code; /* last rsd opcode */ + short rsd_count; /* count for last opcode */ + short op_count; /* operational count */ + int i; + + rsd_src = bm->bits; + rsd_count = 0; + x_right = x+bm->w; + y_bot = y+bm->h; + + /* process each scanline, keeping track of x and splitting opcodes that + span across more than one line. */ + while (y < y_bot) { + /* do enough opcodes to get to the end of the current scanline. */ + while (x < x_right) { + if (rsd_count == 0) + RSD_GET_TOKEN (); + if (x+rsd_count <= x_right) { + /* current code is all on this scanline. */ + switch (rsd_code) { + case RSD_RUN: + gr_set_fcolor (*rsd_src); + gr_uhline (x, y, x+rsd_count-1); + rsd_src++; + break; + case RSD_SKIP: + break; + default: /* RSD_DUMP */ + for (i=0; iw; + y++; + } +rsd_done: + return; +} + +int gri_gen_rsd8_bitmap (grs_bitmap *bm, short x_left, short y_top) +{ + short x,y; /* current destination position */ + short x_right,y_bot; /* opposite edges of bitmap */ + short x_off,y_off; /* x,y offset for clip */ + ulong start_byte; /* byte to start drawing */ + ulong cur_byte; /* current position within rsd */ + uchar *rsd_src; /* rsd source buffer */ + short rsd_code; /* last rsd opcode */ + short rsd_count; /* count for last opcode */ + short op_count; /* operational count */ + int code; /* clip code to return */ + int i; + + rsd_src = bm->bits; + x = x_left; y = y_top; + x_off = y_off = cur_byte = rsd_count = 0; + x_right = x_left+bm->w; + y_bot = y_top +bm->h; + + /* clip bitmap to rectangular clipping window. */ + if (x_left>grd_canvas->gc.clip.i.right || + x_right<=grd_canvas->gc.clip.i.left || + y_top>grd_canvas->gc.clip.i.bot || + y_bot<=grd_canvas->gc.clip.i.top) + /* completely clipped, forget it. */ + return CLIP_ALL; + + code = CLIP_NONE; + if (x_left < grd_canvas->gc.clip.i.left) { + /* clipped off left edge. */ + x_off = grd_clip.left-x_left; + x = grd_clip.left; + code |= CLIP_LEFT; + } + if (y < grd_clip.top) { + /* clipped off top edge. */ + y_off = grd_canvas->gc.clip.i.top-y_top; + y = grd_clip.top; + code |= CLIP_TOP; + } + if (x_right > grd_canvas->gc.clip.i.right) { + /* clipped off right edge. */ + x_right = grd_clip.right; + code |= CLIP_RIGHT; + } + if (y_bot > grd_canvas->gc.clip.i.bot) { + /* clipped off bottom edge. */ + y_bot = grd_clip.bot; + code |= CLIP_BOT; + } + + if (y_off>0 || x_off>0) { + /* been clipped of left and/or top, so we need to skip from beginning + of rsd buffer to be at x_off,y_off within rsd bitmap. */ + start_byte = y_off*bm->row + x_off; + while (cur_byte < start_byte) { + if (rsd_count == 0) + /* no pending opcodes, get a new one. */ + RSD_GET_TOKEN (); + if (cur_byte+rsd_count <= start_byte) { + /* current code doesn't hit start_byte yet, so skip all of it. */ + switch (rsd_code) { + case RSD_RUN: + /* advance past 1 byte of run color. */ + rsd_src++; + break; + case RSD_SKIP: + break; + default: /* RSD_DUMP */ + /* advance past rsd_count bytes of dump pixel data. */ + rsd_src += rsd_count; + break; + } + cur_byte += rsd_count; + rsd_count = 0; + } + else { + /* current code goes past start_byte, so skip only enough to get + to start_byte. */ + op_count = start_byte-cur_byte; + switch (rsd_code) { + case RSD_RUN: + break; + case RSD_SKIP: + break; + default: /* RSD_DUMP */ + rsd_src += op_count; + break; + } + cur_byte += op_count; + rsd_count -= op_count; + } + } + } + + /* process each scanline in two chunks. the first is the clipped section + from the right edge, wrapping around to to the left. the second is the + unclipped area in the middle. */ + while (y < y_bot) { + /* clipped section. */ + while (x < x_left+x_off) { + if (rsd_count == 0) + RSD_GET_TOKEN (); + if (x+rsd_count <= x_left+x_off) { + switch (rsd_code) { + case RSD_RUN: + rsd_src++; + break; + case RSD_SKIP: + break; + default: /* RSD_DUMP */ + rsd_src += rsd_count; + break; + } + x += rsd_count; + rsd_count = 0; + } + else { + op_count = x_left+x_off-x; + switch (rsd_code) { + case RSD_RUN: + break; + case RSD_SKIP: + break; + default: /* RSD_DUMP */ + rsd_src += op_count; + break; + } + rsd_count -= op_count; + x += op_count; + } + } + + /* section to draw. */ + while (x < x_right) { + if (rsd_count == 0) + RSD_GET_TOKEN (); + if (x+rsd_count <= x_right) { + switch (rsd_code) { + case RSD_RUN: + gr_set_fcolor (*rsd_src); + gr_uhline (x, y, x+rsd_count-1); + rsd_src++; + break; + case RSD_SKIP: + break; + default: /* RSD_DUMP */ + for (i=0; iw; + y++; + } +rsd_done: + return code; +} diff --git a/engine/src/Libraries/2D/Source/Gen/genrsdbm.c b/engine/src/Libraries/2D/Source/Gen/genrsdbm.c new file mode 100644 index 0000000..679e478 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genrsdbm.c @@ -0,0 +1,121 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genrsdbm.c $ + * $Revision: 1.3 $ + * $Author: kevin $ + * $Date: 1994/03/15 13:17:09 $ + * + * Generic routines for unpacking and then drawing/scaling rsd bitmaps. + * + * This file is part of the 2d library. + * + * $Log: genrsdbm.c $ + * Revision 1.3 1994/03/15 13:17:09 kevin + * Added clut_bitmap routines. + * + * Revision 1.2 1994/01/19 18:41:55 kaboom + * Fixed typo in clipped clut scale. + * + * Revision 1.1 1993/12/28 19:34:41 kevin + * Initial revision + */ + +#include "grs.h" +#include "grrend.h" +#include "clpcon.h" +#include "grdbm.h" +#include "grcbm.h" +#include "rsdunpck.h" + +void unpack_rsd8_ubitmap(grs_bitmap *bm, short x, short y) +{ + if (grd_unpack_buf!=NULL) { + grs_bitmap tbm; + if (gr_rsd8_convert(bm,&tbm)==GR_UNPACK_RSD8_OK) + gr_ubitmap(&tbm,x,y); + } +} + +int unpack_rsd8_bitmap(grs_bitmap *bm, short x, short y) +{ + if (grd_unpack_buf!=NULL) { + grs_bitmap tbm; + if (gr_rsd8_convert(bm,&tbm)==GR_UNPACK_RSD8_OK) + return gr_bitmap(&tbm,x,y); + } + return CLIP_ALL; +} + +void unpack_rsd8_scale_ubitmap(grs_bitmap *bm, short x, short y, short w, short h) +{ + if (grd_unpack_buf!=NULL) { + grs_bitmap tbm; + if (gr_rsd8_convert(bm,&tbm)==GR_UNPACK_RSD8_OK) + gr_scale_ubitmap(&tbm,x,y,w,h); + } +} + +int unpack_rsd8_scale_bitmap(grs_bitmap *bm, short x, short y, short w, short h) +{ + if (grd_unpack_buf!=NULL) { + grs_bitmap tbm; + if (gr_rsd8_convert(bm,&tbm)==GR_UNPACK_RSD8_OK) + return gr_scale_bitmap(&tbm,x,y,w,h); + } + return CLIP_ALL; +} + +void unpack_rsd8_clut_scale_ubitmap(grs_bitmap *bm, short x, short y, short w, short h, uchar *cl) +{ + if (grd_unpack_buf!=NULL) { + grs_bitmap tbm; + if (gr_rsd8_convert(bm,&tbm)==GR_UNPACK_RSD8_OK) + gr_clut_scale_ubitmap(&tbm,x,y,w,h,cl); + } +} + +int unpack_rsd8_clut_scale_bitmap(grs_bitmap *bm, short x, short y, short w, short h, uchar *cl) +{ + if (grd_unpack_buf!=NULL) { + grs_bitmap tbm; + if (gr_rsd8_convert(bm,&tbm)==GR_UNPACK_RSD8_OK) + return gr_clut_scale_bitmap(&tbm,x,y,w,h,cl); + } + return CLIP_ALL; +} + +void unpack_rsd8_clut_ubitmap(grs_bitmap *bm, short x, short y, uchar *cl) +{ + if (grd_unpack_buf!=NULL) { + grs_bitmap tbm; + if (gr_rsd8_convert(bm,&tbm)==GR_UNPACK_RSD8_OK) + gr_clut_ubitmap(&tbm,x,y,cl); + } +} + +int unpack_rsd8_clut_bitmap(grs_bitmap *bm, short x, short y, uchar *cl) +{ + if (grd_unpack_buf!=NULL) { + grs_bitmap tbm; + if (gr_rsd8_convert(bm,&tbm)==GR_UNPACK_RSD8_OK) + return gr_clut_bitmap(&tbm,x,y,cl); + } + return CLIP_ALL; +} diff --git a/engine/src/Libraries/2D/Source/Gen/genrsdtm.c b/engine/src/Libraries/2D/Source/Gen/genrsdtm.c new file mode 100644 index 0000000..2b9e11d --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genrsdtm.c @@ -0,0 +1,75 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/genrsdtm.c $ + * $Revision: 1.4 $ + * $Author: kevin $ + * $Date: 1994/08/16 13:08:05 $ + * + * Generic routines for texture mapping rsd bitmaps. + * + * This file is part of the 2d library. + * + * $Log: genrsdtm.c $ + * Revision 1.4 1994/08/16 13:08:05 kevin + * Changed to accomodate new function table organization. + * + * Revision 1.3 1994/07/28 01:29:57 kevin + * New general purpose chaining procedures for new texture mapping regime. + * + * Revision 1.2 1993/12/28 16:30:18 kevin + * changed name of gr_unpack_rsd to gr_rsd8_convert. + * + * Revision 1.1 1993/12/06 13:16:17 kevin + * Initial revision + * + */ + +#include "bitmap.h" +#include "grs.h" +#include "ifcn.h" +#include "pertyp.h" +#include "rsdunpck.h" +#include "tmapint.h" +#include "tmaptab.h" +#include "gentf.h" + +void rsd8_tm_init(grs_tmap_loop_info *ti) +{ + if (grd_unpack_buf!=NULL) { + grs_bitmap tbm; + if (gr_rsd8_convert(&(ti->bm),&tbm)==GR_UNPACK_RSD8_OK) { + ti->bm.bits=tbm.bits; + ti->n+=(tbm.type-BMT_RSD8)*GRD_FUNCS; + ((void (*)(grs_tmap_loop_info *))(grd_tmap_init_table[ti->n]))(ti); + } + } +} + +void rsd8_pm_init(grs_bitmap *bm, grs_per_setup *ps) +{ + if (grd_unpack_buf!=NULL) { + grs_bitmap tbm; + if (gr_rsd8_convert(bm,&tbm)==GR_UNPACK_RSD8_OK) { + bm->bits=tbm.bits; + ps->dp+=(tbm.type-BMT_RSD8)*GRD_FUNCS; + ((void (*)(grs_bitmap *, grs_per_setup *))(grd_tmap_init_table[ps->dp]))(bm,ps); + } + } +} diff --git a/engine/src/Libraries/2D/Source/Gen/genslin.c b/engine/src/Libraries/2D/Source/Gen/genslin.c new file mode 100644 index 0000000..ed15280 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genslin.c @@ -0,0 +1,104 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genslin.c $ + * $Revision: 1.7 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 02:26:11 $ + * + * Routine to draw an rgb shaded line to a flat 8 canvas. + * + * This file is part of the 2d library. + * + * $Log: genslin.c $ + * Revision 1.7 1994/06/11 02:26:11 lmfeeney + * moved unclipped drawer, now contains two versions of + * clipped line drawer, one for each i\f - call clipper + * and call unclipped line drawer + * + * Revision 1.6 1994/05/06 18:19:52 lmfeeney + * rewritten for greater accuracy and speed + * + * Revision 1.5 1993/10/02 01:04:01 kaboom + * Fixed clipping include files. + * + * Revision 1.4 1993/07/03 22:51:02 spaz + * Bugfix; treated clipper return code spastically + * + * Revision 1.3 1993/07/01 20:32:29 spaz + * Set last pixel explicitly to i1 in h and vlines + * + * Revision 1.2 1993/06/30 00:43:13 spaz + * Changed a check for (x0>x1) to fix_int(x0) version, + * because it was negating the delta uselessly for + * near-vertical lines. + * + * Revision 1.1 1993/06/22 20:14:28 spaz + * Initial revision + */ + +#include "ctxmac.h" +#include "plytyp.h" +#include "clpcon.h" +#include "clpltab.h" +#include "grlin.h" + +/* Draw a gouraud-shaded line, specified by indices into the palette. + be warned, weird precision bugs abound (check out test programs + in /project/lib/src/2d/test). + + 5/94: Precision errors have been (entirely?) eliminated. See + correctness argument in note.txt. + +*/ + +/* This routine draws clipped goroud-shaded lines as specified by + intensities. returns a clip value */ + +int gen_fix_sline (fix x0, fix y0, fix i0, fix x1, fix y1, fix i1) +{ + int r; + grs_vertex v0, v1; + + v0.x = x0; v0.y = y0; v0.i = i0; + v1.x = x1; v1.y = y1; v1.i = i1; + + r = grd_sline_clip_fill (gr_get_fcolor(), gr_get_fill_parm(), &v0, &v1); + + return r; +} + + +int gri_sline_clip_fill (long c, long parm, grs_vertex *v0, grs_vertex *v1) +{ + int r; + grs_vertex u0, u1; + + /* save inputs (don't really need whole struct) */ + + u0 = *v0; + u1 = *v1; + + r = gri_sline_clip (&u0, &u1); + + if (r != CLIP_ALL) + grd_usline_fill (c, parm, &u0, &u1); + + return r; +} diff --git a/engine/src/Libraries/2D/Source/Gen/gente.c b/engine/src/Libraries/2D/Source/Gen/gente.c new file mode 100644 index 0000000..a6b49de --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/gente.c @@ -0,0 +1,247 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/gente.c $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/08/16 13:03:24 $ + * + * texture mapping edge parameter calculation procedures. + * + * This file is part of the 2d library. + * + */ + +#include "fix.h" +#include "tmapint.h" +#include "gente.h" + +//#include "polyint.h" +// MLA- had to put poly_do-x all on one line so the compiler wouldn't bitch at me +#define poly_do_x(_next,_prev,_d,_frac,_x0,_dx,_y_next) \ + do { \ + _d = _next->y-_prev->y; \ + _x0 = _prev->x; \ + _y_next = fix_cint(_next->y); \ + _dx = fix_div(_next->x-_x0,_d); \ + _frac = fix_ceil(_prev->y)-_prev->y; \ + _x0 += fix_mul(_frac,_dx);} \ + while (0) + +#define poly_do_y(_next,_prev,_d,_frac,_y0,_dy,_x_next) \ +do { \ + _d = _next->x-_prev->x; \ + _y0 = _prev->y; \ + _x_next = fix_cint(_next->x); \ + _dy = fix_div(_next->y-_y0,_d); \ + _frac = fix_ceil(_prev->x)-_prev->x; \ + _y0 += fix_mul(_frac,_dy); \ +} while (0) + +#define poly_do_t(_tf,_ti,_d,_frac,_t0,_dt) \ +do { \ + _t0 = _ti; \ + _dt = fix_div(_tf-_t0, _d); \ + _t0 += fix_mul(_frac,_dt); \ +} while (0) + +#define poly_do_tw(_tf,_ti,_d,_frac,_t0,_dt,_wf,_wi,_w0,_dw) \ +do { \ + if (fix_abs(_tf -_ti)left); + else edge=&(info->right); + prev=*p_prev,next=*p; + poly_do_x(next, prev, d, dy, edge->x, edge->dx, edge->y); +} + +void gri_ix_edge + (grs_tmap_loop_info *info, grs_vertex **p, grs_vertex **p_prev, int side) +{ + grs_tmap_edge *edge; + fix d,frac; + grs_vertex *prev,*next; + + if (side==1) edge=&(info->left); + else edge=&(info->right); + prev=*p_prev,next=*p; + poly_do_x(next, prev, d, frac, edge->x, edge->dx, edge->y); + poly_do_t(next->i, prev->i, d, frac, edge->i, edge->di); +} + +void gri_rgbx_edge + (grs_tmap_loop_info *info, grs_vertex **p, grs_vertex **p_prev, int side) +{ + grs_tmap_edge *edge; + fix d,frac; + grs_vertex *prev,*next; + + if (side==1) edge=&(info->left); + else edge=&(info->right); + prev=*p_prev,next=*p; + poly_do_x(next, prev, d, frac, edge->x, edge->dx, edge->y); + poly_do_t(next->u, prev->u, d, frac, edge->u, edge->du); + poly_do_t(next->v, prev->v, d, frac, edge->v, edge->dv); + poly_do_t(next->w, prev->w, d, frac, edge->i, edge->di); +} + +// MLA #pragma off (unreferenced) +void gri_scale_edge /* for scaler; does both edges at once.*/ + (grs_tmap_loop_info *info, grs_vertex **p, grs_vertex **p_prev, int side) +{ + fix d,frac; + grs_vertex *prev,*next; + + prev=*p_prev,next=*p; + info->left.u=prev->u; + info->left.x=prev->x; + info->right.u=next->u; + info->right.x=next->x; + info->left.dx=info->right.dx=0; + info->left.v=prev->v; + info->left.y=info->right.y=fix_cint(next->y); + frac=fix_ceil(prev->y)-prev->y; + d=next->y-prev->y; + info->left.dv=info->right.dv=fix_div(next->v-prev->v,d); + info->left.v+=fix_mul(info->left.dv,frac); +} +// MLA#pragma on (unreferenced) + +void gri_uvx_edge + (grs_tmap_loop_info *info, grs_vertex **p, grs_vertex **p_prev, int side) +{ + grs_tmap_edge *edge; + fix d,frac; + grs_vertex *prev,*next; + + if (side==1) edge=&(info->left); + else edge=&(info->right); + prev=*p_prev,next=*p; + poly_do_x(next, prev, d, frac, edge->x, edge->dx, edge->y); + poly_do_t(next->u, prev->u, d, frac, edge->u, edge->du); + poly_do_t(next->v, prev->v, d, frac, edge->v, edge->dv); +} + +void gri_uvix_edge + (grs_tmap_loop_info *info, grs_vertex **p, grs_vertex **p_prev, int side) +{ + grs_tmap_edge *edge; + fix d,frac; + grs_vertex *prev,*next; + + if (side==1) edge=&(info->left); + else edge=&(info->right); + prev=*p_prev,next=*p; + poly_do_x(next, prev, d, frac, edge->x, edge->dx, edge->y); + poly_do_t(next->u, prev->u, d, frac, edge->u, edge->du); + poly_do_t(next->v, prev->v, d, frac, edge->v, edge->dv); + poly_do_t(next->i, prev->i, d, frac, edge->i, edge->di); +} + +void gri_uvwx_edge + (grs_tmap_loop_info *info, grs_vertex **p, grs_vertex **p_prev, int side) +{ + grs_tmap_edge *edge; + fix d,frac; + grs_vertex *prev,*next; + + if (side==1) edge=&(info->left); + else edge=&(info->right); + prev=*p_prev,next=*p; + poly_do_x(next, prev, d, frac, edge->x, edge->dx, edge->y); + poly_do_tw(next->u, prev->u, d, frac, edge->u, edge->du, + next->w, prev->w, info->w, info->dw); + poly_do_tw(next->v, prev->v, d, frac, edge->v, edge->dv, + next->w, prev->w, info->w, info->dw); +} + +void gri_uviwx_edge + (grs_tmap_loop_info *info, grs_vertex **p, grs_vertex **p_prev, int side) +{ + grs_tmap_edge *edge; + fix d,frac; + grs_vertex *prev,*next; + + if (side==1) edge=&(info->left); + else edge=&(info->right); + prev=*p_prev,next=*p; + poly_do_x(next, prev, d, frac, edge->x, edge->dx, edge->y); + poly_do_tw(next->u, prev->u, d, frac, edge->u, edge->du, + next->w, prev->w, info->w, info->dw); + poly_do_tw(next->v, prev->v, d, frac, edge->v, edge->dv, + next->w, prev->w, info->w, info->dw); + poly_do_tw(next->i, prev->i, d, frac, edge->i, edge->di, + next->w, prev->w, info->w, info->dw); +} + +void gri_uvwy_edge + (grs_tmap_loop_info *info, grs_vertex **p, grs_vertex **p_prev, int side) +{ + grs_tmap_edge *edge; + fix d,frac; + grs_vertex *prev,*next; + + if (side==1) edge=&(info->top); + else edge=&(info->bot); + prev=*p_prev,next=*p; + poly_do_y(next, prev, d, frac, edge->y, edge->dy, edge->x); + poly_do_tw(next->u, prev->u, d, frac, edge->u, edge->du, + next->w, prev->w, info->w, info->dw); + poly_do_tw(next->v, prev->v, d, frac, edge->v, edge->dv, + next->w, prev->w, info->w, info->dw); +} + +void gri_uviwy_edge + (grs_tmap_loop_info *info, grs_vertex **p, grs_vertex **p_prev, int side) +{ + grs_tmap_edge *edge; + fix d,frac; + grs_vertex *prev,*next; + + if (side==1) edge=&(info->top); + else edge=&(info->bot); + prev=*p_prev,next=*p; + poly_do_y(next, prev, d, frac, edge->y, edge->dy, edge->x); + poly_do_tw(next->u, prev->u, d, frac, edge->u, edge->du, + next->w, prev->w, info->w, info->dw); + poly_do_tw(next->v, prev->v, d, frac, edge->v, edge->dv, + next->w, prev->w, info->w, info->dw); + poly_do_tw(next->i, prev->i, d, frac, edge->i, edge->di, + next->w, prev->w, info->w, info->dw); +} + diff --git a/engine/src/Libraries/2D/Source/Gen/gente.h b/engine/src/Libraries/2D/Source/Gen/gente.h new file mode 100644 index 0000000..4358ec9 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/gente.h @@ -0,0 +1,48 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/gente.h $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/08/16 15:34:31 $ + * + * Texture mapping edge calculation functions. + * Generic canvas. + * + */ + +#include "tmapint.h" + +/* polygon edges */ +extern void gri_x_edge(grs_tmap_loop_info *info, grs_vertex **p, grs_vertex **p_prev, int side); +extern void gri_ix_edge(grs_tmap_loop_info *info, grs_vertex **p, grs_vertex **p_prev, int side); +extern void gri_rgbx_edge(grs_tmap_loop_info *info, grs_vertex **p, grs_vertex **p_prev, int side); + +/* scaler edges */ +extern void gri_scale_edge(grs_tmap_loop_info *info, grs_vertex **p, grs_vertex **p_prev, int side); + +/* texture mapping edges */ +extern void gri_uvx_edge(grs_tmap_loop_info *info, grs_vertex **p, grs_vertex **p_prev, int side); +extern void gri_uvix_edge(grs_tmap_loop_info *info, grs_vertex **p, grs_vertex **p_prev, int side); +extern void gri_uvwx_edge(grs_tmap_loop_info *info, grs_vertex **p, grs_vertex **p_prev, int side); +extern void gri_uviwx_edge(grs_tmap_loop_info *info, grs_vertex **p, grs_vertex **p_prev, int side); +extern void gri_uvwy_edge(grs_tmap_loop_info *info, grs_vertex **p, grs_vertex **p_prev, int side); +extern void gri_uviwy_edge(grs_tmap_loop_info *info, grs_vertex **p, grs_vertex **p_prev, int side); + + diff --git a/engine/src/Libraries/2D/Source/Gen/gentf.h b/engine/src/Libraries/2D/Source/Gen/gentf.h new file mode 100644 index 0000000..3ba8cae --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/gentf.h @@ -0,0 +1,111 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/gentf.h $ + * $Revision: 1.9 $ + * $Author: kevin $ + * $Date: 1994/10/25 16:52:11 $ + * + * Texture mapping internal functions. + * Generic canvas. + * + */ +#include "pertyp.h" +#include "tmapint.h" + +/* bitmap blitters */ +extern void gen_flat8_ubitmap (grs_bitmap *bm, short x, short y); +extern int gen_flat8_bitmap (grs_bitmap *bm, short x, short y); + +/* init functions */ +extern void gri_gen_opaque_lin_umap_init(); +extern void gri_gen_trans_lin_umap_init(); +extern void gri_gen_opaque_clut_lin_umap_init(); +extern void gri_gen_trans_clut_lin_umap_init(); +extern void gri_gen_opaque_lit_lin_umap_init(); +extern void gri_gen_trans_lit_lin_umap_init(); + +extern void gri_gen_opaque_floor_umap_init(); +extern void gri_gen_trans_floor_umap_init(); +extern void gri_gen_opaque_clut_floor_umap_init(); +extern void gri_gen_trans_clut_floor_umap_init(); +extern void gri_gen_opaque_lit_floor_umap_init(); +extern void gri_gen_trans_lit_floor_umap_init(); + +extern void gri_gen_opaque_wall_umap_init(); +extern void gri_gen_trans_wall_umap_init(); +extern void gri_gen_opaque_clut_wall_umap_init(); +extern void gri_gen_trans_clut_wall_umap_init(); +extern void gri_gen_opaque_lit_wall_umap_init(); +extern void gri_gen_trans_lit_wall_umap_init(); + +extern void gri_gen_opaque_per_umap_hscan_init(); +extern void gri_gen_trans_per_umap_hscan_init(); +extern void gri_gen_opaque_clut_per_umap_hscan_init(); +extern void gri_gen_trans_clut_per_umap_hscan_init(); +extern void gri_gen_opaque_lit_per_umap_hscan_init(); +extern void gri_gen_trans_lit_per_umap_hscan_init(); + +extern void gri_gen_opaque_per_umap_vscan_init(); +extern void gri_gen_trans_per_umap_vscan_init(); +extern void gri_gen_opaque_clut_per_umap_vscan_init(); +extern void gri_gen_trans_clut_per_umap_vscan_init(); +extern void gri_gen_opaque_lit_per_umap_vscan_init(); +extern void gri_gen_trans_lit_per_umap_vscan_init(); + +extern void gri_gen_opaque_scale_umap_init(); +extern void gri_gen_trans_scale_umap_init(); +extern void gri_gen_opaque_clut_scale_umap_init(); +extern void gri_gen_trans_clut_scale_umap_init(); +extern void gri_gen_opaque_lit_scale_umap_init(); +extern void gri_gen_trans_lit_scale_umap_init(); +extern void gri_gen_opaque_solid_scale_umap_init(); + +extern void gri_gen_mono_opaque_scale_umap_init(); +extern void gri_gen_mono_trans_scale_umap_init(); +extern void gri_gen_mono_opaque_clut_scale_umap_init(); +extern void gri_gen_mono_trans_clut_scale_umap_init(); +extern void gri_gen_mono_trans_solid_scale_umap_init(); + +/* polys */ +extern void gri_gen_poly_init(); +extern void gri_gen_spoly_init(); +extern void gri_gen_cpoly_init(); +extern void gri_gen_tpoly_init(); +extern void gri_gen_stpoly_init(); + +/* rsd8 */ +extern void gri_gen_rsd8_ubitmap (grs_bitmap *bm, short x, short y); +extern int gri_gen_rsd8_bitmap (grs_bitmap *bm, short x_left, short y_top); + +extern void rsd8_tm_init(grs_tmap_loop_info *ti); +extern void rsd8_pm_init(grs_bitmap *bm, grs_per_setup *ps); + +/* translucent */ +extern void gri_gen_tluc8_opaque_lin_umap_init(); +extern void gri_gen_tluc8_trans_lin_umap_init(); +extern void gri_gen_tluc8_opaque_lit_lin_umap_init(); +extern void gri_gen_tluc8_trans_lit_lin_umap_init(); +extern void gri_gen_tluc8_opaque_clut_lin_umap_init(); +extern void gri_gen_tluc8_trans_clut_lin_umap_init(); + +extern void gri_gen_tluc8_opaque_scale_umap_init(); +extern void gri_gen_tluc8_trans_scale_umap_init(); +extern void gri_gen_tluc8_opaque_clut_scale_umap_init(); +extern void gri_gen_tluc8_trans_clut_scale_umap_init(); diff --git a/engine/src/Libraries/2D/Source/Gen/gentl8.c b/engine/src/Libraries/2D/Source/Gen/gentl8.c new file mode 100644 index 0000000..0124e7b --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/gentl8.c @@ -0,0 +1,120 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/gentl8.c $ + * $Revision: 1.2 $ + * $Author: baf $ + * $Date: 1994/01/14 12:40:57 $ + * + * Generic tluc 8 bitmap routines. + * + * $Log: gentl8.c $ + * Revision 1.2 1994/01/14 12:40:57 baf + * Lit translucency reform. + * + * Revision 1.1 1993/12/01 21:24:27 baf + * Initial revision + * + */ + +#include "bitmap.h" +#include "clpcon.h" +#include "cnvdat.h" +#include "grdbm.h" +#include "grpix.h" +#include "tlucdat.h" + +/* bozo tluc8 bitmap drawer. */ +void gen_tluc8_ubitmap (grs_bitmap *bm, short x, short y) +{ + uchar *src = bm->bits; + short right = x+bm->w; + short bot = y+bm->h; + short cur_x; + + if (bm->flags & BMF_TRANS) { + for ( ; yrow-bm->w) { + for (cur_x=x ; cur_xrow-bm->w) { + for (cur_x=x ; cur_xw; h = bm->h; p = bm->bits; + + /* check for trivial reject. */ + if (x+bm->w=grd_clip.right || + y+bm->h=grd_clip.bot) + return CLIP_ALL; + + /* clip & draw that sucker. */ + if (x < grd_clip.left) { /* off left edge */ + extra = grd_clip.left - x; + bm->w -= extra; + bm->bits += extra; + x = grd_clip.left; + code |= CLIP_LEFT; + } + if (x+bm->w > grd_clip.right) { /* off right edge */ + bm->w -= x+bm->w-grd_clip.right; + code |= CLIP_RIGHT; + } + if (y < grd_clip.top) { /* off top */ + extra = grd_clip.top - y; + bm->h -= extra; + bm->bits += bm->row*extra; + y = grd_clip.top; + code |= CLIP_TOP; + } + if (y+bm->h > grd_clip.bot) { /* off bottom */ + bm->h -= y+bm->h-grd_clip.bot; + code |= CLIP_BOT; + } + gr_tluc8_ubitmap (bm, x, y); + + /* restore bitmap to normal. */ + bm->w = w; bm->h = h; bm->bits = p; + return code; +} diff --git a/engine/src/Libraries/2D/Source/Gen/gentm.c b/engine/src/Libraries/2D/Source/Gen/gentm.c new file mode 100644 index 0000000..93c5980 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/gentm.c @@ -0,0 +1,292 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/gentm.c $ + * $Revision: 1.20 $ + * $Author: kevin $ + * $Date: 1994/09/16 03:58:06 $ + * + * Horizontal and vertical scanning texture mapper dispatchers. + * + * This file is part of the 2d library. + * + */ + +#include "bitmap.h" +#include "buffer.h" +#include "clpcon.h" +#include "clpfcn.h" +#include "cnvdat.h" +#include "fill.h" +#include "grnull.h" +#include "ifcn.h" +#include "polyint.h" +#include "scrmac.h" +#include +#include "tmapint.h" +#include "tmaps.h" +#include "tmaptab.h" +#include "lg.h" + +typedef void (*tm_init_type)(grs_tmap_loop_info *, grs_vertex **); +typedef void (*edge_type)(grs_tmap_loop_info *, grs_vertex **, grs_vertex **, int); + +void h_umap(grs_bitmap *bm, int n, grs_vertex **vpl, grs_tmap_info *ti) +{ + grs_vertex **p_left; /* current left & right vertices */ + grs_vertex **p_right; + uint32_t y_min, y_max; /* min & max vertex y coords */ + fix w_min,w_max; + int y,y_limit; /* current screen coordinates */ + void (*tm_init)(grs_tmap_loop_info *, grs_vertex **); + fix *old_w = NULL; + grs_tmap_loop_info info; /* values for inner loop routine */ + + + info.n=(bm->flags&BMF_TRANS) + ti->tmap_type + GRD_FUNCS*bm->type; +/*{ + Str255 s; + NumToString(info.n,s); + DebugString(s); +}*/ + + + if (grd_gc.fill_type!=FILL_NORM) + info.clut=(uchar *)grd_gc.fill_parm; + else if (ti->flags&TMF_CLUT) + if ((info.clut=ti->clut)==NULL) + info.clut=gr_get_clut(); + + tm_init = (tm_init_type) grd_tmap_init_table[info.n]; + + info.left_edge_func=info.right_edge_func=info.loop_func=gr_null; + + /* start with degenerate min and max values. */ +// poly_find_yw_extrema(y_min,y_max,w_min,w_max,p_left,vpl,n); +// moved this code from the #define in PolyInt.h because the compiler couldn't handle it +do { + grs_vertex **pvp; + int y; + p_left = vpl; + y_min = fix_cint(vpl[0]->y); + y_max = fix_cint(vpl[0]->y); + w_min = vpl[0]->w; + w_max = vpl[0]->w; + for (pvp=vpl+1; pvpy); + if (y < y_min) { + y_min = y; + w_min = (*pvp)->w; + p_left = pvp; + } + if (y > y_max) { + y_max = y; + w_max = (*pvp)->w; + } + } + if (y_min == y_max) return; +} while(0); + + if (ti->flags&TMF_FLOOR) { + grs_vertex **pvp=vpl; + fix y0=(*p_left)->y; + fix dw; + /* fix w_min,w_max if they're too big. Save old w's. */ + if (w_max>0x20000) { + fix i; + old_w=(fix *)gr_alloc_temp(n*sizeof(fix)); + do { + w_max=w_max>>2; + w_min=w_min>>2; + } while (w_max>0x20000); + for (i=0;iw; + } + /* fix w's so w=w_min+dw*(y-y0) for all vertices. */ + info.dw=dw=(w_max-w_min)/((int32_t )(y_max-y_min)); + info.w=w_min+fix_mul(dw,fix_ceil(y0)-y0); + for (; pvpw=w_min+fix_mul((*pvp)->y - y0,dw); + } + p_right=p_left; + + info.bm = *bm; // memcpy(&(info.bm),bm,sizeof(*bm)); + info.y=y_min; + info.u_mask=(1<wlog)-1; + info.v_mask=((1<hlog)-1)<wlog; + info.vtab=NULL; + + /* we want to set n_left and n_right to be leftmost and rightmost vertices + with y = y_min. usually, both are n_min, but if there is a horizontal + edge at y = y_min, they will be different. */ + + /* draw each span, starting at y_min. */ + tm_init(&info,vpl); + for (y=y_min; y!=y_max; ) { + + if (fix_cint((*p_left)->y)<=y) { + fix y_left,y_prev; + grs_vertex *prev; + poly_do_left_edge(p_left,prev,y_left,y_prev,y,vpl,n); + ((edge_type) info.left_edge_func) (&info,p_left,&prev,TMS_LEFT); + } + + if (fix_cint((*p_right)->y)<=y) { + fix y_right,y_prev; + grs_vertex *prev; + poly_do_right_edge(p_right,prev,y_right,y_prev,y,vpl,n); + ((edge_type) info.right_edge_func) (&info,p_right,&prev,TMS_RIGHT); + } + y_limit=lg_min(info.right.y,info.left.y); + info.n=y_limit-y; + + if (((int (*)(grs_tmap_loop_info *))(info.loop_func))(&info)) break; + y=y_limit; + } + if (info.vtab) + gr_free_temp(info.vtab); + if (old_w) { + int i; + for (i=0;iw=old_w[i]; + gr_free_temp(old_w); + } +} + +int h_map(grs_bitmap *bm, int n, grs_vertex **vpl, grs_tmap_info *ti) +{ + grs_vertex **cpl; /* clipped vertices */ + int m; /* number of clipped vertices */ + + cpl = NULL; + m = gr_clip_poly(n,4,vpl,&cpl); + if (m>2) + h_umap(bm,m,cpl,ti); + gr_free_temp(cpl); + + return (m>2) ? CLIP_NONE : CLIP_ALL; +} + +typedef void (*tm_init_type2)(grs_tmap_loop_info *); + +void v_umap(grs_bitmap *bm, int n, grs_vertex **vpl, grs_tmap_info *ti) +{ + grs_vertex **p_top; /* current top & bot vertices */ + grs_vertex **p_bot; + uint32_t x_min, x_max; /* min & max vertex x coords */ + int x,x_limit; /* current screen coordinates */ + fix w_min,w_max; + fix *old_w = NULL; /* list of old w values from vpl */ + grs_tmap_loop_info info; /* values for inner loop routine */ + void (*tm_init)(grs_tmap_loop_info *); + + info.n=bm->flags&BMF_TRANS; + if (info.n+2*grd_gc.fill_type==2*FILL_SOLID) { + h_umap(bm,n,vpl,ti); + return; + } + info.n+=ti->tmap_type+GRD_FUNCS*bm->type; + + if (grd_gc.fill_type!=FILL_NORM) + info.clut=(uchar *)grd_gc.fill_parm; + else if (ti->flags&TMF_CLUT) + if ((info.clut=ti->clut)==NULL) + info.clut=gr_get_clut(); + + tm_init = (tm_init_type2) grd_tmap_init_table[info.n]; + info.top_edge_func=info.bot_edge_func=info.loop_func=gr_null; + + /* start with degenerate min and max values. */ + poly_find_xw_extrema(x_min,x_max,w_min,w_max,p_top,vpl,n); + + if (ti->flags&TMF_WALL) { + grs_vertex **pvp=vpl; + fix x0=(*p_top)->x; + fix dw; + /* fix w_min,w_max if they're too big. Save old w's. */ + if (w_max>0x20000) { + fix i; + old_w=(fix *)gr_alloc_temp(n*sizeof(fix)); + do { + w_max=w_max>>2; + w_min=w_min>>2; + } while (w_max>0x20000); + for (i=0;iw; + } + /* fix w's so w=w0+dw*(x-x0) for all vertices. */ + info.dw=dw=(w_max-w_min)/((int32_t )(x_max-x_min)); + info.w=w_min+fix_mul(dw,fix_ceil(x0)-x0); + for (; pvpw=w_min+fix_mul((*pvp)->x - x0,dw); + } + p_bot=p_top; + + info.bm = *bm; // memcpy(&(info.bm),bm,sizeof(*bm)); + info.x=x_min; + info.u_mask=(1<wlog)-1; + info.v_mask=((1<hlog)-1)<wlog; + + info.vtab=NULL; + /* we want to set n_top and n_bot to be topmost and botmost vertices + with y = y_min. usually, both are n_min, but if there is a horizontal + edge at y = y_min, they will be different. */ + + /* draw each span, starting at x_min. */ + tm_init(&info); + for (x=x_min; x!=x_max; ) { + + if (fix_cint((*p_top)->x)<=x) { + fix x_top,x_prev; + grs_vertex *prev; + poly_do_top_edge(p_top,prev,x_top,x_prev,x,vpl,n); + ((edge_type) info.top_edge_func) (&info,p_top,&prev,TMS_LEFT); + } + + if (fix_cint((*p_bot)->x)<=x) { + fix x_bot,x_prev; + grs_vertex *prev; + poly_do_bot_edge(p_bot,prev,x_bot,x_prev,x,vpl,n); + ((edge_type) info.bot_edge_func) (&info,p_bot,&prev,TMS_RIGHT); + } + x_limit=lg_min(info.bot.x,info.top.x); + info.n=x_limit-x; + + if (((int (*)(grs_tmap_loop_info *))(info.loop_func))(&info)) break; + x=x_limit; + } + if (info.vtab) + gr_free_temp(info.vtab); + if (old_w) { + int i; + for (i=0;iw=old_w[i]; + gr_free_temp(old_w); + } +} + +int v_map(grs_bitmap *bm, int n, grs_vertex **vpl, grs_tmap_info *ti) +{ + grs_vertex **cpl; /* clipped vertices */ + int m; /* number of clipped vertices */ + + cpl = NULL; + m = gr_clip_poly(n,4,vpl,&cpl); + if (m>2) + v_umap(bm,m,cpl,ti); + gr_free_temp(cpl); + + return (m>2) ? CLIP_NONE : CLIP_ALL; +} diff --git a/engine/src/Libraries/2D/Source/Gen/genuclin.c b/engine/src/Libraries/2D/Source/Gen/genuclin.c new file mode 100644 index 0000000..ac09b61 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genuclin.c @@ -0,0 +1,250 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/genuclin.c $ + * $Revision: 1.3 $ + * $Author: kevin $ + * $Date: 1994/10/17 15:00:02 $ +*/ + +/* This file contains originally the routine gen_fix_ucline, found in + genclin.c. */ + +#include +#include "plytyp.h" +#include "scrdat.h" +#include "pixfill.h" + +/* draw unclipped rgb shaded line using grd_pixel_fill for fill + information -- note that the solid fill mode cannot use this routine + -- the gen routine only passes the computed color -- arguably it + should check, but that would be a pain and if the cline function is + accessed by the fill vector, this function is never called when the + fill mode is solid +*/ + +#define fix_make_nof(x) fix_make(x,0x0000) +#define macro_get_ipal(r,g,b) (long) ((r>>19) &0x1f) | ((g>>14) & 0x3e0) | ((b>>9) & 0x7c00) + +/* the color passed into the line fill is the calculated one, not the + param -- see above + */ +// MLA #pragma off (unreferenced) +#define macro_plot_rgb(x,y,i) grd_pixel_fill(grd_ipal[i],parm,x,y) + +void gri_gen_ucline_fill (long c, long parm, grs_vertex *v0, grs_vertex *v1) +{ + fix x0, y0, x1, y1; + fix dx, dy; /* deltas in x and y */ + fix t; /* tmp */ + + uchar r0, g0, b0, r1, g1, b1; /* rgb values of endpt colors */ + fix r,g,b; /* current intensities */ + fix dr, dg, db; /* deltas for each of rgb */ + long i; /* color index */ + + /* NB: this code mimics the code in fl8clin.c for flat8_ucline's. + this leads to much more separation among e.g +/- dx and dy is + ncessary, since increments in the x and y directions can be treated + identically, rather than manipulating a canvas pointer. however, + eventually, i'd like the flat8 and gen to derive from the + same code with just different defines for increments, etc. + */ + + /* NB: directionality policy -- reversible + Lines are drawn in order of increasing x or increasing y, + for lines that have greater x or y extent, repsectively. + This is done for all lines, including horiz., vert., and + 45' lines (increasing x). + */ + + x0 = v0->x; y0 = v0->y; + x1 = v1->x; y1 = v1->y; + + if (x0 < x1) { + x1 -= 1; /* e.g. - epsilon */ + + } + else if (x0 > x1) { + x0 -= 1; + } + + if (y0 < y1) { + y1 -= 1; /* e.g. - epsilon */ + } + else if (y0 > y1) { + y0 -= 1; + } + + dx = fix_trunc (x1) - fix_trunc(x0); /* x extent in pixels, (macro is flakey) */ + dx = fix_abs (dx); + dy = fix_trunc (y1) - fix_trunc(y0); /* y extent in pixels */ + dy = fix_abs (dy); + + if (dx == 0 && dy == 0) + return; + + + /* three cases: absolute value dx < = > dy + + along the longer dimension, the fixpoint x0 (or y0) is treated + as an int + + the points are swapped if needed and the rgb initial and deltas + are calculated accordingly + + the variable (x0 or y0) for the long dimension is treated as a fix + and incremented using ++, the other is kept as fixpoint and + incremented by the fixpoint delta (dx or dy). Then it's shifted + to int for pixel drawing. + + there are two sub-cases - a horizontal or vertical line, + and the dx or dy being added or subtracted. + + the endpoints are walked inclusively in all cases -- + + 45' degree lines are explicitly special cased -- more efficient, + since everything is an integer + */ + + r0 = (uchar) (v0->u); g0 = (uchar) (v0->v); b0 = (uchar) (v0->w); + r1 = (uchar) (v1->u); g1 = (uchar) (v1->v); b1 = (uchar) (v1->w); + + if (dx > dy) { + + x0 = fix_int (x0); x1 = fix_int (x1); + + if (x0 < x1 ) { + r = fix_make(r0,0); g = fix_make(g0,0); b = fix_make(b0,0); + dr = fix_div(fix_make_nof(r1-r0),dx); + dg = fix_div(fix_make_nof(g1-g0),dx); + db = fix_div(fix_make_nof(b1-b0),dx); + } + else { + t = x0; x0 = x1; x1 = t; + t = y0; y0 = y1; y1 = t; + + r = fix_make(r1, 0); g = fix_make(g1,0); b = fix_make(b1,0); + dr = fix_div(fix_make_nof(r0-r1),dx); + dg = fix_div(fix_make_nof(g0-g1),dx); + db = fix_div(fix_make_nof(b0-b1),dx); + } + + if ((fix_int(y0)) == (fix_int(y1))) { + y0 = fix_int (y0); + while (x0 <= x1) { + i = macro_get_ipal(r,g,b); + macro_plot_rgb(x0,y0,i); + x0++; + r += dr; g += dg; b += db; + } + } + else { + dy = fix_div ((y1 - y0), dx); + while (x0 <= x1) { + i = macro_get_ipal(r,g,b); + macro_plot_rgb (x0, fix_fint(y0) ,i); + x0 ++; + y0 += dy; + r += dr; g += dg; b += db; + } + } + } + + else if (dy > dx) { + + y0 = fix_int (y0); y1 = fix_int (y1); + + if (y0 < y1 ) { + r = fix_make(r0,0); g = fix_make(g0,0); b = fix_make(b0,0); + dr = fix_div(fix_make_nof(r1-r0),dy); + dg = fix_div(fix_make_nof(g1-g0),dy); + db = fix_div(fix_make_nof(b1-b0),dy); + } + else { + t = x0; x0 = x1; x1 = t; + t = y0; y0 = y1; y1 = t; + + r = fix_make(r1, 0); g = fix_make(g1,0); b = fix_make(b1,0); + dr = fix_div(fix_make_nof(r0-r1),dy); + dg = fix_div(fix_make_nof(g0-g1),dy); + db = fix_div(fix_make_nof(b0-b1),dy); + } + + if ((fix_int(x0)) == (fix_int(x1))) { + x0 = fix_int (x0); + while (y0 <= y1) { + i = macro_get_ipal(r,g,b); + macro_plot_rgb(x0,y0,i); + y0++; + r += dr; g += dg; b += db; + } + } + else { + dx = fix_div ((x1 - x0), dy); + while (y0 <= y1) { + i = macro_get_ipal(r,g,b); + macro_plot_rgb(fix_fint(x0),y0,i); + x0 += dx; + y0++; + r += dr; g += dg; b += db; + } + } + } + else { /* dy == dx, walk the x axis, all integers */ + + x0 = fix_int (x0); x1 = fix_int (x1); + y0 = fix_int (y0); y1 = fix_int (y1); + + if (x0 < x1 ) { + r = fix_make(r0,0); g = fix_make(g0,0); b = fix_make(b0,0); + dr = fix_div(fix_make_nof(r1-r0),dx); + dg = fix_div(fix_make_nof(g1-g0),dx); + db = fix_div(fix_make_nof(b1-b0),dx); + } + else { + t = x0; x0 = x1; x1 = t; + t = y0; y0 = y1; y1 = t; + + r = fix_make(r1, 0); g = fix_make(g1,0); b = fix_make(b1,0); + dr = fix_div(fix_make_nof(r0-r1),dx); + dg = fix_div(fix_make_nof(g0-g1),dx); + db = fix_div(fix_make_nof(b0-b1),dx); + } + + if (y0 < y1) { + while (y0 <= y1) { + i = macro_get_ipal(r,g,b); + macro_plot_rgb(x0, y0, i); + x0++; + y0++; + r += dr; g += dg; b += db; + } + } + else { + while (y0 >= y1) { + i = macro_get_ipal(r,g,b); + macro_plot_rgb(x0, y0, i); + x0++; + y0--; + r += dr; g += dg; b += db; + } + } + } +} diff --git a/engine/src/Libraries/2D/Source/Gen/genuhlin.c b/engine/src/Libraries/2D/Source/Gen/genuhlin.c new file mode 100644 index 0000000..f121e7d --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genuhlin.c @@ -0,0 +1,43 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genuhlin.c $ + * $Revision: 1.1 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 02:35:29 $ +*/ + +#include "pixfill.h" + +/* draw an unclipped horizontal line with integral coordinates. + the correct fill is obtained via grd_pixel_fill, + which in turn calls gr_set_upixel + */ + +void gri_gen_uhline_fill (short x0, short y0, short x1, long c, long parm) +{ + short t; + + if (x0 > x1) { + t = x0; x0 = x1; x1 = t; + } + while (x0 <= x1) + grd_pixel_fill (c, parm, x0++, y0); +} + diff --git a/engine/src/Libraries/2D/Source/Gen/genulin.c b/engine/src/Libraries/2D/Source/Gen/genulin.c new file mode 100644 index 0000000..2548d2c --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genulin.c @@ -0,0 +1,192 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genulin.c $ + * $Revision: 1.1 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 02:31:36 $ +*/ + +#include "fix.h" +#include "plytyp.h" +#include "pixfill.h" + +/* This file is a slight modification of the routine gen_fix_uline + which was originally in the file genlin.c. It lives in + the uline_fill_table and is accessed via the line_fill_vector. + */ + +/* Draw an unclipped fixed-point line with calls to + grd_upixel_fill -- this fills in the pixel according to the + current fill type (in the grd_pixel_fill_table) */ + +/* NB: directionality policy -- reversible + Lines are drawn in order of increasing x or increasing y, + for lines that have greater x or y extent, repsectively. + This is done for all lines, including horiz., vert., and + 45' lines (increasing x). +*/ + + /* NB: endpoint policy -- inclusive + The left and top endpoints are inclusive, i.e. 'trunc'-ed. The + right and bottom endpoints exclude the ceiling. This is + calculated by subtracting epsilon (i.e. 1/65536) from its + fixed-point representation, then trunc'ing. + + This makes sense if you note that open interval on right + < ceil (x) + is the same as + <= trunc (x - e) + + This means that it might not be necessary to go through the ugliness + of swapping endpoints, but I'm not convinced that there aren't + precision problems involved. Since wire-poly's overdraw, it's + really necessary to ensure that the same points are always drawn. + + */ + +void gri_gen_uline_fill (long c, long parm, grs_vertex *v0, grs_vertex *v1) +{ + fix x0, x1, y0, y1; /* actually use x and y */ + fix dx, dy; /* delta's in x and y */ + fix t; /* temporary fix */ + + x0 = v0->x; y0 = v0->y; + x1 = v1->x; y1 = v1->y; + + /* set endpoints + note that this cannot go negative or change octant, since the == + case is excluded */ + + if (x0 < x1) + x1 -= 1; /* e.g. - epsilon */ + else if (x0 > x1) + x0 -= 1; + + if (y0 < y1) + y1 -= 1; + else if (y0 > y1) + y0 -= 1; + + dx = fix_trunc (x1) - fix_trunc(x0); /* x extent in pixels, (macro is flakey) */ + dx = fix_abs (dx); + dy = fix_trunc (y1) - fix_trunc(y0); /* y extent in pixels */ + dy = fix_abs (dy); + + if (dx == 0 && dy == 0) + return; + + /* three cases: absolute value dx < = > dy + + the variable (x0 or y0) for the long dimension is treated as a fix + and incremented using ++, the other is kept as fixpoint and + incremented by the fixpoint delta (dx or dy). Then it's shifted + to int for pixel drawing. + + the points are swapped if needed and the rgb initial and deltas + are calculated accordingly + + there are two sub-cases - a horizontal or vertical line, + and the dx or dy being added or subtracted. + + the endpoints are walked inclusively in all cases, see above + + 45' degree lines are explicitly special cased -- because it + all runs as integers, but it's probably not frequent enough + to justify the check + + */ + + if (dx > dy) { + + x0 = fix_int (x0); x1 = fix_int (x1); + + if (x0 > x1) { + t = x0; x0 = x1; x1 = t; + t = y0; y0 = y1; y1 = t; + } + + if ((fix_int(y0)) == (fix_int(y1))) { + y0 = fix_int (y0); + while (x0 <= x1) { + grd_pixel_fill (c, parm, x0, y0); + x0++; + } + } + else { + dy = fix_div ((y1 - y0), dx); + while (x0 <= x1) { + grd_pixel_fill(c, parm, x0, fix_int(y0)); + x0 ++; + y0 += dy; + } + } + } + + else if (dy > dx) { + + y0 = fix_int (y0); y1 = fix_int (y1); + + if (y0 > y1 ) { + t = x0; x0 = x1; x1 = t; + t = y0; y0 = y1; y1 = t; + } + + if ((fix_int(x0)) == (fix_int(x1))) { + x0 = fix_int (x0); + while (y0 <= y1) { + grd_pixel_fill(c, parm, x0, y0); + y0++; + } + } + else { + dx = fix_div ((x1 - x0), dy); + while (y0 <= y1) { + grd_pixel_fill(c, parm, fix_int(x0), y0); + x0 += dx; + y0++; + } + } + } + else { /* dy == dx, walk the x axis, all integers */ + + x0 = fix_int (x0); x1 = fix_int (x1); + y0 = fix_int (y0); y1 = fix_int (y1); + + if (x0 > x1 ) { + t = x0; x0 = x1; x1 = t; + t = y0; y0 = y1; y1 = t; + } + + if (y0 < y1) { + while (y0 <= y1) { + grd_pixel_fill(c, parm, x0, y0); + x0++; + y0++; + } + } + else { + while (y0 >= y1) { + grd_pixel_fill(c, parm, x0, y0); + x0++; + y0--; + } + } + } +} diff --git a/engine/src/Libraries/2D/Source/Gen/genuslin.c b/engine/src/Libraries/2D/Source/Gen/genuslin.c new file mode 100644 index 0000000..f106790 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genuslin.c @@ -0,0 +1,232 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genuslin.c $ + * $Revision: 1.1 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 02:33:59 $ + * + * Routine to draw an rgb shaded line to a flat 8 canvas. + * + * This file is part of the 2d library. + * + * $Log: genuslin.c $ + * Revision 1.1 1994/06/11 02:33:59 lmfeeney + * Initial revision + * + * Revision 1.6 1994/05/06 18:19:52 lmfeeney + * rewritten for greater accuracy and speed + * + * Revision 1.5 1993/10/02 01:04:01 kaboom + * Fixed clipping include files. + * + * Revision 1.4 1993/07/03 22:51:02 spaz + * Bugfix; treated clipper return code spastically + * + * Revision 1.3 1993/07/01 20:32:29 spaz + * Set last pixel explicitly to i1 in h and vlines + * + * Revision 1.2 1993/06/30 00:43:13 spaz + * Changed a check for (x0>x1) to fix_int(x0) version, + * because it was negating the delta uselessly for + * near-vertical lines. + * + * Revision 1.1 1993/06/22 20:14:28 spaz + * Initial revision + */ + +#include +#include "fix.h" +#include "plytyp.h" +#include "pixfill.h" + +/* Draw a gouraud-shaded line, specified by indices into the palette. + be warned, weird precision bugs abound (check out test programs + in /project/lib/src/2d/test). + + 5/94: Precision errors have been (entirely?) eliminated. See + correctness argument in note.txt. + +*/ + +/* + draw unclipped shaded line using grd_pixel_fill for fill + information -- note that the solid fill mode cannot use this routine + -- the gen routine only passes the computed color -- arguably it + should check, but that would be slow and painful and if the cline + function is accessed by the fill vector, this function is never + called when the fill mode is solid +*/ + +// MLA #pragma off (unreferenced) +#define macro_plot_i(x,y,i) grd_pixel_fill (i, parm, x, y) + +void gri_gen_usline_fill (long c, long parm, grs_vertex *v0, grs_vertex *v1) +{ + fix x0, y0, x1, y1; + fix dx, dy; /* delta's in x and y */ + fix t; /* temporary fix */ + + fix i0, i1; + fix di; /* # colors per x-pixel */ + + /* set endpoints + note that this cannot go negative or change octant, since the == + case is excluded */ + + x0 = v0->x; y0 = v0->y; + x1 = v1->x; y1 = v1->y; + + i0 = (fix) v0->i; i1 = (fix) v1->i; + + if (x0 < x1) { + x1 -= 1; /* e.g. - epsilon */ + } + else if (x0 > x1) { + x0 -= 1; + } + + if (y0 < y1) { + y1 -= 1; + } + else if (y0 > y1) { + y0 -= 1; + } + + dx = fix_trunc (x1) - fix_trunc(x0); /* x extent in pixels, (macro is flakey) */ + dx = fix_abs (dx); + dy = fix_trunc (y1) - fix_trunc(y0); /* y extent in pixels */ + dy = fix_abs (dy); + + if (dx == 0 && dy == 0) + return; + + /* three cases: absolute value dx < = > dy + + along the longer dimension, the fixpoint x0 (or y0) is treated + as an int + + the points are swapped if needed and the rgb initial and deltas + are calculated accordingly + + there are two sub-cases - a horizontal or vertical line, + and the dx or dy being added + + it might be better to use a funky bit check (tm) and keep an + integer y, similar to the canvas, rather than keep doing shift + in the inner loop + + the endpoints are walked inclusively in all cases, see above + + 45' degree lines are explicitly special cased -- because it + all runs as integers, but it's probably not frequent enough + to justify the check +*/ + + if (dx > dy) { + + x0 = fix_int (x0); x1 = fix_int (x1); + + if (x0 > x1) { + t = x0; x0 = x1; x1 = t; + t = y0; y0 = y1; y1 = t; + t = i0; i0 = i1; i1 = t; + } + + di = fix_div ((i1-i0), dx); + + if ((fix_int(y0)) == (fix_int(y1))) { + y0 = fix_int (y0); + while (x0 <= x1) { + macro_plot_i (x0, y0, fix_fint (i0)); + x0++; + i0 += di; + } + } + else { + dy = fix_div ((y1 - y0), dx); + while (x0 <= x1) { + macro_plot_i (x0, fix_int(y0), fix_fint(i0)); + x0 ++; + y0 += dy; + i0 += di; + } + } + } + else if (dy > dx) { + + y0 = fix_int (y0); y1 = fix_int (y1); + + if (y0 > y1) { + t = x0; x0 = x1; x1 = t; + t = y0; y0 = y1; y1 = t; + t = i0; i0 = i1; i1 = t; + } + + di = fix_div((i1-i0), dy); + + if ((fix_int(x0)) == (fix_int(x1))) { + x0 = fix_int (x0); + while (y0 <= y1) { + macro_plot_i (x0, y0, fix_fint(i0)); + y0++; + i0 += di; + } + } + else { + dx = fix_div ((x1 - x0), dy); + while (y0 <= y1) { + macro_plot_i(fix_fint(x0), y0, fix_fint (i0)); + x0 += dx; + y0++; + i0 += di; + } + } + } + else { /* dy == dx, walk the x axis, all integers */ + x0 = fix_int (x0); x1 = fix_int (x1); + y0 = fix_int (y0); y1 = fix_int (y1); + + if (x0 > x1 ) { + t = x0; x0 = x1; x1 = t; + t = y0; y0 = y1; y1 = t; + t = i0; i0 = i1; i1 = t; + } + + di = fix_div((i1-i0),dx); + + if (y0 < y1) { + while (y0 <= y1) { + macro_plot_i(x0, y0, fix_fint(i0)); + x0++; + y0++; + i0+= di; + } + } + else { + while (y0 >= y1) { + macro_plot_i(x0,y0, fix_fint (i0)); + x0++; + y0--; + i0 += di; + } + } + } +} + diff --git a/engine/src/Libraries/2D/Source/Gen/genuvlin.c b/engine/src/Libraries/2D/Source/Gen/genuvlin.c new file mode 100644 index 0000000..4a67d8a --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genuvlin.c @@ -0,0 +1,41 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genuvlin.c $ + * $Revision: 1.1 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 02:36:15 $ +*/ + +#include "pixfill.h" + +/* unclipped vertical line with integral coordinates. + fill type information is obtained from grd_pixel_fill, + which in turn calls gr_set_upixel +*/ +void gri_gen_uvline_fill (short x0, short y0, short y1, long c, long parm) +{ + short t; + + if (y0 > y1) { + t = y0; y0 = y1; y1 = t; + } + for (; y0<=y1; y0++) + grd_pixel_fill (c, parm, x0, y0); +} diff --git a/engine/src/Libraries/2D/Source/Gen/genvcply.c b/engine/src/Libraries/2D/Source/Gen/genvcply.c new file mode 100644 index 0000000..1d2242e --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genvcply.c @@ -0,0 +1,190 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genvcply.c $ + * $Revision: 1.4 $ + * $Author: baf $ + * $Date: 1994/02/14 20:38:31 $ + * + * This file is part of the 2d library. + * + * $Log: genvcply.c $ + * Revision 1.4 1994/02/14 20:38:31 baf + * Added dummy parameter to cpoly and spoly routines, for uniformity needed by 3D. + * + * Revision 1.3 1993/10/19 10:14:04 kaboom + * Updated calls to polygon routines for new arguments. + * + * Revision 1.2 1993/10/08 01:15:52 kaboom + * Changed quotes in #include lines to angle brackets for Watcom. + * + * Revision 1.1 1993/08/19 21:52:29 jaemz + * Initial revision + */ + +#include "grcply.h" +#include "plytyp.h" +#include "scrdat.h" + +void gen_vox_cpoly(fix x[4],fix y[4],fix dz[3],int near_ver,grs_bitmap *col,grs_bitmap *ht) +{ + int du,dv,initu,endu,initv,endv; + int i,j; + int c; + long z; + int far_ver; + + /* Test of broadcasting system */ + fix fdxdu,fdydu,fdxdv,fdydv; + fix fcurx,fcury; + fix fdxdz,fdydz; + fix oldcurx; + fix oldcury; + fix vlist[20]; + grs_vertex *vpl[4]; +// fix vlist[8]; +// grs_rgb clist[4]; + int xp[2][80]; + int yp[2][80]; + ulong pp[2][80]; + int currow = 0; + + vpl[0]=(grs_vertex *)vlist; vpl[1]=(grs_vertex *)(vlist+5); + vpl[2]=(grs_vertex *)(vlist+10); vpl[3]=(grs_vertex *)(vlist+15); + far_ver = (near_ver+2)%4; + + /* How to scan given near_ver */ + if (near_ver == 0 || near_ver == 3) { initu = col->w -1; du = -1; endu = -1; } + else { initu = 0; du = 1; endu = col->w ; } + if (near_ver < 2) { initv = col->h-1; dv = -1; endv = -1; } + else { initv = 0; dv = 1; endv = col->h; } + + fdxdu = (x[1]-x[0])/(col->w - 1); + fdydu = (y[1]-y[0])/(col->w - 1); + fdxdv = (x[2]-x[1])/(col->w - 1); + fdydv = (y[2]-y[1])/(col->w - 1); + + fdxdz = dz[0]; + fdydz = dz[1]; + + oldcurx = fcurx = x[0] + fdxdu*initu + fdxdv*initv; + oldcury = fcury = y[0] + fdydu*initu + fdydv*initv; + + fdxdu *= du; + fdydu *= du; + fdxdv *= dv; + fdydv *= dv; + + for(j=initv;j!=endv;j+=dv) { + for(i=initu;i!=endu;i+=du) { + c = *( col->bits +i +j*col->row); + //c = get_pal(col,i,j); + if (c != 0) { + z = *(ht->bits + i + j*col->row); + //z = get_pal(ht,i,j); + /* Put in array */ + xp[currow][i] = (fcurx + fdxdz*z); + yp[currow][i] = (fcury + fdydz*z); + pp[currow][i] = c; + } + else { + pp[currow][i] = 0; + } + fcurx += fdxdu; + fcury += fdydu; + } + if (currow==1) { + /* plot the triangles */ + for(i=initu;i!=endu;i+=du) { + /* check for any transparents */ + if (pp[0][i]*pp[1][i]*pp[1][i+du]*pp[0][i+du] != 0) { +// vlist[0] = xp[0][i]; +// vlist[1] = yp[0][i]; +// clist[0] = grd_bpal[pp[0][i]]; + vpl[0]->x = xp[0][i]; + vpl[0]->y = yp[0][i]; + vpl[0]->u = fix_make(grd_pal[pp[0][i]+0],0x8000); + vpl[0]->v = fix_make(grd_pal[pp[0][i]+1],0x8000); + vpl[0]->w = fix_make(grd_pal[pp[0][i]+2],0x8000); + +// vlist[4] = xp[1][i+du]; +// vlist[5] = yp[1][i+du]; +// clist[2] = grd_bpal[pp[1][i+du]]; + vpl[2]->x = xp[1][i+du]; + vpl[2]->y = yp[1][i+du]; + vpl[2]->u = fix_make(grd_pal[pp[1][i+du]+0],0x8000); + vpl[2]->v = fix_make(grd_pal[pp[1][i+du]+1],0x8000); + vpl[2]->w = fix_make(grd_pal[pp[1][i+du]+2],0x8000); + + if (du*dv > 0) { +// vlist[2] = xp[0][i+du]; +// vlist[3] = yp[0][i+du]; +// clist[1] = grd_bpal[pp[0][i+du]]; + vpl[1]->x = xp[0][i+du]; + vpl[1]->y = yp[0][i+du]; + vpl[1]->u = fix_make(grd_pal[pp[0][i+du]+0],0x8000); + vpl[1]->v = fix_make(grd_pal[pp[0][i+du]+1],0x8000); + vpl[1]->w = fix_make(grd_pal[pp[0][i+du]+2],0x8000); + +// vlist[6] = xp[1][i]; +// vlist[7] = yp[1][i]; +// clist[3] = grd_bpal[pp[1][i]]; + vpl[3]->x = xp[1][i+du]; + vpl[3]->y = yp[1][i+du]; + vpl[3]->u = fix_make(grd_pal[pp[1][i+du]+0],0x8000); + vpl[3]->v = fix_make(grd_pal[pp[1][i+du]+1],0x8000); + vpl[3]->w = fix_make(grd_pal[pp[1][i+du]+2],0x8000); + } + else { +// vlist[6] = xp[0][i+du]; +// vlist[7] = yp[0][i+du]; +// clist[3] = grd_bpal[pp[0][i+du]]; + vpl[3]->x = xp[0][i+du]; + vpl[3]->y = yp[0][i+du]; + vpl[3]->u = fix_make(grd_pal[pp[0][i+du]+0],0x8000); + vpl[3]->v = fix_make(grd_pal[pp[0][i+du]+1],0x8000); + vpl[3]->w = fix_make(grd_pal[pp[0][i+du]+2],0x8000); + +// vlist[2] = xp[1][i]; +// vlist[3] = yp[1][i]; +// clist[1] = grd_bpal[pp[1][i]]; + vpl[1]->x = xp[1][i]; + vpl[1]->y = yp[1][i]; + vpl[1]->u = fix_make(grd_pal[pp[1][i]+0],0x8000); + vpl[1]->v = fix_make(grd_pal[pp[1][i]+1],0x8000); + vpl[1]->w = fix_make(grd_pal[pp[1][i]+2],0x8000); + } + + gr_ucpoly(0,4,vpl); + } + } + /* copy from 1 to 0 */ + for(i=initu;i!=endu;i+=du) { + xp[0][i] = xp[1][i]; + yp[0][i] = yp[1][i]; + pp[0][i] = pp[1][i]; + } + } + else ++currow; + fcurx = (oldcurx += fdxdv); + fcury = (oldcury += fdydv); + } +} + + diff --git a/engine/src/Libraries/2D/Source/Gen/genvlin.c b/engine/src/Libraries/2D/Source/Gen/genvlin.c new file mode 100644 index 0000000..dba7639 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genvlin.c @@ -0,0 +1,97 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genvlin.c $ + * $Revision: 1.6 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 02:27:59 $ + * + * Generic routines for drawing vertical lines. + * + * This file is part of the 2d library. + * + * $Log: genvlin.c $ + * Revision 1.6 1994/06/11 02:27:59 lmfeeney + * moved unclipped line drawer, two versions of clipped line drawer, + * one for each i\f - do clipping inline, then call unclipped drawer + * handles all fill types + * + * Revision 1.5 1993/10/19 09:51:35 kaboom + * Replaced #include "grd.h" with new headers split from grd.h. + * + * Revision 1.4 1993/10/02 01:17:36 kaboom + * Changed include of clip.h to include of clpcon.h and/or clpfcn.h. + * + * Revision 1.3 1993/04/29 18:40:59 kaboom + * Changed include of gr.h to smaller more specific grxxx.h. + * + * Revision 1.2 1993/03/02 19:46:13 kaboom + * Changed to not draw the bottommost pixel. + * + * Revision 1.1 1993/02/25 23:12:01 kaboom + * Initial revision + */ + +#include "ctxmac.h" +#include "clpcon.h" +#include "grlin.h" + +/* clipped vertical line with integral coordinates. returns clip + code. */ + +int gen_vline (short x0, short y0, short y1) +{ + int r; + + r = grd_vline_clip_fill (x0, y0, y1, gr_get_fcolor(), gr_get_fill_parm()); + + return r; + +} + +int gri_vline_clip_fill (short x0, short y0, short y1, long c, long parm) +{ + short t; + int r = CLIP_NONE; + + /* the clip code needs to be buried in here so that this can + be called from an interrupt handle ! + */ + + if (y0 > y1) { + t = y0; y0 = y1; y1 = t; + } + + if (x0=grd_clip.right || + y1=grd_clip.bot) + return CLIP_ALL; + + if (y0 < grd_clip.top) { + r |= CLIP_TOP; + y0 = grd_clip.top; + } + if (y1 >= grd_clip.bot) { + r |= CLIP_BOT; + y1 = grd_clip.bot-1; + } + + grd_uvline_fill (x0, y0, y1, c, parm); + + return r; +} diff --git a/engine/src/Libraries/2D/Source/Gen/genvpoly.c b/engine/src/Libraries/2D/Source/Gen/genvpoly.c new file mode 100644 index 0000000..88c2475 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genvpoly.c @@ -0,0 +1,148 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genvpoly.c $ + * $Revision: 1.3 $ + * $Author: kaboom $ + * $Date: 1993/10/19 10:14:18 $ + * + * This file is part of the 2d library. + * + * $Log: genvpoly.c $ + * Revision 1.3 1993/10/19 10:14:18 kaboom + * Updated calls to polygon routines for new arguments. + * + * Revision 1.2 1993/10/08 01:15:53 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.1 1993/08/19 21:52:48 jaemz + * Initial revision + */ + +#include "grs.h" +#include "grply.h" +#include "general.h" + +void gen_vox_poly(fix x[4],fix y[4],fix dz[3],int near_ver,grs_bitmap *col,grs_bitmap *ht) +{ + int du,dv,initu,endu,initv,endv; + int i,j; + int c; + long z; + int far_ver; + + /* Test of broadcasting system */ + fix fdxdu,fdydu,fdxdv,fdydv; + long fcurx,fcury; + fix fdxdz,fdydz; + long oldcurx; + long oldcury; + fix vlist[8]; + fix *vpl[4]; + int xp[2][80]; + int yp[2][80]; + ulong pp[2][80]; + int currow = 0; + + vpl[0]=vlist; vpl[1]=vlist+2; vpl[2]=vlist+4; vpl[3]=vlist+6; + far_ver = (near_ver+2)%4; + + /* How to scan given near_ver */ + if (near_ver == 0 || near_ver == 3) { initu = col->w -1; du = -1; endu = -1; } + else { initu = 0; du = 1; endu = col->w ; } + if (near_ver < 2) { initv = col->h-1; dv = -1; endv = -1; } + else { initv = 0; dv = 1; endv = col->h; } + + fdxdu = (x[1]-x[0])/(col->w - 1); + fdydu = (y[1]-y[0])/(col->w - 1); + fdxdv = (x[2]-x[1])/(col->h - 1); + fdydv = (y[2]-y[1])/(col->h - 1); + + fdxdz = dz[0]; + fdydz = dz[1]; + + oldcurx = fcurx = x[0] + fdxdu*initu + fdxdv*initv; + oldcury = fcury = y[0] + fdydu*initu + fdydv*initv; + + fdxdu *= du; + fdydu *= du; + fdxdv *= dv; + fdydv *= dv; + + for(j=initv;j!=endv;j+=dv) { + for(i=initu;i!=endu;i+=du) { + c = *(col->bits + i + j*col->row); + //c = get_pal(col,i,j); + if (c != 0) { + z = *(ht->bits +i + j*ht->row); + //z = get_pal(ht,i,j); + /* Put in array */ + xp[currow][i] = (fcurx + fdxdz*z); + yp[currow][i] = (fcury + fdydz*z); + pp[currow][i] = c; + } + else { + pp[currow][i] = 0; + } + fcurx += fdxdu; + fcury += fdydu; + } + if (currow==1) { + /* plot the triangles */ + for(i=initu;i!=endu;i+=du) { + /* check for any transparents */ + if (pp[0][i]*pp[1][i]*pp[1][i+du]*pp[0][i+du] != 0) { + vlist[0] = xp[0][i]; + vlist[1] = yp[0][i]; + + vlist[4] = xp[1][i+du]; + vlist[5] = yp[1][i+du]; + + if(du*dv<1) { + vlist[2] = xp[0][i+du]; + vlist[3] = yp[0][i+du]; + + vlist[6] = xp[1][i]; + vlist[7] = yp[1][i]; + } + else { + vlist[6] = xp[0][i+du]; + vlist[7] = yp[0][i+du]; + + vlist[2] = xp[1][i]; + vlist[3] = yp[1][i]; + } + + gr_poly(pp[0][i],4,(grs_vertex **)vpl); + } + } + /* copy from 1 to 0 */ + for(i=initu;i!=endu;i+=du) { + xp[0][i] = xp[1][i]; + yp[0][i] = yp[1][i]; + pp[0][i] = pp[1][i]; + } + } + else ++currow; + fcurx = (oldcurx += fdxdv); + fcury = (oldcury += fdydv); + } +} + + diff --git a/engine/src/Libraries/2D/Source/Gen/genvrect.c b/engine/src/Libraries/2D/Source/Gen/genvrect.c new file mode 100644 index 0000000..081d8af --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genvrect.c @@ -0,0 +1,114 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genvrect.c $ + * $Revision: 1.3 $ + * $Author: kaboom $ + * $Date: 1993/10/19 09:57:56 $ + * + * This file is part of the 2d library. + * + * $Log: genvrect.c $ + * Revision 1.3 1993/10/19 09:57:56 kaboom + * Replaced #include new headers. + * + * Revision 1.2 1993/10/08 01:15:54 kaboom + * Changed quotes in #include lines to angle brackets for Watcom. + * + * Revision 1.1 1993/08/19 21:53:06 jaemz + * Initial revision + */ + +#include "ctxmac.h" +#include "grrect.h" +#include "general.h" + +/* x and y are the screen x and y's in fixed point of the vertices of this voxel piece + dz[0] is the difference in x for a delta one in z + dz[1] is the difference in y for a delta one in z + near_ver is which vertex is closest to the viewer, so it knows which way to traverse + col is the color map + ht is the height map, 0 means at the surface of the bounding box, positive goes in + dotw and doth are the height and width of the rectangles to use at each point */ + +void gen_vox_rect(fix x[4],fix y[4],fix dz[3],int near_ver,grs_bitmap *col,grs_bitmap *ht,int dotw,int doth) +{ + int du,dv,initu,endu,initv,endv; + int i,j; + int c; + long z; + int far_ver; + + /* Test of broadcasting system */ + fix fdxdu,fdydu,fdxdv,fdydv; + fix fcurx,fcury; + long fxp,fyp; + long fdxdz,fdydz; + long oldcurx; + long oldcury; + + far_ver = (near_ver+2)%4; + + /* How to scan given near_ver */ + if (near_ver == 0 || near_ver == 3) { initu = col->w -1; du = -1; endu = -1; } + else { initu = 0; du = 1; endu = col->w ; } + if (near_ver < 2) { initv = col->h-1; dv = -1; endv = -1; } + else { initv = 0; dv = 1; endv = col->h; } + + fdxdu = (x[1]-x[0])/(col->w-1); + fdydu = (y[1]-y[0])/(col->w-1); + fdxdv = (x[2]-x[1])/(col->h-1); + fdydv = (y[2]-y[1])/(col->h-1); + + fdxdz = dz[0]; + fdydz = dz[1]; + + oldcurx = fcurx = x[0] + fdxdu*initu + fdxdv*initv; + oldcury = fcury = y[0] + fdydu*initu + fdydv*initv; + + fdxdu *= du; + fdydu *= du; + fdxdv *= dv; + fdydv *= dv; + + for(j=initv;j!=endv;j+=dv) { + for(i=initu;i!=endu;i+=du) { + c = *(col->bits + i + j*col->row); + //c = get_pal(col,i,j); + if (c != 0) { + z = *(ht->bits + i+ j*ht->row); + //z = get_pal(ht,i,j); + fxp = (fcurx + fdxdz*z)>>16; + fyp = (fcury + fdydz*z)>>16; + + gr_set_fcolor(c); + gr_rect(fxp,fyp,fxp+dotw,fyp+doth); + } + fcurx += fdxdu; + fcury += fdydu; + } + fcurx = (oldcurx += fdxdv); + fcury = (oldcury += fdydv); + } +} + + + + + diff --git a/engine/src/Libraries/2D/Source/Gen/genwclin.c b/engine/src/Libraries/2D/Source/Gen/genwclin.c new file mode 100644 index 0000000..fe9d0e7 --- /dev/null +++ b/engine/src/Libraries/2D/Source/Gen/genwclin.c @@ -0,0 +1,136 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/genwclin.c $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/10/17 15:00:03 $ + * + * Routines to draw wire polys. + * + * This file is part of the 2d library. + * + */ + +#include "pixfill.h" +#include "plytyp.h" +#include "scrdat.h" + +#define gr_get_ipal_index(r,g,b) (long) ((((r)>>19) &0x1f) | (((g)>>14) & 0x3e0) | (((b)>>9) & 0x7c00)) +#define do_hline_inc_x \ + do { \ + c=grd_ipal[gr_get_ipal_index(r0,g0,b0)]; \ + grd_pixel_fill(c,parm,x,y); \ + r0+=dr,g0+=dg,b0+=db; \ + x++; \ + } while (xx_new) + +void gri_gen_wire_poly_ucline(long c, long parm, grs_vertex *v0, grs_vertex *v1) { + int d,y,y_max,x,x_new; + fix x0,y0,r0,b0,g0; + fix x1,y1,r1,b1,g1; + fix dr,dg,db,x_fix,dx; + + if (v1->y>v0->y) { + y=fix_cint(v0->y); + y_max=fix_cint(v1->y); + } else { + y=fix_cint(v1->y); + y_max=fix_cint(v0->y); + } + + /* horizontal? */ + if (y_max-y<=1) { + if (v1->x>v0->x) { + x_new=fix_cint(v1->x),x=fix_cint(v0->x); + r0=fix_make(v0->u,0),g0=fix_make(v0->v,0),b0=fix_make(v0->w,0); + r1=fix_make(v1->u,0),g1=fix_make(v1->v,0),b1=fix_make(v1->w,0); + } + else { + x_new=fix_cint(v0->x),x=fix_cint(v1->x); + r0=fix_make(v1->u,0),g0=fix_make(v1->v,0),b0=fix_make(v1->w,0); + r1=fix_make(v0->u,0),g1=fix_make(v0->v,0),b1=fix_make(v0->w,0); + } + d=x_new-x; + if (d>0) { + dr=(r1-r0)/d; + dg=(g1-g0)/d; + db=(b1-b0)/d; + do_hline_inc_x; + } + return; + } + + /* not horizontal */ + if (v1->y>v0->y) { + y0=v0->y,x0=v0->x; + r0=fix_make(v0->u,0),g0=fix_make(v0->v,0),b0=fix_make(v0->w,0); + y1=v1->y,x1=v1->x; + r1=fix_make(v1->u,0),g1=fix_make(v1->v,0),b1=fix_make(v1->w,0); + } else { + y1=v0->y,x1=v0->x; + r1=fix_make(v0->u,0),g1=fix_make(v0->v,0),b1=fix_make(v0->w,0); + y0=v1->y,x0=v1->x; + r0=fix_make(v1->u,0),g0=fix_make(v1->v,0),b0=fix_make(v1->w,0); + } + dx=fix_div(x1-x0,y1-y0); + if (fix_abs(dx)>FIX_UNIT) { + d=fix_cint(x1)-fix_cint(x0); + if (d<0) d=-d; + } else { + d=y_max-y; + } + dr=(r1-r0)/d; + dg=(g1-g0)/d; + db=(b1-b0)/d; + x=fix_cint(x0); + x_fix=x0+fix_mul(fix_ceil(y0)-y0,dx); + x_new=fix_cint(x_fix); + + /* draw line */ + if (dx>=0) { + do_hline_inc_x; + do { + x=x_new; + x_new=fix_cint(x_fix+=dx); + do_hline_inc_x; + } while ((++y). + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/genwlin.c $ + * $Revision: 1.1 $ + * $Author: kevin $ + * $Date: 1994/08/04 09:54:18 $ + * + * Routines to draw wire polys. + * + * This file is part of the 2d library. + * + */ + +#include "ctxmac.h" +#include "pixfill.h" +#include "plytyp.h" + +#define gr_get_ipal_index(r,g,b) (long) ((((r)>>19) &0x1f) | (((g)>>14) & 0x3e0) | (((b)>>9) & 0x7c00)) +#define do_hline_inc_x \ + do { \ + grd_pixel_fill(c,parm,x,y); \ + x++; \ + } while (xx_new) + +void gri_gen_wire_poly_uline(long c, long parm, grs_vertex *v0, grs_vertex *v1) { + int y,y_max,x,x_new; + fix x0,y0; + fix x1,y1; + fix x_fix,dx; + + parm=gr_get_fill_parm(); + if (v1->y>v0->y) { + y=fix_cint(v0->y); + y_max=fix_cint(v1->y); + } else { + y=fix_cint(v1->y); + y_max=fix_cint(v0->y); + } + + /* horizontal? */ + if (y_max-y<=1) { + if (v1->x>v0->x) { + x_new=fix_cint(v1->x),x=fix_cint(v0->x); + } + else { + x_new=fix_cint(v0->x),x=fix_cint(v1->x); + } + do_hline_inc_x; + return; + } + + /* not horizontal */ + if (v1->y>v0->y) { + y0=v0->y,x0=v0->x; + y1=v1->y,x1=v1->x; + } else { + y1=v0->y,x1=v0->x; + y0=v1->y,x0=v1->x; + } + dx=fix_div(x1-x0,y1-y0); + x=fix_cint(x0); + x_fix=x0+fix_mul(fix_ceil(y0)-y0,dx); + x_new=fix_cint(x_fix); + + /* draw line */ + if (dx>=0) { + do_hline_inc_x; + do { + x=x_new; + x_new=fix_cint(x_fix+=dx); + do_hline_inc_x; + } while ((++y). + +*/ +// device driver for standard Mac 640x480 256 color mode +#include "grnull.h" +#include "grd.h" +#include "lg.h" +#include "MacDev.h" +#include "cnvdrv.h" +#include "cnvtab.h" +#include "fcntab.h" +#include "lintab.h" + +// NOTE! +// +// MacDev (and some other 2d code) relies on the fact that several globals are setup +// in the main game code (InitMac.c and ShockBitmap.c). gScreenAddress & gScreenRowbytes +// are assumed to point to the right part of the main screen, and gMainColorHand is +// assumed to be the main color table handle for the game. So make sure you call CheckConfig +// and SetupOffscreenBitmaps before you start up the 2D system. This stuff should be inside +// the 2D system itself, but its kind of outside of the realm of the 2D library as it exists, +// so its not here. +// + +typedef void (**ptr_type)(); + +// Mac device function table +void (**mac_device_table[])() = { + (ptr_type)gr_null, // init device + (ptr_type)gr_null, // close device + (ptr_type)mac_set_mode, // set mode + (ptr_type)gr_null, // get mode + (ptr_type)mac_set_state, // set state + (ptr_type)mac_get_state, // get state + (ptr_type)gr_null, // was mac_stat_htrace + (ptr_type)gr_null, // was mac_stat_vtrace + (ptr_type)mac_set_pal, // set palette + (ptr_type)mac_get_pal, // get palette + (ptr_type)gr_null, // set width + (ptr_type)gr_null, // get width + (ptr_type)gr_null, // set focus, was mac_set_focus + (ptr_type)gr_null, // get focus, was mac_get_focus + (ptr_type)gr_null, // canvas table + (ptr_type)gr_null // span table +}; + +//======================================================================== +// Mac specific device routines +//======================================================================== + +extern intptr_t *gScreenAddress; + +//------------------------------------------------------------------------ +// init the graphics mode, set up function tables and screen base address +// +void mac_set_mode(void) { + DEBUG("%s: Initializing graphics mode", __FUNCTION__ ); + grd_mode_cap.vbase = gScreenAddress; + + grd_canvas_table_list[BMT_DEVICE] = flat8_canvas_table; + grd_function_table_list[BMT_DEVICE] = (grt_function_table *)flat8_function_table; + grd_uline_fill_table_list[BMT_DEVICE] = (grt_uline_fill_table *)flat8_uline_fill_table; +} + +//------------------------------------------------------------------------ +// set the color palette (copy entries into gMainColorHand, then call SetEntries +// and ResetCTSeed). + +uchar backup_pal[768]; +void mac_set_pal(int start, int n, uint8_t *pal_data) { + extern void SetSDLPalette(int index, int count, uchar *pal); + + // HAX: Only update when given a whole palette! + if (start == 0 && n == 256) { + SetSDLPalette(start, n, pal_data); + + // Save the palette + memmove(&backup_pal, pal_data, sizeof(uchar) * 768); + } +} + +//------------------------------------------------------------------------ +// get the current color palette (copy entries from gMainColorHand) +void mac_get_pal(int start, int n, uint8_t *pal_data) { + memmove(pal_data, &backup_pal, sizeof(uchar) * 768); +} + +// set and get state don't currently do anything, since its not apparent +// any of the stuff from the VGA driver (text mode, palette stuff) is necessary +// (if the game uses these calls to remember palettes between changes we may need +// to implement something here +//------------------------------------------------------------------------ +int mac_set_state(void *buf, int clear) { return (0); } + +//------------------------------------------------------------------------ +int mac_get_state(void *buf, int flags) { return (0); } diff --git a/engine/src/Libraries/2D/Source/MacDev.h b/engine/src/Libraries/2D/Source/MacDev.h new file mode 100644 index 0000000..b8589ef --- /dev/null +++ b/engine/src/Libraries/2D/Source/MacDev.h @@ -0,0 +1,41 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * + * Prototypes and macros for general purpose Mac code. + * + * This file is part of the 2d library. + * + * Revision 1.1 1994/11/02 MLA + * Initial revision + */ + +#include "stdint.h" + +#ifndef __MACDEV_H +#define __MACDEV_H +extern void (**mac_device_table[])(); + +extern int mac_set_state(void *buf,int clear); +extern int mac_get_state(void *buf,int flags); +extern void mac_set_mode(void); +extern void mac_set_pal (int start, int n, uint8_t *pal_data); +extern void mac_get_pal (int start, int n, uint8_t *pal_data); + +#endif /* !__MACDEV_H */ diff --git a/engine/src/Libraries/2D/Source/RSD/RSDUnpack.c b/engine/src/Libraries/2D/Source/RSD/RSDUnpack.c new file mode 100644 index 0000000..634a46c --- /dev/null +++ b/engine/src/Libraries/2D/Source/RSD/RSDUnpack.c @@ -0,0 +1,177 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Rsd unpacking into a bitmap where row=width. +// +// 68K and PowerPC versions +// + +#include "grs.h" +#include + +//---------------------------------------------------------------------------- +// PowerPC version +#define kMinLongLoop 4 // minimum # of bytes to need before using long store loop + +uchar *gr_rsd8_unpack(uchar *src, uchar *dest) + { + uchar code,val; + short count,count2; + ushort longcode; + uint32_t longval, *longdest, *longsrc; + + do + { + code = *(src++); + if (!code) // run of bytes + { + count = *(src++); // get count + val = *(src++); // get val + + if (count>=kMinLongLoop) // if at least kMinLongLoop bytes, do long word stuff + { + longval = val + (((uint32_t) val)<<8); + longval += longval<<16; + count2 = count>>2; + count &= 3; + longdest = (uint32_t *) dest; + + while (count2--) + *(longdest++) = longval; + dest = (uchar *) longdest; + } + + // do rest of bytes + while (count--) + *(dest++) = val; + } + else if (code<0x80) // dump (copy) bytes + { + count = code; + if (code>=kMinLongLoop) // if at least kMinLongLoop bytes, do long word stuff + { + count2 = count>>2; + count &= 3; + longdest = (uint32_t *) dest; + longsrc = (uint32_t *) src; + + while (count2--) + *(longdest++) = *(longsrc++); + dest = (uchar *) longdest; + src = (uchar *) longsrc; + } + + // do rest of bytes + while (count--) + *(dest++) = *(src++); + } + else if (code>0x80) // skip (zero) bytes) + { + count = code & 0x007f; // clear high byte + val = longval = 0L; + + if (count>=kMinLongLoop) // if at least kMinLongLoop bytes, do long word stuff + { + count2 = count>>2; + count &= 3; + longdest = (uint32_t *) dest; + + while (count2--) + *(longdest++) = longval; + dest = (uchar *) longdest; + } + + // do rest of bytes + while (count--) + *(dest++) = val; + } + else // long opcode + { + longcode = * (ushort *) src; + src += 2L; + + if (!longcode) break; // done? + else if (longcode<0x8000) // skip (zero) + { + count = longcode; + val = longval = 0L; + + if (count>=kMinLongLoop) // if at least kMinLongLoop bytes, do long word stuff + { + count2 = count>>2; + count &= 3; + longdest = (uint32_t *) dest; + + while (count2--) + *(longdest++) = longval; + dest = (uchar *) longdest; + } + + // do rest of bytes + while (count--) + *(dest++) = val; + } + else if (longcode<0xC000) // dump (copy) + { + count = longcode & 0x7fff; // clear high bit + + if (count>=kMinLongLoop) // if at least kMinLongLoop bytes, do long word stuff + { + count2 = count>>2; + count &= 3; + longdest = (uint32_t *) dest; + longsrc = (uint32_t *) src; + + while (count2--) + *(longdest++) = *(longsrc++); + dest = (uchar *) longdest; + src = (uchar *) longsrc; + } + + // do rest of bytes + while (count--) + *(dest++) = *(src++); + } + else // run of bytes + { + count = longcode & 0x3fff; + val = *(src++); // get val + + if (count>=kMinLongLoop) // if at least kMinLongLoop bytes, do long word stuff + { + longval = val + (((uint32_t) val)<<8); + longval += longval<<16; + count2 = count>>2; + count &= 3; + longdest = (uint32_t *) dest; + + while (count2--) + *(longdest++) = longval; + dest = (uchar *) longdest; + } + + // do rest of bytes + while (count--) + *(dest++) = val; + } + } + } + while (true); + + return(dest); + } diff --git a/engine/src/Libraries/2D/Source/RSD/rsd.h b/engine/src/Libraries/2D/Source/RSD/rsd.h new file mode 100644 index 0000000..2d0aeb0 --- /dev/null +++ b/engine/src/Libraries/2D/Source/RSD/rsd.h @@ -0,0 +1,107 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/rsd.h $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1993/10/26 02:14:30 $ + * + * Constants and macros for RSD8 bitmap processing. + * + * $Log: rsd.h $ + * Revision 1.2 1993/10/26 02:14:30 kevin + * Changed algorithm so that rsd_src is never advanced + * past the end of the bitmap. (It stays pointed to the + * end when the end is reached.) + * + * Revision 1.1 1992/11/12 13:51:29 kaboom + * Initial revision + * + */ + +#define RSD_RUN 0 +#define RSD_SKIP 1 +#define RSD_DUMP 2 + +/* this is a pretty specific-use macro for getting the next rsd token from an + rsd input buffer. the source buffer has to be named rsd_src, the token's + code goes in rsd_code, and the count for the operation goes in rsd_count. + after a call of this macro, rsd_src is advanced to the actual data for the + code (pixel data for dump, run value for run) if there is any, or to the + beginning of the next token (for skip). */ +#define RSD_GET_TOKEN() \ +{ \ + if (*rsd_src == 0) /* run */ \ + { \ + rsd_code = RSD_RUN; \ + rsd_count = rsd_src[1]; \ + rsd_src += 2; \ +/* mprintf ("run %d ",count); */ \ + } \ + else if (*rsd_src < 0x80) /* dump */ \ + { \ + rsd_code = RSD_DUMP; \ + rsd_count = *rsd_src; \ + rsd_src++; \ +/* mprintf ("dump %d ",count); */ \ + } \ + else if (*rsd_src != 0x80) /* skip */ \ + { \ + rsd_code = RSD_SKIP; \ + rsd_count = *rsd_src & 0x7f; \ + rsd_src++; \ +/* mprintf ("skip %d ",count); */ \ + } \ + else /* long op */ \ + { \ + ushort *rsd_usrc = (ushort *)++rsd_src; \ + \ + if (*rsd_usrc >= 0x8000) \ + { \ + if (*rsd_usrc >= 0xc000) /* long run */ \ + { \ + rsd_code = RSD_RUN; \ + rsd_count = *rsd_usrc & 0x3fff; \ + rsd_src += 2; \ +/* mprintf ("run %d ",count); */ \ + } \ + else /* long dump */ \ + { \ + rsd_code = RSD_DUMP; \ + rsd_count = *rsd_usrc & 0x7fff; \ + rsd_src += 2; \ +/* mprintf ("dump %d ",count); */ \ + } \ + } \ + else if (*rsd_usrc != 0) /* long skip */ \ + { \ + rsd_code = RSD_SKIP; \ + rsd_count = *rsd_usrc; \ + rsd_src += 2; \ +/* mprintf ("skip %d ",count); */ \ + } \ + else { \ +/* subsequent uses of RSD_GET_TOKEN should also return to rsd_done.*/ \ + rsd_code = RSD_SKIP; \ + rsd_src-- ; \ + goto rsd_done; \ + } \ + } \ +} + diff --git a/engine/src/Libraries/2D/Source/RSD/rsdcvt.c b/engine/src/Libraries/2D/Source/RSD/rsdcvt.c new file mode 100644 index 0000000..38291e4 --- /dev/null +++ b/engine/src/Libraries/2D/Source/RSD/rsdcvt.c @@ -0,0 +1,147 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/rsdcvt.c $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/05/19 15:10:27 $ + * + * Routine for unpacking an rsd bitmap to a flat8 bitmap. + * Uses memory provided externally. + * + * This file is part of the 2d library. + * + * $Log: rsdcvt.c $ + * Revision 1.2 1994/05/19 15:10:27 kevin + * Check if BMF_TLUC8 flag is set and if so, set + * uncompressed bitmap type to BMT_TLUC8. + * + * Revision 1.1 1993/12/28 16:32:09 kevin + * Initial revision + * + */ +#include +#include +#include "grs.h" +#include "bitmap.h" +#include "rsd.h" +#define _RSDCVT_C +#include "rsdunpck.h" +#include "lg.h" + +uchar *grd_unpack_buf=NULL; + +/*************************************************/ +/* Puts 0's in place of skips and pads with 0's. */ +/* i.e., grd_unpack_buf is entirely overwritten. */ +/*************************************************/ +int gr_rsd8_convert(grs_bitmap *sbm, grs_bitmap *dbm) +{ + short x_right,y_bot; /* opposite edges of bitmap */ + short x,y; /* current position */ + int over_run; /* unwritten right edge */ + uchar *p_dst; + uchar *rsd_src; /* rsd source buffer */ + short rsd_code; /* last rsd opcode */ + short rsd_count; /* count for last opcode */ + short op_count; /* operational count */ + + if (grd_unpack_buf==NULL) return GR_UNPACK_RSD8_NOBUF; + if (sbm->type != BMT_RSD8) return GR_UNPACK_RSD8_NOTRSD; + *dbm = *sbm; // LG_memcpy (dbm, sbm, sizeof (*sbm)); + if (sbm->flags&BMF_TLUC8) + dbm->type = BMT_TLUC8; + else + dbm->type = BMT_FLAT8; + dbm->bits = grd_unpack_buf; + if (dbm->w==dbm->row) p_dst=gr_rsd8_unpack(sbm->bits,dbm->bits); + else { + rsd_src = sbm->bits; + x = y = rsd_count = 0; + x_right = sbm->w; + y_bot = sbm->h; + p_dst = dbm->bits; + over_run=dbm->row-sbm->w; + + /* process each scanline, keeping track of x and splitting opcodes that + span across more than one line. */ + while (y < y_bot) { + /* do enough opcodes to get to the end of the current scanline. */ + while (x < x_right) { + if (rsd_count == 0) + RSD_GET_TOKEN (); + if (x+rsd_count <= x_right) { + /* current code is all on this scanline. */ + switch (rsd_code) { + case RSD_RUN: + LG_memset (p_dst, *rsd_src, rsd_count); + rsd_src++; + break; + case RSD_SKIP: + LG_memset (p_dst, kSkipColor, rsd_count); + break; + default: /* RSD_DUMP */ + LG_memcpy (p_dst, rsd_src, rsd_count); + rsd_src += rsd_count; + break; + } + x += rsd_count; + p_dst += rsd_count; + rsd_count = 0; + } + else { + /* code goes over to next scanline, do the amount that will fit + on this scanline, put off rest till next line. */ + op_count = x_right-x; + switch (rsd_code) { + case RSD_RUN: + LG_memset (p_dst, *rsd_src, op_count); + break; + case RSD_SKIP: + LG_memset (p_dst, kSkipColor, op_count); + break; + default: /* RSD_DUMP */ + LG_memcpy (p_dst, rsd_src, op_count); + rsd_src += op_count; + break; + } + x += op_count; + p_dst += op_count; + rsd_count -= op_count; + } + } + + /* reset x to be beginning of line and set y to next scanline. */ + x -= sbm->w; + if (over_run) { + assert(over_run > 0); + LG_memset (p_dst, kSkipColor, over_run); + p_dst += over_run; + } + y++; + } + } +rsd_done: + if ((over_run = (dbm->bits+dbm->row*dbm->h)-p_dst)) { + assert(over_run > 0); + LG_memset (p_dst, kSkipColor, over_run); + } + return GR_UNPACK_RSD8_OK; +} + diff --git a/engine/src/Libraries/2D/Source/RSD/rsdunpck.h b/engine/src/Libraries/2D/Source/RSD/rsdunpck.h new file mode 100644 index 0000000..39e8136 --- /dev/null +++ b/engine/src/Libraries/2D/Source/RSD/rsdunpck.h @@ -0,0 +1,62 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/rsdunpck.h $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1993/12/28 16:29:09 $ + * + * Declarations and error codes for gr_unpack_rsd8. + * Uses memory provided externally. + * + * This file is part of the 2d library. + * + * $Log: rsdunpck.h $ + * Revision 1.2 1993/12/28 16:29:09 kevin + * Added assembly unpacker, changed some names. + * + * Revision 1.1 1993/12/06 13:09:47 kevin + * Initial revision + * + */ + +#ifndef __RSDUNPCK_H +#define __RSDUNPCK_H + +#define kSkipColor 0 + +//ÊMLA - removed so we have the prototypes +// #ifndef _RSDCVT_C +extern uchar *grd_unpack_buf; +extern int gr_rsd8_convert(grs_bitmap *sbm, grs_bitmap *dbm); +// #endif + +uchar *gr_rsd8_unpack(uchar* src, uchar *dst); + +//#pragma aux gr_rsd8_unpack parm [esi] [edi] value [edi] modify [eax ecx edx esi edi] + +#define gr_set_unpack_buf(buf) grd_unpack_buf=buf +#define gr_get_unpack_buf() grd_unpack_buf + +/* gr_unpack_rsd8 return codes */ + +#define GR_UNPACK_RSD8_OK 0 +#define GR_UNPACK_RSD8_NOBUF 1 +#define GR_UNPACK_RSD8_NOTRSD 2 +#endif diff --git a/engine/src/Libraries/2D/Source/StateStk.c b/engine/src/Libraries/2D/Source/StateStk.c new file mode 100644 index 0000000..6d105ea --- /dev/null +++ b/engine/src/Libraries/2D/Source/StateStk.c @@ -0,0 +1,74 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// +// $Source: n:/project/lib/src/2d/RCS/stastk.asm $ +// $Revision: 1.1 $ +// $Author: kaboom $ +// $Date: 1993/05/16 00:43:46 $ +// +// Graphics state push routine. +// +// This file is part of the 2d library. +// +// $Log: stastk.asm $ +// Revision 1.3 1994/10/18 13:22:38 kevin +// renamed gr_{push,pop}_state to gr_{push,pop}_video_state. +// +// Revision 1.2 1994/10/13 10:34:38 ept +// Upped allocation in table to 2K from 1K so that can +// allocate up to two graphics states. Also now checks +// if the get_state routine returns < 0 and if so just +// returns it. So gr_push_state now returns an int. +// +// Revision 1.1 1993/05/16 00:43:46 kaboom +// Initial revision +// + +#include "idevice.h" +#include "tabdat.h" + +// globals +long grd_state_stack[512]; +char *grd_state_stack_p = (char *) grd_state_stack; + +int gr_push_video_state (int flags) + { + long bytes; + + bytes = ((int (*)(void *buf,int flags)) grd_device_table[GRT_GET_STATE])((void *) grd_state_stack_p, flags); + if (bytes<0) return(bytes); + + grd_state_stack_p += bytes; + * (long *) grd_state_stack_p = bytes; + grd_state_stack_p += 4L; + + return(bytes); + } + +void gr_pop_video_state (int clear) + { + long bytes; + + grd_state_stack_p -= 4L; + bytes = * (long *) grd_state_stack_p; + grd_state_stack_p -= bytes; + + ((int (*)(void *buf,int clear))grd_device_table[GRT_SET_STATE])(grd_state_stack_p, clear); + } + diff --git a/engine/src/Libraries/2D/Source/bit.c b/engine/src/Libraries/2D/Source/bit.c new file mode 100644 index 0000000..d5efd8f --- /dev/null +++ b/engine/src/Libraries/2D/Source/bit.c @@ -0,0 +1,40 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/bit.c $ + * $Revision: 1.2 $ + * $Author: kaboom $ + * $Date: 1992/12/11 13:01:33 $ + * + * Definition of bit masks for monochrome bitmaps. + * + * This file is part of the 2d library. + * + * $Log: bit.c $ + * Revision 1.2 1992/12/11 13:01:33 kaboom + * Reversed order of bits in the bitmask. + * + * Revision 1.1 1992/11/19 02:27:09 kaboom + * Initial revision + */ + +#include "lg.h" + +/* used by monochrome bitmap routines. */ +uchar bitmask[9] = { 128, 64, 32, 16, 8, 4, 2, 1, 0 }; diff --git a/engine/src/Libraries/2D/Source/bit.h b/engine/src/Libraries/2D/Source/bit.h new file mode 100644 index 0000000..4ccab88 --- /dev/null +++ b/engine/src/Libraries/2D/Source/bit.h @@ -0,0 +1,39 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/bit.h $ + * $Revision: 1.1 $ + * $Author: kaboom $ + * $Date: 1992/11/19 02:27:39 $ + * + * This file is part of the 2d library. + * + * $Log: bit.h $ + * Revision 1.1 1992/11/19 02:27:39 kaboom + * Initial revision + * + */ + +#ifndef __BIT_H +#define __BIT_H + +#include "lg.h" +extern uchar bitmask[]; + +#endif /* __BIT_H */ diff --git a/engine/src/Libraries/2D/Source/bitmap.c b/engine/src/Libraries/2D/Source/bitmap.c new file mode 100644 index 0000000..826ffa4 --- /dev/null +++ b/engine/src/Libraries/2D/Source/bitmap.c @@ -0,0 +1,175 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/bitmap.c $ + * $Revision: 1.11 $ + * $Author: baf $ + * $Date: 1994/02/14 22:14:42 $ + * + * Constants for bitmap flags & type fields. + * + * Bitmap handling routines. + * + * $Log: bitmap.c $ + * Revision 1.11 1994/02/14 22:14:42 baf + * Pruned vestigial 16/bit translucency + * + * Revision 1.10 1994/01/20 16:49:31 kaboom + * Changed temporary bitmap to static so can be called in interrupt. + * + * Revision 1.9 1993/11/19 17:22:02 unknown + * Added 8/bit translucent bitmaps + * + * Revision 1.8 1993/10/21 00:28:40 baf + * Changed TRANSLUCENT to TLUC, for consistency's sake. + * + * Revision 1.7 1993/10/19 09:49:53 kaboom + * Replaced #include with new headers split from grd.h. + * + * Revision 1.6 1993/10/15 12:19:25 baf + * Added support for translucent bitmaps + * + * Revision 1.5 1993/10/08 01:14:52 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.4 1993/09/09 00:17:41 kaboom + * Added gr_alloc_bitmap() routine. + * + * Revision 1.3 1993/07/08 23:02:24 kaboom + * Added code in gr_init_bm() to set wlog and hlog fields. + * + * Revision 1.2 1993/04/29 16:10:30 kaboom + * Fixed up 24-bit case for init_bm. Updated gr_sub_bm to gr_sub_bitmap. + * + * Revision 1.1 1993/02/04 16:58:51 kaboom + * Initial revision + * + ******************************************************************** + * Log entries from old gr.c + * Revision 1.6 1992/12/11 20:31:13 kaboom + * Changed gr_init_bm from macro to function; now it calculates row from + * width and assumes align is 0. Added gr_init_sub_bm to initialize a + * subsection of an existing bitmap. Updated references from BMT_DRIVER to + * BMT_DEVICE. + */ + +#include + +#include "lg.h" +#include "GR/grs.h" +#include "bitmap.h" +#include "grbm.h" + +extern int32_t gScreenRowbytes; + +/* initialize a new bitmap structure. set bits, type, flags, w, and h from + arguments. set align to 0 and calculate row from width depending on what + type of bitmap. */ +void gr_init_bm(grs_bitmap *bm, uchar *p, uchar type, ushort flags, short w, short h) { + int row = 0; + int v; + + /* calculate row from type and w. */ + switch (type) { + case BMT_DEVICE: + row = gScreenRowbytes; + break; // row = gr_calc_row (w); break; + case BMT_MONO: + row = (w + 7) / 8; + break; + case BMT_FLAT8: + case BMT_TLUC8: + row = w; + break; + case BMT_FLAT24: + row = 3 * w; + break; + case BMT_RSD8: + row = 0; + break; + default: + break; + } + + bm->bits = p; + bm->type = type; + bm->flags = flags; + bm->align = 0; + bm->w = w; + bm->h = h; + bm->row = row; + bm->wlog = bm->hlog = 0; + + for (v = w >> 1; v != 0; v >>= 1) + bm->wlog++; + for (v = h >> 1; v != 0; v >>= 1) + bm->hlog++; +} + +/* set up a new bitmap structure to be a subsection of an existing bitmap. + sbm is source bm, dbm destination. (0,0) of dbm maps to (x,y) of sbm, + and dbm is w x h in size. */ +void gr_init_sub_bm(grs_bitmap *sbm, grs_bitmap *dbm, short x, short y, short w, short h) { + *dbm = *sbm; // memcpy (dbm, sbm, sizeof (*sbm)); + dbm->w = w; + dbm->h = h; + + switch (sbm->type) { + case BMT_DEVICE: + /* chain to device sub bm. */ + gr_sub_bitmap(dbm, x, y, w, h); + break; + case BMT_MONO: + dbm->bits += y * dbm->row + x / 8; + if ((dbm->align += x % 8) > 7) { + dbm->align -= 8; + dbm->bits++; + } + break; + case BMT_FLAT8: + case BMT_TLUC8: + dbm->bits += y * dbm->row + x; + break; + case BMT_FLAT24: + dbm->bits += y * dbm->row + 3 * x; + break; + case BMT_RSD8: + break; + default: + break; + } +} + +/* allocate memory for a bitmap structure and the data for a bitmap of + the specified type and flags of size w x h. returns a pointer to the + new bitmap structure. the returned pointer can be freed in order to + free both the structure and data memory. */ +grs_bitmap *gr_alloc_bitmap(uchar type, ushort flags, short w, short h) { + grs_bitmap tmp_bm; /* temporary space for bitmap init */ + uchar *p; /* pointer to allocated buffer */ + + gr_init_bitmap(&tmp_bm, NULL, type, flags, w, h); + // p=(uchar *)gr_malloc(sizeof(tmp_bm)+(tmp_bm.row*tmp_bm.h)); + p = (uchar *)malloc(sizeof(tmp_bm) + (tmp_bm.row * tmp_bm.h)); + if (p) { + tmp_bm.bits = p + sizeof(tmp_bm); + *(grs_bitmap *)p = tmp_bm; // LG_memcpy(p, &tmp_bm, sizeof(tmp_bm)); + } + return (grs_bitmap *)p; +} diff --git a/engine/src/Libraries/2D/Source/bitmap.h b/engine/src/Libraries/2D/Source/bitmap.h new file mode 100644 index 0000000..08e8e3a --- /dev/null +++ b/engine/src/Libraries/2D/Source/bitmap.h @@ -0,0 +1,117 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/bitmap.h $ + * $Revision: 1.12 $ + * $Author: kevin $ + * $Date: 1994/08/16 15:33:32 $ + * + * Constants for bitmap flags & type fields; prototypes for bitmap + * functions. + * + * This file is part of the 2d library. + * + * $Log: bitmap.h $ + * Revision 1.12 1994/08/16 15:33:32 kevin + * Added REAL_BM_TYPES constant. + * + * Revision 1.11 1994/05/19 15:08:20 kevin + * Added flag BMF_TLUC8 for rsd8 bitmaps that want to be tluc8 bitmaps when uncompressed. + * + * Revision 1.10 1994/02/14 22:15:25 baf + * Pruned vestigial 16/bit translucency + * + * Revision 1.9 1993/11/19 17:22:24 unknown + * Added 8/bit translucent bitmaps. + * + * Revision 1.8 1993/11/15 03:23:55 baf + * Added BMT_GEN, for new generic canvas table. + * (Methinks the name 'bitmap_types' is getting + * somewhat strained? + * + * Revision 1.7 1993/11/11 00:09:00 baf + * Added BMT_TYPES to enum, to indicate number of + * types of bitmap + * + * Revision 1.6 1993/10/21 00:29:23 baf + * Changed TRANSLUCENT to TLUC, for consistency's sake + * + * Revision 1.5 1993/10/19 09:49:54 kaboom + * Replaced #include with new headers split from grd.h. + * + * Revision 1.4 1993/10/15 12:19:37 baf + * Added support for translucent bitmaps + * + * Revision 1.3 1993/09/09 00:17:56 kaboom + * Added prototype for gr_alloc_bitmap(). + * + * Revision 1.2 1993/05/03 15:09:26 kaboom + * Added BMT_SPAN to bitmap type list. + * + * Revision 1.1 1993/02/04 16:59:15 kaboom + * Initial revision + * + ******************************************************************** + * Log entries from old 2d.h + * Revision 1.15 1993/01/07 20:03:47 kaboom + * Added new bitmap type, BMT_BANK8 for 8-bit bank-switched memory. + * Changed prototype for gr_set_driver. + * + * Revision 1.10 1992/12/11 20:24:22 kaboom + * Changed name of BMT_DRIVER to BMT_DEVICE for distinction between + * device drivers and bitmap drivers. + */ + +#ifndef __BITMAP_H +#define __BITMAP_H +#include "grs.h" + +/* bitmap types. */ +enum { + BMT_DEVICE, + BMT_MONO, + BMT_FLAT8, + BMT_FLAT24, + BMT_RSD8, + BMT_TLUC8, + BMT_SPAN, + BMT_GEN, + BMT_TYPES +}; + +/* BMT_GEN and BMT_SPAN are not true bitmap types. */ +#define REAL_BMT_TYPES BMT_SPAN + +/* bitmap flags. */ +#define BMF_TRANS 1 +#define BMF_TLUC8 2 + +/* function prototypes for bitmap routines. */ +extern void gr_init_bitmap + (grs_bitmap *bm, uchar *p, uchar type, ushort flags, short w, short h); +extern void gr_init_sub_bitmap + (grs_bitmap *sbm, grs_bitmap *dbm, short x, short y, short w, short h); +extern grs_bitmap *gr_alloc_bitmap + (uchar type, ushort flags, short w, short h); + +/* compatibility defines. */ +#define gr_init_bm gr_init_bitmap +#define gr_init_sub_bm gr_init_sub_bitmap +#define gr_alloc_bm gr_alloc_bitmap +#endif /* !__BITMAP_H */ diff --git a/engine/src/Libraries/2D/Source/blend.c b/engine/src/Libraries/2D/Source/blend.c new file mode 100644 index 0000000..8c67573 --- /dev/null +++ b/engine/src/Libraries/2D/Source/blend.c @@ -0,0 +1,105 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/blend.c $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/09/08 21:57:25 $ + * + * Support for creation and maintenance of blend tables + * + * This file is part of the 2d library. + */ + +#include "grs.h" +#include "blncon.h" +#include "rgb.h" +#include "scrdat.h" + +// prototypes +void gri_build_blend(uchar *base_addr, int blend_fac); +int gr_free_blend(void); +uchar gr_init_blend(int log_blend_levels); + + +// points to blend_tabs-1 tables, each 64k +uchar *grd_blend=NULL; +uchar *grd_half_blend=NULL; +int grd_log_blend_levels=0; + +// blend fac is 0-256, where 0 is all 0, 256 is all 1 +void gri_build_blend(uchar *base_addr, int blend_fac) +{ + uchar *c=grd_ipal, *cur_addr=base_addr, cols[2][3]; + int offs, i, j, k; /* offset from ipal for data, loop controls */ + int blend_bar=GR_BLEND_TABLE_RES-blend_fac; /* remaining blend frac */ + + for (i=0; i<256; i++) + { + gr_split_rgb(grd_bpal[i],&cols[0][0],&cols[0][1],&cols[0][2]); + for (j=0; j<256; j++) + { + if ((i==0)||(i==j)||(cols[0][0]+cols[0][1]+cols[0][2]==0)) + *cur_addr++=i; // transparency and self and black are themselves, for zaniness w/shifts + else + { + gr_split_rgb(grd_bpal[j],&cols[1][0],&cols[1][1],&cols[1][2]); + if ((j==0)||(cols[1][0]+cols[1][1]+cols[1][2]==0)) + *cur_addr++=j; + else + { + for (offs=0, k=2; k>=0; k--) // go do the blends + offs=(offs<<5)+((((cols[0][k]*blend_bar)+(cols[1][k]*blend_fac))>>GR_BLEND_TABLE_RES_LOG)>>3); + *cur_addr++=*(c+offs); + } + } + } + } +} + +/* frees the blending table. returns 0 if ok, nonzero if error. */ +int gr_free_blend(void) +{ + if (grd_blend==NULL) + return 1; + free( grd_blend); // was gr_free + grd_blend=NULL; + grd_log_blend_levels=0; + return 0; +} + +// at the moment, log_blend_levels = 0 deallocates the blend, ie. runs free_blend +uchar gr_init_blend(int log_blend_levels) +{ + if (log_blend_levels>0) + { + int fac=GR_BLEND_TABLE_RES>>log_blend_levels; /* base blend factor*/ + int tab_cnt=(1<>1)*GR_BLEND_TABLE_SIZE; + return TRUE; + } + else return gr_free_blend(); +} diff --git a/engine/src/Libraries/2D/Source/blend.h b/engine/src/Libraries/2D/Source/blend.h new file mode 100644 index 0000000..1d94ef4 --- /dev/null +++ b/engine/src/Libraries/2D/Source/blend.h @@ -0,0 +1,37 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/blend.h $ + * $Revision: 1.1 $ + * $Author: kevin $ + * $Date: 1994/08/16 15:33:57 $ + * + * Blending #defines. + * + * This file is part of the 2d library. + * + */ + +#ifndef __BLEND_H +#define __BLEND_H + +/* until done for real: */ +#define gr_blend(a,b,per) a + +#endif diff --git a/engine/src/Libraries/2D/Source/blncon.h b/engine/src/Libraries/2D/Source/blncon.h new file mode 100644 index 0000000..1000365 --- /dev/null +++ b/engine/src/Libraries/2D/Source/blncon.h @@ -0,0 +1,40 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/blncon.h $ + * $Revision: 1.1 $ + * $Author: kevin $ + * $Date: 1994/03/14 17:48:34 $ + * + * Symbolic constants for blend system. + * + * This file is part of the 2d libarary. + * + * $Log: blncon.h $ + * Revision 1.1 1994/03/14 17:48:34 kevin + * Initial revision + * + */ + +#ifndef __BLNCON_H +#define __BLNCON_H +#define GR_BLEND_TABLE_SIZE 0x10000 +#define GR_BLEND_TABLE_RES 256 +#define GR_BLEND_TABLE_RES_LOG 8 +#endif /* !__BLNCON_H */ diff --git a/engine/src/Libraries/2D/Source/blndat.h b/engine/src/Libraries/2D/Source/blndat.h new file mode 100644 index 0000000..3508aea --- /dev/null +++ b/engine/src/Libraries/2D/Source/blndat.h @@ -0,0 +1,43 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/blndat.h $ + * $Revision: 1.1 $ + * $Author: kevin $ + * $Date: 1994/03/14 17:48:56 $ + * + * Declarations for blend system globals. + * + * This file is part of the 2d library. + * + * $Log: blndat.h $ + * Revision 1.1 1994/03/14 17:48:56 kevin + * Initial revision + * + */ + +#ifndef __BLNDAT_H +#define __BLNDAT_H + +#include + +extern uchar *grd_blend; +extern uchar *grd_half_blend; +extern int grd_log_blend_levels; +#endif /* !__BLNDAT_H */ diff --git a/engine/src/Libraries/2D/Source/blnfcn.h b/engine/src/Libraries/2D/Source/blnfcn.h new file mode 100644 index 0000000..3e3306d --- /dev/null +++ b/engine/src/Libraries/2D/Source/blnfcn.h @@ -0,0 +1,42 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/blnfcn.h $ + * $Revision: 1.1 $ + * $Author: kevin $ + * $Date: 1994/03/14 17:49:25 $ + * + * prototypes and for the blend system + * + * This file is part of the 2d libarary. + * + * $Log: blnfcn.h $ + * Revision 1.1 1994/03/14 17:49:25 kevin + * Initial revision + * + */ + +#ifndef __BLNFCN_H +#define __BLNFCN_H +/* prototypes for blend table maintenance, TRUE means success, FALSE not */ +uchar gr_free_blend(void); +/* tab_cnt is how many blend steps, note cnt<=0 is equivalent to calling + free blend */ +uchar gr_init_blend(int log_blend_levels); +#endif /* !__BLNFCN */ diff --git a/engine/src/Libraries/2D/Source/buffer.h b/engine/src/Libraries/2D/Source/buffer.h new file mode 100644 index 0000000..70965df --- /dev/null +++ b/engine/src/Libraries/2D/Source/buffer.h @@ -0,0 +1,39 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/buffer.h $ + * $Revision: 1.2 $ + * $Author: kaboom $ + * $Date: 1994/08/01 22:02:42 $ + * + * Prototypes for 2d temporary storage management. + * + * This file is part of the 2d library. + */ + +#define GR_TEMP_USE_MEMSTACK +#ifdef GR_TEMP_USE_MEMSTACK +#include "memall.h" +#include "tmpalloc.h" +#define gr_alloc_temp temp_malloc +#define gr_free_temp temp_free +#else /* GR_TEMP_USE_MEMSTACK */ +extern void *gr_alloc_temp (int n); +extern void gr_free_temp (void *p); +#endif /* GR_TEMP_USE_MEMSTACK */ diff --git a/engine/src/Libraries/2D/Source/canvas.c b/engine/src/Libraries/2D/Source/canvas.c new file mode 100644 index 0000000..790d647 --- /dev/null +++ b/engine/src/Libraries/2D/Source/canvas.c @@ -0,0 +1,174 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/canvas.c $ + * $Revision: 1.21 $ + * $Author: kevin $ + * $Date: 1994/11/28 21:17:01 $ + * + * Canvas handling routines. + * + */ + +#include "grs.h" +#include "bitmap.h" +#include "chain.h" +#include "cnvdat.h" +#include "cnvtab.h" +#include "context.h" +#include "ctxmac.h" +#include "fcntab.h" +#include "lintab.h" +#include "tabdat.h" +#include "valloc.h" + +#include // printf() + +#define CANVAS_STACKSIZE 16 +grs_canvas *grd_canvas_stack[CANVAS_STACKSIZE]; +int grd_canvas_stackp = 0; + +/* set current canvas to c. select driver_func from type of bitmap + attached to canvas. */ +void gr_set_canvas (grs_canvas *c) +{ + int i; + + if (c == NULL) + return; + + grd_canvas = c; + if (gr_generic) + i = BMT_GEN; + else + i = c->bm.type; + + grd_pixel_index = c->bm.type; + grd_canvas_index = i; + grd_pixel_table = grd_canvas_table_list[grd_pixel_index]; + grd_canvas_table = grd_canvas_table_list[i]; + + grd_uline_fill_table = grd_uline_fill_table_list[c->bm.type]; + grd_uline_fill_vector = (*grd_uline_fill_table)[c->gc.fill_type]; + grd_function_fill_table = grd_function_table_list[i]; + grd_function_table = (*grd_function_fill_table)[c->gc.fill_type]; +} + +/* push current canvas onto canvas stack and make passed in canvas active. + returns 0 if stack is ok, -1 if there is an overflow. */ +int gr_push_canvas (grs_canvas *c) +{ + if (grd_canvas_stackp >= CANVAS_STACKSIZE) + return -1; + grd_canvas_stack[grd_canvas_stackp++] = grd_canvas; + gr_set_canvas (c); + return 0; +} + +/* pop last canvas off of stack and make it active. return it, or NULL if + there is an underflow. */ +grs_canvas *gr_pop_canvas (void) +{ + grs_canvas *c; + if (grd_canvas_stackp <= 0) + return NULL; + c = grd_canvas_stack[--grd_canvas_stackp]; + gr_set_canvas (c); + return c; +} + +#pragma scheduling off +#pragma global_optimizer off + +void gr_init_canvas (grs_canvas *c, uchar *p, int type, short w, short h) +{ +#ifdef GR_DOUBLE_CANVAS + if (type==BMT_FLAT8_DOUBLE) { + gr_init_bm (&c->bm, p, BMT_FLAT8, 0, w, h); + gr_init_gc (c); + gr_cset_fix_cliprect (c, 0, 0, fix_make ((w>>1),0), fix_make (h,0)); + c->bm.type=BMT_FLAT8_DOUBLE; + } else +#endif + { + gr_init_bm (&c->bm, p, type, 0, w, h); + gr_init_gc (c); + gr_cset_fix_cliprect (c, 0, 0, fix_make (w,0), fix_make (h,0)); + } + c->ytab = NULL; +} + +#pragma scheduling reset +#pragma global_optimizer reset + +void gr_init_sub_canvas (grs_canvas *sc, grs_canvas *dc, short x, short y, + short w, short h) +{ + gr_init_sub_bm (&sc->bm, &dc->bm, x, y, w, h); + gr_init_gc (dc); + gr_cset_fix_cliprect (dc, 0, 0, fix_make (w,0), fix_make (h,0)); + dc->ytab = NULL; +} + +void gr_make_canvas (grs_bitmap *bm, grs_canvas *c) +{ + gr_init_canvas (c, bm->bits, bm->type, bm->w, bm->h); +} + +grs_canvas *gr_alloc_canvas (int type, short w, short h) +{ + grs_canvas *c; + uchar *p; + + if ((c=(grs_canvas *)malloc (sizeof (*c))) == NULL) // was Malloc + return NULL; + if (type == BMT_DEVICE) + p = our_valloc (w,h); + else + p = (uchar *)malloc (w*h);// was Malloc + gr_init_canvas (c, p, type, w, h); + + return c; +} + +void gr_free_canvas (grs_canvas *c) +{ + printf("Free canvas"); + if (c->bm.type == BMT_DEVICE) + vfree (c->bm.bits); + else + free( c->bm.bits); // was gr_free + free( c); // was gr_free +} + +grs_canvas *gr_alloc_sub_canvas (grs_canvas *c, short x, short y, + short w, short h) +{ + grs_canvas *c_new; + + c_new = (grs_canvas *)malloc (sizeof (*c_new));// was Malloc + if (c_new != NULL) + gr_init_sub_canvas (c, c_new, x, y, w, h); + return c_new; +} + +void gr_free_sub_canvas (grs_canvas *c) +{ + free( c); // was gr_free +} diff --git a/engine/src/Libraries/2D/Source/canvas.h b/engine/src/Libraries/2D/Source/canvas.h new file mode 100644 index 0000000..1ae04e8 --- /dev/null +++ b/engine/src/Libraries/2D/Source/canvas.h @@ -0,0 +1,54 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/canvas.h $ + * $Revision: 1.2 $ + * $Author: kaboom $ + * $Date: 1993/06/02 16:31:39 $ + * + * Prototypes for canvas routines. + * + * This file is part of the 2d library. + * + * $Log: canvas.h $ + * Revision 1.2 1993/06/02 16:31:39 kaboom + * Added prototype for gr_make_canvas(). + * + * Revision 1.1 1993/02/04 17:00:25 kaboom + * Initial revision + * + */ + +#ifndef __CANVAS_H +#define __CANVAS_H + +extern void gr_set_canvas (grs_canvas *c); +extern int gr_push_canvas (grs_canvas *c); +extern grs_canvas *gr_pop_canvas (void); +extern void gr_make_canvas (grs_bitmap *bm, grs_canvas *c); +extern void gr_init_canvas (grs_canvas *c, uchar *p, int type, short w, short h); +extern void gr_init_sub_canvas (grs_canvas *sc, grs_canvas *dc, + short x, short y, short w, short h); +extern grs_canvas *gr_alloc_canvas (int type, short w, short h); +extern void gr_free_canvas (grs_canvas *c); +extern grs_canvas *gr_alloc_sub_canvas (grs_canvas *c, short x, short y, + short w, short h); +extern void gr_free_sub_canvas (grs_canvas *c); + +#endif /* !__CANVAS_H */ diff --git a/engine/src/Libraries/2D/Source/chain.h b/engine/src/Libraries/2D/Source/chain.h new file mode 100644 index 0000000..bd2631c --- /dev/null +++ b/engine/src/Libraries/2D/Source/chain.h @@ -0,0 +1,104 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/chain.h $ + * $Revision: 1.8 $ + * $Author: baf $ + * $Date: 1993/12/08 19:14:57 $ + * + * Prototypes and macros for function chaining. + * + * This file is part of the 2D library. + * + * $Log: chain.h $ + * Revision 1.8 1993/12/08 19:14:57 baf + * Fixed bug in gr_toggle_generic + * + * Revision 1.7 1993/12/07 11:46:34 baf + * Renamed gr_chain_add + * + * Revision 1.6 1993/12/02 13:44:45 baf + * Added the ability to unchain and rechain. + * + * Revision 1.5 1993/11/30 20:47:24 baf + * Chainged generic mode stuff so it can + * be done at any time. + * + * Revision 1.4 1993/11/30 19:31:22 baf + * Added more macros for manipulating + * chaining and generic mode + * + * Revision 1.3 1993/11/16 23:06:58 baf + * Added the ability to chain void functions + * after the primitive. + * + * Revision 1.2 1993/11/15 03:28:49 baf + * Added gr_chain_add_void, as well as + * support for forcing generic mode and + * turning chaining off. + * + * Revision 1.1 1993/11/12 09:30:18 baf + * Initial revision + * + */ + +#ifndef __CHAIN +#define __CHAIN + +#include "grs.h" +#include "icanvas.h" + + +typedef struct iaaiiaia{ + void (*f)(); + struct iaaiiaia *next; + uchar flags; +} grs_func_chain; + +extern short grd_pixel_index; +extern short grd_canvas_index; + +extern uchar chn_flags; +#define CHN_ON 1 +#define CHN_GEN 2 + +extern grs_func_chain *gr_chain_add_over(int n, void (*f)()); +extern grs_func_chain *gr_chain_add_before(int n, void (*f)(void)); +extern grs_func_chain *gr_chain_add_after(int n, void (*f)(void)); +extern void (*chain_rest())(); + +extern void gr_unchain(int n); +extern void gr_rechain(int n); +extern void gr_unchain_all(); +extern void gr_rechain_all(); + +#define gr_do_chain (chain_rest()) +#define gr_chaining_on() (chn_flags |= CHN_ON) +#define gr_chaining_off() (chn_flags &= ~CHN_ON) +#define gr_chaining_toggle() (chn_flags ^= CHN_ON) + +#define gr_generic (chn_flags & CHN_GEN) +extern void gr_force_generic(); +extern void gr_unforce_generic(); +#define gr_toggle_generic() (gr_generic? gr_unforce_generic() : gr_force_generic()) + +#define gr_start_frame ((void (*)())grd_canvas_table[START_FRAME]) +#define gr_end_frame ((void (*)())grd_canvas_table[END_FRAME]) + +#endif diff --git a/engine/src/Libraries/2D/Source/close.c b/engine/src/Libraries/2D/Source/close.c new file mode 100644 index 0000000..2029b35 --- /dev/null +++ b/engine/src/Libraries/2D/Source/close.c @@ -0,0 +1,64 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/close.c $ + * $Revision: 1.6 $ + * $Author: kevin $ + * $Date: 1994/10/18 13:22:36 $ + * + * Routine to shut down 2d system. + * + * This file is part of the 2d library. + * + * $Log: close.c $ + * Revision 1.6 1994/10/18 13:22:36 kevin + * renamed gr_{push,pop}_state to gr_{push,pop}_video_state. + * + * Revision 1.5 1993/10/19 10:12:16 kaboom + * Removed null function pointer check---macro now does it + * + * Revision 1.4 1993/10/08 01:15:05 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.3 1993/07/12 23:29:15 kaboom + * Now gr_close() can be called before gr_init() without ill effect. + * + * Revision 1.2 1993/05/16 00:31:42 kaboom + * Now calls gr_pop_state() to restore old video state. + * + * Revision 1.1 1993/04/29 16:30:38 kaboom + * Initial revision + */ + +#include "grd.h" +#include "grdev.h" +#include "state.h" +#include "status_2D.h" + +/* shut down 2d system. call device-dependent shutdown routine and + restore video state. */ +int gr_close(void) +{ + if (grd_active == 0) + return 0; + gr_pop_video_state (TRUE); + gr_close_device (&grd_info); + grd_active = 0; + return 0; +} diff --git a/engine/src/Libraries/2D/Source/close.h b/engine/src/Libraries/2D/Source/close.h new file mode 100644 index 0000000..25eb1db --- /dev/null +++ b/engine/src/Libraries/2D/Source/close.h @@ -0,0 +1,38 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/close.h $ + * $Revision: 1.1 $ + * $Author: kaboom $ + * $Date: 1993/05/03 13:44:21 $ + * + * Declarations for gr shutdown. + * + * This file is part of the 2d library. + * + * $Log: close.h $ + * Revision 1.1 1993/05/03 13:44:21 kaboom + * Initial revision + * + */ + +#ifndef __CLOSE_H +#define __CLOSE_H +extern int gr_close (void); +#endif /* !__CLOSE_H */ diff --git a/engine/src/Libraries/2D/Source/cnvdat.h b/engine/src/Libraries/2D/Source/cnvdat.h new file mode 100644 index 0000000..8e7453d --- /dev/null +++ b/engine/src/Libraries/2D/Source/cnvdat.h @@ -0,0 +1,49 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/cnvdat.h $ + * $Revision: 1.1 $ + * $Author: kaboom $ + * $Date: 1993/10/19 10:12:33 $ + * + * Declarations for current canvas and related globals. + * + * This file is part of the 2d library. + * + * $Log: cnvdat.h $ + * Revision 1.1 1993/10/19 10:12:33 kaboom + * Initial revision + * + */ + +#ifndef __CNVDAT_H +#define __CNVDAT_H +#include "grs.h" + +extern grs_canvas *grd_screen_canvas; +extern grs_canvas *grd_visible_canvas; +extern grs_canvas *grd_canvas; + +#define grd_bm (grd_canvas->bm) +#define grd_gc (grd_canvas->gc) +#define grd_ytab (grd_canvas->ytab) +#define grd_int_clip (grd_gc.clip.i) +#define grd_fix_clip (grd_gc.clip.f) +#define grd_clip (grd_int_clip) +#endif /* !__CNVDAT_H */ diff --git a/engine/src/Libraries/2D/Source/cnvdrv.h b/engine/src/Libraries/2D/Source/cnvdrv.h new file mode 100644 index 0000000..828bda7 --- /dev/null +++ b/engine/src/Libraries/2D/Source/cnvdrv.h @@ -0,0 +1,50 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/cnvdrv.h $ + * $Revision: 1.3 $ + * $Author: kevin $ + * $Date: 1994/10/04 18:41:34 $ + * + * Declarations for canvas tables. + * + * This file is part of the 2d library. + * + * $Log: cnvdrv.h $ + * Revision 1.3 1994/10/04 18:41:34 kevin + * *** empty log message *** + * + * Revision 1.2 1993/11/15 03:31:14 baf + * Added generic canvas table. + * + * Revision 1.1 1993/10/20 15:45:33 kaboom + * Initial revision + * + */ + +#ifndef __CNVDRV_H +#define __CNVDRV_H +extern void (*flat8_canvas_table[])(); +extern void (*flat8d_canvas_table[])(); +extern void (*modex_canvas_table[])(); +extern void (*bank8_canvas_table[])(); +extern void (*bank24_canvas_table[])(); +extern void (*span_canvas_table[])(); +extern void (*gen_canvas_table[])(); +#endif diff --git a/engine/src/Libraries/2D/Source/cnvtab.c b/engine/src/Libraries/2D/Source/cnvtab.c new file mode 100644 index 0000000..b3c58a6 --- /dev/null +++ b/engine/src/Libraries/2D/Source/cnvtab.c @@ -0,0 +1,43 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/cnvtab.c $ + * $Revision: 1.7 $ + * $Author: kevin $ + * $Date: 1994/12/05 21:06:36 $ + * + * List of canvas driver function tables. + * + * This file is part of the 2d library. + * + */ + +#include "cnvdrv.h" +#include + +void (**grd_canvas_table_list[])() = { + NULL, /* device driver-initialized by gr_set_mode */ + NULL, /* monochrome-not supported */ + flat8_canvas_table, /* flat 8 canvas */ + NULL, /* flat 24-not supported */ + NULL, /* doubling, RSD8 canvas-not supported */ + NULL, /* translucent 8-not supported */ + NULL, /* span-obsolete */ + NULL /* generic-initialized by gr_force_generic */ +}; diff --git a/engine/src/Libraries/2D/Source/cnvtab.h b/engine/src/Libraries/2D/Source/cnvtab.h new file mode 100644 index 0000000..3563d69 --- /dev/null +++ b/engine/src/Libraries/2D/Source/cnvtab.h @@ -0,0 +1,38 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/cnvtab.h $ + * $Revision: 1.1 $ + * $Author: kaboom $ + * $Date: 1993/05/03 13:44:31 $ + * + * Declarations for canvas table list. + * + * This file is part of the 2d library. + * + * $Log: cnvtab.h $ + * Revision 1.1 1993/05/03 13:44:31 kaboom + * Initial revision + * + */ + +#ifndef __CNVTAB_H +#define __CNVTAB_H +extern void (**grd_canvas_table_list[])(); +#endif /* !__CNVTAB_H */ diff --git a/engine/src/Libraries/2D/Source/context.c b/engine/src/Libraries/2D/Source/context.c new file mode 100644 index 0000000..9f9c6d7 --- /dev/null +++ b/engine/src/Libraries/2D/Source/context.c @@ -0,0 +1,50 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/context.c $ + * $Revision: 1.2 $ + * $Author: kaboom $ + * $Date: 1993/10/08 01:15:06 $ + * + * Macros for access to table-driver 2d functions. + * + * This file is part of the 2d library. + * + * $Log: context.c $ + * Revision 1.2 1993/10/08 01:15:06 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.1 1993/02/04 17:09:34 kaboom + * Initial revision + * + */ + +#include "grs.h" + +/* Default graphic context; gets copied by init_gc. */ +grs_context grd_defgc = { + 15, /* current drawing color */ + 0, /* background color */ + 0, /* font id */ + 0, /* attributes for text */ + 0, /* how to fill primitives */ + 0, /* parameter for fill */ + /* clipping region. */ + { NULL, 0, 0, 0, 0 } +}; diff --git a/engine/src/Libraries/2D/Source/context.h b/engine/src/Libraries/2D/Source/context.h new file mode 100644 index 0000000..029b8d0 --- /dev/null +++ b/engine/src/Libraries/2D/Source/context.h @@ -0,0 +1,40 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/context.h $ + * $Revision: 1.1 $ + * $Author: kaboom $ + * $Date: 1993/02/04 17:09:53 $ + * + * Context + * + * This file is part of the 2d library. + * + * $Log: context.h $ + * Revision 1.1 1993/02/04 17:09:53 kaboom + * Initial revision + * + */ + +#ifndef __CONTEXT_H +#define __CONTEXT_H + +extern grs_context grd_defgc; + +#endif /* !__CONTEXT_H */ diff --git a/engine/src/Libraries/2D/Source/ctxmac.h b/engine/src/Libraries/2D/Source/ctxmac.h new file mode 100644 index 0000000..c35b7e2 --- /dev/null +++ b/engine/src/Libraries/2D/Source/ctxmac.h @@ -0,0 +1,154 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/ctxmac.h $ + * $Revision: 1.7 $ + * $Author: kevin $ + * $Date: 1994/11/16 12:24:37 $ + * + * Macros for handling elements of the grs_context structure + * + * This file is part of the 2d library. + */ + +#ifndef __CTXMAC_H +#define __CTXMAC_H +#include "cnvdat.h" +#include "lintab.h" +#include "fcntab.h" +#include "tabdat.h" + +#define gr_init_gc(c) { (c)->gc=grd_defgc; \ + (c)->gc.clip.f.right=((c)->bm.w)<<16; \ + (c)->gc.clip.f.bot=((c)->bm.h)<<16; } + +/* macros for setting the clipping region of the current canvas. */ +#define gr_set_cliprect(l, t, r, b) \ + grd_clip.sten=NULL, \ + grd_clip.left=(l), grd_clip.top=(t), \ + grd_clip.right=(r), grd_clip.bot=(b) + +#define gr_safe_set_cliprect(l, t, r, b) \ + do { \ + grd_clip.sten=NULL; \ + grd_clip.left=(((l)<0)?0:(l)); \ + grd_clip.right=(((r)>grd_bm.w)?grd_bm.w:(r)); \ + grd_clip.top=(((t)<0)?0:(t)); \ + grd_clip.bot=(((b)>grd_bm.h)?grd_bm.h:(b)); \ + } while (0) + +#define gr_set_fix_cliprect(l, t, r, b) \ + grd_fix_clip.sten=NULL, \ + grd_fix_clip.left=(l), grd_fix_clip.top=(t), \ + grd_fix_clip.right=(r), grd_fix_clip.bot=(b) + +#define gr_set_clipmask(t,b,mask) \ + grd_clip.top=(t), grd_clip.bot=(b), \ + grd_clip.sten = (mask), \ + gr_set_canvas (grd_canvas) + +/* macros for getting parts of the graphic context of the current canvas. */ +#define gr_set_fcolor(color) (grd_canvas->gc.fcolor=color) +#define gr_get_fcolor() (grd_canvas->gc.fcolor) +#define gr_set_bcolor(color) (grd_canvas->gc.bcolor=color) +#define gr_get_bcolor() (grd_canvas->gc.bcolor) +#define gr_set_font(fnt) (grd_canvas->gc.font=fnt) +#define gr_get_font() (grd_canvas->gc.font) +#define gr_set_text_attr(attr) (grd_canvas->gc.text_attr=attr) +#define gr_get_text_attr() (grd_canvas->gc.text_attr) + +/* this horrifying mess is necessary to ensure that grd_gc.fill_type is set + _before_ the function tables. Otherwise if an interrupt that uses the 2d + occurs before grd_gc.fill_type is set, the function table ptrs may get out + of sync when the interrupt tries to restore the canvas. */ +// implementation of gri_set_fill_globals is in PixFill.c +extern void gri_set_fill_globals(long *fill_type_ptr, long fill_type, + void (***function_table_ptr)(), void (**function_table)(), + grt_uline_fill **line_vector_ptr, grt_uline_fill *line_vector); +/* +#pragma aux gri_set_fill_globals = \ + "mov [edx],eax" \ + "mov [esi],ebx" \ + "mov [edi],ecx" \ + parm [edx] [eax] [esi] [ebx] [edi] [ecx]; +*/ +#define gr_set_fill_type(__ft) \ +do { \ + long fill_type=__ft; \ + gri_set_fill_globals(&(grd_canvas->gc.fill_type),fill_type, \ + &grd_function_table,(*grd_function_fill_table)[fill_type], \ + &grd_uline_fill_vector,(*grd_uline_fill_table)[fill_type]); \ +} while (0) +#define gr_get_fill_type() (grd_canvas->gc.fill_type) + +#define gr_set_fill_parm(parm) \ + (grd_canvas->gc.fill_parm=(intptr_t)(parm)) +#define gr_get_fill_parm() (grd_canvas->gc.fill_parm) + +/* macros for setting the clipping region of a specified canvas. */ +#define gr_cset_cliprect(c, l, t, r, b) \ + (c)->gc.clip.i.sten=NULL, \ + (c)->gc.clip.i.left=(l), (c)->gc.clip.i.top=t, \ + (c)->gc.clip.i.right=(r), (c)->gc.clip.i.bot=(b) +//KLC - changed (c)->gc.clip.i.sten->flags=NULL +#define gr_cset_fix_cliprect(c, l, t, r, b) \ + (c)->gc.clip.i.sten=NULL, \ + (c)->gc.clip.f.left=(l), (c)->gc.clip.f.top=(t), \ + (c)->gc.clip.f.right=(r), (c)->gc.clip.f.bot=(b) +#define gr_cset_clipmask(canvas,t,b,mask) \ + (canvas)->gc.clip.i.top=(t), (canvas)->gc.clip.i.bot=(b), \ + (canvas)->gc.clip.i.sten = (mask) +#define gr_cset_fcolor(canvas,color) ((canvas)->gc.fcolor=color) +#define gr_cget_fcolor(canvas) ((canvas)->gc.fcolor) +#define gr_cset_bcolor(canvas,color) ((canvas)->gc.bcolor=color) +#define gr_cget_bcolor(canvas) ((canvas)->gc.bcolor) +#define gr_cset_font(canvas,fnt) ((canvas)->gc.font=fnt) +#define gr_cget_font(canvas) ((canvas)->gc.font) + +/* macros for getting part of the clipping region of the current canvas. */ +#define gr_get_cliprect(l,t,r,b) (*(l)=grd_clip.left,*(t)=grd_clip.top, \ + *(r)=grd_clip.right,*(b)=grd_clip.bot) +#define gr_get_fix_cliprect(l,t,r,b) (*(l)=grd_fix_clip.left, \ + *(t)=grd_fix_clip.top,*(r)=grd_clip.right,*(b)=grd_clip.bot) +#define gr_get_clip_l() (grd_clip.left) +#define gr_get_clip_t() (grd_clip.top) +#define gr_get_clip_r() (grd_clip.right) +#define gr_get_clip_b() (grd_clip.bot) +#define gr_get_fclip_l() (grd_fix_clip.left) +#define gr_get_fclip_t() (grd_fix_clip.top) +#define gr_get_fclip_r() (grd_fix_clip.right) +#define gr_get_fclip_b() (grd_fix_clip.bot) + +/* macros for getting part of the clipping region of a specified canvas. */ +#define gr_cget_cliprect(c,l,t,r,b) (\ + *(l)=(c)->gc.clip.i.left,*(t)=(c)->gc.clip.i.top,\ + *(r)=(c)->gc.clip.i.right,*(b)=(c)->gc.clip.i.bot) +#define gr_cget_fix_cliprect(l,t,r,b) (\ + *(l)=(c)->gc.clip.f.left,*(t)=(c)->gc.clip.f.top,\ + *(r)=(c)->gc.clip.f.right,*(b)=(c)->gc.clip.f.bot) +#define gr_cget_clip_l(c) ((c)->gc.clip.i.left) +#define gr_cget_clip_t(c) ((c)->gc.clip.i.top) +#define gr_cget_clip_r(c) ((c)->gc.clip.i.right) +#define gr_cget_clip_b(c) ((c)->gc.clip.i.bot) +#define gr_cget_fclip_l(c) ((c)->gc.clip.f.left) +#define gr_cget_fclip_t(c) ((c)->gc.clip.f.top) +#define gr_cget_fclip_r(c) ((c)->gc.clip.f.right) +#define gr_cget_fclip_b(c) ((c)->gc.clip.f.bot) + +#endif /* !__CTXMAC */ diff --git a/engine/src/Libraries/2D/Source/detect.c b/engine/src/Libraries/2D/Source/detect.c new file mode 100644 index 0000000..beaa2c2 --- /dev/null +++ b/engine/src/Libraries/2D/Source/detect.c @@ -0,0 +1,155 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/detect.c $ + * $Revision: 1.11 $ + * $Author: kevin $ + * $Date: 1994/08/16 13:16:57 $ + * + * Routine to detect what kind of video card is present and which + * graphics modes are available. + * + * This file is part of the 2d library. + */ + +#include "grs.h" +#include "bitmap.h" +#include "cnvtab.h" +#include "idevice.h" +#include "mode.h" +#include "tabdat.h" + +// extern +extern void (**grd_device_table_list[])(); + +// ====================================================================== +// Mac version of gr_detect +int gr_detect(grs_sys_info *info) + { + /* default to 640x480x8 standard Mac res */ + info->id_maj = 0; + info->id_min = 0; + info->memory = 300; + info->modes[0] = GRM_640x480x8; + info->modes[1] = GRM_320x400x8; + info->modes[2] = GRM_640x400x8; + info->modes[3] = GRM_320x200x8; + info->modes[4] = GRM_1024x768x8; + + grd_device_table = grd_device_table_list[info->id_maj]; + grd_canvas_table_list[BMT_DEVICE] = (void (**)())grd_device_table[GRT_CANVAS_TABLE]; + + return(0); + } + +// ====================================================================== +// PC version of gr_detect +#if 0 + +char *command[] = { "type","vendor","memory","modes" }; +char *card_type[] = { "vga","svga","tiga",NULL }; +char *vga_vendor[] = { "standard",NULL }; +char *svga_vendor[] = { "vesa","paradise","stealth","trident","tseng","video7",NULL }; +char *tiga_vendor[] = { "standard",NULL }; +char **card_vendor[] = { vga_vendor,svga_vendor,tiga_vendor,NULL }; +Datapath datapathCfg; + +int gr_detect(grs_sys_info *info) +{ + char token[80]; + FILE *fp; + int i; + int err=0; + + DatapathAddEnv(&datapathCfg, "CFGDIR"); + DatapathAdd(&datapathCfg, "c:/bin"); + if ((fp=DatapathOpen(&datapathCfg, "video.cfg", "r"))==NULL) { + if (vesa_get_info(info) != 0) { + /* default to vga. urk. */ + info->id_maj = 0; + info->id_min = 0; + info->memory = 256; + info->modes[0] = GRM_320x200x8; + info->modes[1] = GRM_320x200x8X; + info->modes[2] = GRM_320x240x8; + info->modes[3] = GRM_320x400x8; + info->modes[4] = GRM_320x480x8; + } + else { + int mode_val, cmode, tog; + for (mode_val=GRM_320x200x8; mode_val<=GRM_320x480x8; mode_val++) + { // go add the 5 modes above if they are missing? + cmode=0; + while ((info->modes[cmode]!=-1)&&(info->modes[cmode]!=mode_val)) + cmode++; + if (info->modes[cmode]==-1) + { info->modes[cmode++]=mode_val; info->modes[cmode]=-1; } + } + for (mode_val=cmode-1; mode_val>=0; mode_val--) + for (tog=0; togmodes[tog]>info->modes[tog+1]) { + int tmp=info->modes[tog+1]; + info->modes[tog+1]=info->modes[tog]; + info->modes[tog]=tmp; + } + } + } else { + while (!feof(fp)) { + fscanf(fp, "%s", token); + for (i=0; command[i]!=NULL; i++) + if (!stricmp(command[i], token)) + break; + switch (i) { + case 0: /* type */ + fscanf(fp, "%s", token); + for (i=0; card_type[i]!=NULL; i++) + if (!stricmp(card_type[i], token)) + info->id_maj = i; + break; + case 1: /* vendor */ + fscanf(fp, "%s", token); + for (i=0; card_vendor[info->id_maj][i]!=NULL; i++) + if (! stricmp(card_vendor[info->id_maj][i], token)) + info->id_min = i; + break; + case 2: /* memory */ + fscanf(fp, "%s", token); + info->memory = atoi (token); + break; + case 3: /* modes */ + i = 0; + while (fscanf(fp, "%s", token)!=EOF) + info->modes[i++] = atoi(token); + info->modes[i] = -1; + break; + default: + err = 1; + } + } + fclose (fp); + } + if (err==0) { + grd_device_table = grd_device_table_list[info->id_maj]; + grd_canvas_table_list[BMT_DEVICE] = + (void (**)())grd_device_table[GRT_CANVAS_TABLE]; + } + return err; +} +#endif + diff --git a/engine/src/Libraries/2D/Source/detect.h b/engine/src/Libraries/2D/Source/detect.h new file mode 100644 index 0000000..23030a3 --- /dev/null +++ b/engine/src/Libraries/2D/Source/detect.h @@ -0,0 +1,38 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/detect.h $ + * $Revision: 1.1 $ + * $Author: unknown $ + * $Date: 1993/04/08 16:24:18 $ + * + * Prototypes for detection routines. + * + * This file is part of the 2d library. + * + * $Log: detect.h $ + * Revision 1.1 1993/04/08 16:24:18 unknown + * Initial revision + * + */ + +#ifndef __DETECT_H +#define __DETECT_H +extern int gr_detect (grs_sys_info *info); +#endif /* !__DETECT_H */ diff --git a/engine/src/Libraries/2D/Source/devtab.c b/engine/src/Libraries/2D/Source/devtab.c new file mode 100644 index 0000000..8c90b06 --- /dev/null +++ b/engine/src/Libraries/2D/Source/devtab.c @@ -0,0 +1,41 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// $Source: n:/project/lib/src/2d/RCS/devtab.asm $ +// $Revision: 1.2 $ +// $Author: kaboom $ +// $Date: 1993/05/18 12:14:53 $ +// +// List of device driver function tables. +// +// This file is part of the 2d library. +// +// $Log: devtab.asm $ +// Revision 1.2 1993/05/18 12:14:53 kaboom +// Changed to use dseg.inc for data segment declaration. +// +// Revision 1.1 1993/04/29 16:45:14 kaboom +// Initial revision +// + +#include "MacDev.h" + +typedef void (**ptr_type)(); + +void (**grd_device_table_list[])() = {(ptr_type) mac_device_table}; + diff --git a/engine/src/Libraries/2D/Source/devtab.h b/engine/src/Libraries/2D/Source/devtab.h new file mode 100644 index 0000000..6088d0f --- /dev/null +++ b/engine/src/Libraries/2D/Source/devtab.h @@ -0,0 +1,43 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/devtab.h $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/12/05 21:13:51 $ + * + * Declaration for device table list. + * + * This file is part of the 2d library. + * + * $Log: devtab.h $ + * Revision 1.2 1994/12/05 21:13:51 kevin + * reworked to aviod linking in unused function tables. + * + * Revision 1.1 1993/05/03 13:45:02 kaboom + * Initial revision + * + */ + +#ifndef __DEVTAB_H +#define __DEVTAB_H +extern void (*flat8_device_table[])(); +extern void (*vga_device_table[])(); +extern void (*vesa_device_table[])(); +#endif /* !__DEVTAB_H */ diff --git a/engine/src/Libraries/2D/Source/fcntab.c b/engine/src/Libraries/2D/Source/fcntab.c new file mode 100644 index 0000000..5b0a8d3 --- /dev/null +++ b/engine/src/Libraries/2D/Source/fcntab.c @@ -0,0 +1,43 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fcntab.c $ + * $Revision: 1.4 $ + * $Author: kevin $ + * $Date: 1994/12/05 21:06:38 $ + * + * Function table lists and globals. + * + * This file is part of the 2d library. + */ + +#include "tabdrv.h" + +void (**grd_function_table)(); +grt_function_table *grd_function_fill_table; +grt_function_table *grd_function_table_list[] = { + NULL, /* device driver-initialized by gr_set_mode */ + NULL, /* monochrome-not supported */ + (grt_function_table *) flat8_function_table, /* flat 8 canvas */ + NULL, /* flat 24-not supported */ + NULL, /* doubling, RSD8 canvas-not supported */ + NULL, /* translucent 8-not supported */ + NULL, /* span-obsolete */ + NULL /* generic-initialized by gr_force_generic */ +}; diff --git a/engine/src/Libraries/2D/Source/fcntab.h b/engine/src/Libraries/2D/Source/fcntab.h new file mode 100644 index 0000000..18947bf --- /dev/null +++ b/engine/src/Libraries/2D/Source/fcntab.h @@ -0,0 +1,38 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/fcntab.h $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/09/06 00:35:07 $ + * + * Function table lists. + * + * This file is part of the 2d library. + * + */ +#ifndef __FCNTAB_H +#define __FCNTAB_H + +#include "tabdrv.h" +extern grt_function_table *grd_function_table_list[]; +extern grt_function_table *grd_function_fill_table; + +#endif /* __FCNTAB_H */ + diff --git a/engine/src/Libraries/2D/Source/fill.h b/engine/src/Libraries/2D/Source/fill.h new file mode 100644 index 0000000..75ac527 --- /dev/null +++ b/engine/src/Libraries/2D/Source/fill.h @@ -0,0 +1,66 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/fill.h $ + * $Revision: 1.8 $ + * $Author: unknown $ + * $Date: 1993/11/19 17:23:18 $ + * + * Fill constants. + * + * This file is part of the 2d library. + * + * $Log: fill.h $ + * Revision 1.8 1993/11/19 17:23:18 unknown + * Removed tluc fill type + * + * Revision 1.7 1993/11/18 03:57:56 baf + * Added tluc fill type + * + * Revision 1.6 1993/10/08 19:54:24 baf + * Added solid fill mode + * + * Revision 1.5 1993/08/23 14:44:32 jaemz + * Added blending primitives + * + * Revision 1.4 1993/06/03 15:07:31 kaboom + * Added constant for XOR fill mode. + * + * Revision 1.3 1993/05/03 13:49:17 kaboom + * Added GRD_FILL_TYPES to end of enum instead of as define. + * + * Revision 1.2 1993/02/24 10:56:24 kaboom + * Took out FILL_TRANS and added FILL_CLUT. + * + * Revision 1.1 1993/02/04 17:12:12 kaboom + * Initial revision + */ + +#ifndef __FILL_H +#define __FILL_H +/* span fill types. */ +enum { + FILL_NORM, + FILL_CLUT, + FILL_XOR, + FILL_BLEND, + FILL_SOLID, + GRD_FILL_TYPES +}; +#endif /* !__FILL_H */ diff --git a/engine/src/Libraries/2D/Source/icanvas.h b/engine/src/Libraries/2D/Source/icanvas.h new file mode 100644 index 0000000..f64a152 --- /dev/null +++ b/engine/src/Libraries/2D/Source/icanvas.h @@ -0,0 +1,483 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/icanvas.h $ + * $Revision: 1.27 $ + * $Author: kevin $ + * $Date: 1994/11/12 02:23:37 $ + * + * Symbolic constants for function table references. + * + * This file is part of the 2d library. + * + * $Log: icanvas.h $ + * Revision 1.27 1994/11/12 02:23:37 kevin + * replaced obsolete (u)hline constants with interrupt + * crunchy set pixel constants. + * + * Revision 1.26 1994/10/19 16:27:41 kevin + * Replaced obsolete line drawing indices with state push/pop entries. + * + * Revision 1.25 1994/04/09 07:27:15 lmfeeney + * added enum for new scaled string primiives + * , + * + * Revision 1.24 1994/03/14 11:01:59 kevin + * Added bitmap doubling placeholders and clut UNscaled bitmap placeholders. + * + * Revision 1.23 1994/02/14 22:15:34 baf + * Pruned vestigial 16/bit translucency + * + * Revision 1.22 1993/12/30 11:04:31 baf + * non/span solid filled polygons + * + * Revision 1.21 1993/12/28 22:04:39 baf + * Added solid RSD stuff + * + * Revision 1.20 1993/12/04 11:51:47 kevin + * Added placeholders for all kinds of texture mappers. + * + * Revision 1.19 1993/11/29 20:31:37 baf + * General revisions and tidying/up of translucency + * and shading routines + * + * Revision 1.18 1993/11/24 01:47:01 kevin + * Added wall and floor texture mapping primitive placeholders. + * + * Revision 1.17 1993/11/23 17:52:48 baf + * Added tluc/8 interpolation + * + * Revision 1.16 1993/11/19 17:29:20 baf + * Added 8/bit translucent bitmaps + * + * Revision 1.15 1993/11/15 03:32:11 baf + * Added primitiveless chains. + * + * Revision 1.14 1993/10/26 02:05:19 kevin + * Reorganized scaling and clut-scaling entries to allow versions for + * all bitmap types. + * + * Revision 1.13 1993/10/21 01:14:40 baf + * Added scaled translucent bitmaps and + * translucent linear texture maps. + * + * Revision 1.11 1993/10/15 12:06:39 baf + * Added translucent bitmap functions + * + * Revision 1.10 1993/10/06 13:41:56 kevin + * added constants for CLUT_LIN_{U}MAP and CLUT_HFLIP_*. + * + * Revision 1.9 1993/09/02 20:16:08 kaboom + * Added table constants for 24-bit pixels and lin_{u}map dispatching. + * + * Revision 1.8 1993/08/19 21:51:24 jaemz + * Added bitmap scale clut and voxel functions + * + * Revision 1.7 1993/08/10 19:16:49 kaboom + * Added constants for LIT_PER_{U}MAP. + * + * Revision 1.6 1993/07/20 16:24:55 jaemz + * Added interp2 and filterw2 + * + * Revision 1.5 1993/06/22 19:59:14 spaz + * Added slots for goroud-shaded lines: + * FIX_USLINE, SLINE, UCLINE, and CLINE. + * + * Revision 1.4 1993/06/14 14:12:23 kaboom + * New constants for lin_{u}map and lin_lit_lin_{u}map routines. Also + * changed names of old tmappers to per_map, etc. + * + * Revision 1.3 1993/06/06 15:13:42 kaboom + * Added constants for horizontally flipped bitmap entries. + * + * Revision 1.2 1993/06/01 13:50:35 kaboom + * Added constants for lit perspective tmappers. + * + * Revision 1.1 1993/05/03 13:45:46 kaboom + * Initial revision + */ + +#ifndef __ICANVAS_H +#define __ICANVAS_H +/* here are the indices for all the indirected driver functions. */ +enum { + /* first are the analytic primitive functions. */ + SET_UPIXEL8, /* pixel 8-bit set/get */ + SET_PIXEL8, + GET_UPIXEL8, + GET_PIXEL8, + + SET_UPIXEL24, /* pixel 24-bit set/get */ + SET_PIXEL24, + GET_UPIXEL24, + GET_PIXEL24, + + DRAW_CLEAR, /* integral, straight primitives */ + DRAW_UPOINT, + DRAW_POINT, + SET_UPIXEL8_INTERRUPT, + SET_PIXEL8_INTERRUPT, + DRAW_UVLINE, + DRAW_VLINE, + DRAW_URECT, + DRAW_RECT, + DRAW_UBOX, + DRAW_BOX, + + PUSH_STATE, + POP_STATE, + + FIX_USLINE, /* fixed-point rendering primitives */ + FIX_SLINE, + FIX_UCLINE, + FIX_CLINE, + FIX_UPOLY, + FIX_POLY, + FIX_USPOLY, + FIX_SPOLY, + FIX_UCPOLY, + FIX_CPOLY, + FIX_TLUC8_UPOLY, + FIX_TLUC8_POLY, + FIX_TLUC8_USPOLY, + FIX_TLUC8_SPOLY, + + VOX_RECT, + VOX_POLY, + VOX_CPOLY, + INTERP2_UBITMAP, + FILTER2_UBITMAP, + ROLL_UBITMAP, + ROLL_BITMAP, + + FLAT8_WALL_UMAP, + FLAT8_WALL_MAP, + FLAT8_LIT_WALL_UMAP, + FLAT8_LIT_WALL_MAP, + FLAT8_CLUT_WALL_UMAP, + FLAT8_CLUT_WALL_MAP, + + FLAT8_FLOOR_UMAP, + FLAT8_FLOOR_MAP, + FLAT8_LIT_FLOOR_UMAP, + FLAT8_LIT_FLOOR_MAP, + FLAT8_CLUT_FLOOR_UMAP, + FLAT8_CLUT_FLOOR_MAP, + + DEVICE_ULMAP, /* linear mapper */ + DEVICE_LMAP, + MONO_ULMAP, + MONO_LMAP, + FLAT8_ULMAP, + FLAT8_LMAP, + FLAT24_ULMAP, + FLAT24_LMAP, + RSD_ULMAP, + RSD_LMAP, + TLUC8_ULMAP, + TLUC8_LMAP, + + DEVICE_LIT_LIN_UMAP, /* lit linear mapper */ + DEVICE_LIT_LIN_MAP, + MONO_LIT_LIN_UMAP, + MONO_LIT_LIN_MAP, + FLAT8_LIT_LIN_UMAP, + FLAT8_LIT_LIN_MAP, + FLAT24_LIT_LIN_UMAP, + FLAT24_LIT_LIN_MAP, + RSD_LIT_LIN_UMAP, + RSD_LIT_LIN_MAP, + TLUC8_LIT_LIN_UMAP, + TLUC8_LIT_LIN_MAP, + + DEVICE_CLUT_LIN_UMAP, /* clut linear mapper */ + DEVICE_CLUT_LIN_MAP, + MONO_CLUT_LIN_UMAP, + MONO_CLUT_LIN_MAP, + FLAT8_CLUT_LIN_UMAP, + FLAT8_CLUT_LIN_MAP, + FLAT24_CLUT_LIN_UMAP, + FLAT24_CLUT_LIN_MAP, + RSD_CLUT_LIN_UMAP, + RSD_CLUT_LIN_MAP, + TLUC8_CLUT_LIN_UMAP, + TLUC8_CLUT_LIN_MAP, + + FLAT8_SOLID_LIN_UMAP, + FLAT8_SOLID_LIN_MAP, /* solid linear mapper */ + + DEVICE_PER_UMAP, /* perspective mapper */ + DEVICE_PER_MAP, + MONO_PER_UMAP, + MONO_PER_MAP, + FLAT8_PER_UMAP, + FLAT8_PER_MAP, + FLAT24_PER_UMAP, + FLAT24_PER_MAP, + RSD_PER_UMAP, + RSD_PER_MAP, + TLUC8_PER_UMAP, + TLUC8_PER_MAP, + + DEVICE_LIT_PER_UMAP, /* lit perspective mapper */ + DEVICE_LIT_PER_MAP, + MONO_LIT_PER_UMAP, + MONO_LIT_PER_MAP, + FLAT8_LIT_PER_UMAP, + FLAT8_LIT_PER_MAP, + FLAT24_LIT_PER_UMAP, + FLAT24_LIT_PER_MAP, + RSD_LIT_PER_UMAP, + RSD_LIT_PER_MAP, + TLUC8_LIT_PER_UMAP, + TLUC8_LIT_PER_MAP, + + DEVICE_CLUT_PER_UMAP, /* clut perspective mapper */ + DEVICE_CLUT_PER_MAP, + MONO_CLUT_PER_UMAP, + MONO_CLUT_PER_MAP, + FLAT8_CLUT_PER_UMAP, + FLAT8_CLUT_PER_MAP, + FLAT24_CLUT_PER_UMAP, + FLAT24_CLUT_PER_MAP, + RSD_CLUT_PER_UMAP, + RSD_CLUT_PER_MAP, + TLUC8_CLUT_PER_UMAP, + TLUC8_CLUT_PER_MAP, + + FLAT8_SOLID_PER_UMAP, /* solid perspective mapper */ + FLAT8_SOLID_PER_MAP, + + INT_UCIRCLE, /* curves, should change to fixed-point */ + INT_CIRCLE, + FIX_UCIRCLE, + FIX_CIRCLE, + INT_UDISK, + INT_DISK, + FIX_UDISK, + FIX_DISK, + INT_UROD, + INT_ROD, + FIX_UROD, + FIX_ROD, + + DRAW_DEVICE_UBITMAP, /* bitmap drawing functions */ + DRAW_DEVICE_BITMAP, + DRAW_MONO_UBITMAP, + DRAW_MONO_BITMAP, + DRAW_FLAT8_UBITMAP, + DRAW_FLAT8_BITMAP, + DRAW_FLAT24_UBITMAP, + DRAW_FLAT24_BITMAP, + DRAW_RSD8_UBITMAP, + DRAW_RSD8_BITMAP, + DRAW_TLUC8_UBITMAP, + DRAW_TLUC8_BITMAP, + + CLUT_DRAW_DEVICE_UBITMAP, /* bitmap drawing functions through a clut */ + CLUT_DRAW_DEVICE_BITMAP, + CLUT_DRAW_MONO_UBITMAP, + CLUT_DRAW_MONO_BITMAP, + CLUT_DRAW_FLAT8_UBITMAP, + CLUT_DRAW_FLAT8_BITMAP, + CLUT_DRAW_FLAT24_UBITMAP, + CLUT_DRAW_FLAT24_BITMAP, + CLUT_DRAW_RSD8_UBITMAP, + CLUT_DRAW_RSD8_BITMAP, + CLUT_DRAW_TLUC8_UBITMAP, + CLUT_DRAW_TLUC8_BITMAP, + + SOLID_RSD8_UBITMAP, /* solid bitmap drawing functions */ + SOLID_RSD8_BITMAP, + + SCALE_DEVICE_UBITMAP, /* scaled bitmap drawing functions */ + SCALE_DEVICE_BITMAP, + SCALE_MONO_UBITMAP, + SCALE_MONO_BITMAP, + SCALE_FLAT8_UBITMAP, + SCALE_FLAT8_BITMAP, + SCALE_FLAT24_UBITMAP, + SCALE_FLAT24_BITMAP, + SCALE_RSD8_UBITMAP, + SCALE_RSD8_BITMAP, + SCALE_TLUC8_UBITMAP, + SCALE_TLUC8_BITMAP, + + SOLID_SCALE_RSD8_UBITMAP, /* solid bitmap drawing functions */ + SOLID_SCALE_RSD8_BITMAP, + + CLUT_SCALE_DEVICE_UBITMAP, /* bitmap scaling functions through a clut */ + CLUT_SCALE_DEVICE_BITMAP, + CLUT_SCALE_MONO_UBITMAP, + CLUT_SCALE_MONO_BITMAP, + CLUT_SCALE_FLAT8_UBITMAP, + CLUT_SCALE_FLAT8_BITMAP, + CLUT_SCALE_FLAT24_UBITMAP, + CLUT_SCALE_FLAT24_BITMAP, + CLUT_SCALE_RSD8_UBITMAP, + CLUT_SCALE_RSD8_BITMAP, + CLUT_SCALE_TLUC8_UBITMAP, + CLUT_SCALE_TLUC8_BITMAP, + + MASK_DEVICE_UBITMAP, /* bitmap mask draw functions */ + MASK_DEVICE_BITMAP, + MASK_MONO_UBITMAP, + MASK_MONO_BITMAP, + MASK_FLAT8_UBITMAP, + MASK_FLAT8_BITMAP, + MASK_FLAT24_UBITMAP, + MASK_FLAT24_BITMAP, + MASK_RSD8_UBITMAP, + MASK_RSD8_BITMAP, + MASK_TLUC8_UBITMAP, + MASK_TLUC8_BITMAP, + + GET_DEVICE_UBITMAP, /* bitmap get functions */ + GET_DEVICE_BITMAP, + GET_MONO_UBITMAP, + GET_MONO_BITMAP, + GET_FLAT8_UBITMAP, + GET_FLAT8_BITMAP, + GET_FLAT24_UBITMAP, + GET_FLAT24_BITMAP, + GET_RSD8_UBITMAP, + GET_RSD8_BITMAP, + GET_TLUC8_UBITMAP, + GET_TLUC8_BITMAP, + + HFLIP_DEVICE_UBITMAP, /* bitmap horizontal flip functions */ + HFLIP_DEVICE_BITMAP, + HFLIP_MONO_UBITMAP, + HFLIP_MONO_BITMAP, + HFLIP_FLAT8_UBITMAP, + HFLIP_FLAT8_BITMAP, + HFLIP_FLAT24_UBITMAP, + HFLIP_FLAT24_BITMAP, + HFLIP_RSD8_UBITMAP, + HFLIP_RSD8_BITMAP, + HFLIP_TLUC8_UBITMAP, + HFLIP_TLUC8_BITMAP, + + CLUT_HFLIP_DEVICE_UBITMAP, /* bitmap color lookup table hozo flip fcts*/ + CLUT_HFLIP_DEVICE_BITMAP, + CLUT_HFLIP_MONO_UBITMAP, + CLUT_HFLIP_MONO_BITMAP, + CLUT_HFLIP_FLAT8_UBITMAP, + CLUT_HFLIP_FLAT8_BITMAP, + CLUT_HFLIP_FLAT24_UBITMAP, + CLUT_HFLIP_FLAT24_BITMAP, + CLUT_HFLIP_RSD8_UBITMAP, + CLUT_HFLIP_RSD8_BITMAP, + CLUT_HFLIP_TLUC8_UBITMAP, + CLUT_HFLIP_TLUC8_BITMAP, + + DOUBLE_H_DEVICE_UBITMAP, + DOUBLE_H_DEVICE_BITMAP, + DOUBLE_H_MONO_UBITMAP, + DOUBLE_H_MONO_BITMAP, + DOUBLE_H_FLAT8_UBITMAP, + DOUBLE_H_FLAT8_BITMAP, + DOUBLE_H_FLAT24_UBITMAP, + DOUBLE_H_FLAT24_BITMAP, + DOUBLE_H_RSD8_UBITMAP, + DOUBLE_H_RSD8_BITMAP, + DOUBLE_H_TLUC8_UBITMAP, + DOUBLE_H_TLUC8_BITMAP, + + DOUBLE_V_DEVICE_UBITMAP, + DOUBLE_V_DEVICE_BITMAP, + DOUBLE_V_MONO_UBITMAP, + DOUBLE_V_MONO_BITMAP, + DOUBLE_V_FLAT8_UBITMAP, + DOUBLE_V_FLAT8_BITMAP, + DOUBLE_V_FLAT24_UBITMAP, + DOUBLE_V_FLAT24_BITMAP, + DOUBLE_V_RSD8_UBITMAP, + DOUBLE_V_RSD8_BITMAP, + DOUBLE_V_TLUC8_UBITMAP, + DOUBLE_V_TLUC8_BITMAP, + + DOUBLE_HV_DEVICE_UBITMAP, + DOUBLE_HV_DEVICE_BITMAP, + DOUBLE_HV_MONO_UBITMAP, + DOUBLE_HV_MONO_BITMAP, + DOUBLE_HV_FLAT8_UBITMAP, + DOUBLE_HV_FLAT8_BITMAP, + DOUBLE_HV_FLAT24_UBITMAP, + DOUBLE_HV_FLAT24_BITMAP, + DOUBLE_HV_RSD8_UBITMAP, + DOUBLE_HV_RSD8_BITMAP, + DOUBLE_HV_TLUC8_UBITMAP, + DOUBLE_HV_TLUC8_BITMAP, + + SMOOTH_DOUBLE_H_DEVICE_UBITMAP, + SMOOTH_DOUBLE_H_DEVICE_BITMAP, + SMOOTH_DOUBLE_H_MONO_UBITMAP, + SMOOTH_DOUBLE_H_MONO_BITMAP, + SMOOTH_DOUBLE_H_FLAT8_UBITMAP, + SMOOTH_DOUBLE_H_FLAT8_BITMAP, + SMOOTH_DOUBLE_H_FLAT24_UBITMAP, + SMOOTH_DOUBLE_H_FLAT24_BITMAP, + SMOOTH_DOUBLE_H_RSD8_UBITMAP, + SMOOTH_DOUBLE_H_RSD8_BITMAP, + SMOOTH_DOUBLE_H_TLUC8_UBITMAP, + SMOOTH_DOUBLE_H_TLUC8_BITMAP, + + SMOOTH_DOUBLE_V_DEVICE_UBITMAP, + SMOOTH_DOUBLE_V_DEVICE_BITMAP, + SMOOTH_DOUBLE_V_MONO_UBITMAP, + SMOOTH_DOUBLE_V_MONO_BITMAP, + SMOOTH_DOUBLE_V_FLAT8_UBITMAP, + SMOOTH_DOUBLE_V_FLAT8_BITMAP, + SMOOTH_DOUBLE_V_FLAT24_UBITMAP, + SMOOTH_DOUBLE_V_FLAT24_BITMAP, + SMOOTH_DOUBLE_V_RSD8_UBITMAP, + SMOOTH_DOUBLE_V_RSD8_BITMAP, + SMOOTH_DOUBLE_V_TLUC8_UBITMAP, + SMOOTH_DOUBLE_V_TLUC8_BITMAP, + + SMOOTH_DOUBLE_HV_DEVICE_UBITMAP, + SMOOTH_DOUBLE_HV_DEVICE_BITMAP, + SMOOTH_DOUBLE_HV_MONO_UBITMAP, + SMOOTH_DOUBLE_HV_MONO_BITMAP, + SMOOTH_DOUBLE_HV_FLAT8_UBITMAP, + SMOOTH_DOUBLE_HV_FLAT8_BITMAP, + SMOOTH_DOUBLE_HV_FLAT24_UBITMAP, + SMOOTH_DOUBLE_HV_FLAT24_BITMAP, + SMOOTH_DOUBLE_HV_RSD8_UBITMAP, + SMOOTH_DOUBLE_HV_RSD8_BITMAP, + SMOOTH_DOUBLE_HV_TLUC8_UBITMAP, + SMOOTH_DOUBLE_HV_TLUC8_BITMAP, + + DRAW_USTRING, /* text/font functions */ + DRAW_STRING, + DRAW_SCALE_USTRING, + DRAW_SCALE_STRING, + DRAW_UCHAR, + DRAW_CHAR, + + CALC_ROW, /* bitmap type specific functions */ + SUB_BITMAP, + + START_FRAME, /* no primitives, just chains */ + END_FRAME, + + GRD_CANVAS_FUNCS +}; +#endif /* !__ICANVAS_H */ diff --git a/engine/src/Libraries/2D/Source/idevice.h b/engine/src/Libraries/2D/Source/idevice.h new file mode 100644 index 0000000..a282e90 --- /dev/null +++ b/engine/src/Libraries/2D/Source/idevice.h @@ -0,0 +1,56 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/idevice.h $ + * $Revision: 1.1 $ + * $Author: kaboom $ + * $Date: 1993/05/03 13:46:06 $ + * + * Symbolic constants for function table references. + * + * This file is part of the 2d library. + * + * $Log: idevice.h $ + * Revision 1.1 1993/05/03 13:46:06 kaboom + * Initial revision + * + */ + +#ifndef __IDEVICE_H +#define __IDEVICE_H +enum { + GRT_INIT_DEVICE, + GRT_CLOSE_DEVICE, + GRT_SET_MODE, + GRT_GET_MODE, + GRT_SET_STATE, + GRT_GET_STATE, + GRT_STAT_HTRACE, + GRT_STAT_VTRACE, + GRT_SET_PAL, + GRT_GET_PAL, + GRT_SET_WIDTH, + GRT_GET_WIDTH, + GRT_SET_FOCUS, + GRT_GET_FOCUS, + GRT_CANVAS_TABLE, + GRT_SPAN_TABLE, + GRD_DEVICE_FUNCS +}; +#endif /* !__IDEVICE_H */ diff --git a/engine/src/Libraries/2D/Source/ifcn.h b/engine/src/Libraries/2D/Source/ifcn.h new file mode 100644 index 0000000..a07f544 --- /dev/null +++ b/engine/src/Libraries/2D/Source/ifcn.h @@ -0,0 +1,126 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/ifcn.h $ + * $Revision: 1.1 $ + * $Author: kevin $ + * $Date: 1994/08/16 15:35:57 $ + * + * Function type indices for table lookups. + * + * This file is part of the 2d library. + * + * $Log: ifcn.h $ + * Revision 1.1 1994/08/16 15:35:57 kevin + * Initial revision + * + * +*/ + +#ifndef __IFCN_H +#define __IFCN_H + +enum { + /* lines and pixel primitives */ + GRC_PIXEL, +#define GRC_LINE GRC_PIXEL /* lines & pixels are multiplexed by bm type */ + GRC_WIRE_POLY_LINE, + GRC_DEGEN_LINE, /* hlines and vlines */ + + GRC_BITMAP, + GRC_STENCIL_BITMAP, + GRC_CLUT_BITMAP, + + GRC_HFLIP_BITMAP, + GRC_CLUT_HFLIP_BITMAP, + + GRC_MASK_BITMAP, + + GRC_HDOUBLE_BITMAP, + GRC_VDOUBLE_BITMAP, + GRC_HVDOUBLE_BITMAP, + + GRC_HDOUBLE_BLEND_BITMAP, + GRC_VDOUBLE_BLEND_BITMAP, + GRC_HVDOUBLE_BLEND_BITMAP, + + GRC_SCALE, + GRC_TRANS_SCALE, + GRC_LIT_SCALE, + GRC_TRANS_LIT_SCALE, + GRC_CLUT_SCALE, + GRC_TRANS_CLUT_SCALE, + + GRC_POLY, /* Actually all poly types; uses bitmap types to multiplex */ + GRC_MORE_POLY, /* reserved for future use */ + + GRC_LIN, + GRC_TRANS_LIN, + GRC_LIT_LIN, + GRC_TRANS_LIT_LIN, + GRC_CLUT_LIN, + GRC_TRANS_CLUT_LIN, + + GRC_BILIN, + GRC_TRANS_BILIN, + GRC_LIT_BILIN, + GRC_TRANS_LIT_BILIN, + GRC_CLUT_BILIN, + GRC_TRANS_CLUT_BILIN, + + GRC_FLOOR, + GRC_TRANS_FLOOR, + GRC_LIT_FLOOR, + GRC_TRANS_LIT_FLOOR, + GRC_CLUT_FLOOR, + GRC_TRANS_CLUT_FLOOR, + + GRC_WALL2D, + GRC_TRANS_WALL2D, + GRC_LIT_WALL2D, + GRC_TRANS_LIT_WALL2D, + GRC_CLUT_WALL2D, + GRC_TRANS_CLUT_WALL2D, + + GRC_WALL1D, + GRC_TRANS_WALL1D, + GRC_LIT_WALL1D, + GRC_TRANS_LIT_WALL1D, + GRC_CLUT_WALL1D, + GRC_TRANS_CLUT_WALL1D, + + GRC_PER, + GRC_TRANS_PER, + GRC_LIT_PER, + GRC_TRANS_LIT_PER, + GRC_CLUT_PER, + GRC_TRANS_CLUT_PER, + + GRC_PER_VSCAN, + GRC_TRANS_PER_VSCAN, + GRC_LIT_PER_VSCAN, + GRC_TRANS_LIT_PER_VSCAN, + GRC_CLUT_PER_VSCAN, + GRC_TRANS_CLUT_PER_VSCAN, + + GRD_FUNCS +}; +#endif /* !__IFCN_H */ + + diff --git a/engine/src/Libraries/2D/Source/init.c b/engine/src/Libraries/2D/Source/init.c new file mode 100644 index 0000000..72d584f --- /dev/null +++ b/engine/src/Libraries/2D/Source/init.c @@ -0,0 +1,66 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/init.c $ + * $Revision: 1.12 $ + * $Author: kevin $ + * $Date: 1994/12/05 21:07:38 $ + * + * Routine to initialize the 2d system for use. + * + * This file is part of the 2d library. + * + */ + +#include "grd.h" +#include "detect.h" +#include "state.h" +#include "memall.h" +#include "tmpalloc.h" + +/* flag for whether 2d system has been fired up. */ +int grd_active = 0; + +/* start up 2d system. try to detect what kind of video hardware is + present, call device-dependent initialization, and save state. + returns same as gr_detect() 0 if all is well, or error code. */ +int gri_init(void) +{ + int err; + MemStack *tmp; + + if (grd_active != 0) + return 0; + + tmp = temp_mem_get_stack(); + if (tmp == NULL) { + err = temp_mem_init(NULL); + if (err != 0) + return err; + } + + err = gr_detect (&grd_info); + if (err != 0) + return err; + gr_push_video_state (1); + grd_active = 1; + //init_inverse_table(); + + return 0; +} diff --git a/engine/src/Libraries/2D/Source/init_2D.h b/engine/src/Libraries/2D/Source/init_2D.h new file mode 100644 index 0000000..8210632 --- /dev/null +++ b/engine/src/Libraries/2D/Source/init_2D.h @@ -0,0 +1,47 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * DG 2018-05-12: renamed to init_2D.h to avoid confusion with other init.h + * + * $Source: r:/prj/lib/src/2d/RCS/init.h $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/12/05 21:13:53 $ + * + * Declarations for public initialization functions. + * + * This file is part of the 2d library. + * + * $Log: init.h $ + * Revision 1.2 1994/12/05 21:13:53 kevin + * reworked to aviod linking in unused function tables. + * + * Revision 1.1 1993/05/03 13:46:14 kaboom + * Initial revision + * + */ + +#ifndef __INIT_H +#define __INIT_H +extern int gr_init (void); +extern int gr_vga_init (void); +extern int gr_flat8_init (void); +#define gr_svga_init gr_init +#define gr_vesa_init gr_init +#endif /* !__INIT_H */ diff --git a/engine/src/Libraries/2D/Source/initint.h b/engine/src/Libraries/2D/Source/initint.h new file mode 100644 index 0000000..a8c9c0d --- /dev/null +++ b/engine/src/Libraries/2D/Source/initint.h @@ -0,0 +1,41 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/initint.h $ + * $Revision: 1.1 $ + * $Author: kevin $ + * $Date: 1994/12/05 21:13:55 $ + * + * Declarations for public initialization functions. + * + * This file is part of the 2d library. + * + * $Log: initint.h $ + * Revision 1.1 1994/12/05 21:13:55 kevin + * Initial revision + * + * Revision 1.1 1993/05/03 13:46:14 kaboom + * Initial revision + * + */ + +#ifndef __INITINT_H +#define __INITINT_H +extern int gri_init (void); +#endif /* !__INITINT_H */ diff --git a/engine/src/Libraries/2D/Source/line.h b/engine/src/Libraries/2D/Source/line.h new file mode 100644 index 0000000..eff4978 --- /dev/null +++ b/engine/src/Libraries/2D/Source/line.h @@ -0,0 +1,42 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/line.h $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/08/04 09:46:35 $ + */ + +#ifndef __LINE_H +#define __LINE_H + +enum { + GR_LINE, + GR_ILINE, + GR_HLINE, + GR_VLINE, + GR_SLINE, + GR_CLINE, + GR_WIRE_POLY_LINE, + GR_WIRE_POLY_SLINE, + GR_WIRE_POLY_CLINE, + GRD_LINE_TYPES +}; + +#endif diff --git a/engine/src/Libraries/2D/Source/linfcn.h b/engine/src/Libraries/2D/Source/linfcn.h new file mode 100644 index 0000000..70a758d --- /dev/null +++ b/engine/src/Libraries/2D/Source/linfcn.h @@ -0,0 +1,101 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/linfcn.h $ + * $Revision: 1.1 $ + * $Author: kevin $ + * $Date: 1994/09/06 02:24:49 $ + */ + +#ifndef __LINFCN_H +#define __LINFCN_H + +#include "plytyp.h" +/* functions living in the tables */ + +/* all - means that it is a dispatcher */ +extern void gri_all_uiline_fill(long, long, grs_vertex *, grs_vertex *); + +/* gen is also bank8, bank24 and modex */ +extern void gri_gen_uline_fill(long, long, grs_vertex *, grs_vertex *); +extern void gri_gen_uhline_fill(short, short, short, long, long); +extern void gri_gen_uvline_fill(short, short, short, long, long); +extern void gri_gen_usline_fill(long, long, grs_vertex *, grs_vertex *); +extern void gri_gen_ucline_fill(long, long, grs_vertex *, grs_vertex *); +extern void gri_gen_wire_poly_uline(long, long, grs_vertex *, grs_vertex *); +// extern void gri_gen_wire_poly_usline(long, long, grs_vertex *, grs_vertex *); +extern void gri_gen_wire_poly_ucline(long, long, grs_vertex *, grs_vertex *); + +/* flat8 -- for each line type and fill type */ +extern void gri_flat8_uline_ns(long, long, grs_vertex *, grs_vertex *); +extern void gri_flat8_uline_clut(long, long, grs_vertex *, grs_vertex *); +extern void gri_flat8_uline_xor(long, long, grs_vertex *, grs_vertex *); +extern void gri_flat8_uline_blend(long, long, grs_vertex *, grs_vertex *); + +extern void gri_flat8_uhline_ns(short, short, short, long, long); +extern void gri_flat8_uhline_clut(short, short, short, long, long); +extern void gri_flat8_uhline_xor(short, short, short, long, long); +extern void gri_flat8_uhline_blend(short, short, short, long, long); + +extern void gri_flat8_uvline_ns(short, short, short, long, long); +extern void gri_flat8_uvline_clut(short, short, short, long, long); +extern void gri_flat8_uvline_xor(short, short, short, long, long); +extern void gri_flat8_uvline_blend(short, short, short, long, long); + +extern void gri_flat8_ucline_norm(long, long, grs_vertex *, grs_vertex *); +extern void gri_flat8_ucline_clut(long, long, grs_vertex *, grs_vertex *); +extern void gri_flat8_ucline_xor(long, long, grs_vertex *, grs_vertex *); +extern void gri_flat8_ucline_blend(long, long, grs_vertex *, grs_vertex *); + +extern void gri_flat8_usline_norm(long, long, grs_vertex *, grs_vertex *); +extern void gri_flat8_usline_clut(long, long, grs_vertex *, grs_vertex *); +extern void gri_flat8_usline_xor(long, long, grs_vertex *, grs_vertex *); +extern void gri_flat8_usline_blend(long, long, grs_vertex *, grs_vertex *); + +extern void gri_flat8_wire_poly_uline(long, long, grs_vertex *, grs_vertex *); +// extern void gri_flat8_wire_poly_uline_xor(long, long, grs_vertex *, grs_vertex *); +// extern void gri_flat8_wire_poly_uline_blend(long, long, grs_vertex *, grs_vertex *); + +/* +extern void gri_flat8_wire_poly_usline_norm(long, long, grs_vertex *, grs_vertex *); +extern void gri_flat8_wire_poly_usline_clut(long, long, grs_vertex *, grs_vertex *); +extern void gri_flat8_wire_poly_usline_xor(long, long, grs_vertex *, grs_vertex *); +extern void gri_flat8_wire_poly_usline_blend(long, long, grs_vertex *, grs_vertex *); +*/ + +extern void gri_flat8_wire_poly_ucline_norm(long, long, grs_vertex *, grs_vertex *); +extern void gri_flat8_wire_poly_ucline_clut(long, long, grs_vertex *, grs_vertex *); +// extern void gri_flat8_wire_poly_ucline_xor(long, long, grs_vertex *, grs_vertex *); +// extern void gri_flat8_wire_poly_ucline_blend(long, long, grs_vertex *, grs_vertex *); + +/* bank8 and modex have their own hlines only */ +/* +extern void gri_modex_uhline_ns(short, short, short, long, long); +extern void gri_modex_uhline_clut(short, short, short, long, long); +extern void gri_modex_uhline_xor(short, short, short, long, long); +extern void gri_modex_uhline_blend(short, short, short, long, long); +*/ +/* +extern void gri_bank8_uhline_ns(short, short, short, long, long); +extern void gri_bank8_uhline_clut(short, short, short, long, long); +extern void gri_bank8_uhline_xor(short, short, short, long, long); +extern void gri_bank8_uhline_blend(short, short, short, long, long); +*/ +#endif diff --git a/engine/src/Libraries/2D/Source/lintab.c b/engine/src/Libraries/2D/Source/lintab.c new file mode 100644 index 0000000..75b767f --- /dev/null +++ b/engine/src/Libraries/2D/Source/lintab.c @@ -0,0 +1,350 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/lintab.c $ + * $Revision: 1.5 $ + * $Author: kevin $ + * $Date: 1994/10/04 18:46:52 $ + */ + +#include "grnull.h" +#include "lintyp.h" +#include "linfcn.h" + +/* in addition to the tables that are in the list, there are device specific tables */ + +grt_uline_fill *grd_uline_fill_vector; +grt_uline_fill_table *grd_uline_fill_table; + +grt_uline_fill flat8_uline_fill_table[GRD_FILL_TYPES][GRD_LINE_TYPES] = { + // FILL_NORM + { + gri_flat8_uline_ns, // GR_LINE + gri_all_uiline_fill, // GR_ILINE + gri_flat8_uhline_ns, // GR_HLINE + gri_flat8_uvline_ns, // GR_VLINE + gri_flat8_usline_norm, // GR_SLINE + gri_flat8_ucline_norm, // GR_CLINE + gri_flat8_wire_poly_uline, // GR_WIRE_POLY_LINE + gr_null, // GR_WIRE_POLY_SLINE + gri_flat8_wire_poly_ucline_norm // GR_WIRE_POLY_CLINE + }, + // FILL_CLUT + { + gri_flat8_uline_clut, + gri_all_uiline_fill, + gri_flat8_uhline_clut, + gri_flat8_uvline_clut, + gri_flat8_usline_clut, + gri_flat8_ucline_norm, + gri_flat8_wire_poly_uline, + gr_null, + gri_flat8_wire_poly_ucline_clut + }, + // FILL_XOR + { + gri_flat8_uline_xor, + gri_all_uiline_fill, + gri_flat8_uhline_xor, + gri_flat8_uvline_xor, + gri_flat8_usline_xor, + gri_flat8_ucline_norm, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, + // FILL_BLEND + { + gri_flat8_uline_blend, + gri_all_uiline_fill, + gri_flat8_uhline_blend, + gri_flat8_uvline_blend, + gri_flat8_usline_blend, + gri_flat8_ucline_norm, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, + // FILL_SOLID + { + gri_flat8_uline_ns, + gri_all_uiline_fill, + gri_flat8_uhline_ns, + gri_flat8_uvline_ns, + gri_flat8_uline_ns, + gri_flat8_uline_ns, + gri_flat8_wire_poly_uline, + gr_null, + gri_flat8_wire_poly_uline + }, +}; + +/* gen have their fill types info buries in grd_fill_pixel */ +grt_uline_fill gen_uline_fill_table[GRD_FILL_TYPES][GRD_LINE_TYPES] = { + { + gri_gen_uline_fill, + gri_all_uiline_fill, + gri_gen_uhline_fill, + gri_gen_uvline_fill, + gri_gen_usline_fill, + gri_gen_ucline_fill, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, + { + gri_gen_uline_fill, + gri_all_uiline_fill, + gri_gen_uhline_fill, + gri_gen_uvline_fill, + gri_gen_usline_fill, + gri_gen_ucline_fill, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, + { + gri_gen_uline_fill, + gri_all_uiline_fill, + gri_gen_uhline_fill, + gri_gen_uvline_fill, + gri_gen_usline_fill, + gri_gen_ucline_fill, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, + { + gri_gen_uline_fill, + gri_all_uiline_fill, + gri_gen_uhline_fill, + gri_gen_uvline_fill, + gri_gen_usline_fill, + gri_gen_ucline_fill, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, + { + gri_gen_uline_fill, + gri_all_uiline_fill, + gri_gen_uhline_fill, + gri_gen_uvline_fill, + gri_gen_uline_fill, + gri_gen_uline_fill, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, +}; + +/* WH - these tables not used by game +// temporarily modex, bank8 and bank24 are all gen's +grt_uline_fill modex_uline_fill_table[GRD_FILL_TYPES][GRD_LINE_TYPES] = { + { + gri_gen_uline_fill, + gri_all_uiline_fill, + (void *)gr_null, // MLA gri_modex_uhline_ns, + gri_gen_uvline_fill, + gri_gen_usline_fill, + gri_gen_ucline_fill, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, + { + gri_gen_uline_fill, + gri_all_uiline_fill, + gri_gen_uhline_fill, + gri_gen_uvline_fill, + gri_gen_usline_fill, + gri_gen_ucline_fill, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, + { + gri_gen_uline_fill, + gri_all_uiline_fill, + gri_gen_uhline_fill, + gri_gen_uvline_fill, + gri_gen_usline_fill, + gri_gen_ucline_fill, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, + { + gri_gen_uline_fill, + gri_all_uiline_fill, + gri_gen_uhline_fill, + gri_gen_uvline_fill, + gri_gen_usline_fill, + gri_gen_ucline_fill, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, + { + gri_gen_uline_fill, + gri_all_uiline_fill, + (void *)gr_null, // MLA gri_modex_uhline_ns, + gri_gen_uvline_fill, + gri_gen_uline_fill, + gri_gen_uline_fill, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, +}; + +// WH - this too +grt_uline_fill bank8_uline_fill_table[GRD_FILL_TYPES][GRD_LINE_TYPES] = { + { + gri_gen_uline_fill, + gri_all_uiline_fill, + (void *)gr_null, // MLA gri_bank8_uhline_ns, + gri_gen_uvline_fill, + gri_gen_usline_fill, + gri_gen_ucline_fill, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, + { + gri_gen_uline_fill, + gri_all_uiline_fill, + gri_gen_uhline_fill, + gri_gen_uvline_fill, + gri_gen_usline_fill, + gri_gen_ucline_fill, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, + { + gri_gen_uline_fill, + gri_all_uiline_fill, + gri_gen_uhline_fill, + gri_gen_uvline_fill, + gri_gen_usline_fill, + gri_gen_ucline_fill, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, + { + gri_gen_uline_fill, + gri_all_uiline_fill, + gri_gen_uhline_fill, + gri_gen_uvline_fill, + gri_gen_usline_fill, + gri_gen_ucline_fill, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, + { + gri_gen_uline_fill, + gri_all_uiline_fill, + (void *)gr_null, // MLA gri_bank8_uhline_ns, + gri_gen_uvline_fill, + gri_gen_uline_fill, + gri_gen_uline_fill, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, +}; + +// WH - and this +grt_uline_fill bank24_uline_fill_table[GRD_FILL_TYPES][GRD_LINE_TYPES] = { + { + gri_gen_uline_fill, + gri_all_uiline_fill, + gri_gen_uhline_fill, + gri_gen_uvline_fill, + gri_gen_usline_fill, + gri_gen_ucline_fill, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, + { + gri_gen_uline_fill, + gri_all_uiline_fill, + gri_gen_uhline_fill, + gri_gen_uvline_fill, + gri_gen_usline_fill, + gri_gen_ucline_fill, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, + { + gri_gen_uline_fill, + gri_all_uiline_fill, + gri_gen_uhline_fill, + gri_gen_uvline_fill, + gri_gen_usline_fill, + gri_gen_ucline_fill, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, + { + gri_gen_uline_fill, + gri_all_uiline_fill, + gri_gen_uhline_fill, + gri_gen_uvline_fill, + gri_gen_usline_fill, + gri_gen_ucline_fill, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, + { + gri_gen_uline_fill, + gri_all_uiline_fill, + gri_gen_uhline_fill, + gri_gen_uvline_fill, + gri_gen_uline_fill, + gri_gen_uline_fill, + gri_gen_wire_poly_uline, + gr_null, + gri_gen_wire_poly_ucline + }, +}; +*/ + +grt_uline_fill_table *grd_uline_fill_table_list[] = { + NULL, + NULL, + (grt_uline_fill_table *)flat8_uline_fill_table, + NULL, +#ifdef GR_DOUBLE_CANVAS + (grt_uline_fill_table *)flat8_uline_fill_table, +#else + NULL, +#endif + NULL, + NULL, + (grt_uline_fill_table *)gen_uline_fill_table +}; diff --git a/engine/src/Libraries/2D/Source/lintab.h b/engine/src/Libraries/2D/Source/lintab.h new file mode 100644 index 0000000..d8fd5d2 --- /dev/null +++ b/engine/src/Libraries/2D/Source/lintab.h @@ -0,0 +1,48 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/lintab.h $ + * $Revision: 1.4 $ + * $Author: kevin $ + * $Date: 1994/09/06 02:25:12 $ + */ + +#ifndef __LINTAB_H +#define __LINTAB_H + +#include "line.h" +#include "lintyp.h" +#include "plytyp.h" + +/* these tables are used in the 2d, but are not visible, except though the vector (see lintyp.h) */ + +extern grt_uline_fill_table *grd_uline_fill_table; +extern grt_uline_fill_table *grd_uline_fill_table_list[]; + +extern grt_uline_fill gen_uline_fill_table[][GRD_LINE_TYPES]; +extern grt_uline_fill flat8_uline_fill_table[][GRD_LINE_TYPES]; + +/* +// WH - these tables not used by game, see lintab.c +extern grt_uline_fill bank8_uline_fill_table[][GRD_LINE_TYPES]; +extern grt_uline_fill bank24_uline_fill_table[][GRD_LINE_TYPES]; +extern grt_uline_fill modex_uline_fill_table[][GRD_LINE_TYPES]; +*/ +#endif diff --git a/engine/src/Libraries/2D/Source/lintyp.h b/engine/src/Libraries/2D/Source/lintyp.h new file mode 100644 index 0000000..68530eb --- /dev/null +++ b/engine/src/Libraries/2D/Source/lintyp.h @@ -0,0 +1,76 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/lintyp.h $ + * $Revision: 1.3 $ + * $Author: kevin $ + * $Date: 1994/09/06 02:28:40 $ + */ + +#ifndef __LINTYP_H +#define __LINTYP_H + +#include "plytyp.h" +#include "line.h" +#include "fill.h" + + +/* The function pointer is actutally a ptr to void, because it contains + two different kinds of pointers. Don't use a union since I want to + be able to initialize as automatic. + + LINES, ILINES, ULINES and CLINES take a vertex interface (fix pt coord) + + For these, the conversion to vertex is cheap relative to the overall + cost and needed for rgb and i values. (ILINES is converted, it does + fix-pt internally.) + + HLINES and VLINES take an xy interface (short coord) + + For these, these take short args and are used for speed and + for lots of calulations on the arglements (e.g. menues). + Unfortunately, they now take 5 params (3 points and 2 parms). + + Fill parm last, hpoing compiler notices it's not always used. + +*/ + +typedef + void *grt_uline_fill; + +typedef + void (*grt_uline_fill_v) (long, long, grs_vertex *, grs_vertex *); + +typedef + void (*grt_uline_fill_xy) (short, short, short, long, long); + +typedef + void (*grt_wire_poly_uline) (long, long, grs_vertex *, grs_vertex *); + +typedef + void (*grt_wire_poly_ucline) (long, long, grs_vertex *, grs_vertex *); + +typedef + grt_uline_fill grt_uline_fill_table[GRD_FILL_TYPES][GRD_LINE_TYPES]; + +#define grt_wire_poly_usline grt_wire_poly_ucline; + +extern grt_uline_fill *grd_uline_fill_vector; + +#endif diff --git a/engine/src/Libraries/2D/Source/mode.c b/engine/src/Libraries/2D/Source/mode.c new file mode 100644 index 0000000..35ca2d7 --- /dev/null +++ b/engine/src/Libraries/2D/Source/mode.c @@ -0,0 +1,84 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/mode.c $ + * $Revision: 1.2 $ + * $Author: kaboom $ + * $Date: 1993/10/08 01:16:09 $ + * + * Mode information table. + * + * This file is part of the 2d library. + * + * $Log: mode.c $ + * Revision 1.2 1993/10/08 01:16:09 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.1 1993/04/29 18:54:40 kaboom + * Initial revision + * + */ + +#include "grs.h" +#include "grd.h" +#include "grdev.h" +#include "mode.h" +#include "cnvtab.h" +#include "bitmap.h" + +grs_mode_info grd_mode_info[GRD_MODES] = { + { 320, 200, 8 }, + { 320, 200, 8 }, + { 320, 400, 8 }, + { 320, 240, 8 }, + { 320, 480, 8 }, + { 640, 400, 8 }, + { 640, 480, 8 }, + { 800, 600, 8 }, + { 1024, 768, 8 }, + { 1280, 1024, 8 }, + { 320, 200, 24 }, + { 640, 480, 24 }, + { 800, 600, 24 }, + { 1024, 768, 24 }, + { 1280, 1024, 24 } +}; + +// code from SMODE.ASM +int gr_set_mode (int mode, int clear) + { + gr_set_screen_mode(mode, clear); // try to set graphics mode. + gr_init_device(&grd_info); // try to initialize device if pointer isn't NULL + + // copy width & height values from info table to capability list + grd_mode = mode; + grd_mode_cap.w = grd_mode_info[mode].w; + grd_mode_cap.h = grd_mode_info[mode].h; + + // store aspect into capability struct. + grd_mode_cap.aspect = 0x010000; // fixed 1:1 aspect ratio on Mac + + grd_canvas_table = grd_canvas_table_list[BMT_DEVICE]; + grd_pixel_table = grd_canvas_table_list[BMT_DEVICE]; + + return(0); + } + + + \ No newline at end of file diff --git a/engine/src/Libraries/2D/Source/mode.h b/engine/src/Libraries/2D/Source/mode.h new file mode 100644 index 0000000..eb36a39 --- /dev/null +++ b/engine/src/Libraries/2D/Source/mode.h @@ -0,0 +1,84 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/mode.h $ + * $Revision: 1.3 $ + * $Author: kaboom $ + * $Date: 1993/10/08 01:16:10 $ + * + * Constants for graphics modes. + * + * This file is part of the 2d library. + * + * $Log: mode.h $ + * Revision 1.3 1993/10/08 01:16:10 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.2 1993/05/16 00:36:57 kaboom + * Added mirror constants with lower case x's. + * + * Revision 1.1 1993/05/03 13:46:37 kaboom + * Initial revision + */ + +#ifndef __MODE_H +#define __MODE_H + +#include "grs.h" +/* definitions for all supported device-independent graphics modes. */ +enum { + GRM_320x200x8, + GRM_320x200x8X, + GRM_320x400x8, + GRM_320x240x8, + GRM_320x480x8, + GRM_640x400x8, + GRM_640x480x8, + GRM_800x600x8, + GRM_1024x768x8, + GRM_1280x1024x8, + GRM_320x200x24, + GRM_640x480x24, + GRM_800x600x24, + GRM_1024x768x24, + GRM_1280x1024x24 +}; + +enum { + GRM_320X200X8, + GRM_320X200X8X, + GRM_320X400X8, + GRM_320X240X8, + GRM_320X480X8, + GRM_640X400X8, + GRM_640X480X8, + GRM_800X600X8, + GRM_1024X768X8, + GRM_1280X1024X8, + GRM_320X200X24, + GRM_640X480X24, + GRM_800X600X24, + GRM_1024X768X24, + GRM_1280X1024X24, + GRD_MODES +}; + +extern grs_mode_info grd_mode_info[]; +extern int gr_set_mode (int mode, int clear); +#endif /* !__MODE_H */ diff --git a/engine/src/Libraries/2D/Source/pal.c b/engine/src/Libraries/2D/Source/pal.c new file mode 100644 index 0000000..52d4e5c --- /dev/null +++ b/engine/src/Libraries/2D/Source/pal.c @@ -0,0 +1,93 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/pal.c $ + * $Revision: 1.8 $ + * $Author: lmfeeney $ + * $Date: 1994/06/18 04:05:26 $ + * + * Routines and data for non-hardware-dependent palette control. + * + * This file is part of the 2d library. + * + * $Log: pal.c $ + * Revision 1.8 1994/06/18 04:05:26 lmfeeney + * added set palette with gamma correct + * + * Revision 1.7 1993/10/19 09:51:43 kaboom + * Replaced #include "grd.h> with new headers split from grd.h. + * + * Revision 1.6 1993/10/08 01:16:18 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.5 1993/07/12 23:31:37 kaboom + * Now bounds checks number of palette entries. + * + * Revision 1.4 1993/04/29 19:07:29 kaboom + * Changed include of old gr.h to grdev.h. + * + * Revision 1.3 1993/02/08 11:58:05 kaboom + * Fixed bug in gr_set_pal -- wasn't adjusting for start of pal. + * + * Revision 1.2 1993/02/04 17:42:48 kaboom + * Changed includes. + * + * Revision 1.1 1993/01/29 17:27:29 kaboom + * Initial revision + */ + +#include +#include "rgb.h" +#include "grdev.h" +#include "scrdat.h" +#include "fix.h" +#include "lg.h" + +//gamma param not used here; see SetSDLPalette() in Shock.c +void gr_set_gamma_pal (int start, int n, fix gamma) +{ + gr_set_screen_pal (start, n, grd_pal+3*start); +} + +/* copy user's palette into shadow palette, then set real palette. */ +void gr_set_pal (int start, int n, uchar *pal_data) +{ + int i; + uchar r,g,b; /* red,green,blue values */ + + if (n <= 0) + return; + + LG_memcpy (grd_pal+3*start, pal_data, 3*n); + for (i=start; i. + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/pal.h $ + * $Revision: 1.3 $ + * $Author: kevin $ + * $Date: 1994/07/28 23:40:18 $ + * + * Prototypes and macros for palette manipulation routines. + * + * This file is part of the 2d library. + * + * $Log: pal.h $ + * Revision 1.3 1994/07/28 23:40:18 kevin + * c:\dev\mach64\install 33 gamma_set_pal prototype. + * + * Revision 1.2 1994/06/18 04:05:47 lmfeeney + * added set palette with gamma correct + * , + * + * Revision 1.1 1993/02/04 17:43:02 kaboom + * Initial revision + * + */ + +#ifndef __PAL_H +#define __PAL_H + +extern void gr_set_pal (int start, int n, uchar *pal_data); +extern void gr_set_gamma_pal (int start, int n, fix gamma); +extern void gr_get_pal (int start, int n, uchar *pal_data); + +#endif /* !__PAL_H */ diff --git a/engine/src/Libraries/2D/Source/permap.c b/engine/src/Libraries/2D/Source/permap.c new file mode 100644 index 0000000..ddecb5a --- /dev/null +++ b/engine/src/Libraries/2D/Source/permap.c @@ -0,0 +1,173 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/permap.c $ + * $Revision: 1.8 $ + * $Author: kevin $ + * $Date: 1994/12/01 14:59:57 $ + * + * Full perspective texture mapping dispatchers. + * +*/ + +#include "bitmap.h" +#include "buffer.h" +#include "clpcon.h" +#include "clpfcn.h" +#include "cnvdat.h" +#include "fill.h" +#include "fl8p.h" +#include "ifcn.h" +#include "grnull.h" +#include "pertyp.h" +#include "scrmac.h" +#include "tmapfcn.h" +#include "tmaps.h" +#include "tmaptab.h" + +extern int gri_per_umap_setup(int n, grs_vertex **vpl, grs_per_setup *ps); + +uchar grd_enable_quad_blend=FALSE; + +int per_map (grs_bitmap *bm, int n, grs_vertex **vpl, grs_tmap_info *ti) +{ + grs_vertex **cpl; /* clipped vertices */ + int m; /* number of clipped vertices */ + + cpl = NULL; + m = gr_clip_poly(n,5,vpl,&cpl); + if (m>2) + per_umap(bm,m,cpl,ti); + gr_free_temp(cpl); + + return ((m>2) ? CLIP_NONE : CLIP_ALL); +} + +void per_umap (grs_bitmap *bm, int n, grs_vertex **vpl, grs_tmap_info *ti) +{ + short percode; + grs_per_setup ps; + uchar *save_bits; + + ps.dp=bm->flags&BMF_TRANS; + if (2*grd_gc.fill_type + ps.dp==2*FILL_SOLID) { + h_umap(bm, n, vpl, ti); + return; + } +//¥¥¥ÊMLA - doesnt' ever appear to use this +/* + if ((bm->type==BMT_FLAT8)&&grd_enable_quad_blend) { + int u_min=vpl[0]->u; + int u_max=u_min; + int v_min=vpl[0]->v; + int v_max=v_min; + int x_min=vpl[0]->x; + int x_max=x_min; + int y_min=vpl[0]->y; + int y_max=y_min; + int i,canvas_delta; + + for (i=1;iu>u_max) u_max=vpl[i]->u; + if (vpl[i]->uu; + if (vpl[i]->v>v_max) v_max=vpl[i]->v; + if (vpl[i]->vv; + } + + for (i=1;iy>y_max) y_max=vpl[i]->y; + if (vpl[i]->yy; + if (vpl[i]->x>x_max) x_max=vpl[i]->x; + if (vpl[i]->xx; + } + if (x_max-x_min>y_max-y_min) + canvas_delta=x_max-x_min; + else + canvas_delta=y_max-y_min; + if (canvas_delta>30*FIX_UNIT) { + if ((fix_int(u_max)-fix_int(u_min)<4)&& + (fix_int(v_max)-fix_int(v_min)<4)) { + extern void gri_flat8_hv_quadruple_sub_bitmap(grs_bitmap *sbm, grs_bitmap *dbm, int u, int v); + int u=fix_int(u_min); + int v=fix_int(v_min); + grs_bitmap tbm; + + if (u+4>=bm->w) u=bm->w-4; + if (v+4>=bm->h) v=bm->h-4; + tbm.type=BMT_FLAT8; + tbm.flags=bm->flags; + gri_flat8_hv_quadruple_sub_bitmap(bm, &tbm, u, v); + for (i=0;iu = ((vpl[i]->u-fix_make(u,0))<<2)+8*FIX_UNIT; + vpl[i]->v = ((vpl[i]->v-fix_make(v,0))<<2)+8*FIX_UNIT; + } + per_umap(&tbm, n, vpl, ti); + for (i=0;iu = ((vpl[i]->u-8*FIX_UNIT)>>2)+fix_make(u,0); + vpl[i]->v = ((vpl[i]->v-8*FIX_UNIT)>>2)+fix_make(v,0); + } + return; + } + } + } +*/ + + percode=gri_per_umap_setup(n, vpl, &ps); + + /* should be set by init func, but just in case...*/ + ps.shell_func=gr_null; + + ps.dp+=ti->tmap_type+(GRD_FUNCS*bm->type); + if (grd_gc.fill_type!=FILL_NORM) + ps.fill_parm=grd_gc.fill_parm; + else if (ti->flags&TMF_CLUT) + if ((ps.clut=ti->clut)==NULL) + ps.clut=gr_get_clut(); + + save_bits=bm->bits; /* in case bitmap type is rsd8 */ + + switch (percode) { + case GR_PER_CODE_BIGSLOPE: + ((void (*)(grs_bitmap *, grs_per_setup *))(grd_tmap_hscan_init_table[ps.dp]))(bm,&ps); + ((void (*)(grs_bitmap *, int, grs_vertex **, grs_per_setup *))(ps.shell_func))(bm,n,vpl,&ps); + break; + case GR_PER_CODE_SMALLSLOPE: + ((void (*)(grs_bitmap *, grs_per_setup *))(grd_tmap_vscan_init_table[ps.dp]))(bm,&ps); + ((void (*)(grs_bitmap *, int, grs_vertex **, grs_per_setup *))(ps.shell_func))(bm,n,vpl,&ps); + break; + case GR_PER_CODE_LIN: + ti->tmap_type+=GRC_BILIN-GRC_PER; + h_umap(bm,n,vpl,ti); + break; + case GR_PER_CODE_FLOOR: + ti->tmap_type+=GRC_FLOOR-GRC_PER; + ti->flags|=TMF_FLOOR; + h_umap(bm,n,vpl,ti); + break; + case GR_PER_CODE_WALL: + ti->tmap_type+=GRC_WALL2D-GRC_PER; + ti->flags|=TMF_WALL; + v_umap(bm,n,vpl,ti); + break; + } + bm->bits=save_bits; + return; +} + + diff --git a/engine/src/Libraries/2D/Source/persetup.c b/engine/src/Libraries/2D/Source/persetup.c new file mode 100644 index 0000000..391ad3f --- /dev/null +++ b/engine/src/Libraries/2D/Source/persetup.c @@ -0,0 +1,223 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/persetup.c $ + * $Revision: 1.3 $ + * $Author: kevin $ + * $Date: 1994/11/02 19:39:48 $ + * + * setup routines for full perspective mappers. + * +*/ + +#include "cnvdat.h" +#include "plytyp.h" +#include "scrdat.h" +#include "buffer.h" +#include "pertyp.h" +#include "fix.h" +#include "fl8p.h" + +//#include "mprintf.h" + +// prototypes +int gri_per_umap_setup (int n, grs_vertex **vplist, grs_per_setup *ps); + +grs_per_context *grd_per_context=NULL; /* perspective mapping context */ + +/*int fix_mul_16_32_20(int one, int two) { + return one * two; +} + +int fix_mul_3_16_20(int one, int two) { + return one * two; +} + +int fix_mul_3_32_16(int one, int two) { + return one * two; +} + +int fix_mul_3_3_3(int one, int two) { + return one * two; +}*/ + +int gri_per_umap_setup (int n, grs_vertex **vplist, grs_per_setup *ps) +{ + fix a,b,c0,cz,z_min,aos,bos; + fix z_max,y_min,y_max,x_min,x_max; + fix wux,wuy,wuz,wvx,wvy,wvz; + grs_point3d *l3d,*pl3d,*l3d0,*l3d1,*l3d2; + grs_vertex *vp,**pvp,*vpl0,*vpl1,*vpl2,*vpl_z_max,*vpl_z_min; + int i; + fix delta_min; + int scale; + + scale = (grd_bm.w>grd_bm.h) ? grd_bm.w:grd_bm.h; + l3d = (grs_point3d *)gr_alloc_temp(n*sizeof(grs_point3d)); + if (l3d==NULL) return GR_PER_CODE_MEMERR; + +/* First get variables for plane equation: a*x+b*y+c*z=k */ + z_max=FIX_MIN; z_min=FIX_MAX; + y_max=FIX_MIN; y_min=FIX_MAX; + x_max=FIX_MIN; x_min=FIX_MAX; + for (pl3d=l3d,vp=*(pvp=vplist),i=0;ix=(vp->x-fix_make(ACENT,0)); + pl3d->y=(vp->y-fix_make(BCENT,0)); + pl3d->z=fix_div(FIX_UNIT,vp->w); + if (z_maxz) {z_max=pl3d->z; vpl_z_max=vp;} + if (vp->x>x_max) x_max=vp->x; + if (vp->xx; + if (vp->y>y_max) y_max=vp->y; + if (vp->yy; + } + delta_min=x_max-x_min; + if (delta_minz = fix_div_16_16_3(pl3d->z,z_max); + if (z_min>pl3d->z) {z_min=pl3d->z; vpl_z_min=vplist[i];} + pl3d->x = fix_mul_div_3_16_16_3(pl3d->x,pl3d->z,fix_make(scale,0)); + pl3d->y = fix_mul_div_3_16_16_3(pl3d->y,pl3d->z,fix_make(scale,0)); + } + if (fix_sar(z_min,flat8_per_ltol) > (FIX_UNIT_3-z_min)) { + /*use linear mapper.*/ + gr_free_temp(l3d); + return GR_PER_CODE_LIN; + } +/********************************************************** +Determine best vertices to use for plane calculations. +Use the three vertices with the maximum shortest distance +between pairs. +**********************************************************/ + { + fix maxsd, minsd, sd; + grs_vertex *v1,*v2,*v3; + short j,k,i0=0,i1=1,i2=2; + + maxsd=0; + for (v1=vplist[i=0]; ix - v2->x) + +fix_abs(v1->y - v2->y); + sd=fix_abs(v3->x - v2->x) + +fix_abs(v3->y - v2->y); + if (sdx - v3->x) + +fix_abs(v1->y - v3->y); + if (sdmaxsd) { + maxsd=minsd; i0=i; i1=j; i2=k; + } + } + } + } + l3d0=l3d+i0; l3d1=l3d+i1; l3d2=l3d+i2; + vpl0=vplist[i0]; vpl1=vplist[i1]; vpl2=vplist[i2]; + } + + a=fix_mul_3_3_3((l3d1->y-l3d0->y),(l3d2->z-l3d0->z)) + -fix_mul_3_3_3((l3d2->y-l3d0->y),(l3d1->z-l3d0->z)); + b=fix_mul_3_3_3((l3d1->z-l3d0->z),(l3d2->x-l3d0->x)) + -fix_mul_3_3_3((l3d2->z-l3d0->z),(l3d1->x-l3d0->x)); + cz=fix_mul_3_3_3((l3d1->x-l3d0->x),(l3d2->y-l3d0->y)) + -fix_mul_3_3_3((l3d2->x-l3d0->x),(l3d1->y-l3d0->y)); + c0=(fix_mul_3_3_3(a,l3d0->x)+fix_mul_3_3_3(b,l3d0->y)+fix_mul_3_3_3(cz,l3d0->z)); + + if (fix_sar(fix_abs(a),7) > fix_sar(fix_abs(b),7-flat8_per_wftol)) { +// use wall mapper + gr_free_temp(l3d); + return GR_PER_CODE_WALL; + } + if (fix_sar(fix_abs(b),7) > fix_sar(fix_abs(a),7-flat8_per_wftol)) { +// use floor mapper + gr_free_temp(l3d); + return GR_PER_CODE_FLOOR; + } + +/****************************************************** +so virtual scan line equations are given by +a*x_scr+b*y_scr=scale*((c0/z)-cz) +******************************************************/ + +/************************************************************************** +Get wu, wv: vectors s.t. u-u0=wu*(r-r0); v-v0=wv*(r-r0). +the solutions are determined from two points in the plane (besides r0) and +the fact that wu and wv are perpendicular to the normal to the plane, +f=(a,b,cz). +**************************************************************************/ + { + fix dx1, dx2, dy1, dy2, dz1, dz2, denom; /*3's*/ + fix du1, du2; /*16's*/ + fix dv1, dv2; + + dx1=l3d1->x-l3d0->x; dx2=l3d2->x-l3d0->x; + dy1=l3d1->y-l3d0->y; dy2=l3d2->y-l3d0->y; + dz1=l3d1->z-l3d0->z; dz2=l3d2->z-l3d0->z; + du1=vpl1->u-vpl0->u; du2=vpl2->u-vpl0->u; + dv1=vpl1->v-vpl0->v; dv2=vpl2->v-vpl0->v; + + denom=-(fix_mul_3_3_3(a,a)+fix_mul_3_3_3(b,b) + +fix_mul_3_3_3(cz,cz)); + if (denom==0) {gr_free_temp(l3d); return GR_PER_CODE_BADPLANE;} + + wux=fix_div_16_3_16(fix_mul_3_16_16(b,fix_mul_3_16_16(dz2,du1)-fix_mul_3_16_16(dz1,du2)) + -fix_mul_3_16_16(cz,fix_mul_3_16_16(dy2,du1)-fix_mul_3_16_16(dy1,du2)),denom); + wuy=fix_div_16_3_16(fix_mul_3_16_16(cz,fix_mul_3_16_16(dx2,du1)-fix_mul_3_16_16(dx1,du2)) + -fix_mul_3_16_16(a,fix_mul_3_16_16(dz2,du1)-fix_mul_3_16_16(dz1,du2)),denom); + wuz=fix_div_16_3_16(fix_mul_3_16_16(a,fix_mul_3_16_16(dy2,du1)-fix_mul_3_16_16(dy1,du2)) + -fix_mul_3_16_16(b,fix_mul_3_16_16(dx2,du1)-fix_mul_3_16_16(dx1,du2)),denom); + + wvx=fix_div_16_3_16(fix_mul_3_16_16(b,fix_mul_3_16_16(dz2,dv1)-fix_mul_3_16_16(dz1,dv2)) + -fix_mul_3_16_16(cz,fix_mul_3_16_16(dy2,dv1)-fix_mul_3_16_16(dy1,dv2)),denom); + wvy=fix_div_16_3_16(fix_mul_3_16_16(cz,fix_mul_3_16_16(dx2,dv1)-fix_mul_3_16_16(dx1,dv2)) + -fix_mul_3_16_16(a,fix_mul_3_16_16(dz2,dv1)-fix_mul_3_16_16(dz1,dv2)),denom); + wvz=fix_div_16_3_16(fix_mul_3_16_16(a,fix_mul_3_16_16(dy2,dv1)-fix_mul_3_16_16(dy1,dv2)) + -fix_mul_3_16_16(b,fix_mul_3_16_16(dx2,dv1)-fix_mul_3_16_16(dx1,dv2)),denom); + + } + aos=fix_make(ACENT,0)/scale; + bos=fix_make(BCENT,0)/scale; + ps->alpha_v=fix_mul_3_16_20(c0,wvx); + ps->beta_v=fix_mul_3_16_20(c0,wvy); + ps->gamma_v=fix_mul_16_32_20(fix_mul_3_16_16(c0,wvz- + (fix_mul(wvx,aos)+fix_mul(wvy,bos))),scale); + ps->alpha_u=fix_mul_3_16_20(c0,wux); + ps->beta_u=fix_mul_3_16_20(c0,wuy); + ps->gamma_u=fix_mul_16_32_20(fix_mul_3_16_16(c0,wuz- + (fix_mul(wux,aos)+fix_mul(wuy,bos))),scale); +// mprintf("gu:%x gv:%x\n",ps->gamma_u,ps->gamma_v); + ps->a=fix_3_16(a); + ps->b=fix_3_16(b); + ps->c=fix_mul_3_32_16(cz,scale)-fix_mul_3_32_16(a,ACENT)-fix_mul_3_32_16(b,BCENT); + if (fix_abs(a)<=fix_abs(b)) { + ps->scan_slope=-fix_div_3_3_16(a,b); + gr_free_temp(l3d); + return GR_PER_CODE_BIGSLOPE; + } + else { + ps->scan_slope=-fix_div_3_3_16(b,a); + gr_free_temp(l3d); + return GR_PER_CODE_SMALLSLOPE; + } +} diff --git a/engine/src/Libraries/2D/Source/pertol.c b/engine/src/Libraries/2D/Source/pertol.c new file mode 100644 index 0000000..ad1de77 --- /dev/null +++ b/engine/src/Libraries/2D/Source/pertol.c @@ -0,0 +1,104 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/pertol.c $ + * $Revision: 1.9 $ + * $Author: kevin $ + * $Date: 1994/08/28 16:26:43 $ + * + * Function to set wall, floor, and linear mapping + * tolerances for the perspective mappers. + * ltol and wftol should be between 0 and 7. + * Lower values allow more frequent use of the + * specialized wall, floor and linear mappers. + * + * $Log: pertol.c $ + * Revision 1.9 1994/08/28 16:26:43 kevin + * Lowered maximum tolerance to place less stress + * on the perspective mappers. + * + * Revision 1.8 1994/08/16 13:24:13 kevin + * lowered tolerances slightly. + * + * Revision 1.7 1994/07/28 16:56:30 kevin + * Increased default tolerances. + * + * Revision 1.6 1994/01/16 12:03:21 kevin + * Added clut lighting global, general detail level stuff. + * + * Revision 1.5 1994/01/05 04:34:23 kevin + * Lowered default linear tolerance to take advantage of new linear mappers. + * + * Revision 1.4 1993/12/15 02:42:14 kevin + * Set initial tolerances to 4. + * + * Revision 1.3 1993/12/14 22:38:47 kevin + * Moved persective mapper context globals to fl8ps.c + * + * Revision 1.2 1993/12/04 12:35:49 kevin + * Added global context variable for persective mappers. + * Don't ask me why I put it here. + * + * Revision 1.1 1993/11/18 23:40:27 kevin + * Initial revision + * +*/ + +#include "fix.h" +#include "grs.h" +#include "pertol.h" + +ubyte flat8_per_ltol=5; /* Linear tolerance */ +ubyte flat8_per_wftol=5; /* Wall/Floor tolerance */ + +/* Clut Lighting tolerance: higher=more clut lighting. */ +fix gr_clut_lit_tol=2*FIX_UNIT; + +gr_per_detail_level gr_per_detail_list[GR_NUM_PER_DETAIL_LEVELS]= +{ + {0,0,4*FIX_UNIT}, + {3,4,2*FIX_UNIT}, + {5,5,0} +}; + +void gr_set_per_tol(ubyte ltol, ubyte wftol) +{ + flat8_per_ltol=(ltol&7); + flat8_per_wftol=(wftol&7); +} + +void gr_set_clut_lit_tol(fix cltol) +{ + gr_clut_lit_tol=cltol; +} + +void gr_set_per_detail_level(int level) +{ + flat8_per_ltol=gr_per_detail_list[level].ltol; + flat8_per_wftol=gr_per_detail_list[level].wftol; + gr_clut_lit_tol=gr_per_detail_list[level].cltol; +} + +void gr_set_per_detail_level_param(int ltol, int wftol, fix cltol, int level) +{ + gr_per_detail_list[level].ltol=ltol&7; + gr_per_detail_list[level].wftol=wftol&7; + gr_per_detail_list[level].cltol=cltol; +} + diff --git a/engine/src/Libraries/2D/Source/pertol.h b/engine/src/Libraries/2D/Source/pertol.h new file mode 100644 index 0000000..c70939d --- /dev/null +++ b/engine/src/Libraries/2D/Source/pertol.h @@ -0,0 +1,56 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/pertol.h $ + * $Revision: 1.1 $ + * $Author: kevin $ + * $Date: 1994/01/16 12:01:31 $ + * + * Structures, constants, and prototypes for + * perspective detail setting procedures. + * $Log: pertol.h $ + * Revision 1.1 1994/01/16 12:01:31 kevin + * Initial revision + * +*/ + +#ifndef __PERTOL_H +#define __PERTOL_H + +#include "fix.h" + +typedef struct { + ubyte ltol, wftol; + fix cltol; +} gr_per_detail_level; + +enum { + GR_LOW_PER_DETAIL, + GR_MEDIUM_PER_DETAIL, + GR_HIGH_PER_DETAIL, + GR_NUM_PER_DETAIL_LEVELS +}; + +extern void gr_set_per_tol(ubyte linear_tol, ubyte wall_floor_tol); +extern void gr_set_clut_lit_tol(fix clut_lit_tol); +extern void gr_set_per_detail_level(int detail_level); +extern void gr_set_per_detail_level_param + (int linear_tol, int wall_floor_tol, fix clut_lit_tol, int detail_level); + +#endif diff --git a/engine/src/Libraries/2D/Source/pertyp.h b/engine/src/Libraries/2D/Source/pertyp.h new file mode 100644 index 0000000..612141e --- /dev/null +++ b/engine/src/Libraries/2D/Source/pertyp.h @@ -0,0 +1,102 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/pertyp.h $ + * $Revision: 1.3 $ + * $Author: kevin $ + * $Date: 1994/07/20 23:07:19 $ + * + * Perspective mapper public data structures. + * + * This file is part of the 2d library. + * + * $Log: pertyp.h $ + * Revision 1.3 1994/07/20 23:07:19 kevin + * Added fill_parm as synonym for clut in grs_per_info and grs_per_setup structures. + * + * Revision 1.2 1994/07/04 01:37:24 kevin + * added new structure. + * + * Revision 1.1 1993/12/14 22:35:12 kevin + * Initial revision + * +*/ + +/* Perspective mapper context structure. */ + +#ifndef __PERTYP_H +#define __PERTYP_H + +#include "grs.h" + +typedef struct { + grs_point3d normal; + grs_point3d u_grad; + grs_point3d v_grad; +} grs_per_context; + +/*********************************************************/ +/* these are variables such that for screen coords x,y: */ +/* u=u0+(alpha_u*x+beta_u*y+gamma_u)/(a*x+b*y+c) */ +/* v=v0+(alpha_v*x+beta_v*y+gamma_v)/(a*x+b*y+c) */ +/* and lines of constant z are given by a*x+b*y=k */ +/* so scan_slope= -a/b for hscan, -b/a for vscan, */ +/* and its magnitude is always <= 1. */ +/*********************************************************/ +typedef struct { + void (*scanline_func)(); /* function to do scanline. */ + void (*shell_func)(); /* perspective mapping shell. */ + union {uchar *clut; intptr_t fill_parm;}; + fix scan_slope; + int dp; + fix alpha_u; + fix beta_u; + fix gamma_u; + fix alpha_v; + fix beta_v; + fix gamma_v; + fix a; + fix b; + fix c; +} grs_per_setup; + +typedef struct { + uchar *p_dst_final; + int p_dst_off; + union {fix y_fix,x_fix;}; + fix u,du,v,dv,i,di; + fix u0,v0; + union {uchar *clut; intptr_t fill_parm;}; + fix unum,vnum,dunum,dvnum,denom; + fix dxl,dyl,dtl,dxr,dyr,dtr; + fix cl,cr; + fix scan_slope; + int dp; + union {int yp,xp;}; + union {int x,y;}; + union {int xl,yl;}; + union {int xr,yr;}; + union {int xr0,yr0;}; + int u_mask,v_mask,v_shift; + int scale; +} grs_per_info; + +#endif /* !__PERTYP_H */ + + diff --git a/engine/src/Libraries/2D/Source/pixfill.h b/engine/src/Libraries/2D/Source/pixfill.h new file mode 100644 index 0000000..9558c31 --- /dev/null +++ b/engine/src/Libraries/2D/Source/pixfill.h @@ -0,0 +1,32 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/pixfill.h $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/08/16 15:36:39 $ +*/ + +#ifndef __PIXFILL_H +#define __PIXFILL_H + +#include "grpix.h" +#define grd_pixel_fill(c, parm, x, y) gr_fill_upixel(c, x, y) + +#endif /* __PIXFILL_H */ diff --git a/engine/src/Libraries/2D/Source/pixtab.h b/engine/src/Libraries/2D/Source/pixtab.h new file mode 100644 index 0000000..90a3041 --- /dev/null +++ b/engine/src/Libraries/2D/Source/pixtab.h @@ -0,0 +1,44 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/pixtab.h $ + * $Revision: 1.1 $ + * $Author: lmfeeney $ + * $Date: 1994/06/11 00:55:52 $ +*/ + +#ifndef __PIXFILL_H +#define __PIXFILL_H + +#include "pixfill.h" + +/* these tables are used in the 2d, but are not visible, except though the + current fill ptr (see pixfill.h) +*/ + +// MLA (this won't compile?) extern grt_pixel_fill grd_pixel_fill_table[]; + +/* fill type functions living in the table -- wrappers around gr_set_upixel */ + +extern void gri_pixel_ns (long color, long fill_parm, int x, int y); +extern void gri_pixel_clut (long color, long fill_parm, int x, int y); +extern void gri_pixel_xor (long color, long fill_parm, int x, int y); +extern void gri_pixel_blend (long color, long fill_parm, int x, int y); + +#endif diff --git a/engine/src/Libraries/2D/Source/plytyp.h b/engine/src/Libraries/2D/Source/plytyp.h new file mode 100644 index 0000000..76bb2b7 --- /dev/null +++ b/engine/src/Libraries/2D/Source/plytyp.h @@ -0,0 +1,47 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/plytyp.h $ + * $Revision: 1.2 $ + * $Author: kaboom $ + * $Date: 1993/10/19 10:24:09 $ + * + * Polygon-related structures. + * + * This file is part of the 2d library. + * + * $Log: plytyp.h $ + * Revision 1.2 1993/10/19 10:24:09 kaboom + * Includes fix.h for self-sufficency. + * + * Revision 1.1 1993/10/02 01:12:47 kaboom + * Initial revision + */ + +#ifndef __PLYTYP +#define __PLYTYP +#include "fix.h" + +/* format for vertex buffers. */ +typedef struct { + fix x, y; /* screen coordinates */ + fix u, v, w; /* texture parameters/rgb */ + fix i; /* intensity */ +} grs_vertex; +#endif /* !__PLYTYP */ diff --git a/engine/src/Libraries/2D/Source/poly.h b/engine/src/Libraries/2D/Source/poly.h new file mode 100644 index 0000000..5666e29 --- /dev/null +++ b/engine/src/Libraries/2D/Source/poly.h @@ -0,0 +1,55 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/poly.h $ + * $Revision: 1.5 $ + * $Author: kevin $ + * $Date: 1994/08/16 15:37:24 $ + * + * Constants for polygon scanning. + * + * This file is part of the 2d library. + * + * $Log: poly.h $ + * Revision 1.5 1994/08/16 15:37:24 kevin + * Added tluc8 loop type. + * + * Revision 1.4 1994/07/21 16:35:36 kevin + * Added GRL_SOLID loop flag. + * + * Revision 1.3 1994/07/18 17:07:53 kevin + * Added clut loop flag. + * + * Revision 1.2 1993/10/02 01:03:14 kaboom + * Added inner loop id constants. + * + * Revision 1.1 1993/08/10 19:18:19 kaboom + * Initial revision + */ + +#ifndef __PLYCON +#define __PLYCON +#define GRD_POLY_VERTS 128 /* maximum polygon vertices */ +#define GRL_OPAQUE 0 +#define GRL_TRANS 1 +#define GRL_LOG2 2 +#define GRL_CLUT 4 +#define GRL_SOLID 8 +#define GRL_TLUC8 16 +#endif /* !__PLYCON */ diff --git a/engine/src/Libraries/2D/Source/polyint.h b/engine/src/Libraries/2D/Source/polyint.h new file mode 100644 index 0000000..0a5e03b --- /dev/null +++ b/engine/src/Libraries/2D/Source/polyint.h @@ -0,0 +1,315 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/polyint.h $ + * $Revision: 1.8 $ + * $Author: kevin $ + * $Date: 1994/07/18 17:08:25 $ + * + * Polygon scanning internal macros. + * +*/ + +#ifndef __POLYINT_H +#define __POLYINT_H + +#include "fix.h" +#include "grs.h" +#include "plytyp.h" + +#define poly_find_y_extrema(_y_min,_y_max,_p_left,_vpl,_n) \ +do { \ + grs_vertex **pvp; \ + int __y; \ + _y_min = fix_cint(_vpl[0]->y); \ + _y_max = fix_cint(_vpl[0]->y); \ + _p_left = _vpl; \ + for (pvp=_vpl+1; pvp<_vpl+_n; ++pvp) { \ + __y=fix_cint((*pvp)->y); \ + if (__y < _y_min) { \ + _y_min = __y; \ + _p_left = pvp; \ + } \ + if (__y > _y_max) \ + _y_max = __y; \ + } \ + if (_y_min == _y_max) return; \ +} while(0) +/* +#define poly_find_yw_extrema(_y_min,_y_max,_w_min,_w_max,_p_left,_vpl,_n) \ +do { \ + grs_vertex **pvp; \ + int y; \ + _p_left = _vpl; \ + _y_min = fix_cint(_vpl[0]->y); \ + _y_max = fix_cint(_vpl[0]->y); \ + _w_min = _vpl[0]->w; \ + _w_max = _vpl[0]->w; \ + for (pvp=_vpl+1; pvp<_vpl+_n; ++pvp) { \ + y=fix_cint((*pvp)->y); \ + if (y < _y_min) { \ + _y_min = y; \ + _w_min = (*pvp)->w; \ + _p_left = pvp; \ + } \ + if (y > _y_max) { \ + _y_max = y; \ + _w_max = (*pvp)->w; \ + } \ + } \ + if (_y_min == _y_max) return; \ +} while(0) +*/ +#define poly_find_x_extrema(_x_min,_x_max,_p_top,_vpl,_n) \ +do { \ + grs_vertex **pvp; \ + int __x; \ + _x_min = fix_cint(_vpl[0]->x); \ + _x_max = fix_cint(_vpl[0]->x); \ + _p_top = _vpl; \ + for (pvp=_vpl+1; pvp<_vpl+_n; ++pvp) { \ + __x=fix_cint((*pvp)->x); \ + if (__x < _x_min) { \ + _x_min = __x; \ + _p_top = pvp; \ + } \ + if (__x > _x_max) \ + _x_max = __x; \ + } \ + if (_x_min == _x_max) return; \ +} while(0) + +#define poly_find_xw_extrema(_x_min,_x_max,_w_min,_w_max,_p_top,_vpl,_n) \ +do { \ + grs_vertex **pvp; \ + int __x; \ + _x_min = fix_cint(_vpl[0]->x); \ + _x_max = fix_cint(_vpl[0]->x); \ + _w_min = _vpl[0]->w; \ + _w_max = _vpl[0]->w; \ + _p_top = _vpl; \ + for (pvp=_vpl+1; pvp<_vpl+_n; ++pvp) { \ + __x=fix_cint((*pvp)->x); \ + if (__x < _x_min) { \ + _x_min = __x; \ + _w_min = (*pvp)->w; \ + _p_top = pvp; \ + } \ + if (__x > _x_max) { \ + _w_max = (*pvp)->w; \ + _x_max = __x; \ + } \ + } \ + if (_x_min == _x_max) return; \ +} while(0) + +#define poly_find_x_extrema_retval(_x_min,_x_max,_p_top,_vpl,_n,_retval) \ +do { \ + grs_vertex **pvp; \ + int __x; \ + _x_min = fix_cint(_vpl[0]->x); \ + _x_max = fix_cint(_vpl[0]->x); \ + _p_top = _vpl; \ + for (pvp=_vpl+1; pvp<_vpl+_n; ++pvp) { \ + __x=fix_cint((*pvp)->x); \ + if (__x < _x_min) { \ + _x_min = __x; \ + _p_top = pvp; \ + } \ + if (__x > _x_max) \ + _x_max = __x; \ + } \ + if (_x_min == _x_max) return _retval; \ +} while(0) + + +#define poly_do_left_edge(_p_left,_prev,_y_left,_y_prev,_y,_vpl,_n) \ +do { \ + _y_left = (*_p_left)->y; \ + do { \ + if (fix_cint(_y_left)==_y) _prev=*_p_left;\ + if (--_p_left < _vpl) \ + _p_left=_vpl+_n-1; \ + _y_prev=_y_left; \ + _y_left=(*_p_left)->y; \ + } while (fix_cint(_y_left)<=_y); \ +} while (0) + +#define poly_do_top_edge(_p_top,_prev,_x_top,_x_prev,_x,_vpl,_n) \ +do { \ + _x_top = (*_p_top)->x; \ + do { \ + if (fix_cint(_x_top)==_x) _prev=*_p_top;\ + if (++_p_top >= _vpl+n) \ + _p_top=_vpl; \ + _x_prev=_x_top; \ + _x_top=(*_p_top)->x; \ + } while (fix_cint(_x_top)<=_x); \ +} while (0) + +#define poly_do_right_edge(_p_right,_prev,_y_right,_y_prev,_y,_vpl,_n) \ +do { \ + _y_right = (*_p_right)->y; \ + do { \ + if (fix_cint(_y_right)==_y) _prev=*_p_right; \ + if (++_p_right >= _vpl+_n) \ + _p_right=_vpl; \ + _y_prev=_y_right; \ + _y_right=(*_p_right)->y; \ + } while (fix_cint(_y_right)<=_y); \ +} while (0) + +#define poly_do_bot_edge(_p_bot,_prev,_x_bot,_x_prev,_x,_vpl,_n) \ +do { \ + _x_bot = (*_p_bot)->x; \ + do { \ + if (fix_cint(_x_bot)==_x) _prev=*_p_bot;\ + if (--_p_bot < _vpl) \ + _p_bot=_vpl+_n-1; \ + _x_prev=_x_bot; \ + _x_bot=(*_p_bot)->x; \ + } while (fix_cint(_x_bot)<=_x); \ +} while (0) + +#define poly_do_x(p_next,_prev,y_next,_y_prev,_d,x0,dx) \ +do { \ + _d = y_next-_y_prev; \ + x0 = _prev->x; \ + dx = fix_div((*p_next)->x-x0, _d); \ + x0 += fix_mul(dx,fix_ceil(_y_prev)-_y_prev); \ +} while (0) + +#define poly_do_y_bot(p_next,_prev,x_next,_x_prev,_d,y0,dy) \ +do { \ + _d = x_next-_x_prev; \ + y0 = _prev->y; \ + dy = fix_div((*p_next)->y-y0, _d); \ + y0 += fix_mul(dy,fix_ceil(_x_prev)-_x_prev); \ + if (dy>0) y0++; \ +} while (0) + +#define poly_do_y_top(p_next,_prev,x_next,_x_prev,_d,y0,dy) \ +do { \ + _d = x_next-_x_prev; \ + y0 = _prev->y; \ + dy = fix_div((*p_next)->y-y0, _d); \ + y0 += fix_mul(dy,fix_ceil(_x_prev)-_x_prev); \ + if (dy>0) y0++; \ +} while (0) + +#define poly_do_uv(p_next,_prev,_d,u0,du,v0,dv) \ +do { \ + u0 = (_prev->u); \ + du = fix_div((*p_next)->u-u0, _d); \ + u0 += fix_mul(du,fix_ceil(_prev->y)-_prev->y);\ + v0 = (_prev->v); \ + dv = fix_div((*p_next)->v-v0, _d); \ + v0 += fix_mul(dv,fix_ceil(_prev->y)-_prev->y);\ +} while (0) + +#define poly_do_rgb(p_next,_prev,_d,r0,dr,g0,dg,b0,db) \ +do { \ + r0 = (_prev->u); \ + dr = fix_div((*p_next)->u-r0, _d); \ + r0 += fix_mul(dr,fix_ceil(_prev->y)-_prev->y);\ + g0 = (_prev->v); \ + dg = fix_div((*p_next)->v-g0, _d); \ + g0 += fix_mul(dg,fix_ceil(_prev->y)-_prev->y);\ + b0 = (_prev->w); \ + db = fix_div((*p_next)->w-b0, _d); \ + b0 += fix_mul(db,fix_ceil(_prev->y)-_prev->y);\ +} while (0) + +#define poly_do_uvw(p_next,_prev,_d,u0,du,v0,dv,w0,dw) \ +do { \ + w0 = _prev->w; \ + dw = fix_div((*p_next)->w-w0, _d); \ + u0 = fix_mul(_prev->u,w0); \ + du = fix_div(fix_mul((*p_next)->u,(*p_next)->w) - u0, _d); \ + u0 += fix_mul(du,fix_ceil(_prev->y)-_prev->y); \ + v0 = fix_mul(_prev->v,w0); \ + dv = fix_div(fix_mul((*p_next)->v,(*p_next)->w) - v0, _d); \ + v0 += fix_mul(dv,fix_ceil(_prev->y)-_prev->y); \ +} while (0) + +#define poly_do_i(p_next,_prev,_d,i0,di) \ +do { \ + i0 = _prev->i; \ + di = fix_div((*p_next)->i-i0, _d); \ + i0 += fix_mul(di,fix_ceil(_prev->y)-_prev->y);\ +} while (0) + +#define poly_do_iw(p_next,_prev,_d,i0,di,w0) \ +do { \ + i0 = fix_mul(_prev->i,w0); \ + di = fix_div(fix_mul((*p_next)->i,(*p_next)->w) - i0, _d);\ + i0 += fix_mul(di,fix_ceil(_prev->y)-_prev->y);\ +} while (0) + +#define poly_do_uv_vscan(p_next,_prev,_d,u0,du,v0,dv) \ +do { \ + u0 = (_prev->u); \ + du = fix_div((*p_next)->u-u0, _d); \ + u0 += fix_mul(du,fix_ceil(_prev->x)-_prev->x);\ + v0 = (_prev->v); \ + dv = fix_div((*p_next)->v-v0, _d); \ + v0 += fix_mul(dv,fix_ceil(_prev->x)-_prev->x);\ +} while (0) + +#define poly_do_rgb_vscan(p_next,_prev,_d,r0,dr,g0,dg,b0,db) \ +do { \ + r0 = (_prev->u); \ + dr = fix_div((*p_next)->u-r0, _d); \ + r0 += fix_mul(dr,fix_ceil(_prev->x)-_prev->x);\ + g0 = (_prev->v); \ + dg = fix_div((*p_next)->v-g0, _d); \ + g0 += fix_mul(dg,fix_ceil(_prev->x)-_prev->x);\ + b0 = (_prev->w); \ + db = fix_div((*p_next)->w-b0, _d); \ + b0 += fix_mul(db,fix_ceil(_prev->x)-_prev->x);\ +} while (0) + +#define poly_do_uvw_vscan(p_next,_prev,_d,u0,du,v0,dv,w0,dw) \ +do { \ + w0 = _prev->w; \ + dw = fix_div((*p_next)->w-w0, _d); \ + u0 = fix_mul(_prev->u,w0); \ + du = fix_div(fix_mul((*p_next)->u,(*p_next)->w) - u0, _d); \ + u0 += fix_mul(du,fix_ceil(_prev->x)-_prev->x); \ + v0 = fix_mul(_prev->v,w0); \ + dv = fix_div(fix_mul((*p_next)->v,(*p_next)->w) - v0, _d); \ + v0 += fix_mul(dv,fix_ceil(_prev->x)-_prev->x); \ +} while (0) + +#define poly_do_i_vscan(p_next,_prev,_d,i0,di) \ +do { \ + i0 = _prev->i; \ + di = fix_div((*p_next)->i-i0, _d); \ + i0 += fix_mul(di,fix_ceil(_prev->x)-_prev->x);\ +} while (0) + +#define poly_do_iw_vscan(p_next,_prev,_d,i0,di,w0) \ +do { \ + i0 = fix_mul(_prev->i,w0); \ + di = fix_div(fix_mul((*p_next)->i,(*p_next)->w) - i0, _d);\ + i0 += fix_mul(di,fix_ceil(_prev->x)-_prev->x);\ +} while (0) +#endif /* !__POLYINT_H */ + + diff --git a/engine/src/Libraries/2D/Source/rgb.c b/engine/src/Libraries/2D/Source/rgb.c new file mode 100644 index 0000000..c250edc --- /dev/null +++ b/engine/src/Libraries/2D/Source/rgb.c @@ -0,0 +1,440 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/rgb.c $ + * $Revision: 1.5 $ + * $Author: kaboom $ + * $Date: 1994/07/15 20:15:43 $ + * + * RGB color manipulation routines. + * + * This file is part of the 2d library. + */ + +#include "grs.h" +#include "grmalloc.h" +#include "rgb.h" +#include "scrdat.h" + +// prototypes +static int _redloop(); + +/* Static Globals for his and her pleasure */ +static int bcenter, gcenter, rcenter; +static long gdist, rdist, cdist; +static long cbinc, cginc, crinc; +static ulong *gdp, *rdp, *cdp; +static uchar *grgbp, *rrgbp, *crgbp; +static int gstride, rstride; +static long x, xsqr, colormax; +static int cindex; + +static void inv_cmap_2(int colors, uchar *colormap[3],int bits,ulong *dist_buf, uchar *rgbmap); +int redloop(); +static int _greenloop(int restart); +static int _blueloop(int restart); +static void _maxfill(ulong *buffer); + +/* The routines in this file operate on grs_rgb color values. The color + values are encoded so that each r,g, and b has 8 bits of integer, 2 + bits of fraction, and one bit of padding. */ +/* split a grs_rgb into its component 8-bit r, g, and b values. */ +void gr_split_rgb (grs_rgb c, uchar *r, uchar *g, uchar *b) +{ + *r = (c>>2)&0xff; + *g = (c>>13)&0xff; + *b = (c>>24)&0xff; +} + +/* This routine allocates a 15 bit inverse palette for the + current screen palette (that 32768 bytes) and initializes + it for the current palette. Returns OUT_OF_MEMORY + if it can't allocate anything. If ipal is currently non-null + it tries to delete it first, in the interests of robustness */ + +int gr_alloc_ipal(void) +{ + int err; + + if (grd_ipal != NULL) { + if ((err = gr_free_ipal())<0) return err; + } + + if ((grd_ipal = (uchar *) malloc(32768))==NULL) return RGB_OUT_OF_MEMORY; // was gr_malloc + + if ((err = gr_init_ipal())<0) return err; + return RGB_OK; +} + +int gr_free_ipal(void) +{ + if (grd_ipal==NULL) return RGB_CANT_DEALLOCATE; + free( grd_ipal); // was gr_free + grd_ipal=NULL; + return RGB_OK; +} + +/* Initializes the inverse palette to the current screen + palette. */ + +int gr_init_ipal(void) +{ + int i; + uchar r,g,b; + uchar *data,*colormap[3]; + ulong *dist_buf; + + if (grd_ipal == NULL) return RGB_IPAL_NOT_ALLOCATED; + + if ((data = (uchar *) malloc(3*256)) == NULL) return RGB_OUT_OF_MEMORY; // was gr_malloc + /* needs to be split up into r,g,b planes */ + colormap[0] = data; + colormap[1] = data + 256; + colormap[2] = data + 2*256; + + for(i=0;i<256;++i) { + gr_split_rgb(grd_bpal[i],&r,&g,&b); + colormap[2][i] = r; + colormap[1][i] = g; + colormap[0][i] = b; + } + + if ((dist_buf = (ulong *) malloc(sizeof(ulong) * 32768))==NULL) return RGB_OUT_OF_MEMORY; // was gr_malloc + + inv_cmap_2(256,colormap,5,dist_buf,grd_ipal); + + free( dist_buf); // was gr_free + free( data); // was gr_free + + return RGB_OK; +} + + +static void inv_cmap_2(int colors, uchar *colormap[3],int bits,ulong *dist_buf, uchar *rgbmap ) +{ + int nbits = 8 - bits; + + colormax = 1 << bits; + x = 1 << nbits; + xsqr = 1 << (2 * nbits); + + /* Compute strides for accessing the arrays. */ + + gstride = colormax; + rstride = colormax * colormax ; + + _maxfill(dist_buf); + + for(cindex = 0;cindex> nbits; + gcenter = colormap[1][cindex] >> nbits; + bcenter = colormap[2][cindex] >> nbits; + + rdist = colormap[0][cindex] - (rcenter * x + x/2); + gdist = colormap[1][cindex] - (gcenter * x + x/2); + cdist = colormap[2][cindex] - (bcenter * x + x/2); + cdist = rdist*rdist + gdist*gdist + cdist*cdist; + + crinc = 2 * ((rcenter+1) * xsqr - (colormap[0][cindex] * x)); + cginc = 2 * ((gcenter+1) * xsqr - (colormap[1][cindex] * x)); + cbinc = 2 * ((bcenter+1) * xsqr - (colormap[2][cindex] * x)); + + /* Array starting points. */ + cdp = dist_buf + rcenter*rstride + gcenter*gstride + bcenter; + crgbp = rgbmap + rcenter*rstride + gcenter*gstride + bcenter; + + _redloop(); + } +} + +/* redloop -- loop up and down from red center. */ +static int _redloop() +{ + int detect, r, first; + long txsqr = xsqr + xsqr; + static long rxx; + + detect = 0; + + /* Basic loop up */ + for (r = rcenter, rdist = cdist, rxx = crinc, + rdp = cdp, rrgbp = crgbp, first = 1; + r= 0; + r--, rdp -= rstride, rrgbp -= rstride, + rxx -= txsqr, rdist -= rxx, first = 0) { + if (_greenloop(first)) + detect = 1; + else if (detect) + break; + } + + return detect; +} + + +/* greenloop -- loop up and down from green center. */ +static int _greenloop(int restart) +{ + int detect, g, first; + long txsqr = xsqr + xsqr; + static int here, min, max; + static int prevmax, prevmin; + int thismax, thismin; + static long ginc, gxx, gcdist; + static ulong *gcdp; + static uchar *gcrgbp; + + /* Red loop restarted, reset variables to "center" position */ + if (restart) { + here = gcenter; + min = 0; + max = colormax - 1; + ginc = cginc; + prevmax = 0; + prevmin = colormax; + } + + /* finding actual min and max on this line. */ + thismin = min; + thismax = max; + detect = 0; + + /* Basic loop up. */ + for (g=here, gcdist = gdist = rdist, gxx = ginc, + gcdp = gdp = rdp, gcrgbp = grgbp = rrgbp, first = 1; + g <= max; + g++, gdp += gstride, gcdp += gstride, + grgbp += gstride, gcrgbp += gstride, + gdist += gxx, gcdist += gxx, gxx += txsqr, first = 0) { + if (_blueloop(first)) { + if (!detect) { + /* remember here and associated data! */ + if (g>here) { + here = g; + rdp = gcdp; + rrgbp = gcrgbp; + rdist = gcdist; + ginc = gxx; + thismin = here; + } + detect = 1; + } + } + else if (detect) { + thismax = g - 1; + break; + } + } + + /* Basic loop down */ + for (g=here - 1, gxx = ginc - txsqr, gcdist = gdist = rdist - gxx, + gcdp = gdp = rdp - gstride, gcrgbp = grgbp = rrgbp - gstride, + first = 1; + g >= min; + g--, gdp -= gstride, gcdp -= gstride, + grgbp -= gstride, gcrgbp -= gstride, + gxx -= txsqr, gdist -= gxx, gcdist -= gxx, first = 0) { + if (_blueloop(first)) { + if (!detect) { + /* remember here! */ + here = g; + rdp = gcdp; + rrgbp = gcrgbp; + rdist = gcdist; + ginc = gxx; + thismax = here; + detect = 1; + } + } + else if (detect) { + thismin = g + 1; + break; + } + } + + /* If we saw something, update the edge tracers. Only + * tracks edges that are "shrinking" (min increating, max + * decreasing. + */ + + if (detect) { + if (thismax < prevmax) + max = thismax; + prevmax = thismax; + + if (thismin > prevmin ) + min = thismin; + prevmin = thismin; + } + + return detect; +} + +/* blueloop -- loop up and down from blue center. */ +static int _blueloop(int restart) +{ + int detect; + /* These are all registers on a Sun 3. Your mileage may differ. */ + ulong *dp; + uchar *rgbp; + long bdist, bxx; + int b, i=cindex; + long txsqr = xsqr + xsqr; + int lim; /* for min and max, avoid extra registers. */ + static int here, min, max; + static int prevmin, prevmax; /* For tracking min and max. */ + int thismin, thismax; + static long binc; + + if (restart) { + here = bcenter; + min = 0; + max = colormax - 1; + binc = cbinc; + prevmin = colormax; + prevmax = 0; + } + + detect = 0; + thismin = min; + thismax = max; + + /* Basic loop up. */ + /* First loop just finds first applicable cell. */ + for (b = here, bdist = gdist, bxx = binc, dp = gdp, rgbp = grgbp, + lim = max; + b <= lim; + b++, dp++, rgbp++, bdist += bxx, bxx += txsqr) { + if (*dp > bdist) { + /* Remember new here and associated data! */ + if (b>here) { + here = b; + gdp = dp; + grgbp = rgbp; + gdist = bdist; + binc = bxx; + thismin = here; + } + detect = 1; + break; + } + } + /* Second loop fills in a run of closer cells. */ + for (; + b <= lim; + b++, dp++, rgbp++, bdist += bxx, bxx += txsqr) { + if (*dp > bdist ) { + *dp = bdist; + *rgbp = i; + } else { + thismax = b - 1; + break; + } + } + + /* Basic loop down */ + /* Do initializations here, since the 'find' loop might not get + * executed. + */ + lim = min; + b = here - 1; + bxx = binc - txsqr; + bdist = gdist - bxx; + dp = gdp - 1; + rgbp = grgbp - 1; + /* The 'find' loop ios executed on ly if we didn't already find + * something. + */ + if (!detect) + for(; + b >= lim; + b--, dp--, rgbp--, bxx -= txsqr, bdist -= bxx) { + if ( *dp > bdist) { + /* Remember here! */ + /* No test for b against here necessary because b < + * here by definition. + */ + here = b; + gdp = dp; + grgbp = rgbp; + gdist = bdist; + binc = bxx; + thismax = here; + detect = 1; + break; + } + } + /* the 'update' loop */ + for (; + b>= lim; + b--, dp--, rgbp--, bxx -= txsqr, bdist -= bxx) { + if ( *dp > bdist) { + *dp = bdist; + *rgbp = i; + } else { + thismin = b + 1; + break; + } + } + + /* If we saw something, update the edge trackers. */ + if (detect) { + /* Only tracks edges that are 'shrinking' (*min increasin, max + * decreasing). + */ + if (thismax < prevmax) + max = thismax; + if (thismin > prevmin ) + min = thismin; + + /* Remember the min and max values. */ + prevmax = thismax; + prevmin = thismin; + } + + return detect; +} + +/* Fill a buffer with the largest unsigned long. */ +static void _maxfill(ulong *buffer) +{ + ulong maxv = (long)-1; + long i; + ulong *bp; + + for(i=colormax * colormax * colormax, bp = buffer; + i > 0; + i--, bp++ ) + *bp = maxv; +} diff --git a/engine/src/Libraries/2D/Source/rgb.h b/engine/src/Libraries/2D/Source/rgb.h new file mode 100644 index 0000000..bb41da3 --- /dev/null +++ b/engine/src/Libraries/2D/Source/rgb.h @@ -0,0 +1,81 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/rgb.h $ + * $Revision: 1.4 $ + * $Author: kaboom $ + * $Date: 1994/07/21 08:25:17 $ + * + * Prototypes and macros for rgb manipulation routines. + * + * This file is part of the 2d library. + * + * $Log: rgb.h $ + * Revision 1.4 1994/07/21 08:25:17 kaboom + * Added gr_index_lrgb() macro. + * + * Revision 1.3 1993/10/19 10:31:44 kaboom + * Changed gr_index_brgb to calculate an index from a bound rgb value + * and gr_index_rgb to calculate an index from 3 fixed rgb values. + * + * Revision 1.2 1993/10/15 14:42:56 baf + * Removed offending semicolon from end of macro + * + * Revision 1.1 1993/02/04 17:44:03 kaboom + * Initial revision + * + */ + +#ifndef __RGB_H +#define __RGB_H + +#include "grs.h" + +#define RGB_OK (0) +#define RGB_OUT_OF_MEMORY (-1) +#define RGB_CANT_DEALLOCATE (-2) +#define RGB_IPAL_NOT_ALLOCATED (-3) + +/* convert fixed-point (r,g,b) triplet into a 15-bit ipal index. */ +#define gr_index_rgb(r,g,b) \ + (((r)>>19)&0x1f) | (((g)>>14)&0x3e0) | (((b)>>9)&0x7c00) + +/* convert an 8-8-8 long rgb into a 15-bit ipal index. */ +#define gr_index_lrgb(t) \ + ((((t)>>3)&0x1f) | (((t)>>6)&0x3e0) | (((t)>>9)&0x7c00)) + +/* convert 8-bit r,g,b into grs_rgb format. */ +#define gr_bind_rgb(r,g,b) (((r)<<2)|((g)<<13)|((b)<<24)) + +/* convert a grs_rgb value into a 15-bit inverse palette table index. */ +#define gr_index_brgb(c) \ + ((((c)>>5)&0x1f)|(((c)>>11)&0x3e0)|(((c)>>17)&0x7c00)) + +extern void gr_split_rgb (grs_rgb c, uchar *r, uchar *g, uchar *b); + +/* Generate an inverse palette for the given screen palette */ +int gr_alloc_ipal(void); + +/* Reinitialize the ipal for the current palette */ +int gr_init_ipal(void); + +/* Destroy the current inverse palette, freeing memory */ +int gr_free_ipal(void); + +#endif /* !__RGB_H */ diff --git a/engine/src/Libraries/2D/Source/scrdat.h b/engine/src/Libraries/2D/Source/scrdat.h new file mode 100644 index 0000000..9188d8a --- /dev/null +++ b/engine/src/Libraries/2D/Source/scrdat.h @@ -0,0 +1,49 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/scrdat.h $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/10/19 17:51:25 $ + * + * Declarations for current screen and related globals. + * + * This file is part of the 2d library. + * + * $Log: scrdat.h $ + * Revision 1.2 1994/10/19 17:51:25 kevin + * Make grd_bpal, grd_pal, grd_ipal into globals. + * + * Revision 1.1 1993/10/19 10:24:38 kaboom + * Initial revision + * + */ + +#ifndef __SCRDAT_H +#define __SCRDAT_H +#include "grs.h" + +extern grs_screen *grd_screen; +extern uchar grd_default_pal[]; +extern uchar *grd_pal; +extern grs_rgb grd_default_bpal[]; +extern grs_rgb *grd_bpal; +extern uchar *grd_ipal; + +#endif /* !__SCRDAT_H */ diff --git a/engine/src/Libraries/2D/Source/screen.c b/engine/src/Libraries/2D/Source/screen.c new file mode 100644 index 0000000..a81f3fc --- /dev/null +++ b/engine/src/Libraries/2D/Source/screen.c @@ -0,0 +1,101 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/screen.c $ + * $Revision: 1.5 $ + * $Author: kevin $ + * $Date: 1994/10/19 17:57:31 $ + * + * Screen handling routines. + * + * This file is part of the 2d library. + * + * $Log: screen.c $ + * Revision 1.5 1994/10/19 17:57:31 kevin + * No pal, bpal, and ipal are no longer used in the screen structure. + * + * Revision 1.4 1993/10/08 01:16:21 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.3 1993/05/25 18:53:14 kaboom + * Fixed bug in gr_free_screen---was erroneously freeing bpal. + * + * Revision 1.2 1993/04/29 19:08:03 kaboom + * Cleaned up memory allocation in gr_alloc_screen. + * + * Revision 1.1 1993/02/04 17:44:20 kaboom + * Initial revision + */ + +#include "grs.h" +#include "grd.h" +#include "bitmap.h" +#include "canvas.h" +#include "valloc.h" + +/* allocate enoguh video memory for a screen of the specified size. then set + up the screen structure describing this screen and the 2 system canvases + for drawing on it. return a pointer to the new screen structure. */ +grs_screen *gr_alloc_screen(short w, short h) { + grs_screen *s = 0L; + grs_canvas *c; + uchar *p; + uchar *b; + + /* get memory for screen structure itself and 2 system canvases, and video ram for the screen itself. */ + // was gr_malloc + if ((p = (uchar *)malloc(sizeof(*s) + 2 * sizeof(*c))) == NULL) { + return NULL; + } + if ((b = our_valloc(w, h)) == (uchar *)-1) { + free(s); + return NULL; + } + + /* set up bitmap. */ + s = (grs_screen *)p; + c = (grs_canvas *)(p + sizeof(*s)); + gr_init_bm(&s->bm, b, BMT_DEVICE, 0, w, h); + + /* start with upper left visible. */ + s->x = 0; + s->y = 0; + + /* set up global canvases. */ + s->c = c; + gr_init_canvas(c, s->bm.bits, BMT_DEVICE, w, h); + gr_init_canvas(c + 1, s->bm.bits, BMT_DEVICE, grd_cap->w, grd_cap->h); + s->pal = NULL; + s->ipal = NULL; + s->ltab = NULL; + s->clut = NULL; + + return s; +} + +/* free memory for screen and its related data structures. */ +void gr_free_screen(grs_screen *s) { + vfree(s->bm.bits); + if (s->c->ytab) + free(s->c->ytab); // was gr_free + if ((s->c + 1)->ytab) + free((s->c + 1)->ytab); // was gr_free + free(s->c); // was gr_free + free(s); // was gr_free +} diff --git a/engine/src/Libraries/2D/Source/screen.h b/engine/src/Libraries/2D/Source/screen.h new file mode 100644 index 0000000..4b8edfa --- /dev/null +++ b/engine/src/Libraries/2D/Source/screen.h @@ -0,0 +1,42 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/screen.h $ + * $Revision: 1.2 $ + * $Author: kaboom $ + * $Date: 1993/05/03 13:56:43 $ + * + * Screen handling prototypes. + * + * This file is part of the 2d library. + * + * $Log: screen.h $ + * Revision 1.2 1993/05/03 13:56:43 kaboom + * Added prototype for gr_set_screen(). + * + * Revision 1.1 1993/02/04 17:44:28 kaboom + * Initial revision + */ + +#ifndef __SCREEN_H +#define __SCREEN_H +extern grs_screen *gr_alloc_screen (short w, short h); +extern void gr_free_screen (grs_screen *s); +extern void gr_set_screen (grs_screen *s); +#endif /* !__SCREEN_H */ diff --git a/engine/src/Libraries/2D/Source/scrmac.h b/engine/src/Libraries/2D/Source/scrmac.h new file mode 100644 index 0000000..d91621a --- /dev/null +++ b/engine/src/Libraries/2D/Source/scrmac.h @@ -0,0 +1,46 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/scrmac.h $ + * $Revision: 1.1 $ + * $Author: kaboom $ + * $Date: 1993/10/19 10:24:50 $ + * + * + * + * This file is part of the 2d library. + * + * $Log: scrmac.h $ + * Revision 1.1 1993/10/19 10:24:50 kaboom + * Initial revision + * + */ + +#ifndef __SCRMAC_H +#define __SCRMAC_H +#include "scrdat.h" + +/* macros for getting & setting elements of the current screen. */ +#define gr_get_light_tab() (grd_screen->ltab) +#define gr_set_light_tab(p) (grd_screen->ltab=(p)) + +#define gr_get_clut() (grd_screen->clut) +#define gr_set_clut(cl) (grd_screen->clut=(cl)) +#endif /* !__SCRMAC_H */ + diff --git a/engine/src/Libraries/2D/Source/sscrn.c b/engine/src/Libraries/2D/Source/sscrn.c new file mode 100644 index 0000000..76fd3ea --- /dev/null +++ b/engine/src/Libraries/2D/Source/sscrn.c @@ -0,0 +1,47 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/sscrn.c $ + * $Revision: 1.1 $ + * $Author: kaboom $ + * $Date: 1993/11/03 12:48:41 $ + * + * Routine to set active screen. + * + * This file is part of the 2d library. + * + * $Log: sscrn.c $ + * Revision 1.1 1993/11/03 12:48:41 kaboom + * Initial revision + * + */ + +#include "grs.h" +#include "canvas.h" +#include "cnvdat.h" +#include "scrdat.h" + +/* set current screen to s. also default canvas to full screen. */ +void gr_set_screen(grs_screen *s) +{ + grd_screen=s; + grd_screen_canvas=s->c; + grd_visible_canvas=s->c+1; + gr_set_canvas(grd_screen_canvas); +} diff --git a/engine/src/Libraries/2D/Source/state.h b/engine/src/Libraries/2D/Source/state.h new file mode 100644 index 0000000..eb403da --- /dev/null +++ b/engine/src/Libraries/2D/Source/state.h @@ -0,0 +1,58 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/state.h $ + * $Revision: 1.3 $ + * $Author: kevin $ + * $Date: 1994/10/18 13:22:39 $ + * + * Declarations for video state push/pop. + * + * This file is part of the 2d library. + * + * $Log: state.h $ + * Revision 1.3 1994/10/18 13:22:39 kevin + * renamed gr_{push,pop}_state to gr_{push,pop}_video_state. + * + * Revision 1.2 1994/10/13 10:36:10 ept + * Added several constants to match the inc file and also + * made gr_push_state return an int for error checking + * purposes. + * + * Revision 1.1 1993/05/16 00:44:01 kaboom + * Initial revision + * + */ + +#ifndef STATE_H +#define STATE_H + +#define GRD_STATE_GRAPHICS_OURS 0 // in house graphics mode +#define GRD_STATE_GRAPHICS_VGA 1 // VGA or VESA graphics moed +#define GRD_STATE_BIOS_TEXT 2 // VGA text mode +#define GRD_STATE_VESA_TEXT 3 // VESA text mode + +#define GRD_STATE_DEF 0 +#define GRD_STATE_PAL 1 + +int gr_push_video_state (int flags); +void gr_pop_video_state (int clear); + +#endif + diff --git a/engine/src/Libraries/2D/Source/status_2D.h b/engine/src/Libraries/2D/Source/status_2D.h new file mode 100644 index 0000000..d529278 --- /dev/null +++ b/engine/src/Libraries/2D/Source/status_2D.h @@ -0,0 +1,35 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * DG: 2018-05-12 renamed this to status_2D.h to avoid confusion with GameSrc/Headers/status.h + * + * $Source: n:/project/lib/src/2d/RCS/status.h $ + * $Revision: 1.1 $ + * $Author: kaboom $ + * $Date: 1993/07/12 23:32:11 $ + * + * Declaration for staus flags and globals. + * + * $Log: status.h $ + * Revision 1.1 1993/07/12 23:32:11 kaboom + * Initial revision + * + */ + +extern int grd_active; diff --git a/engine/src/Libraries/2D/Source/string/chr.h b/engine/src/Libraries/2D/Source/string/chr.h new file mode 100644 index 0000000..588098e --- /dev/null +++ b/engine/src/Libraries/2D/Source/string/chr.h @@ -0,0 +1,42 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/chr.h $ + * $Revision: 1.1 $ + * $Author: kaboom $ + * $Date: 1993/06/02 16:33:01 $ + * + * Constants for special character codes. + * + * This file is part of the 2d library. + * + * $Log: chr.h $ + * Revision 1.1 1993/06/02 16:33:01 kaboom + * Initial revision + * + */ + +/* soft characters for line wrapped text. */ +#ifndef CHAR_SOFTCR +#define CHAR_SOFTCR 1 +#endif +#ifndef CHAR_SOFTSP +#define CHAR_SOFTSP 2 +#endif diff --git a/engine/src/Libraries/2D/Source/string/chrsiz.c b/engine/src/Libraries/2D/Source/string/chrsiz.c new file mode 100644 index 0000000..25040bc --- /dev/null +++ b/engine/src/Libraries/2D/Source/string/chrsiz.c @@ -0,0 +1,81 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/chrsiz.c $ + * $Revision: 1.5 $ + * $Author: lmfeeney $ + * $Date: 1994/06/15 01:19:53 $ + * + * Character width and height calculator. + * + * This file is part of the 2d library. + * + * $Log: chrsiz.c $ + * Revision 1.5 1994/06/15 01:19:53 lmfeeney + * support extended ascii (c > 127) w\ uchars, don't change fn i\f + * + * Revision 1.4 1994/04/09 00:10:39 lmfeeney + * routine takes grs_font* arguement, #define in str.h for compatibility + * + * Revision 1.3 1993/10/19 02:57:50 kaboom + * Replaced #include new headers. + * + * Revision 1.2 1993/10/08 01:15:02 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.1 1993/06/02 16:17:21 kaboom + * Initial revision + */ + +#include "grs.h" + +/** + * Returns the width in pixels of character in the specified font + * @param font font + * @param c character + * @param width returned width + * @param height returned height + */ +void gr_font_char_size(grs_font *font, char c, short *width, short *height) { + short *off_tab; /* character offset table */ + short offset; /* offset of current character */ + + if ((uchar)c < font->min || (uchar)c > font->max) + return; + off_tab = font->off_tab; + offset = off_tab[(uchar)c - font->min]; + if ((uchar)c < font->min || (uchar)c > font->max) + *width = 0; + else + *width = off_tab[(uchar)c - font->min + 1] - offset; + *height = font->h; +} + +/** + * Returns the width of character in pixels for the specified font + * @param font font + * @param c character + * @return width of character + */ +short gr_font_char_width(grs_font *font, char c) { + short width = 0, height = 0; + gr_font_char_size(font, c, &width, &height); + return width; +} diff --git a/engine/src/Libraries/2D/Source/string/genchr.c b/engine/src/Libraries/2D/Source/string/genchr.c new file mode 100644 index 0000000..75b4b7d --- /dev/null +++ b/engine/src/Libraries/2D/Source/string/genchr.c @@ -0,0 +1,81 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genchr.c $ + * $Revision: 1.6 $ + * $Author: lmfeeney $ + * $Date: 1994/06/15 01:20:11 $ + * + * Generic clipped character drawer. + * + * This file is part of the 2d library. + * + * $Log: genchr.c $ + * Revision 1.6 1994/06/15 01:20:11 lmfeeney + * support extended ascii (c > 127) w\ uchars, don't change fn i\f + * + * Revision 1.5 1994/04/09 07:20:12 lmfeeney + * routine takes grs_font * as first arg, #define for compatibility in + * str.h + * + * Revision 1.4 1993/10/19 09:57:52 kaboom + * Replaced #include new headers. + * + * Revision 1.3 1993/10/02 01:17:16 kaboom + * Changed include of clip.h to include of clpcon.h and/or clpfcn.h. + * + * Revision 1.2 1993/04/29 18:40:17 kaboom + * Changed include of gr.h to smaller more specific grxxx.h. + * + * Revision 1.1 1993/04/08 18:54:36 kaboom + * Initial revision + */ + +#include "bitmap.h" +#include "clpcon.h" +#include "grdbm.h" + +/* draw a clipped character c from the specified font at (x,y). */ + +int gen_font_char(grs_font *f, char c, short x, short y) { + grs_bitmap bm; /* character bitmap */ + short *off_tab; /* character offset table */ + uchar *data_buf; /* pixel data buffer */ + short offset; /* current character offset */ + + /* range check char, get font table pointers, offset. */ + if ((uchar)c > f->max || (uchar)c < f->min) + return CLIP_ALL; + data_buf = (uchar *)f + f->buf; + off_tab = f->off_tab; + offset = off_tab[(uchar)c - f->min]; + gr_init_bm(&bm, NULL, (f->id == 0xcccc) ? BMT_FLAT8 : BMT_MONO, + BMF_TRANS, off_tab[(uchar)c - f->min + 1] - offset, f->h); + bm.row = f->w; + /* draw the character with clipping. */ + if (bm.type == BMT_MONO) { + bm.bits = data_buf + (offset >> 3); + bm.align = offset & 7; + return gr_mono_bitmap(&bm, x, y); + } else { + bm.bits = data_buf + offset; + return gr_flat8_bitmap(&bm, x, y); + } +} diff --git a/engine/src/Libraries/2D/Source/string/genstr.c b/engine/src/Libraries/2D/Source/string/genstr.c new file mode 100644 index 0000000..ef491c9 --- /dev/null +++ b/engine/src/Libraries/2D/Source/string/genstr.c @@ -0,0 +1,185 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genstr.c $ + * $Revision: 1.7 $ + * $Author: lmfeeney $ + * $Date: 1994/06/15 01:18:46 $ + * + * Generic clipped string drawer. + * + * This file is part of the 2d library. + * + * $Log: genstr.c $ + * Revision 1.7 1994/06/15 01:18:46 lmfeeney + * support extended ascii (c > 127) w\ uchars, don't change fn i\f + * + * Revision 1.6 1994/04/09 07:23:14 lmfeeney + * mostly rewritten for improved efficiency, doesn't clip + * each character bitmap + * + * Revision 1.5 1993/10/19 09:57:54 kaboom + * Replaced #include new headers. + * + * Revision 1.4 1993/10/02 01:17:35 kaboom + * Changed include of clip.h to include of clpcon.h and/or clpfcn.h. + * + * Revision 1.3 1993/06/02 16:19:44 kaboom + * Now handles strings with hard and soft carriage returns and soft + * spaces, from line wrapping. + * + * Revision 1.2 1993/04/29 18:40:53 kaboom + * Changed include of gr.h to smaller more specific grxxx.h. + * + * Revision 1.1 1993/04/08 16:26:02 kaboom + * Initial revision + */ + +#include "bitmap.h" +#include "chr.h" +#include "clpcon.h" +#include "cnvdat.h" +#include "grdbm.h" + +int gen_font_string(grs_font *f, char *s, short x0, short y0) { + grs_bitmap bm; /* character bitmap */ + short *offset_tab; /* table of character offsets */ + uchar *char_buf; /* font pixel data */ + short offset; /* offset of current character */ + short x, y; /* position of current character */ + uchar c; /* current character */ + short yok; /* current line in t/b clip */ + + if (x0 > grd_clip.right || y0 > grd_clip.bot) { + return CLIP_NONE; + } + + char_buf = (uchar *)f + f->buf; + offset_tab = f->off_tab; + gr_init_bm(&bm, NULL, (f->id == 0xcccc) ? BMT_FLAT8 : BMT_MONO, BMF_TRANS, 0, f->h); + bm.row = f->w; + + x = x0; + y = y0; + + while (1) { + + /* y in clip region */ + + if ((y + f->h >= grd_clip.top && y + f->h <= grd_clip.bot) || (y >= grd_clip.top && y <= grd_clip.bot)) { + + yok = (y >= grd_clip.top && y + f->h <= grd_clip.bot); + + /* line coming into range */ + while ((c = (uchar)(*s++)) != CHAR_SOFTCR && c != '\n') { + if (c == '\0') + return CLIP_NONE; + if (c > f->max || c < f->min || c == CHAR_SOFTSP) + continue; + offset = offset_tab[c - f->min]; + bm.w = offset_tab[c - f->min + 1] - offset; + if (x + bm.w >= grd_clip.left) + break; + x += bm.w; + } + + if (c == '\n' || c == CHAR_SOFTCR) { + x = x0; + y += f->h; + continue; + } + + /* clip boundary character */ + if (bm.type == BMT_MONO) { + bm.bits = char_buf + (offset >> 3); + bm.align = offset & 7; + gr_mono_bitmap(&bm, x, y); + } else { + bm.bits = char_buf + offset; + gr_flat8_bitmap(&bm, x, y); + } + x += bm.w; + + /* line in range */ + while ((c = *s++) != CHAR_SOFTCR && c != '\n') { + if (c == '\0') + return CLIP_NONE; + if (c > f->max || c < f->min || c == CHAR_SOFTSP) + continue; + offset = offset_tab[c - f->min]; + bm.w = offset_tab[c - f->min + 1] - offset; + if (x + bm.w > grd_clip.right) + break; + if (bm.type == BMT_MONO) { + bm.bits = char_buf + (offset >> 3); + bm.align = offset & 7; + if (yok) + gr_mono_ubitmap(&bm, x, y); + else + gr_mono_bitmap(&bm, x, y); + } else { + bm.bits = char_buf + offset; + if (yok) + gr_flat8_ubitmap(&bm, x, y); + else + gr_flat8_bitmap(&bm, x, y); + } + x += bm.w; + } + + if (c == '\n' || c == CHAR_SOFTCR) { + x = x0; + y += f->h; + continue; + } + + /* clip boundary character */ + if (bm.type == BMT_MONO) { + bm.bits = char_buf + (offset >> 3); + bm.align = offset & 7; + gr_mono_bitmap(&bm, x, y); + } else { + bm.bits = char_buf + offset; + gr_flat8_bitmap(&bm, x, y); + } + + /* end of line */ + while ((c = *s++) != CHAR_SOFTCR && c != '\n') { + if (c == '\0') + return CLIP_NONE; + } + x = x0; + y += f->h; + } + + /* not yet in y-range */ + else if (y < grd_clip.top) { + while ((c = *s++) != CHAR_SOFTCR && c != '\n') { + if (c == '\0') + return CLIP_NONE; + } + x = x0; + y += f->h; + } + /* can't be in range */ + else + return CLIP_NONE; + } +} diff --git a/engine/src/Libraries/2D/Source/string/genuchr.c b/engine/src/Libraries/2D/Source/string/genuchr.c new file mode 100644 index 0000000..9479dfe --- /dev/null +++ b/engine/src/Libraries/2D/Source/string/genuchr.c @@ -0,0 +1,79 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genuchr.c $ + * $Revision: 1.6 $ + * $Author: lmfeeney $ + * $Date: 1994/06/15 01:20:04 $ + * + * Generic unclipped character drawer. + * + * This file is part of the 2d library. + * + * $Log: genuchr.c $ + * Revision 1.6 1994/06/15 01:20:04 lmfeeney + * support extended ascii (c > 127) w\ uchars, don't change fn i\f + * + * Revision 1.5 1994/04/09 07:24:11 lmfeeney + * added grs_font * as first arguement, #define for compatibility in str.h + * + * Revision 1.4 1993/10/19 09:57:55 kaboom + * Replaced #include new headers. + * + * Revision 1.3 1993/10/08 01:15:51 kaboom + * Changed quotes in #include lines to angle brackets for Watcom. + * + * Revision 1.2 1993/04/29 18:40:56 kaboom + * Changed include of gr.h to smaller more specific grxxx.h. + * + * Revision 1.1 1993/04/08 18:54:48 kaboom + * Initial revision + */ + +#include "bitmap.h" +#include "grdbm.h" + +/* draw an unclipped character c from the current font at (x,y). */ + +void gen_font_uchar(grs_font *f, char c, short x, short y) { + grs_bitmap bm; /* character bitmap */ + short *off_tab; /* character offset table */ + uchar *data_buf; /* pixel data buffer */ + short offset; /* current character offset */ + + /* range check char, get font table pointers, offset. */ + if ((uchar)c > f->max || (uchar)c < f->min) + return; + data_buf = (uchar *)f + f->buf; + off_tab = f->off_tab; + offset = off_tab[(uchar)c - f->min]; + gr_init_bm(&bm, NULL, (f->id == 0xcccc) ? BMT_FLAT8 : BMT_MONO, + BMF_TRANS, off_tab[(uchar)c - f->min + 1] - offset, f->h); + bm.row = f->w; + /* draw the character with no clipping. */ + if (bm.type == BMT_MONO) { + bm.bits = data_buf + (offset >> 3); + bm.align = offset & 7; + gr_mono_ubitmap(&bm, x, y); + } else { + bm.bits = data_buf + offset; + gr_flat8_ubitmap(&bm, x, y); + } +} diff --git a/engine/src/Libraries/2D/Source/string/genustr.c b/engine/src/Libraries/2D/Source/string/genustr.c new file mode 100644 index 0000000..cae8fb3 --- /dev/null +++ b/engine/src/Libraries/2D/Source/string/genustr.c @@ -0,0 +1,96 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/genustr.c $ + * $Revision: 1.7 $ + * $Author: lmfeeney $ + * $Date: 1994/06/15 01:18:55 $ + * + * Generic unclipped string drawer. + * + * This file is part of the 2d library. + * + * $Log: genustr.c $ + * Revision 1.7 1994/06/15 01:18:55 lmfeeney + * support extended ascii (c > 127) w\ uchars, don't change fn i\f + * + * Revision 1.6 1994/04/09 07:24:51 lmfeeney + * added grs_font * as first argument, #define for compatibility in str.h + * + * Revision 1.5 1993/10/19 09:57:55 kaboom + * Replaced #include new headers. + * + * Revision 1.4 1993/10/08 01:15:51 kaboom + * Changed quotes in #include lines to angle brackets for Watcom. + * + * Revision 1.3 1993/06/02 16:21:27 kaboom + * Now handles hard and soft carriage returns and soft spaces, as + * inserted by line wrapping. + * + * Revision 1.2 1993/04/29 18:40:58 kaboom + * Changed include of gr.h to smaller more specific grxxx.h. + * + * Revision 1.1 1993/04/08 16:26:22 kaboom + * Initial revision + */ + +#include "bitmap.h" +#include "grdbm.h" +#include "chr.h" + +/* draw a string s in the specified font at (x0,y0). does not perform + any clipping. */ + +void gen_font_ustring(grs_font *f, char *s, short x0, short y0) { + grs_bitmap bm; /* character bitmap */ + short *offset_tab; /* table of character offsets */ + uchar *char_buf; /* font pixel data */ + short offset; /* offset of current character */ + short x, y; /* position of current character */ + uchar c; /* current character */ + + char_buf = (uchar *)f + f->buf; + offset_tab = f->off_tab; + gr_init_bm(&bm, NULL, (f->id == 0xcccc) ? BMT_FLAT8 : BMT_MONO, BMF_TRANS, 0, f->h); + bm.row = f->w; + + x = x0; + y = y0; + while ((c = (uchar)(*s++)) != '\0') { + if (c == '\n' || c == CHAR_SOFTCR) { + x = x0; + y += f->h; + continue; + } + if (c > f->max || c < f->min || c == CHAR_SOFTSP) + continue; + offset = offset_tab[c - f->min]; + bm.w = offset_tab[c - f->min + 1] - offset; + if (bm.type == BMT_MONO) { + bm.bits = char_buf + (offset >> 3); + bm.align = offset & 7; + gr_mono_ubitmap(&bm, x, y); + } else { + bm.bits = char_buf + offset; + gr_flat8_ubitmap(&bm, x, y); + } + x += bm.w; + } +} diff --git a/engine/src/Libraries/2D/Source/string/str.h b/engine/src/Libraries/2D/Source/string/str.h new file mode 100644 index 0000000..4e40a05 --- /dev/null +++ b/engine/src/Libraries/2D/Source/string/str.h @@ -0,0 +1,55 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/str.h $ + * $Revision: 1.5 $ + * $Author: lmfeeney $ + * $Date: 1994/04/09 00:31:21 $ + * + * Prototypes for non-table string functions. + * + * This file is part of the 2d library. + * + * $Log: str.h $ + * Revision 1.5 1994/04/09 00:31:21 lmfeeney + * added new height and wrapping routines, added #defines + * for compatibility with string and char routines taking + * grs_font * as first arg + * + * Revision 1.4 1993/06/02 21:29:21 kaboom + * Moved n argument for gr_string_nwidth from last to second. + * + * Revision 1.3 1993/06/02 16:33:42 kaboom + * Added prototypes for various new size & n-char routines. + * + * Revision 1.2 1993/04/08 18:57:12 kaboom + * Added prototypes for character functions. + * + * Revision 1.1 1993/04/08 16:28:06 kaboom + * Initial revision + */ + +/* prototypes for non-table driven string handling routines. */ +extern void gr_font_string_size(grs_font *font, char *string, short *width, short *height); +extern short gr_font_string_width(grs_font *font, char *string); +extern short gr_font_char_width(grs_font *f, char c); +extern void gr_font_char_size(grs_font *font, char c, short *width, short *height); +extern int gr_font_string_wrap(grs_font *pfont, char *ps, short width); +extern void gr_font_string_unwrap(char *s); diff --git a/engine/src/Libraries/2D/Source/string/strnsiz.c b/engine/src/Libraries/2D/Source/string/strnsiz.c new file mode 100644 index 0000000..7dd7ccd --- /dev/null +++ b/engine/src/Libraries/2D/Source/string/strnsiz.c @@ -0,0 +1,81 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/strnsiz.c $ + * $Revision: 1.6 $ + * $Author: lmfeeney $ + * $Date: 1994/06/15 01:16:44 $ + * + * Substring width and height calculator. + * + * This file is part of the 2d library. + * + * $Log: strnsiz.c $ + * Revision 1.6 1994/06/15 01:16:44 lmfeeney + * support extended ascii (c > 127) w\ uchars, don't change fn i\f + * + * Revision 1.5 1994/04/09 07:35:26 lmfeeney + * added grs_font * as first argument, #define for comapibility in str.h + * + * Revision 1.4 1993/10/19 09:57:58 kaboom + * Replaced #include new headers. + * + * Revision 1.3 1993/10/08 01:16:26 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.2 1993/06/02 21:28:46 kaboom + * Moved n argument for gr_string_nwidth from last to second. + * + * Revision 1.1 1993/06/02 16:23:16 kaboom + * Initial revision + */ + +#include "chr.h" +#include "ctxmac.h" +#include "str.h" + +/* calculate the width and height of a string in the specified font, and + return in the given pointers. */ + +void gr_font_string_nsize(grs_font *f, char *s, int n, short *w, short *h) { + short *offset_tab; /* table of character offsets */ + short offset; /* offset of current character */ + short w_lin = 0; /* current line's width so far */ + short w_str = 0; /* width of widest line */ + short h_str; /* height of string */ + uchar c; /* current character */ + + offset_tab = f->off_tab; + h_str = f->h; + while ((c = (uchar)(*s++)) != '\0' && n--) { + if (c == CHAR_SOFTSP) + continue; + if (c == '\n' || c == CHAR_SOFTCR) { + if (w_lin > w_str) + w_str = w_lin; + w_lin = 0; + h_str += f->h; + continue; + } + offset = offset_tab[c - f->min]; + w_lin += offset_tab[c - f->min + 1] - offset; + } + *w = (w_lin > w_str) ? w_lin : w_str; + *h = h_str; +} diff --git a/engine/src/Libraries/2D/Source/string/strscl.c b/engine/src/Libraries/2D/Source/string/strscl.c new file mode 100644 index 0000000..1cc2539 --- /dev/null +++ b/engine/src/Libraries/2D/Source/string/strscl.c @@ -0,0 +1,104 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* $Source: n:/project/lib/src/2d/RCS/strscl.c $ + * $Revision: 1.2 $ + * $Author: lmfeeney $ + * $Date: 1994/06/15 01:17:23 $ + */ + +/* scale each character as a separate bitmap: use std scaling calculation + to set the ul of each (next) pixmap and set w/h for bitmap scaling + routine + + this is hugely inefficient, like the old unscaled string routines + it clips each constituent bitmap + + */ + +#include "bitmap.h" +#include "clpcon.h" +#include "grdbm.h" +#include "grrend.h" +#include "str.h" +#include "chr.h" + +#include // printf() + +int gen_font_scale_string(grs_font *f, char *s, short x0, short y0, short w, short h) { + grs_bitmap bm; /* character bitmap */ + short *offset_tab; /* table of character offsets */ + uchar *char_buf; /* font pixel data */ + short offset; /* offset of current character */ + short str_w, str_h; /* width and height of src string */ + fix x, y; /* position of current character */ + fix x_scale, y_scale; /* x and y scale factors */ + fix next_x, next_y, del_y; /* need to use next, del_y since it's const */ + int i; + uchar c; /* current character */ + + if (w == 0 || h == 0) { + return CLIP_NONE; + } + + char_buf = (uchar *)f + f->buf; + offset_tab = f->off_tab; + gr_init_bm(&bm, NULL, (f->id == 0xcccc) ? BMT_FLAT8 : BMT_MONO, BMF_TRANS, 0, f->h); + bm.row = f->w; + + gr_font_string_size(f, s, &str_w, &str_h); + + x_scale = (w << 16) / str_w; + y_scale = (h << 16) / str_h; + + x = x0 << 16; + y = y0 << 16; + + for (i = 0, del_y = 0; i < f->h; del_y += y_scale, i++) + ; /* multiply fix by int, faster ?? */ + next_y = y + del_y; + + while ((c = (uchar)(*s++)) != '\0') { + if (c == '\n' || c == CHAR_SOFTCR) { + x = x0 << 16; + y = next_y; + next_y = y + del_y; + continue; + } + if (c > f->max || c < f->min || c == CHAR_SOFTSP) + continue; + offset = offset_tab[c - f->min]; + bm.w = offset_tab[c - f->min + 1] - offset; + + for (i = 0, next_x = x; i < bm.w; next_x += x_scale, i++) + ; /* multiply fix by int, faster ?? */ + + if (bm.type == BMT_MONO) { + bm.bits = char_buf + (offset >> 3); + bm.align = offset & 7; + gr_scale_bitmap(&bm, fix_int(x), fix_int(y), fix_int(next_x) - fix_int(x), fix_int(next_y) - fix_int(y)); + + } else { + bm.bits = char_buf + offset; + gr_scale_bitmap(&bm, fix_int(x), fix_int(y), fix_int(next_x) - fix_int(x), fix_int(next_y) - fix_int(y)); + } + x = next_x; + } + return CLIP_NONE; +} diff --git a/engine/src/Libraries/2D/Source/string/strsiz.c b/engine/src/Libraries/2D/Source/string/strsiz.c new file mode 100644 index 0000000..a070f8b --- /dev/null +++ b/engine/src/Libraries/2D/Source/string/strsiz.c @@ -0,0 +1,95 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/strsiz.c $ + * $Revision: 1.5 $ + * $Author: lmfeeney $ + * $Date: 1994/06/15 01:17:16 $ + * + * String width and height calculator. + * + * This file is part of the 2d library. + * + * $Log: strsiz.c $ + * Revision 1.5 1994/06/15 01:17:16 lmfeeney + * support extended ascii (c > 127) w\ uchars, don't change fn i\f + * + * Revision 1.4 1994/04/09 07:38:20 lmfeeney + * added grs_font * as first argument, #define for compatibility in str.h + * + * Revision 1.3 1993/10/19 09:57:59 kaboom + * Replaced #include new headers. + * + * Revision 1.2 1993/10/08 01:16:28 kaboom + * Changed quotes in #include lines to angle brackets for Watcom. + * + * Revision 1.1 1993/06/02 16:24:23 kaboom + * Initial revision + */ + +#include "chr.h" +#include "grs.h" +#include "str.h" + +/** + * Calculate the width and height of a string in the specified font, and return in the given pointers. + * @param font font + * @param string string + * @param width returned width + * @param height returned height + */ +void gr_font_string_size(grs_font *font, char *string, short *width, short *height) { + short *offset_tab; /* table of character offsets */ + short offset; /* offset of current character */ + short w_lin = 0; /* current line's width so far */ + short w_str = 0; /* width of widest line */ + short h_str; /* height of string */ + uchar c; /* current character */ + + offset_tab = font->off_tab; + h_str = font->h; + while ((c = (uchar)(*string++)) != '\0') { + if (c == CHAR_SOFTSP) + continue; + if (c == '\n' || c == CHAR_SOFTCR) { + if (w_lin > w_str) + w_str = w_lin; + w_lin = 0; + h_str += font->h; + continue; + } + offset = offset_tab[c - font->min]; + w_lin += offset_tab[c - font->min + 1] - offset; + } + *width = (w_lin > w_str) ? w_lin : w_str; + *height = h_str; +} + +/** + * Returns the width of string in pixels for the specified font + * @param font font + * @param string string + * @return width of string + */ +short gr_font_string_width(grs_font *font, char *string) { + short width = 0, height = 0; + gr_font_string_size(font, string, &width, &height); + return width; +} diff --git a/engine/src/Libraries/2D/Source/string/struscl.c b/engine/src/Libraries/2D/Source/string/struscl.c new file mode 100644 index 0000000..aba79c4 --- /dev/null +++ b/engine/src/Libraries/2D/Source/string/struscl.c @@ -0,0 +1,92 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/struscl.c $ + * $Revision: 1.2 $ + * $Author: lmfeeney $ + * $Date: 1994/06/15 01:17:24 $ + */ + +/* scale an unclipped string by scaling each of its internal character bitmaps, + */ + +#include "bitmap.h" +#include "grdbm.h" +#include "grrend.h" +#include "str.h" +#include "chr.h" + +void gen_font_scale_ustring(grs_font *f, char *s, short x0, short y0, short w, short h) { + grs_bitmap bm; /* character bitmap */ + short *offset_tab; /* table of character offsets */ + uchar *char_buf; /* font pixel data */ + short offset; /* offset of current character */ + short str_w, str_h; /* width and height of src string */ + fix x, y; /* position of current character */ + fix x_scale, y_scale; /* x and y scale factors */ + fix next_x, next_y, del_y; /* need to use next, del_y since it's const */ + int i; + uchar c; /* current character */ + + char_buf = (uchar *)f + f->buf; + offset_tab = f->off_tab; + gr_init_bm(&bm, NULL, (f->id == 0xcccc) ? BMT_FLAT8 : BMT_MONO, BMF_TRANS, 0, f->h); + bm.row = f->w; + + gr_font_string_size(f, s, &str_w, &str_h); + + x_scale = (w << 16) / str_w; /* NB - dst_size / src_size, not rate of incr thru src*/ + y_scale = (h << 16) / str_h; + + x = x0 << 16; + y = y0 << 16; + + for (i = 0, del_y = 0; i < f->h; del_y += y_scale, i++) + ; /* multiply fix by int, faster ?? */ + next_y = y + del_y; + + while ((c = (uchar)(*s++)) != '\0') { + if (c == '\n' || c == CHAR_SOFTCR) { + x = x0 << 16; + y = next_y; + next_y = y + del_y; + continue; + } + + if (c > f->max || c < f->min || c == CHAR_SOFTSP) + continue; + offset = offset_tab[c - f->min]; + bm.w = offset_tab[c - f->min + 1] - offset; + + for (i = 0, next_x = x; i < bm.w; next_x += x_scale, i++) + ; /* multiply fix by int, faster ?? */ + + if (bm.type == BMT_MONO) { + bm.bits = char_buf + (offset >> 3); + bm.align = offset & 7; + gr_scale_ubitmap(&bm, fix_int(x), fix_int(y), /* does this exist? */ + fix_int(next_x) - fix_int(x), fix_int(next_y) - fix_int(y)); + } else { + bm.bits = char_buf + offset; + gr_scale_ubitmap(&bm, fix_int(x), fix_int(y), fix_int(next_x) - fix_int(x), fix_int(next_y) - fix_int(y)); + } + x = next_x; + } +} diff --git a/engine/src/Libraries/2D/Source/string/strwrap.c b/engine/src/Libraries/2D/Source/string/strwrap.c new file mode 100644 index 0000000..5182e51 --- /dev/null +++ b/engine/src/Libraries/2D/Source/string/strwrap.c @@ -0,0 +1,161 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +/* $Source: r:/prj/lib/src/2d/RCS/strwrap.c $ + * $Revision: 1.3 $ + * $Author: lmfeeney $ + * $Date: 1994/08/19 02:39:22 $ + */ + +/* wrapping and unwrapping routines moved into the 2d library, other + routines already present in 2d +*/ + +// Font.C Font-handling routines +// Rex E. Bradford (REX) +// +// This module provides routines for accessing fonts +// and for calculating the area needed to display +// text in a font, including automatic wrapping. + +/* log from /project/ff/code/gfx/font.c + + * Revision 1.6 1993/11/18 11:15:50 rex + * Changed Font* to grs_font* + * + * Revision 1.5 1993/11/04 21:37:40 kaboom + * Changed quotes to angle brackets in includes for watcom + * + * Revision 1.4 1993/05/04 13:47:45 rex + * Fixed return value in FontWrapText() + * + * Revision 1.3 1993/04/22 19:37:14 rex + * Removed FontSetFont, added pfont param to other funcs + * + * Revision 1.2 1993/02/04 12:24:09 rex + * Converted to new debug system + * + * Revision 1.1 1992/08/31 16:16:53 unknown + * Initial revision + * +*/ + +#include "lg_types.h" +#include "chr.h" +#include "grs.h" + +static short *pCharPixOff; // ptr to char offset table, with pfont->minch + // already subtracted out! + +#define CHARALIGN(pfont, c) (pCharPixOff[(uchar)c] & 7) +#define CHARPTR(pfont, c) (&pfont->bits[pCharPixOff[(uchar)c] >> 3]) +#define CHARWIDTH(pfont, c) (pCharPixOff[(uchar)c + 1] - pCharPixOff[(uchar)c]) + +#define FONT_SETFONT(pfont) (pCharPixOff = &(pfont)->off_tab[0] - (pfont)->min) + +/** + * FontWrapText() inserts wrapping codes into text. + * It inserts soft carriage returns into the text, and returns the number of lines needed for display. + * @note renamed to gr_font_string_wrap in library + * + * @param pfont ptr to font + * @param ps ptr to string (soft cr's and soft spaces inserted into it) + * @param width width of area to wrap into, in pixels + * @return # lines string wraps into + */ +int gr_font_string_wrap(grs_font *pfont, char *ps, short width) { + uchar *p; + char *pmark; + short numLines; + short currWidth; + + // Set up to do wrapping + FONT_SETFONT(pfont); + numLines = 0; // ps = base of current line + + // Do wrapping for each line till hit end + while (*ps) { + pmark = NULL; // no SOFTCR insert point yet + currWidth = 0; // and zero width so far + p = (uchar *)ps; + + // Loop thru each word + while (*p) { + // Skip through to next CR or space or '\0', keeping track of width + while ((*p != 0) && (*p != '\n') && (*p != ' ')) { + currWidth += CHARWIDTH(pfont, *p); + p++; + } + + if (currWidth > width) { + // If bypassed width, break out of word loop + if ((pmark == NULL) && (*p != 0) && (*p != '\n')) + pmark = (char *)p; + break; + } else { + // Else set new mark point (unless eol or eos, then bust out) + if ((*p == 0) || (*p == '\n')) // hit end of line, wipe marker + { + pmark = NULL; + break; + } + pmark = (char *)p; // else advance marker + currWidth += CHARWIDTH(pfont, ' '); // and account for space + p++; + } + } + + if (pmark) { + // Now insert soft cr if marked one + *pmark = CHAR_SOFTCR; + ps = pmark + 1; + if (*ps == ' ') // if wrapped and following space, + *ps++ = CHAR_SOFTSP; // turn into (ignored) soft space + } else { + // Otherwise, bump past cr + if (*p) + ++p; + ps = (char *)p; + } + + // Bump line counter in any case + ++numLines; + } + + // When hit end of string, return # lines encountered + return (numLines); +} + +/** + * FontUnwrapText() turns soft carriage returns back into spaces. Usually this is done prior to re-wrapping + * text with a new width. + * @note renamed to gr_font_string_unwrap in 2d library + * + * @param s ptr to string (soft cr's and spaces turned back to spaces) + */ +void gr_font_string_unwrap(char *s) { + int c; + + while ((c = *s) != 0) { + if ((c == CHAR_SOFTCR) || (c == CHAR_SOFTSP)) + *s = ' '; + s++; + } +} diff --git a/engine/src/Libraries/2D/Source/svgainit.c b/engine/src/Libraries/2D/Source/svgainit.c new file mode 100644 index 0000000..2f7a47a --- /dev/null +++ b/engine/src/Libraries/2D/Source/svgainit.c @@ -0,0 +1,42 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/svgainit.c $ + * $Revision: 1.1 $ + * $Author: kevin $ + * $Date: 1994/12/05 21:07:57 $ + * + * Routine to initialize the 2d system for svga use. + * + * This file is part of the 2d library. + * + * $Log: svgainit.c $ + * Revision 1.1 1994/12/05 21:07:57 kevin + * Initial revision + * + */ + +#include "initint.h" + +int gr_init(void) +{ +// MLA - ditched this, because there is only one mode to detect on the Mac +// grd_detect_func=svga_detect; + return gri_init(); +} diff --git a/engine/src/Libraries/2D/Source/tabdat.h b/engine/src/Libraries/2D/Source/tabdat.h new file mode 100644 index 0000000..138f56b --- /dev/null +++ b/engine/src/Libraries/2D/Source/tabdat.h @@ -0,0 +1,44 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/tabdat.h $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/08/16 15:38:58 $ + * + * Declarations for function tables. + * + * This file is part of the 2d library. + * + * $Log: tabdat.h $ + * Revision 1.2 1994/08/16 15:38:58 kevin + * new function table declataions. + * + * Revision 1.1 1993/10/19 10:30:49 kaboom + * Initial revision + * + */ + +#ifndef __TABDAT_H +#define __TABDAT_H +extern void (**grd_pixel_table)(); +extern void (**grd_device_table)(); +extern void (**grd_canvas_table)(); +extern void (**grd_function_table)(); +#endif /* !__TABDAT_H */ diff --git a/engine/src/Libraries/2D/Source/tabdrv.h b/engine/src/Libraries/2D/Source/tabdrv.h new file mode 100644 index 0000000..c9bf2c5 --- /dev/null +++ b/engine/src/Libraries/2D/Source/tabdrv.h @@ -0,0 +1,54 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/tabdrv.h $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/10/04 18:44:24 $ + * + * Declarations for function tables. + * + * This file is part of the 2d library. + * + * $Log: tabdrv.h $ + * Revision 1.2 1994/10/04 18:44:24 kevin + * Added #define for doubling canvas in case we might someday use it. + * + * Revision 1.1 1994/08/16 15:39:30 kevin + * Initial revision + * + */ + +#ifndef __TABDRV_H +#define __TABDRV_H + +#include "bitmap.h" +#include "fill.h" +#include "ifcn.h" + +typedef void (*grt_function_table[GRD_FILL_TYPES][GRD_FUNCS*REAL_BMT_TYPES])(); + +extern grt_function_table gen_function_table; +extern grt_function_table flat8_function_table; +extern grt_function_table flat8d_function_table; +extern grt_function_table modex_function_table; +extern grt_function_table bank8_function_table; +extern grt_function_table bank24_function_table; + +#endif /* __TABDRV_H */ diff --git a/engine/src/Libraries/2D/Source/tempbm.c b/engine/src/Libraries/2D/Source/tempbm.c new file mode 100644 index 0000000..4a58a6d --- /dev/null +++ b/engine/src/Libraries/2D/Source/tempbm.c @@ -0,0 +1,38 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include "bitmap.h" +#include "cnvdat.h" +#include "tabdat.h" +#include "ifcn.h" +#include "grs.h" + + +void temp_flat8_ubitmap (grs_bitmap *bm, int x, int y) +{ + ((void (*)(grs_bitmap *_bm,int _x, int _y)) + grd_function_table[GRC_BITMAP+BMT_FLAT8*GRD_FUNCS])(bm, x, y); +} + +void temp_flat8_bitmap (grs_bitmap *bm, int x, int y) +{ + ((void (*)(grs_bitmap *_bm,int _x, int _y, grs_stencil *_sten)) + grd_function_table[GRC_STENCIL_BITMAP+BMT_FLAT8*GRD_FUNCS])(bm, x, y, grd_clip.sten); +} + + diff --git a/engine/src/Libraries/2D/Source/temptm.c b/engine/src/Libraries/2D/Source/temptm.c new file mode 100644 index 0000000..0826ba6 --- /dev/null +++ b/engine/src/Libraries/2D/Source/temptm.c @@ -0,0 +1,555 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/temptm.c $ + * $Revision: 1.9 $ + * $Author: kevin $ + * $Date: 1994/11/09 21:14:00 $ + * + * Temporary texture mapper dispatchers. + * + * This file is part of the 2d library. + * + */ + +#include "bitmap.h" +#include "clpcon.h" +#include "cnvdat.h" +#include "fill.h" +#include "grpix.h" +#include "grs.h" +#include "plytyp.h" +#include "ifcn.h" +#include "tabdat.h" +#include "tmapfcn.h" +#include "tmaps.h" +#include "general.h" + +enum { + POLY, SPOLY, CPOLY, TPOLY, STPOLY +}; + +// prototypes +void temp_upoint(short x, short y); +void temp_point(short x, short y); +void temp_flat8_mask_bitmap (grs_bitmap *bm, int x, int y, grs_stencil *sten); +void temp_flat8_clut_ubitmap (grs_bitmap *bm, int x, int y, uchar *cl); +void temp_tluc8_ubitmap (grs_bitmap *bm, int x, int y); +void temp_per_map (grs_bitmap *bm, int n, grs_vertex **vpl); +void temp_per_umap (grs_bitmap *bm, int n, grs_vertex **vpl); +void temp_clut_per_map (grs_bitmap *bm, int n, grs_vertex **vpl, uchar *clut); +void temp_clut_per_umap (grs_bitmap *bm, int n, grs_vertex **vpl, uchar *clut); +void temp_lit_per_umap (grs_bitmap *bm, int n, grs_vertex **vpl); +void temp_lin_umap(grs_bitmap *bm, int n, grs_vertex **vpl); +void temp_lin_map(grs_bitmap *bm, int n, grs_vertex **vpl); +void temp_lit_lin_umap(grs_bitmap *bm, int n, grs_vertex **vpl); +void temp_lit_lin_map(grs_bitmap *bm, int n, grs_vertex **vpl); +void temp_clut_lin_umap(grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); +void temp_clut_lin_map(grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); +void temp_floor_umap(grs_bitmap *bm, int n, grs_vertex **vpl); +void temp_clut_floor_umap(grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); +void temp_lit_floor_umap(grs_bitmap *bm, int n, grs_vertex **vpl); +void temp_wall_umap(grs_bitmap *bm, int n, grs_vertex **vpl); +void temp_clut_wall_umap(grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl); +void temp_lit_wall_umap(grs_bitmap *bm, int n, grs_vertex **vpl); +void temp_scale_umap(grs_bitmap *bm, short x, short y, short w, short h); +void temp_clut_scale_umap(grs_bitmap *bm, short x, short y, short w, short h, uchar *cl); +int gri_scale_clip(grs_vertex *v0, grs_vertex *v1); + +void temp_rsd8_ubitmap (grs_bitmap *bm, int x, int y); +void temp_rsd8_bitmap (grs_bitmap *bm, int x, int y); + + +void temp_upoint(short x, short y) +{ + gr_fill_upixel(grd_gc.fcolor, x, y); +} + +void temp_point(short x, short y) +{ + gr_fill_pixel(grd_gc.fcolor, x, y); +} + +void temp_rsd8_ubitmap (grs_bitmap *bm, int x, int y) +{ + ((void (*)(grs_bitmap *_bm,int _x, int _y)) + grd_function_table[GRC_BITMAP+BMT_RSD8*GRD_FUNCS])(bm, x, y); +} + +void temp_rsd8_bitmap (grs_bitmap *bm, int x, int y) +{ + ((void (*)(grs_bitmap *_bm,int _x, int _y, grs_stencil *_sten)) + grd_function_table[GRC_STENCIL_BITMAP+BMT_RSD8*GRD_FUNCS])(bm, x, y, grd_clip.sten); +} + +void temp_flat8_mask_bitmap (grs_bitmap *bm, int x, int y, grs_stencil *sten) +{ + ((void (*)(grs_bitmap *_bm,int _x, int _y, grs_stencil *_sten)) + grd_function_table[GRC_STENCIL_BITMAP+BMT_FLAT8*GRD_FUNCS])(bm, x, y, sten); +} + +void temp_flat8_clut_ubitmap (grs_bitmap *bm, int x, int y, uchar *cl) +{ + ((void (*)(grs_bitmap *_bm,int _x, int _y, uchar *_cl)) + grd_function_table[GRC_BITMAP+BMT_FLAT8*GRD_FUNCS])(bm, x, y, cl); +} + +void temp_tluc8_ubitmap (grs_bitmap *bm, int x, int y) +{ + ((void (*)(grs_bitmap *_bm,int _x, int _y)) + grd_function_table[GRC_BITMAP+BMT_TLUC8*GRD_FUNCS])(bm, x, y); +} + +int temp_poly (long c, int n, grs_vertex **vpl) +{ + grs_tmap_info ti; + grs_bitmap bm; + + ti.tmap_type=GRC_POLY; + ti.flags=0; + bm.bits=(uchar *)c; + bm.type=POLY; + bm.flags=0; + return h_map(&bm,n,vpl,&ti); +} + +void temp_upoly (long c, int n, grs_vertex **vpl) +{ + grs_tmap_info ti; + grs_bitmap bm; + + ti.tmap_type=GRC_POLY; + ti.flags=0; + bm.bits=(uchar *)c; + bm.type=POLY; + bm.flags=0; + h_umap(&bm,n,vpl,&ti); +} + +int temp_spoly (long c, int n, grs_vertex **vpl) +{ + grs_tmap_info ti; + grs_bitmap bm; + + ti.tmap_type=GRC_POLY; + ti.flags=0; + bm.bits=(uchar *)c; + bm.type=SPOLY; + bm.flags=0; + return h_map(&bm,n,vpl,&ti); +} + +void temp_uspoly (long c, int n, grs_vertex **vpl) +{ + grs_tmap_info ti; + grs_bitmap bm; + + ti.tmap_type=GRC_POLY; + ti.flags=0; + bm.bits=(uchar *)c; + bm.type=SPOLY; + bm.flags=0; + h_umap(&bm,n,vpl,&ti); +} + +int temp_cpoly (long c, int n, grs_vertex **vpl) +{ + grs_tmap_info ti; + grs_bitmap bm; + + ti.tmap_type=GRC_POLY; + ti.flags=0; + bm.bits=(uchar *)c; + bm.type=CPOLY; + bm.flags=0; + return h_map(&bm,n,vpl,&ti); +} + +void temp_ucpoly (long c, int n, grs_vertex **vpl) +{ + grs_tmap_info ti; + grs_bitmap bm; + + ti.tmap_type=GRC_POLY; + ti.flags=0; + bm.bits=(uchar *)c; + bm.type=CPOLY; + bm.flags=0; + h_umap(&bm,n,vpl,&ti); +} + +int temp_tpoly (long c, int n, grs_vertex **vpl) +{ + grs_tmap_info ti; + grs_bitmap bm; + + ti.tmap_type=GRC_POLY; + bm.bits=(uchar *)c; + ti.flags=0; + bm.bits=(uchar *)c; + bm.type=TPOLY; + bm.flags=0; + return h_map(&bm,n,vpl,&ti); +} + +void temp_utpoly (long c, int n, grs_vertex **vpl) +{ + grs_tmap_info ti; + grs_bitmap bm; + + ti.tmap_type=GRC_POLY; + ti.flags=0; + bm.bits=(uchar *)c; + bm.type=TPOLY; + bm.flags=0; + h_umap(&bm,n,vpl,&ti); +} + +int temp_stpoly (long c, int n, grs_vertex **vpl) +{ + grs_tmap_info ti; + grs_bitmap bm; + + ti.tmap_type=GRC_POLY; + ti.flags=0; + bm.bits=(uchar *)c; + bm.type=STPOLY; + bm.flags=0; + return h_map(&bm,n,vpl,&ti); +} + +void temp_ustpoly (long c, int n, grs_vertex **vpl) +{ + grs_tmap_info ti; + grs_bitmap bm; + + ti.tmap_type=GRC_POLY; + ti.flags=0; + bm.bits=(uchar *)c; + bm.type=STPOLY; + bm.flags=0; + h_umap(&bm,n,vpl,&ti); +} + +void temp_per_map (grs_bitmap *bm, int n, grs_vertex **vpl) +{ + if (bm->row==1<<(bm->wlog)) { + grs_tmap_info ti; + ti.tmap_type=GRC_PER; + ti.flags=0; + per_map(bm,n,vpl,&ti); + } +} + +void temp_per_umap (grs_bitmap *bm, int n, grs_vertex **vpl) +{ + if (bm->row==1<<(bm->wlog)) { + grs_tmap_info ti; + ti.tmap_type=GRC_PER; + ti.flags=0; + per_umap(bm,n,vpl,&ti); + } +} + +void temp_clut_per_map (grs_bitmap *bm, int n, grs_vertex **vpl, uchar *clut) +{ + if (bm->row==1<<(bm->wlog)) { + grs_tmap_info ti; + ti.tmap_type=GRC_CLUT_PER; + ti.flags=TMF_CLUT; + ti.clut=clut; + per_map(bm,n,vpl,&ti); + } +} + +void temp_clut_per_umap (grs_bitmap *bm, int n, grs_vertex **vpl, uchar *clut) +{ + if (bm->row==1<<(bm->wlog)) { + grs_tmap_info ti; + ti.tmap_type=GRC_CLUT_PER; + ti.flags=TMF_CLUT; + ti.clut=clut; + per_umap(bm,n,vpl,&ti); + } +} + +void temp_lit_per_umap (grs_bitmap *bm, int n, grs_vertex **vpl) +{ + if (bm->row==1<<(bm->wlog)) { + grs_tmap_info ti; + ti.tmap_type=GRC_LIT_PER; + ti.flags=0; + per_umap(bm,n,vpl,&ti); + } +} + +void temp_lin_umap(grs_bitmap *bm, int n, grs_vertex **vpl) +{ + if ((bm->row==1<<(bm->wlog))||(grd_gc.fill_type==FILL_CLUT)) { + grs_tmap_info ti; + if ((n==3)&&((bm->flags&BMF_TRANS)==0)) + ti.tmap_type=GRC_LIN; + else + ti.tmap_type=GRC_BILIN; + ti.flags=0; + h_umap(bm,n,vpl,&ti); + } +} + +void temp_lin_map(grs_bitmap *bm, int n, grs_vertex **vpl) +{ + if ((bm->row==1<<(bm->wlog))||(grd_gc.fill_type==FILL_CLUT)) { + grs_tmap_info ti; + if ((n==3)&&((bm->flags&BMF_TRANS)==0)) + ti.tmap_type=GRC_LIN; + else + ti.tmap_type=GRC_BILIN; + ti.flags=0; + h_map(bm,n,vpl,&ti); + } +} + +void temp_lit_lin_umap(grs_bitmap *bm, int n, grs_vertex **vpl) +{ + if ((bm->row==1<<(bm->wlog))||(grd_gc.fill_type==FILL_CLUT)) { + grs_tmap_info ti; + ti.tmap_type=GRC_LIT_BILIN; + ti.flags=0; + h_umap(bm,n,vpl,&ti); + } +} + +void temp_lit_lin_map(grs_bitmap *bm, int n, grs_vertex **vpl) +{ + if ((bm->row==1<<(bm->wlog))||(grd_gc.fill_type==FILL_CLUT)) { + grs_tmap_info ti; + ti.tmap_type=GRC_LIT_BILIN; + ti.flags=0; + h_map(bm,n,vpl,&ti); + } +} + +void temp_clut_lin_umap(grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl) +{ + grs_tmap_info ti; + + if ((n==3)&&((bm->flags&BMF_TRANS)==0)) + ti.tmap_type=GRC_CLUT_LIN; + else + ti.tmap_type=GRC_CLUT_BILIN; + ti.flags=TMF_CLUT; + ti.clut=cl; + h_umap(bm,n,vpl,&ti); +} + +void temp_clut_lin_map(grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl) +{ + grs_tmap_info ti; + + if ((n==3)&&((bm->flags&BMF_TRANS)==0)) + ti.tmap_type=GRC_CLUT_LIN; + else + ti.tmap_type=GRC_CLUT_BILIN; + ti.flags=TMF_CLUT; + ti.clut=cl; + h_map(bm,n,vpl,&ti); +} + +void temp_floor_umap(grs_bitmap *bm, int n, grs_vertex **vpl) +{ + if (bm->row==1<<(bm->wlog)) { + grs_tmap_info ti; + ti.tmap_type=GRC_FLOOR; + ti.flags=TMF_FLOOR; + h_umap(bm,n,vpl,&ti); + } +} + +void temp_clut_floor_umap(grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl) +{ + if (bm->row==1<<(bm->wlog)) { + grs_tmap_info ti; + ti.tmap_type=GRC_CLUT_FLOOR; + ti.flags=TMF_CLUT|TMF_FLOOR; + ti.clut=cl; + h_umap(bm,n,vpl,&ti); + } +} + +void temp_lit_floor_umap(grs_bitmap *bm, int n, grs_vertex **vpl) +{ + if (bm->row==1<<(bm->wlog)) { + grs_tmap_info ti; + ti.tmap_type=GRC_LIT_FLOOR; + ti.flags=TMF_FLOOR; + h_umap(bm,n,vpl,&ti); + } +} + +void temp_wall_umap(grs_bitmap *bm, int n, grs_vertex **vpl) +{ + if (bm->row==1<<(bm->wlog)) { + grs_tmap_info ti; + ti.tmap_type=GRC_WALL2D; + ti.flags=TMF_WALL; + v_umap(bm,n,vpl,&ti); + } +} + +void temp_clut_wall_umap(grs_bitmap *bm, int n, grs_vertex **vpl, uchar *cl) +{ + if (bm->row==1<<(bm->wlog)) { + grs_tmap_info ti; + ti.tmap_type=GRC_CLUT_WALL2D; + ti.flags=TMF_CLUT|TMF_WALL; + ti.clut=cl; + v_umap(bm,n,vpl,&ti); + } +} + +void temp_lit_wall_umap(grs_bitmap *bm, int n, grs_vertex **vpl) +{ + if (bm->row==1<<(bm->wlog)) { + grs_tmap_info ti; + ti.tmap_type=GRC_LIT_WALL2D; + ti.flags=TMF_WALL; + v_umap(bm,n,vpl,&ti); + } +} + +/* take int _x,_y; fix _u,_v; stuff them into grs_vertex _vertex */ + +#define make_vertex(_vertex,_x,_y,_u,_v) \ + _vertex.x = fix_make(_x,0), \ + _vertex.y = fix_make(_y,0), \ + _vertex.u = _u, \ + _vertex.v = _v + +void temp_scale_umap(grs_bitmap *bm, short x, short y, short w, short h) +{ + grs_tmap_info ti; + grs_vertex *vpl[2]; + grs_vertex v0,v1; + + vpl[0]=&v0; + vpl[1]=&v1; + make_vertex(v0,x,y,0,0); + make_vertex(v1,x+w,y+h,fix_make(bm->w,0),fix_make(bm->h,0)); + + ti.tmap_type=GRC_SCALE; + ti.flags=0; + h_umap(bm,2,vpl,&ti); +} + +void temp_clut_scale_umap(grs_bitmap *bm, short x, short y, short w, short h, uchar *cl) +{ + grs_tmap_info ti; + grs_vertex *vpl[2]; + grs_vertex v0,v1; + + vpl[0]=&v0; + vpl[1]=&v1; + make_vertex(v0,x,y,0,0); + make_vertex(v1,x+w,y+h,fix_make(bm->w,0),fix_make(bm->h,0)); + + ti.tmap_type=GRC_CLUT_SCALE; + ti.flags=TMF_CLUT; + ti.clut=cl; + h_umap(bm,2,vpl,&ti); +} + +int gri_scale_clip(grs_vertex *v0, grs_vertex *v1) +{ + int code; + fix u_scale,v_scale; + + if ((v0->x>=grd_fix_clip.right) || (v1->x<=grd_fix_clip.left) || + (v0->y>=grd_fix_clip.bot) || (v1->y<=grd_fix_clip.top)) + return CLIP_ALL; + + code = CLIP_NONE; + + u_scale = fix_div(v1->u,v1->x-v0->x); + v_scale = fix_div(v1->v,v1->y-v0->y); + + if (v0->xu = fix_mul(u_scale,grd_fix_clip.left-v0->x); + v0->x = grd_fix_clip.left; + code |= CLIP_LEFT; + } + if (v1->x>grd_fix_clip.right) { + v1->x = grd_fix_clip.right; + v1->u = v0->u+fix_mul(u_scale,v1->x-v0->x); + code |= CLIP_RIGHT; + } + if (v0->yv = fix_mul(v_scale,grd_fix_clip.top-v0->y); + v0->y = grd_fix_clip.top; + code |= CLIP_TOP; + } + if (v1->y>grd_fix_clip.bot) { + v1->y = grd_fix_clip.bot; + v1->v = v0->v+fix_mul(v_scale,v1->y-v0->y); + code |= CLIP_RIGHT; + } + return code; +} + +int temp_scale_map(grs_bitmap *bm, short x, short y, short w, short h) +{ + grs_tmap_info ti; + grs_vertex *vpl[2]; + grs_vertex v0,v1; + int code; + + vpl[0]=&v0; + vpl[1]=&v1; + make_vertex(v0,x,y,0,0); + make_vertex(v1,x+w,y+h,fix_make(bm->w,0),fix_make(bm->h,0)); + + code=gri_scale_clip(&v0,&v1); + if (code==CLIP_ALL) return code; + + ti.tmap_type=GRC_SCALE; + ti.flags=0; + h_umap(bm,2,vpl,&ti); + return code; +} + +int temp_clut_scale_map(grs_bitmap *bm, short x, short y, short w, short h, uchar *cl) +{ + grs_tmap_info ti; + grs_vertex *vpl[2]; + grs_vertex v0,v1; + int code; + + vpl[0]=&v0; + vpl[1]=&v1; + make_vertex(v0,x,y,0,0); + make_vertex(v1,x+w,y+h,fix_make(bm->w,0),fix_make(bm->h,0)); + + code=gri_scale_clip(&v0,&v1); + if (code==CLIP_ALL) return code; + + ti.tmap_type=GRC_CLUT_SCALE; + ti.flags=TMF_CLUT; + ti.clut=cl; + h_umap(bm,2,vpl,&ti); + return CLIP_NONE; +} diff --git a/engine/src/Libraries/2D/Source/tlucdat.c b/engine/src/Libraries/2D/Source/tlucdat.c new file mode 100644 index 0000000..b90c269 --- /dev/null +++ b/engine/src/Libraries/2D/Source/tlucdat.c @@ -0,0 +1,47 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/tlucdat.c $ + * $Revision: 1.3 $ + * $Author: baf $ + * $Date: 1994/01/17 22:13:14 $ + * + * Globals for translucency. + * + * This file is part of the 2d library. + * + * $Log: tlucdat.c $ + * Revision 1.3 1994/01/17 22:13:14 baf + * Redid tluc8 spolys (again). + * + * Revision 1.2 1994/01/14 12:41:07 baf + * Lit translucency reform. + * + * Revision 1.1 1993/12/01 21:20:05 baf + * Initial revision + * + * + */ + +#include "lg.h" + +uchar *tluc8tab[256]; +uchar *tluc8ltab[256]; +uchar *tluc8stab; +int tluc8nstab = 0; diff --git a/engine/src/Libraries/2D/Source/tlucdat.h b/engine/src/Libraries/2D/Source/tlucdat.h new file mode 100644 index 0000000..5ef0ee9 --- /dev/null +++ b/engine/src/Libraries/2D/Source/tlucdat.h @@ -0,0 +1,50 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/tlucdat.h $ + * $Revision: 1.3 $ + * $Author: baf $ + * $Date: 1994/01/17 22:13:21 $ + * + * Globals for translucency. + * + * This file is part of the 2d library. + * + * $Log: tlucdat.h $ + * Revision 1.3 1994/01/17 22:13:21 baf + * Redid tluc8 spolys (again). + * + * Revision 1.2 1994/01/14 12:41:11 baf + * Lit translucency reform. + * + * Revision 1.1 1993/12/01 21:19:42 baf + * Initial revision + * + * + */ + +#ifndef __SPNDAT +#define __SPNDAT + +extern uchar *tluc8tab[256]; +extern uchar *tluc8ltab[256]; +extern uchar *tluc8stab; +extern int tluc8nstab; + +#endif diff --git a/engine/src/Libraries/2D/Source/tluctab.c b/engine/src/Libraries/2D/Source/tluctab.c new file mode 100644 index 0000000..b58d5ba --- /dev/null +++ b/engine/src/Libraries/2D/Source/tluctab.c @@ -0,0 +1,207 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/tluctab.c $ + * $Revision: 1.6 $ + * $Author: baf $ + * $Date: 1994/01/17 22:13:15 $ + * + * This file is, for good or ill, part of the 2d library. + * + * $Log: tluctab.c $ + * Revision 1.6 1994/01/17 22:13:15 baf + * Redid tluc8 spolys (again). + * + * Revision 1.5 1994/01/14 12:41:09 baf + * Lit translucency reform. + * + * Revision 1.4 1993/12/09 17:39:23 baf + * Added lighting + * + * Revision 1.3 1993/11/29 20:32:03 baf + * General revisions and tidying/up of translucency + * and shading routines + * + * Revision 1.2 1993/11/15 15:01:27 baf + * Added this RCS header + * + * + */ + +/* +This is a routine to generate CLUTs to simulate +looking at colors through a translucent frammice. +To do this, we need to know three things: +The color of the frammice, how opaque it is, and +how effective it is as a color filter (purity). +Opacity and purity should vary from 0 to 1. + +Essentially, we multiply the background color by +the frammice color, then take a weighted average +of the result and the background color, then take a +weighted average of the new result and the frammice +color. The two weights are purity and opacity, +respectively. (Apologia: Averaging the two factors in +sequentially like this may seem strange, but I found +it to be more intuitive than any other system I could +think of. Opacity means exactly what it sounds like, and +filter purity only applies to the light coming through.) + +Increasing the opacity of the frammice will make it look +cloudier, whereas increasing the purity will make it +look more strongly colored. Put a green frammice on a +red background. If it has high opacity, it will be +green. If it has low opacity but high purity, it will +be black, as no red light gets through. If it has low +opacity and low purity, it will be transparent and +therefore look red. Somewhere in the middle of all this, +it will look brown, which is probably what you want. +*/ + +#include +#include "fix.h" +#include "grs.h" +#include "scrdat.h" +#include "rgb.h" +#include "tlucdat.h" +#include "lg.h" + +// convert red in a glomped rgb to a fixed point +#define rtof(b) ( ((b)&0x3ff)<<12) +// convert green in a glomped rgb to a fixed point +#define gtof(b) ( ((b)&0x1ff800)<<1) +// convert blue in a glomped rgb to a fixed point +#define btof(b) ( ((b)&0xffc00000)>>10) + +#define WHITE (0x7fff) + +/* Note: RGB values are 6-bit. */ + +/* Fills table with groovy values, and incedentally returns it. */ +uchar *gr_init_translucency_table(uchar *p, fix opacity, fix purity, grs_rgb color) +{ + grs_rgb background; + int i; + fix r, g, b; + fix baser, baseg, baseb; /* surface component */ + fix filterr, filterg, filterb; /* translucent component before addition of background */ + fix filter, clarity; + long thingge; + + baser = fix_mul(rtof(color), opacity); + baseg = fix_mul(gtof(color), opacity); + baseb = fix_mul(btof(color), opacity); + filter = fix_mul(0x10000 - opacity, purity); + clarity = 0x10000 - opacity - filter; + filterr = clarity + (fix_mul(rtof(color), filter) >> 6); + filterg = clarity + (fix_mul(gtof(color), filter) >> 6); + filterb = clarity + (fix_mul(btof(color), filter) >> 6); + for (i=0; i<256; i++) { + background = grd_bpal[i]; + r = fix_mul(rtof(background), filterr) + baser; + g = fix_mul(gtof(background), filterg) + baseg; + b = fix_mul(btof(background), filterb) + baseb; + thingge = gr_index_rgb(r<<2, g<<2, b<<2); + p[i] = grd_ipal[thingge]; + } + return p; +} + +uchar *gr_init_lit_translucency_table(uchar *p, fix opacity, fix purity, grs_rgb color, grs_rgb light) +{ + grs_rgb background; + int i; + fix r, g, b; + fix baser, baseg, baseb; /* surface component */ + fix filterr, filterg, filterb; /* translucent component before addition of background */ + fix filter, clarity; + long thingge; + + filter = fix_mul(0x10000 - opacity, purity); + clarity = 0x10000 - opacity - filter; + filterr = clarity + (fix_mul(rtof(color), filter) >> 6); + filterg = clarity + (fix_mul(gtof(color), filter) >> 6); + filterb = clarity + (fix_mul(btof(color), filter) >> 6); + baser = fix_mul(fix_mul(rtof(color), rtof(light)), opacity) >> 6; + baseg = fix_mul(fix_mul(gtof(color), gtof(light)), opacity) >> 6; + baseb = fix_mul(fix_mul(btof(color), btof(light)), opacity) >> 6; + for (i=0; i<256; i++) { + background = grd_bpal[i]; + r = fix_mul(rtof(background), filterr) + baser; + g = fix_mul(gtof(background), filterg) + baseg; + b = fix_mul(btof(background), filterb) + baseb; + thingge = gr_index_rgb(r<<2, g<<2, b<<2); + p[i] = grd_ipal[thingge]; + } + return p; +} + +uchar *gr_init_lit_translucency_tables(uchar *p, fix opacity, fix purity, grs_rgb color, int n) +{ + int k; + grs_rgb light; + if (n == 0) return NULL; + for (k=0; kltab[k*256+grd_ipal[WHITE]]]; + gr_init_lit_translucency_table(p+256*k, opacity, purity, color, light); + } + return p; +} + +int gr_dump_tluc8_table(uchar *buf, int nlit) +{ + uchar *p; + int k, lsize = nlit*256; + p = buf; + p += sizeof(int); + *(p++) = nlit; + *(p++) = tluc8nstab; + LG_memcpy(p, tluc8stab, tluc8nstab*256); + for (k=0; k<256; k++) { + if (tluc8tab[k]) { + *(p++) = k; + LG_memcpy(p, tluc8tab[k], 256); + p += 256; + LG_memcpy(p, tluc8ltab[k], lsize); + p += lsize; + } + } + *(int *)buf = p-buf; + return p-buf; +} + +void gr_read_tluc8_table(uchar *buf) +{ + uchar *p, *end; + int lsize, k; + end = buf + *(int *)buf; + p = buf + sizeof(int); + lsize = *(p++) * 256; + tluc8nstab = *(p++); + tluc8stab=p; + p += 256*tluc8nstab; + while (p < end) { + k = *(p++); + tluc8tab[k] = p; + p += 256; + if (lsize) tluc8ltab[k] = p; + p += lsize; + } +} + diff --git a/engine/src/Libraries/2D/Source/tluctab.h b/engine/src/Libraries/2D/Source/tluctab.h new file mode 100644 index 0000000..cdf3636 --- /dev/null +++ b/engine/src/Libraries/2D/Source/tluctab.h @@ -0,0 +1,90 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/tluctab.h $ + * $Revision: 1.9 $ + * $Author: baf $ + * $Date: 1994/01/31 14:46:54 $ + * + * Declarations and macros for translucency + * table generation. + * + * This file is part of the 2d library. + * + * $Log: tluctab.h $ + * Revision 1.9 1994/01/31 14:46:54 baf + * Fixed a typo. + * + * Revision 1.8 1994/01/17 22:13:22 baf + * Redid tluc8 spolys (again). + * + * Revision 1.7 1994/01/14 12:41:13 baf + * Lit translucency reform. + * + * Revision 1.6 1993/12/09 17:39:36 baf + * Added lighting + * + * Revision 1.5 1993/12/01 21:20:50 baf + * Added gr_set_tluc8_table. + * + * + */ + +#ifndef _TLUCTAB +#define _TLUCTAB + +#include "fix.h" +#include "grs.h" +#include "grmalloc.h" +#include "tlucdat.h" + +extern uchar *gr_init_translucency_table(uchar *p, fix opacity, fix purity, grs_rgb color); +extern uchar *gr_init_lit_translucency_table(uchar *p, fix opacity, fix purity, grs_rgb color, grs_rgb light); +extern uchar *gr_init_lit_translucency_tables(uchar *p, fix opacity, fix purity, grs_rgb color, int n); + +extern int gr_dump_tluc8_table(uchar *buf, int nlit); +extern void gr_read_tluc8_table(uchar *buf); + +#define gr_alloc_translucency_table(n) \ + ((uchar *)malloc(n*256)) +#define gr_free_translucency_table(tab) (free(tab)) + +#define gr_make_translucency_table(op, pu, co) \ + (gr_init_translucency_table(gr_alloc_translucency_table(1), op, pu, co)) +#define gr_make_lit_translucency_table(op, pu, co, li) \ + (gr_init_translucency_table(gr_alloc_translucency_table(1), op, pu, co, li)) +#define gr_make_lit_translucency_tables(op, pu, co, lnum) \ + (gr_init_lit_translucency_tables(gr_alloc_translucency_table(lnum), op, pu, co, lnum)) + +#define gr_make_tluc8_table(num, op, pu, co) \ + (tluc8tab[num]=gr_make_translucency_table(op, pu, co)) +#define gr_make_lit_tluc8_table(num, op, pu, co, li) \ + (tluc8ltab[num]=gr_make_lit_translucency_tables(op, pu, co, li), \ + gr_make_tluc8_table(num, op, pu, co)) +#define gr_alloc_tluc8_spoly_table(num) \ + (tluc8nstab=num, tluc8stab=gr_alloc_translucency_table(num)) +#define gr_init_tluc8_spoly_table(num, op, pu, co, li) \ + (gr_init_lit_translucency_table(tluc8stab+(256*num), op, pu, co, li)) +#define gr_init_tluc8_spoly_tables(num, op, pu, co, li) \ + (gr_init_lit_translucency_tables(tluc8stab+(256*num), op, pu, co, li)) +#define gr_bind_tluc8_table(num, p) (tluc8tab[num]=p) +#define gr_bind_lit_tluc8_table(num, p) (tluc8ltab[num]=p) +#define gr_bind_tluc8_spoly_table(p) (tluc8stab=p) + +#endif diff --git a/engine/src/Libraries/2D/Source/tmapfcn.h b/engine/src/Libraries/2D/Source/tmapfcn.h new file mode 100644 index 0000000..332f9ed --- /dev/null +++ b/engine/src/Libraries/2D/Source/tmapfcn.h @@ -0,0 +1,45 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/tmapfcn.h $ + * $Revision: 1.13 $ + * $Author: kevin $ + * $Date: 1994/07/29 12:02:37 $ + * + * Public texture mapping procedures. + * + * This file is part of the 2d library. + * + */ + +#ifndef __TMAPFCN_H +#define __TMAPFCN_H + +#include "grs.h" +#include "plytyp.h" +#include "tmaps.h" + +extern void per_umap(grs_bitmap *bm, int n, grs_vertex **vpl, grs_tmap_info *ti); +extern void h_umap(grs_bitmap *bm, int n, grs_vertex **vpl, grs_tmap_info *ti); +extern void v_umap(grs_bitmap *bm, int n, grs_vertex **vpl, grs_tmap_info *ti); +extern int per_map(grs_bitmap *bm, int n, grs_vertex **vpl, grs_tmap_info *ti); +extern int h_map(grs_bitmap *bm, int n, grs_vertex **vpl, grs_tmap_info *ti); +extern int v_map(grs_bitmap *bm, int n, grs_vertex **vpl, grs_tmap_info *ti); + +#endif /* __TMAPFCN_H */ diff --git a/engine/src/Libraries/2D/Source/tmapint.h b/engine/src/Libraries/2D/Source/tmapint.h new file mode 100644 index 0000000..49446a1 --- /dev/null +++ b/engine/src/Libraries/2D/Source/tmapint.h @@ -0,0 +1,152 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/tmapint.h $ + * $Revision: 1.10 $ + * $Author: kevin $ + * $Date: 1994/08/24 18:45:56 $ + * + * texture mapping internal data structures. + * + * This file is part of the 2d library. + * + * $Log: tmapint.h $ + * Revision 1.10 1994/08/24 18:45:56 kevin + * Added scanline func field. + * + * Revision 1.9 1994/07/26 00:22:23 kevin + * imbedded entire grs_bitmap structure into grs_tmap_loop_info. + * + * Revision 1.8 1994/07/18 17:09:59 kevin + * Eliminated per_setup structure (superceded by grs_per_setup in pertyp.h). + * Changed tmap_edge_info and tmap_loop_info structures to reduce size. + * Added new aliases in same. + * + * Revision 1.7 1994/06/17 10:39:15 kevin + * Changed redefinitions of fix_ceil and fix_cint so that they don't break. + * + * Revision 1.6 1994/06/03 20:33:27 kevin + * Added l3d pointer to per_setup struct so it can be freed in the right order. + * + * Revision 1.5 1994/02/26 22:45:41 kevin + * made p_src_off signed to enable proper storage of negative values for v. + * + * Revision 1.4 1994/02/09 23:27:16 kevin + * Changed grs_loop_info structure for use with new wacky edges. + * + * Revision 1.3 1994/01/18 13:11:26 kevin + * Added optimized ulong_min,max pragmas. Also sides for edge calculations. + * + * Revision 1.2 1994/01/13 12:21:10 kevin + * changed tmap_inner_loop prototype to take flags parameter. + * + * Revision 1.1 1994/01/03 22:03:06 kevin + * Initial revision + * + * +*/ + +#ifndef __TMAPINT_H +#define __TMAPINT_H + +// I GIVE UP! I'm going to define the offsets for all the stupid grs_tmap_loop_info entries!!!!!! - MLA +#define T_X 0x04 +#define T_Y 0x04 +#define HLog 0x17 +#define T_W 0x18 +#define T_DW 0x64 + +#define LeftX 0x1c +#define LeftY 0x20 +#define LeftU 0x24 +#define LeftV 0x28 +#define LeftI 0x2C +#define LeftDX 0x30 +#define LeftDY 0x30 +#define LeftDU 0x34 +#define LeftDV 0x38 +#define LeftDI 0x3C + +#define RightX 0x40 +#define RightY 0x44 +#define RightU 0x48 +#define RightV 0x4C +#define RightI 0x50 +#define RightDX 0x54 +#define RightDY 0x54 +#define RightDU 0x58 +#define RightDV 0x5C +#define RightDI 0x60 + +#include "fix.h" +#include "grs.h" +#include "plytyp.h" + +typedef struct { + fix x,y,u,v,i; + union {fix dx,dy;}; + fix du,dv,di; +} grs_tmap_edge; + +typedef struct { + int n; /* number of lines */ + union { /* destination pointer/scanline coord */ + uchar *d; + int x,y; + }; + union { + grs_bitmap bm; + struct { + uchar *s; /* bitmap bits pointer */ + uchar bm_type,bm_align; + short bm_flags,bm_w,bm_h,bm_row; /* bitmap width & height */ + uchar wlog; + union {uchar hlog,loop;}; + }; + }; + fix w; + /* edge data */ + union {grs_tmap_edge left,top;}; + union {grs_tmap_edge right,bot;}; + fix dw; + uint32_t u_mask; + union {uint32_t v_mask,mask;}; + union { + uchar *clut; /* color lookup table */ + uchar solid; + }; + int32_t *vtab; /* for non power of 2 widths */ + void (*scanline_func)(); /* function for individual scanline */ + void (*loop_func)(); /* actually, chunk function */ + union {void (*left_edge_func)(), (*top_edge_func)();}; + union {void (*right_edge_func)(),(*bot_edge_func)();}; +} grs_tmap_loop_info; + +#define TMS_RIGHT 0 +#define TMS_LEFT 1 +#define TMS_BOT 0 +#define TMS_TOP 1 + +#define sgn(a) (((a)>0) ? 1 : ((a)<0) ? -1 : 0) + +#define fix_light(i) ((i>>8)&0xff00) + +#endif /* !__TMAPINT_H */ + + diff --git a/engine/src/Libraries/2D/Source/tmaps.h b/engine/src/Libraries/2D/Source/tmaps.h new file mode 100644 index 0000000..b1c3f82 --- /dev/null +++ b/engine/src/Libraries/2D/Source/tmaps.h @@ -0,0 +1,58 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/tmaps.h $ + * $Revision: 1.2 $ + * $Author: kevin $ + * $Date: 1994/02/26 22:44:02 $ + * + * Texture mapping public structures. + * + * This file is part of the 2d library. + * + * $Log: tmaps.h $ + * Revision 1.2 1994/02/26 22:44:02 kevin + * New stuff for new sloppy linear mappers. + * + * Revision 1.1 1994/01/03 22:03:36 kevin + * Initial revision + * + * +*/ + +/* Perspective mapper context structure. */ +#include "GR/grs.h" + +#ifndef __TMAPS_H +#define __TMAPS_H + +typedef struct { + short tmap_type; + short flags; + uchar *clut; +} grs_tmap_info; + +#define TMF_PER 1 +#define TMF_CLUT 2 +#define TMF_FLOOR 4 +#define TMF_WALL 4 + +#endif /* !__TMAPS_H */ + + diff --git a/engine/src/Libraries/2D/Source/tmaptab.h b/engine/src/Libraries/2D/Source/tmaptab.h new file mode 100644 index 0000000..5fdabb5 --- /dev/null +++ b/engine/src/Libraries/2D/Source/tmaptab.h @@ -0,0 +1,42 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/tmaptab.h $ + * $Revision: 1.5 $ + * $Author: kevin $ + * $Date: 1994/08/16 15:39:59 $ + * + * Declarations for texture mapping inner loop + * table list globals and lists. + * + * This file is part of the 2d library. + * +*/ + +#ifndef __TMAPTAB_H +#define __TMAPTAB_H + +#include "tabdat.h" +#define grd_tmap_init_table grd_function_table +#define grd_tmap_hscan_init_table grd_tmap_init_table +#define grd_tmap_vscan_init_table (grd_tmap_init_table+6) + +#endif /* !__TMAPTAB_H */ + + diff --git a/engine/src/Libraries/2D/Source/valloc.c b/engine/src/Libraries/2D/Source/valloc.c new file mode 100644 index 0000000..61ce616 --- /dev/null +++ b/engine/src/Libraries/2D/Source/valloc.c @@ -0,0 +1,74 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/valloc.c $ + * $Revision: 1.7 $ + * $Author: kaboom $ + * $Date: 1993/10/08 01:16:32 $ + * + * Video memory management routines. + * + * This file is part of the 2d library. + * + * $Log: valloc.c $ + * Revision 1.7 1993/10/08 01:16:32 kaboom + * Changed quotes in #include liness to angle brackets for Watcom problem. + * + * Revision 1.6 1993/05/03 15:08:52 kaboom + * Removed extraneous #include lines. + * + * Revision 1.5 1993/04/30 17:52:13 kaboom + * Added grd_valloc_mode. Pared down vblock structure. + * + * Revision 1.4 1993/02/04 17:47:14 kaboom + * Changed includes. + * + * Revision 1.3 1993/01/07 21:12:50 kaboom + * Put in SVGA hack which should be fixed. + */ + +#include "grs.h" +#include "grd.h" + +// MLA- took the v_table out, it doesn't appear to be referenced anymore +#if 0 +#define VTAB_SIZE 100 + +typedef struct { + uchar *p; /* pointer to block. */ + long size; /* size of block (address). */ +} v_block; + +v_block v_table[VTAB_SIZE]; +#endif + +// globals +uchar grd_valloc_mode = 0; + +uchar *our_valloc (short w, short h) +{ + if (grd_valloc_mode) + return (uchar *)0; + else + return (uchar *)grd_cap->vbase; +} + +void vfree (uchar *p) +{ +} diff --git a/engine/src/Libraries/2D/Source/valloc.h b/engine/src/Libraries/2D/Source/valloc.h new file mode 100644 index 0000000..5266094 --- /dev/null +++ b/engine/src/Libraries/2D/Source/valloc.h @@ -0,0 +1,42 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/valloc.h $ + * $Revision: 1.2 $ + * $Author: kaboom $ + * $Date: 1993/05/03 14:01:59 $ + * + * Declarations for video memory manager. + * + * This file is part of the 2d library. + * + * $Log: valloc.h $ + * Revision 1.2 1993/05/03 14:01:59 kaboom + * Added declaration for grd_valloc_mode. + * + * Revision 1.1 1993/02/22 20:35:27 kaboom + * Initial revision + */ + +#ifndef __VALLOC_H +#define __VALLOC_H +extern uchar grd_valloc_mode; +extern uchar *our_valloc (short w, short h); +extern void vfree (uchar *p); +#endif /* !__VALLOC_H */ diff --git a/engine/src/Libraries/2D/Source/vesa.h b/engine/src/Libraries/2D/Source/vesa.h new file mode 100644 index 0000000..b09f4cb --- /dev/null +++ b/engine/src/Libraries/2D/Source/vesa.h @@ -0,0 +1,48 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/2d/RCS/vesa.h $ + * $Revision: 1.2 $ + * $Author: kaboom $ + * $Date: 1993/05/03 14:05:06 $ + * + * VESA-related data structures. + * + * This file is part of the 2d library. + * + * $Log: vesa.h $ + * Revision 1.2 1993/05/03 14:05:06 kaboom + * Took out vesa_mode structure and vesa_get_mode and vesa_set_bank declarations. + * Added declarations for vesa_get_info() and vesa_get_gran(). + * + * Revision 1.1 1993/01/07 21:11:57 kaboom + * Initial revision + */ + +#ifndef __VESA_H +#define __VESA_H +#define VESA_GET_CARD_INFO 0x4f00 +#define VESA_GET_MODE_INFO 0x4f01 +#define VESA_SET_MODE 0x4f02 +#define VESA_GET_MODE 0x4f03 + +/* VESA support routines prototypes. */ +extern int vesa_get_info (grs_sys_info *info); +extern int vesa_get_gran (int mode); +#endif /* !__VESA_H */ diff --git a/engine/src/Libraries/2D/Source/vtab.c b/engine/src/Libraries/2D/Source/vtab.c new file mode 100644 index 0000000..69c69ca --- /dev/null +++ b/engine/src/Libraries/2D/Source/vtab.c @@ -0,0 +1,56 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// +// $Source: r:/prj/lib/src/2d/RCS/vtab.c $ +// $Revision: 1.1 $ +// $Author: kevin $ +// $Date: 1994/07/28 01:23:36 $ +// +// Procedure to create temporary vtab. +// +// This file is part of the 2d library. +// + +#include "grs.h" +#include "buffer.h" + + +// build a table of line starts for the bitmap parameter +int32_t *gr_make_vtab (grs_bitmap *bm) + { + void *mem; + int32_t *dest; + int32_t i,add,row; + int32_t maxh; + + mem = gr_alloc_temp(bm->h * sizeof(int32_t)); + row = bm->row; + add = 0L; + maxh = bm->h; + dest = (int32_t *) mem; + + for (i=0; i. + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/vtab.h $ + * $Revision: 1.1 $ + * $Author: kevin $ + * $Date: 1994/07/28 01:24:07 $ + * + * Prototypes for 2d temporary vtab creation. + * + * This file is part of the 2d library. + * + * $Log: vtab.h $ + * Revision 1.1 1994/07/28 01:24:07 kevin + * Initial revision + * + * Revision 1.1 1993/02/22 14:33:56 kaboom + * Initial revision + * + */ + +#ifndef __VTAB_H +#define __VTAB_H + +extern int32_t *gr_make_vtab (grs_bitmap *bm); + +#endif diff --git a/engine/src/Libraries/2D/Source/wire.h b/engine/src/Libraries/2D/Source/wire.h new file mode 100644 index 0000000..ae43a1f --- /dev/null +++ b/engine/src/Libraries/2D/Source/wire.h @@ -0,0 +1,48 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/2d/RCS/wire.h $ + * $Revision: 1.3 $ + * $Author: kevin $ + * $Date: 1994/08/04 09:46:20 $ + * + * Declarations for wire frame primitives. + * + * This file is part of the 2d library. + * + * $Log: wire.h $ + * Revision 1.3 1994/08/04 09:46:20 kevin + * Added new wire poly line functionality. + * + * Revision 1.2 1993/10/19 10:31:09 kaboom + * Updated arguments to be the same as solid polygon. + * + * Revision 1.1 1993/06/02 16:35:23 kaboom + * Initial revision + */ + +#ifndef __WIRE_H +#define __WIRE_H +#include "plytyp.h" + +extern void gr_wire_upoly(long c,int n,grs_vertex **vpl); +extern void gr_wire_poly(long c,int n,grs_vertex **vpl); +extern void gr_wire_ucpoly(int n,grs_vertex **vpl); +extern void gr_wire_cpoly(int n,grs_vertex **vpl); +#endif /* __WIRE_H */ diff --git a/engine/src/Libraries/3D/Source/3d.h b/engine/src/Libraries/3D/Source/3d.h new file mode 100644 index 0000000..c331aeb --- /dev/null +++ b/engine/src/Libraries/3D/Source/3d.h @@ -0,0 +1,1021 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/3d/RCS/3d.h $ + * $Revision: 1.35 $ + * $Author: jaemz $ + * $Date: 1994/09/20 13:21:45 $ + * + * Header file for LookingGlass 3D library + * + * $Log: 3d.h $ + + *------------------------------------------------------* + * MAC VERSION NOTES + * + * All of the #pragma aux statements have been removed, because they only + * specify the register order parameters are passed into routines. Since + * all this stuff has to work on both 68K and PowerPC machines, we have + * to use C calling conventions almost everwhere. + * + *------------------------------------------------------* + + * Revision 1.35 1994/09/20 13:21:45 jaemz + * Added lighting features, took out old call interpret_object + * + * Revision 1.33 1994/08/28 14:48:05 kevin + * Added linear mapping alternative for detail level stuff. + * + * Revision 1.32 1994/08/18 03:45:08 jaemz + * Added call for g3_object_scale + * + * Revision 1.31 1994/07/21 00:28:25 jaemz + * Added stereo declares + * + * Revision 1.29 1994/06/08 21:20:12 jaemz + * Commented transform matrix + * + * Revision 1.28 1994/06/01 15:57:22 jaemz + * Added a "*" to g3_compute_normal + * + * Revision 1.27 1994/05/31 16:38:36 jaemz + * Added documenting comment to .h file about matrices being + * column based + * + * Revision 1.26 1994/05/19 09:47:42 kevin + * g3_light(draw)_t(l,floor_,wall_)map now use watcom register passing + conventions. + * + * Revision 1.25 1994/05/02 23:39:01 kevin + * Added prototypes for wall and floor map procedures. + * + * Revision 1.24 1994/02/08 20:48:06 kaboom + * Added translucent polygon prototypes\pragmas. + * + * Revision 1.23 1993/12/15 01:52:17 dc + * alloc_list + * + * Revision 1.22 1993/12/14 22:58:04 kevin + * Added declarations for biasx,biasy, and perspective mapper context + manipulation routines. + * + * Revision 1.21 1993/12/11 04:03:12 kevin + * Added declarations for g3_rotate_grad and _norm. + * + * Revision 1.20 1993/12/06 15:51:25 unknown + * c:\app\star24\vmode 132x44 uv mappers to take a phandle *, as they do + * + * Revision 1.19 1993/12/04 17:00:02 kaboom + * Added declarations for bitmap lighters. + * + * Revision 1.18 1993/11/07 09:02:15 dc + * support for replace_add_delta along axis + * + * Revision 1.17 1993/10/22 09:37:11 kaboom + * Added new prototypes and pragmas for linear map routines. + * + * Revision 1.16 1993/10/02 11:01:36 kaboom + * Changed names of clip_{line,polygon} to g3_clip_{line,polygon} to avoid + * name collisions. + * + * Revision 1.15 1993/10/02 09:11:30 kaboom + * Added declarations for scrw,scrh. Added prototypes&pragmas for new + * clipping routines and g3_{draw,light}_tmap. New inline for g3_set_vcolor. + * + * Revision 1.14 1993/08/11 15:02:42 kaboom + * Added prototypes and pragmas for lighting texture mappers. + * + * Revision 1.13 1993/08/04 00:48:39 dc + * move interpreter zaniness to separate file + * + * Revision 1.12 1993/07/08 23:36:08 kaboom + * Added prototypes for g3_bitmap(), g3_anchor_bitmap(), and + * g3_set_bitmap_scale(). + * + * Revision 1.11 1993/06/30 11:20:01 spaz + * Added prototypes for g3_draw_cline, g3_draw_sline + * + * Revision 1.10 1993/06/18 15:47:24 kaboom + * Updated prototype for g3_project_point to reflect return value. + * + * Revision 1.9 1993/06/09 04:23:46 kaboom + * Changed prototype and comment for g3_draw_tmap_tile to reflect new usage. + * + * Revision 1.8 1993/06/04 16:53:29 matt + * Added hacks to get around c++ non-support of anonymous structures in unions. + * + * Revision 1.7 1993/05/24 15:51:04 matt + * Added g3_get_slew_step(), removed g3_draw_smooth_horizon(), changed a few + * comments. + * + * Revision 1.6 1993/05/21 16:09:17 matt + * Added new way to specifiy axis orientation, which may be more intuitive + * + * Revision 1.5 1993/05/13 12:17:08 matt + * Added new function, g3_check_codes() + * + * Revision 1.4 1993/05/11 15:23:49 matt + * Changed comment for g3_draw_tmap_tile() to reflect new functionality + * + * Revision 1.3 1993/05/11 14:57:53 matt + * Changed g3_vec_scale() to takes seperate dest & src. + * Added g3_get_view_pyramid(). + * + * Revision 1.2 1993/05/10 13:25:14 matt + * Added the ability to access the elements of a vector as an array. Fields + * x,y,z can be accessed as xyz[0..2]. + * + * Revision 1.1 1993/05/04 17:39:56 matt + * Initial revision + */ + +#ifndef __3D_H +#define __3D_H + +#include + +#include "2d.h" +#include "fix.h" + +#pragma pack(push,2) + +// MLA defines +#define SwapFix(x, y) \ + { \ + fix temp = (x); \ + (x) = (y); \ + (y) = temp; \ + } +#define vm1 view_matrix.m1 +#define vm2 view_matrix.m2 +#define vm3 view_matrix.m3 +#define vm4 view_matrix.m4 +#define vm5 view_matrix.m5 +#define vm6 view_matrix.m6 +#define vm7 view_matrix.m7 +#define vm8 view_matrix.m8 +#define vm9 view_matrix.m9 + +#define uvm1 unscaled_matrix.m1 +#define uvm2 unscaled_matrix.m2 +#define uvm3 unscaled_matrix.m3 +#define uvm4 unscaled_matrix.m4 +#define uvm5 unscaled_matrix.m5 +#define uvm6 unscaled_matrix.m6 +#define uvm7 unscaled_matrix.m7 +#define uvm8 unscaled_matrix.m8 +#define uvm9 unscaled_matrix.m9 + +// Defines for stuff strewn all about the world +// #define stereo_on 1 + +#define f1_0 fix_make(1, 0) +#define f0_5 fix_make(0, 0x8000) +#define f0_25 fix_make(0, 0x4000) + +// gets the next available pnt in reg. +#define getpnt(res) \ + { \ + g3s_point *scratch; \ + if ((res = first_free)) { \ + scratch = res->next; \ + first_free = scratch; \ + } \ + } + +// frees the point in the specified register. uses ebx as scratch +#define freepnt(src) \ + { \ + g3s_point *scratch = first_free; \ + src->next = scratch; \ + first_free = src; \ + } + +// FIXME Move to FIX +// new types: +typedef short sfix; + +#define sfix_make(a, b) ((((short)(a)) << 8) | (b)) +#define fix_from_sfix(a) (((fix)(a)) << 8) + +#define g3_set_i(pnt, ii) \ + do { \ + pnt->i = sfix_make(ii, 0); \ + pnt->p3_flags |= PF_I; \ + } while (0); +#define g3_set_rgb(pnt, r, g, b) \ + do { \ + pnt->rgb = gr_bind_rgb(r, g, b); \ + pnt->p3_flags |= PF_RGB; \ + } while (0); + +// constants + +// these are for rotation order when generating a matrix from angles +// bit 2 means y before z if 0, 1 means x before z if 0, 0 means x before y if 0 + +#define ORDER_XYZ 0 // 000 +#define ORDER_YXZ 1 // 001 +#define ORDER_YZX 3 // 011 +#define ORDER_XZY 4 // 100 +#define ORDER_ZXY 6 // 110 +#define ORDER_ZYX 7 // 111 + +// To specify user's coordinate system: use one of these for each user_x_axis, +// user_y_axis,& user_z_axis in g3_init to tell the 3d what your x,y,& z mean. + +#define AXIS_RIGHT 1 +#define AXIS_UP 2 +#define AXIS_IN 3 +#define AXIS_LEFT -AXIS_RIGHT +#define AXIS_DOWN -AXIS_UP +#define AXIS_OUT -AXIS_IN + +// vectors, points, matrices + +// NIGHTMARE CODE!!!!!! ANONYMOUS UNIONS ARE BAAAAAAAD THINGS MAN +// MLA - left in the #ifdef so the C++ compiler handles it, we just have to +// change references of g3s_vector.x to g3s_vector.xyz[0] (or .gX, .gY, etc. +// from the #defines) +typedef struct g3s_vector { +#if 0 // MLA #ifdef __cplusplus + fix x,y,z; +#else + union { + struct { + fix x, y, z; + }; + fix xyz[3]; + }; +#endif +} g3s_vector; + +#define gX xyz[0] +#define gY xyz[1] +#define gZ xyz[2] + +typedef struct g3s_angvec { + fixang tx, ty, tz; +} g3s_angvec; + +// This transformation matrix is row based, ie +//|m1 m2 m3| |x| |x'| +//|m4 m5 m6| * |y| = |y'| +//|m7 m8 m9| |z| |z'| +// +// but of course the incoming y coordinates +// are inverted, have a nice day +typedef struct g3s_matrix { + fix m1, m2, m3, m4, m5, m6, m7, m8, m9; +} g3s_matrix; + +// typedef short g3s_phandle; //used to refer to points, equal to pntnum * 4 +typedef struct g3s_point *g3s_phandle; + +typedef struct g3s_point { +#if 0 // #ifdef __cplusplus + fix x,y,z; +#else + union { // rotated 3d coords, use as vector or elements + g3s_vector vec; + struct { + fix x, y, z; + }; + fix xyz[3]; + g3s_phandle next; // next in free list, when point is unused + }; +#endif + + fix sx, sy; // screen coords + ubyte codes; // clip codes + ubyte p3_flags; // misc flags +#if 0 // #ifdef __cplusplus + sfix u,v; +#else + union { + struct { + sfix u, v; + } uv; // for texturing, etc. + grs_rgb rgb; // for RGB-space gouraud shading + }; +#endif + sfix i; // gouraud shading & lighting +} g3s_point; + +// clip codes +#define CC_OFF_LEFT 1 +#define CC_OFF_RIGHT 2 +#define CC_OFF_BOT 4 +#define CC_OFF_TOP 8 +#define CC_BEHIND 128 +#define CC_CLIP_OVERFLOW 16 + +// flags for the point structure +#define PF_U 1 // is u value used? +#define PF_V 2 // is v value used? +#define PF_I 4 // is i value used? +#define PF_PROJECTED 8 // has this point been projected? +#define PF_RGB 16 // are the RBG values used? +#define PF_CLIPPNT 32 // this point created by clipper +#define PF_LIT 64 // has this point been lit by the lighter? + +// lighting codes +#define LT_NONE 0 +#define LT_SOLID 1 +#define LT_DIFF 2 +#define LT_SPEC 4 +#define LT_GOUR 128 + +#define LT_NEAR_LIGHT 16 // TRUE if light is near and has to be evaluated +#define LT_NEAR_VIEW 8 // TRUE if viewing point is near, and has to be reevaled +#define LT_LOC_LIGHT 32 // TRUE if light is a local point, not a vector +#define LT_TABSIZE 24 // size of the shading table +#define LT_BASELIT 15 // table entry of normal intensity (before saturating) + +extern fix scrw, scrh; +extern fix biasx, biasy; + +extern ubyte g3d_light_type; +extern fix g3d_amb_light, g3d_diff_light, g3d_spec_light; +extern fix g3d_ldotv, g3d_sdotl, g3d_sdotv, g3d_flash; +extern ubyte *g3d_light_tab; +extern g3s_vector g3d_light_src, g3d_light_trans; +extern g3s_vector g3d_light_src, g3d_light_trans; +extern g3s_vector g3d_view_vec, g3d_light_vec; + +// DG: my compiler was not happy about the names "or" and "and", so I appended a +// _ +typedef struct g3s_codes { + byte or_; + byte and_; +} g3s_codes; + +/* + * We're going to want a bunch of general-purpose 3d vector math + * routines. These are: + * + * Question: some of these take a destination, and some change the + * vector passed to them. Should we adopt a consistent interface? + * + * There's sure to be more of these + * + */ +void g3_vec_sub(g3s_vector *dest, g3s_vector *src1, g3s_vector *src2); +void g3_vec_add(g3s_vector *dest, g3s_vector *src1, g3s_vector *src2); + +fix g3_vec_mag(g3s_vector *v); +void g3_vec_scale(g3s_vector *dest, g3s_vector *src, fix s); +void g3_vec_normalize(g3s_vector *v); + +void g3_compute_normal(g3s_vector *norm, g3s_vector *v0, g3s_vector *v1, g3s_vector *v2); + +fix g3_vec_dotprod(g3s_vector *v0, g3s_vector *v1); + +void g3_vec_rotate(g3s_vector *dest, g3s_vector *src, g3s_matrix *m); +// src and dest can be the same + +void g3_transpose(g3s_matrix *m); // transpose in place +void g3_copy_transpose(g3s_matrix *dest, g3s_matrix *src); // copy and transpose +void g3_matrix_x_matrix(g3s_matrix *dest, g3s_matrix *src1, g3s_matrix *src2); + +int g3_clip_line(g3s_point *src[], g3s_point *dest[]); +// MLA #pragma aux g3_clip_line parm [esi] [edi] modify [eax ebx ecx edx esi +// edi]; +int g3_clip_polygon(int n, g3s_point *src[], g3s_point *dest[]); +// MLA #pragma aux g3_clip_polygon parm [ecx] [esi] [edi] modify [eax ebx ecx +// edx esi edi]; + +/* + * Graphics-specific 3d routines + * + */ + +// stereo functions +short g3_init_stereo(short max_points, int user_x_axis, int user_y_axis, int user_z_axis); +// sets up system for stereo by allocating twice as many +// points, setting g3d_stereo_base to the amount of memory +// the points take up + +void g3_start_stereo_frame(grs_canvas *rt); +// mark all points as being unused, sets g3d_stereo to true +// sets g3d_rt_canv to rt so you know where to render to + +void g3_set_eyesep(fix sep); +// sets g3d_eyesep_raw to sep + +// System inialization, etc. + +short g3_init(short max_points, int user_x_axis, int user_y_axis, int user_z_axis); +// the three axis vars describe your coordintate system. Use the constants +// X_AXIS,Y_AXIS,Z_AXIS, or negative of these, to describe what your +// coordinates mean. For each of width_,height_, and depth_axis, specify +// which of your axes goes in that dimension. Depth is into the screen, +// height is up, and width is to the right +// returns number actually allocated + +void g3_shutdown(void); // frees allocated points, and whatever else +int g3_count_free_points(void); + +// Point definition and manipulation + +g3s_phandle g3_alloc_point(void); +// returns a free point + +int g3_alloc_list(int n, g3s_phandle *p); +// allocates n points into p, returns 0 for none, or n for ok + +g3s_phandle g3_rotate_point(g3s_vector *v); +// translate, rotate, and code point in 3-space. returns point handle + +g3s_phandle g3_rotate_norm(g3s_vector *v); +// rotate normal in 3-space. returns point handle. + +// g3s_phandle g3_rotate_grad(g3s_vector *v); +// MLA - defined as same routine in POINT.C +#define g3_rotate_grad g3_rotate_norm +// rotate gradient in 3-space. returns point handle. + +g3s_phandle g3_rotate_light_norm(g3s_vector *v); +// rotate light norm from obj space into viewer space + +int g3_project_point(g3s_phandle p); +// project already-rotated point. returns true if z>0 + +g3s_phandle g3_transform_point(g3s_vector *v); +// translate, rotate, code, and project point (rotate_point + project_point); +// returns point handle + +void g3_rotate_delta_v(g3s_vector *dest, g3s_vector *src); +// rotates a delta - takes vector input, fills in vector + +void g3_rotate_delta_x(g3s_vector *dest, fix dx); +void g3_rotate_delta_y(g3s_vector *dest, fix dy); +void g3_rotate_delta_z(g3s_vector *dest, fix dz); +void g3_rotate_delta_xz(g3s_vector *dest, fix dx, fix dz); +void g3_rotate_delta_yz(g3s_vector *dest, fix dy, fix dz); +void g3_rotate_delta_xy(g3s_vector *dest, fix dx, fix dy); +void g3_rotate_delta_xyz(g3s_vector *dest, fix dx, fix dy, fix dz); +// rotate a deltas - take just the spefified values + +void g3_add_delta_v(g3s_phandle p, g3s_vector *delta); +void g3_add_delta_x(g3s_phandle p, fix dx); +void g3_add_delta_y(g3s_phandle p, fix dy); +void g3_add_delta_z(g3s_phandle p, fix dz); +void g3_add_delta_xy(g3s_phandle p, fix dx, fix dy); +void g3_add_delta_xz(g3s_phandle p, fix dx, fix dz); +void g3_add_delta_yz(g3s_phandle p, fix dy, fix dz); +void g3_add_delta_xyz(g3s_phandle p, fix dx, fix dy, fix dz); +// adds a delta to a point + +g3s_phandle g3_copy_add_delta_v(g3s_phandle src, g3s_vector *delta); +g3s_phandle g3_copy_add_delta_x(g3s_phandle src, fix dx); +g3s_phandle g3_copy_add_delta_y(g3s_phandle src, fix dy); +g3s_phandle g3_copy_add_delta_z(g3s_phandle src, fix dz); +g3s_phandle g3_copy_add_delta_xy(g3s_phandle src, fix dx, fix dy); +g3s_phandle g3_copy_add_delta_xz(g3s_phandle src, fix dx, fix dz); +g3s_phandle g3_copy_add_delta_yz(g3s_phandle src, fix dy, fix dz); +g3s_phandle g3_copy_add_delta_xyz(g3s_phandle src, fix dx, fix dy, fix dz); +// adds a delta to a point, and stores in a new point + +g3s_phandle g3_replace_add_delta_x(g3s_phandle src, g3s_phandle dst, fix dx); +g3s_phandle g3_replace_add_delta_y(g3s_phandle src, g3s_phandle dst, fix dy); +g3s_phandle g3_replace_add_delta_z(g3s_phandle src, g3s_phandle dst, fix dz); +// adds a delta to src and stores to preallocated point dst + +g3s_phandle g3_dup_point(g3s_phandle p); // makes copy of a point + +void g3_copy_point(g3s_phandle dest, g3s_phandle src); + +// do a whole bunch of points. returns codes and & or +g3s_codes g3_rotate_list(short n, g3s_phandle *dest_list, g3s_vector *v); +g3s_codes g3_transform_list(short n, g3s_phandle *dest_list, g3s_vector *v); +g3s_codes g3_project_list(short n, g3s_phandle *point_list); + +void g3_free_point(g3s_phandle p); // adds to free list +void g3_free_list(int n_points, g3s_phandle *p); // adds to free list + +// Frame setup commands + +void g3_start_frame(void); // mark all points as unused +void g3_set_view_matrix(g3s_vector *pos, g3s_matrix *m, fix zoom); +void g3_set_view_angles(g3s_vector *pos, g3s_angvec *angles, int rotation_order, + fix zoom); // takes ptr to angles + +int g3_end_frame(void); // returns number of points lost. thus, 0==no error + +// Lighting commands +void g3_light_diff(g3s_phandle norm, g3s_phandle pos); // takes normal vector + // transformed, dots with + // the light vec, puts + // light val in norm +// MLA #pragma aux g3_light_diff "*" parm [eax] [edx] modify [eax edx ebx ecx +// esi edi]; + +void g3_light_spec(g3s_phandle norm, + g3s_phandle pos); // takes norm and point position, lights point +// MLA #pragma aux g3_light_spec "*" parm [eax] [edx] modify [eax edx ebx ecx +// esi edi]; + +void g3_light_dands(g3s_phandle norm, g3s_phandle pos); // lights with both both +// MLA #pragma aux g3_light_dands "*" parm [eax] [edx] modify [eax edx ebx ecx +// esi edi]; + +// farms out a point based on flags +fix g3_light(g3s_phandle norm, g3s_phandle pos); +// MLA #pragma aux g3_light "*" parm [eax] [edx] modify [eax edx ebx ecx esi +// edi]; + +// generic list gronker, farms these points out +void g3_light_list(int n, g3s_phandle *norm, g3s_phandle *pos); + +// sets a light vector in source space directly +// this light vector has to be in user space so we can dot it with +// other vector. This is either a point source (LT_LOC_LIGHT == TRUE) +// or a vector. +void g3_set_light_src(g3s_vector *l); +// MLA #pragma aux g3_set_light_src "*" parm [eax] modify [esi edi]; + +// note these need to be called *after* you've built a frame or object +// in which you transform points +// evaluates light vector, putting it into light_vec. Do not call this +// after setting an object up. Only call before object frames. Only +// for vector evaluation. Only call for non local lighting. +// When lighting "modes" get set up, this will be called automagically +// when necessary. +void g3_eval_vec_light(void); +// MLA #pragma aux g3_eval_vec_light "*" modify [eax edx ebx ecx esi edi]; + +// transforms light point into viewer coords with all scaling intact +// this prepares it to be subtracted from loc light points in eval_loc +// _light. Call this after start_frame and before an object. +void g3_trans_loc_light(void); +// MLA #pragma aux g3_trans_loc_light "*" modify [eax edx ebx ecx esi edi]; + +// evaluates light point relative to another point, uses light point +// this is only for local lighting, inside an object. If its not local +// lighting, you don't need to call this one. This assumes you'll want +// to set NEAR_LIGHT to zero. +void g3_eval_loc_light(g3s_phandle pos); +// MLA #pragma aux g3_eval_loc_light "*" parm [eax] modify [eax edx ebx ecx esi +// edi]; + +// Evaluates and sets light vectors as necessary at the start of +// an object. Does view vec if SPEC is set. Transforms light +// vector or point depending how LOC_LIGHT is set. Use this if +// both light and view will be modelled as far. In fact, make +// sure both are set as far, or you will be sorry. +// Evaluates at the object center. If necessary, evaluates the +// ldotv for light and view +void g3_eval_light_obj_cen(void); +// MLA #pragma aux g3_eval_light_obj_cen "*" modify [eax edx ebx ecx esi edi]; + +// evaluates the light vector straight ahead pointing in +// at you, then evaluates ldotv as well. Use after light vec +// has been evaluated. Only need for specular +void g3_eval_view_ahead(void); +// MLA #pragma aux g3_eval_view_ahead "*" modify [eax edx ebx ecx esi edi]; + +// takes the dot product of view and light, for specular light +void g3_eval_ldotv(void); +// MLA #pragma aux g3_eval_ldotv "*" modify [eax edx ebx ecx esi edi]; + +// evaluate the view vector relative to a point +void g3_eval_view(g3s_phandle pos); +// MLA #pragma aux g3_eval_view "*" parm [eax] modify [eax edx ebx ecx esi edi]; + +// Horizon + +void g3_draw_horizon(int sky_color, int ground_color); + +// Misc commands + +g3s_codes g3_check_codes(int n_verts, g3s_phandle *p); +// returns codes_and & codes_or of points + +bool g3_check_normal_facing(g3s_vector *v, g3s_vector *normal); +// takes surface normal and unrotated point on poly. normal need not +// be normalized + +bool g3_check_poly_facing(g3s_phandle p0, g3s_phandle p1, g3s_phandle p2); +// takes 3 rotated points on poly + +void g3_get_FOV(fixang *x, fixang *y); +// fills in field of view across width and height of screen + +fix g3_get_zoom(char axis, fixang angle, int window_width, int window_height); +// returns zoom factor to achieve the desired view angle. axis is 'y' or 'x' + +void g3_get_view_pyramid(g3s_vector *corners); +// fills in 4 vectors, which unit vectors from the eye that describe the +// view pyramid. first vector is upper right, then clockwise + +void g3_get_slew_step(fix step_size, g3s_vector *x_step, g3s_vector *y_step, g3s_vector *z_step); +// fills in three vectors, each of length step_size, in the specified +// direction in the viewer's frame of reference. any (or all) of the +// vector pointers can be NULL to skip that axis. + +// Instancing. These all return true if everything ok + +uchar g3_start_object(g3s_vector *p); // position only (no orientation). + +uchar g3_start_object_matrix(g3s_vector *p, g3s_matrix *m); // position and orientation. these can nest + +uchar g3_start_object_angles_v(g3s_vector *p, g3s_angvec *o, + int rotation_order); // position and orientation vector. these can nest +uchar g3_start_object_angles_xyz(g3s_vector *p, fixang tx, fixang ty, fixang tz, int rotation_order); + +uchar g3_start_object_angles_x(g3s_vector *p, fixang tx); +uchar g3_start_object_angles_y(g3s_vector *p, fixang ty); +uchar g3_start_object_angles_z(g3s_vector *p, fixang tz); +uchar g3_start_object_angles_xy(g3s_vector *p, fixang tx, fixang ty, int rotation_order); +uchar g3_start_object_angles_xz(g3s_vector *p, fixang tx, fixang tz, int rotation_order); +uchar g3_start_object_angles_yz(g3s_vector *p, fixang ty, fixang tz, int rotation_order); + +// you can use this to scale things like make small boxes and the like. The +// effect is to shrink or expand the points in their SOURCE coordinate system. +// Only call this after calling one of the start_object routines. You can do it +// within a frame as well, the effect will be to make surrounding space smaller +// or bigger. This will make you shoot towards or away from the origin, +// probably not the effect you're looking for +void g3_scale_object(fix s); + +void g3_end_object(void); + +int code_point(g3s_point *pt); + +// Drawing commands. +// all return 2d clip codes. See 2d header for values + +// note that for the 2 gouraud line drawers, the 3d assumes that the calling +// function has not only set the rgb or i fields of the passed points, but has +// also set the p3_flags byte to indicate which is being used. 'tis a terrible +// hack indeed (Spaz, 6/29) + +int g3_draw_point(g3s_phandle p); +int g3_draw_line(g3s_phandle p0, g3s_phandle p1); +int g3_draw_cline(g3s_phandle p0, g3s_phandle p1); // rgb-space gouraud line +int g3_draw_sline(g3s_phandle p0, g3s_phandle p1); // 2d-intensity gouraud line + +int g3_draw_poly(long c, int n_verts, g3s_phandle *p); +// MLA #pragma aux g3_draw_poly "*" parm [eax] [ecx] [esi] value [eax] modify +// [eax ebx ecx edx esi edi]; +int g3_draw_tluc_poly(long c, int n_verts, g3s_phandle *p); +// MLA #pragma aux g3_draw_tluc_poly "*" parm [eax] [ecx] [esi] value [eax] +// modify [eax ebx ecx edx esi edi]; +int g3_draw_spoly(int n_verts, g3s_phandle *p); // smooth poly +// MLA #pragma aux g3_draw_spoly "*" parm [ecx] [esi] value [eax] modify [eax +// ebx ecx edx esi edi]; +int g3_draw_tluc_spoly(int n_verts, g3s_phandle *p); // smooth poly +// MLA #pragma aux g3_draw_tluc_spoly "*" parm [ecx] [esi] value [eax] modify +// [eax ebx ecx edx esi edi]; +int g3_draw_cpoly(int n_verts, g3s_phandle *p); // RBG-space smooth poly +// MLA #pragma aux g3_draw_cpoly "*" parm [ecx] [esi] value [eax] modify [eax +// ebx ecx edx esi edi]; + +int g3_check_and_draw_poly(long c, int n_verts, g3s_phandle *p); +// MLA #pragma aux g3_check_and_draw_poly "*" parm [eax] [ecx] [esi] value [eax] +// modify [eax ebx ecx edx esi edi]; +int g3_check_and_draw_tluc_poly(long c, int n_verts, g3s_phandle *p); +// MLA #pragma aux g3_check_and_draw_tluc_poly "*" parm [eax] [ecx] [esi] value +// [eax] modify [eax ebx ecx edx esi edi]; +int g3_check_and_draw_spoly(int n_verts, g3s_phandle *p); +// MLA #pragma aux g3_check_and_draw_spoly "*" parm [ecx] [esi] value [eax] +// modify [eax ebx ecx edx esi edi]; +int g3_check_and_draw_tluc_spoly(int n_verts, g3s_phandle *p); +// MLA #pragma aux g3_check_and_draw_tluc_spoly "*" parm [ecx] [esi] value [eax] +// modify [eax ebx ecx edx esi edi]; +int g3_check_and_draw_cpoly(int n_verts, g3s_phandle *p); +// MLA #pragma aux g3_check_and_draw_cpoly "*" parm [ecx] [esi] value [eax] +// modify [eax ebx ecx edx esi edi]; + +// versions of the poly routines which take the args on the stack +int g3_draw_poly_st(int n_verts, ...); +int g3_draw_cpoly_st(int n_verts, ...); // RBG-space smooth poly +int g3_draw_spoly_st(int n_verts, ...); // smooth poly +int g3_check_and_draw_poly_st(int n_verts, ...); +int g3_check_and_draw_cpoly_st(int n_verts, ...); +int g3_check_and_draw_spoly_st(int n_verts, ...); + +grs_vertex **g3_bitmap(grs_bitmap *bm, g3s_phandle p); +// MLA #pragma aux g3_bitmap "*" parm [esi] [edi] modify [eax ebx ecx edx]; +grs_vertex **g3_anchor_bitmap(grs_bitmap *bm, g3s_phandle p, short u_anchor, short v_anchor); +// MLA #pragma aux g3_anchor_bitmap "*" parm [esi] [edi] [eax] [edx] modify [eax +// ebx ecx edx]; +grs_vertex **g3_light_bitmap(grs_bitmap *bm, g3s_phandle p); +// MLA #pragma aux g3_light_bitmap "*" parm [esi] [edi] modify [eax ebx ecx +// edx]; +grs_vertex **g3_light_anchor_bitmap(grs_bitmap *bm, g3s_phandle p, short u_anchor, short v_anchor); +// MLA #pragma aux g3_light_anchor_bitmap "*" parm [esi] [edi] [eax] [edx] +// modify [eax ebx ecx edx]; +grs_vertex **g3_full_light_bitmap(grs_bitmap *bm, grs_vertex **p); +// MLA #pragma aux g3_full_light_bitmap "*" parm [esi] [edi] modify [eax ebx ecx +// edx]; +grs_vertex **g3_full_light_anchor_bitmap(grs_bitmap *bm, grs_vertex **p, short u_anchor, short v_anchor); +// MLA #pragma aux g3_full_light_anchor_bitmap "*" parm [esi] [edi] [eax] [edx] +// modify [eax ebx ecx edx]; + +void g3_set_bitmap_scale(fix u_scale, fix v_scale); +// MLA #pragma aux g3_set_bitmap_scale "*" parm [ebx] [ecx] modify [eax ebx ecx +// edx]; + +int g3_draw_tmap(int n, g3s_phandle *vp, grs_bitmap *bm); +int g3_light_tmap(int n, g3s_phandle *vp, grs_bitmap *bm); +int g3_draw_floor_map(int n, g3s_phandle *vp, grs_bitmap *bm); +int g3_light_floor_map(int n, g3s_phandle *vp, grs_bitmap *bm); +int g3_draw_wall_map(int n, g3s_phandle *vp, grs_bitmap *bm); +int g3_light_wall_map(int n, g3s_phandle *vp, grs_bitmap *bm); +int g3_draw_lmap(int n, g3s_phandle *vp, grs_bitmap *bm); +int g3_light_lmap(int n, g3s_phandle *vp, grs_bitmap *bm); + +#define g3_draw_tmap_quad(vp, bm) g3_draw_tmap_quad_tile(vp, bm, 1, 1) +#define g3_check_and_draw_tmap_quad(vp, bm) g3_check_and_draw_tmap_quad_tile(vp, bm, 1, 1) +// these take four points, which match the four points of the texture map +// corners. The points are clockwise, and the first point is the upper left. + +int g3_draw_tmap_quad_tile(g3s_phandle *vp, grs_bitmap *bm, int width_count, int height_count); +// MLA #pragma aux g3_draw_tmap_quad_tile "*" parm [esi] [edi] [eax] [ebx] value +// [eax] modify [eax ebx ecx edx esi edi]; +int g3_check_and_draw_tmap_quad_tile(g3s_phandle *vp, grs_bitmap *bm, int width_count, int height_count); +// MLA #pragma aux g3_check_and_draw_tmap_quad_tile "*" parm [esi] [edi] [eax] +// [ebx] value [eax] modify [eax ebx ecx edx esi edi]; +// these are like draw_tmap_quad(), but tile the specified number of times +// across and down. +int g3_light_tmap_quad_tile(g3s_phandle *vp, grs_bitmap *bm, int width_count, int height_count); +// MLA #pragma aux g3_light_tmap_quad_tile "*" parm [esi] [edi] [eax] [ebx] +// value [eax] modify [eax ebx ecx edx esi edi]; +int g3_check_and_light_tmap_quad_tile(g3s_phandle *vp, grs_bitmap *bm, int width_count, int height_count); +// MLA #pragma aux g3_check_and_light_tmap_quad_tile "*" parm [esi] [edi] [eax] +// [ebx] value [eax] modify [eax ebx ecx edx esi edi]; + +int g3_draw_lmap_quad_tile(g3s_phandle *vp, grs_bitmap *bm, int width_count, int height_count); +// MLA #pragma aux g3_draw_lmap_quad_tile "*" parm [esi] [edi] [eax] [ebx] value +// [eax] modify [eax ebx ecx edx esi edi]; +int g3_check_and_draw_lmap_quad_tile(g3s_phandle *vp, grs_bitmap *bm, int width_count, int height_count); +// MLA #pragma aux g3_check_and_draw_lmap_quad_tile "*" parm [esi] [edi] [eax] +// [ebx] value [eax] modify [eax ebx ecx edx esi edi]; +int g3_light_lmap_quad_tile(g3s_phandle *vp, grs_bitmap *bm, int width_count, int height_count); +// MLA #pragma aux g3_light_lmap_quad_tile "*" parm [esi] [edi] [eax] [ebx] +// value [eax] modify [eax ebx ecx edx esi edi]; +int g3_check_and_light_lmap_quad_tile(g3s_phandle *vp, grs_bitmap *bm, int width_count, int height_count); +// MLA #pragma aux g3_check_and_light_lmap_quad_tile "*" parm [esi] [edi] [eax] +// [ebx] value [eax] modify [eax ebx ecx edx esi edi]; + +int g3_draw_tmap_tile(g3s_phandle upperleft, g3s_vector *u_vec, g3s_vector *v_vec, int nverts, g3s_phandle *vp, + grs_bitmap *bm); +// MLA #pragma aux g3_draw_tmap_tile "*" parm [eax] [ebx] [ecx] [edx] [esi] +// [edi] value [eax] modify [eax ebx ecx edx esi edi]; +// this will tile a texture map over an arbitrary polygon. upperleft is the +// point in 3-space the matches the upper left corner of the texture map. +// u_vec and v_vec are the u and v basis vectors respectively. +// upperleft need not be in the polygon. If upperleft is 0, the warp matrix +// from the last texture map that drew (i.e. was at least partly on screen) +// will be used. +int g3_light_tmap_tile(g3s_phandle upperleft, g3s_vector *u_vec, g3s_vector *v_vec, int nverts, g3s_phandle *vp, + grs_bitmap *bm); +// MLA #pragma aux g3_light_tmap_tile "*" parm [eax] [ebx] [ecx] [edx] [esi] +// [edi] value [eax] modify [eax ebx ecx edx esi edi]; +int g3_draw_lmap_tile(g3s_phandle upperleft, g3s_vector *u_vec, g3s_vector *v_vec, int nverts, g3s_phandle *vp, + grs_bitmap *bm); +// MLA #pragma aux g3_draw_lmap_tile "*" parm [eax] [ebx] [ecx] [edx] [esi] +// [edi] value [eax] modify [eax ebx ecx edx esi edi]; +int g3_light_lmap_tile(g3s_phandle upperleft, g3s_vector *u_vec, g3s_vector *v_vec, int nverts, g3s_phandle *vp, + grs_bitmap *bm); +// MLA #pragma aux g3_light_lmap_tile "*" parm [eax] [ebx] [ecx] [edx] [esi] +// [edi] value [eax] modify [eax ebx ecx edx esi edi]; + +void g3_interpret_object(ubyte *object_ptr, ...); +extern void g3_set_tmaps_linear(void); +extern void g3_reset_tmaps(void); + +// Pragmas for all these functions + +/* MLA +#pragma aux g3_init_stereo "*" parm [eax] [ebx] [ecx] [edx] value [ax]; +#pragma aux g3_start_stereo_frame "*" parm [eax] modify [eax ebx ecx]; +#pragma aux g3_set_eyesep "*" parm [eax]; + +#pragma aux g3_vec_dotprod "*" parm [esi] [edi] modify exact [eax ebx ecx edx +esi] #pragma aux g3_vec_mag "*" parm [esi] modify [eax ebx ecx edx esi edi] +#pragma aux g3_vec_normalize "*" parm [esi] modify [eax ebx ecx edx edi]; +#pragma aux g3_vec_add "*" parm [edi] [esi] [ebx] modify exact [eax]; +#pragma aux g3_vec_sub "*" parm [edi] [esi] [ebx] modify exact [eax]; +#pragma aux g3_vec_scale "*" parm [edi] [esi] [ebx] modify exact [eax edx]; +#pragma aug g3_vec_rotate "*" parm [ebx] [esi] [edi] modify [eax ecx edx]; + +#pragma aux g3_init "*" parm [eax] [ebx] [ecx] [edx] value [ax]; +#pragma aux g3_shutdown "*"; +#pragma aux g3_count_free_points "*" value [eax]; + +#pragma aux g3_alloc_point "*" value [eax] modify exact [eax esi ebx]; +#pragma aux g3_alloc_list "*" value [eax] parm [ecx] [esi] modify exact [eax +ebx esi]; + +#pragma aux g3_rotate_point "*" parm [esi] value [edi] modify [eax ebx ecx edx +esi edi]; #pragma aux g3_rotate_grad "*" parm [esi] value [edi] modify [eax ebx +ecx edx esi edi]; #pragma aux g3_rotate_norm "*" parm [esi] value [edi] modify +[eax ebx ecx edx esi edi]; #pragma aux g3_project_point "*" parm [edi] modify +exact [eax ecx edx]; #pragma aux g3_transform_point "*" parm [esi] value [edi] +modify [eax ebx ecx edx esi edi]; #pragma aux g3_rotate_light_norm "*" parm +[esi] value [edi] modify [eax ebx ecx edx esi edi]; + + +#pragma aux g3_rotate_list "*" parm [ecx] [edi] [esi] value [ax] modify [eax ebx +ecx edx esi edi]; #pragma aux g3_project_list "*" parm [ecx] [esi] value [ax] +modify [eax ebx ecx edx esi edi]; #pragma aux g3_transform_list "*" parm [ecx] +[edi] [esi] value [ax] modify [eax ebx ecx edx esi edi]; + +#pragma aux g3_rotate_delta_v "*" parm [edi] [esi] modify [eax ebx ecx edx esi]; + +#pragma aux g3_add_delta_v "*" parm [edi] [esi] modify [eax ebx edx esi]; +#pragma aux g3_add_delta_x "*" parm [edi] [eax] modify [eax ebx edx]; +#pragma aux g3_add_delta_y "*" parm [edi] [eax] modify [eax ebx edx]; +#pragma aux g3_add_delta_z "*" parm [edi] [eax] modify [eax ebx edx]; +#pragma aux g3_add_delta_xy "*" parm [edi] [eax] [ebx] modify exact [eax ebx ecx +edx esi]; #pragma aux g3_add_delta_xz "*" parm [edi] [eax] [ebx] modify exact +[eax ebx ecx edx esi]; #pragma aux g3_add_delta_yz "*" parm [edi] [eax] [ebx] +modify exact [eax ebx ecx edx esi]; #pragma aux g3_add_delta_xyz "*" parm [edi] +[eax] [ebx] [ecx] modify exact [eax ebx ecx edx esi]; + + +#pragma aux g3_copy_add_delta_v "*" parm [esi] [ebx] value [edi] modify [eax +ebx]; #pragma aux g3_copy_add_delta_x "*" parm [esi] [eax] value [edi] modify +exact [eax ebx edx esi]; #pragma aux g3_copy_add_delta_y "*" parm [esi] [eax] +value [edi] modify exact [eax ebx edx esi]; #pragma aux g3_copy_add_delta_z "*" +parm [esi] [eax] value [edi] modify exact [eax ebx edx esi]; #pragma aux +g3_copy_add_delta_xy "*" parm [esi] [eax] [ebx] value [edi] modify exact [eax +ebx ecx edx]; #pragma aux g3_copy_add_delta_xz "*" parm [esi] [eax] [ebx] value +[edi] modify exact [eax ebx ecx edx]; #pragma aux g3_copy_add_delta_yz "*" parm +[esi] [eax] [ebx] value [edi] modify exact [eax ebx ecx edx]; #pragma aux +g3_copy_add_delta_xyz "*" parm [esi] [eax] [ebx] [ecx] value [edi] modify exact +[eax ebx ecx edx esi]; + +#pragma aux g3_replace_add_delta_x "*" parm [esi] [edi] [eax] value [edi] modify +exact [eax ebx edx esi]; #pragma aux g3_replace_add_delta_y "*" parm [esi] [edi] +[eax] value [edi] modify exact [eax ebx edx esi]; #pragma aux +g3_replace_add_delta_z "*" parm [esi] [edi] [eax] value [edi] modify exact [eax +ebx edx esi]; + +#pragma aux g3_rotate_delta_x "*" parm [edi] [eax] modify exact [eax ebx edx]; +#pragma aux g3_rotate_delta_y "*" parm [edi] [eax] modify exact [eax ebx edx]; +#pragma aux g3_rotate_delta_z "*" parm [edi] [eax] modify exact [eax ebx edx]; +#pragma aux g3_rotate_delta_xz "*" parm [edi] [eax] [ebx] modify exact [eax ebx +ecx edx esi]; #pragma aux g3_rotate_delta_xy "*" parm [edi] [eax] [ebx] modify +exact [eax ebx ecx edx esi]; #pragma aux g3_rotate_delta_yz "*" parm [edi] [eax] +[ebx] modify exact [eax ebx ecx edx esi]; #pragma aux g3_rotate_delta_xyz "*" +parm [edi] [eax] [ebx] [ecx] modify exact [eax ebx ecx edx esi]; + +#pragma aux g3_dup_point "*" parm [esi] value [edi] modify [ebx ecx esi]; +#pragma aux g3_copy_point "*" parm [edi] [esi] modify exact [ecx esi]; + +#pragma aux g3_free_point "*" parm [eax] modify exact [ebx esi]; +#pragma aux g3_free_list "*" parm [ecx] [esi] modify exact [eax ebx ecx esi]; + +#pragma aux g3_set_view_matrix "*" parm [esi] [ebx] [eax] modify [eax ebx ecx +edx esi edi]; #pragma aux g3_set_view_angles "*" parm [esi] [ebx] [ecx] [eax] +modify [eax ebx ecx edx esi edi]; + +#pragma aux g3_start_frame "*" modify [eax ebx ecx]; +#pragma aux g3_end_frame "*" value [eax]; + +#pragma aux g3_draw_horizon "*" parm [eax] [edx] modify [eax ebx ecx edx esi +edi]; + +#pragma aux g3_check_codes "*" parm [ecx] [esi] value [bx] modify exact [ebx ecx +edx esi]; + +#pragma aux g3_check_normal_facing "*" parm [esi] [edi] value [al] modify exact +[eax ebx ecx edx]; #pragma aux g3_check_poly_facing "*" parm [eax] [edx] [ebx] +value [al] modify [eax ebx ecx edx esi edi]; + +#pragma aux g3_start_object "*" parm [esi] value [al] modify exact [eax]; +#pragma aux g3_start_object_matrix "*" parm [esi] [edi] value [al] modify [eax +ebx ecx edx esi edi]; #pragma aux g3_start_object_angles_v "*" parm [esi] [edi] +[ecx] value [al] modify [eax ebx ecx edx esi edi]; #pragma aux +g3_start_object_angles_xyz "*" parm [esi] [eax] [ebx] [edx] [ecx] value [al] +modify [eax ebx ecx edx esi edi]; #pragma aux g3_start_object_angles_x "*" parm +[esi] [ebx] value [al] modify [eax ebx ecx edx esi edi]; #pragma aux +g3_start_object_angles_y "*" parm [esi] [ebx] value [al] modify [eax ebx ecx edx +esi edi]; #pragma aux g3_start_object_angles_z "*" parm [esi] [ebx] value [al] +modify [eax ebx ecx edx esi edi]; #pragma aux g3_start_object_angles_xy "*" parm +[esi] [ebx] [edx] [ecx] value [al] modify [eax ebx ecx edx esi edi]; #pragma aux +g3_start_object_angles_xz "*" parm [esi] [ebx] [edx] [ecx] value [al] modify +[eax ebx ecx edx esi edi]; #pragma aux g3_start_object_angles_yz "*" parm [esi] +[ebx] [edx] [ecx] value [al] modify [eax ebx ecx edx esi edi]; #pragma aux +g3_end_object "*" modify [ecx esi edi]; #pragma aux g3_scale_object "*" parm +[eax] modify [ecx eax edx]; + +#pragma aux g3_draw_line "*" parm [esi] [edi] value [eax] modify [eax ebx ecx +edx esi edi]; #pragma aux g3_draw_cline "*" parm [esi] [edi] value [eax] modify +[eax ebx ecx edx esi edi]; #pragma aux g3_draw_sline "*" parm [esi] [edi] value +[eax] modify [eax ebx ecx edx esi edi]; #pragma aux g3_draw_point "*" parm [esi] +value [eax] modify [eax ecx edx esi]; + +#pragma aux g3_get_FOV parm [esi] [edi] modify exact [eax ebx ecx edx]; +#pragma aux g3_get_zoom "*" parm [eax] [ebx] [ecx] [edx] value [eax] modify +exact [eax ebx ecx edx esi edi]; + +#pragma aux g3_get_view_pyramid "*" parm [edi] modify exact [eax ebx ecx edx esi +edi]; + +#pragma aux g3_get_slew_step "*" parm [eax] [ebx] [ecx] [edi] modify exact [eax +edx esi]; + +#pragma aux g3_compute_normal "*" parm [edi] [eax] [edx] [ebx] modify exact [eax +ebx ecx edx esi edi]; + +#pragma aux g3_transpose parm [esi] modify exact [eax]; +#pragma aux g3_copy_transpose parm [edi] [esi] modify exact [eax]; + +#pragma aux g3_matrix_x_matrix parm [ebx] [esi] [edi] modify exact [eax ebx ecx +edx]; + +#pragma aux g3_interpret_object "*" parm caller modify [eax ebx ecx edx esi +edi]; +*/ + +// inline code to handle stack args for polygon routines + +// Note that these are a lot uglier than they need to be, because C is so +// annoying. For starters, I shouldn't even have to specifiy the number of +// parms, since C obviously knows this number. Secondly, if I have varargs, +// C forces me to put all the args on the stack, when I really want the +// count in register and the rest on the stack. Lastly, since these are +// inline functions, C won't do the stack fixup, and since I don't have +// access, once again, to the parameter count, I have to use the variable +// to fixup the stack. This is really ugly since a wrong count supplied to +// the function will mess up the stack. Too bad C doesn't have a constant +// defined to be the parameter count for the current function. + +// awwwww, poor matt + +// MLA- took these out because we can't do inline asm in PowerPC (or 68K for +// that matter) and all routines need to be C callable +/* +#pragma aux g3_draw_poly_st = \ + "mov ecx,[esp]" \ + "lea esi,4[esp]" \ + "call g3_draw_poly" \ + "pop ecx" \ + "lea esp,[esp+ecx*4]" \ + parm [ecx] [esi] value [eax] modify [eax ebx ecx edx esi edi]; + +#pragma aux g3_draw_spoly_st = \ + "mov ecx,[esp]" \ + "lea esi,4[esp]" \ + "call g3_draw_spoly" \ + "pop ecx" \ + "lea esp,[esp+ecx*4]" \ + parm [ecx] [esi] value [eax] modify [eax ebx ecx edx esi edi]; + +#pragma aux g3_draw_cpoly_st = \ + "mov ecx,[esp]" \ + "lea esi,4[esp]" \ + "call g3_draw_cpoly" \ + "pop ecx" \ + "lea esp,[esp+ecx*4]" \ + parm [ecx] [esi] value [eax] modify [eax ebx ecx edx esi edi]; + +#pragma aux g3_check_and_draw_poly_st = \ + "mov ecx,[esp]" \ + "lea esi,4[esp]" \ + "call g3_check_and_draw_poly" \ + "pop ecx" \ + "lea esp,[esp+ecx*4]" \ + parm [ecx] [esi] value [eax] modify [eax ebx ecx edx esi edi]; + +#pragma aux g3_check_and_draw_cpoly_st = \ + "mov ecx,[esp]" \ + "lea esi,4[esp]" \ + "call g3_check_and_draw_cpoly" \ + "pop ecx" \ + "lea esp,[esp+ecx*4]" \ + parm [ecx] [esi] value [eax] modify [eax ebx ecx edx esi edi]; + +#pragma aux g3_check_and_draw_spoly_st = \ + "mov ecx,[esp]" \ + "lea esi,4[esp]" \ + "call g3_check_and_draw_spoly" \ + "pop ecx" \ + "lea esp,[esp+ecx*4]" \ + parm [ecx] [esi] value [eax] modify [eax ebx ecx edx esi edi]; +*/ + +#pragma pack(pop) + +#endif /* __3D_H */ diff --git a/engine/src/Libraries/3D/Source/3dinterp.h b/engine/src/Libraries/3D/Source/3dinterp.h new file mode 100644 index 0000000..f5c13ae --- /dev/null +++ b/engine/src/Libraries/3D/Source/3dinterp.h @@ -0,0 +1,67 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/3d/RCS/3dinterp.h $ + * $Revision: 1.2 $ + * $Author: kaboom $ + * $Date: 1993/10/02 09:19:46 $ + * + * includes for the 3d interpreter + * + * $Log: 3dinterp.h $ + * Revision 1.2 1993/10/02 09:19:46 kaboom + * New include for g3_set_vtext(). + * + * Revision 1.1 1993/08/04 00:48:56 dc + * Initial revision + */ + +// actual inline code +extern uchar _vcolor_tab[]; +#define g3_set_vcolor(vcolor_id, color_val) _vcolor_tab[vcolor_id] = (color_val); + +/*void g3_set_vcolor(int vcolor_id, int color_val); +#pragma aux g3_set_vcolor = \ + "add eax, OFFSET vcolor_tab" \ + "mov [eax], bl" \ + parm [eax] [ebx] modify exact [eax]*/ + +extern g3s_point *_vpoint_tab[]; +#define g3_set_vpoint(vpoint_id, point_ptr) _vpoint_tab[vpoint_id] = (g3s_point *)(point_ptr); + +/* +void g3_set_vpoint(int vpoint_id, void *point_ptr); +#pragma aux g3_set_vpoint = \ + "mov vpoint_tab[eax*4], ebx" \ + parm [eax] [ebx] modify exact [eax]*/ + +extern grs_bitmap *_vtext_tab[]; +#define g3_set_vtext(vtext_id, text_ptr) _vtext_tab[vtext_id] = (grs_bitmap *)(text_ptr); +/* +void g3_set_vtext(int vtext_id, void *text_ptr); +#pragma aux g3_set_vtext = \ + "mov vtext_tab[eax*4], ebx" \ + parm [eax] [ebx] modify exact [eax]*/ + +// flags for polygon draw type +extern uchar itrp_gour_flg, itrp_wire_flag, itrp_check_flg; + +#define g3_set_gour_flag(x) itrp_gour_flag = x +#define g3_set_wire_flag(x) itrp_wire_flag = x +#define g3_set_check_flag(x) itrp_check_flag = x diff --git a/engine/src/Libraries/3D/Source/Bitmap.c b/engine/src/Libraries/3D/Source/Bitmap.c new file mode 100644 index 0000000..d6d88b2 --- /dev/null +++ b/engine/src/Libraries/3D/Source/Bitmap.c @@ -0,0 +1,440 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// +// $Source: r:/prj/lib/src/3d/RCS/bitmap.asm $ +// $Revision: 1.16 $ +// $Author: kevin $ +// $Date: 1994/09/08 21:52:16 $ +// +// 3d bitmap routines. +// +// $Log: bitmap.asm $ +// Revision 1.16 1994/09/08 21:52:16 kevin +// Check bitmap size before blending, don't blend translucent bitmaps. +// +// Revision 1.15 1994/09/07 23:48:52 kevin +// Streamlined interface to 2d, added blending support. +// +// Revision 1.14 1994/08/18 03:46:54 jaemz +// Changed stereo glob names to have underscore for c +// +// Revision 1.13 1994/08/04 16:35:54 jaemz +// Added end statement to return these to being real programs +// +// Revision 1.12 1994/07/19 13:48:32 jaemz +// Added support for stereo +// +// Revision 1.11 1994/06/02 15:06:40 junochoe +// changed matrix_scale to _matrix_scale +// +// Revision 1.10 1994/03/12 20:32:22 kevin +// Added overflow checks to avoid passing bogus points to the 2d. +// Also cleaned things up a bit in general. +// +// Revision 1.9 1994/02/08 20:43:53 kaboom +// Added support for rest of bitmap types. +// +// Revision 1.8 1993/12/21 17:51:08 kevin +// Hacked in rsd8 support. +// +// Revision 1.7 1993/12/04 16:59:56 kaboom +// Added lighting support. +// +// Revision 1.5 1993/10/30 18:37:41 kaboom +// Changed SCALE_BITMAP constant to SCALE_FLAT8_BITMAP. +// +// Revision 1.4 1993/10/22 09:31:44 kaboom +// Updated call to linear mapper to use new calling convention. +// +// Revision 1.3 1993/10/02 09:24:33 kaboom +// Rewrote rolling routine to use the new uv linear mapper. Also changed names +// of scrw and scrh to _scrw and _scrh. +// +// Revision 1.2 1993/09/07 19:37:45 kaboom +// Updated LIN_MAP constant to FLAT8_LMAP. +// +// Revision 1.1 1993/07/08 23:37:34 kaboom +// Initial revision +// + +#include "3d.h" +#include "GlobalV.h" +#include "lg.h" +#include "OpenGL.h" +#include + +// need this from 2D lib +extern int h_map(grs_bitmap *bm, int n, grs_vertex **vpl, grs_tmap_info *ti); + +fix _g3d_bitmap_x_scale = 0x010000; +fix _g3d_bitmap_y_scale = 0x010000; +// fix _g3d_bitmap_x_iscale = 0x010000; +// fix _g3d_bitmap_y_iscale = 0x010000; +long _g3d_bitmap_u_anchor = 0; +long _g3d_bitmap_v_anchor = 0; +fix _g3d_roll_matrix[6]; +uchar *_g3d_bitmap_clut; +int _g3d_light_flag; + +grs_vertex **_g3d_bitmap_poly; +grs_vertex vlist[16]; +grs_vertex *vpl[] = {&vlist[0], &vlist[1], &vlist[2], &vlist[3]}; + +grs_tmap_info tmap_info; + +char _g3d_enable_blend = 0; + +// prototypes +bool SubLongWithOverflow(int32_t *result, int32_t src, int32_t dest); +bool AddLongWithOverflow(int32_t *result, int32_t src, int32_t dest); + +grs_vertex **do_bitmap(grs_bitmap *bm, g3s_phandle p); +grs_vertex **g3_bitmap_common(grs_bitmap *bm, g3s_phandle p); + +// arguments: +// ebx: u scale factor +// ecx: v scale factor +void g3_set_bitmap_scale(fix u_scale, fix v_scale) { + _g3d_bitmap_x_scale = fix_mul(fix_mul(_matrix_scale.gX, u_scale), _scrw); + // _g3d_bitmap_x_iscale = fix64_div(fix64_make(1, 0), _g3d_bitmap_x_scale); + + _g3d_bitmap_y_scale = fix_mul(fix_mul(_matrix_scale.gY, v_scale), _scrh); + // _g3d_bitmap_y_iscale = fix64_div(fix64_make(1, 0), _g3d_bitmap_y_scale); +} + +grs_vertex **g3_full_light_bitmap(grs_bitmap *bm, grs_vertex **p) { + _g3d_light_flag = 1; + _g3d_bitmap_poly = p; + return (do_bitmap(bm, (g3s_phandle)p)); +} + +grs_vertex **g3_full_light_anchor_bitmap(grs_bitmap *bm, grs_vertex **p, short u_anchor, short v_anchor) { + _g3d_light_flag = 1; + _g3d_bitmap_u_anchor = u_anchor; + _g3d_bitmap_v_anchor = v_anchor; + _g3d_bitmap_poly = p; + return (g3_bitmap_common(bm, (g3s_phandle)p)); +} + +grs_vertex **g3_light_anchor_bitmap(grs_bitmap *bm, g3s_phandle p, short u_anchor, short v_anchor) { + _g3d_light_flag = 2; + _g3d_bitmap_u_anchor = u_anchor; + _g3d_bitmap_v_anchor = v_anchor; + _g3d_bitmap_clut = (p->i & 0x00ff00) + grd_screen->ltab; + _g3d_bitmap_poly = vpl; + return (g3_bitmap_common(bm, p)); +} + +grs_vertex **g3_light_bitmap(grs_bitmap *bm, g3s_phandle p) { + _g3d_light_flag = 2; + _g3d_bitmap_clut = (p->i & 0x00ff00) + grd_screen->ltab; + _g3d_bitmap_poly = vpl; + return (do_bitmap(bm, p)); +} + +grs_vertex **g3_anchor_bitmap(grs_bitmap *bm, g3s_phandle p, short u_anchor, short v_anchor) { + _g3d_light_flag = 0; + _g3d_bitmap_u_anchor = u_anchor; + _g3d_bitmap_v_anchor = v_anchor; + _g3d_bitmap_poly = vpl; + return (g3_bitmap_common(bm, p)); +} + +grs_vertex **g3_bitmap(grs_bitmap *bm, g3s_phandle p) { + _g3d_light_flag = 0; + _g3d_bitmap_poly = vpl; + return (do_bitmap(bm, p)); +} + +grs_vertex **do_bitmap(grs_bitmap *bm, g3s_phandle p) { + _g3d_bitmap_u_anchor = bm->w >> 1; + _g3d_bitmap_v_anchor = bm->h - 1; + return (g3_bitmap_common(bm, p)); +} + +grs_vertex **g3_bitmap_common(grs_bitmap *bm, g3s_phandle p) { + fix tempF, tempF2; + int32_t tempL, tempL2; + int16_t tempS, tempS2; + fix sintemp, costemp; + grs_vertex *tempG1, *tempG2; + fix tempResult; + int32_t bm_w, bm_h; + fix dx, dy; + + // MLA- these were globals, I made them locals for PPC speed, they aren't + // referenced externally + long rm0; + long rm1; + long rm2; + long rm3; + +#ifdef stereo_on + if (_g3d_stereo & 1) { + + ; + edi is point handle pushm edi, + esi call g3_bitmap_common_raw set_rt_canv + + popm edi, + esi add edi, + _g3d_stereo_base call g3_bitmap_common_raw set_lt_canv + + ret + + g3_bitmap_common_raw: + } +#endif + + if ((p->p3_flags & PF_PROJECTED) == 0) + if (g3_project_point(p) == 0) + p->codes |= CC_CLIP_OVERFLOW; + + if ((p->codes & CC_CLIP_OVERFLOW) != 0) + return (0L); + + // copy a few things into locals + bm_w = bm->w; + bm_h = bm->h; + + tempL = p->gZ; // mov eax,[edi].z + tempL <<= 8; // sal eax,8 ;should be + // 16-log(max(bitmap_width, bitmap_height)) + tempF = view_bank; // mov ebx,view_bank + + if ((p->gZ & 0xff000000) == 0) // ;skip checks if z large enough + { + if (tempL < _g3d_bitmap_x_scale) + return (0L); + if (tempL < _g3d_bitmap_y_scale) + return (0L); + } + + // compute polygon for bitmap + // args: ebx=bank, esi=bitmap, edi=anchor pt. + // gb_compute_poly: + + fix_sincos((fixang)tempF, &sintemp, &costemp); // call fix_sincos + + tempL = p->gZ; + + // rm0 = cos(bank)*_g3d_bitmap_x_scale/z + rm0 = fix_mul_div(costemp, _g3d_bitmap_x_scale, tempL); + + // rm1 = sin(bank)*_g3d_bitmap_x_scale/z + rm1 = fix_mul_div(sintemp, _g3d_bitmap_x_scale, tempL); + + // rm2 = -sin(bank)*_g3d_bitmap_y_scale/z + rm2 = -fix_mul_div(sintemp, _g3d_bitmap_y_scale, tempL); + + // rm3 = cos(bank)*_g3d_bitmap_y_scale/z + rm3 = fix_mul_div(costemp, _g3d_bitmap_y_scale, tempL); + + // 0 x, 1 y, 2 u, 3 v, 4 w, 5 i + tempG1 = _g3d_bitmap_poly[0]; + + // vpl[0][0] = x-u_anchor*rm0-v_anchor*rm1 + tempL2 = p->sx; + tempL = _g3d_bitmap_u_anchor * rm0; + if (SubLongWithOverflow(&tempL2, tempL2, tempL)) + return 0; // tempL2 -= tempL + + tempL = _g3d_bitmap_v_anchor * rm1; + if (SubLongWithOverflow(&tempL2, tempL2, tempL)) + return 0; // tempL2 -= tempL + tempG1->x = tempL2; + + // vpl[0][1] = y-u_anchor*rm2-v_anchor*rm3 + tempL2 = p->sy; + tempL = _g3d_bitmap_u_anchor * rm2; + if (SubLongWithOverflow(&tempL2, tempL2, tempL)) + return 0; // tempL2 -= tempL + + tempL = _g3d_bitmap_v_anchor * rm3; + if (SubLongWithOverflow(&tempL2, tempL2, tempL)) + return 0; // tempL2 -= tempL + tempG1->y = tempL2; + + // vpl[0][2] = 0 + tempG1->u = 0; + + // vpl[0][3] = 0 + tempG1->v = 0; + + // vpl[0][4] undefined + + // vpl[0][5] provided + + tempG2 = tempG1; + tempG1 = _g3d_bitmap_poly[1]; + + // vpl[1][0] = vpl[0][0]+rm0*w + tempL2 = tempG2->x; + tempL = rm0 * bm_w; + if (AddLongWithOverflow(&tempL2, tempL2, tempL)) + return 0; // tempL2 += tempL + tempG1->x = tempL2; + + // vpl[1][1] = vpl[0][1]+rm2*w + tempL2 = tempG2->y; + tempL = rm2 * bm_w; + if (AddLongWithOverflow(&tempL2, tempL2, tempL)) + return 0; // tempL2 += tempL + tempG1->y = tempL2; + + // vpl[1][2] = bitmap.w-1 + tempG1->u = (bm_w - 1) << 16; + + // vpl[1][3] = 0 + tempG1->v = 0; + + // vpl[1][4] undefined + + // vpl[1][5] provided + + tempG2 = tempG1; + tempG1 = _g3d_bitmap_poly[2]; + + // vpl[2][0] = vpl[1][0]+rm1*h + tempL2 = tempG2->x; + tempL = rm1 * bm_h; + if (AddLongWithOverflow(&tempL2, tempL2, tempL)) + return 0; // tempL2 += tempL + tempG1->x = tempL2; + + // vpl[2][1] = vpl[1][1]+rm3*h + tempL2 = tempG2->y; + tempL = rm3 * bm_h; + if (AddLongWithOverflow(&tempL2, tempL2, tempL)) + return 0; // tempL2 += tempL + tempG1->y = tempL2; + + // vpl[2][2] = bitmap.w-1 + tempG1->u = (bm_w - 1) << 16; + + // vpl[2][3] = bitmap.h-1 + tempG1->v = (bm_h - 1) << 16; + + // vpl[2][4] undefined + + // vpl[2][5] provided + + tempG2 = _g3d_bitmap_poly[0]; + tempG1 = _g3d_bitmap_poly[3]; + + // vpl[3][0] = vpl[0][0]+rm1*h + tempL2 = tempG2->x; + tempL = rm1 * bm_h; + if (AddLongWithOverflow(&tempL2, tempL2, tempL)) + return 0; // tempL2 += tempL + tempG1->x = tempL2; + + // vpl[3][1] = vpl[0][1]+rm3*h + tempL2 = tempG2->y; + tempL = rm3 * bm_h; + if (AddLongWithOverflow(&tempL2, tempL2, tempL)) + return 0; // tempL2 += tempL + tempG1->y = tempL2; + + // vpl[3][2] = 0 + tempG1->u = 0; + + // vpl[3][3] = bitmap.h + tempG1->v = bm_h << 16; + + // vpl[3][4] undefined + + // vpl[3][5] provided + tmap_info.flags = 0; + // do blending? + if (_g3d_light_flag == 2) { + tmap_info.clut = _g3d_bitmap_clut; + tmap_info.flags = TMF_CLUT; + + if (_g3d_enable_blend) { + + // only blend if no translucent or no compressed translucent bitmap + if ((bm->type != BMT_TLUC8) && (bm->flags != BMF_TLUC8)) { + // check for bitmap widthx - _g3d_bitmap_poly[2]->x); + dy = fix_abs(_g3d_bitmap_poly[0]->y - _g3d_bitmap_poly[2]->y); + + if (dy > dx) + dx = dy; // get max value in dx + + // shift down because bm_w isn't fixed, and we only double when twice the size + dx >>= 0x11; + + // make sure doubled bitmap won't overflow unpack buffer (that's roughly 1/5 of 64k) + if ((bm_w <= dx) && (bm_w * bm_h <= 12000)) { + // first fix all u's and v's + for (tempL = 0; tempL < 4; tempL++) { + _g3d_bitmap_poly[tempL]->u <<= 1; + _g3d_bitmap_poly[tempL]->v <<= 1; + } + + tmap_info.tmap_type = GRC_POLY; + if (!use_opengl()) { + h_map(bm, 4, _g3d_bitmap_poly, &tmap_info); + } else { + int opengl_bitmap(grs_bitmap *bm, int n, grs_vertex **vpl, grs_tmap_info *ti); + opengl_bitmap(bm, 4, _g3d_bitmap_poly, &tmap_info); + } + return (_g3d_bitmap_poly); + } + } + } + } + tmap_info.tmap_type = (_g3d_light_flag << 1) + GRC_BILIN; + extern bool use_opengl(); + if (!use_opengl()) { + h_map(bm, 4, _g3d_bitmap_poly, &tmap_info); + } else { + int opengl_bitmap(grs_bitmap *bm, int n, grs_vertex **vpl, grs_tmap_info *ti); + opengl_bitmap(bm, 4, _g3d_bitmap_poly, &tmap_info); + } + + return (_g3d_bitmap_poly); +} + +// subtract two longs, put the result in result, and return true if overflow +// result = src-dest; +bool SubLongWithOverflow(int32_t *result, int32_t src, int32_t dest) { + long tempres; + + *result = tempres = src - dest; + if ((dest >= 0 && src < 0 && tempres >= 0) || (dest < 0 && src >= 0 && tempres < 0)) + return true; + else + return false; +} + +// add two longs, put the result in result, and return true if overflow +// result = src+dest; +bool AddLongWithOverflow(int32_t *result, int32_t src, int32_t dest) { + long tempres; + + *result = tempres = src + dest; + if ((dest >= 0 && src >= 0 && tempres < 0) || (dest < 0 && src < 0 && tempres >= 0)) + return true; + else + return false; +} diff --git a/engine/src/Libraries/3D/Source/GlobalV.c b/engine/src/Libraries/3D/Source/GlobalV.c new file mode 100644 index 0000000..90975ec --- /dev/null +++ b/engine/src/Libraries/3D/Source/GlobalV.c @@ -0,0 +1,197 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// +// $Source: r:/prj/lib/src/3d/RCS/globv.asm $ +// $Revision: 1.5 $ +// $Author: jaemz $ +// $Date: 1994/11/06 19:10:28 $ +// +// Global vars for the 3d system +// +// $Log: globv.asm $ +// Revision 1.5 1994/11/06 19:10:28 jaemz +// Allow stereo to be externed even in non stereo version to test +// +// Revision 1.4 1994/09/28 19:01:00 jaemz +// Fixed stereo bug +// +// Revision 1.3 1994/09/20 13:29:08 jaemz +// Added globals for lighting +// +// Revision 1.2 1994/08/18 03:46:56 jaemz +// Changed stereo glob names to have underscore for c +// +// Revision 1.1 1994/08/04 17:47:21 jaemz +// Initial revision +// +// + +#include "3d.h" +#include "lg.h" + +// point allocation vars + +g3s_point *point_list = 0; // dd 0 ;ptr to point buffer +short n_points = 0; // dw 0 ;num points allocated +g3s_point *first_free = 0; // dd 0 ;ptr to first free pnt + +g3s_matrix unscaled_matrix; // g3s_matrix <> ;unscaled & unadjusted + +// note: view_matrix and view_position must remain in this order! +g3s_matrix view_matrix; // g3s_matrix <> +g3s_vector _view_position; // g3s_vector <> +fix _view_zoom; // fix ? +fix view_heading; // fix ? +fix view_pitch; // fix ? +fix view_bank; // fix ? + +// are to save inverse object to world matrix and position +// to go from world to object, take Ax + a (like in real 3d) +g3s_matrix _wtoo_matrix; // g3s_matrix <> +g3s_vector _wtoo_position; // g3s_vector <> + +fix pixel_ratio; // fix ? ;copy from 2d drv_cap + +long window_width; // dd ? +long window_height; // dd ? + +long ww2; // dd ? ;one-half widht,height +long wh2; // dd ? ;..for texture mapper + +long _scrw; // dd ? ;need to do double-word mul +long _scrh; // dd ? + +fix _biasx; // fix ? +fix _biasy; // fix ? + +g3s_vector _matrix_scale; // <> ;how the columns are scaled +g3s_vector horizon_vector; // <> ;info for drawing the horizon + +// clang-format off +// this tables tells you many bits to shift to get zero +uchar shift_table[256] = { + 0, + 1, + 2, 2, + 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 +}; +// clang-format on + +/* db 0 + db 1 + db 2 dup (2) + db 4 dup (3) + db 8 dup (4) + db 16 dup (5) + db 32 dup (6) + db 64 dup (7) + db 128 dup (8)*/ + +// these vars describe the translation from the user's coordinate system +// to our coordinate system + +long up_axis; // dd ? + +// which axis is our x,y,z? +long axis_x; // dd ? +long axis_z; // dd ? +long axis_y; // dd ? + +// offset into matrix of axis which is x,y,z +long axis_x_ofs; // dd ? +long axis_z_ofs; // dd ? +long axis_y_ofs; // dd ? + +char axis_swap_flag; // db ? +char axis_neg_flag; // db ? + +// Lighting globals +char _g3d_light_type = 0; // db 0 ; The lighting type, see above + +fix _g3d_amb_light = 0; // fix 0 ; amount of ambient light +fix _g3d_diff_light = 0x10000; // fix 10000h ; intensity of light source +fix _g3d_spec_light = 0; // fix 0 ; amount of spec light +fix _g3d_flash = 0; // fix 0 ; specular flash point below which none is applied +// note that specular light is used to provide a "flash" mostly +// so you can artificially inflate it a bit, or we could try +// funny functions to make it "flash" only within a certain +// range. + +g3s_vector _g3d_light_src; // g3s_vector <> ; light source, either + // local or vector +g3s_vector _g3d_light_trans; // g3s_vector <> ; point + // source in view coords +g3s_vector _g3d_light_vec; // g3s_vector <> ; current light vector, + // computed from src and flag + +g3s_vector _g3d_view_vec; // g3s_vector <> ; current viewing vector, + // may have to be computed periodically + +fix _g3d_ldotv; // fix ? ; light vector dotted with view vector (for + // specular only) +fix _g3d_sdotl; // fix ? ; surface vector dotted with light vector + // (for diffuse and spec) +fix _g3d_sdotv; // fix ? ; surface vector dotted with view vector + // (ostensibly jnorm) + +long _g3d_light_tab = 0; // dd 0 ; lighting table with 32 or 24 + // entries. Should go from black to white, + +// stereo globals, read em and weep +fix _g3d_eyesep_raw = 0; // fix 0 ;raw 3d sep between eyes +fix _g3d_eyesep = 0; // fix 0 ;scaled eye sep between eyes +long _g3d_stereo_base = 0; // dd 0 ;stereo point offset, default zero, means non +long _g3d_stereo_list = 0; // dd 0 ;start of stereo point list, makes it easy to detect +char _g3d_stereo = 0; // db 0 ;stereo this frame +long _g3d_rt_canv = 0; // dd 0 ;pointer to right eye canvas +long _g3d_rt_canv_bits = 0; // dd 0 ;pointer to bits of rt canvas +long _g3d_lt_canv_bits = 0; // dd 0 ;pointer to bits of lt canvas +long _g3d_stereo_tmp[14]; // dd 14 dup (?) ;temporary point list + +// palette base for gouraud-shaded polys + +sfix gouraud_base; // sfix ? + +// for statistics +/* ifdef dbg_on + +n_polys dw ? +n_polys_drawn dw ? +n_polys_triv_acc dw ? +n_polys_triv_rej dw ? +n_polys_clip_2d dw ? +n_polys_clip_3d dw ? + + endif*/ diff --git a/engine/src/Libraries/3D/Source/GlobalV.h b/engine/src/Libraries/3D/Source/GlobalV.h new file mode 100644 index 0000000..bd0c006 --- /dev/null +++ b/engine/src/Libraries/3D/Source/GlobalV.h @@ -0,0 +1,164 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// +// $Source: r:/prj/lib/src/3d/RCS/globv.h $ +// $Revision: 1.5 $ +// $Author: jaemz $ +// $Date: 1994/11/06 19:10:28 $ +// +// Global vars for the 3d system +// +// $Log: globv.asm $ +// Revision 1.5 1994/11/06 19:10:28 jaemz +// Allow stereo to be externed even in non stereo version to test +// +// Revision 1.4 1994/09/28 19:01:00 jaemz +// Fixed stereo bug +// +// Revision 1.3 1994/09/20 13:29:08 jaemz +// Added globals for lighting +// +// Revision 1.2 1994/08/18 03:46:56 jaemz +// Changed stereo glob names to have underscore for c +// +// Revision 1.1 1994/08/04 17:47:21 jaemz +// Initial revision +// +// + +#include "3d.h" +#include "lg.h" + +// point allocation vars + +extern g3s_point *point_list; // dd 0 ;ptr to point buffer +extern short n_points; // dw 0 ;num points allocated +extern g3s_point *first_free; // dd 0 ;ptr to first free pnt + +extern g3s_matrix unscaled_matrix; // g3s_matrix <> ;unscaled & unadjusted + +// note: view_matrix and view_position must remain in this order! +extern g3s_matrix view_matrix; // g3s_matrix <> +extern g3s_vector _view_position; // g3s_vector <> +extern fix _view_zoom; // fix ? +extern fix view_heading; // fix ? +extern fix view_pitch; // fix ? +extern fix view_bank; // fix ? + +// are to save inverse object to world matrix and position +// to go from world to object, take Ax + a (like in real 3d) +extern g3s_matrix _wtoo_matrix; // g3s_matrix <> +extern g3s_vector _wtoo_position; // g3s_vector <> + +extern fix pixel_ratio; // fix ? ;copy from 2d drv_cap + +extern long window_width; // dd ? +extern long window_height; // dd ? + +extern long ww2; // dd ? ;one-half widht,height +extern long wh2; // dd ? ;..for texture mapper + +extern long _scrw; // dd ? ;need to do double-word mul +extern long _scrh; // dd ? + +extern fix _biasx; // fix ? +extern fix _biasy; // fix ? + +extern g3s_vector _matrix_scale; // <> ;how the columns are scaled +extern g3s_vector horizon_vector; // <> ;info for drawing the horizon + +// this tables tells you many bits to shift to get zero +extern uchar shift_table[]; +extern long up_axis; // dd ? + +// which axis is our x,y,z? +extern long axis_x; // dd ? +extern long axis_z; // dd ? +extern long axis_y; // dd ? + +// offset into matrix of axis which is x,y,z +extern long axis_x_ofs; // dd ? +extern long axis_z_ofs; // dd ? +extern long axis_y_ofs; // dd ? + +extern char axis_swap_flag; // db ? +extern char axis_neg_flag; // db ? + +// Lighting globals +extern char _g3d_light_type; // db 0 ; The lighting type, see above + +extern fix _g3d_amb_light; // fix 0 ; amount of ambient light +extern fix _g3d_diff_light; // fix 10000h ; intensity of light source +extern fix _g3d_spec_light; // fix 0 ; amount of spec light +extern fix _g3d_flash; // fix 0 ; specular flash point below which none + // is applied +// note that specular light is used to provide a "flash" mostly +// so you can artificially inflate it a bit, or we could try +// funny functions to make it "flash" only within a certain +// range. + +extern g3s_vector _g3d_light_src; // g3s_vector <> ; light source, + // either local or vector +extern g3s_vector _g3d_light_trans; // g3s_vector <> ; + // point source in view coords +extern g3s_vector _g3d_light_vec; // g3s_vector <> ; current light + // vector, computed from src and flag + +extern g3s_vector _g3d_view_vec; // g3s_vector <> ; current viewing + // vector, may have to be computed + // periodically + +extern fix _g3d_ldotv; // fix ? ; light vector dotted with view + // vector (for specular only) +extern fix _g3d_sdotl; // fix ? ; surface vector dotted with light + // vector (for diffuse and spec) +extern fix _g3d_sdotv; // fix ? ; surface vector dotted with view + // vector (ostensibly jnorm) + +extern long _g3d_light_tab; // dd 0 ; lighting table with 32 or 24 + // entries. Should go from black to white, + +// stereo globals, read em and weep +extern fix _g3d_eyesep_raw; // fix 0 ;raw 3d sep between eyes +extern fix _g3d_eyesep; // fix 0 ;scaled eye sep between eyes +extern long _g3d_stereo_base; // dd 0 ;stereo point offset, default + // zero, means non +extern long _g3d_stereo_list; // dd 0 ;start of stereo point list, + // makes it easy to detect +extern char _g3d_stereo; // db 0 ;stereo this frame +extern long _g3d_rt_canv; // dd 0 ;pointer to right eye canvas +extern long _g3d_rt_canv_bits; // dd 0 ;pointer to bits of rt canvas +extern long _g3d_lt_canv_bits; // dd 0 ;pointer to bits of lt canvas +extern long _g3d_stereo_tmp[14]; // dd 14 dup (?) ;temporary point list + +// palette base for gouraud-shaded polys + +extern sfix gouraud_base; // sfix ? + +// for statistics +/* ifdef dbg_on + +n_polys dw ? +n_polys_drawn dw ? +n_polys_triv_acc dw ? +n_polys_triv_rej dw ? +n_polys_clip_2d dw ? +n_polys_clip_3d dw ? + + endif*/ diff --git a/engine/src/Libraries/3D/Source/alloc.c b/engine/src/Libraries/3D/Source/alloc.c new file mode 100644 index 0000000..7c8782b --- /dev/null +++ b/engine/src/Libraries/3D/Source/alloc.c @@ -0,0 +1,303 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// +// $Source: r:/prj/lib/src/3d/RCS/alloc.c $ +// $Revision: 1.19 $ +// $Author: jaemz $ +// $Date: 1994/09/28 19:01:01 $ +// +// Point allocation, system init and shutdown +// + +#include "3d.h" +#include "GlobalV.h" +#include "lg.h" + +//---------------------------------------------------------------------------- +// Function: short g3_init(short max_points,int user_x_axis,int user_y_axis,int +// user_z_axis) +// +// Starts up the 3d system, allocating the requested number of points, +// installing the divide overflow handler, and setting up the axes +// +// Input: number of points requested +// axis numbers +// Output: number actually allocated +// Side effects: allocates point array +//---------------------------------------------------------------------------- +short g3_init(short max_points, int user_x_axis, int user_y_axis, int user_z_axis) { + int temp_user_y_axis; + long temp_long; + char temp_char; + long allocSize; + char temp_neg_flags[3] = {0, 0, 0}; + +#ifdef stereo_on + extn divide_overflow_r3d, proj_div_2 +#endif + + // set axis neg flags + axis_swap_flag = 0; // mov axis_swap_flag,0 + axis_neg_flag = 0; // mov axis_neg_flag,0 + + temp_user_y_axis = user_y_axis; // push ecx ;save y axis + if (user_x_axis < 0) // or ebx,ebx ;check sign jns no_neg_x + { + user_x_axis = -user_x_axis; // neg ebx + temp_neg_flags[0] = 1; // mov temp_neg_flags,1 + } + axis_x = user_x_axis; // no_neg_x: mov axis_x,ebx + + if (user_y_axis < 0) // or ecx,ecx ;check sign jns no_neg_y + { + user_y_axis = -user_y_axis; // neg ecx + temp_neg_flags[1] = 1; // mov temp_neg_flags+1,1 + } + axis_y = user_y_axis; // no_neg_y: mov axis_y,ecx + + if (user_z_axis < 0) // or edx,edx ;check sign jns no_neg_z + { + user_z_axis = -user_z_axis; // neg edx + temp_neg_flags[2] = 1; // mov temp_neg_flags+2,1 + } + axis_z = user_z_axis; // no_neg_z: mov axis_z,edx + + // set axis swap flags + if (user_x_axis >= user_y_axis) // cmp ebx,ecx ;check swap x,y jl + // no_swap_xy + { + axis_swap_flag |= 1; // or axis_swap_flag,1 + temp_long = user_x_axis; + user_x_axis = user_y_axis; + user_y_axis = temp_long; // xchg ebx,ecx + + temp_char = temp_neg_flags[0]; + temp_neg_flags[0] = temp_neg_flags[1]; + temp_neg_flags[1] = temp_char; // mswap temp_neg_flags,temp_neg_flags+1 + } + + if (user_x_axis >= user_z_axis) // cmp ebx,edx ;check swap x,z jl + // no_swap_xy + { + axis_swap_flag |= 2; // or axis_swap_flag,1 + temp_long = user_x_axis; + user_x_axis = user_z_axis; + user_z_axis = temp_long; // xchg ebx,edx + + temp_char = temp_neg_flags[0]; + temp_neg_flags[0] = temp_neg_flags[2]; + temp_neg_flags[2] = temp_char; // mswap temp_neg_flags,temp_neg_flags+2 + } + + if (user_y_axis >= user_z_axis) // cmp ecx,edx ;check swap y,z jl + // no_swap_xy + { + axis_swap_flag |= 4; // or axis_swap_flag,1 + temp_char = temp_neg_flags[1]; + temp_neg_flags[1] = temp_neg_flags[2]; + temp_neg_flags[2] = temp_char; // mswap temp_neg_flags+1,temp_neg_flags+2 + } + + // set neg flags bitmask + axis_neg_flag = (temp_neg_flags[2] << 2) | (temp_neg_flags[1] << 1) | temp_neg_flags[0]; + + user_y_axis = temp_user_y_axis - 1; // pop ecx ;get back y axis + // dec ecx ;make y axis 0,1,2 + up_axis = (user_y_axis << 1) + user_y_axis; + + // set axis offset vars. offset is number of elements, not bytes + axis_x_ofs = ((axis_x - 1) << 1) + (axis_x - 1); + axis_y_ofs = ((axis_y - 1) << 1) + (axis_y - 1); + axis_z_ofs = ((axis_z - 1) << 1) + (axis_z - 1); + + // get pixel ratio + pixel_ratio = grd_cap->aspect; + + // now allocate point memory + + // _mark_ + allocSize = max_points * sizeof(g3s_point); + +#ifdef stereo_on // ; if stereo mode multiply by 2 at last moment + if (_g3d_stereo_base) + allocSize <<= 1; +#endif + + point_list = (g3s_point *)malloc(allocSize); + if (!point_list) + return (0); + + // MLA - all divide overflow/divide by zero errors are handled around the + // individual divide instructions. + // + // Since we can't do divide overflow/zero traps on both the 68k and PPC. + + // install divide overflow callbacks + /* mov eax,EXM_DIVIDE_ERR ;tell handler to do callbacks + call ex_startup_ ;install handler + + lea eax,proj_div_0 + lea edx,divide_overflow_3d + call ex_push_div_call_ ;callback for first pyr div + + lea eax,proj_div_1 + lea edx,divide_overflow_3d + call ex_push_div_call_ ;callback for 2nd pyr div + + ifdef stereo_on + ; save stereo_list + mov eax,point_list + add eax,_g3d_stereo_base + mov _g3d_stereo_list,eax + + lea eax,proj_div_2 + lea edx,divide_overflow_r3d + call ex_push_div_call_ ;callback for 2nd pyr div + endif + */ + n_points = max_points; + return (n_points); +} + +void g3_start_frame(void) { + int i; + g3s_point *pt3; + + // get pixel ratio again in case it's changed + pixel_ratio = grd_cap->aspect; + + //>>>>>>> 1.15 + // set up window vars + window_width = grd_canvas->bm.w; + _biasx = _scrw = window_width << 15; + ww2 = _scrw >> 16; + + window_height = grd_canvas->bm.h; + _biasy = _scrh = window_height << 15; + wh2 = _scrh >> 16; + + // mark all points as free + if (n_points) { + first_free = point_list; + pt3 = point_list; + for (i = 0; i < n_points - 1; i++, pt3++) + pt3->next = (g3s_phandle)(pt3 + 1); + + pt3->next = 0L; + } +} + +// shut down the 3d system +void g3_shutdown(void) { + if (point_list) + free(point_list); + + n_points = 0; + first_free = 0; + +#ifdef stereo_on + _g3d_stereo_base = 0; +#endif +} + +//;does extactly what you would think +int g3_count_free_points(void) { + int i; + g3s_point *free_p = first_free; + + i = 0; + while (free_p) { + i++; + free_p = free_p->next; + } + return (i); +} + +// check if all points free. returns number of points lost +int g3_end_frame(void) { +#ifdef stereo_on + mov _g3d_stereo, 0; kill stereo for now +#endif + return(g3_count_free_points()-n_points); +} + +// allocate a list of points +int g3_alloc_list(int n, g3s_phandle *p) { + int i; + g3s_point *cur_ptr; + + if (!first_free) + return (0); + cur_ptr = first_free; + + for (i = 0; i < n; i++) { + p[i] = cur_ptr; + cur_ptr = cur_ptr->next; + } + + first_free = cur_ptr; + return (n); +} + +// allocate one point, returning handle in ax +g3s_phandle g3_alloc_point(void) { + g3s_point *tempPtr; + + if (!first_free) + return (0L); + tempPtr = first_free; + first_free = tempPtr->next; + return (tempPtr); +} + +// free the point in eax. trashes ebx +void g3_free_point(g3s_phandle p) // adds to free list +{ + p->next = first_free; + first_free = p; +} + +// free the list of points pointed at by esi, count in ecx +void g3_free_list(int n_points, g3s_phandle *p) // adds to free list +{ + int i; + g3s_point *tempPtr; + + for (i = 0; i < n_points; i++) { + tempPtr = p[i]; + tempPtr->next = first_free; + first_free = tempPtr; + } +} + +// make a duplicate of a point. takes esi, returns edi. trashes ebx,ecx +g3s_phandle g3_dup_point(g3s_phandle p) // makes copy of a point +{ + g3s_point *destPtr; + + destPtr = first_free; + first_free = destPtr->next; + + g3_copy_point(destPtr, p); + + return (destPtr); +} + +// copy point at esi to one at edi. trashes esi,ecx +void g3_copy_point(g3s_phandle dest, g3s_phandle src) { *dest = *src; } diff --git a/engine/src/Libraries/3D/Source/clip.c b/engine/src/Libraries/3D/Source/clip.c new file mode 100644 index 0000000..e11e9d3 --- /dev/null +++ b/engine/src/Libraries/3D/Source/clip.c @@ -0,0 +1,620 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/3d/RCS/clip.c $ + * $Revision: 1.6 $ + * $Author: kaboom $ + * $Date: 1994/03/23 11:48:45 $ + * + * Polygon and line clipping routines ripped off from freefall and + * hacked to use inverted y coordinates. + */ + +#include + +#include "2d.h" +#include "3d.h" +#include "GlobalV.h" +#include "fix.h" + +#define NEXTI(x) (((x) + 1 == n) ? 0 : (x) + 1) + +// these indicate that you have clipped it there +#define CC_OFF_X 16 +#define CC_OFF_Y 32 +#define CC_MASK (0xff - (CC_OFF_X | CC_OFF_Y)) + +void g3_intersect(void); + +void g3_left_intersect(void); +void g3_top_intersect(void); +void g3_right_intersect(void); +void g3_bottom_intersect(void); +void g3_back_intersect(void); +// void project_point(g3s_point *src[],int n); + +static g3s_point tbuff[20]; +static int tnum; +// static fix _d = 65536/18; +static fix _d = 1; +static fix _a, _b, _c; +static fix num, den; // num/den is dist from s to interseciton +static g3s_point *_tmp; +static g3s_point *s; // start of line segment +static g3s_point *e; // end of line segment + +int g3_clip_line(g3s_point *src[], g3s_point *dest[]) { + int i, j; + byte cc; + byte ca; + // assume 10 points max + g3s_point *tmp0[10]; + g3s_point *tmp1[10]; + int b; // current destination buffer + g3s_point **tmps; // pointer to the tmp buffer + g3s_point **tmpd; // pointer to dest buffer + + cc = src[0]->codes | src[1]->codes; + ca = src[0]->codes & src[1]->codes; + + // if all the same, leave + if (cc == 0) { + // copy src to dest + LG_memcpy(dest, src, 2 * sizeof(g3s_point *)); + return CLIP_NONE; + } + if (ca != 0) + return CLIP_ALL; + + tnum = 0; + // only cycle through ones you need to, + // which is usually only one side, except + // when right up close + + tmps = src; + tmpd = tmp0; + b = 0; + + // Clip to _d in the z axis if it needs it + if (cc < 0) { + s = tmps[0]; + e = tmps[1]; + j = 0; + + // If the inside one is in + // if CC_BEHIND is set, codes is negative + if ((s->codes & CC_BEHIND) == 0) { + tmpd[j] = s; + j++; + } + // if they intersect put the intersection + // start in and end out or out and in + if (((s->codes ^ e->codes) & CC_BEHIND) != 0) { + g3_back_intersect(); + tmpd[j] = _tmp; + j++; + } + if ((e->codes & CC_BEHIND) == 0) { + tmpd[j] = e; + j++; + } + // recalculate codes + cc = tmpd[0]->codes | tmpd[1]->codes; + if (tmpd[0]->codes & tmpd[1]->codes) + return CLIP_ALL; + tmps = tmpd; + b = 1 - b; + tmpd = (b == 0) ? tmp0 : tmp1; + } + + // clip to the left, 0 in the x axis + if ((cc & CC_OFF_LEFT) != 0) { + s = tmps[0]; + e = tmps[1]; + j = 0; + + if ((s->codes & CC_OFF_LEFT) == 0) { + tmpd[j] = s; + j++; + } + // if they intersect put the intersection + // start in and end out or out and in + if (((s->codes ^ e->codes) & CC_OFF_LEFT) != 0) { + g3_left_intersect(); + tmpd[j] = _tmp; + j++; + } + if ((e->codes & CC_OFF_LEFT) == 0) { + tmpd[j] = e; + j++; + } + // recalculate codes + cc = tmpd[0]->codes | tmpd[1]->codes; + if (tmpd[0]->codes & tmpd[1]->codes) + return CLIP_ALL; + tmps = tmpd; + b = 1 - b; + tmpd = (b == 0) ? tmp0 : tmp1; + } + + // Clip to 0 in the y axis, top + if ((cc & CC_OFF_TOP) != 0) { + s = tmps[0]; + e = tmps[1]; + j = 0; + + if ((s->codes & CC_OFF_TOP) == 0) { + tmpd[j] = s; + j++; + } + // if they intersect put the intersection + // start in and end out or out and in + if (((s->codes ^ e->codes) & CC_OFF_TOP) != 0) { + g3_top_intersect(); + tmpd[j] = _tmp; + j++; + } + if ((e->codes & CC_OFF_TOP) == 0) { + tmpd[j] = e; + j++; + } + // recalculate codes + cc = tmpd[0]->codes | tmpd[1]->codes; + if (tmpd[0]->codes & tmpd[1]->codes) + return CLIP_ALL; + tmps = tmpd; + b = 1 - b; + tmpd = (b == 0) ? tmp0 : tmp1; + } + + // Clip to w in the x axis, right + if ((cc & CC_OFF_RIGHT) != 0) { + s = tmps[0]; + e = tmps[1]; + j = 0; + + if ((s->codes & CC_OFF_RIGHT) == 0) { + tmpd[j] = s; + j++; + } + // if they intersect put the intersection + // start in and end out or out and in + if (((s->codes ^ e->codes) & CC_OFF_RIGHT) != 0) { + g3_right_intersect(); + tmpd[j] = _tmp; + j++; + } + if ((e->codes & CC_OFF_RIGHT) == 0) { + tmpd[j] = e; + j++; + } + // recalculate codes + cc = tmpd[0]->codes | tmpd[1]->codes; + if (tmpd[0]->codes & tmpd[1]->codes & CC_MASK) + return CLIP_ALL; + tmps = tmpd; + b = 1 - b; + tmpd = (b == 0) ? tmp0 : tmp1; + } + + // Clip to w in the y axis, bottom + if ((cc & CC_OFF_BOT) != 0) { + s = tmps[0]; + e = tmps[1]; + j = 0; + + if ((s->codes & CC_OFF_BOT) == 0) { + tmpd[j] = s; + j++; + } + // if they intersect put the intersection + // start in and end out or out and in + if (((s->codes ^ e->codes) & CC_OFF_BOT) != 0) { + g3_bottom_intersect(); + tmpd[j] = _tmp; + j++; + } + if ((e->codes & CC_OFF_BOT) == 0) { + tmpd[j] = e; + j++; + } + tmps = tmpd; + if (tmpd[0]->codes & tmpd[1]->codes & CC_MASK) + return CLIP_ALL; + } + + // project those that need it + // if its been clipped it needs it + + // final copy to tmp + LG_memcpy(dest, tmps, 2 * sizeof(g3s_point *)); + + for (i = 0; i < 2; i++) { + _tmp = dest[i]; + + if (_tmp->p3_flags & PF_CLIPPNT) { + // do x + if ((_tmp->codes & CC_OFF_X) == 0 || (_tmp->codes & CC_OFF_Y) != 0) + _tmp->sx = fix_mul(_scrw, (FIX_UNIT + fix_div(_tmp->gX, _tmp->gZ))); + else + _tmp->sx = (_tmp->gX > 0) ? fix_make(grd_bm.w, 0) : 0; + // do y + if ((_tmp->codes & CC_OFF_Y) == 0 || (_tmp->codes & CC_OFF_X) != 0) + _tmp->sy = fix_mul(_scrh, (FIX_UNIT - fix_div(_tmp->gY, _tmp->gZ))); + else + _tmp->sy = (_tmp->gY < 0) ? fix_make(grd_bm.h, 0) : 0; + } + } + + return CLIP_NONE; +} + +int g3_clip_polygon(int n, g3s_point *src[], g3s_point *dest[]) { + int i, j, k; + byte cc; + // assume 10 points max + g3s_point *tmp0[10]; + g3s_point *tmp1[10]; + int b; // current destination buffer + g3s_point **tmps; // pointer to the tmp buffer + g3s_point **tmpd; // pointer to dest buffer + + for (i = 0, cc = 0; i < n; ++i) + cc |= src[i]->codes; + + // if all the same, leave + if (cc == 0) { + // copy src to dest + LG_memcpy(dest, src, n * sizeof(g3s_point *)); + return n; + } + + tnum = 0; + // only cycle through ones you need to, + // which is usually only one side, except + // when right up close + + tmps = src; + tmpd = tmp0; + b = 0; + + // Clip to _d in the z axis if it needs it + if (cc < 0) { + for (i = j = 0; i < n; i++) { + s = tmps[i]; + k = NEXTI(i); + e = tmps[k]; + + // If the inside one is in + // if CC_BEHIND is set, codes is negative + if ((s->codes & CC_BEHIND) == 0) { + tmpd[j] = tmps[i]; + j++; + } + // if they intersect put the intersection + // start in and end out or out and in + if (((s->codes ^ e->codes) & CC_BEHIND) != 0) { + g3_back_intersect(); + tmpd[j] = _tmp; + j++; + } + } + // recalculate codes + n = j; + for (i = 0, cc = 0; i < n; ++i) + cc |= tmpd[i]->codes; + tmps = tmpd; + b = 1 - b; + tmpd = (b == 0) ? tmp0 : tmp1; + } + + // clip to the left, 0 in the x axis + if ((cc & CC_OFF_LEFT) != 0) { + for (i = j = 0; i < n; i++) { + s = tmps[i]; + k = NEXTI(i); + e = tmps[k]; + + if ((s->codes & CC_OFF_LEFT) == 0) { + tmpd[j] = tmps[i]; + j++; + } + // if they intersect put the intersection + // start in and end out or out and in + if (((s->codes ^ e->codes) & CC_OFF_LEFT) != 0) { + g3_left_intersect(); + tmpd[j] = _tmp; + j++; + } + } + // recalculate codes + n = j; + for (i = 0, cc = 0; i < n; ++i) + cc |= tmpd[i]->codes; + tmps = tmpd; + b = 1 - b; + tmpd = (b == 0) ? tmp0 : tmp1; + } + + // Clip to 0 in the y axis, top + if ((cc & CC_OFF_TOP) != 0) { + for (i = j = 0; i < n; i++) { + s = tmps[i]; + k = NEXTI(i); + e = tmps[k]; + + if ((s->codes & CC_OFF_TOP) == 0) { + tmpd[j] = tmps[i]; + j++; + } + // if they intersect put the intersection + // start in and end out or out and in + if (((s->codes ^ e->codes) & CC_OFF_TOP) != 0) { + g3_top_intersect(); + tmpd[j] = _tmp; + j++; + } + } + n = j; + // recalculate codes + for (i = 0, cc = 0; i < n; ++i) + cc |= tmpd[i]->codes; + tmps = tmpd; + b = 1 - b; + tmpd = (b == 0) ? tmp0 : tmp1; + } + + // Clip to w in the x axis, right + if ((cc & CC_OFF_RIGHT) != 0) { + for (i = j = 0; i < n; i++) { + s = tmps[i]; + k = NEXTI(i); + e = tmps[k]; + + if ((s->codes & CC_OFF_RIGHT) == 0) { + tmpd[j] = tmps[i]; + j++; + } + // if they intersect put the intersection + // start in and end out or out and in + if (((s->codes ^ e->codes) & CC_OFF_RIGHT) != 0) { + g3_right_intersect(); + tmpd[j] = _tmp; + j++; + } + } + // copy dest to the src just to be funny + n = j; + for (i = 0, cc = 0; i < n; ++i) + cc |= tmpd[i]->codes; + tmps = tmpd; + b = 1 - b; + tmpd = (b == 0) ? tmp0 : tmp1; + } + + // Clip to w in the y axis, bottom + if ((cc & CC_OFF_BOT) != 0) { + for (i = j = 0; i < n; i++) { + s = tmps[i]; + k = NEXTI(i); + e = tmps[k]; + + if ((s->codes & CC_OFF_BOT) == 0) { + tmpd[j] = tmps[i]; + j++; + } + // if they intersect put the intersection + // start in and end out or out and in + if (((s->codes ^ e->codes) & CC_OFF_BOT) != 0) { + g3_bottom_intersect(); + tmpd[j] = _tmp; + j++; + } + } + n = j; + tmps = tmpd; + } + + // project those that need it + // if its been clipped it needs it + + // final copy to tmp + LG_memcpy(dest, tmps, n * sizeof(g3s_point *)); + + for (i = 0; i < n; i++) { + _tmp = dest[i]; + + if (_tmp->p3_flags & PF_CLIPPNT) { + // do x + if ((_tmp->codes & CC_OFF_X) == 0 || (_tmp->codes & CC_OFF_Y) != 0) + _tmp->sx = fix_mul(_scrw, (FIX_UNIT + fix_div(_tmp->gX, _tmp->gZ))); + else + _tmp->sx = (_tmp->gX > 0) ? fix_make(grd_bm.w, 0) : 0; + // do y + if ((_tmp->codes & CC_OFF_Y) == 0 && (_tmp->codes & CC_OFF_X) != 0) + _tmp->sy = fix_mul(_scrh, (FIX_UNIT - fix_div(_tmp->gY, _tmp->gZ))); + else + _tmp->sy = (_tmp->gY < 0) ? fix_make(grd_bm.h, 0) : 0; + } + } + + return j; +} + +// take care of analyzing +void g3_intersect(void) { + fix rs, gs, bs; + fix re, ge, be; + grs_rgb c; + + _tmp->p3_flags = s->p3_flags | PF_CLIPPNT | PF_PROJECTED; + + if ((_tmp->p3_flags & PF_RGB) != 0) { + c = s->rgb; + rs = (c << 12) & 0x3ff000; + gs = (c << 1) & 0x3ff000; + bs = (c >> 10) & 0x3ff000; + + c = e->rgb; + re = (c << 12) & 0x3ff000; + ge = (c << 1) & 0x3ff000; + be = (c >> 10) & 0x3ff000; + + rs += fix_mul_div(re - rs, num, den); + gs += fix_mul_div(ge - gs, num, den); + bs += fix_mul_div(be - bs, num, den); + + _tmp->rgb = ((rs & 0x3ff000) >> 12) | ((gs & 0x3ff000) >> 1) | ((bs & 0x3ff000) << 10); + + } else { + if ((_tmp->p3_flags & PF_U) != 0) + // zany shifts for shorts + _tmp->uv.u = s->uv.u + fix_mul_div(e->uv.u - s->uv.u, num, den); + + if ((_tmp->p3_flags & PF_V) != 0) + // zany shifts for shorts + _tmp->uv.v = s->uv.v + fix_mul_div(e->uv.v - s->uv.v, num, den); + } + if ((_tmp->p3_flags & PF_I) != 0) + // zany shifts for shorts + _tmp->i = s->i + fix_mul_div(e->i - s->i, num, den); +} + +void g3_back_intersect(void) { + _tmp = &tbuff[tnum++]; + + _a = e->gX - s->gX; + _b = e->gY - s->gY; + _c = e->gZ - s->gZ; + + num = _d - s->gZ; + den = _c; + _tmp->gX = s->gX + fix_mul_div(_a, num, den); + _tmp->gY = s->gY + fix_mul_div(_b, num, den); + _tmp->gZ = _d; + + _tmp->codes = ((_tmp->gX >= _tmp->gZ) ? CC_OFF_RIGHT : ((_tmp->gX <= -_tmp->gZ) ? CC_OFF_LEFT : 0)) | + ((_tmp->gY >= _tmp->gZ) ? CC_OFF_TOP : ((_tmp->gY <= -_tmp->gZ) ? CC_OFF_BOT : 0)); + + g3_intersect(); +} + +void g3_left_intersect(void) { + _tmp = &tbuff[tnum++]; + + _a = e->gX - s->gX; + _b = e->gY - s->gY; + _c = e->gZ - s->gZ; + + num = -s->gZ - s->gX; + den = _a + _c; + _tmp->gY = s->gY + fix_mul_div(_b, num, den); + _tmp->gZ = s->gZ + fix_mul_div(_c, num, den); + _tmp->gX = -_tmp->gZ; + + _tmp->codes = ((_tmp->gY >= _tmp->gZ) ? CC_OFF_TOP : ((_tmp->gY <= -_tmp->gZ) ? CC_OFF_BOT : 0)) | CC_OFF_X; + + g3_intersect(); +} + +void g3_top_intersect(void) { + _tmp = &tbuff[tnum++]; + + _a = e->gX - s->gX; + _b = -e->gY + s->gY; + _c = e->gZ - s->gZ; + + num = s->gY - s->gZ; + den = _b + _c; + _tmp->gX = s->gX + fix_mul_div(_a, num, den); + _tmp->gZ = s->gZ + fix_mul_div(_c, num, den); + _tmp->gY = _tmp->gZ; + + _tmp->codes = ((_tmp->gX >= _tmp->gZ) ? CC_OFF_RIGHT : 0) | CC_OFF_Y | (s->codes & e->codes & CC_OFF_X); + + g3_intersect(); +} + +void g3_right_intersect(void) { + _tmp = &tbuff[tnum++]; + + _a = e->gX - s->gX; + _b = e->gY - s->gY; + _c = e->gZ - s->gZ; + + num = s->gZ - s->gX; + den = _a - _c; + _tmp->gY = s->gY + fix_mul_div(_b, num, den); + _tmp->gZ = s->gZ + fix_mul_div(_c, num, den); + _tmp->gX = _tmp->gZ; + + _tmp->codes = ((_tmp->gY <= -_tmp->gZ) ? CC_OFF_BOT : 0) | CC_OFF_X | (s->codes & e->codes & CC_OFF_Y); + + g3_intersect(); +} + +void g3_bottom_intersect(void) { + _tmp = &tbuff[tnum++]; + + _a = e->gX - s->gX; + _b = -e->gY + s->gY; + _c = e->gZ - s->gZ; + + num = s->gZ + s->gY; + den = _b - _c; + _tmp->gX = s->gX + fix_mul_div(_a, num, den); + _tmp->gZ = s->gZ + fix_mul_div(_c, num, den); + _tmp->gY = -_tmp->gZ; + + _tmp->codes = CC_OFF_Y | (s->codes & e->codes & CC_OFF_X); + g3_intersect(); +} + +/* +void project_point(g3s_point *src[],int n) +{ + g3s_point *p; + ubyte c; + int i; + + for (i=0;ip3_flags&PF_PROJECTED) == 0) { + + // subtract, mask sign bit and shift into place + if (p->gZ < 0) c |= CC_BEHIND; + if (p->gX > p->gZ) c|= CC_OFF_RIGHT; + else if (p->gX <= -p->gZ) c|= CC_OFF_LEFT; + if (p->gY >= p->gZ) c |= CC_OFF_TOP; + else if (p->gY <= -p->gZ) c |= CC_OFF_BOT; + + p->codes = c; + + // project if inside + if (c==0) { + p->sx = fix_mul(_scrw,(FIX_UNIT+fix_div(p->gX,p->gZ))); + p->sy = fix_mul(_scrh,(FIX_UNIT-fix_div(p->gY,p->gZ))); + p->p3_flags |= PF_PROJECTED; + } + } + } +} +*/ \ No newline at end of file diff --git a/engine/src/Libraries/3D/Source/detail.c b/engine/src/Libraries/3D/Source/detail.c new file mode 100644 index 0000000..6e47bde --- /dev/null +++ b/engine/src/Libraries/3D/Source/detail.c @@ -0,0 +1,40 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/3d/RCS/detail.c $ + * $Revision: 1.1 $ + * $Author: kevin $ + * $Date: 1994/08/30 19:29:05 $ + * + */ + +#include "3d.h" + +void (*g3_tmap_func)() = (void (*)())g3_draw_tmap; +void (*g3_lit_tmap_func)() = (void (*)())g3_light_tmap; + +void g3_set_tmaps_linear(void) { + g3_tmap_func = (void (*)())g3_draw_lmap; + g3_lit_tmap_func = (void (*)())g3_light_lmap; +} + +void g3_reset_tmaps(void) { + g3_tmap_func = (void (*)())g3_draw_tmap; + g3_lit_tmap_func = (void (*)())g3_light_tmap; +} diff --git a/engine/src/Libraries/3D/Source/fauxrend.h b/engine/src/Libraries/3D/Source/fauxrend.h new file mode 100644 index 0000000..039e563 --- /dev/null +++ b/engine/src/Libraries/3D/Source/fauxrend.h @@ -0,0 +1,143 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __FAUXREND_H +#define __FAUXREND_H + +#include "3d.h" + +#define POPUPS_ALLOWED + +// structures +typedef struct { + grs_canvas *draw_canvas; + uchar double_buffer; + int xtop, ytop; + fix viewer_zoom; +} fauxrend_context; + +// fauxrend.c +void fauxrend(fauxrend_context *fr); +void fauxrend_set_context(fauxrend_context *frc); +void fauxrend_free_context(fauxrend_context *frc); +fauxrend_context *fauxrend_place_3d(fauxrend_context *fr, uchar db_buf, char axis, int fov, int xc, int yc, int wid, + int hgt); +void fauxrend_zoom(fauxrend_context *fr, fix mod_fac); +void fauxrend_setup_3d(void); +void fauxrend_close_3d(void); +void fauxrend_clear_3d(fauxrend_context *fr, int clear_color); + +void frame_check(void); + +// void eyepos_set(long x, long y, long z, long h, long p, long b); +void eyepos_tele_to(int x, int y); +void eyepos_setone(int which, int val); +void eyepos_moveone(int which, int how); +void eyepos_init(void); + +#define MAP_SC 256 +#define MAP_SH 8 +#define MAP_MK 0xff +#define MAP_MS 8 + +#define EYE_X 0 +#define EYE_Y 1 +#define EYE_Z 2 +#define EYE_H 3 +#define EYE_P 4 +#define EYE_B 5 + +#ifdef __FAUXREND_SRC +long eye[6] = {0, 0, 0, 0, 0, 0}; +long eye_scale[6] = {1, 1, 1, 128, 128, 128}; +#else +extern long eye[6]; +extern long eye_scale[6]; +extern char eye_slew; +#endif + +// axis setup for the zany extra math-o-tron 3d +//#define AXIS_ORDER X_AXIS,Z_AXIS,Y_AXIS +//#define ANGLE_ORDER ORDER_ZXY + +//#define AXIS_ORDER X_AXIS,-Y_AXIS,Z_AXIS +//#define AXIS_ORDER X_AXIS,Y_AXIS,Z_AXIS + +#define AXIS_ORDER AXIS_RIGHT, AXIS_DOWN, AXIS_IN + +#define ANGLE_ORDER ORDER_YXZ +#define pitch tx +#define bank tz +#define head ty +#define xaxis x +#define yaxis y +#define zaxis z + +// conversions +#define build_fix_angle(ang) ((65536 * (ang)) / 360) + +// defaults +#define DEFAULT_FOV 80 +#define DEFAULT_AXIS 'X' +#define DEFAULT_PT_CNT 80 // sure, why not + +// masks for quadrant/octant free facing check +#define FMK_NW (1 << 0) +#define FMK_EW (1 << 1) +#define FMK_SW (1 << 2) +#define FMK_WW (1 << 3) +#define FMK_D1 (1 << 4) +#define FMK_D3 (1 << 5) +#define FMK_D5 (1 << 6) +#define FMK_D7 (1 << 7) + +#define MK_O_N (0) +#define MK_O_E (1) +#define MK_O_S (2) +#define MK_O_W (3) + +#define HG_NW (1 << 1) +#define HG_NE (1 << 2) +#define HG_SE (1 << 3) +#define HG_SW (1 << 0) + +/* note: 9 is unused + * + * 1 B 3 + * 0 2 + * 8 D A not detected yet + * 4 6 + * 5 C 7 + */ + +#define QUAD_N_BASE 0 +#define QUAD_S_BASE 4 +#define QUAD_A_BASE 8 +#define QUAD_X_OFF 2 +#define QUAD_D_OFF 1 +#define QUAD_CENTER 0xB + +#define FACE_FLOOR 0 +#define FACE_CIEL 1 +#define FACE_WALLS 2 + +#define FLOOR_COL 32 +#define CIEL_COL 44 +#define WALL_COL 56 + +#endif diff --git a/engine/src/Libraries/3D/Source/fov.c b/engine/src/Libraries/3D/Source/fov.c new file mode 100644 index 0000000..6cab7bc --- /dev/null +++ b/engine/src/Libraries/3D/Source/fov.c @@ -0,0 +1,100 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// +// $Source: r:/prj/lib/src/3d/RCS/fov.asm $ +// $Revision: 1.4 $ +// $Author: jaemz $ +// $Date: 1994/10/26 21:30:21 $ +// +// Routines to get FOV and zoom +// +// $Log: fov.asm $ +// Revision 1.4 1994/10/26 21:30:21 jaemz +// Added get_zoom refresh aspect rat from 2d +// +// Revision 1.3 1994/06/02 15:07:37 junochoe +// changed matrix_scale to _matrix_scale +// +// +// Revision 1.2 1993/08/10 22:54:07 dc +// add _3d.inc to includes +// +// Revision 1.1 1993/05/04 17:39:45 matt +// Initial revision +// +// + +#include "3d.h" +#include "GlobalV.h" +#include "lg.h" + +// returns current field of view. returns ax=x FOV, bx=y FOV +// trashes eax,ebx,ecx,edx,edi +// formula is fov = acos( (x-z) / (x+z) ) where x,z are the matrix scale values +void g3_get_FOV(fixang *x, fixang *y) { + fix X2, Y2, Z2; + + Z2 = fix_mul(_matrix_scale.gZ, _matrix_scale.gZ); // get z squared + + // compute y + Y2 = fix_mul(_matrix_scale.gY, _matrix_scale.gY); // get y squared + *y = fix_acos(fix_div(Y2 - Z2, Y2 + Z2)); + + // compute x + X2 = fix_mul(_matrix_scale.gX, _matrix_scale.gX); // get z squared + *x = fix_acos(fix_div(X2 - Z2, X2 + Z2)); +} + +// returns zoom for a desired FOV. +// takes bx=FOV angle, al=axis ('X' or 'Y'), ecx=window width, edx=window height +// returns in eax. trashes all but ebp + +fix g3_get_zoom(char axis, fixang angle, int window_width, int window_height) { + fix sin_val, cos_val; + fix unscalezoom, temp1; + long templong; + + fix_sincos(angle, &sin_val, &cos_val); // call fix_sincos ;angle in + // bx + temp1 = fix_div(f1_0 - cos_val, cos_val + f1_0); + + unscalezoom = fix_sqrt(temp1); // call fix_sqrt_ ;eax = unscaled zoom + + // now, temp1 would be zoom if not for window and pixel matrix scaling. + // correct for these + + // get pixel ratio + pixel_ratio = grd_cap->aspect; + + // get matrix scale value for given window size + templong = fix_mul_div(window_height, pixel_ratio, + window_width); // imul pixel_ratio ;height * pixrat + // idiv ebx ;eax = h * pixrat / w + // window and pixrat scaling affects y. see if y FOV requested + if (templong <= f1_0) // cmp eax,f1_0 ;< 1.0? jle scale_x ;scale x + { + if (axis != 'X') + return (unscalezoom); + return (fix_mul(unscalezoom, templong)); + } else { + if (axis != 'Y') + return (unscalezoom); + return (fix_div(unscalezoom, templong)); + } +} diff --git a/engine/src/Libraries/3D/Source/instance.c b/engine/src/Libraries/3D/Source/instance.c new file mode 100644 index 0000000..71bafce --- /dev/null +++ b/engine/src/Libraries/3D/Source/instance.c @@ -0,0 +1,451 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// +// $Source: r:/prj/lib/src/3d/RCS/instance.asm $ +// $Revision: 1.8 $ +// $Author: jaemz $ +// $Date: 1994/10/24 01:05:30 $ +// +// Instancing routines +// +// $Log: instance.asm $ +// Revision 1.8 1994/10/24 01:05:30 jaemz +// Fixed inverted pitch problem by saving state of ecx +// +// Revision 1.7 1994/10/12 01:09:06 jaemz +// Inverted wtoo_matrix to make it correct +// for lighting +// +// Revision 1.6 1994/09/20 13:32:48 jaemz +// Lighting support +// +// Revision 1.5 1994/08/18 03:46:57 jaemz +// Changed stereo glob names to have underscore for c +// +// Revision 1.4 1994/07/15 14:13:28 jaemz +// Added _view_position with an underscore to make it c readable +// +// Revision 1.3 1993/08/10 22:54:12 dc +// add _3d.inc to includes +// +// Revision 1.2 1993/06/22 18:35:32 kaboom +// Changed g3_matrix_x_matrix to g3_matrix_x_matrix_ so it's callable +// from watcom C w/register passing. +// +// Revision 1.1 1993/05/04 17:39:49 matt +// Initial revision +// +// + +#include "3d.h" +#include "GlobalV.h" +#include "lg.h" + +// externs +extern void angles_2_matrix(g3s_angvec *angles, g3s_matrix *view_matrix, int rotation_order); + +// prototypes +uchar instance_x(fixang tx); +uchar instance_y(fixang ty); +uchar instance_z(fixang tz); +void instance_matrix(g3s_matrix *src, g3s_matrix *dest); +uchar save_context(void); +uchar g3_start_object_angles_zy(g3s_vector *p, fixang ty, fixang tz, int rotation_order); +uchar start_obj_common(g3s_vector *p, g3s_angvec *o, int rotation_order); + +#define MAX_INSTANCE_DEPTH 5 + +#define CONTEXT_SIZE (sizeof(g3s_matrix) + sizeof(g3s_vector)) + +// stack for pushed context while instanced +char context_stack[MAX_INSTANCE_DEPTH * CONTEXT_SIZE]; +char *cstack_ptr = context_stack; + +long cstack_depth; + +// takes esi=position. No orientation, just offset +uchar g3_start_object(g3s_vector *p) // position only (no orientation) +{ + if (save_context()) + return 0; + + // compute new view position + _view_position.gX -= p->gX; + _view_position.gY -= p->gY; + _view_position.gZ -= p->gZ; + + return -1; // success +} + +// takes esi=position, ecx=rotation order, angles=eax,ebx,edx +uchar g3_start_object_angles_xyz(g3s_vector *p, fixang tx, fixang ty, fixang tz, int rotation_order) { + g3s_angvec temp_angles; + + if (save_context()) + return 0; + + temp_angles.tx = tx; + temp_angles.ty = ty; + temp_angles.tz = tz; + + return (start_obj_common(p, &temp_angles, rotation_order)); +} + +// takes esi=position, edi=orientation vector, ecx=rotation order +uchar g3_start_object_angles_v(g3s_vector *p, g3s_angvec *o, int rotation_order) { + if (save_context()) + return 0; + return (start_obj_common(p, o, rotation_order)); +} + +// takes esi=position, edi=orientation vector, ecx=rotation order +uchar start_obj_common(g3s_vector *p, g3s_angvec *o, int rotation_order) { + g3s_matrix temp_matrix; + + // compute new context + _view_position.gX -= p->gX; + _view_position.gY -= p->gY; + _view_position.gZ -= p->gZ; + + // copy obj offset to world to obj structure + // used for lighting, dude + _wtoo_position = *p; + + angles_2_matrix(o, &temp_matrix, rotation_order); + + // rotate view vector through instance matrix + g3_vec_rotate(&_view_position, &_view_position, &temp_matrix); + + // save off to the obj_to_world matrix + // untransposed, to get inverse + _wtoo_matrix = temp_matrix; + + g3_transpose(&temp_matrix); // transpose esi in place + instance_matrix(&temp_matrix, &view_matrix); + + return -1; // ok! +} + +// dest=c1*s1+c2*s2 +fix update_m(fix c1, fix s1, fix c2, fix s2) { + int64_t r = fix64_mul(c1, s1) + fix64_mul(c2, s2); + return fix64_to_fix(r); +} + +// dest=c1*s1-c2*s2 +fix update_ms(fix c1, fix s1, fix c2, fix s2) { + int64_t r = fix64_mul(c1, s1) - fix64_mul(c2, s2); + return fix64_to_fix(r); +} + +// rotate around the specified axis. angle = ebx +// takes esi=position, +uchar g3_start_object_angles_y(g3s_vector *p, fixang ty) { + if (save_context()) + return 0; + + // compute new view position + _view_position.gX -= p->gX; + _view_position.gY -= p->gY; + _view_position.gZ -= p->gZ; + + return (instance_y(ty)); +} + +// get sin & cos - angles still in ebx +uchar instance_y(fixang ty) { + fix sin_y, cos_y; + fix temp1, temp2, temp3; + int64_t r; + + fix_sincos(ty, &sin_y, &cos_y); + + // rotate viewer vars + r = fix64_mul(_view_position.gX, cos_y) - fix64_mul(_view_position.gZ, sin_y); + temp1 = fix64_to_fix(r); + + r = fix64_mul(_view_position.gX, sin_y) + fix64_mul(_view_position.gZ, cos_y); + + _view_position.gX = temp1; + _view_position.gZ = fix64_to_fix(r); + + // now modify matrix + temp1 = update_ms(cos_y, vm1, sin_y, vm7); + temp2 = update_ms(cos_y, vm2, sin_y, vm8); + temp3 = update_ms(cos_y, vm3, sin_y, vm9); + vm7 = update_m(sin_y, vm1, cos_y, vm7); + vm8 = update_m(sin_y, vm2, cos_y, vm8); + vm9 = update_m(sin_y, vm3, cos_y, vm9); + vm1 = temp1; + vm2 = temp2; + vm3 = temp3; + + // we're done + return -1; // ok! +} + +// rotate around the specified axis. angle = ebx +// takes esi=position, +uchar g3_start_object_angles_x(g3s_vector *p, fixang tx) { + if (save_context()) + return 0; + + // compute new view position + _view_position.gX -= p->gX; + _view_position.gY -= p->gY; + _view_position.gZ -= p->gZ; + + return (instance_x(tx)); +} + +// get sin & cos - angles still in ebx +uchar instance_x(fixang tx) { + fix temp1, temp2, temp3; + fix sin_x, cos_x; + int64_t r; + + fix_sincos(tx, &sin_x, &cos_x); + + // rotate viewer vars + r = fix64_mul(_view_position.gZ, sin_x) + fix64_mul(_view_position.gY, cos_x); + temp1 = fix64_to_fix(r); + + r = fix64_mul(_view_position.gZ, cos_x) - fix64_mul(_view_position.gY, sin_x); + _view_position.gY = temp1; + _view_position.gZ = fix64_to_fix(r); + + // now modify matrix + + temp1 = update_m(cos_x, vm4, sin_x, vm7); + temp2 = update_m(cos_x, vm5, sin_x, vm8); + temp3 = update_m(cos_x, vm6, sin_x, vm9); + vm7 = update_ms(cos_x, vm7, sin_x, vm4); + vm8 = update_ms(cos_x, vm8, sin_x, vm5); + vm9 = update_ms(cos_x, vm9, sin_x, vm6); + vm4 = temp1; + vm5 = temp2; + vm6 = temp3; + + // we're done + return -1; // ok! +} + +// rotate around the specified axis. angle = ebx +// takes esi=position, +uchar g3_start_object_angles_z(g3s_vector *p, fixang tz) { + if (save_context()) + return 0; + + // compute new view position + _view_position.gX -= p->gX; + _view_position.gY -= p->gY; + _view_position.gZ -= p->gZ; + + return (instance_z(tz)); +} + +// get sin & cos - angles still in ebx +uchar instance_z(fixang tz) { + fix temp1, temp2, temp3; + fix sin_z, cos_z; + int64_t r; + + fix_sincos(tz, &sin_z, &cos_z); + + // rotate viewer vars + r = fix64_mul(_view_position.gY, sin_z) + fix64_mul(_view_position.gX, cos_z); + temp1 = fix64_to_fix(r); + + r = fix64_mul(_view_position.gY, cos_z) - fix64_mul(_view_position.gX, sin_z); + _view_position.gX = temp1; + _view_position.gY = fix64_to_fix(r); + + // now modify matrix + temp1 = update_m(cos_z, vm1, sin_z, vm4); + temp2 = update_m(cos_z, vm2, sin_z, vm5); + temp3 = update_m(cos_z, vm3, sin_z, vm6); + vm4 = update_ms(cos_z, vm4, sin_z, vm1); + vm5 = update_ms(cos_z, vm5, sin_z, vm2); + vm6 = update_ms(cos_z, vm6, sin_z, vm3); + vm1 = temp1; + vm2 = temp2; + vm3 = temp3; + + // we're done + return -1; // ok! +} + +// rotate around the specified axes. angles = ebx edx. esi=position +uchar g3_start_object_angles_xy(g3s_vector *p, fixang tx, fixang ty, int rotation_order) { + if (save_context()) + return 0; + + // compute new view position + _view_position.gX -= p->gX; + _view_position.gY -= p->gY; + _view_position.gZ -= p->gZ; + + if ((rotation_order & 1) == 0) // check xy order + { + instance_x(tx); + return (instance_y(ty)); + } else { + instance_y(ty); + return (instance_x(tx)); + } +} + +// rotate around the specified axes. angles = ebx edx. esi=position +uchar g3_start_object_angles_xz(g3s_vector *p, fixang tx, fixang tz, int rotation_order) { + if (save_context()) + return 0; + + // compute new view position + _view_position.gX -= p->gX; + _view_position.gY -= p->gY; + _view_position.gZ -= p->gZ; + + if ((rotation_order & 2) == 0) // check xz order + { + instance_x(tx); + return (instance_z(tz)); + } else { + instance_z(tz); + return (instance_x(tx)); + } +} + +// rotate around the specified axes. angles = ebx edx. esi=position +uchar g3_start_object_angles_yz(g3s_vector *p, fixang ty, fixang tz, int rotation_order) { + if (save_context()) + return 0; + + // compute new view position + _view_position.gX -= p->gX; + _view_position.gY -= p->gY; + _view_position.gZ -= p->gZ; + + if ((rotation_order & 4) == 0) // check yz order + { + instance_y(ty); + return (instance_z(tz)); + } else { + instance_z(tz); + return (instance_y(ty)); + } +} + +// rotate around the specified axes. angles = ebx edx. esi=position +uchar g3_start_object_angles_zy(g3s_vector *p, fixang ty, fixang tz, int rotation_order) { + if (save_context()) + return 0; + + // compute new view position + _view_position.gX -= p->gX; + _view_position.gY -= p->gY; + _view_position.gZ -= p->gZ; + + instance_z(tz); + return (instance_y(ty)); +} + +// takes esi=position, edi=object matrix +uchar g3_start_object_matrix(g3s_vector *p, g3s_matrix *m) { + g3s_matrix temp_matrix; + + if (save_context()) + return 0; + + // compute new view position + _view_position.gX -= p->gX; + _view_position.gY -= p->gY; + _view_position.gZ -= p->gZ; + + // rotate view vector through instance matrix + g3_vec_rotate(&_view_position, &_view_position, m); + + // copy to temp matrix, since instance routine transposes in place + g3_copy_transpose(&temp_matrix, m); + instance_matrix(&temp_matrix, &view_matrix); + + return -1; // ok! +} + +// save the current view matrix + view position. +// returns carry set if cannot save. saves all regs +uchar save_context(void) { + if (cstack_depth == MAX_INSTANCE_DEPTH) + return 1; + + cstack_depth++; + + // save current context + *(g3s_matrix *)cstack_ptr = view_matrix; + cstack_ptr += sizeof(g3s_matrix); + *(g3s_vector *)cstack_ptr = _view_position; + cstack_ptr += sizeof(g3s_vector); + + return 0; +} + +// scales an object within an object context +// argument in eax per c convention +// trashes ecx edx and eax +void g3_scale_object(fix s) { + // scale vm by scale, and divide view_position + // down by scale + + _view_position.gX = fix_div(_view_position.gX, s); + _view_position.gY = fix_div(_view_position.gY, s); + _view_position.gZ = fix_div(_view_position.gZ, s); + + // scale vm up by scale + vm1 = fix_mul(vm1, s); + vm2 = fix_mul(vm2, s); + vm3 = fix_mul(vm3, s); + vm4 = fix_mul(vm4, s); + vm5 = fix_mul(vm5, s); + vm6 = fix_mul(vm6, s); + vm7 = fix_mul(vm7, s); + vm8 = fix_mul(vm8, s); + vm9 = fix_mul(vm9, s); +} + +void g3_end_object(void) { + if (cstack_depth == 0) + return; + + cstack_depth--; + + cstack_ptr -= sizeof(g3s_vector); + _view_position = *(g3s_vector *)cstack_ptr; + cstack_ptr -= sizeof(g3s_matrix); + view_matrix = *(g3s_matrix *)cstack_ptr; +} + +// edi = esi * edi. esi should be transposed before calling +void instance_matrix(g3s_matrix *src, g3s_matrix *dest) { + g3s_matrix temp_matrix2; + + // do multiply + g3_matrix_x_matrix(&temp_matrix2, src, dest); + + // copy to real dest + *dest = temp_matrix2; +} diff --git a/engine/src/Libraries/3D/Source/interp.c b/engine/src/Libraries/3D/Source/interp.c new file mode 100644 index 0000000..751f00c --- /dev/null +++ b/engine/src/Libraries/3D/Source/interp.c @@ -0,0 +1,682 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// +// $Source: r:/prj/lib/src/3d/RCS/interp.asm $ +// $Revision: 1.19 $ +// $Author: jaemz $ +// $Date: 1994/10/13 20:51:43 $ +// +// 3d object interpreter +// +// $Log: interp.asm $ +// Revision 1.19 1994/10/13 20:51:43 jaemz +// Fixed lighting bug +// +// Revision 1.18 1994/09/20 13:34:36 jaemz +// Lighting +// +// Revision 1.14 1994/08/18 03:45:30 jaemz +// Added stereo to objects for real, reevals bsp tree +// +// Revision 1.13 1994/07/15 14:13:34 jaemz +// Added _view_position with an underscore to make it c readable +// +// Revision 1.12 1994/05/19 09:46:39 kevin +// g3_draw_tmap now uses watcom register parameter passing conventions. +// +// Revision 1.11 1994/02/08 20:46:17 kaboom +// Updated usage of gour_flag. +// +// Revision 1.10 1993/11/18 10:08:11 dc +// first set of debug setup for the interpreter +// +// Revision 1.9 1993/10/25 16:24:46 kaboom +// Changed call to polygon routine to use new calling convention. +// +// Revision 1.8 1993/10/02 09:27:49 kaboom +// Added vtext_tab. Also updated texture map opcode to call the uv perspective +// mapper. +// +// Revision 1.7 1993/09/15 04:01:23 dc +// tmap interface, well, except there isnt a tmapper +// +// Revision 1.6 1993/08/10 22:54:13 dc +// add _3d.inc to includes +// +// Revision 1.5 1993/08/04 00:47:10 dc +// support for new interpreter opcodes +// +// Revision 1.4 1993/06/03 14:34:00 matt +// Removed int -> sfix conversion in defres_i & setshade +// +// Revision 1.3 1993/06/02 16:57:01 matt +// Gouraud polys handled differently: gouraud base now added at poly draw +// time, not point definition time. +// +// Revision 1.2 1993/05/27 18:11:46 matt +// Added getparms opcodes, and changed parameter passing scheme. +// +// Revision 1.1 1993/05/04 17:39:50 matt +// Initial revision +// +// + +#include "3d.h" +#include "GlobalV.h" +#include "lg.h" +//#include +//#include <_stdarg.h> +//#include + +// prototypes; +uchar *do_eof(uchar *); +uchar *do_jnorm(uchar *); +uchar *do_ldjnorm(uchar *); +uchar *do_ljnorm(uchar *); +uchar *do_lnres(uchar *); +uchar *do_multires(uchar *); +uchar *do_polyres(uchar *); +uchar *do_setcolor(uchar *); +uchar *do_sortnorm(uchar *); +uchar *do_debug(uchar *); +uchar *do_setshade(uchar *); +uchar *do_goursurf(uchar *); +uchar *do_x_rel(uchar *); +uchar *do_y_rel(uchar *); +uchar *do_z_rel(uchar *); +uchar *do_xy_rel(uchar *); +uchar *do_xz_rel(uchar *); +uchar *do_yz_rel(uchar *); +uchar *do_icall_p(uchar *); +uchar *do_icall_b(uchar *); +uchar *do_icall_h(uchar *); +uchar *do_sfcal(uchar *); +uchar *do_defres(uchar *); +uchar *do_defres_i(uchar *); +uchar *do_getparms(uchar *); +uchar *do_getparms_i(uchar *); +uchar *do_gour_p(uchar *); +uchar *do_gour_vc(uchar *); +uchar *do_getvcolor(uchar *); +uchar *do_getvscolor(uchar *); +uchar *do_rgbshades(uchar *); +uchar *do_draw_mode(uchar *); +uchar *do_getpcolor(uchar *); +uchar *do_getpscolor(uchar *); +uchar *do_scaleres(uchar *); +uchar *do_vpnt_p(uchar *); +uchar *do_vpnt_v(uchar *); +uchar *do_setuv(uchar *); +uchar *do_uvlist(uchar *); +uchar *do_tmap_op(uchar *); +uchar *do_dbg(uchar *); + +extern int check_and_draw_common(long c, int n_verts, g3s_phandle *p); +extern int draw_poly_common(long c, int n_verts, g3s_phandle *p); +extern void g3_light_obj(g3s_phandle norm, g3s_phandle pos); + +void interpreter_loop(uchar *object); + +// globals +extern char gour_flag; // gour flag for actual polygon drawer + +#define OP_EOF 0 +#define OP_JNORM 1 + +#define n_ops 40 + void *opcode_table[n_ops] = { + do_eof, do_jnorm, do_lnres, do_multires, do_polyres, do_setcolor, do_sortnorm, + do_debug, do_setshade, do_goursurf, do_x_rel, do_y_rel, do_z_rel, do_xy_rel, + do_xz_rel, do_yz_rel, do_icall_p, do_icall_b, do_icall_h, 0, do_sfcal, + do_defres, do_defres_i, do_getparms, do_getparms_i, do_gour_p, do_gour_vc, do_getvcolor, + do_getvscolor, do_rgbshades, do_draw_mode, do_getpcolor, do_getpscolor, do_scaleres, do_vpnt_p, + do_vpnt_v, do_setuv, do_uvlist, do_tmap_op, do_dbg}; + +#define N_RES_POINTS 1000 +#define PARM_DATA_SIZE 4 * 100 + +#define N_VCOLOR_ENTRIES 32 +#define N_VPOINT_ENTRIES 32 +#define N_VTEXT_ENTRIES 64 + +// This determines when we no longer reevaluate +// the bsp tree. It corresponds to the tan of +// 7.12 degrees, which empirically seems fine +// we might have to set it lower some day if +// you see polygons drop out in stereo +#define STEREO_DIST_LIM = 0x2000 + +g3s_point *resbuf[N_RES_POINTS]; +g3s_point *poly_buf[100]; + +// clang-format off +uchar _vcolor_tab[N_VCOLOR_ENTRIES] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +g3s_point *_vpoint_tab[N_VPOINT_ENTRIES] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +grs_bitmap *_vtext_tab[N_VTEXT_ENTRIES] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +// clang-format on + +// ptr to stack parms +ubyte *parm_ptr; //va_list parm_ptr; + +// space for parms to objects +char parm_data[PARM_DATA_SIZE]; + +char _itrp_gour_flg = 0; +char _itrp_wire_flg = 0; +char _itrp_check_flg = 0; + +// MLA not used, - uchar *struct_ptr; + +// c callable context setting routines + +// takes ptr to object +// this is bullshit, man, takes ptr to object on the freakin' stack! +void g3_interpret_object(ubyte *object_ptr, ...) { + int i, scale; + + // lighting stuff, params are on the stack + // so don't sweat it + // set fill type so 2d can light the thang + if ((_g3d_light_type & (LT_SPEC | LT_DIFF)) != 0) { + gr_set_fill_type(FILL_CLUT); + if (_g3d_light_type == LT_DIFF) + opcode_table[OP_JNORM] = &do_ldjnorm; + else + opcode_table[OP_JNORM] = &do_ljnorm; + } + + // get addr of stack parms + parm_ptr = (ubyte *)((&object_ptr) + sizeof(object_ptr)); //va_start(parm_ptr, object_ptr); + + // mark res points as free + LG_memset(resbuf, 0, N_RES_POINTS * 4); + + // scale view vector for scale + scale = *(short *)(object_ptr - 2); + if (scale) { + if (scale > 0) { + _view_position.gX >>= scale; + _view_position.gY >>= scale; + _view_position.gZ >>= scale; + } else { + int temp; + + scale = -scale; + + temp = (((ulong)_view_position.gX) >> 16); // get high 16 bits + // FIXME: DG: I guess they meant &, not && + // shamaz: Fixed that + if (((temp << scale) & 0xffff0000) != 0) + return; // overflow + temp = (((ulong)_view_position.gY) >> 16); // get high 16 bits + if (((temp << scale) & 0xffff0000) != 0) + return; // overflow + temp = (((ulong)_view_position.gZ) >> 16); // get high 16 bits + if (((temp << scale) & 0xffff0000) != 0) + return; // overflow + + _view_position.gX <<= scale; + _view_position.gY <<= scale; + _view_position.gZ <<= scale; + } + } + + interpreter_loop(object_ptr); + + // free res points + for (i = N_RES_POINTS - 1; i >= 0; i--) + if (resbuf[i]) + freepnt(resbuf[i]); + + // set lighting back to how it was + if ((_g3d_light_type & (LT_SPEC | LT_DIFF)) != 0) { + gr_set_fill_type(FILL_NORM); + opcode_table[OP_JNORM] = &do_jnorm; + } + +} + +// interpret the object +void interpreter_loop(uchar *object) { + do { + object = ((uchar * (*)(uchar *)) opcode_table[*(short *)object])(object); + } while (object); +} + +// opcodes. [ebp] points at op on entry +uchar *do_debug(uchar *opcode) { return 0; } + +uchar *do_eof(uchar *opcode) // and return extra level +{ + return 0; +} + +// jnorm lbl,px,py,pz,nx,ny,nz +// v=viewer coords-p +// if (n*v)<0 then branch to lbl +uchar *do_jnorm(uchar *opcode) { + if (g3_check_normal_facing((g3s_vector *)(opcode + 16), (g3s_vector *)(opcode + 4))) + return opcode + 28; // surface is visible. continue + else + return opcode + (*(short *)(opcode + 2)); // surface not visible +} + +// lnres pnt0,pnt1 +uchar *do_lnres(uchar *opcode) { + g3_draw_line(resbuf[*(unsigned short *)(opcode + 2)], resbuf[*(unsigned short *)(opcode + 4)]); + return opcode + 6; +} + +uchar *do_multires(uchar *opcode) { + short count; + + count = *(short *)(opcode + 2); + + g3_transform_list(count, (g3s_phandle *)(resbuf + (*(short *)(opcode + 4))), (g3s_vector *)(opcode + 6)); + return opcode + 6 + (count * 12); +} + +// this should do some cute matrix transform trick, not this ugly hack + +// that kid from the wrong side came over my house again, decapitated all my +// dolls +// and if you bore me, you lose your soul to me - "Gepetto", Belly, +// _Star_ +uchar *do_scaleres(uchar *opcode) { + // MLA - this routine appears to be buggy and can't possibly work, so I'm not + // doing it yet. + DEBUG("%s Call Mark!", __FUNCTION__); + + /* int count,scale; + long temp_pnt[3]; + g3s_phandle temp_hand; + + count = * (unsigned short *) (opcode+2); + scale = * (unsigned short *) (opcode+4); + temp_hand = (g3s_phandle) (parm_data+(* (unsigned short *) + (opcode+6))); + + opcode += 8; + do + { + } + while (--count>0); + + return opcode; + */ + return 0; +} + +// these put the address of an old point in the interpreter respnt array +// note they will get freed when the interpreter punts +uchar *do_vpnt_p(uchar *opcode) { + resbuf[*(short *)(opcode + 4)] = (g3s_point *)(*(long *)(parm_data + (*(unsigned short *)(opcode + 2)))); + return opcode + 6; +} + +uchar *do_vpnt_v(uchar *opcode) { + resbuf[*(short *)(opcode + 4)] = _vpoint_tab[(*(unsigned short *)(opcode + 2)) >> 2]; + return opcode + 6; +} + +uchar *do_defres(uchar *opcode) { + resbuf[*(unsigned short *)(opcode + 2)] = g3_transform_point((g3s_vector *)(opcode + 4)); + return opcode + 16; +} + +uchar *do_defres_i(uchar *opcode) { + g3s_phandle temphand; + + temphand = g3_transform_point((g3s_vector *)(opcode + 4)); + resbuf[*(unsigned short *)(opcode + 2)] = temphand; + + temphand->i = *(short *)(opcode + 16); + temphand->p3_flags |= PF_I; + + return opcode + 18; +} + +// polyres cnt,pnt0,pnt1,... +uchar *do_polyres(uchar *opcode) { + int count, count2; + + count2 = count = *(unsigned short *)(opcode + 2); + opcode += 4; + while (--count >= 0) { + poly_buf[count] = resbuf[*(unsigned short *)(opcode + (count << 1))]; + } + + opcode += count2 << 1; + + gour_flag = _itrp_gour_flg; + if ((_itrp_check_flg & 1) == 0) + draw_poly_common(gr_get_fcolor(), count2, poly_buf); + else + check_and_draw_common(gr_get_fcolor(), count2, poly_buf); + + return opcode; +} + +uchar *do_sortnorm(uchar *opcode) { + if (g3_check_normal_facing((g3s_vector *)(opcode + 14), (g3s_vector *)(opcode + 2))) { + interpreter_loop(opcode + (*(short *)(opcode + 26))); + interpreter_loop(opcode + (*(short *)(opcode + 28))); + } else { + interpreter_loop(opcode + (*(short *)(opcode + 28))); + interpreter_loop(opcode + (*(short *)(opcode + 26))); + } + + return opcode + 30; +} + +uchar *do_goursurf(uchar *opcode) { + gouraud_base = (*(short *)(opcode + 2)) << 8; + _itrp_gour_flg = 2; + return opcode + 4; +} + +uchar *do_gour_p(uchar *opcode) { + gouraud_base = parm_data[(*(short *)(opcode + 2))] << 8; + _itrp_gour_flg = 2; + return opcode + 4; +} + +uchar *do_gour_vc(uchar *opcode) { + gouraud_base = ((long)_vcolor_tab[*(unsigned short *)(opcode + 2)]) << 8; + _itrp_gour_flg = 2; + return opcode + 4; +} + +uchar *do_draw_mode(uchar *opcode) { + short flags; + + flags = *(short *)(opcode + 2); + _itrp_wire_flg = flags >> 8; + flags &= 0x00ff; + flags <<= 1; + _itrp_check_flg = flags >> 8; + flags &= 0x00ff; + flags <<= 2; + _itrp_gour_flg = flags - 1; + return opcode + 4; +} + +uchar *do_setshade(uchar *opcode) { + int i; + uchar *new_opcode; + g3s_phandle temphand; + + i = *(unsigned short *)(opcode + 2); // get number of shades + new_opcode = opcode + 4 + (i << 2); + + while (--i >= 0) { + temphand = resbuf[*(unsigned short *)(opcode + 4 + (i << 2))]; // get point handle + temphand->i = *(short *)(opcode + 6 + (i << 2)); + temphand->p3_flags |= PF_I; + } + + return new_opcode; +} + +uchar *do_rgbshades(uchar *opcode) { + uchar *new_opcode; + int i; + g3s_phandle temphand; + + i = *(unsigned short *)(opcode + 2); // get number of shades + new_opcode = opcode + 4; + while (--i >= 0) { + temphand = resbuf[*(unsigned short *)new_opcode]; // get point handle + temphand->rgb = *(long *)(new_opcode + 2); + temphand->p3_flags |= PF_RGB; + new_opcode += 10; + } + return new_opcode; +} + +uchar *do_setuv(uchar *opcode) { + g3s_phandle temphand; + + temphand = resbuf[*(unsigned short *)(opcode + 2)]; // get point handle + temphand->uv.u = (*(unsigned long *)(opcode + 4)) >> 8; + temphand->uv.v = (*(unsigned long *)(opcode + 8)) >> 8; + temphand->p3_flags |= PF_U | PF_V; + + return opcode + 12; +} + +uchar *do_uvlist(uchar *opcode) { + int i; + g3s_phandle temphand; + + i = *(unsigned short *)(opcode + 2); // get number of shades + opcode += 4; + while (--i >= 0) { + temphand = resbuf[*(unsigned short *)opcode]; // get point handle + temphand->uv.u = (*(unsigned long *)(opcode + 2)) >> 8; + temphand->uv.v = (*(unsigned long *)(opcode + 6)) >> 8; + temphand->p3_flags |= PF_U | PF_V; + opcode += 10; + } + + return opcode; +} + +// should we be hacking _itrp_gour_flg? +uchar *do_setcolor(uchar *opcode) { + gr_set_fcolor(*(unsigned short *)(opcode + 2)); + _itrp_gour_flg = 0; + return opcode + 4; +} + +uchar *do_getvcolor(uchar *opcode) { + gr_set_fcolor(_vcolor_tab[*(unsigned short *)(opcode + 2)]); + _itrp_gour_flg = 0; + return opcode + 4; +} + +uchar *do_getpcolor(uchar *opcode) { + gr_set_fcolor(*(unsigned short *)(parm_data + (*(unsigned short *)(opcode + 2)))); + _itrp_gour_flg = 0; + return opcode + 4; +} + +uchar *do_getvscolor(uchar *opcode) { + short temp; + + temp = (byte)_vcolor_tab[*(unsigned short *)(opcode + 2)]; + temp |= (*(short *)(opcode + 4)) << 8; + gr_set_fcolor(gr_get_light_tab()[temp]); + return opcode + 6; +} + +uchar *do_getpscolor(uchar *opcode) { + short temp; + + temp = (unsigned short)parm_data[*(unsigned short *)(opcode + 2)]; + temp &= 0x00ff; + temp |= (*(short *)(opcode + 4)) << 8; + gr_set_fcolor(gr_get_light_tab()[temp]); + return opcode + 6; +} + +uchar *do_x_rel(uchar *opcode) { + resbuf[*(short *)(opcode + 2)] = g3_copy_add_delta_x(resbuf[*(short *)(opcode + 4)], *(fix *)(opcode + 6)); + return opcode + 10; +} + +uchar *do_y_rel(uchar *opcode) { + resbuf[*(short *)(opcode + 2)] = g3_copy_add_delta_y(resbuf[*(short *)(opcode + 4)], *(fix *)(opcode + 6)); + return opcode + 10; +} + +uchar *do_z_rel(uchar *opcode) { + resbuf[*(short *)(opcode + 2)] = g3_copy_add_delta_z(resbuf[*(short *)(opcode + 4)], *(fix *)(opcode + 6)); + return opcode + 10; +} + +uchar *do_xy_rel(uchar *opcode) { + resbuf[*(short *)(opcode + 2)] = + g3_copy_add_delta_xy(resbuf[*(short *)(opcode + 4)], *(fix *)(opcode + 6), *(fix *)(opcode + 10)); + return opcode + 14; +} + +uchar *do_xz_rel(uchar *opcode) { + resbuf[*(short *)(opcode + 2)] = + g3_copy_add_delta_xz(resbuf[*(short *)(opcode + 4)], *(fix *)(opcode + 6), *(fix *)(opcode + 10)); + return opcode + 14; +} + +uchar *do_yz_rel(uchar *opcode) { + resbuf[*(short *)(opcode + 2)] = + g3_copy_add_delta_yz(resbuf[*(short *)(opcode + 4)], *(fix *)(opcode + 6), *(fix *)(opcode + 10)); + return opcode + 14; +} + +uchar *do_icall_p(uchar *opcode) { + g3_start_object_angles_x((g3s_vector *)(opcode + 6), *(fixang *)(parm_data + (*(unsigned short *)(opcode + 18)))); + interpreter_loop((uchar *)(*(long *)(opcode + 2))); + g3_end_object(); + + return opcode + 20; +} + +uchar *do_icall_h(uchar *opcode) { + g3_start_object_angles_y((g3s_vector *)(opcode + 6), *(fixang *)(parm_data + (*(unsigned short *)(opcode + 18)))); + interpreter_loop((uchar *)(*(long *)(opcode + 2))); + g3_end_object(); + + return opcode + 20; +} + +uchar *do_icall_b(uchar *opcode) { + g3_start_object_angles_z((g3s_vector *)(opcode + 6), *(fixang *)(parm_data + (*(unsigned short *)(opcode + 18)))); + interpreter_loop((uchar *)(*(long *)(opcode + 2))); + g3_end_object(); + + return opcode + 20; +} + +uchar *do_sfcal(uchar *opcode) { + interpreter_loop(opcode + (*(unsigned short *)(opcode + 2))); + return opcode + 4; +} + +// copy parms of stack. takes offset,count +uchar *do_getparms(uchar *opcode) { + long *src, *dest; + int count; + + dest = (long *)(parm_data + (*(unsigned short *)(opcode + 2))); + src = (long *)(parm_ptr + (*(unsigned short *)(opcode + 4))); + count = *(unsigned short *)(opcode + 6); + while (count-- > 0) + *(dest++) = *(src)++; + + return opcode + 8; +} + +// copy parm block. ptr is on stack. takes dest_ofs,src_ptr_ofs,size +uchar *do_getparms_i(uchar *opcode) { + long *src, *dest; + int count; + + dest = *(long **)(parm_data + (*(unsigned short *)(opcode + 2))); + src = (long *)(parm_ptr + (*(unsigned short *)(opcode + 4))); + count = *(unsigned short *)(opcode + 6); + while (count-- > 0) + *(dest++) = *(src)++; + + return opcode + 8; +} + +uchar *do_dbg(uchar *opcode) { + return opcode + 8; +} + +extern void (*g3_tmap_func)(); +extern int temp_poly(long c, int n, grs_vertex **vpl); + +uchar *do_tmap_op(uchar *opcode) { + int count, count2; + short temp; + + count2 = count = *(unsigned short *)(opcode + 4); + count--; + do { + temp = *(short *)(opcode + 6 + (count << 1)); + + poly_buf[count] = resbuf[temp]; + } while (--count >= 0); + + ((int (*)(int, g3s_phandle *, grs_bitmap *)) * g3_tmap_func)(count2, poly_buf, + _vtext_tab[*(unsigned short *)(opcode + 2)]); + + return opcode + 6 + (count2 * 2); +} + +// routines to shade objects +// mostly replacements for jnorm +// ljnorm lbl,px,py,pz,nx,ny,nz +// v=viewer coords-p +// if (n*v)<0 then branch to lbl +// does lit version of jnorm, for flat lighting +uchar *do_ljnorm(uchar *opcode) { + if (g3_check_normal_facing((g3s_vector *)(opcode + 16), (g3s_vector *)(opcode + 4))) { + g3_light_obj((g3s_phandle)(opcode + 4), (g3s_phandle)(opcode + 16)); + return opcode + 28; + } else + return opcode + (*(short *)(opcode + 2)); // surface not visible +} + +// light diff not near norm +uchar *do_ldjnorm(uchar *opcode) { + fix temp; + + if (g3_check_normal_facing((g3s_vector *)(opcode + 16), (g3s_vector *)(opcode + 4))) { + temp = g3_vec_dotprod(&_g3d_light_vec, (g3s_vector *)(opcode + 4)); + temp <<= 1; + if (temp < 0) + temp = 0; + temp += _g3d_amb_light; + temp >>= 4; + temp &= 0x0ffffff00; + temp += _g3d_light_tab; + gr_set_fill_parm(temp); + + return opcode + 28; + } else + return opcode + (*(short *)(opcode + 2)); // surface not visible +} + +//external calls to these do-nothing functions can be safely removed +void FlipShort(short *sh) {} +void FlipLong(long *lng) {} +void FlipVector(short n, g3s_vector *vec) {} diff --git a/engine/src/Libraries/3D/Source/light.c b/engine/src/Libraries/3D/Source/light.c new file mode 100644 index 0000000..257e0c6 --- /dev/null +++ b/engine/src/Libraries/3D/Source/light.c @@ -0,0 +1,477 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// +// $Source: r:/prj/lib/src/3d/RCS/light.asm $ +// $Revision: 1.2 $ +// $Author: jaemz $ +// $Date: 1994/10/13 20:51:49 $ +// +// Light routines +// + +#include "3d.h" +#include "GlobalV.h" +#include "lg.h" + +// prototypes +void check_for_near(void); +void scale_light_vec(void); +void g3_light_obj(g3s_phandle norm, g3s_phandle pos); +fix light_diff_raw(g3s_phandle src, g3s_phandle dest); +fix light_spec_raw(g3s_phandle src, g3s_phandle dest); +fix light_dands_raw(g3s_phandle src, g3s_phandle dest); + +// look ma, a zero vector +g3s_phandle tmp1; +g3s_phandle tmp2; +g3s_vector zero_vec = {0, 0, 0}; + +// sets a light vector in source space directly +// this light vector has to be in user space so we can dot it with +// other vector +// g3_set_light_src(g3s_vector *l) +// takes eax, trashes esi,edi +void g3_set_light_src(g3s_vector *l) { _g3d_light_src = *l; } + +// This should be called after a frames' angles and stuff have been +// set, does not need to be done per object +// void g3_eval_vec_light(void) +// Means you should be not near +void g3_eval_vec_light(void) { + // needs to be rotated through view matrix + g3_vec_rotate(&_g3d_light_vec, &_g3d_light_src, &_wtoo_matrix); + scale_light_vec(); +} + +// should be normalized already +// multiply by _g3d_diff_light +// to set light intensity +void scale_light_vec(void) { + _g3d_light_vec.gX = fix_mul(_g3d_light_vec.gX, _g3d_diff_light); + _g3d_light_vec.gY = fix_mul(_g3d_light_vec.gY, _g3d_diff_light); + _g3d_light_vec.gZ = fix_mul(_g3d_light_vec.gZ, _g3d_diff_light); +} + +// transforms local light source into viewer coords in anticipation +// of calling g3_eval_loc_light. Saves a transformation don't you know +// ideally you'd want to transform the view vec and light vec into +// object coords. That way you wouldn't have to transform their normals +// at all. The new 3d should provide inverse transforms. That way things +// could be lit more cheaply +void g3_trans_loc_light(void) { + g3s_phandle p; + + p = g3_rotate_point(&_g3d_light_src); + _g3d_light_trans = *(g3s_vector *)p; +} + +// evaluates light point relative to another point, src is in world coords +// and pos is already transformed into eye coords +// void g3_eval_loc_light(eax)// +void g3_eval_loc_light(g3s_phandle pos) { + fix temp; + + // transform light src point to eye coords + + // take difference with pos, and unscale them + temp = -(pos->gX - _g3d_light_trans.gX); + _g3d_light_vec.gX = fix_div(temp, _matrix_scale.gX); + + temp = -(pos->gY - _g3d_light_trans.gY); + _g3d_light_vec.gY = fix_div(temp, _matrix_scale.gY); + + temp = -(pos->gZ - _g3d_light_trans.gZ); + _g3d_light_vec.gZ = fix_div(temp, _matrix_scale.gZ); + + // normalize vector + g3_vec_normalize(&_g3d_light_vec); + + // multiply by diff and divide by scale + // so dot product just works + scale_light_vec(); +} + +// evaluate the view vector relative to a point +// similar to above except src is always 0,0,0 +// pos in eax has to be transformed into viewer coords +// eax points to the point in 3d space +void g3_eval_view(g3s_phandle pos) { + fix temp; + + _g3d_view_vec.gX = pos->gX - _view_position.gX; + _g3d_view_vec.gY = pos->gY - _view_position.gY; + _g3d_view_vec.gZ = pos->gZ - _view_position.gZ; + + // normalize + g3_vec_normalize(&_g3d_view_vec); + + // multiply by spec and negate vector + // since it currently points at the + // point instead of the viewer + // you might think, why not do this + // afterwards, and you're right. But only + // only if this view vec only gets used once + + temp = -_g3d_spec_light; + _g3d_view_vec.gX = fix_mul(_g3d_view_vec.gX, temp); + _g3d_view_vec.gY = fix_mul(_g3d_view_vec.gY, temp); + _g3d_view_vec.gZ = fix_mul(_g3d_view_vec.gZ, temp); +} + +// takes the dot product of view and light, for specular light +// assumes both light_vec and view_vec have been evaluated already +// everything is normal and in object space +// void g3_eval_ldotv(void) +void g3_eval_ldotv(void) { + // multiply and scale once to find true dotproduct. + // I hate this scaling stuff. Erg. + // transforming the light and view vectors into object + // space would avoid this entirely + + _g3d_ldotv = g3_vec_dotprod(&_g3d_light_vec, &_g3d_view_vec); +} + +// Evaluates and sets light vectors as necessary at the start of +// an object. Does view vec if SPEC is set. Transforms light +// vector or point depending how LOC_LIGHT is set. Use this if +// both light and view will be modelled as far. In fact, make +// sure both are set as far, or you will be sorry. +// Evaluates at the object center. If necessary, evaluates the +// ldotv for light and view +// void g3_eval_light_obj_cen(void) +// this could be optimized a bit more when both spec +// both spec and diff is true +// this should do eval vec as well, basically everything +void g3_eval_light_obj_cen(void) { + DEBUG("%s: Call Mark if you see this", __FUNCTION__); + + // MLA - this routine is buggy as far as I can tell, it doesn't work at all + // edi is never set or just happens to be set right, or it always falls + // through the first test (non_local) + + /* ; is local? + test _g3d_light_type,LT_LOC_LIGHT or LT_SPEC + jz non_local + + ; find center of object, duh, center is at 0,0,0 + ;lea esi,zero_vec + ;call g3_rotate_point; this would be point in viewer space + ; returns point in edi + ;mov tmp1,edi + + ; evaluate local light + test _g3d_light_type,LT_LOC_LIGHT + jz no_loc + mov eax,edi + call g3_eval_loc_light + jmp test_spec + no_loc: + call g3_eval_vec_light + + ; is spec set? + test_spec: + test _g3d_light_type,LT_SPEC + jz spec_done + + ; evaluate view + ; view vector relative to zero is in _view_position + ; already + ;mov eax,tmp1 + lea eax,zero_vec ;in reality should write a diff routine + call g3_eval_view + + call g3_eval_ldotv + + spec_done: + ;mov edi,tmp1 + ;freepnt edi + ret + + non_local: + ; assumes you've transformed + ; it already + jmp g3_eval_vec_light*/ +} + +// set your view to be straight ahead, use when using specular, +// and after the light has been evaluated. This is ultra cheap hack +// to get view vector for a whole scene, just points straight in +void g3_eval_view_ahead(void) { + // this is in view coords, need in + // object coords, this won't work + _g3d_view_vec.gX = 0; + _g3d_view_vec.gY = 0; + _g3d_view_vec.gZ = -0x01000; + + // now eval ldotv, hm. + _g3d_ldotv = -_g3d_light_vec.gZ; +} + +// check to see if local stuff has to get set and +// set it if necessary +// takes args in tmp1,tmp2 +void check_for_near(void) { + if (!(_g3d_light_type & (LT_NEAR_VIEW | LT_NEAR_LIGHT))) + return; + + // if light near, evaluate + if (_g3d_light_type & LT_NEAR_LIGHT) + g3_eval_loc_light(tmp2); + + // if view near, eval + if (_g3d_light_type & LT_NEAR_VIEW) + g3_eval_view(tmp2); + + // evaluate ldotv if either was local + // MLA - this is stupid, the code that tests for whether or not to call + // g3_eval_ldotv makes no sense, it does a JZ on an undetermined condition + // code setup. So I just call it all the time. Look in Light.ASM in the PC 3D + // code for the original stuff. + g3_eval_ldotv(); +} + +// void g3_light_diff(g3s_phandle norm,g3s_phandle pos)// +// takes normal vector transformed, dots with the light vec, +// puts light val in norm, +// takes args in [eax,edx] +void g3_light_diff(g3s_phandle norm, g3s_phandle pos) { + // push eax if not gouraud, or edx if, so we know + // whether to light the normal or the point + // maybe we could make this self modifying based + // on a light type setter, if this is slow + + // MLA - whatever, I made it normal C code + + tmp1 = norm; + tmp2 = pos; + check_for_near(); + + if ((_g3d_light_type & LT_GOUR) == 0) + light_diff_raw(tmp1, norm); + else + light_diff_raw(tmp1, pos); +} + +// raw version +// dot product with normal +// esi and edi +// ret eax +fix light_diff_raw(g3s_phandle src, g3s_phandle dest) { + fix temp; + + temp = g3_vec_dotprod(&_g3d_light_vec, (g3s_vector *)src); + + // set lighting value in norm + // test eax for negativity, zero if negative + if (temp < 0) + temp = 0; + temp += _g3d_amb_light; // add ambient light + temp >>= 4; // convert to sfix, consider row 16 normal + dest->i = temp; + return (temp); +} + +// void g3_light_spec(g3s_phandle norm,g3s_phandle pos)// +// takes norm and point position, lights point +// could both be the same, of course [eax,edx] +void g3_light_spec(g3s_phandle norm, g3s_phandle pos) { + // push eax if not gouraud, or edx if, so we know + // whether to light the normal or the point + // maybe we could make this self modifying based + // on a light type setter, if this is slow + + // MLA - whatever, I made it normal C code + + // save norm and pos off so we don't push and + // pop them forever + tmp1 = norm; + tmp2 = pos; + check_for_near(); + + if ((_g3d_light_type & LT_GOUR) == 0) + light_spec_raw(tmp1, norm); + else + light_spec_raw(tmp1, pos); +} + +// pure specular lighting is equal to +// 2(s.l)(s.v) - (l.v) +// take (s.l) +fix light_spec_raw(g3s_phandle src, g3s_phandle dest) { + fix temp; + + temp = g3_vec_dotprod(&_g3d_light_vec, (g3s_vector *)src); + if (temp < 0) { + dest->i = _g3d_amb_light >> 4; + return (dest->i); + } + + _g3d_sdotl = temp; + + // take (s.v), note that this is jnorm, if its been done + // we can eliminate this step intelligently somehow + _g3d_sdotv = temp = g3_vec_dotprod(&_g3d_view_vec, (g3s_vector *)tmp1); + temp <<= 1; // multiply (s.v) by 2 + temp = fix_mul(temp, _g3d_sdotl); // mult by (s.l) + temp -= _g3d_ldotv; // subtract ldotv, done! + + // test eax for flash point zero if under + // or better test eax for spec threshhold + if (temp < _g3d_flash) + temp = 0; + + // add ambient light + temp += _g3d_amb_light; + + // check to see if its greater than the max row + // and truncate if it is + if (temp >= (LT_TABSIZE << 12)) + ; + temp = (LT_TABSIZE << 12) - 1; // if its over the max, set it to just under max + + dest->i = temp >> 4; // convert to sfix, consider row 16 normal + return (temp); +} + +// void g3_light_dands(g3s_phandle norm,g3s_phandle pos)// +// lights with both diff and spec +//[eax,edx] +void g3_light_dands(g3s_phandle norm, g3s_phandle pos) { + // MLA - same stuff as before, changed to C.... + tmp1 = norm; + tmp2 = pos; + check_for_near(); + + if ((_g3d_light_type & LT_GOUR) == 0) + light_dands_raw(tmp1, norm); + else + light_dands_raw(tmp1, pos); +} + +// raw version of dands without local checking +fix light_dands_raw(g3s_phandle src, g3s_phandle dest) { + fix temp; + + // pure specular lighting is equal to + // 2(s.l)(s.v) - (l.v) + // take (s.l) if neg, you know you're done, surface HAS to face the light + + temp = g3_vec_dotprod(&_g3d_light_vec, (g3s_vector *)src); + if (temp < 0) { + dest->i = _g3d_amb_light >> 4; + return (dest->i); + } + _g3d_sdotl = temp; + + // take (s.v), note that this is jnorm, if its been done + // we can eliminate this step intelligently somehow + _g3d_sdotv = temp = g3_vec_dotprod(&_g3d_view_vec, (g3s_vector *)tmp1); + temp = fix_mul(temp, _g3d_sdotl); // mult by (s.l) + temp <<= 1; // multiply (s.v)(s.l) by 2 + temp -= _g3d_ldotv; // subtract ldotv, done! + + // test eax for flash point zero if under + // or better test eax for spec threshhold + if (temp < _g3d_flash) + temp = 0; + + // add diffuse component & ambient light + temp += _g3d_sdotl + _g3d_amb_light; + + // check to see if its greater than the max row + // and truncate if it is + if (temp >= (LT_TABSIZE << 12)) + temp = (LT_TABSIZE << 12) - 1; // if its over the max, set it to just under max + + dest->i = temp >> 4; // convert to sfix, consider row 16 normal + return (temp); +} + +// farms out a point based on flags +// void g3_light(g3s_phandle norm,g3s_phandle pos)// +//[eax,edx] +fix g3_light(g3s_phandle norm, g3s_phandle pos) { + g3s_phandle temp; + + if ((_g3d_light_type & LT_GOUR) == 0) + temp = norm; + else + temp = pos; + + tmp1 = norm; + tmp2 = pos; + + if ((_g3d_light_type & (LT_NEAR_VIEW | LT_NEAR_LIGHT)) != 0) + check_for_near(); + + // determine which routine to jump to based on flags + switch (_g3d_light_type) { + case LT_DIFF: + return (light_diff_raw(tmp1, temp)); + case LT_SPEC: + return (light_spec_raw(tmp1, temp)); + default: + return (light_dands_raw(tmp1, temp)); + } +} + +// farms out a point based on flags +// void g3_light_obj(g3s_vector *norm,g3s_vector *pos)// +//[eax,edx] +// norm is set with only 15 bits of fraction, pos is normal +// all vectors are in object space +// though we put these in points, they are in object space, +// not world space +void g3_light_obj(g3s_phandle norm, g3s_phandle pos) { + g3s_point *norm_point; + g3s_point *pos_point; + fix shade; + + tmp1 = norm; + tmp2 = pos; + getpnt(norm_point); + + norm_point->gX = norm->gX << 1; + norm_point->gY = norm->gY << 1; + norm_point->gZ = norm->gZ << 1; + + // Copy position over to its own point + getpnt(pos_point); + *(g3s_vector *)pos_point = *(g3s_vector *)pos; + + shade = g3_light(norm_point, pos_point); + + // set the lighting when non gouraud + // set fill type to address of shading table + shade &= 0xffffff00; + shade += _g3d_light_tab; + + gr_set_fill_parm(shade); + + freepnt(norm_point); + freepnt(pos_point); +} + +// generic list gronker, farms these points out +// call this inside an object or inside a frame +// at any rate, the points need to have been +// transformed +// g3_light_list(int n,g3s_phandle *norm,g3s_phandle *pos) +// [eax,edx,ebx] +void g3_light_list(int n, g3s_phandle *norm, g3s_phandle *pos) {} diff --git a/engine/src/Libraries/3D/Source/matrix.c b/engine/src/Libraries/3D/Source/matrix.c new file mode 100644 index 0000000..71e6161 --- /dev/null +++ b/engine/src/Libraries/3D/Source/matrix.c @@ -0,0 +1,530 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// +// $Source: r:/prj/lib/src/3d/RCS/matrix.C $ +// $Revision: 1.17 $ +// $Author: jaemz $ +// $Date: 1994/09/20 13:33:41 $ +// +// Matrix setup and multiply routines +// +// $Log: matrix.asm $ +// Revision 1.17 1994/09/20 13:33:41 jaemz +// *** empty log message *** +// +// Revision 1.16 1994/08/18 03:46:59 jaemz +// Changed stereo glob names to have underscore for c +// +// Revision 1.15 1994/08/04 16:36:13 jaemz +// *** empty log message *** +// +// Revision 1.14 1994/07/19 13:48:35 jaemz +// Added support for stereo +// +// Revision 1.13 1994/07/15 19:31:23 jaemz +// changed view_zoom to _view_zoom for c access +// +// Revision 1.12 1994/07/15 14:13:37 jaemz +// Added _view_position with an underscore to make it c readable +// +// Revision 1.11 1994/06/02 15:09:36 junochoe +// changed matrix_scale to _matrix_scale +// +// Revision 1.10 1994/02/08 20:46:39 kaboom +// Moved back clipping plane to z=1\65536. +// +// Revision 1.9 1993/12/14 14:04:23 kevin +// Swap and negate axis before saving unscaled view matrix. +// Also commented out code that breaks citadel under wvideo. +// +// Revision 1.8 1993/10/02 09:28:38 kaboom +// Changed point coder to check for magic minimum z value. +// +// Revision 1.7 1993/08/10 22:54:16 dc +// add _3d.inc to includes +// +// Revision 1.6 1993/07/13 11:58:05 kaboom +// Fixed bugs in saving off of angles. +// +// Revision 1.5 1993/07/08 23:39:01 kaboom +// Now sets pitch, heading, bank, values for scaler/roller. +// +// Revision 1.4 1993/06/22 18:35:35 kaboom +// Changed g3_matrix_x_matrix to g3_matrix_x_matrix_ so it's callable +// from watcom C w/register passing. +// +// Revision 1.3 1993/05/24 15:48:56 matt +// process_view_matrix now copies view_matrix to unscaled_matrix before +// messing with it. +// +// Revision 1.2 1993/05/11 15:03:58 matt +// Added g3_get_view_pyramid() +// +// Revision 1.1 1993/05/04 17:39:51 matt +// Initial revision +// +// + +//#include +#include "3d.h" +#include "GlobalV.h" +#include "lg.h" + +/*#define f1_0 fixmake(1) +#define f0_5 fixmake(0,8000h) +#define f0_25 fixmake(0,4000h) +*/ + +fix sinp; // fix ? +fix cosp; // fix ? +fix sinb; // fix ? +fix cosb; // fix ? +fix sinh_s; // fix ? +fix cosh_s; // fix ? + +// vars for get_view_pyramid +fix d13; // fix ? +fix d23; // fix ? +fix d46; // fix ? +fix d56; // fix ? +fix d79; // fix ? +fix d89; // fix ? +fix den; // fix ? + +// prototypes +void angles_2_matrix(g3s_angvec *angles, g3s_matrix *view_matrix, int rotation_order); +void process_view_matrix(void); +void scale_view_matrix(void); +void get_pyr_vector(g3s_vector *corners); + +int code_point(g3s_point *pt); + +void compute_XYZ(g3s_matrix *view_matrix); +void compute_YXZ(g3s_matrix *view_matrix); +void compute_YZX(g3s_matrix *view_matrix); +void compute_XZY(g3s_matrix *view_matrix); +void compute_ZXY(g3s_matrix *view_matrix); +void compute_ZYX(g3s_matrix *view_matrix); +void compute_invalid(g3s_matrix *view_matrix); + +// function table +void (*rotation_table[])(g3s_matrix *) = {compute_XYZ, compute_YXZ, compute_invalid, compute_YZX, + compute_XZY, compute_invalid, compute_ZXY, compute_ZYX}; + +// build the view matrix from view angles, etc. +// takes esi=pos, ebx=angles, eax=zoom, ecx=rotation order +void g3_set_view_angles(g3s_vector *pos, g3s_angvec *angles, int rotation_order, fix zoom) { + _view_zoom = zoom; // mov _view_zoom,eax ;save zoom + _view_position = *pos; + + view_pitch = angles->tx; + view_heading = angles->ty; + view_bank = angles->tz; + + angles_2_matrix(angles, &view_matrix, rotation_order); + process_view_matrix(); +} + +// build the view matrix from an object matrix +// takes esi=pos, ebx=matrix, eax=zoom +void g3_set_view_matrix(g3s_vector *pos, g3s_matrix *m, fix zoom) { + _view_zoom = zoom; + _view_position = *pos; + view_matrix = *m; + + process_view_matrix(); +} + +// generates a matrix from 3 angles. esi=angles,edi=matrix, ecx=order +// trashes esi, ebx, ecx, eax, edx, plus whatever fix_sincos trashes +// note that these routines use variables called sinp,sinb,etc., which +// are really just rotations around certain axes, and don't necessarily +// mean pitch,bank, or heading in each coordinated system +void angles_2_matrix(g3s_angvec *angles, g3s_matrix *view_matrix, int rotation_order) { + fix_sincos(angles->tx, &sinp, &cosp); + fix_sincos(angles->ty, &sinh_s, &cosh_s); + fix_sincos(angles->tz, &sinb, &cosb); + + rotation_table[rotation_order](view_matrix); +} + +// compute a matrix with the given order. takes sin & cos vars set, edi=dest +void compute_XYZ(g3s_matrix *view_matrix) { + fix cbsp, cpsb, cpshsb, spsb, cpshcb, spshcb, spshsb, cpcb; + + view_matrix->m1 = fix_mul(cosh_s, cosb); // m1 = chcb + view_matrix->m2 = -fix_mul(cosh_s, sinb); // m2 = -chsb + view_matrix->m3 = sinh_s; // m3 = sinh + + cbsp = fix_mul(cosb, sinp); + spshcb = fix_mul(cbsp, sinh_s); + cpsb = fix_mul(cosp, sinb); + view_matrix->m4 = cpsb + spshcb; // m4 = cpsb+spshcb + + cpshsb = fix_mul(cpsb, sinh_s); + view_matrix->m8 = cbsp + cpshsb; // m8 = spcb+cpshsb + + spsb = fix_mul(sinp, sinb); + spshsb = fix_mul(spsb, sinh_s); + cpcb = fix_mul(cosp, cosb); + view_matrix->m5 = cpcb - spshsb; // m5 = cpcb-spshsb + + cpshcb = fix_mul(cpcb, sinh_s); + view_matrix->m7 = spsb - cpshcb; // m7 = spsb-cpshcb + + view_matrix->m6 = -fix_mul(sinp, cosh_s); // m6 = -spch + view_matrix->m9 = fix_mul(cosp, cosh_s); // m9 = cpch +} + +void compute_YXZ(g3s_matrix *view_matrix) { + fix cbch, sbsh, sbch, cbsh; + + // m1 = cb*ch + sb*sp*sh + cbch = fix_mul(cosb, cosh_s); + sbsh = fix_mul(sinb, sinh_s); + view_matrix->m1 = cbch + fix_mul(sbsh, sinp); + + // m8 = sb*sh + cb*ch*sp + view_matrix->m8 = sbsh + fix_mul(cbch, sinp); + + // m2 = -sb*ch + cb*sp*sh + sbch = fix_mul(sinb, cosh_s); + cbsh = fix_mul(cosb, sinh_s); + view_matrix->m2 = fix_mul(cbsh, sinp) - sbch; + + // m7 = -cb*sh + sb*ch*sp + view_matrix->m7 = fix_mul(sbch, sinp) - cbsh; + + // m3 = sh*cp + view_matrix->m3 = fix_mul(sinh_s, cosp); + + // m4 = sb*cp + view_matrix->m4 = fix_mul(sinb, cosp); + + // m5 = cb*cp + view_matrix->m5 = fix_mul(cosb, cosp); + + // m6 = - sp + view_matrix->m6 = -sinp; + + // m9 = ch*cp + view_matrix->m9 = fix_mul(cosh_s, cosp); +} + +void compute_YZX(g3s_matrix *view_matrix) { DEBUG("%s: needs to be implemented", __FUNCTION__); } + +void compute_XZY(g3s_matrix *view_matrix) { DEBUG("%s: needs to be implemented", __FUNCTION__); } + +void compute_ZXY(g3s_matrix *view_matrix) { DEBUG("%s: needs to be implemented", __FUNCTION__); } + +void compute_ZYX(g3s_matrix *view_matrix) { DEBUG("%s: needs to be implemented", __FUNCTION__); } + +// invalid does nothing (and does it well!) +void compute_invalid(g3s_matrix *view_matrix) {} + +// scale, fix, etc, the view matrix +void process_view_matrix(void) { + + // adjust matrix for user's coordinate system + if ((axis_swap_flag & 1) != 0) { + SwapFix(vm1, vm2); + SwapFix(vm4, vm5); + SwapFix(vm7, vm8); + } + if ((axis_swap_flag & 2) != 0) { + SwapFix(vm1, vm3); + SwapFix(vm4, vm6); + SwapFix(vm7, vm9); + } + if ((axis_swap_flag & 4) != 0) { + SwapFix(vm2, vm3); + SwapFix(vm5, vm6); + SwapFix(vm8, vm9); + } + + // get vars for horizon drawer + horizon_vector.gX = ((fix *)&view_matrix)[up_axis]; + horizon_vector.gY = ((fix *)&view_matrix)[up_axis + 1]; + horizon_vector.gZ = ((fix *)&view_matrix)[up_axis + 2]; + + // now fix signs + if ((axis_neg_flag & 1) != 0) { + vm1 = -vm1; + vm4 = -vm4; + vm7 = -vm7; + } + + if ((axis_neg_flag & 2) != 0) { + vm2 = -vm2; + vm5 = -vm5; + vm8 = -vm8; + } + + if ((axis_neg_flag & 4) != 0) { + vm3 = -vm3; + vm6 = -vm6; + vm9 = -vm9; + } + + unscaled_matrix = view_matrix; + scale_view_matrix(); +} + +// performs various scaling and other operations on the view matrix +void scale_view_matrix(void) { + int32_t temp_long; + fix temp_fix; + + // set matrix scale vector based on zoom + _matrix_scale.gX = f1_0; // use 1.0 as defaults + _matrix_scale.gY = f1_0; + _matrix_scale.gZ = f1_0; + + if (_view_zoom <= f1_0) + _matrix_scale.gZ = _view_zoom; + else + _matrix_scale.gY = _matrix_scale.gX = fix_div(f1_0, _view_zoom); + + // scale set matrix scale vector based on window and pixel ratio + temp_long = fix_mul_div(window_height, pixel_ratio, window_width); + +#ifdef stereo_on + _g3d_eyesep = fix_mul(-temp_long, _g3d_eyesep_raw); // calculate true eyesep +#endif + + if (temp_long <= f1_0) + _matrix_scale.gX = fix_mul(_matrix_scale.gX, temp_long); + else + _matrix_scale.gY = fix_div(_matrix_scale.gY, temp_long); + + // now actually scale the matrix + temp_fix = _matrix_scale.gX; + vm1 = fix_mul(vm1, temp_fix); + vm4 = fix_mul(vm4, temp_fix); + vm7 = fix_mul(vm7, temp_fix); + + temp_fix = _matrix_scale.gY; + vm2 = fix_mul(vm2, temp_fix); + vm5 = fix_mul(vm5, temp_fix); + vm8 = fix_mul(vm8, temp_fix); + + temp_fix = _matrix_scale.gZ; + vm3 = fix_mul(vm3, temp_fix); + vm6 = fix_mul(vm6, temp_fix); + vm9 = fix_mul(vm9, temp_fix); + + // scale horizon vector + horizon_vector.gX = fix_mul(fix_mul(_matrix_scale.gY, _matrix_scale.gZ), horizon_vector.gX); + horizon_vector.gY = fix_mul(fix_mul(_matrix_scale.gX, _matrix_scale.gZ), horizon_vector.gY); + horizon_vector.gZ = fix_mul(fix_mul(_matrix_scale.gX, _matrix_scale.gY), horizon_vector.gZ); +} + +// takes point in edi, set codes in point, returns codes in bl +// trashes eax,bl +// note: in an effort to optimize the coder, I tried several variants, +// including the C&D coder, and a clever one using the set instruction +// that contained no jumps. On my (Matt's) 486, this dull, straightforward +// one was just as fast as any other, and short, too. +int code_point(g3s_point *pt) { + int code; + int tempX, tempY, tempZ; + + tempX = pt->gX; + tempY = pt->gY; + tempZ = pt->gZ; + code = 0; + if (tempX > tempZ) + code |= CC_OFF_RIGHT; + if (tempY > tempZ) + code |= CC_OFF_TOP; + + tempZ = -tempZ; + if (tempZ > -1) + code |= CC_BEHIND; + + if (tempX < tempZ) + code |= CC_OFF_LEFT; + if (tempY < tempZ) + code |= CC_OFF_BOT; + + pt->codes = code; + return (code); +} + +// general matrix multiply. takes esi=src vector, edi=matrix, ebx=dest vector +// src and dest vectors can be the same +void g3_vec_rotate(g3s_vector *dest, g3s_vector *src, g3s_matrix *m) { + int32_t srcX, srcY, srcZ; // in locals for PPC speed + int64_t r; + + srcX = src->gX; + srcY = src->gY; + srcZ = src->gZ; + + // first column + r = fix64_mul(srcX, m->m1) + fix64_mul(srcY, m->m4) + fix64_mul(srcZ, m->m7); + dest->gX = fix64_to_fix(r); + + // second column + r = fix64_mul(srcX, m->m2) + fix64_mul(srcY, m->m5) + fix64_mul(srcZ, m->m8); + dest->gY = fix64_to_fix(r); + + // third column + r = fix64_mul(srcX, m->m3) + fix64_mul(srcY, m->m6) + fix64_mul(srcZ, m->m9); + dest->gZ = fix64_to_fix(r); +} + +// transpose a matrix at esi in place +// trashes eax +void g3_transpose(g3s_matrix *m) // transpose in place +{ + SwapFix(m->m2, m->m4); + SwapFix(m->m3, m->m7); + SwapFix(m->m6, m->m8); +} + +// transpose the matrix at esi into matrix at edi +// trashes eax +void g3_copy_transpose(g3s_matrix *dest, g3s_matrix *src) // copy and transpose +{ + dest->m1 = src->m1; + dest->m5 = src->m5; + dest->m9 = src->m9; + + dest->m2 = src->m4; + dest->m4 = src->m2; + + dest->m3 = src->m7; + dest->m7 = src->m3; + + dest->m6 = src->m8; + dest->m8 = src->m6; +} + +// MLA- oh no I've got LookingGlass disease, I'm making multi-line #defines! +// No worries, WH comes to help! +fix mxm_mul(fix s1_1, fix s1_2, fix s1_3, fix s2_1, fix s2_2, fix s2_3) { + int64_t r = fix64_mul(s1_1, s2_1) + fix64_mul(s1_2, s2_2) + fix64_mul(s1_3, s2_3); + return fix64_to_fix(r); +} +/* +#define mxm_mul(dst, s1_1, s1_2, s1_3, s2_1, s2_2, s2_3) \ + { \ + int64_t r = fix64_mul(src1->s1_1, src2->s2_1) + \ + fix64_mul(src1->s1_2, src2->s2_2) + \ + fix64_mul(src1->s1_3, src2->s2_3); \ + dest->dst = fix64_to_fix(r); \ + } +*/ + +// matrix by matrix multiply: ebx = esi * edi +// does ebx = edi * esi +// it does the inverse actually, edi*esi, assuming +// standard layout: +// 147 +// 258 +// 369 +// +// dest = bx, src1 = si, src2 = di +void g3_matrix_x_matrix(g3s_matrix *dest, g3s_matrix *src1, g3s_matrix *src2) { + dest->m1 = mxm_mul(src1->m1, src1->m2, src1->m3, src2->m1, src2->m4, src2->m7); + dest->m2 = mxm_mul(src1->m1, src1->m2, src1->m3, src2->m2, src2->m5, src2->m8); + dest->m3 = mxm_mul(src1->m1, src1->m2, src1->m3, src2->m3, src2->m6, src2->m9); + + dest->m4 = mxm_mul(src1->m4, src1->m5, src1->m6, src2->m1, src2->m4, src2->m7); + dest->m5 = mxm_mul(src1->m4, src1->m5, src1->m6, src2->m2, src2->m5, src2->m8); + dest->m6 = mxm_mul(src1->m4, src1->m5, src1->m6, src2->m3, src2->m6, src2->m9); + + dest->m7 = mxm_mul(src1->m7, src1->m8, src1->m9, src2->m1, src2->m4, src2->m7); + dest->m8 = mxm_mul(src1->m7, src1->m8, src1->m9, src2->m2, src2->m5, src2->m8); + dest->m9 = mxm_mul(src1->m7, src1->m8, src1->m9, src2->m3, src2->m6, src2->m9); +} + +static int64_t cross(int v1, int v2, int v3, int v4) { return (int64_t)v1 * v2 - (int64_t)v3 * v4; } + +// fills in edi with vector. takes deltas set +void get_pyr_vector(g3s_vector *corners) { + int64_t r; + + // calculate denominators, divide each by the longest of the three + int64_t den_x = cross(d46, d89, d56, d79); + int64_t den_y = cross(d23, d79, d13, d89); + int64_t den_z = cross(d13, d56, d23, d46); + + if (llabs(den_x) >= llabs(den_y) && llabs(den_x) >= llabs(den_z)) { + corners->gX = f1_0; + corners->gY = den_y / (den_x >> 16); + corners->gZ = den_z / (den_x >> 16); + } else if (llabs(den_y) >= llabs(den_x) && llabs(den_y) >= llabs(den_z)) { + corners->gX = den_x / (den_y >> 16); + corners->gY = f1_0; + corners->gZ = den_z / (den_y >> 16); + } else { + corners->gX = den_x / (den_z >> 16); + corners->gY = den_y / (den_z >> 16); + corners->gZ = f1_0; + } + + // got_vector + g3_vec_normalize(corners); + + // make sure vector points right way + r = fix64_mul(corners->gX, vm3) + fix64_mul(corners->gY, vm6) + fix64_mul(corners->gZ, vm9); + if (fix64_int(r) < 0) { + corners->gX = -corners->gX; + corners->gY = -corners->gY; + corners->gZ = -corners->gZ; + } +} + +// fills in four vectors which are the corners of the view pyramid +// takes edi=ptr to array of 4 vectors. trashes all but edp +void g3_get_view_pyramid(g3s_vector *corners) { + fix save_d23, save_d56, save_d89; + + d13 = vm1 - vm3; + d23 = vm2 - vm3; + d46 = vm4 - vm6; + d56 = vm5 - vm6; + d79 = vm7 - vm9; + d89 = vm8 - vm9; + get_pyr_vector(corners); + corners++; + + save_d23 = d23; + save_d56 = d56; + save_d89 = d89; + + d23 -= vm2 << 1; + d56 -= vm5 << 1; + d89 -= vm8 << 1; + get_pyr_vector(corners); + corners++; + + d13 -= vm1 << 1; + d46 -= vm4 << 1; + d79 -= vm7 << 1; + get_pyr_vector(corners); + corners++; + + d23 = save_d23; + d56 = save_d56; + d89 = save_d89; + get_pyr_vector(corners); +} diff --git a/engine/src/Libraries/3D/Source/points.c b/engine/src/Libraries/3D/Source/points.c new file mode 100644 index 0000000..6ee10ea --- /dev/null +++ b/engine/src/Libraries/3D/Source/points.c @@ -0,0 +1,788 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// +// $Source: r:/prj/lib/src/3d/RCS/points.asm $ +// $Revision: 1.17 $ +// $Author: jaemz $ +// $Date: 1994/09/28 19:00:52 $ +// +// Point definition routines +// + +//#include +#include "3d.h" +#include "GlobalV.h" +#include "lg.h" + +// prototypes +void rotate_norm(g3s_vector *v, fix *x, fix *y, fix *z); +void do_norm_rotate(fix x, fix y, fix z, fix *rx, fix *ry, fix *rz); + +void do_rotate(fix x, fix y, fix z, fix *rx, fix *ry, fix *rz); + +// void xlate_rotate_point(g3s_vector *v, fix *x, fix *y, fix *z); +#define xlate_rotate_point(v, x, y, z) \ + do_rotate(v->gX - _view_position.gX, v->gY - _view_position.gY, v->gZ - _view_position.gZ, x, y, z) + +extern int code_point(g3s_point *pt); +extern char SubLongWithOverflow(int32_t *result, int32_t src, int32_t dest); +extern bool AddLongWithOverflow(int32_t *result, int32_t src, int32_t dest); + +// for temp use in rotate_list, etc. +g3s_codes g_codes; + +// rotate a normal or gradient vector. esi=vector, returns edi=point, bl=codes. +// assumes perspective mapper scale factor will be set to _scrw. +g3s_phandle g3_rotate_norm(g3s_vector *v) { + fix x, y, z; + fix temp, temp2, temp3; + g3s_point *point; + + rotate_norm(v, &x, &y, &z); + + temp = fix_div(z, _matrix_scale.gZ); + temp2 = fix_div(x, _matrix_scale.gX); + temp3 = fix_div(y, _matrix_scale.gY); + + temp3 = -fix_mul_div(temp3, _scrw, _scrh); // because projecting negates too, of course. Grrr. + + getpnt(point); + point->gX = temp2; + point->gY = temp3; + point->gZ = temp; + point->p3_flags = 0; + + return (point); +} + +g3s_phandle g3_rotate_point(g3s_vector *v) { + g3s_point *point; + + getpnt(point); + xlate_rotate_point(v, &point->gX, &point->gY, &point->gZ); + point->p3_flags = 0; + + code_point(point); + return (point); +} + +// matrix multiply and project a point. esi=vector, returns edi=point +g3s_phandle g3_transform_point(g3s_vector *v) { + g3s_phandle tempH; + + // printf("g3_transform_point\n"); + + tempH = g3_rotate_point(v); + g3_project_point(tempH); + return (tempH); +} + +// takes edi = ptr to point. projects, fills in sx,sy, sets flag. +// returns 0 if z<=0, 1 if z>0. +// trashes eax,ecx,edx. +int g3_project_point(g3s_phandle p) { + fix x, y, z, res; + +#ifdef stereo_on + test _g3d_stereo, + 1 jz no_stereo1 + // is this a sister point? + cmp edi, + _g3d_stereo_list jl not_sister + + // debug_brk 'yo, found projecting sister' + + // copy the point and add + mov esi, + edi sub esi, _g3d_stereo_base mov ecx, + (size g3s_point) / 4 rep movsd + + mov eax, + _g3d_eyesep sub edi, + (size g3s_point) // restore edi + add[edi] + .x, + eax + + // call clip encoder on this point + mov ecx, + ebx call code_point mov ebx, + ecx + + // project point like a normal point + mov _g3d_stereo, + 0 pop esi call g3_project_point mov _g3d_stereo, + 1 + + ret + + not_sister : + // copy the point + mov esi, + edi add edi, + _g3d_stereo_base mov ecx, + (size g3s_point) / 4 rep movsd mov eax, + _g3d_eyesep sub edi, + (size g3s_point) // restore edi + add[edi] + .x, + eax + + // call clip encoder + mov ecx, + ebx call code_point mov ebx, + ecx + + sub edi, + _g3d_stereo_base no_stereo1 : +#endif + + /*printf("g3_project_point\n"); + + char printy[100]; + fix_sprint(printy, p->gX); + printf("%s\n", printy); + + fix_sprint(printy, p->gY); + printf("%s\n", printy); + + fix_sprint(printy, p->gZ); + printf("%s\n", printy);*/ + + // check if this point is in front of the back plane. + z = p->gZ; + if (z <= 0) + return 0; + x = p->gX; + y = p->gY; + + // point is in front of back plane---do projection. + // project y coordinate. + res = fix_mul_div(y, _scrh, z); + if (gOVResult) { + p->codes |= CC_CLIP_OVERFLOW; + return 1; + } + res = -res; + if (AddLongWithOverflow(&res, res, _biasy)) { + p->codes |= CC_CLIP_OVERFLOW; + return 1; + } + p->sy = res; + + // now project x point + res = fix_mul_div(x, _scrw, z); + if (gOVResult) { + p->codes |= CC_CLIP_OVERFLOW; + return 1; + } + if (AddLongWithOverflow(&res, res, _biasx)) { + p->codes |= CC_CLIP_OVERFLOW; + return 1; + } + p->sx = res; + + // modify point flags to indicate projection. + p->p3_flags |= PF_PROJECTED; + +#ifdef stereo_on + test _g3d_stereo, 1 jz no_stereo2 mov eax, + [edi].sy // copy over old sy + add edi, + _g3d_stereo_base // load twin address + mov[edi] + .sy, + eax // make new sy, could add the .5 addition here too + + mov eax, + [edi].x + // reproject the x coord + imul _scrw //* screen width + proj_div_2 : idiv ecx /// z + add eax, + _biasx //+center + mov[edi] + .sx, + eax // save + + // indicate projection + or [edi].p3_flags, + PF_PROJECTED + + // restore edi + sub edi, + _g3d_stereo_base no_stereo2 : +#endif + + // point has been projected. + return 1; +} + +// MLA - all the divide exception handler overflow stuff was removed, and +// checked before each divide. So all of this stuf isn't needed +/* + public proj_div_0,proj_div_1,divide_overflow_3d +ifdef stereo_on + public proj_div_2,divide_overflow_r3d +endif + +//this gets called by the system divide overflow handler when there is an +//overflow at proj_div_0, proj_div_1 +divide_overflow_3d: +// fall project_overflow +project_overflow: + or [edi].codes,CC_CLIP_OVERFLOW + +ifdef stereo_on + test _g3d_stereo,1 + jz no_stereo3 + add edi,_g3d_stereo_base +divide_overflow_r3d: + or [edi].codes,CC_CLIP_OVERFLOW + sub edi,_g3d_stereo_base +no_stereo3: +endif + + cspew "!" //"project overflow!" + // this did not use to restore this + ex_set_div_action esi + pop esi + ret +*/ + +// takes esi=ptr to array of vectors, edi=ptr to list for point handles, +// ecx=count +g3s_codes g3_transform_list(short n, g3s_phandle *dest_list, g3s_vector *v) { + int i; + g3s_phandle temphand; + + g_codes.or_ = 0; + g_codes.and_ = 0xff; + + for (i = n; i > 0; i--) { + temphand = g3_transform_point(v++); + g_codes.or_ |= temphand->codes; + g_codes.and_ &= temphand->codes; + + *(dest_list++) = temphand; + } + return (g_codes); +} + +// takes esi=ptr to array of vectors, edi=ptr to list for point handles, +// ecx=count returns bh=codes and, bl=codes or +g3s_codes g3_rotate_list(short n, g3s_phandle *dest_list, g3s_vector *v) { + int i; + g3s_phandle temphand; + + g_codes.or_ = 0; + g_codes.and_ = 0xff; + + for (i = n; i > 0; i--) { + temphand = g3_rotate_point(v++); + g_codes.or_ |= temphand->codes; + g_codes.and_ &= temphand->codes; + + *(dest_list++) = temphand; + } + return (g_codes); +} + +// takes esi=ptr to array of point handles, ecx=count +g3s_codes g3_project_list(short n, g3s_phandle *point_list) { + int i; + g3s_phandle temphand; + + g_codes.or_ = 0; + g_codes.and_ = 0xff; + + for (i = n; i > 0; i--) { + temphand = *(point_list++); + g_codes.or_ |= temphand->codes; + g_codes.and_ &= temphand->codes; + + g3_project_point(temphand); + } + + return (g_codes); +} + +// takes esi=ptr to array of vectors, edi=ptr to dest vectors +g3s_phandle g3_rotate_light_norm(g3s_vector *v) { + g3s_point *point; + + getpnt(point); + do_rotate(v->gX, v->gY, v->gZ, &point->gX, &point->gY, &point->gZ); + return (point); +} + +// takes esi=ptr to normal vector. returns in . trashes all regs +void rotate_norm(g3s_vector *v, fix *x, fix *y, fix *z) { do_norm_rotate(v->gX, v->gY, v->gZ, x, y, z); } + +// does the rotate with the view matrix. +// takes = , returns = +void do_norm_rotate(fix x, fix y, fix z, fix *rx, fix *ry, fix *rz) { + int64_t r; + + // this matrix multiply here will someday be optimized for zero and one terms + // uses unscaled rotation matrix. + + // first column + r = fix64_mul(x, uvm1) + fix64_mul(y, uvm4) + fix64_mul(z, uvm7); + *rx = fix64_to_fix(r); + + // second column + r = fix64_mul(x, uvm2) + fix64_mul(y, uvm5) + fix64_mul(z, uvm8); + *ry = fix64_to_fix(r); + + // third column + r = fix64_mul(x, uvm3) + fix64_mul(y, uvm6) + fix64_mul(z, uvm9); + *rz = fix64_to_fix(r); +} + +// made this a define - MLA +/*//takes esi=ptr to vector. returns in . trashes all regs +void xlate_rotate_point(g3s_vector *v, fix *x, fix *y, fix *z) + { + do_rotate(v->gX-_view_position.gX, v->gY-_view_position.gY, +v->gZ-_view_position.gZ,x,y,z); + }*/ + +// does the rotate with the view matrix. +// takes = , returns = +void do_rotate(fix x, fix y, fix z, fix *rx, fix *ry, fix *rz) { + // this matrix multiply here will someday be optimized for zero and one terms + int64_t r; + // first column + r = fix64_mul(x, vm1) + fix64_mul(y, vm4) + fix64_mul(z, vm7); + *rx = fix64_to_fix(r); + + // second column + r = fix64_mul(x, vm2) + fix64_mul(y, vm5) + fix64_mul(z, vm8); + *ry = fix64_to_fix(r); + + // third column + r = fix64_mul(x, vm3) + fix64_mul(y, vm6) + fix64_mul(z, vm9); + *rz = fix64_to_fix(r); +} + +// rotate an x delta. takes edi=dest vector, eax=dx +// trashes eax,ebx,edx +void g3_rotate_delta_x(g3s_vector *dest, fix dx) { + dest->gX = fix_mul(dx, vm1); + dest->gY = fix_mul(dx, vm2); + dest->gZ = fix_mul(dx, vm3); +} + +// rotate a y delta. takes edi=dest vector, eax=dy +// trashes eax,ebx,edx +void g3_rotate_delta_y(g3s_vector *dest, fix dy) { + dest->gX = fix_mul(dy, vm4); + dest->gY = fix_mul(dy, vm5); + dest->gZ = fix_mul(dy, vm6); +} + +// rotate a z delta. takes edi=dest vector, eax=dz +// trashes eax,ebx,edx +void g3_rotate_delta_z(g3s_vector *dest, fix dz) { + dest->gX = fix_mul(dz, vm7); + dest->gY = fix_mul(dz, vm8); + dest->gZ = fix_mul(dz, vm9); +} + +// rotate an xz delta. takes edi=dest vector, eax=dx, ebx=dz +// trashes eax,ebx,edx +void g3_rotate_delta_xz(g3s_vector *dest, fix dx, fix dz) { + int64_t r; + + // first column + r = fix64_mul(dx, vm1) + fix64_mul(dz, vm7); + dest->gX = fix64_to_fix(r); + + // second column + r = fix64_mul(dx, vm2) + fix64_mul(dz, vm8); + dest->gY = fix64_to_fix(r); + + // third column + r = fix64_mul(dx, vm3) + fix64_mul(dz, vm9); + dest->gZ = fix64_to_fix(r); +} + +// rotate an xy delta. takes edi=dest vector, eax=dx, ebx=dy +// trashes eax,ebx,edx +void g3_rotate_delta_xy(g3s_vector *dest, fix dx, fix dy) { + int64_t r; + + // first column + r = fix64_mul(dx, vm1) + fix64_mul(dy, vm4); + dest->gX = fix64_to_fix(r); + + // second column + r = fix64_mul(dx, vm2) + fix64_mul(dy, vm5); + dest->gY = fix64_to_fix(r); + + // third column + r = fix64_mul(dx, vm3) + fix64_mul(dy, vm6); + dest->gZ = fix64_to_fix(r); +} + +// rotate a yz delta. takes edi=dest vector, eax=dy, ebx=dz +// trashes eax,ebx,edx +void g3_rotate_delta_yz(g3s_vector *dest, fix dy, fix dz) { + int64_t r; + + // first column + r = fix64_mul(dy, vm4) + fix64_mul(dz, vm7); + dest->gX = fix64_to_fix(r); + + // second column + r = fix64_mul(dy, vm5) + fix64_mul(dz, vm8); + dest->gY = fix64_to_fix(r); + + // third column + r = fix64_mul(dy, vm6) + fix64_mul(dz, vm9); + dest->gZ = fix64_to_fix(r); +} + +// rotate a delta vector. takes edi=dest, eax,ebx,ecx=dx,dy,dz +// trashes all but ebp,edi +void g3_rotate_delta_xyz(g3s_vector *dest, fix dx, fix dy, fix dz) { + do_rotate(dx, dy, dz, &dest->gX, &dest->gY, &dest->gZ); +} + +// rotate a delta vector. takes edi=dest, esi=src +// trashes all but ebp,edi +void g3_rotate_delta_v(g3s_vector *dest, g3s_vector *src) { + do_rotate(src->gX, src->gY, src->gZ, &dest->gX, &dest->gY, &dest->gZ); +} + +// like add_delta, but creates and returns a new point +// takes esi=src, ebx=delta, returns edi=new point +// trashes eax,ebx +g3s_phandle g3_copy_add_delta_v(g3s_phandle src, g3s_vector *delta) { + g3s_point *point; + + getpnt(point); + point->gX = src->gX + delta->gX; + point->gY = src->gY + delta->gY; + point->gZ = src->gZ + delta->gZ; + point->p3_flags = 0; + code_point(point); + return (point); +} + +// adds a delta vector (created by rotate delta) to a point +// takes edi=point, esi=delta. clears projected bit, computes codes +// trashes eax,esi,bl +void g3_add_delta_v(g3s_phandle p, g3s_vector *delta) { + p->gX += delta->gX; + p->gY += delta->gY; + p->gZ += delta->gZ; + + p->p3_flags &= ~PF_PROJECTED; + code_point(p); +} + +// add an x delta to a point. takes edi=point, eax=dx +// trashes eax,ebx,edx +void g3_add_delta_x(g3s_phandle p, fix dx) { + p->gX += fix_mul(vm1, dx); + p->gY += fix_mul(vm2, dx); + p->gZ += fix_mul(vm3, dx); + p->p3_flags &= ~PF_PROJECTED; + + code_point(p); +} + +// add a y delta to a point. takes edi=point, eax=dy +// trashes eax,ebx,edx +void g3_add_delta_y(g3s_phandle p, fix dy) { + p->gX += fix_mul(vm4, dy); + p->gY += fix_mul(vm5, dy); + p->gZ += fix_mul(vm6, dy); + p->p3_flags &= ~PF_PROJECTED; + + code_point(p); +} + +// add a z delta to a point. takes edi=point, eax=dz +// trashes eax,ebx,edx +void g3_add_delta_z(g3s_phandle p, fix dz) { + p->gX += fix_mul(vm7, dz); + p->gY += fix_mul(vm8, dz); + p->gZ += fix_mul(vm9, dz); + p->p3_flags &= ~PF_PROJECTED; + + code_point(p); +} + +// add an xy delta to a point. takes edi=point, eax=dx, ebx=dy +// trashes eax,ebx,ecx,edx,esi +void g3_add_delta_xy(g3s_phandle p, fix dx, fix dy) { + int64_t r; + + // first column + r = fix64_mul(dx, vm1) + fix64_mul(dy, vm4); + p->gX += fix64_to_fix(r); + + // second column + r = fix64_mul(dx, vm2) + fix64_mul(dy, vm5); + p->gY += fix64_to_fix(r); + + // third column + r = fix64_mul(dx, vm3) + fix64_mul(dy, vm6); + p->gZ += fix64_to_fix(r); + + p->p3_flags &= ~PF_PROJECTED; + code_point(p); +} + +// add an xz delta to a point. takes edi=point, eax=dx, ebx=dz +// trashes eax,ebx,ecx,edx,esi +void g3_add_delta_xz(g3s_phandle p, fix dx, fix dz) { + int64_t r; + + // first column + r = fix64_mul(dx, vm1) + fix64_mul(dz, vm7); + p->gX += fix64_to_fix(r); + + // second column + r = fix64_mul(dx, vm2) + fix64_mul(dz, vm8); + p->gY += fix64_to_fix(r); + + // third column + r = fix64_mul(dx, vm3) + fix64_mul(dz, vm9); + p->gZ += fix64_to_fix(r); + + p->p3_flags &= ~PF_PROJECTED; + code_point(p); +} + +// add an yz delta to a point. takes edi=point, eax=dy, ebx=dz +// trashes eax,ebx,ecx,edx,esi +void g3_add_delta_yz(g3s_phandle p, fix dy, fix dz) { + int64_t r; + + // first column + r = fix64_mul(dy, vm4) + fix64_mul(dz, vm7); + p->gX += fix64_to_fix(r); + + // second column + r = fix64_mul(dy, vm5) + fix64_mul(dz, vm8); + p->gY += fix64_to_fix(r); + + // third column + r = fix64_mul(dy, vm6) + fix64_mul(dz, vm9); + p->gZ += fix64_to_fix(r); + + p->p3_flags &= ~PF_PROJECTED; + code_point(p); +} + +// add an xyz delta to a point. takes edi=point, eax=dx, ebx=dy, ecx=dz +// trashes eax,ebx,ecx,edx,esi +void g3_add_delta_xyz(g3s_phandle p, fix dx, fix dy, fix dz) { + fix rx, ry, rz; + + do_rotate(dx, dy, dz, &rx, &ry, &rz); + + p->gX += rx; + p->gY += ry; + p->gZ += rz; + + p->p3_flags &= ~PF_PROJECTED; + code_point(p); +} + +// like add_delta, but creates and returns a new point in edi +// add an x delta to a point. takes esi=point, eax=dx +// trashes eax,ebx,edx +g3s_phandle g3_copy_add_delta_x(g3s_phandle src, fix dx) { + g3s_point *point; + + getpnt(point); + point->gX = src->gX + fix_mul(dx, vm1); + point->gY = src->gY + fix_mul(dx, vm2); + point->gZ = src->gZ + fix_mul(dx, vm3); + point->p3_flags = 0; + code_point(point); + return (point); +} + +// like add_delta, but creates and returns a new point in edi +// add a y delta to a point. takes esi=point, eax=dy +// trashes eax,ebx,edx +g3s_phandle g3_copy_add_delta_y(g3s_phandle src, fix dy) { + g3s_point *point; + + getpnt(point); + point->gX = src->gX + fix_mul(dy, vm4); + point->gY = src->gY + fix_mul(dy, vm5); + point->gZ = src->gZ + fix_mul(dy, vm6); + point->p3_flags = 0; + code_point(point); + return (point); +} + +// like add_delta, but creates and returns a new point in edi +// add a z delta to a point. takes esi=point, eax=dz +// trashes eax,ebx,edx +g3s_phandle g3_copy_add_delta_z(g3s_phandle src, fix dz) { + g3s_point *point; + + getpnt(point); + point->gX = src->gX + fix_mul(dz, vm7); + point->gY = src->gY + fix_mul(dz, vm8); + point->gZ = src->gZ + fix_mul(dz, vm9); + point->p3_flags = 0; + code_point(point); + return (point); +} + +// like add_delta, but modifies an existing point in edi +// add an x delta to a point. takes esi=src point, edi=replace point, ax=dx +// trashes eax,ebx,edx +g3s_phandle g3_replace_add_delta_x(g3s_phandle src, g3s_phandle dst, fix dx) { + dst->gX = src->gX + fix_mul(dx, vm1); + dst->gY = src->gY + fix_mul(dx, vm2); + dst->gZ = src->gZ + fix_mul(dx, vm3); + dst->p3_flags = 0; + code_point(dst); + return (dst); +} + +// like add_delta, but modifies an existing point in edi +// add a y delta to a point. takes esi=src point, edi=replace point, ax=dy +// trashes eax,ebx,edx +g3s_phandle g3_replace_add_delta_y(g3s_phandle src, g3s_phandle dst, fix dy) { + dst->gX = src->gX + fix_mul(dy, vm4); + dst->gY = src->gY + fix_mul(dy, vm5); + dst->gZ = src->gZ + fix_mul(dy, vm6); + dst->p3_flags = 0; + code_point(dst); + return (dst); +} + +// like add_delta, but modifies an existing point in edi +// add a z delta to a point. takes esi=src point, edi=replace point, ax=dz +// trashes eax,ebx,edx +g3s_phandle g3_replace_add_delta_z(g3s_phandle src, g3s_phandle dst, fix dz) { + dst->gX = src->gX + fix_mul(dz, vm7); + dst->gY = src->gY + fix_mul(dz, vm8); + dst->gZ = src->gZ + fix_mul(dz, vm9); + dst->p3_flags = 0; + code_point(dst); + return (dst); +} + +// like add_delta, but creates and returns a new point in edi +// add an xy delta to a point. takes edi=point, eax=dx, ebx=dy +// trashes eax,ebx,ecx,edx,esi +g3s_phandle g3_copy_add_delta_xy(g3s_phandle src, fix dx, fix dy) { + g3s_point *point; + int64_t r; + + getpnt(point); + + // first column + r = fix64_mul(dx, vm1) + fix64_mul(dy, vm4); + point->gX = src->gX + fix64_to_fix(r); + + // second column + r = fix64_mul(dx, vm2) + fix64_mul(dy, vm5); + point->gY = src->gY + fix64_to_fix(r); + + // third column + r = fix64_mul(dx, vm3) + fix64_mul(dy, vm6); + point->gZ = src->gZ + fix64_to_fix(r); + + point->p3_flags = 0; + code_point(point); + return (point); +} + +// like add_delta, but creates and returns a new point in edi +// add an xz delta to a point. takes edi=point, eax=dx, ebx=dz +// trashes eax,ebx,ecx,edx,esi +g3s_phandle g3_copy_add_delta_xz(g3s_phandle src, fix dx, fix dz) { + g3s_point *point; + int64_t r; + + getpnt(point); + + // first column + r = fix64_mul(dx, vm1) + fix64_mul(dz, vm7); + point->gX = src->gX + fix64_to_fix(r); + + // second column + r = fix64_mul(dx, vm2) + fix64_mul(dz, vm8); + point->gY = src->gY + fix64_to_fix(r); + + // third column + r = fix64_mul(dx, vm3) + fix64_mul(dz, vm9); + point->gZ = src->gZ + fix64_to_fix(r); + + point->p3_flags = 0; + code_point(point); + return (point); +} + +// like add_delta, but creates and returns a new point in edi +// add an yz delta to a point. takes edi=point, eax=dy, ebx=dz +// trashes eax,ebx,ecx,edx,esi +g3s_phandle g3_copy_add_delta_yz(g3s_phandle src, fix dy, fix dz) { + g3s_point *point; + int64_t r; + + getpnt(point); + + // first column + r = fix64_mul(dy, vm4) + fix64_mul(dz, vm7); + point->gX = src->gX + fix64_to_fix(r); + + // second column + r = fix64_mul(dy, vm5) + fix64_mul(dz, vm8); + point->gY = src->gY + fix64_to_fix(r); + + // third column + r = fix64_mul(dy, vm6) + fix64_mul(dz, vm9); + point->gZ = src->gZ + fix64_to_fix(r); + + point->p3_flags = 0; + code_point(point); + return (point); +} + +// like add_delta, but creates and returns a new point in edi +// add an xyz delta to a point. takes edi=point, eax=dx, ebx=dy, ecx=dz +// trashes eax,ebx,ecx,edx,esi +g3s_phandle g3_copy_add_delta_xyz(g3s_phandle src, fix dx, fix dy, fix dz) { + g3s_point *point; + + getpnt(point); + do_rotate(dx, dy, dz, &point->gX, &point->gY, &point->gZ); + + point->gX += src->gX; + point->gY += src->gY; + point->gZ += src->gZ; + + point->p3_flags = 0; + code_point(point); + return (point); +} diff --git a/engine/src/Libraries/3D/Source/polygon.c b/engine/src/Libraries/3D/Source/polygon.c new file mode 100644 index 0000000..9cb639b --- /dev/null +++ b/engine/src/Libraries/3D/Source/polygon.c @@ -0,0 +1,535 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// +// $Source: r:/prj/lib/src/3d/RCS/polygon.asm $ +// $Revision: 1.31 $ +// $Author: jaemz $ +// $Date: 1994/11/06 13:59:23 $ +// +// Polygon drawers +// + +#include "3d.h" +#include "GlobalV.h" +#include "lg.h" +#include "OpenGL.h" + +// prototypes +int check_and_draw_common(long c, int n_verts, g3s_phandle *p); +int draw_poly_common(long c, int n_verts, g3s_phandle *p); +int draw_line_common(g3s_phandle p0, g3s_phandle p1); + +#define GR_WIRE_POLY_LINE 6 +#define GR_WIRE_POLY_SLINE 7 +#define GR_WIRE_POLY_CLINE 8 + +#define MAX_VERTS 100 // max for one poly + +// array of 2d points +grs_vertex p_vlist[MAX_VERTS]; +grs_vertex *p_vpl[MAX_VERTS]; +long _n_verts; +long poly_color; + +// arrays of point handles, used in clipping +g3s_phandle vbuf[MAX_VERTS]; +g3s_phandle _vbuf2[MAX_VERTS]; + +// for surface normal check +g3s_vector temp_vector; + +// used by line clipper +/*long temp_points[4]; +long n_temp_used;*/ + +long draw_color; +long poly_index[] = {FIX_UPOLY, FIX_TLUC8_UPOLY, FIX_USPOLY, FIX_TLUC8_SPOLY, FIX_UCPOLY}; + +char gour_flag; // 0=normal,1=tluc_poly,2=spoly,3=tluc_spoly,4=cpoly + +// check if a list of point (as in a polygon) are on screen. returns codes +// takes esi=list of points, ecx=codes, returns bx=codes. +// trashes ebx,ecx,edx,esi +g3s_codes g3_check_codes(int n_verts, g3s_phandle *p) { + int i; + g3s_codes retcode; + char andcode, orcode; + + andcode = 0xff; + orcode = 0; + + for (i = n_verts; i > 0; i--) { + andcode &= (*p)->codes; + orcode |= (*p)->codes; + p++; + } + + retcode.or_ = orcode; + retcode.and_ = andcode; + return (retcode); +} + +extern void g3_compute_normal_quick(g3s_vector *v, g3s_vector *v0, g3s_vector *v1, g3s_vector *v2); + +// takes 3 rotated points: eax,edx,ebx. +// returns al=true (& s flag set) if facing. trashes all but ebp +bool g3_check_poly_facing(g3s_phandle p0, g3s_phandle p1, g3s_phandle p2) { + g3_compute_normal_quick(&temp_vector, (g3s_vector *)p0, (g3s_vector *)p1, (g3s_vector *)p2); + + int64_t result = + fix64_mul(p0->gX, temp_vector.gX) + fix64_mul(p0->gY, temp_vector.gY) + fix64_mul(p0->gZ, temp_vector.gZ); + + return (fix64_int(result) < 0); +} + +// takes same input as draw_poly, but first checks if facing +int g3_check_and_draw_cpoly(int n_verts, g3s_phandle *p) { + gour_flag = 4; + return (check_and_draw_common(0, n_verts, p)); +} + +int g3_check_and_draw_tluc_spoly(int n_verts, g3s_phandle *p) { + gour_flag = 3; + return (check_and_draw_common(0, n_verts, p)); +} + +int g3_check_and_draw_spoly(int n_verts, g3s_phandle *p) { + gour_flag = 2; + return (check_and_draw_common(0, n_verts, p)); +} + +int g3_check_and_draw_tluc_poly(long c, int n_verts, g3s_phandle *p) { + gour_flag = 1; + return (check_and_draw_common(c, n_verts, p)); +} + +int g3_check_and_draw_poly(long c, int n_verts, g3s_phandle *p) { + gour_flag = 0; + return (check_and_draw_common(c, n_verts, p)); +} + +int check_and_draw_common(long c, int n_verts, g3s_phandle *p) { +// clang-format off +#ifdef stereo_on + test _g3d_stereo,1 + jz check_and_draw_common_raw + pushm eax,ecx,esi + + call check_and_draw_common_raw + set_rt_canv + + popm eax,ecx,esi + pushm eax,ecx + + // moves list at esi to temp and repoints esi + test gour_flag,6 + jnz do_uvi_copy1 + move_to_stereo + jmp raw_poly_continue1 +do_uvi_copy1: + mov edx,esi + mov eax,ecx + move_to_stereo_and_uvi + mov esi,edx +raw_poly_continue1: + + popm eax,ecx + call check_and_draw_common_raw + + set_lt_canv + ret +check_and_draw_common_raw: +#endif + // clang-format on + + if (g3_check_poly_facing(p[0], p[1], p[2])) { +#ifdef stereo_on + js draw_poly_common_raw +#else + return draw_poly_common(c, n_verts, p); +#endif + } + else return 0; // no draw +} + +// takes ecx=# verts, esi=ptr to list of point handles +// modify all but ebp + +// RBG-space smooth poly +int g3_draw_cpoly(int n_verts, g3s_phandle *p) { + gour_flag = 4; + return draw_poly_common(0, n_verts, p); +} + +// smooth poly +int g3_draw_tluc_spoly(int n_verts, g3s_phandle *p) { + gour_flag = 3; + return draw_poly_common(0, n_verts, p); +} + +// smooth poly +int g3_draw_spoly(int n_verts, g3s_phandle *p) { + gour_flag = 2; + return draw_poly_common(0, n_verts, p); +} + +int g3_draw_tluc_poly(long c, int n_verts, g3s_phandle *p) { + gour_flag = 1; + return draw_poly_common(c, n_verts, p); +} + +int g3_draw_poly(long c, int n_verts, g3s_phandle *p) { + gour_flag = 0; + return draw_poly_common(c, n_verts, p); +} + +int draw_poly_common(long c, int n_verts, g3s_phandle *p) { + if (use_opengl()) { + extern int opengl_draw_poly(long, int, g3s_phandle *, char); + return opengl_draw_poly(c, n_verts, p, gour_flag); + } + + char andcode, orcode; + g3s_phandle *old_p; + int i; + g3s_phandle *src; + g3s_phandle src_pt; + grs_vertex *dest; + long rgb; + +// clang-format off +#ifdef stereo_on + test _g3d_stereo, 1 + jz draw_poly_common_raw + + pushm eax,ecx,esi + + call draw_poly_common_raw + set_rt_canv + + popm eax,ecx,esi + pushm eax,ecx + + // moves list at esi to temp and repoints esi + test gour_flag,6 + jnz do_uvi_copy2 + move_to_stereo + jmp raw_poly_continue2 +do_uvi_copy2: + mov edx,esi + mov eax,ecx + move_to_stereo_and_uvi + mov esi,edx +raw_poly_continue2: + + popm eax,ecx + call draw_poly_common_raw + + set_lt_canv + ret + +draw_poly_common_raw: +#endif + + // clang-format on + + poly_color = c; + + // first, go through points and get codes + andcode = 0xff; + orcode = 0; + old_p = p; + + for (i = n_verts; i > 0; i--) { + andcode &= (*p)->codes; + orcode |= (*p)->codes; + p++; + } + + if (andcode) + return CLIP_ALL; // punt! + + p = old_p; + + // copy to temp buffer for clipping + // BlockMove(p,vbuf,n_verts<<2); + memmove(vbuf, p, n_verts * sizeof *p); + + n_verts = g3_clip_polygon(n_verts, vbuf, _vbuf2); + if (!n_verts) + return CLIP_ALL; + + // now, copy 2d points to buffer for polygon draw, projecting if neccesary + src = _vbuf2; + dest = p_vlist; + + for (i = 0; i < n_verts; i++) { + src_pt = *(src++); + + // check if this point has been projected + if ((src_pt->p3_flags & PF_PROJECTED) == 0) // projected yet? + g3_project_point(src_pt); + + dest->x = src_pt->sx; // store 2D X & Y + dest->y = src_pt->sy; + p_vpl[i] = dest; // store ptr + dest++; + } + + if (gour_flag >= 2) // some kind of shading + { + if (gour_flag >= 4) // cpoly + { + src = _vbuf2; + dest = p_vlist; + for (i = 0; i < n_verts; i++) { + src_pt = *(src++); + rgb = src_pt->rgb; + dest->u = (rgb & 0x000003ff) << 14; // r + dest->v = (rgb & 0x001ffc00) << 3; // g + dest->w = (rgb & 0xffe00000) >> 8; // b + + dest++; + } + } else // spoly + { + src = _vbuf2; + dest = p_vlist; + for (i = 0; i < n_verts; i++) { + src_pt = *(src++); + dest->i = (((ulong)src_pt->i) + gouraud_base) << 8; + + dest++; + } + } + } + + // draw it + ((void (*)(long c, int n, grs_vertex **vpl))grd_canvas_table[poly_index[gour_flag]])(poly_color, n_verts, p_vpl); + + return CLIP_NONE; +} + +// draw a point in 3-space. takes esi=point. returns al=drew. +// trashes eax,edx,esi and if must project, ecx +int g3_draw_point(g3s_phandle p) { + int sx, sy; + + if (p->codes) + return CLIP_ALL; + + if ((p->p3_flags & PF_PROJECTED) == 0) // check if projected + g3_project_point(p); + + sx = (p->sx + 0x08000) >> 16; // round & get int part + sy = (p->sy + 0x08000) >> 16; // round & get int part + return (((int (*)(short x, short y))grd_canvas_table[DRAW_POINT])(sx, sy)); +} + +// draws a line in 3-space. takes esi,edi=points + +// fixed 7/24 dc to have a common and have draw_line set gour_flag, not ignore +// it +int g3_draw_cline(g3s_phandle p0, g3s_phandle p1) // rgb-space gouraud line +{ + if (p0->rgb != p1->rgb) { + gour_flag = 1; + return (draw_line_common(p0, p1)); + } else { + gour_flag = 0; + draw_color = grd_ipal[gr_index_brgb(p0->rgb)]; + return (draw_line_common(p0, p1)); + } +} + +int g3_draw_sline(g3s_phandle p0, g3s_phandle p1) // 2d-intensity gouraud line +{ + gour_flag = -1; + return (draw_line_common(p0, p1)); +} + +int g3_draw_line(g3s_phandle p0, g3s_phandle p1) { + draw_color = gr_get_fcolor(); + gour_flag = 0; + return (draw_line_common(p0, p1)); +} + +int draw_line_common(g3s_phandle p0, g3s_phandle p1) { + byte code0, code1; + int result; + grs_vertex v0, v1; + + vbuf[0] = p0; + vbuf[1] = p1; + if (g3_clip_line(vbuf, _vbuf2) == 16) + return CLIP_ALL; + + p0 = _vbuf2[0]; + p1 = _vbuf2[1]; + code0 = p0->codes; + code1 = p1->codes; + + // ok, draw now with points = esi,edi. bl=codes_or + // note that in stereo mode, you're doing this twice. We should + // just always project all points, or have the code clipper update stuff + + if ((p0->p3_flags & PF_PROJECTED) == 0) + g3_project_point(p0); + if ((p1->p3_flags & PF_PROJECTED) == 0) + g3_project_point(p1); + + if (draw_color == 255) + draw_color = 0; + + if (gour_flag == 0) // normal line + { + // use wire poly lines. Always clip. + // set up args -- vertex contents on stack, pass sp + // for line only need 1st 2 elements of grs_vertex, only push them + v0.x = p0->sx; + v0.y = p0->sy; + v1.x = p1->sx; + v1.y = p1->sy; + ((int (*)(long c, long parm, grs_vertex *v0, grs_vertex *v1))grd_line_clip_fill_vector[GR_WIRE_POLY_LINE])( + draw_color, gr_get_fill_parm(), &v0, &v1); + + result = CLIP_NONE; + } else if (gour_flag > 0) // cline + { + uchar a, b, c; + + v0.x = p0->sx; + v0.y = p0->sy; + gr_split_rgb(p0->rgb, &a, &b, &c); + v0.u = a; + v0.v = b; + v0.w = c; + + v1.x = p1->sx; + v1.y = p1->sy; + gr_split_rgb(p1->rgb, &a, &b, &c); + v1.u = a; + v1.v = b; + v1.w = c; + ((int (*)(long c, long parm, grs_vertex *v0, grs_vertex *v1))grd_line_clip_fill_vector[GR_WIRE_POLY_CLINE])( + gr_get_fcolor(), gr_get_fill_parm(), &v0, &v1); + + result = CLIP_NONE; + // DebugString("implement me?"); + /* + // mov edx,ebx // dl=clip codes + + // set up args -- vertex contents on stack, pass sp + // for cline only need 1st 5 elements of grs_vertex, only push them + gr_splitrgb [esi].rgb,eax // eax scratch + pushm [esi].sy,[esi].sx + mov ebx,esp // v0 on stack, addr is arg + gr_splitrgb [edi].rgb,eax // eax scratch + pushm [edi].sy,[edi].sx + mov ecx,esp // v1 on stack, addr is arg + + gr_getcol eax + gr_getfp edx + mov edi,grd_line_clip_fill_vector + call d [edi + 4*GR_WIRE_POLY_CLINE] + add esp,40 // 2 vertex's each 5 fix's + + mov eax,CLIP_NONE + jmp leave_draw_line*/ + } else // sline + { + DEBUG("%s: implement me?", __FUNCTION__); + // we have to do this annoyingly because i is an sfix, + // and 2d takes a fix, so we dump things in eax and munge + /* + // new smaller converter + xor eax,eax + mov ax,[edi].i + shl eax,8 + push eax + push [edi].sy + xor eax,eax + mov ax,[esi].i + shl eax,8 + + or bl,bl // check triv acc + + mov ebx,eax // we had to do all the ugliness in eax b/c + needed + // the codes in bl to check for triv acc, + and we + // couldnt do the check earlier because all + our + // and's reset the zero flag needed for the + jz below mov eax,[esi].sx mov edx,[esi].sy mov ecx,[edi].sx + + jz unclipped_sline + call gen_fix_sline_ + jmp leave_draw_line + + ret + + unclipped_sline: + // gr_call FIX_USLINE + // set up args -- vertex contents on stack, pass sp + // pushd contents of vertex -- don't care about u,v,w + // i needs to have sfix to fix + xor eax,eax // eax scratch + mov ax,[esi].i + shl eax,8 + push eax + pushm 0,0,0 // uvw are don't cares + pushm [esi].sy,[esi].sx + mov ebx,esp // v0 on stack, addr is arg + xor eax,eax // eax scratch + mov ax,[edi].i + shl eax,8 + push eax + pushm 0,0,0 // uvw are don't cares + pushm [edi].sy,[edi].sx + mov ecx,esp // v1 on stack, addr is arg + // now its OK to trash esi + gr_getcol eax + gr_getfp edx + mov esi,grd_uline_fill_vector + call d [esi + 4*SLINE] + add esp,48 // 2 vertex's each 6 fix's + + // junk from old stuff -- kill these + pop esi + pop esi + + mov eax,CLIP_NONE + jmp leave_draw_line + */ + } + + return result; +} + +// check if a surface is facing the viewer +// takes esi=point on surface, edi=surface normal (can be unnormalized) +// trashes eax,ebx,ecx,edx. returns al=true & sign set, if facing +bool g3_check_normal_facing(g3s_vector *v, g3s_vector *normal) { + int64_t result = fix64_mul(v->gX - _view_position.gX, normal->gX) + + fix64_mul(v->gY - _view_position.gY, normal->gY) + + fix64_mul(v->gZ - _view_position.gZ, normal->gZ); + + return (fix64_int(result) < 0); +} diff --git a/engine/src/Libraries/3D/Source/slew.c b/engine/src/Libraries/3D/Source/slew.c new file mode 100644 index 0000000..ab60a7c --- /dev/null +++ b/engine/src/Libraries/3D/Source/slew.c @@ -0,0 +1,66 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// +// $Source: n:/project/lib/src/3d/RCS/slew.asm $ +// $Revision: 1.2 $ +// $Author: dc $ +// $Date: 1993/08/10 22:54:25 $ +// +// Support function(s) for slew system +// +// $Log: slew.asm $ +// Revision 1.2 1993/08/10 22:54:25 dc +// add _3d.inc to includes +// +// Revision 1.1 1993/05/24 16:27:24 matt +// Initial revision +// +// + +#include "3d.h" +#include "GlobalV.h" +#include "lg.h" + +// fills in three vectors, each of length step_size, in the x,y, & z directions +// in the viewer's frame of reference. Any (or all) of the vector ptrs can +// be NULL and will be skipped. +// takes eax=size, ebx,ecx,edi=x,y, & z vectors +// trashes eax,edx,esi +void g3_get_slew_step(fix step_size, g3s_vector *x_step, g3s_vector *y_step, g3s_vector *z_step) { + // x vector + if (x_step) { + x_step->gX = fix_mul(step_size, unscaled_matrix.m1); + x_step->gY = fix_mul(step_size, unscaled_matrix.m4); + x_step->gZ = fix_mul(step_size, unscaled_matrix.m7); + } + + // y vector + if (y_step) { + y_step->gX = fix_mul(step_size, unscaled_matrix.m2); + y_step->gY = fix_mul(step_size, unscaled_matrix.m5); + y_step->gZ = fix_mul(step_size, unscaled_matrix.m8); + } + + // z vector + if (z_step) { + z_step->gX = fix_mul(step_size, unscaled_matrix.m3); + z_step->gY = fix_mul(step_size, unscaled_matrix.m6); + z_step->gZ = fix_mul(step_size, unscaled_matrix.m9); + } +} diff --git a/engine/src/Libraries/3D/Source/tmap.c b/engine/src/Libraries/3D/Source/tmap.c new file mode 100644 index 0000000..48e9b61 --- /dev/null +++ b/engine/src/Libraries/3D/Source/tmap.c @@ -0,0 +1,750 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// $Source: r:/prj/lib/src/3d/RCS/tmap.asm $ +// $Revision: 1.30 $ +// $Author: jaemz $ +// $Date: 1994/09/06 00:12:15 $ +// +// Texture mappers +// +// $Log: tmap.asm $ +// Revision 1.30 1994/09/06 00:12:15 jaemz +// Externed vbuf2 and nvert so we can peek at them later +// +// Revision 1.29 1994/08/18 03:47:36 jaemz +// added underscore for c to stereo globvs +// +// Revision 1.28 1994/08/16 18:37:43 kevin +// moved constants to 2d.inc. Modified code for compatability with new 2d. +// +// Revision 1.27 1994/08/04 16:36:36 jaemz +// *** empty log message *** +// +// Revision 1.26 1994/07/21 22:44:46 jaemz +// arrrgh +// +// Revision 1.25 1994/07/19 13:48:47 jaemz +// Added support for stereo +// +// Revision 1.24 1994/07/06 22:37:14 kevin +// use per_umap directly instead of going through canvas table. +// +// Revision 1.23 1994/05/19 09:41:20 kevin +// g3_light(draw)_t(l,floor_,wall_)map now use watcom register passing +// conventions. All perspective maps pass linearity tests. Non/power/of/2 +// bitmaps are punted (for now). Point copy loop has been somewhat optimized. +// +// Revision 1.22 1994/05/04 18:46:44 kevin +// Do clut lighting check correctly. Check floors and full perspective +// tmaps for linearity. +// +// Revision 1.21 1994/05/02 23:00:52 kevin +// Added support for wall and floor specific texture maps. +// +// Revision 1.20 1993/12/27 21:29:32 kevin +// Hacked in rsd support. +// +// Revision 1.19 1993/12/04 12:44:22 kevin +// Changed 2d calls to conform to new icanvas.inc. +// +// Revision 1.18 1993/10/25 16:28:48 kaboom +// Same bug as last time for g3_draw_tmap. +// +// Revision 1.17 1993/10/22 12:58:02 kaboom +// Fixed bug in quad_tile routines---was losing pointer to vertices. +// +// Revision 1.16 1993/10/22 09:35:26 kaboom +// Added new linear map routines. +// +// Revision 1.15 1993/10/13 13:38:05 kevin +// Changed copy_loop to scale to full bitmap; hopefully without causing +// wrapping. +// +// Revision 1.14 1993/10/03 10:20:30 kaboom +// Fixed 2 register saving bugs. Also cleanup up code a little. +// +// Revision 1.13 1993/10/02 11:02:07 kaboom +// Changed names of clip_{line,polygon} to g3_clip_{line,polygon} to avoid +// name collisions. +// +// Revision 1.12 1993/10/02 09:29:54 kaboom +// Now calls uv versions of 2d primitives. +// +// Revision 1.11 1993/08/11 15:02:14 kaboom +// Added support for lighting texture mappers. +// +// Revision 1.10 1993/08/10 22:54:27 dc +// add _3d.inc to includes +// +// Revision 1.9 1993/07/08 23:37:54 kaboom +// Changed old use of align field in grs_bitmap to new wlog and hlog. +// +// Revision 1.8 1993/06/16 14:05:06 kaboom +// Replaced code for tiling in calc_warp_matrix accidentally deleted in +// last revision. +// +// Revision 1.7 1993/06/15 19:00:49 kaboom +// Fixed overflow and scaling problems with warp matrix calculation. +// +// Revision 1.6 1993/06/09 15:34:29 kaboom +// Fixed non-initialization of codes for g3_draw_tmap_tile, which +// sometimes caused massive destruction. +// +// Revision 1.5 1993/06/09 04:24:25 kaboom +// Changed g3_draw_tmap_tile to take basis vectors instead of texture map +// width and height. +// +// Revision 1.4 1993/05/11 15:24:21 matt +// Changed g3_draw_tmap_tile() to reuse warp matrix if anchor==0 +// +// Revision 1.3 1993/05/11 15:02:00 matt +// Fixed an overflow problem in the warp matrix setup +// +// Revision 1.2 1993/05/10 15:07:51 matt +// Fixed g3_draw_tmap_quad_tile(), which didn't do divide for width and height +// count, so always acted like w,h=1,1. +// +// Revision 1.1 1993/05/04 17:39:54 matt +// Initial revision +// + +#include "3d.h" +#include "GlobalV.h" +#include "lg.h" + +extern void per_umap(grs_bitmap *bm, int n, grs_vertex **vpl, grs_tmap_info *ti); +extern void h_umap(grs_bitmap *bm, int n, grs_vertex **vpl, grs_tmap_info *ti); +extern void v_umap(grs_bitmap *bm, int n, grs_vertex **vpl, grs_tmap_info *ti); + +// prototypes +int do_tmap_tile(g3s_phandle upperleft, g3s_vector *u_vec, g3s_vector *v_vec, int nverts, g3s_phandle *vp, + grs_bitmap *bm); +int do_check_tmap_quad_tile(g3s_phandle *vp, grs_bitmap *bm, int width_count, int height_count); +int do_tmap(int n, g3s_phandle *vp, grs_bitmap *bm); +int do_tmap_quad_tile(g3s_phandle *vp, grs_bitmap *bm, int width_count, int height_count); +void calc_warp_matrix2(g3s_phandle upperleft, fix x10, fix x20, fix y10, fix y20, fix z10, fix z20, grs_bitmap *bm); +int draw_tmap_common(int n, g3s_phandle *vp, grs_bitmap *bm); +int check_linear(int w); + +#define _bm_w 8 +#define _bm_h 10 + +void *tmap_func; +grs_bitmap unpack_bm; +grs_tmap_info ti; +grs_tmap_info *ti_ptr = &ti; + +// matrix for 2d +fix warp[9]; + +long light_flag; + +// arrays of point handles, used in clipping +extern g3s_phandle vbuf[]; +extern g3s_phandle _vbuf2[]; + +// array of 2d points +extern grs_vertex p_vlist[]; +extern grs_vertex *p_vpl[]; +extern long _n_verts; + +// tiles a texture map over an arbitarary polygon. +// takes eax=upperleft, ebx=width, ecx=height, edx=nverts, esi=ptr to points, +// edi=ptr to bitmap +int g3_draw_lmap_tile(g3s_phandle upperleft, g3s_vector *u_vec, g3s_vector *v_vec, int nverts, g3s_phandle *vp, + grs_bitmap *bm) { + tmap_func = (void *)&h_umap; + ti.tmap_type = GRC_BILIN; + ti.flags = 0; + light_flag = 0; + return (do_tmap_tile(upperleft, u_vec, v_vec, nverts, vp, bm)); +} + +int g3_light_lmap_tile(g3s_phandle upperleft, g3s_vector *u_vec, g3s_vector *v_vec, int nverts, g3s_phandle *vp, + grs_bitmap *bm) { + tmap_func = (void *)&h_umap; + ti.tmap_type = GRC_LIT_BILIN; + ti.flags = 0; + light_flag = 1; + return (do_tmap_tile(upperleft, u_vec, v_vec, nverts, vp, bm)); +} + +int g3_light_tmap_tile(g3s_phandle upperleft, g3s_vector *u_vec, g3s_vector *v_vec, int nverts, g3s_phandle *vp, + grs_bitmap *bm) { + tmap_func = (void *)&per_umap; + ti.tmap_type = GRC_LIT_PER; + ti.flags = 0; + light_flag = 1; + return (do_tmap_tile(upperleft, u_vec, v_vec, nverts, vp, bm)); +} + +int g3_draw_tmap_tile(g3s_phandle upperleft, g3s_vector *u_vec, g3s_vector *v_vec, int nverts, g3s_phandle *vp, + grs_bitmap *bm) { + tmap_func = (void *)&per_umap; + ti.tmap_type = GRC_PER; + ti.flags = 0; + light_flag = 0; + return (do_tmap_tile(upperleft, u_vec, v_vec, nverts, vp, bm)); +} + +int do_tmap_tile(g3s_phandle upperleft, g3s_vector *u_vec, g3s_vector *v_vec, int nverts, g3s_phandle *vp, + grs_bitmap *bm) { + byte andcode, orcode; + int i; + g3s_phandle *src; + g3s_phandle tempHand; + fix x10; + fix y10; + fix z10; + fix x20; + fix y20; + fix z20; + + // get codes for this polygon + andcode = 0xff; + orcode = 0; + src = vp; + for (i = nverts; i > 0; i--) { + tempHand = *(src++); + andcode &= tempHand->codes; + orcode |= tempHand->codes; + } + + // check codes for trivial reject. + if (andcode) + return CLIP_ALL; + + // upper left handle of 0 means reuse old warp matrix. + + // store elements of u difference vector as 10 warp differences. + x10 = u_vec->gX; + y10 = u_vec->gY; + z10 = u_vec->gZ; + + // store elements of v difference vector as 20 warp differences. + x20 = v_vec->gX; + y20 = v_vec->gY; + z20 = v_vec->gZ; + + // do warp matrix calculations + calc_warp_matrix2(upperleft, x10, x20, y10, y20, z10, z20, bm); + + src = vp; + for (i = nverts; i > 0; i--) { + fix a, b; + fix blah1; + fix blah2; + fix blah3; + + tempHand = *(src++); + a = fix_div(tempHand->gX, tempHand->gZ); + b = fix_div(tempHand->gY, tempHand->gZ); + + blah1 = fix_mul(warp[0], a) + fix_mul(warp[1], b) + warp[2]; + blah2 = fix_mul(warp[3], a) + fix_mul(warp[4], b) + warp[5]; + blah3 = fix_mul(warp[6], a) + fix_mul(warp[7], b) + warp[8]; + + tempHand->uv.u = fix_div(blah1, blah3) >> 8; + tempHand->uv.v = fix_div(blah2, blah3) >> 8; + tempHand->p3_flags |= PF_U | PF_V; + } + + return (draw_tmap_common(nverts, vp, bm)); +} + +int g3_check_and_draw_lmap_quad_tile(g3s_phandle *vp, grs_bitmap *bm, int width_count, int height_count) { + tmap_func = (void *)&h_umap; + ti.tmap_type = GRC_BILIN; + ti.flags = 0; + light_flag = 0; + return (do_check_tmap_quad_tile(vp, bm, width_count, height_count)); +} + +int g3_check_and_light_lmap_quad_tile(g3s_phandle *vp, grs_bitmap *bm, int width_count, int height_count) { + tmap_func = (void *)&h_umap; + ti.tmap_type = GRC_LIT_BILIN; + ti.flags = 0; + light_flag = 1; + return (do_check_tmap_quad_tile(vp, bm, width_count, height_count)); +} + +int g3_check_and_light_tmap_quad_tile(g3s_phandle *vp, grs_bitmap *bm, int width_count, int height_count) { + tmap_func = (void *)&per_umap; + ti.tmap_type = GRC_LIT_PER; + ti.flags = 0; + light_flag = 1; + return (do_check_tmap_quad_tile(vp, bm, width_count, height_count)); +} + +int g3_check_and_draw_tmap_quad_tile(g3s_phandle *vp, grs_bitmap *bm, int width_count, int height_count) { + tmap_func = (void *)&per_umap; + ti.tmap_type = GRC_PER; + ti.flags = 0; + light_flag = 0; + return (do_check_tmap_quad_tile(vp, bm, width_count, height_count)); +} + +int do_check_tmap_quad_tile(g3s_phandle *vp, grs_bitmap *bm, int width_count, int height_count) { + if (g3_check_poly_facing(vp[0], vp[1], vp[2])) + return do_tmap_quad_tile(vp, bm, width_count, height_count); + else + return CLIP_ALL; +} + +// takes eax=nverts edx=ptr to points, ebx=ptr to bitmap +int g3_draw_floor_map(int n, g3s_phandle *vp, grs_bitmap *bm) { + tmap_func = (void *)&h_umap; + ti.tmap_type = GRC_FLOOR; + ti.flags = TMF_FLOOR; + light_flag = 0; + return (do_tmap(n, vp, bm)); +} + +int g3_light_floor_map(int n, g3s_phandle *vp, grs_bitmap *bm) { + tmap_func = (void *)&h_umap; + ti.tmap_type = GRC_LIT_FLOOR; + ti.flags = TMF_FLOOR; + light_flag = 1; + return (do_tmap(n, vp, bm)); +} + +int g3_draw_wall_map(int n, g3s_phandle *vp, grs_bitmap *bm) { + tmap_func = (void *)&v_umap; + ti.tmap_type = GRC_WALL1D; + ti.flags = TMF_WALL; + light_flag = 0; + return (do_tmap(n, vp, bm)); +} + +int g3_light_wall_map(int n, g3s_phandle *vp, grs_bitmap *bm) { + tmap_func = (void *)&v_umap; + ti.tmap_type = GRC_LIT_WALL1D; + ti.flags = TMF_WALL; + light_flag = 1; + return (do_tmap(n, vp, bm)); +} + +int g3_draw_lmap(int n, g3s_phandle *vp, grs_bitmap *bm) { + tmap_func = (void *)&h_umap; + ti.tmap_type = GRC_BILIN; + ti.flags = 0; + light_flag = 0; + return (do_tmap(n, vp, bm)); +} + +int g3_light_lmap(int n, g3s_phandle *vp, grs_bitmap *bm) { + tmap_func = (void *)&h_umap; + ti.tmap_type = GRC_LIT_BILIN; + ti.flags = 0; + light_flag = 1; + return (do_tmap(n, vp, bm)); +} + +int g3_light_tmap(int n, g3s_phandle *vp, grs_bitmap *bm) { + tmap_func = (void *)&per_umap; + ti.tmap_type = GRC_LIT_PER; + ti.flags = 0; + light_flag = 1; + return (do_tmap(n, vp, bm)); +} + +int g3_draw_tmap(int n, g3s_phandle *vp, grs_bitmap *bm) { + tmap_func = (void *)&per_umap; + ti.tmap_type = GRC_PER; + ti.flags = 0; + light_flag = 0; + return (do_tmap(n, vp, bm)); +} + +int do_tmap(int n, g3s_phandle *vp, grs_bitmap *bm) { + byte andcode, orcode; + int i; + g3s_phandle *src; + g3s_phandle tempHand; + +// clang-format off +#ifdef stereo_on + test _g3d_stereo,1 + jz no_stereo1 + push eax // calling destroys eax + mov ecx,d [ti_ptr] + push ecx + mov ecx,d [ti_ptr+4] + push ecx + mov ecx,tmap_func + push ecx + mov ecx,light_flag + push ecx + call do_tmap_raw + pop eax + mov light_flag,eax + pop eax + mov tmap_func,eax + pop eax + mov d [ti_ptr+4],eax + pop eax + mov d [ti_ptr],eax + pop eax + + pushm eax,ebx // copy list and codes and uv and rgb and i + // num points,pointer to bmap + + move_to_stereo_and_uvi + + set_rt_canv + + popm eax,ebx + call do_tmap_raw + + set_lt_canv + popad + ret + +do_tmap_raw: + pushad +no_stereo1: +#endif + + // clang-format on + + // convert RSD bitmap to normal + if (bm->type == BMT_RSD8) { + if (gr_rsd8_convert(bm, &unpack_bm) != GR_UNPACK_RSD8_OK) + return CLIP_ALL; + else + bm = &unpack_bm; + } + + // get codes for this polygon + andcode = 0xff; + orcode = 0; + src = vp; + for (i = n; i > 0; i--) { + tempHand = *(src++); + andcode &= tempHand->codes; + orcode |= tempHand->codes; + } + + // check codes for trivial reject. + if (andcode) + return CLIP_ALL; + + return (draw_tmap_common(n, vp, bm)); +} + +// draws a square texture map, where the corners of the 3d quad match the +// corners of the bitmap +// takes esi=ptr to points, edi=ptr to bitmap, eax=width count, ebx=height count +int g3_draw_lmap_quad_tile(g3s_phandle *vp, grs_bitmap *bm, int width_count, int height_count) { + tmap_func = (void *)&h_umap; + ti.tmap_type = GRC_BILIN; + ti.flags = 0; + light_flag = 0; + return (do_tmap_quad_tile(vp, bm, width_count, height_count)); +} + +int g3_light_lmap_quad_tile(g3s_phandle *vp, grs_bitmap *bm, int width_count, int height_count) { + tmap_func = (void *)&h_umap; + ti.tmap_type = GRC_LIT_BILIN; + ti.flags = 0; + light_flag = 1; + return (do_tmap_quad_tile(vp, bm, width_count, height_count)); +} + +int g3_light_tmap_quad_tile(g3s_phandle *vp, grs_bitmap *bm, int width_count, int height_count) { + tmap_func = (void *)&per_umap; + ti.tmap_type = GRC_LIT_PER; + ti.flags = 0; + light_flag = 1; + return (do_tmap_quad_tile(vp, bm, width_count, height_count)); +} + +int g3_draw_tmap_quad_tile(g3s_phandle *vp, grs_bitmap *bm, int width_count, int height_count) { + tmap_func = (void *)&per_umap; + ti.tmap_type = GRC_PER; + ti.flags = 0; + light_flag = 0; + return (do_tmap_quad_tile(vp, bm, width_count, height_count)); +} + +int do_tmap_quad_tile(g3s_phandle *vp, grs_bitmap *bm, int width_count, int height_count) { + int temp_w, temp_h; + byte andcode, orcode; + int i; + g3s_phandle *src; + g3s_phandle tempHand; + + temp_w = width_count << 8; + temp_h = height_count << 8; + _n_verts = 4; + + vp[0]->uv.u = vp[0]->uv.v = 0; + vp[0]->p3_flags |= PF_U | PF_V; + + vp[1]->uv.u = temp_w; + vp[1]->uv.v = 0; + vp[1]->p3_flags |= PF_U | PF_V; + + vp[2]->uv.u = temp_w; + vp[2]->uv.v = temp_h; + vp[2]->p3_flags |= PF_U | PF_V; + + vp[3]->uv.u = 0; + vp[3]->uv.v = temp_h; + vp[3]->p3_flags |= PF_U | PF_V; + + // first, go though points and get codes + andcode = 0xff; + orcode = 0; + src = vp; + for (i = 4; i > 0; i--) { + tempHand = *(src++); + andcode &= tempHand->codes; + orcode |= tempHand->codes; + } + + // check codes for trivial reject. + if (andcode) + return CLIP_ALL; + + return (draw_tmap_common(4, vp, bm)); +} + +int draw_tmap_common(int n, g3s_phandle *vp, grs_bitmap *bm) { + int branch_to_copy = 0; + int hlog, wlog; + g3s_phandle temphand; + int i, temp; + grs_vertex *cur_vert; + + // always clip for now + // copy to temp buffer for clipping + // BlockMove(vp,vbuf,n*4); + memmove(vbuf, vp, n * sizeof *vbuf); + + _n_verts = n = g3_clip_polygon(n, vbuf, _vbuf2); + if (n == 0) + return CLIP_ALL; + if (ti.tmap_type >= GRC_CLUT_BILIN + 2) + branch_to_copy = check_linear(n); + else + branch_to_copy = 1; + + // check if bitmap is power of 2 + wlog = bm->wlog; + hlog = bm->hlog; + + if (((1 << wlog) != bm->w) || ((1 << hlog) != bm->h)) + return CLIP_ALL; + + wlog += 8; + hlog += 8; + + // now, copy 2d points to buffer for tmap call, projecting if neccesary + // for (i=n-1; i--; i>=0) + for (i = 0; i < n; i++) { + temphand = _vbuf2[i]; + p_vpl[i] = cur_vert = &p_vlist[i]; + + // check if this point has been projected + if ((temphand->p3_flags & PF_PROJECTED) == 0) + g3_project_point(temphand); + + cur_vert->x = temphand->sx; + cur_vert->y = temphand->sy; + + temp = temphand->uv.u; + if (temp <= 0) + temp++; + else + temp--; + cur_vert->u = temp << wlog; + + temp = temphand->uv.v; + if (temp <= 0) + temp++; + else + temp--; + cur_vert->v = temp << hlog; + + cur_vert->i = temphand->i << 8; + + if (!branch_to_copy) // fix Z + { + temp = temphand->gZ; + cur_vert->w = fix_div(0x010000, temp); // 1/Z + } + } + + if (!light_flag) { + ((void (*)(grs_bitmap * bm, int n, grs_vertex **vpl, grs_tmap_info *ti)) tmap_func)(bm, _n_verts, p_vpl, &ti); + return CLIP_NONE; + } else { + extern fix gr_clut_lit_tol; + int temp_n; + fix imax, imin, temp_i; + grs_vertex *temp_p_vlist; + + temp_p_vlist = p_vlist; + temp_n = n - 1; + imax = imin = temp_p_vlist[temp_n].i; + while (--n >= 0) { + temp_i = temp_p_vlist[n].i; + if (temp_i < imin) + imin = temp_i; + if (temp_i > imax) + imax = temp_i; + } + + temp_i = imax - imin; + if (temp_i >= gr_clut_lit_tol) { + ((void (*)(grs_bitmap * bm, int n, grs_vertex **vpl, grs_tmap_info *ti)) tmap_func)(bm, _n_verts, p_vpl, + &ti); + return CLIP_NONE; + } else { + uchar *temp_ptr; + + imin += imax; + imin >>= 9; + imin &= 0xff00; + temp_ptr = gr_get_light_tab() + imin; + ti.clut = temp_ptr; + ti.tmap_type += 2; + ti.flags |= TMF_CLUT; + + ((void (*)(grs_bitmap * bm, int n, grs_vertex **vpl, grs_tmap_info *ti)) tmap_func)(bm, _n_verts, p_vpl, + &ti); + return CLIP_NONE; + } + } +} + +// return 1 if punt (ignore Z), 0 if use it +int check_linear(int n) { + extern ubyte flat8_per_ltol; + g3s_phandle temphand; + fix zmin, zmax; + fix temp; + g3s_phandle *temp_vbuf2; + + temp_vbuf2 = _vbuf2; + n--; + temphand = temp_vbuf2[n]; + zmax = zmin = temphand->gZ; + + while (--n >= 0) { + temphand = temp_vbuf2[n]; + temp = temphand->gZ; + + if (temp < zmin) + zmin = temp; + if (temp > zmax) + zmax = temp; + } + + zmax -= zmin; + temp = zmin >> flat8_per_ltol; + if (temp >= zmax) { + ti.tmap_type = GRC_BILIN + (light_flag << 1); + tmap_func = (void *)&h_umap; + return 1; // punt + } else + return 0; // use Z +} + +// compute warp matrix with deltas already set. +// arguments: +// deltas in x10,x20,y10,y20,z10,z20 +// pointer to bitmap in bm_ptr +// esi=basis 0 +void calc_warp_matrix2(g3s_phandle upperleft, fix x10, fix x20, fix y10, fix y20, fix z10, fix z20, grs_bitmap *bm) { + fix x0 = upperleft->gX; + fix y0 = upperleft->gY; + fix z0 = upperleft->gZ; + + // compute the actual matrix. + + warp[0] = fix_mul(y20, z0) - fix_mul(y0, z20); + warp[1] = fix_mul(x0, z20) - fix_mul(x20, z0); + warp[2] = fix_mul(x20, y0) - fix_mul(x0, y20); + warp[3] = fix_mul(y0, z10) - fix_mul(y10, z0); + warp[4] = fix_mul(x10, z0) - fix_mul(x0, z10); + warp[5] = fix_mul(x0, y10) - fix_mul(x10, y0); + warp[6] = fix_mul(y10, z20) - fix_mul(y20, z10); + warp[7] = fix_mul(x20, z10) - fix_mul(x10, z20); + warp[8] = fix_mul(x10, y20) - fix_mul(x20, y10); +} + +// ------------------------------------------------------------------------------------------------ +// MLA - calc_warp_matrix never called! none of this stuff used!? + +/* +getdel macro dest,ofs,src1,src2 + mov eax,[src1].ofs + sub eax,[src2].ofs + break_if o,'overflow in getdel' + mov dest,eax + endm + +divm macro dest,reg + mov eax,dest + cdq + idiv reg + mov dest,eax + endm + + + +// figure out the goofy warp matrix. +// takes three points: ebx,ecx,edx=basis 0,1,2 and bm_ptr must point to +// the address of the bitmap. +// only handles square bitmaps now. +void calc_warp_matrix(void) +{ +// get deltas +// x10 = r1->x - r0->x x20 = r2->x - r0->x +// y10 = r1->y - r0->y y20 = r2->y - r0->y +// z10 = r1->z - r0->z z20 = r2->z - r0->z + // get 10 differences. + getdel x10,x,ecx,ebx + getdel y10,y,ecx,ebx + getdel z10,z,ecx,ebx + // get 20 differences. + getdel x20,x,edx,ebx + getdel y20,y,edx,ebx + getdel z20,z,edx,ebx + mov esi,ebx // esi -> basis0 + + mov ebx,width_count + cmp ebx,1 + je skip_w_div + divm x10,ebx + divm y10,ebx + divm z10,ebx +skip_w_div: + mov ebx,height_count + cmp ebx,1 + je skip_h_div + divm x20,ebx + divm y20,ebx + divm z20,ebx +skip_h_div: + jmp calc_warp_matrix2 +} + */ diff --git a/engine/src/Libraries/3D/Source/vector.c b/engine/src/Libraries/3D/Source/vector.c new file mode 100644 index 0000000..120c205 --- /dev/null +++ b/engine/src/Libraries/3D/Source/vector.c @@ -0,0 +1,155 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// +// $Source: n:/project/lib/src/3d/RCS/vector.asm $ +// $Revision: 1.3 $ +// $Author: dc $ +// $Date: 1993/08/10 22:54:29 $ +// +// Vector math routines for 3d library +// +// $Log: vector.asm $ +// Revision 1.3 1993/08/10 22:54:29 dc +// add _3d.inc to includes +// +// Revision 1.2 1993/05/11 15:03:18 matt +// Changed g3_vec_scale() to take seperate dest & src +// Fixed bug in vector compute +// +// Revision 1.1 1993/05/04 17:39:56 matt +// Initial revision +// +// + +//#include + +#include // sqrtl() + +#include "3d.h" +#include "GlobalV.h" +#include "fix.h" +#include "lg.h" + +// prototypes +void g3_compute_normal_quick(g3s_vector *v, g3s_vector *v0, g3s_vector *v1, g3s_vector *v2); + +// adds two vectors: edi = esi + ebx +void g3_vec_add(g3s_vector *dest, g3s_vector *src1, g3s_vector *src2) { + dest->gX = src1->gX + src2->gX; + dest->gY = src1->gY + src2->gY; + dest->gZ = src1->gZ + src2->gZ; +} + +// subtracts two vectors: edi = esi - ebx. trashes eax +void g3_vec_sub(g3s_vector *dest, g3s_vector *src1, g3s_vector *src2) { + dest->gX = src1->gX - src2->gX; + dest->gY = src1->gY - src2->gY; + dest->gZ = src1->gZ - src2->gZ; +} + +// scale a vector in place. takes edi=dest vector, esi=src vector, ebx=scale +void g3_vec_scale(g3s_vector *dest, g3s_vector *src, fix s) { + dest->gX = fix_mul(src->gX, s); + dest->gY = fix_mul(src->gY, s); + dest->gZ = fix_mul(src->gZ, s); +} + +// fix mag(vector *v) +// takes esi = v. returns mag in eax. trashes all but ebp +fix g3_vec_mag(g3s_vector *v) { + int64_t result = fix64_mul(v->gX, v->gX) + fix64_mul(v->gY, v->gY) + fix64_mul(v->gZ, v->gZ); + return (fix)sqrtl(result); +} + +// compute dot product of vectors at [esi] & [edi] +fix g3_vec_dotprod(g3s_vector *v0, g3s_vector *v1) { + int64_t result = fix64_mul(v0->gX, v1->gX) + fix64_mul(v0->gY, v1->gY) + fix64_mul(v0->gZ, v1->gZ); + + return fix64_to_fix(result); +} + +// compute normalized surface normal from three points. +// takes edi=dest, eax,edx,ebx = points. fills in [edi]. +// trashes eax,ebx,ecx,edx,esi +void g3_compute_normal(g3s_vector *norm, g3s_vector *v0, g3s_vector *v1, g3s_vector *v2) { + g3_compute_normal_quick(norm, v0, v1, v2); + g3_vec_normalize(norm); // now normalize +} + +// normalizes the vector at esi. trashes all but esi,ebp +void g3_vec_normalize(g3s_vector *v) { + fix temp; + + temp = g3_vec_mag(v); + + v->gX = fix_div(v->gX, temp); + v->gY = fix_div(v->gY, temp); + v->gZ = fix_div(v->gZ, temp); +} + +// compute surface normal from three points. DOES NOT NORMALIZE! +// takes edi=dest, eax,edx,ebx = points. fills in [edi]. +// trashes eax,ebx,ecx,edx,esi +// the quick version does not normalize +void g3_compute_normal_quick(g3s_vector *v, g3s_vector *v0, g3s_vector *v1, g3s_vector *v2) { + int64_t r_temp, r[3]; + g3s_vector temp_v0; + g3s_vector temp_v1; + int32_t temp_long = 0; + int32_t shiftcount; + + g3_vec_sub(&temp_v0, v1, v0); + g3_vec_sub(&temp_v1, v2, v1); + + // dest->x = v1z * v0y - v1y * v0z; + r[0] = fix64_mul(temp_v1.gZ, temp_v0.gY) - fix64_mul(temp_v1.gY, temp_v0.gZ); + v->gX = fix64_frac(r[0]); + + // dest->y = v1x * v0z - v1z * v0x; + r[1] = fix64_mul(temp_v1.gX, temp_v0.gZ) - fix64_mul(temp_v1.gZ, temp_v0.gX); + v->gY = fix64_frac(r[1]); + + // dest->z = v1y * v0x - v1x * v0y; + r[2] = fix64_mul(temp_v1.gY, temp_v0.gX) - fix64_mul(temp_v1.gX, temp_v0.gY); + v->gZ = fix64_frac(r[2]); + + // see if fit into a longword + for(int i = 0; i < 3; i++) { + r_temp = r[i]; + if (r_temp < 0) + r_temp = -r_temp; + temp_long |= fix64_int(2 * r_temp); + } + if (!temp_long) + return; // everything fits in the low longword. hurrah. see ya. + + // see how far to shift to fit in a longword + shiftcount = 0; + while (temp_long >= 0x0100) { + shiftcount += 8; + temp_long >>= 8; + } + shiftcount += shift_table[temp_long]; + + // now get the results + for (int i = 0; i < 3; i++) { + r[i] >>= shiftcount; + v->xyz[i] = fix64_frac(r[i]); + } +} diff --git a/engine/src/Libraries/AFILE/Source/afile.c b/engine/src/Libraries/AFILE/Source/afile.c new file mode 100644 index 0000000..e22f335 --- /dev/null +++ b/engine/src/Libraries/AFILE/Source/afile.c @@ -0,0 +1,613 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// AFILE.C Read/write anim files +// Rex E. Bradford (REX) +// +/* + * $Header: r:/prj/lib/src/afile/RCS/afile.c 1.9 1994/10/18 16:00:29 rex Exp $ + * $Log: afile.c $ + * Revision 1.9 1994/10/18 16:00:29 rex + * Removed warning if can't set frame pal + * + * Revision 1.8 1994/10/04 20:30:41 rex + * Removed warning if no frame pal + * + * Revision 1.7 1994/10/03 18:06:33 rex + * Added warning if w,h of bitmap changes from frame to frame + * + * Revision 1.6 1994/09/29 10:29:42 rex + * Added time arg to read/write frame + * + * Revision 1.5 1994/09/27 17:21:42 rex + * Added qtmMethods to list of supported file types (Quicktime movies) + * + * Revision 1.4 1994/09/22 16:42:41 rex + * Took out warning when read past last frame + * + * Revision 1.3 1994/09/13 12:22:32 rex + * Added lots of spew + * + * Revision 1.2 1994/08/04 11:39:48 rex + * When reset afile, clear compose buffer + * + * Revision 1.1 1994/07/22 13:20:00 rex + * Initial revision + * + */ + +#include +#include + +#include "afile.h" +#include "compose.h" +#include "lg.h" +//#include + +//#include <_2d.h> + +static char *afExts[] = {"FLC", "FLI", "CEL", "ANM", "qtm", "mov", NULL}; +static AfileType afTypes[] = {AFILE_FLC, AFILE_FLC, AFILE_FLC, AFILE_ANM, AFILE_QTM, AFILE_MOV}; + +// extern Amethods flcMethods; +// extern Amethods anmMethods; +// extern Amethods qtmMethods; +extern Amethods movMethods; + +static Amethods *methods[] = { + NULL, // &flcMethods, // AFILE_FLC + NULL, // &anmMethods, // AFILE_ANM + NULL, // &qtmMethods, // AFILE_QTM + &movMethods, // AFILE_MOV +}; + +// Allocate enough room in case RSD goes overboard +// It doesn't look as if this is an issue any more. +#define BM_PLENTY_SIZE(szuncomp) (szuncomp+16) +// But just in case it is, keep a known value at the end of the buffer and check +// it for overruns. This is just a random uuid, it has no other significance. +static const uint8_t BM_CANARY[] = { + 0xe3, 0x58, 0x0a, 0x9c, 0xad, 0xa8, 0x4d, 0x15, + 0xb9, 0x96, 0x2a, 0x07, 0xa4, 0x7a, 0xdb, 0xdc +}; + +// ------------------------------------------------------- +// GENERAL ACCESS ROUTINES - READING +// ------------------------------------------------------- +// + +/** + * Opens an anim file and begins reading from it. + * @param paf ptr to (unused) animation file struct + * @param ptr to opened memory "file" + * @param aftype type of file (see AfileType) + * @return 0 - OK, -1 - unknown file type, -3 - bad file format + */ +int32_t AfileOpen(Afile *paf, MFILE *mf, AfileType aftype) { + AfileType type = AFILE_BAD; + uint8_t bmtype; + char *p; + + DEBUG("%s: trying to open memory \"file\"", __FUNCTION__); + + // Extract file extension, get type + for (int i = 0; i < (sizeof(afTypes)/sizeof(afTypes[0])); i++) { + if (afTypes[i] == aftype) { + type = aftype; + break; + } + } + + if (type == AFILE_BAD) { + ERROR("%s: unknown file type", __FUNCTION__); + return (-1); + } + + // Set up afile struct + memset(paf, 0, sizeof(Afile)); + paf->mf = mf; + paf->type = aftype; + paf->writing = false; + paf->pm = methods[paf->type]; + paf->currFrame = 0; + + // Call method to read header + TRACE("%s: reading header", __FUNCTION__); + + if ((*paf->pm->f_ReadHeader)(paf) < 0) { + ERROR("%s: bad header", __FUNCTION__); + return -3; + } + + // Figure bitmap type and frame length + if (paf->v.numBits == 8) { + bmtype = BMT_FLAT8; + paf->frameLen = (int32_t)paf->v.width * paf->v.height; + } else { + bmtype = BMT_FLAT24; + paf->frameLen = (int32_t)paf->v.width * paf->v.height * 3; + } + + TRACE("%s: numBits: %d w,h: %d,%d frameLen: %d", __FUNCTION__, paf->v.numBits, paf->v.width, paf->v.height, + paf->frameLen); + + // Set up work buffer, compose buffer, and prev buffer + TRACE("%s: initing work buffer of size: %d", __FUNCTION__, BM_PLENTY_SIZE(paf->frameLen)); + + uint8_t *bits = malloc(BM_PLENTY_SIZE(paf->frameLen)); + memcpy(bits + paf->frameLen, BM_CANARY, 16); + gr_init_bitmap(&paf->bmWork, bits, bmtype, 0, paf->v.width, + paf->v.height); + + TRACE("%s: initing compose buffer and prev buffer", __FUNCTION__); + + ComposeInit(&paf->bmCompose, bmtype, paf->v.width, paf->v.height); + ComposeInit(&paf->bmPrev, bmtype, paf->v.width, paf->v.height); + + // Return ok + DEBUG("%s: successful open", __FUNCTION__); + + return 0; +} + +// ------------------------------------------------------------- +// +// AfileReadFullFrame() reads the next frame in the sequence, full style. +// +// paf = ptr to animfile struct +// pbm = ptr to bitmap struct (if ptr NULL, will alloc) +// ptime = ptr to time field (if ptr NULL, no time returned) +// +// Returns: size of frame, or -1 if error + +int32_t AfileReadFullFrame(Afile *paf, grs_bitmap *pbm, fix *ptime) { + int32_t len; + fix time; + + // Hey, did we hit end? + TRACE("%s: reading frame: %d", __FUNCTION__, paf->currFrame); + + if (paf->currFrame >= paf->v.numFrames) { + return (-1); + } + + // Read bitmap from reader into working buffer + len = (*paf->pm->f_ReadFrame)(paf, &paf->bmWork, &time); + if (ptime) + *ptime = time; + if (len <= 0) { + ERROR("%s: problem reading frame", __FUNCTION__); + return (len); + } + TRACE("%s: read frame, len: %d", __FUNCTION__, len); + + // Check for overruns + if (memcmp(paf->bmWork.bits + paf->frameLen, BM_CANARY, 16) != 0) { + ERROR("%s: buffer overrun reading frame: %d", __FUNCTION__, + paf->currFrame); + return -1; + } + + // Add to compose buffer + TRACE("%s: adding to compose buffer", __FUNCTION__); + ComposeAdd(&paf->bmCompose, &paf->bmWork); + + // Make sure bitmap has memory + if (pbm->bits == NULL) { + TRACE("%s: mallocing bitmap", __FUNCTION__); + pbm->bits = (uchar *)malloc(paf->frameLen); + if (pbm->bits == NULL) { + ERROR("%s: can't find memory for bitmap", __FUNCTION__); + return -1; + } + } + + // Copy current compose buffer to caller + gr_init_bm(pbm, pbm->bits, paf->bmCompose.type, 0, paf->v.width, paf->v.height); + memcpy(pbm->bits, paf->bmCompose.bits, paf->frameLen); + + // Return length + paf->currFrame++; + return (paf->frameLen); +} + +// ------------------------------------------------------------- +// +// AfileReadDiffFrame() reads the next frame in the sequence, diff style. +// +// paf = ptr to animfile struct +// pbm = ptr to bitmap struct (if ptr NULL, will alloc) +// ptime = ptr to time field (if ptr NULL, no time returned) +// +// Returns: size of frame, or -1 if error + +int32_t AfileReadDiffFrame(Afile *paf, grs_bitmap *pbm, fix *ptime) { + int32_t len; + fix time; + + // Hey, did we hit end? + TRACE("%s: reading frame: %d", __FUNCTION__, paf->currFrame); + + if (paf->currFrame >= paf->v.numFrames) { + return (-1); + } + + // Read bitmap from reader into working buffer + len = (*paf->pm->f_ReadFrame)(paf, &paf->bmWork, &time); + if (ptime) + *ptime = time; + if (len <= 0) { + WARN("%s: problem reading frame", __FUNCTION__); + return (len); + } + TRACE("%s: read frame, len: %d", __FUNCTION__, len); + + // Check for overruns + if (memcmp(paf->bmWork.bits + paf->frameLen, BM_CANARY, 16) != 0) { + ERROR("%s: buffer overrun reading frame: %d", __FUNCTION__, + paf->currFrame); + return -1; + } + + // Move compose buffer to previous + if (paf->currFrame > 0) + memcpy(paf->bmPrev.bits, paf->bmCompose.bits, paf->frameLen); + + // Add to compose buffer + ComposeAdd(&paf->bmCompose, &paf->bmWork); + + // Make sure bitmap has memory, init it + + if (pbm->bits == NULL) { + TRACE("%s: mallocing bitmap", __FUNCTION__); + pbm->bits = malloc(paf->frameLen); + if (pbm->bits == NULL) { + WARN("AfileReadDiffFrame: can't find memory for bitmap"); + return (0); + } + } + + // Extract difference into bitmap + TRACE("%s: finding diff with compose buff", __FUNCTION__); + len = ComposeDiff(&paf->bmPrev, &paf->bmCompose, pbm); + + // Return length + + paf->currFrame++; + return (len); +} + +// -------------------------------------------------------------- +// +// AfileGetFramePal() gets a (partial) palette associated with this +// frame only. Call AFTER AfileReadFrame(). +// +// paf = ptr to animfile struct +// ppal = ptr to palette struct +// +// Returns: TRUE if palette for this frame, FALSE if none + +bool AfileGetFramePal(Afile *paf, Apalette *ppal) { + TRACE("AfileGetFramePal: getting pal", __FUNCTION__); + + if (paf->pm->f_ReadFramePal == NULL) + return false; + + (*paf->pm->f_ReadFramePal)(paf, ppal); + return (ppal->numcols != 0); +} + +// -------------------------------------------------------------- +// +// AfileGetAudio() gets giant block of audio for entire animation. +// Use AudioFileLength() for sizing when allocate buffer. +// +// paf = ptr to animfile struct +// paudio = ptr to audio buffer +// +// Returns: 0 if ok, -1 if error + +int AfileGetAudio(Afile *paf, void *paudio) { + TRACE("%s: getting audio", __FUNCTION__); + + if (paf->pm->f_ReadAudio == NULL) { + ERROR("%s: anim file format doesn't support audio", __FUNCTION__); + return (-1); + } + + return ((*paf->pm->f_ReadAudio)(paf, paudio)); +} + +// -------------------------------------------------------------- +// +// AfileReadReset() resets to frame 0. + +int AfileReadReset(Afile *paf) { + TRACE("%s: resetting", __FUNCTION__); + + paf->currFrame = 0; + memset(paf->bmCompose.bits, 0, paf->bmCompose.row * paf->bmCompose.h); + memset(paf->bmPrev.bits, 0, paf->bmPrev.row * paf->bmPrev.h); + return ((*paf->pm->f_ReadReset)(paf)); +} + +// -------------------------------------------------------------- +// +// AfileFree() closes animfile. +// +// paf = ptr to animfile struct + +void AfileFree(Afile *paf) { + // Close properly based on whether writing or reading + + DEBUG("%s: freeing memory", __FUNCTION__); + + if (paf->writing) { +/* + paf->v.numFrames = paf->currFrame; + (*paf->pm->f_WriteClose)(paf); +*/ + } else + (*paf->pm->f_ReadClose)(paf); + + // Free up buffers + if (paf->bmCompose.bits) + free(paf->bmCompose.bits); + if (paf->bmWork.bits) + free(paf->bmWork.bits); + if (paf->bmPrev.bits) + free(paf->bmPrev.bits); +} + +// -------------------------------------------------------------- +// INFORMATIONAL AND HELPER ROUTINES +// -------------------------------------------------------------- +// +// AfileLookupType() looks up anim file type given extension. + +AfileType AfileLookupType(char *ext) { + int itype = 0; + while (afExts[itype]) { + if (strcmp(ext, afExts[itype]) == 0) + return (afTypes[itype]); + ++itype; + } + return (AFILE_BAD); +} + +// -------------------------------------------------------------- +// +// AfileBitmapLength() returns amount of space needed to read bitmaps. + +int32_t AfileBitmapLength(Afile *paf) { return (paf->frameLen); } + +// ------------------------------------------------------------- +// +// AfileAudioLength() computes the length of the buffer needed +// to store audio data. Hope it all fits into ram! +// Please note, you need multiply return value with 8192! +int32_t AfileAudioLength(Afile *paf) { + if (paf->a.numChans == 0) + return (0); + else + return (paf->a.numChans * paf->a.sampleSize * paf->a.numSamples); +} + +/* + +// ------------------------------------------------------------- +// GENERAL ACCESS ROUTINES - WRITING +// ------------------------------------------------------------- +// +// AfileCreate() creates a new anim file. +// +// paf = ptr to (unused) animation file struct +// filename = ptr to filename +// frameRate = video frame rate +// +// Returns: +// 0 = ok +// -1 = bad extension +// -2 = no writer for this type +// -3 = can't open file + +int AfileCreate(Afile *paf, char *filename, fix frameRate) { + AfileType aftype; + FILE *fp; + char *p; + + // Extract file extension, get type + DEBUG("%s: creating %s", __FUNCTION__, filename); + + aftype = AFILE_BAD; + p = strchr(filename, '.'); + if (p) { + p++; + *(p + 3) = 0; + aftype = AfileLookupType(p); + } + if (aftype == AFILE_BAD) { + ERROR("%s: unknown extension", __FUNCTION__); + return (-1); + } + + // Check if writer + if (methods[aftype]->f_WriteBegin == NULL) { + ERROR("%s: anim file format doesn't support writing", __FUNCTION__); + return (-2); + } + + // Open file + fp = fopen(filename, "wb"); + if (fp == NULL) { + ERROR("%s: can't open file", __FUNCTION__); + return (-3); + } + + // If opened successfully, set up afile struct + memset(paf, 0, sizeof(Afile)); + paf->fp = fp; + paf->type = aftype; + paf->writing = true; + paf->pm = methods[paf->type]; + paf->v.frameRate = frameRate; + + // Call method to begin writing + TRACE("%s: initializing anim file", __FUNCTION__); + + (*paf->pm->f_WriteBegin)(paf); + + // Return ok + DEBUG("%s: successful create", __FUNCTION__); + + return 0; +} + +// ----------------------------------------------------------- +// +// AfilePutAudio() hands off audio buffer to writer. +// Call this before writing any frames. + +int AfilePutAudio(Afile *paf, AaudioInfo *pai, void *paudio) { + // Can we do audio? + TRACE("%s: putting audio", __FUNCTION__); + + if (*paf->pm->f_WriteAudio == NULL) { + ERROR("%s: anim file doesn't support writing audio", __FUNCTION__); + return (-1); + } + + // Set audio section of animfile struct + paf->a = *pai; + + // Hand off to function + return ((*paf->pm->f_WriteAudio)(paf, paudio)); +} + +// ------------------------------------------------------------ +// +// AfileWriteFrame() writes frame out to writer. + +int AfileWriteFrame(Afile *paf, grs_bitmap *pbm, fix time) { + int32_t bmtype; + int32_t len; + int32_t ret; + + TRACE("%s: writing frame: %d", __FUNCTION__, paf->currFrame); + + // If 1st frame, init some vars. + // Set up work buffer, compose buffer, and prev buffer + if (paf->currFrame == 0) { + paf->v.width = pbm->w; + paf->v.height = pbm->h; + if ((pbm->type == BMT_FLAT8) || (pbm->type == BMT_RSD8)) { + paf->v.numBits = 8; + bmtype = BMT_FLAT8; + paf->frameLen = (int32_t)paf->v.width * paf->v.height; + } else { + paf->v.numBits = 24; + bmtype = BMT_FLAT24; + paf->frameLen = (int32_t)paf->v.width * paf->v.height * 3; + } + TRACE("%s: numBits: %d w,h: %d,%d frameLen: %d", __FUNCTION__, paf->v.numBits, paf->v.width, paf->v.height, + paf->frameLen); + + TRACE("%s: initing work buffer of size: %d", BM_PLENTY_SIZE(paf->frameLen)); + + gr_init_bitmap(&paf->bmWork, (uchar *)malloc(BM_PLENTY_SIZE(paf->frameLen)), bmtype, 0, paf->v.width, + paf->v.height); + + TRACE("%s: initing compose buffers", __FUNCTION__); + + ComposeInit(&paf->bmCompose, bmtype, paf->v.width, paf->v.height); + ComposeInit(&paf->bmPrev, bmtype, paf->v.width, paf->v.height); + } + // Else for other frames, copy current to previous + else { + if ((pbm->w != paf->v.width) || (pbm->h != paf->v.height)) { + WARN("%s: new w,h: %d,%d (was: %d,%d)", __FUNCTION__, pbm->w, pbm->h, paf->v.width, paf->v.height); + } + memcpy(paf->bmPrev.bits, paf->bmCompose.bits, paf->frameLen); + if (time == 0) + time = paf->currFrame * fix_div(FIX_UNIT, paf->v.frameRate); + } + + // Now put current frame into compose buffer + TRACE("%s: adding to compose buff", __FUNCTION__); + + ComposeAdd(&paf->bmCompose, pbm); + + // If writer wants rsd, give rsd-encoded frame (diff if past frame 0) + if (paf->writerWantsRsd) { + TRACE("%s: converting to rsd", __FUNCTION__); + + if (paf->currFrame == 0) { + if (paf->bmCompose.type == BMT_FLAT8) + paf->bmWork.type = BMT_RSD8; + else + // paf->bmWork.type = BMT_RSD24; // no rsd24 support yet + paf->bmWork.type = BMT_FLAT24; + len = ComposeConvert(&paf->bmCompose, &paf->bmWork); + } else { + // The next 3 lines should be unnecessary, but rsd24 broken + if (paf->bmCompose.type == BMT_FLAT24) + len = ComposeConvert(&paf->bmCompose, &paf->bmWork); + else + len = ComposeDiff(&paf->bmPrev, &paf->bmCompose, &paf->bmWork); + } + TRACE("%s: writing, rsd len = %d", __FUNCTION__, len); + + ret = (*paf->pm->f_WriteFrame)(paf, &paf->bmWork, len, time); + } + // Else just write flat frame + else { + TRACE("%s: writing, len = %d", __FUNCTION__, paf->frameLen); + ret = (*paf->pm->f_WriteFrame)(paf, &paf->bmCompose, paf->frameLen, time); + } + + // Bump frame counter and return + + if (ret >= 0) + paf->currFrame++; + return (ret); +} + +// ------------------------------------------------------------- +// +// AfileSetPal() sets overall anim palette. +// Call this before writing any frames. + +void AfileSetPal(Afile *paf, Apalette *ppal) { + TRACE("%s: setting palette", __FUNCTION__); + + memcpy(&paf->v.pal, ppal, sizeof(Apalette)); +} + +// ------------------------------------------------------------- +// +// AfileSetFramePal() sets palette for upcoming frame. + +int AfileSetFramePal(Afile *paf, Apalette *ppal) { + TRACE("%s: setting frame pal", __FUNCTION__); + + if (paf->pm->f_WriteFramePal == NULL) + return (-1); + return ((*paf->pm->f_WriteFramePal)(paf, ppal)); +} + +*/ diff --git a/engine/src/Libraries/AFILE/Source/afile.h b/engine/src/Libraries/AFILE/Source/afile.h new file mode 100644 index 0000000..98c35e0 --- /dev/null +++ b/engine/src/Libraries/AFILE/Source/afile.h @@ -0,0 +1,151 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// AFILE.H Generic animation file access +// Rex E. Bradford (REX) +/* + * $Header: r:/prj/lib/src/afile/RCS/afile.h 1.2 1994/09/29 10:29:58 rex Exp $ + * $Log: afile.h $ + * Revision 1.2 1994/09/29 10:29:58 rex + * Added time arg to read/write frame + * + * Revision 1.1 1994/07/22 13:20:59 rex + * Initial revision + * + */ + +#ifndef __AFILE_H +#define __AFILE_H + +#ifndef STDIO_H +#include +#endif +#ifndef __2D_H +#include "2d.h" +#endif +//#ifndef DATAPATH_H +//#include +//#endif +#ifndef __FIX_H +#include "fix.h" +#endif + +// Animation file types + +typedef enum { + AFILE_FLC, // .flc, .fli, .cel (Autodesk Animator Pro) + AFILE_ANM, // .anm (DeluxePaint Anim) + AFILE_QTM, // .qtm (QuickTime, brought over from Mac) + AFILE_MOV, // .mov (LookingGlass movie) + AFILE_GFILE, // set of gfiles (.pcx, .gif, etc.) + AFILE_BAD // unknown/bad type +} AfileType; + +// Structures used in dealing with anim files + +typedef struct { + int16_t index; // index at which to start writing + int16_t numcols; // number of colors to write + uint8_t rgb[3 * 256]; // 0-256 rgb entries +} Apalette; + +typedef struct { + int32_t numFrames; // number of frames of video + fix frameRate; // frame rate in frames per second + int16_t width; // width in pixels + int16_t height; // height in pixels + int16_t numBits; // number of bits per pixel (8, 15, 24) + Apalette pal; // (base) palette +} AvideoInfo; + +typedef struct { + int16_t numChans; // 0 = no audio, 1 = mono, 2 = stereo + int16_t sampleSize; // 1 = 8-bit, 2 = 16-bit + fix sampleRate; // in Khz + int32_t numSamples; // total number of samples (per channel) +} AaudioInfo; + +struct Amethods_; + +typedef struct MFILE { + unsigned char *p; + int size; + int pos; +} MFILE; + +typedef struct Afile_ { + MFILE *mf; // ptr to memory access "file" + AfileType type; // file type + uint8_t writing; // if TRUE, opened for write (FALSE = read) + uint8_t writerWantsRsd; // if TRUE, writer wants rsd frames + struct Amethods_ *pm; // ptr to access method ptrs + AvideoInfo v; // video info + AaudioInfo a; // audio info + void *pspec; // ptr to type-specific info + int32_t currFrame; // current frame index + int32_t frameLen; // length of frame (width & height * sizeof(pixel)) + grs_bitmap bmCompose; // compose buffer + grs_bitmap bmPrev; // previous compose buffer + grs_bitmap bmWork; // working buffer bitmap +} Afile; + +typedef struct Amethods_ { + int32_t (*f_ReadHeader)(Afile *paf); + int32_t (*f_ReadFrame)(Afile *paf, grs_bitmap *pbm, fix *ptime); + int32_t (*f_ReadFramePal)(Afile *paf, Apalette *ppal); + int32_t (*f_ReadAudio)(Afile *paf, void *paudio); + int32_t (*f_ReadReset)(Afile *paf); + int32_t (*f_ReadClose)(Afile *paf); +/* + int32_t (*f_WriteBegin)(Afile *paf); + int32_t (*f_WriteAudio)(Afile *paf, void *paudio); + int32_t (*f_WriteFrame)(Afile *paf, grs_bitmap *pbm, int32_t bmlength, fix time); + int32_t (*f_WriteFramePal)(Afile *paf, Apalette *ppal); + int32_t (*f_WriteClose)(Afile *paf); +*/ +} Amethods; + +// Function prototypes: reading anim files + +int32_t AfileOpen(Afile *paf, MFILE *mf, AfileType aftype); +int32_t AfileReadFullFrame(Afile *paf, grs_bitmap *pbm, fix *ptime); +int32_t AfileReadDiffFrame(Afile *paf, grs_bitmap *pbm, fix *ptime); +bool AfileGetFramePal(Afile *paf, Apalette *ppal); +int32_t AfileGetAudio(Afile *paf, void *paudio); +int32_t AfileReadReset(Afile *paf); +void AfileFree(Afile *paf); // write also + +/* + +// Function prototypes: writing anim files + +int32_t AfileCreate(Afile *paf, char *filename, fix frameRate); +int32_t AfilePutAudio(Afile *paf, AaudioInfo *pai, void *paudio); +int32_t AfileWriteFrame(Afile *paf, grs_bitmap *pbm, fix time); +void AfileSetPal(Afile *paf, Apalette *ppal); +int32_t AfileSetFramePal(Afile *paf, Apalette *ppal); + +*/ + +// Function prototypes: information & miscellaneous + +AfileType AfileLookupType(char *ext); +int32_t AfileBitmapLength(Afile *paf); +int32_t AfileAudioLength(Afile *paf); + +#endif diff --git a/engine/src/Libraries/AFILE/Source/amov.c b/engine/src/Libraries/AFILE/Source/amov.c new file mode 100644 index 0000000..ab3f48d --- /dev/null +++ b/engine/src/Libraries/AFILE/Source/amov.c @@ -0,0 +1,561 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// AMOV.C Animfile handler for LG .mov files +// Rex E. Bradford (REX) +// +/* + * $Header: r:/prj/lib/src/afile/RCS/amov.c 1.6 1994/10/18 16:01:15 rex Exp $ + * $Log: amov.c $ + * Revision 1.6 1994/10/18 16:01:15 rex + * Added processing of PALETTE chunk, so can return frame pals + * + * Revision 1.5 1994/10/04 10:34:50 rex + * Fixed so can handle movies > 128 frames + * + * Revision 1.4 1994/10/03 18:04:33 rex + * Added ability to read 4x4-compressed movies + * + * Revision 1.3 1994/09/29 10:31:00 rex + * Added time arg to read/write frame + * + * Revision 1.2 1994/09/01 11:06:34 rex + * Changed flag name + * + * Revision 1.1 1994/07/22 13:19:51 rex + * Initial revision + * + */ + +#include +#include + +#include "lg.h" +#include "afile.h" +#include "movie.h" +#include "rect.h" +#include "draw4x4.h" +#include "huff.h" + +// Type-specific information + +typedef struct { + MovieHeader movieHdr; // movie header + MovieChunk *pmc; // ptr to movie chunk array + MovieChunk *pcurrChunk; // current chunk ptr + FILE *fpTemp; // temp file for writing + uint8_t pal[768]; // space for palette + uint8_t newPal; // new pal flag +} AmovInfo; + +// Methods + +int32_t AmovReadHeader(Afile *paf); +int32_t AmovReadFrame(Afile *paf, grs_bitmap *pbm, fix *ptime); +int32_t AmovReadFramePal(Afile *paf, Apalette *ppal); +int32_t AmovReadAudio(Afile *paf, void *paudio); +int32_t AmovReadReset(Afile *paf); +int32_t AmovReadClose(Afile *paf); + +int32_t AmovWriteBegin(Afile *paf); +int32_t AmovWriteFrame(Afile *paf, grs_bitmap *pbm, int32_t bmlength, fix time); +int32_t AmovWriteClose(Afile *paf); + +Amethods movMethods = { + AmovReadHeader, // Read header + AmovReadFrame, // Read frame + AmovReadFramePal, // Read frame palette + AmovReadAudio, // f_ReadAudio + AmovReadReset, + AmovReadClose, +/* + AmovWriteBegin, + NULL, // f_WriteAudio + AmovWriteFrame, + NULL, // f_WriteFramePal + AmovWriteClose, +*/ +}; + +#define MAX_MOV_FRAMES 4096 + +#define MOV_TEMP_FILENAME "__movie_.tmp" + +//assumes count is 1 +int mfread(void *p, int size, MFILE *mf) +{ + if (mf->pos < 0 || size <= 0) return 0; + if (mf->pos + size > mf->size) size = mf->size - mf->pos; + if (size <= 0) return 0; + + memcpy(p, mf->p + mf->pos, size); + mf->pos += size; + + return size; +} + +//assumes origin is SEEK_SET +void mfseek(MFILE *mf, int pos) +{ + mf->pos = pos; +} + +// ------------------------------------------------------ +// READER METHODS +// ------------------------------------------------------ +// +// AmovReadHeader() reads in movie file header & verifies. + +int32_t AmovReadHeader(Afile *paf) { + AmovInfo *pmi; + MovieChunk *pchunk; + + // Allocate type-specific info + + paf->pspec = malloc(sizeof(AmovInfo)); + pmi = (AmovInfo *)paf->pspec; + + // Read in movie header + mfread(&pmi->movieHdr, sizeof(pmi->movieHdr), paf->mf); + if (pmi->movieHdr.magicId != MOVI_MAGIC_ID) { + free(paf->pspec); + return (-1); + } + + // Record header information + paf->v.frameRate = pmi->movieHdr.frameRate; + paf->v.width = pmi->movieHdr.frameWidth; + paf->v.height = pmi->movieHdr.frameHeight; + paf->v.numBits = pmi->movieHdr.gfxNumBits; + if (pmi->movieHdr.isPalette) { + paf->v.pal.index = 0; + paf->v.pal.numcols = 256; + memcpy(paf->v.pal.rgb, pmi->movieHdr.palette, 256 * 3); + } + + // Audio information + paf->a.numChans = pmi->movieHdr.audioNumChans; + paf->a.sampleRate = pmi->movieHdr.audioSampleRate; + paf->a.sampleSize = pmi->movieHdr.audioSampleSize; + + // Read in chunk offsets + pmi->pmc = (MovieChunk *)malloc(pmi->movieHdr.sizeChunks); + mfread(pmi->pmc, pmi->movieHdr.sizeChunks, paf->mf); + + // Compute # frames + paf->v.numFrames = 0; + for (pchunk = pmi->pmc; pchunk->chunkType != MOVIE_CHUNK_END; pchunk++) { + if (pchunk->chunkType == MOVIE_CHUNK_VIDEO) + paf->v.numFrames++; + if (pchunk->chunkType == MOVIE_CHUNK_AUDIO) + paf->a.numSamples++; + } + + // No new palette + pmi->newPal = false; + + // Current chunk is first one + pmi->pcurrChunk = pmi->pmc; + + DEBUG("PMI: %x", pmi); + + // Return + + return (0); +} + +// ---------------------------------------------------------- +// +// AmovReadFrame() reads the next frame. + +//filled when chunk contains subtitle data; used extern in cutsloop.c +char EngSubtitle[256]; +char FrnSubtitle[256]; +char GerSubtitle[256]; + +int32_t AmovReadFrame(Afile *paf, grs_bitmap *pbm, fix *ptime) { + static uint8_t *pColorSet; // ptr to color set table (4x4 codec) + static uint8_t *pHuffTabComp; // ptr to compressed huffman tab (4x4 codec) + static uint8_t *pHuffTab; // ptr to expanded huffman table (4x4 codec) + + AmovInfo *pmi; + int32_t len; + uint8_t *p; + grs_canvas cv; + + pmi = (AmovInfo *)paf->pspec; + +NEXT_CHUNK: + + switch (pmi->pcurrChunk->chunkType) { + case MOVIE_CHUNK_END: + return (-1); + + case MOVIE_CHUNK_VIDEO: + DEBUG("MOVIE_CHUNK_VIDEO"); + pbm->type = pmi->pcurrChunk->flags & MOVIE_FVIDEO_BMTMASK; + if (pbm->type == MOVIE_FVIDEO_BMF_4X4) { + mfseek(paf->mf, pmi->pcurrChunk->offset); + len = MovieChunkLength(pmi->pcurrChunk); + p = (uint8_t *)malloc(len); + mfread(p, len, paf->mf); + pbm->type = BMT_FLAT8; + // Hi-res movie frames should not be transparent: the 4x4 codec + // makes its own arrangements for transparency, and at least 1 + // iframe in the intro movie is transparent when it should not be. + pbm->flags = 0; + gr_make_canvas(pbm, &cv); + gr_push_canvas(&cv); + Draw4x4(p, paf->v.width, paf->v.height); + gr_pop_canvas(); + free(p); + } else { + mfseek(paf->mf, pmi->pcurrChunk->offset + sizeof(LGRect)); + len = MovieChunkLength(pmi->pcurrChunk) - sizeof(LGRect); + mfread(pbm->bits, len, paf->mf); + } + *ptime = pmi->pcurrChunk->time; + pmi->pcurrChunk++; + return (len); + + case MOVIE_CHUNK_TABLE: + DEBUG("MOVIE_CHUNK_TABLE"); + mfseek(paf->mf, pmi->pcurrChunk->offset); + switch (pmi->pcurrChunk->flags) { + case MOVIE_FTABLE_COLORSET: + if (pColorSet) + free(pColorSet); + pColorSet = (uint8_t *)malloc(MovieChunkLength(pmi->pcurrChunk)); + mfread(pColorSet, MovieChunkLength(pmi->pcurrChunk), paf->mf); + break; + + case MOVIE_FTABLE_HUFFTAB: { + uint32_t len, *pl; + pHuffTabComp = (uint8_t *)malloc(MovieChunkLength(pmi->pcurrChunk)); + mfread(pHuffTabComp, MovieChunkLength(pmi->pcurrChunk), paf->mf); + pl = (uint32_t *)pHuffTabComp; + len = *pl++; + if (pHuffTab) + free(pHuffTab); + pHuffTab = (uint8_t *)malloc(len); + HuffExpandFlashTables(pHuffTab, len, pl, 3); + Draw4x4Reset(pColorSet, pHuffTab); + free(pHuffTabComp); + } break; + } + pmi->pcurrChunk++; + goto NEXT_CHUNK; + + case MOVIE_CHUNK_PALETTE: + DEBUG("MOVIE_CHUNK_PALETTE"); + if (pmi->pcurrChunk->flags == MOVIE_FPAL_SET) { + mfseek(paf->mf, pmi->pcurrChunk->offset); + mfread(pmi->pal, 768, paf->mf); + pmi->newPal = TRUE; + } + else if (pmi->pcurrChunk->flags & MOVIE_FPAL_CLEAR) { + // Clear the bitmap data prior to decoding an iframe. Setting to 0 + // isn't ideal here since it's the transparency colour, but we don't + // have a better candidate without searching the palette (which is + // probably in the NEXT chunk); we don't treat hi-res movie frames + // as transparent (for precisely this reason); and palette entry 0 + // is set to black in practice. + memset(pbm->bits, 0, paf->frameLen); + } + + *EngSubtitle = 0; + *FrnSubtitle = 0; + *GerSubtitle = 0; + + pmi->pcurrChunk++; + goto NEXT_CHUNK; + + case MOVIE_CHUNK_TEXT: + { + DEBUG("MOVIE_CHUNK_TEXT"); + mfseek(paf->mf, pmi->pcurrChunk->offset); + + char tag[4]; + uint32_t offset; + char string[256], ch; + int i; + + mfread(tag, sizeof(tag), paf->mf); + mfread(&offset, sizeof(offset), paf->mf); + mfseek(paf->mf, pmi->pcurrChunk->offset + offset); + + for (i=0; imf); + if (ch == 0) break; + string[i] = ch; + } + string[i] = 0; + + if (!memcmp(tag, "AREA", 4)) {} // "# # # # CLR" : left, top, right, bottom + else if (!memcmp(tag, "STD ", 4)) strcpy(EngSubtitle, string); + else if (!memcmp(tag, "FRN ", 4)) strcpy(FrnSubtitle, string); + else if (!memcmp(tag, "GER ", 4)) strcpy(GerSubtitle, string); + + pmi->pcurrChunk++; + goto NEXT_CHUNK; + } + + default: + pmi->pcurrChunk++; + goto NEXT_CHUNK; + } +} + +// ---------------------------------------------------------- +// +// AmovReadFramePal() reads pal for this frame just read. + +int32_t AmovReadFramePal(Afile *paf, Apalette *ppal) { + AmovInfo *pmi = (AmovInfo *)paf->pspec; + + ppal->index = 0; + ppal->numcols = 0; + + if (pmi->newPal) { + ppal->numcols = 256; + memcpy(ppal->rgb, pmi->pal, 768); + pmi->newPal = false; + return false; + } + + return false; +} + +// Read audio data to buffer +int32_t AmovReadAudio(Afile *paf, void *paudio) { + + AmovInfo *pmi; + uint32_t i = 0, size; + void *p = (uint8_t *)malloc(MOVIE_DEFAULT_BLOCKLEN); + + pmi = (AmovInfo *)paf->pspec; + while (pmi->pcurrChunk->chunkType != MOVIE_CHUNK_END) { + // Got audio chunk + if (pmi->pcurrChunk->chunkType == MOVIE_CHUNK_AUDIO) { + // TRACE("%s: got audio chunk in 0x%08x offset", __FUNCTION__, pmi->pcurrChunk->offset); + mfseek(paf->mf, pmi->pcurrChunk->offset); + size = mfread(p, MOVIE_DEFAULT_BLOCKLEN, paf->mf); + memcpy(((uint8_t *)paudio) + i, p, size); + if (size < MOVIE_DEFAULT_BLOCKLEN) + memset(((uint8_t *)paudio) + i + size, 128, + MOVIE_DEFAULT_BLOCKLEN - size); // fill rest with silence (128) + i += size; + } + pmi->pcurrChunk++; + } + free(p); + + //prevent pop at end of audio playback + float vol = 1.0; + size = i; + i -= 512; if (i >= size) i = 0; + for (; i < size; i++, vol *= 0.8) + *((uint8_t *)paudio + i) = 128 + (uint8_t)((*((uint8_t *)paudio + i) - 128) * vol); + + return 0; +} + +// ---------------------------------------------------------- +// +// AmovReadReset() resets the movie for reading. + +int32_t AmovReadReset(Afile *paf) { + AmovInfo *pmi = (AmovInfo *)paf->pspec; + + pmi->pcurrChunk = pmi->pmc; + + return (0); +} + +// ---------------------------------------------------------- +// +// AmovReadClose() does cleanup and closes file. + +int32_t AmovReadClose(Afile *paf) { + AmovInfo *pmi = (AmovInfo *)paf->pspec; + + free(pmi->pmc); + free(pmi); + + free(paf->mf->p); + free(paf->mf); + + return (0); +} + +/* + +// ------------------------------------------------------ +// WRITER METHODS +// ------------------------------------------------------ +// +// AmovWriteBegin() starts up writer. + +int32_t AmovWriteBegin(Afile *paf) { + AmovInfo *pmi; + + // Allocate type-specific info + + paf->pspec = calloc(1, sizeof(AmovInfo)); + pmi = paf->pspec; + pmi->pmc = calloc(MAX_MOV_FRAMES, sizeof(MovieChunk)); + + // Current chunk is first one + pmi->pcurrChunk = pmi->pmc; + + // We want rsd! + paf->writerWantsRsd = true; + + // Open temp file + pmi->fpTemp = fopen(MOV_TEMP_FILENAME, "wb"); + if (pmi->fpTemp == NULL) { + Warning(("AmovWriteBegin: can't open temp file\n")); + return (-1); + } + + // Return + return (0); +} + +// ------------------------------------------------------ +// +// AmovWriteFrame() writes out next frame. + +int32_t AmovWriteFrame(Afile *paf, grs_bitmap *pbm, int32_t bmlength, fix time) { + AmovInfo *pmi; + LGRect area; + + pmi = paf->pspec; + + // Error-check + + if (paf->currFrame >= MAX_MOV_FRAMES) { + WARN("%s: exceeded max # frames", __FUNCTION__); + return (-1); + } + + // Set current chunk + pmi->pcurrChunk->time = time; + pmi->pcurrChunk->chunkType = MOVIE_CHUNK_VIDEO; + pmi->pcurrChunk->flags = pbm->type; + pmi->pcurrChunk->offset = ftell(pmi->fpTemp); + + // Write update area + area.ul.x = 0; + area.ul.y = 0; + area.lr.x = pbm->w; + area.lr.y = pbm->h; + fwrite(&area, sizeof(area), 1, pmi->fpTemp); + + // Write bitmap + fwrite(pbm->bits, bmlength, 1, pmi->fpTemp); + + // Update stuff + pmi->pcurrChunk++; + return (0); +} + +// ------------------------------------------------------- +// +// AmovWriteClose() closes output .mov + +int32_t AmovWriteClose(Afile *paf) { + AmovInfo *pmi; + int32_t nc, numBlocks, numExtra; + int32_t i; + MovieChunk *pmc; + uint8_t buff[2048]; + + pmi = paf->pspec; + + // Set end chunk + nc = pmi->pcurrChunk - pmi->pmc; + if (nc == 0) + pmi->pcurrChunk->time = 0; + else if (nc == 1) + pmi->pcurrChunk->time = (pmi->pcurrChunk - 1)->time * 2; + else + pmi->pcurrChunk->time = + (pmi->pcurrChunk - 1)->time + ((pmi->pcurrChunk - 1)->time - (pmi->pcurrChunk - 2)->time); + pmi->pcurrChunk->chunkType = MOVIE_CHUNK_END; + pmi->pcurrChunk->flags = 0; + pmi->pcurrChunk->offset = ftell(pmi->fpTemp); + pmi->pcurrChunk++; + + // Set movie header and write out + pmi->movieHdr.magicId = MOVI_MAGIC_ID; + pmi->movieHdr.numChunks = pmi->pcurrChunk - pmi->pmc; + pmi->movieHdr.sizeChunks = ((pmi->movieHdr.numChunks * sizeof(MovieChunk)) + 1023) & 0xFFFFFC00L; + if ((pmi->movieHdr.sizeChunks & 0x400) == 0) + pmi->movieHdr.sizeChunks += 0x0400; // 1K, 3K, 5K, etc. + pmi->movieHdr.sizeData = ftell(pmi->fpTemp); + pmi->movieHdr.totalTime = (pmi->pcurrChunk - 1)->time; + pmi->movieHdr.frameRate = paf->v.frameRate; + pmi->movieHdr.frameWidth = paf->v.width; + pmi->movieHdr.frameHeight = paf->v.height; + pmi->movieHdr.gfxNumBits = paf->v.numBits; + pmi->movieHdr.isPalette = paf->v.pal.numcols != 0; + + // Skip audio for now + + if (pmi->movieHdr.isPalette) + memcpy(&pmi->movieHdr.palette, &paf->v.pal.rgb[0], 768); + + // Adjust offsets + for (pmc = pmi->pmc;; pmc++) { + pmc->offset += (sizeof(MovieHeader) + pmi->movieHdr.sizeChunks); + if (pmc->chunkType == MOVIE_CHUNK_END) + break; + } + + // Now close temp file, reopen, and copy to real file + fclose(pmi->fpTemp); + pmi->fpTemp = fopen(MOV_TEMP_FILENAME, "rb"); + if (pmi->fpTemp == NULL) + WARN("%s: can't reopen temp file", __FUNCTION__); + else { + fwrite(&pmi->movieHdr, sizeof(MovieHeader), 1, paf->fp); + fwrite(pmi->pmc, pmi->movieHdr.sizeChunks, 1, paf->fp); + numBlocks = pmi->movieHdr.sizeData / sizeof(buff); + numExtra = pmi->movieHdr.sizeData % sizeof(buff); + for (i = 0; i < numBlocks; i++) { + fread(buff, sizeof(buff), 1, pmi->fpTemp); + fwrite(buff, sizeof(buff), 1, paf->fp); + } + if (numExtra) { + fread(buff, numExtra, 1, pmi->fpTemp); + fwrite(buff, numExtra, 1, paf->fp); + } + } + + // Free up stuff + free(pmi->pmc); + fclose(pmi->fpTemp); + free(pmi); + fclose(paf->fp); + unlink(MOV_TEMP_FILENAME); + return (0); +} + +*/ diff --git a/engine/src/Libraries/AFILE/Source/circbuff.h b/engine/src/Libraries/AFILE/Source/circbuff.h new file mode 100644 index 0000000..e993926 --- /dev/null +++ b/engine/src/Libraries/AFILE/Source/circbuff.h @@ -0,0 +1,58 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// CIRCBUFF.H Circular buffer +// Rex E. Bradford + +/* + * $Source: r:/prj/lib/src/afile/RCS/circbuff.h $ + * $Revision: 1.1 $ + * $Author: rex $ + * $Date: 1994/07/22 13:21:07 $ + * $Log: circbuff.h $ + * Revision 1.1 1994/07/22 13:21:07 rex + * Initial revision + * + */ + +#ifndef __CIRCBUFF_H +#define __CIRCBUFF_H + +#ifndef __TYPES_H +#include "lg_types.h" +#endif + +typedef struct { + uint8_t *buff; // ptr to circular buffer + uint8_t *buffEnd; // end of buffer + uint8_t *pput; // ptr to put data to + uint8_t *pget; // ptr to get data from +} CircBuff; + +void CircBuffInit(CircBuff *pcb, uint8_t *buff, int32_t length); +void CircBuffReset(CircBuff *pcb); +uint32_t CircBuffRoom(CircBuff *pcb); +uint32_t CircBuffUsed(CircBuff *pcb); +void CircBuffAdvancePut(CircBuff *pcb, int32_t amt); +void CircBuffAdvanceGet(CircBuff *pcb, int32_t amt); +uint8_t CircBuffBetween(uint8_t *ptest, uint8_t *pbeg, uint8_t *pend); + +#define CircBuffEmpty(pcb) ((pcb)->pput == (pcb)->pget) +#define CircBuffHitEnd(pcb, p) ((p) >= (pcb)->buffEnd) + +#endif diff --git a/engine/src/Libraries/AFILE/Source/compose.c b/engine/src/Libraries/AFILE/Source/compose.c new file mode 100644 index 0000000..2748725 --- /dev/null +++ b/engine/src/Libraries/AFILE/Source/compose.c @@ -0,0 +1,362 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// COMPOSEW.C Routines for handling wide8 compose +// buffer +// Rex E. Bradford (REX) +// +/* + * $Header: r:/prj/lib/src/afile/RCS/compose.c 1.4 1994/10/18 08:19:13 rex Exp $ + * $Log: compose.c $ + * Revision 1.4 1994/10/18 08:19:13 rex + * Added BMF_TRANS flag to computed RSD bitmaps, so they'll work properly with + * 2d. + * + * Revision 1.3 1994/09/13 12:23:27 rex + * Push and pop stack canvas, instead of just set + * + * Revision 1.2 1994/09/06 18:02:56 rex + * Fixed check for same size bitmap as compose buffer (no need for row to match) + * + * Revision 1.1 1994/07/22 13:20:04 rex + * Initial revision + * + */ + +#include +#include +#include + +#include "compose.h" +#include "lg.h" + +// ------------------------------------------------------- +// +// ComposeInit() allocates memory and inits bitmap. + +void ComposeInit(grs_bitmap *pcompose, int32_t bmtype, int32_t w, int32_t h) { + // Compute rowbytes based on type + + switch (bmtype) { + case BMT_FLAT8: + pcompose->row = sizeof(uint8_t) * w; + break; + case BMT_FLAT24: + pcompose->row = 3 * sizeof(uint8_t) * w; + break; + default: + WARN("%s invalid bitmap type: %d", __FUNCTION__, bmtype); + return; + } + + // Init bitmap + + pcompose->bits = (uint8_t *)malloc((int32_t)pcompose->row * h); + pcompose->type = bmtype; + pcompose->flags = 0; + pcompose->align = 0; + pcompose->w = w; + pcompose->h = h; +} + +// ------------------------------------------------------- +// +// ComposeAdd() adds a bitmap to the compose buffer. + +void ComposeAdd(grs_bitmap *pcompose, grs_bitmap *pbm) { + switch (pcompose->type) { + case BMT_FLAT8: + ComposeFlat8Add(pcompose, pbm); + break; + case BMT_FLAT24: + ComposeFlat24Add(pcompose, pbm); + break; + default: + WARN("%s: can't handle compose buffer type: %d", __FUNCTION__, pcompose->type); + break; + } +} + +// ------------------------------------------------------- +// +// ComposeFlat8Add() adds a bitmap to a flat8 compose buffer. + +void ComposeFlat8Add(grs_bitmap *pcompose, grs_bitmap *pbm) { + grs_canvas cv; + + if ((pcompose->w != pbm->w) || (pcompose->h != pbm->h)) { + printf("ComposeFlat8Add: not same size bitmaps!\n"); + return; + } + + switch (pbm->type) { + // case BMT_RSD24: + // printf("ComposeFlat8Add: can't add RSD24 to compose buffer\n"); + // break; + + default: + gr_make_canvas(pcompose, &cv); + gr_push_canvas(&cv); + gr_bitmap(pbm, 0, 0); + gr_pop_canvas(); + break; + } +} + +// ------------------------------------------------------- +// +// ComposeFlat24Add() adds a bitmap to a flat8 compose buffer. + +void gr_flat24_flat24_ubitmap(grs_bitmap *pbm, int32_t x, int32_t y) { + uint8_t *ps, *pd; + int32_t iy; + + ps = pbm->bits; + pd = grd_canvas->bm.bits + (grd_canvas->bm.row * y) + (x * 3); + for (iy = y; iy < (y + pbm->h); iy++) { + memcpy(pd, ps, pbm->w * 3); + ps += pbm->row; + pd += grd_canvas->bm.row; + } +} + +void ComposeFlat24Add(grs_bitmap *pcompose, grs_bitmap *pbm) { + grs_canvas cv; + + if ((pcompose->w != pbm->w) || (pcompose->h != pbm->h) || (pcompose->row != pbm->row)) { + WARN("%s: not same size bitmaps!", __FUNCTION__); + return; + } + + switch (pbm->type) { + // case BMT_RSD24: + // Warning(("ComposeFlat24Add: can't add RSD24 to compose buffer!\n")); + // break; + + case BMT_FLAT24: + gr_make_canvas(pcompose, &cv); + gr_push_canvas(&cv); + gr_flat24_flat24_ubitmap(pbm, 0, 0); + gr_pop_canvas(); + break; + + default: + gr_make_canvas(pcompose, &cv); + gr_push_canvas(&cv); + gr_bitmap(pbm, 0, 0); + gr_pop_canvas(); + break; + } +} + +// -------------------------------------------------------- +// +// ComposeDiff() computes difference between compose buffer & bitmap. + +int32_t ComposeDiff(grs_bitmap *pcompose, grs_bitmap *pbmNew, grs_bitmap *pbmDiff) { + switch (pcompose->type) { + case BMT_FLAT8: + return (ComposeFlat8Diff(pcompose, pbmNew, pbmDiff)); + + // case BMT_FLAT24: + // return(ComposeFlat24Diff(pcompose, pbmNew, pbmDiff)); + + default: + printf("ComposeDiff: can't handle compose buffer type: %d\n", pcompose->type); + return (0); + } +} + +// ----------------------------------------------------------- +// +// ComposeFlat8Diff() computes diff between flat8 compose buff & bitmap. + +int32_t ComposeFlat8Diff(grs_bitmap *pcompose, grs_bitmap *pbmNew, grs_bitmap *pbmDiff) { + int32_t numPixels, len; + + // Error-check + + if (pbmNew->type != BMT_FLAT8) { + printf("ComposeFlat8Diff: new bitmap wrong type: %d\n", pbmNew->type); + return (-1); + } + + // Init rsd8 bitmap + + gr_init_bm(pbmDiff, pbmDiff->bits, BMT_RSD8, BMF_TRANS, pcompose->w, pcompose->h); + pbmDiff->row = pbmDiff->w; + + // Try rsd compression + + numPixels = (int32_t)pbmDiff->row * pbmDiff->h; + len = 0; + // len = RsdCompressDiff(pbmDiff->bits, numPixels, pcompose->bits, + // pbmNew->bits, numPixels); + + // If failed, revert to flat8 bitmap + + if (len <= 0) { + pbmDiff->type = BMT_FLAT8; + memcpy(pbmDiff->bits, pcompose->bits, numPixels); + len = numPixels; + } + + // Return length + + return (len); +} +/* +// ----------------------------------------------------------- +// +// ComposeFlat24Diff() computes diff between flat24 compose buff & bitmap. +int32_t ComposeFlat24Diff(grs_bitmap *pcompose, grs_bitmap *pbmNew, grs_bitmap *pbmDiff) { + int32_t numPixels, numPixels3, len; + + // Error-check + if (pbmNew->type != BMT_FLAT24) { + WARN("%s: new bitmap wrong type: %d", __FUNCTION__, pbmNew->type); + return (-1); + } + + // Init rsd24 bitmap + pbmDiff->type = BMT_RSD24; + pbmDiff->flags = BMF_TRANS; + pbmDiff->align = 0; + pbmDiff->w = pcompose->w; + pbmDiff->h = pcompose->h; + pbmDiff->row = pcompose->row; + + // Try rsd compression + numPixels = (int32_t)pbmDiff->row * pbmDiff->h; + numPixels3 = numPixels * 3; + len = Rsd24CompressDiff(pbmDiff->bits, numPixels3, pcompose->bits, pbmNew->bits, numPixels); + + // If failed, revert to flat24 bitmap + if (len <= 0) { + pbmDiff->type = BMT_FLAT24; + memcpy(pbmDiff->bits, pcompose->bits, numPixels3); + len = numPixels3; + } + + // Return length + return (len); +} +*/ +// ----------------------------------------------------------- +// +// ComposeConvert() converts compose buffer into bitmap. + +int32_t ComposeConvert(grs_bitmap *pcompose, grs_bitmap *pbm) { + switch (pcompose->type) { + case BMT_FLAT8: + return (ComposeFlat8Convert(pcompose, pbm)); + case BMT_FLAT24: + return(ComposeFlat24Convert(pcompose, pbm)); + + default: + WARN("%s: can't handle compose buffer type: %d", __FUNCTION__, pcompose->type); + return (0); + } +} + +// --------------------------------------------------------- +// +// ComposeFlat8Convert() converts flat8 compose buffer into bitmap. + +int32_t ComposeFlat8Convert(grs_bitmap *pcompose, grs_bitmap *pbm) { + int32_t numPixels, len; + grs_canvas cv; + + if ((pcompose->w != pbm->w) || (pcompose->h != pbm->h) || (pcompose->row != pbm->row)) { + WARN("%s: not same size bitmaps!", __FUNCTION__); + return (0); + } + + numPixels = (int32_t)pcompose->w * pcompose->h; + +CONVERT: + + switch (pbm->type) { + case BMT_FLAT8: + memcpy(pbm->bits, pcompose->bits, numPixels); + return (numPixels); + + case BMT_RSD8: + // len = RsdCompress(pbm->bits, numPixels, pcompose->bits, -1, numPixels); + len = -1; + if (len < 0) { + pbm->type = BMT_FLAT8; + goto CONVERT; + } + return (len); + case BMT_FLAT24: + gr_make_canvas(pbm, &cv); + gr_push_canvas(&cv); + gr_bitmap(pcompose, 0, 0); + gr_pop_canvas(); + return(numPixels * 3); + + default: + printf("ComposeFlat8Convert: can't convert to bm type: %d\n", pbm->type); + return (0); + } +} + +// --------------------------------------------------------- +// +// ComposeFlat24Convert() converts flat24 compose buffer into bitmap. + +int32_t ComposeFlat24Convert(grs_bitmap *pcompose, grs_bitmap *pbm) { + int32_t numPixels; + grs_canvas cv; + + if ((pcompose->w != pbm->w) || (pcompose->h != pbm->h) || (pcompose->row != pbm->row)) { + WARN("%s: not same size bitmaps!", __FUNCTION__); + return (0); + } + + numPixels = (int32_t)pcompose->w * pcompose->h; + + switch (pbm->type) { + case BMT_FLAT8: + gr_make_canvas(pbm, &cv); + gr_push_canvas(&cv); + gr_bitmap(pcompose, 0, 0); + gr_pop_canvas(); + return (numPixels); + + case BMT_FLAT24: + memcpy(pbm->bits, pcompose->bits, numPixels * 3); + return (numPixels * 3); + + default: + WARN("%s: can't convert to bm type: %d", __FUNCTION__, pbm->type); + return (0); + } +} + +// --------------------------------------------------------- +// +// ComposeFree() frees up compose buffer. + +void ComposeFree(grs_bitmap *pcompose) { + if (pcompose->bits) { + free(pcompose->bits); + pcompose->bits = NULL; + } +} diff --git a/engine/src/Libraries/AFILE/Source/compose.h b/engine/src/Libraries/AFILE/Source/compose.h new file mode 100644 index 0000000..64f6a0f --- /dev/null +++ b/engine/src/Libraries/AFILE/Source/compose.h @@ -0,0 +1,72 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// COMPOSEW.H WIDE8 Compose buffering +// Rex E. Bradford (REX) +// +// These routines implement a buffer for composing images. You +// should +// use one of the following types when defining such a compose +// buffer: +// +// BMT_FLAT8: Compose 8-bit images, don't need difference info +// BMT_FLAT24: Compose 24-bit images, don't need difference +// info + +/* + * $Header: r:/prj/lib/src/afile/RCS/compose.h 1.1 1994/07/22 13:21:13 rex Exp $ + * $Log: compose.h $ + * Revision 1.1 1994/07/22 13:21:13 rex + * Initial revision + * + */ + +#ifndef __COMPOSEW_H +#define __COMPOSEW_H + +#ifndef __2D_H +#include "2d.h" +#endif +//#ifndef __RSD24_H +//#include +//#endif + +// Prototypes + +void ComposeInit(grs_bitmap *pcompose, int32_t bmtype, int32_t w, int32_t h); +void ComposeAdd(grs_bitmap *pcompose, grs_bitmap *pbm); +int32_t ComposeDiff(grs_bitmap *pcompose, grs_bitmap *pbmNew, grs_bitmap *pbmDiff); +int32_t ComposeConvert(grs_bitmap *pcompose, grs_bitmap *pbm); +void ComposeFree(grs_bitmap *pcompose); + +// Specific compose routines (type is compose buffer type, not bm!) + +void ComposeFlat8Add(grs_bitmap *pcompose, grs_bitmap *pbm); +void ComposeFlat24Add(grs_bitmap *pcompose, grs_bitmap *pbm); + +// Specific diff routines (type is compose buffer type, not bm!) + +int32_t ComposeFlat8Diff(grs_bitmap *pcompose, grs_bitmap *pbmNew, grs_bitmap *pbmDiff); +//int32_t ComposeFlat24Diff(grs_bitmap *pcompose, grs_bitmap *pbmNew, grs_bitmap *pbmDiff); + +// Specific convert routines (type is compose buffer type, not bm!) + +int32_t ComposeFlat8Convert(grs_bitmap *pcompose, grs_bitmap *pbm); +int32_t ComposeFlat24Convert(grs_bitmap *pcompose, grs_bitmap *pbm); + +#endif diff --git a/engine/src/Libraries/AFILE/Source/draw4x4.cpp b/engine/src/Libraries/AFILE/Source/draw4x4.cpp new file mode 100644 index 0000000..30b7c69 --- /dev/null +++ b/engine/src/Libraries/AFILE/Source/draw4x4.cpp @@ -0,0 +1,365 @@ +// +// System Shock Enhanced Edition +// +// Copyright (C) 2015-2018 Night Dive Studios, LLC. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// DESCRIPTION: +// 4x4 Drawing routines +// Reverse engineered by Alex Reimann Cunha Lima +// + +//============================================================================ +// Includes +#include "lg.h" +#include "2d.h" + +// Holds bitstream information between invocations. +class BitstreamInfo +{ + uint8_t* ptr; + uint32_t word; // working value + int count; // how many bits are available in word +public: + explicit BitstreamInfo(uint8_t* p) : ptr(p), word(0), count(0) {} + inline void reserve(int nbits) { + while (count < nbits) { + word = (word << 8) | *ptr++; + count += 8; + } + } + inline void skip(int nbits) { + count -= nbits; + } + inline uint32_t peek(int nbits) { + reserve(nbits); + uint32_t const mask = ~(0xffffffff << nbits); + return (word >> (count - nbits)) & mask; + } + inline uint32_t take(int nbits) { + uint32_t const value = peek(nbits); + skip(nbits); + return value; + } +}; + +static uchar* d4x4_hufftab; +static uchar* d4x4_colorset; + +static uchar* Draw4x4_InternalBeta(uint32_t* xtab, int b, uchar* bits, int d, uchar* mask_stream); +static void Draw4x4_InternalAlpha(uint32_t* xtab, int b, BitstreamInfo& bitstream); + +//============================================================================ +// Functions + +// +// Draw4x4Reset +// + +extern "C" { + +void Draw4x4Reset(uchar* colorset, uchar* hufftab) +{ + d4x4_hufftab = hufftab; + d4x4_colorset = colorset; +} + +// +// Draw4x4 +// + +void Draw4x4(uchar* p, int width, int height) +{ + uint32_t xtab[640 / 4]; + + int cell_column; + int row = grd_canvas->bm.row; + cell_column = width / 4; + BitstreamInfo bitstream(p+2); + uchar* mask_stream = (p + *((ushort*)(p))); + + uchar* bits = grd_canvas->bm.bits; + + int row4 = row * 4; + uint aligned_height = height & ~3; + for (uint y = 0; y < aligned_height; y += 4) { + Draw4x4_InternalAlpha(xtab, cell_column, bitstream); + mask_stream = Draw4x4_InternalBeta(xtab, cell_column, bits, row, mask_stream); + bits += row4; + } +} + +} + +// +// Draw4x4_InternalAlpha +// +static void Draw4x4_InternalAlpha(uint32_t* xtab, int b, BitstreamInfo& bitstream) +{ + while (b > 0) { + // Pull 12 bits from the bitstream and use it as an index into the + // hufftable. + auto const hindex = bitstream.peek(12); + uint8_t* huffptr = &d4x4_hufftab[hindex * 3]; + uint32_t huffword = *((uint32_t*)huffptr) & 0x00FFFFFF; + // Bits 20-23 are the count field. + auto count = (huffword & 0xf00000) >> 20; + if (count == 0) { + // A count of 0 is a long offset. It always advances the bitpointer + // by 12, since obviously it can't use the count to advance. + bitstream.skip(12); + + while (count == 0) { + // Use the entire (effectively 20-bit) huffword as an index into + // the hufftable. + huffptr = d4x4_hufftab + huffword * 3; + // Pull 4 bits from the bitstream and add that to the huffindex. + auto const offset = bitstream.peek(4); + + huffptr += offset * 3; + // Check the next count: if it's still zero, go round again. + huffword = *((uint32_t*)huffptr) & 0x00FFFFFF; + count = (huffword & 0xf00000) >> 20; + if (count == 0) + { + // If we're going around again, eat the 4 bits of offset. + // Otherwise, let the new control word determine the count. + bitstream.skip(4); + } + } + } + // Otherwise it is the count of bits to consume. This is not + // necessarily the full 12 bits we just read; fields may overlap. + bitstream.skip(count); + + b--; + // Check the control word for special types. + auto const type = (huffword & 0x000e0000) >> 17; + switch (type) + { + case 5: + { + // Type 5: skip. The next 5 bits from the bitstream form the count. + auto hcnt = bitstream.take(5); + // A count of 31 skips the rest of the row. + if (hcnt == 0x1f) { + hcnt = b; + } + b -= hcnt; + *xtab++ = hcnt | 0x000a0000; + break; + } + case 6: + case 7: // not used, but in the original exe has the same meaning as 6 + // Type 6: repeat. Use the previous control word. + *xtab = xtab[-1]; + ++xtab; + break; + default: + // All other types are significant to the low-level decoder only. + *xtab++ = huffword; + break; + } + } +} + +// +// Low-level decode routine. Takes the control-word table decoded above and +// the current mask stream pointer. Returns the updated mask_stream pointer. +// +static uint8_t* Draw4x4_InternalBeta(uint32_t* xtab, int framesize, + uint8_t* bits, int row, + uint8_t* mask_stream) +{ + uint8_t* tbits; // temp copy of bits + int i; // generic loop index + + auto const end = bits + 4*framesize; + while (bits < end) { + // Read a control word from the decoded xtab and interpret it. + // Bits 0-16 are the 'parameter' field. + // Bits 17-19 are the 'type' field. + // Bits 20-23 are only used by the first-stage decoder. + auto const xtype = (*xtab >> 17) & 0x07; + auto const xparam = *xtab & 0x01ffff; + + switch (xtype) { + case 0: + { + // Type 0 : direct colour. Bits 0-15 of the control word form a 2- + // pixel block to be replicated 8 times into the tile. + uint8_t ctab[2] = { + uint8_t(xparam & 0xff), + uint8_t((xparam & 0xff00) >> 8) + }; + tbits = bits; + for (i = 0; i < 4; ++i) { + tbits[0] = ctab[0]; + tbits[1] = ctab[1]; + tbits[2] = ctab[0]; + tbits[3] = ctab[1]; + tbits += row; + } + break; + } + case 1: + { + // Type 1 : 1-bit index. Read a 16-bit value from the mask stream. + // Each bit forms an index into a 2-byte colour table taken directly + // from bits 0-15 of the control word. Set each corresponding pixel + // in the tile accordingly, taking a zero value in the first colour + // table ONLY to be transparent. + uint8_t ctab[2] = { + uint8_t(xparam & 0xff), + uint8_t((xparam & 0xff00) >> 8) + }; + auto mask = *(uint16_t*)mask_stream; + mask_stream += 2; + tbits = bits; + if (ctab[0] != 0) { + // No transparency, just blat to the tile. + for (i = 0; i < 4; ++i) { + *tbits = ctab[mask&1]; + tbits[1] = ctab[(mask>>1) & 1]; + tbits[2] = ctab[(mask>>2) & 1]; + tbits[3] = ctab[(mask>>3) & 1]; + mask >>= 4; + tbits += row; + } + } + else { + // Have to take transparency into account. + for (i = 0; i < 4; ++i) { + if (mask & 1) *tbits = ctab[mask&1]; + if (mask & 2) tbits[1] = ctab[(mask>>1) & 1]; + if (mask & 4) tbits[2] = ctab[(mask>>2) & 1]; + if (mask & 8) tbits[3] = ctab[(mask>>3) & 1]; + mask >>= 4; + tbits += row; + } + } + break; + } + case 2: + { + // Type 2: 2-bit index. Read a 32-bit value from the mask stream. + // The parameter field of the control word forms an index into the + // main colour table, taken to point to a 4-byte colour table. Set + // each tile pixel to the colour table indexed by the corresponding + // 2 bits of the mask word. + auto mask = *(uint32_t*)mask_stream; + mask_stream += 4; + tbits = bits; + auto const ctab = d4x4_colorset + xparam; + if (ctab[0] != 0) { + // No transparency. + for (i = 0; i < 4; ++i) { + tbits[0] = ctab[mask & 3]; + tbits[1] = ctab[(mask>>2) & 3]; + tbits[2] = ctab[(mask>>4) & 3]; + tbits[3] = ctab[(mask>>6) & 3]; + mask >>= 8; + tbits += row; + } + break; + } else { + for (i = 0; i < 4; ++i) { + if (mask & 0x03) tbits[0] = ctab[mask & 0x03]; + if (mask & 0x0c) tbits[1] = ctab[(mask & 0x0c) >> 2]; + if (mask & 0x30) tbits[2] = ctab[(mask & 0x30) >> 4]; + if (mask & 0xc0) tbits[3] = ctab[(mask & 0xc0) >> 6]; + mask >>= 8; + tbits += row; + } + } + break; + } + case 3: + { + // Type 3: 3-bit index. As above, but read 48 bits from the mask + // stream and use each 3-bit field as an index into an 8-byte colour + // table. + // Note: rather than implement the ad-hoc schemes of the original + // code, I think it's more convenient and probably just as efficient + // to let the compiler deal with wide data types, even on a 32-bit + // platform. + auto mask = *(uint64_t*)mask_stream; + mask_stream += 6; + tbits = bits; + auto const ctab = d4x4_colorset + xparam; + if (ctab[0] != 0) { + // No transparency. + for (i = 0; i < 4; ++i) { + tbits[0] = ctab[mask & 7]; + tbits[1] = ctab[(mask>>3) & 7]; + tbits[2] = ctab[(mask>>6) & 7]; + tbits[3] = ctab[(mask>>9) & 7]; + mask >>= 12; + tbits += row; + } + break; + } else { + for (i = 0; i < 4; ++i) { + if (mask & 0x007) tbits[0] = ctab[mask & 0x007]; + if (mask & 0x038) tbits[1] = ctab[(mask & 0x038) >> 3]; + if (mask & 0x1c0) tbits[2] = ctab[(mask & 0x1c0) >> 6]; + if (mask & 0xe00) tbits[3] = ctab[(mask & 0xe00) >> 9]; + mask >>= 12; + tbits += row; + } + } + break; + } + case 4: + { + // Type 4: 4-bit index. Mask word is 64 bits, colour table has 16 + // entries. + auto mask = *(uint64_t*)mask_stream; + mask_stream += 8; + tbits = bits; + auto const ctab = d4x4_colorset + xparam; + if (ctab[0] != 0) { + // No transparency. + for (i = 0; i < 4; ++i) { + tbits[0] = ctab[mask & 0xf]; + tbits[1] = ctab[(mask>>4) & 0xf]; + tbits[2] = ctab[(mask>>8) & 0xf]; + tbits[3] = ctab[(mask>>12) & 0xf]; + mask >>= 16; + tbits += row; + } + break; + } else { + for (i = 0; i < 4; ++i) { + if (mask & 0x000f) tbits[0] = ctab[mask & 0x000f]; + if (mask & 0x00f0) tbits[1] = ctab[(mask & 0x00f0) >> 4]; + if (mask & 0x0f00) tbits[2] = ctab[(mask & 0x0f00) >> 8]; + if (mask & 0xf000) tbits[3] = ctab[(mask & 0xf000) >> 12]; + mask >>= 16; + tbits += row; + } + } + break; + } + case 5: + // Type 5: skip tiles horizontally. + bits += xparam * 4; + break; + } + ++xtab; + bits += 4; + } + return mask_stream; +} diff --git a/engine/src/Libraries/AFILE/Source/draw4x4.h b/engine/src/Libraries/AFILE/Source/draw4x4.h new file mode 100644 index 0000000..cfe69b7 --- /dev/null +++ b/engine/src/Libraries/AFILE/Source/draw4x4.h @@ -0,0 +1,38 @@ +// +// System Shock Enhanced Edition +// +// Copyright (C) 2015-2018 Night Dive Studios, LLC. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// DESCRIPTION: +// 4x4 Drawing routines +// Reverse engineered by Alex Reimann Cunha Lima +// + +#ifndef __DRAW4X4_H +#define __DRAW4X4_H + +#ifdef __cplusplus +extern "C" { +#endif + +void Draw4x4(uchar* p, int width, int height); +void Draw4x4Reset(uchar* colorset, uchar* hufftab); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/engine/src/Libraries/AFILE/Source/huff.h b/engine/src/Libraries/AFILE/Source/huff.h new file mode 100644 index 0000000..c0d0693 --- /dev/null +++ b/engine/src/Libraries/AFILE/Source/huff.h @@ -0,0 +1,122 @@ +// +// System Shock Enhanced Edition +// +// Copyright (C) 2015-2018 Night Dive Studios, LLC. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// DESCRIPTION: +// HUFF.H Huffman coding routines +// Rex E. Bradford + +/* +* $Header: r:/prj/lib/src/dstruct/RCS/huff.h 1.1 1994/08/22 17:13:06 rex Exp $ +* $Log: huff.h $ + * Revision 1.1 1994/08/22 17:13:06 rex + * Initial revision + * +*/ + +#ifndef __HUFF_H +#define __HUFF_H + +// This node is used when building a huffman coding tree. You need to +// allocate an array twice as big as the number of actual leaf nodes +// that are to be coded. Fill in the bottom half with leaf nodes +// (token and freq) after clearing the entire array. Then use HuffBuild() +// to build the tree. + +typedef struct _HuffNode { + uint token; // token value + uint freq; // frequency + struct _HuffNode *parent; // ptr to parent node + struct _HuffNode *right; // ptr to right node + struct _HuffNode *left; // ptr to left node +} HuffNode; + +HuffNode *HuffBuild(HuffNode *htree, int numTerminals, int *pNumNodes); + +// You can walk a tree using HuffWalk(). HuffDump() uses the walker +// with HuffPrintNode() to dump the tree. HuffPrintNode() can be used +// to print any node, and HuffWalk() may be used with your own routine +// as well. + +void HuffDump(HuffNode *htree, HuffNode *hroot, int numNodes); +void HuffWalk(HuffNode *htree, HuffNode *hroot, + void (*func)(HuffNode *htree, HuffNode *hnode, uint code, int numBits)); +void HuffPrintNode(HuffNode *htree, HuffNode *hnode, uint code, int numBits); + +// To speed up decoding, "flash tables" are used. Normally, a huffman +// stream must be examined a bit at a time, walking the tree till a +// terminal node is hit. A flash-tree looks up a number of bits at once, +// into a table with redundant entries which encodes not only the lookup +// token but also the number of bits used in that token. When you make +// a flash table, you can control the number of bits used to look up into +// the primary table (default is 12-bit), and the number of bits used in +// secondary tables, which are used when a given token is longer than the +// primary table allows (default is 4-bit secondary tables, of which many +// can be chained). The overall token length in this implementation is +// 32 bits. +// +// Because 4 bits are reserved for the "numbits" field of the flash table, +// the actual active bits of a token encoded in a flash table is limited +// to 28 bits. Remember to include the 4 numbits bits when calculating +// the width of your flash entries + +#define HUFF_DEFAULT_BITS_PRI 12 +#define HUFF_DEFAULT_BITS_SEC 4 + +int HuffMakeFlashTables(HuffNode *htree, HuffNode *hroot, uchar *pFlashTab, + int pftLength, int tokSize, int bitsPri, int bitsSec); +void HuffPrintFlashTables(uchar *pFlashTab, uint length, int tokSize); +int HuffCompressFlashTables(uchar *pFlashTab, uint length, int tokSize, + uint *pcBuff); + +// Flash tables must be decompressed to be used: + +#ifdef __cplusplus +extern "C" { +#endif + +void HuffExpandFlashTables(uchar *pFlashTab, uint lenTab, uint *pc, + int tokSize); + +#ifdef __cplusplus +} +#endif + +// Writing huffman-encoded data can be done using the following +// routines and macros. Make sure to use HuffEndOutput() to terminate +// an output stream. See huffde.h for decoding (reading) macros. + +typedef struct { + uchar *bits; // ptr to huff-encoded bits + int ibit; // bit # within current byte, 0-7 +} HuffPtr; + +#define HuffResetWritePtr(ph,pbits) { \ + (ph)->bits=(pbits); \ + (ph)->ibit=7; \ + *((ph)->bits)=0; \ +} + +void HuffEncode(HuffPtr *ph, uint code, int numBits); +void HuffEncodeNode(HuffPtr *ph, HuffNode *hnode); + +#define HuffEndOutput(ph) ((ph)->bits++) + +#define HuffStreamLen(ph,pbase) ((ph)->bits - pbase) + +#endif + diff --git a/engine/src/Libraries/AFILE/Source/huffde.cpp b/engine/src/Libraries/AFILE/Source/huffde.cpp new file mode 100644 index 0000000..5dba904 --- /dev/null +++ b/engine/src/Libraries/AFILE/Source/huffde.cpp @@ -0,0 +1,88 @@ +// +// System Shock Enhanced Edition +// +// Copyright (C) 2015-2018 Night Dive Studios, LLC. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// DESCRIPTION: +// Huffman decompression routines +// + +//============================================================================ +// Original header +// HUFFDE.C Huffman decompression routines +// Rex E. Bradford + +/* +* $Header: r:/prj/lib/src/dstruct/RCS/huffde.c 1.1 1994/08/22 17:13:01 rex Exp $ +* $Log: huffde.c $ + * Revision 1.1 1994/08/22 17:13:01 rex + * Initial revision + * +*/ + +//============================================================================ +// Includes +#include + +#include "lg.h" + +//#include "game/libraries/lg/dbg.h" +#include "huff.h" + +//============================================================================ +// Functions + +// ---------------------------------------------------------------- +// DECOMPRESS HUFFMAN MULTI-TABLES +// ---------------------------------------------------------------- +// +// HuffExpandFlashTables() compresses huffman flash-decoder tables. + +extern "C" { + +void HuffExpandFlashTables(uchar *pFlashTab, uint lenTab, uint *pc, + int tokSize) +{ + uchar *pft; + uint token, runCount; + int runShift; + + // Setup + + pft = pFlashTab; + runShift = tokSize * 8; + + // While still inside dest table, keep going + + while (pft < (pFlashTab + lenTab)) + { + + // Get next token, extract run count + + token = *pc++; + runCount = token >> runShift; + + // Copy that many times into dest + + while (runCount-- != 0) + { + memcpy(pft, &token, tokSize); + pft += tokSize; + } + } +} + +} diff --git a/engine/src/Libraries/AFILE/Source/movie.c b/engine/src/Libraries/AFILE/Source/movie.c new file mode 100644 index 0000000..c0c393a --- /dev/null +++ b/engine/src/Libraries/AFILE/Source/movie.c @@ -0,0 +1,41 @@ +/* + +Copyright (C) 2018 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#include "afile.h" +#include "res.h" + +int32_t AfilePrepareRes(Id id, Afile *afile) { + + // Grab the raw data and let the library deal with it. + uint8_t *ptr = ResLock(id); + int size = ResSize(id); + MFILE *mf; + + mf = (MFILE *)malloc(sizeof(MFILE)); + mf->p = (unsigned char *)malloc(size); + memcpy(mf->p, ptr, size); + mf->size = size; + mf->pos = 0; + + ResUnlock(id); + + int32_t error = AfileOpen(afile, mf, AFILE_MOV); + + return error; +} diff --git a/engine/src/Libraries/AFILE/Source/movie.h b/engine/src/Libraries/AFILE/Source/movie.h new file mode 100644 index 0000000..9945a42 --- /dev/null +++ b/engine/src/Libraries/AFILE/Source/movie.h @@ -0,0 +1,229 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/afile/RCS/movie.h $ + * $Revision: 1.16 $ + * $Author: dc $ + * $Date: 1994/12/01 04:17:50 $ + */ + +#ifndef __MOVIE_H +#define __MOVIE_H + +#ifndef __FIX_H +#include "fix.h" +#endif +#ifndef __2D_H +#include "2d.h" +#endif +#ifndef __RES_H +#include "res.h" +#endif +#ifndef __CIRCBUFF_H +#include "circbuff.h" +#endif + +#include "afile.h" + +/* +#ifndef AIL_H +#include +#endif +*/ +// Movie file-format structures: +// +// 1. MovieHeader (1K) at head of file (check MOVI_MAGIC_ID) +// 2. MovieChunk[] array, padded to 1K/3K/5K (so array + hdr mult of 2K) +// 3. Actual chunks, as pointed at in MovieChunk[] array + +// Movie chunk format +#pragma pack(push,1) +// FIXME bitfield and little-endian hell of madness... +/* +typedef struct { + uint32_t time : 24; // fixed-point time since movie start + uint32_t played : 1; // has this chunk been clocked out? + uint32_t flags : 4; // chunkType-specific + uint32_t chunkType : 3; // MOVIE_CHUNK_XXX + uint32_t offset; // int8_t offset to chunk start +} MovieChunk __attribute__ ((__packed__));; +*/ +typedef struct { + uint32_t time : 24; // fixed-point time since movie start + uint32_t chunkType : 3; // MOVIE_CHUNK_XXX + uint32_t flags : 4; // chunkType-specific + uint32_t played : 1; // has this chunk been clocked out? + uint32_t offset; // int8_t offset to chunk start +} MovieChunk; +#pragma pack(pop) + +// Movie chunk types + +#define MOVIE_CHUNK_END 0 +#define MOVIE_CHUNK_VIDEO 1 +#define MOVIE_CHUNK_AUDIO 2 +#define MOVIE_CHUNK_TEXT 3 +#define MOVIE_CHUNK_PALETTE 4 +#define MOVIE_CHUNK_TABLE 5 + +// Movie chunk flags + +#define MOVIE_FVIDEO_BMTMASK 0x0F // video chunk, 4 bits of flags is bmtype +#define MOVIE_FVIDEO_BMF_4X4 0x0F // 4x4 movie format + +#define MOVIE_FPAL_EFFECTMASK 0x07 // pal chunk, 4 bits of flags is effect +#define MOVIE_FPAL_SET 0x00 // set palette from data +#define MOVIE_FPAL_BLACK 0x01 // set palette to black +#define MOVIE_FPAL_CLEAR 0x08 // if bit set, also clear screen + +#define MOVIE_FTABLE_COLORSET 0 // table chunk, table is color set +#define MOVIE_FTABLE_HUFFTAB 1 // huffman table (compressed) + +// Movie header layout +#pragma pack(push,1) +typedef struct { + uint32_t magicId; // 'MOVI' (MOVI_MAGIC_ID) + int32_t numChunks; // number of chunks in movie + int32_t sizeChunks; // size in bytes of chunk array + int32_t sizeData; // size in bytes of chunk data + fix totalTime; // total playback time + fix frameRate; // frames/second, for info only + int16_t frameWidth; // frame width in pixels + int16_t frameHeight; // frame height in pixels + int16_t gfxNumBits; // 8, 15, 24 + int16_t isPalette; // is palette present? + int16_t audioNumChans; // 0 = no audio, 1 = mono, 2 = stereo + int16_t audioSampleSize; // 1 = 8-bit, 2 = 16-bit + fix audioSampleRate; // in Khz + uint8_t reserved[216]; // so chunk is 1K in size + uint8_t palette[768]; // palette +} MovieHeader; +#pragma pack(pop) + +#ifndef SAMPRATE_11KHZ // also appear in voc.h +#define SAMPRATE_11KHZ fix_make(11127, 0) +#define SAMPRATE_22KHZ fix_make(22254, 0) +#endif + +// FIXME: unportable, need change to FourCC +// Little endian, "MOVI" +#define MOVI_MAGIC_ID 0x49564F4D + +// Movie text chunk begins with a 0-terminated array of these: + +typedef struct { + uint32_t tag; // 'XXXX' + uint32_t offset; // offset to text string +} MovieTextItem; + +#define MOVIE_TEXTITEM_MAKETAG(c1, c2, c3, c4) \ + ((((uint32_t)c4) << 24) | (((uint32_t)c3) << 16) | (((uint32_t)c2) << 8) | (c1)) +#define MOVIE_TEXTITEM_TAG(pmti, index) ((pmti + (index))->tag) +#define MOVIE_TEXTITEM_PTR(pmti, index) ((char *)(pmti) + (pmti + (index))->offset) +#define MOVIE_TEXTITEM_EXISTS(pmti, index) MOVIE_TEXTITEM_TAG(pmti, index) + +#define MOVIE_TEXTITEM_STDTAG MOVIE_TEXTITEM_MAKETAG('S', 'T', 'D', ' ') + +// Movie runtime structures + +typedef struct { + int16_t sizeBuffers; // size of each buffer + uint8_t *pbuff[2]; // sound buffers (raw ptrs) +} MovieAudioBuffers; + +typedef struct { + CircBuff cb; // circular data buffer + int32_t blockLen; // # bytes to read in each block + int32_t ovfLen; // # overflow bytes past circular buffer + MovieChunk *pCurrChunk; // ptr to current chunk to use + int32_t bytesLeft; // bytes left to read +} MovieBuffInfo; + +typedef struct { + int32_t snd_in; + int16_t nextBuff; // next buffer to load (0 or 1, -1 for none) + int16_t smp_id; // snd lib id of the current sample +} MovieAudioState; + +typedef struct Movie_ { + MovieHeader *pmh; // ptr to movie header (read from 1st bytes of movie) + MovieChunk *pmc; // ptr to movie chunk array + int32_t fd; // file being read from + int32_t fileOff; // offset in file to start of movie + grs_canvas *pcanvas; // ptr to canvas being played into + fix tStart; // time movie started + MovieBuffInfo bi; // movie buffering info + MovieAudioState as; // current audio state for each channel + uint8_t *pColorSet; // ptr to color set table (4x4 codec) + int32_t lenColorSet; // length of color set table + uint8_t *pHuffTab; // ptr to huffman table (4x4 codec) + int32_t lenHuffTab; // length of huffman table + void (*f_VideoCallback)(struct Movie_ *pmovie); // video callback for composing + void (*f_TextCallback)(struct Movie_ *pmovie, MovieTextItem *pitem); // text chunk callback + void *pTextCallbackInfo; // info maintained by text callback + uint8_t playing; // is movie playing? + uint8_t processing; // is movie processing? + uint8_t singleStep; // single step movie + uint8_t clipCanvas; // clip to canvas? +} Movie; + +// Prototypes + +Movie *MoviePrepare(int32_t fd, uint8_t *buff, int32_t buffLen, int32_t blockLen); +Movie *MoviePrepareRes(Id id, uint8_t *buff, int32_t buffLen, int32_t blockLen); +void MovieReadAhead(Movie *pmovie, int32_t numBlocks); +void MoviePlay(Movie *pmovie, grs_canvas *pcanvas); +void MovieUpdate(Movie *pmovie); +void MovieAdvance(Movie *pmovie); +void MovieRestart(Movie *pmovie); +void MovieKill(Movie *pmovie); + +#define TXTCB_FLAG_CENTER_X 0x01 +#define TXTCB_FLAG_CENTER_Y 0x02 +#define TXTCB_FLAG_CENTERED TXTCB_FLAG_CENTER_X | TXTCB_FLAG_CENTER_Y + +void MovieInstallStdTextCallback(Movie *pmovie, uint32_t lang, Id fontId, uint8_t color, uint8_t flags); + +#define MovieChunkLength(pmc) (((pmc) + 1)->offset - (pmc)->offset) +#define MoviePlaying(pmovie) ((pmovie)->playing) +#define MovieSetSingleStep(pmovie, on) ((pmovie)->singleStep = (on)) +#define MovieSetVideoCallback(pmovie, f) ((pmovie)->f_VideoCallback = (f)) +#define MovieSetTextCallback(pmovie, f) ((pmovie)->f_TextCallback = (f)) +#define MovieSetAudioBuffers(pmab) movieAudioBuffers = *(pmab) +#define MovieClearCanvas(pmovie) \ + { \ + gr_push_canvas((pmovie)->pcanvas); \ + gr_clear(0); \ + gr_pop_canvas(); \ + } +#define MovieSetPal(pmovie, s, n) \ + if ((pmovie)->pmh->isPalette) \ + gr_set_pal(s, n, (pmovie)->pmh->palette) + +extern MovieAudioBuffers movieAudioBuffers; + +#define MOVIE_DEFAULT_BLOCKLEN 8192 + +// 4x4 cleanup routine (frees + +void Draw4x4FreeResources(); + +// Custom functions +int32_t AfilePrepareRes(Id id, Afile *afile); +#endif diff --git a/engine/src/Libraries/CMakeLists.txt b/engine/src/Libraries/CMakeLists.txt new file mode 100644 index 0000000..358a4c3 --- /dev/null +++ b/engine/src/Libraries/CMakeLists.txt @@ -0,0 +1,371 @@ +enable_language(ASM) +set(can_use_assembler TRUE) + +set(2D_SRC + 2D/Source/bit.c + 2D/Source/bitmap.c + 2D/Source/blend.c + 2D/Source/canvas.c + 2D/Source/close.c + 2D/Source/cnvtab.c + 2D/Source/context.c + 2D/Source/detect.c + 2D/Source/devtab.c + 2D/Source/fcntab.c + 2D/Source/init.c + 2D/Source/lintab.c + 2D/Source/mode.c + 2D/Source/pal.c + 2D/Source/permap.c + 2D/Source/persetup.c + 2D/Source/pertol.c + 2D/Source/rgb.c + 2D/Source/screen.c + 2D/Source/sscrn.c + 2D/Source/StateStk.c + 2D/Source/svgainit.c + 2D/Source/tempbm.c + 2D/Source/temptm.c + 2D/Source/tlucdat.c + 2D/Source/tluctab.c + 2D/Source/valloc.c + 2D/Source/vtab.c + 2D/Source/MacDev.c + 2D/Source/GR/grilin.c + 2D/Source/GR/grmalloc.c + 2D/Source/GR/grnull.c + 2D/Source/GR/gruilin.c + 2D/Source/GR/grd.c + 2D/Source/Gen/gendisk.c + 2D/Source/Gen/gentm.c + 2D/Source/Gen/genwclin.c + 2D/Source/Gen/genuclin.c + 2D/Source/Gen/genrsd8.c + 2D/Source/Gen/genuvlin.c + 2D/Source/Gen/genhlin.c + 2D/Source/Gen/genrsdbm.c + 2D/Source/Gen/genvlin.c + 2D/Source/Gen/genvcply.c + 2D/Source/Gen/genclin.c + 2D/Source/Gen/genfl8c.c + 2D/Source/Gen/genrsdtm.c + 2D/Source/Gen/genel.c + 2D/Source/Gen/genwlin.c + 2D/Source/Gen/genpix.c + 2D/Source/Gen/genuhlin.c + 2D/Source/Gen/genhfl8.c + 2D/Source/Gen/genov.c + 2D/Source/Gen/gente.c + 2D/Source/Gen/genlin.c + 2D/Source/Gen/gencwlin.c + 2D/Source/Gen/genchfl8.c + 2D/Source/Gen/gengfl8.c + 2D/Source/Gen/genbox.c + 2D/Source/Gen/genmono.c + 2D/Source/Gen/genulin.c + 2D/Source/Gen/gencnv.c + 2D/Source/Gen/genuslin.c + 2D/Source/Gen/genvpoly.c + 2D/Source/Gen/general.c + 2D/Source/Gen/gencirc.c + 2D/Source/Gen/genfl8.c + 2D/Source/Gen/genf24.c + 2D/Source/Gen/gentl8.c + 2D/Source/Gen/genrect.c + 2D/Source/Gen/genvrect.c + 2D/Source/Gen/genslin.c + 2D/Source/Flat8/fl8vlin.c + 2D/Source/Flat8/FL8OPL.c + 2D/Source/Flat8/fl8cply.c + 2D/Source/Flat8/fl8clin.c + 2D/Source/Flat8/fl8lp.c + 2D/Source/Flat8/fl8lf.c + 2D/Source/Flat8/fl8ll.c + 2D/Source/Flat8/fl8s.c + 2D/Source/Flat8/fl8hfl8.c + 2D/Source/Flat8/fl8pnt.c + 2D/Source/Flat8/fl8fl8m.c + 2D/Source/Flat8/fl8ltp.c + 2D/Source/Flat8/fl8fl8c.c + 2D/Source/Flat8/fl8w.c + 2D/Source/Flat8/fl8cop.c + 2D/Source/Flat8/fl8wlin.c + 2D/Source/Flat8/fl8lw.c + 2D/Source/Flat8/fl8pix.c + 2D/Source/Flat8/fl8row.c + 2D/Source/Flat8/fl8ft.c + 2D/Source/Flat8/fl8rsd8.c + 2D/Source/Flat8/Fl8F.c + 2D/Source/Flat8/fl8bldbl.c + 2D/Source/Flat8/fl8clear.c + 2D/Source/Flat8/fl8p.c + 2D/Source/Flat8/fl8wclin.c + 2D/Source/Flat8/fl8hlin.c + 2D/Source/Flat8/fl8bl.c + 2D/Source/Flat8/fl8gpix.c + 2D/Source/Flat8/fl8ns.c + 2D/Source/Flat8/fl8lnop.c + 2D/Source/Flat8/fl8dbl.c + 2D/Source/Flat8/fl8clin.h + 2D/Source/Flat8/fl8cnv.c + 2D/Source/Flat8/fl8slin.c + 2D/Source/Flat8/fl8lin.c + 2D/Source/Flat8/fl8tsmap.c + 2D/Source/Flat8/fl8ply.c + 2D/Source/Flat8/fl8sply.c + 2D/Source/Flat8/fl8rect.c + 2D/Source/Flat8/fl8gfl8.c + 2D/Source/Flat8/fl8tl8.c + 2D/Source/Flat8/fl8p24.c + 2D/Source/Flat8/fl8g24.c + 2D/Source/Flat8/fl8ctp.c + 2D/Source/Flat8/fl8mscl.c + 2D/Source/Flat8/fl8lop.c + 2D/Source/Flat8/fl8ntrp2.c + 2D/Source/Flat8/fl8sub.c + 2D/Source/Flat8/fl8tpl.c + 2D/Source/Flat8/fl8wclin.h + 2D/Source/Flat8/fl8nl.c + 2D/Source/Flat8/fl8fltr2.c + 2D/Source/Flat8/fl8mono.c + 2D/Source/Flat8/fl8fl8.c + 2D/Source/Flat8/fl8chfl8.c + 2D/Source/Clip/clpply.c + 2D/Source/Clip/clplin.c + 2D/Source/Clip/clpclin.c + 2D/Source/Clip/clpltab.c + 2D/Source/Clip/clpf24.c + 2D/Source/Clip/clplin2.c + 2D/Source/Clip/clppoly.c + 2D/Source/Clip/clpslin.c + 2D/Source/Clip/clprect.c + 2D/Source/Clip/clpmono.c + 2D/Source/RSD/rsdcvt.c + 2D/Source/RSD/RSDUnpack.c + 2D/Source/string/chrsiz.c + 2D/Source/string/genchr.c + 2D/Source/string/genstr.c + 2D/Source/string/genuchr.c + 2D/Source/string/genustr.c + 2D/Source/string/strscl.c + 2D/Source/string/strsiz.c + 2D/Source/string/struscl.c + 2D/Source/string/strwrap.c +) + +set(GR_SRC + 2D/Source/GR/grd.c + 2D/Source/GR/grilin.c + 2D/Source/GR/grmalloc.c + 2D/Source/GR/grnull.c + 2D/Source/GR/gruilin.c +) + + +set(3D_SRC + 3D/Source/alloc.c + 3D/Source/Bitmap.c + 3D/Source/clip.c + 3D/Source/detail.c + 3D/Source/fov.c + 3D/Source/GlobalV.c + 3D/Source/instance.c + 3D/Source/interp.c + 3D/Source/light.c + 3D/Source/matrix.c + 3D/Source/points.c + 3D/Source/polygon.c + 3D/Source/slew.c + 3D/Source/tmap.c + 3D/Source/vector.c +) + +set(AFILE_SRC + AFILE/Source/afile.c + AFILE/Source/amov.c + AFILE/Source/compose.c + AFILE/Source/draw4x4.cpp + AFILE/Source/huffde.cpp + AFILE/Source/movie.c) + +set(DSTRUCT_SRC + DSTRUCT/Source/array.c + DSTRUCT/Source/hash.c + DSTRUCT/Source/pqueue.c + DSTRUCT/Source/rect.c +) + + +set(FIX_SRC + FIX/Source/f_exp.c + FIX/Source/fix.c + FIX/Source/fix_pow.c + FIX/Source/fix_sqrt.c + FIX/Source/MakeTables.c +) + +set(INPUT_SRC + INPUT/Source/kbcook.c + INPUT/Source/mouse.c + INPUT/Source/sdl_events.c +) + +set(LG_SRC + LG/Source/LOG/src/log.c + LG/Source/memall.c + LG/Source/stack.c + LG/Source/tmpalloc.c +) + +set(PALETTE_SRC + PALETTE/Source/palette.c +) + +set(RES_SRC + RES/Source/caseless.c + RES/Source/lzw.c + RES/Source/refacc.c + RES/Source/resacc.c + RES/Source/resbuild.c + RES/Source/res.c + RES/Source/resfile.c + RES/Source/resformat.c + RES/Source/resload.c + RES/Source/resmake.c + RES/Source/restypes.c +) + +set(RND_SRC + RND/Source/rnd.c +) + +set(SND_SRC + SND/Source/dig_init.c + SND/Source/dig_ops.c + SND/Source/master.c + SND/Source/mid_init.c + SND/Source/mid_ops.c + SND/Source/snd_util.c +) + +set(UI_SRC + UI/Source/curdrw.c + UI/Source/cursors.c + UI/Source/event.c + UI/Source/hotkey.c + UI/Source/region.c + UI/Source/slab.c + UI/Source/vmouse.c +) + +set(FIXPP_SRC + FIXPP/Source/fixpp.cpp +) + +set(EDMS_SRC + EDMS/Source/interfac.cc + EDMS/Source/collide.cc + EDMS/Source/intrsect.cc + EDMS/Source/globals.cc + EDMS/Source/phy_tool.cc + EDMS/Source/soliton.cc + EDMS/Source/MODELS/robot.cc + EDMS/Source/MODELS/pelface.cc + EDMS/Source/MODELS/ftl.cc + EDMS/Source/MODELS/pelvis.cc + EDMS/Source/MODELS/d_frame.cc + EDMS/Source/MODELS/d_f_face.cc + EDMS/Source/MODELS/ftlface.cc + EDMS/Source/MODELS/d_f_2.cc +) + +set(ADLMIDI_SRC + adlmidi/adlmidi_sequencer.cpp + adlmidi/adlmidi_bankmap.tcc + adlmidi/cvt_mus2mid.hpp + adlmidi/adlmidi_bankmap.h + adlmidi/midi_sequencer.hpp + adlmidi/chips/dosbox_opl3.h + adlmidi/chips/opl_chip_base.h + adlmidi/chips/nuked_opl3.h + adlmidi/chips/dosbox/dbopl.h + adlmidi/chips/dosbox/dbopl.cpp + adlmidi/chips/dosbox_opl3.cpp + adlmidi/chips/nuked_opl3_v174.cpp + adlmidi/chips/nuked/nukedopl3_174.c + adlmidi/chips/nuked/nukedopl3.c + adlmidi/chips/nuked/nukedopl3_174.h + adlmidi/chips/nuked/nukedopl3.h + adlmidi/chips/opl_chip_base.tcc + adlmidi/chips/nuked_opl3.cpp + adlmidi/chips/nuked_opl3_v174.h + adlmidi/wopl/wopl_file.c + adlmidi/wopl/wopl_file.h + adlmidi/include/adlmidi.hpp + adlmidi/include/adlmidi.h + adlmidi/adlmidi_private.hpp + adlmidi/file_reader.hpp + adlmidi/adlmidi_ptr.hpp + adlmidi/adlmidi_opl3.cpp + adlmidi/midi_sequencer_impl.hpp + adlmidi/adldata.hh + adlmidi/adlmidi_private.cpp + adlmidi/adlmidi_midiplay.cpp + adlmidi/adlmidi.cpp + adlmidi/cvt_xmi2mid.hpp + adlmidi/adlmidi_load.cpp + adlmidi/adldata.cpp + adlmidi/fraction.hpp + adlmidi/midi_sequencer.h +) + +set(VOX_SRC + VOX/Source/vox2d.c + VOX/Source/vox3d.c + VOX/Source/voxinit.c +) + +include_directories( + ${CMAKE_SOURCE_DIR}/src/GameSrc/Headers + ${CMAKE_SOURCE_DIR}/src/MacSrc + ${CMAKE_SOURCE_DIR}/src/Libraries/adlmidi/include + ${CMAKE_SOURCE_DIR}/src/Libraries/2D/Source/GR + ${CMAKE_SOURCE_DIR}/src/Libraries/2D/Source/Clip + ${CMAKE_SOURCE_DIR}/src/Libraries/2D/Source/Flat8 + ${CMAKE_SOURCE_DIR}/src/Libraries/2D/Source/Gen + ${CMAKE_SOURCE_DIR}/src/Libraries/2D/Source/RSD + ${CMAKE_SOURCE_DIR}/src/Libraries/2D/Source + ${CMAKE_SOURCE_DIR}/src/Libraries/3D/Source + ${CMAKE_SOURCE_DIR}/src/Libraries/AFILE/Source + ${CMAKE_SOURCE_DIR}/src/Libraries/DSTRUCT/Source + ${CMAKE_SOURCE_DIR}/src/Libraries/H + ${CMAKE_SOURCE_DIR}/src/Libraries/INPUT/Source + ${CMAKE_SOURCE_DIR}/src/Libraries/FIX/Source + ${CMAKE_SOURCE_DIR}/src/Libraries/LG/Source + ${CMAKE_SOURCE_DIR}/src/Libraries/LG/Source/LOG/src + ${CMAKE_SOURCE_DIR}/src/Libraries/RES/Source + ${CMAKE_SOURCE_DIR}/src/Libraries/FIXPP/Source + ${CMAKE_SOURCE_DIR}/src/Libraries/EDMS/Source + ${CMAKE_SOURCE_DIR}/src/Libraries/EDMS/Source/MODELS +) + +add_library(2D_LIB ${2D_SRC}) +target_link_libraries(2D_LIB LG_LIB) +add_library(GR_LIB ${GR_SRC}) +add_library(3D_LIB ${3D_SRC}) +target_link_libraries(3D_LIB m) +add_library(AFILE_LIB ${AFILE_SRC}) +target_link_libraries(AFILE_LIB 2D_LIB RES_LIB) +add_library(PALETTE_LIB ${PALETTE_SRC}) +add_library(DSTRUCT_LIB ${DSTRUCT_SRC}) +add_library(FIX_LIB ${FIX_SRC}) +target_link_libraries(FIX_LIB LG_LIB m) +add_library(INPUT_LIB ${INPUT_SRC}) +add_library(LG_LIB ${LG_SRC}) +add_library(RES_LIB ${RES_SRC}) +add_library(RND_LIB ${RND_SRC}) +add_library(UI_LIB ${UI_SRC}) +add_library(VOX_LIB ${VOX_SRC}) +add_library(FIXPP_LIB ${FIXPP_SRC}) +add_library(EDMS_LIB ${EDMS_SRC}) +add_library(ADLMIDI_LIB ${ADLMIDI_SRC}) diff --git a/engine/src/Libraries/DSTRUCT/Source/array.c b/engine/src/Libraries/DSTRUCT/Source/array.c new file mode 100644 index 0000000..9717c8c --- /dev/null +++ b/engine/src/Libraries/DSTRUCT/Source/array.c @@ -0,0 +1,105 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include +#include +#include "lg.h" +#include "array.h" + +//--------- +// Prototypes +//--------- +errtype array_grow(Array *a, int size); + + +//------------------------------------------------------ +// For Mac version: Use malloc and free. + +#define FREELIST_EMPTY -1 +#define FREELIST_NOTFREE -2 + +errtype array_init(Array* initme, int elemsize, int vecsize) +{ + if (elemsize == 0) return ERR_RANGE; + initme->elemsize = elemsize; + initme->vecsize = vecsize; + initme->fullness = 0; + initme->freehead = FREELIST_EMPTY; + initme->freevec = (int*)malloc(vecsize*sizeof(int)); + if (initme->freevec == NULL) return ERR_NOMEM; + initme->vec = (char*) malloc(elemsize*vecsize); + if (initme->vec == NULL) return ERR_NOMEM; + return OK; +} + +errtype array_grow(Array *a, int size) +{ + char* tmpvec; + int* tmplist; + if (size <= a->vecsize) return OK; + tmpvec = (char *)malloc(a->elemsize*size); + if (tmpvec == NULL) return ERR_NOMEM; + LG_memcpy(tmpvec,a->vec,a->vecsize*a->elemsize); + tmplist = (int *)malloc(size*sizeof(int)); + if (tmplist == NULL) return ERR_NOMEM; + LG_memcpy(tmplist,a->vec,a->vecsize*sizeof(int)); + free(a->vec); + free(a->freevec); + a->vecsize = size; + a->vec = tmpvec; + a->freevec = tmplist; + return OK; +} + +errtype array_newelem(Array* a, int* index) +{ + if (a->freehead != FREELIST_EMPTY) + { + *index = a->freehead; + a->freehead = a->freevec[*index]; + a->freevec[*index] = FREELIST_NOTFREE; + return OK; + } + if (a->fullness >= a->vecsize) + { + errtype err = array_grow(a,a->vecsize*2); + if (err != OK) return err; + } + *index = a->fullness++; + a->freevec[*index] = FREELIST_NOTFREE; + return OK; +} + + +errtype array_dropelem(Array* a, int index) +{ + if (index >= a->fullness || a->freevec[index] != FREELIST_NOTFREE) return OK; // already freed. + a->freevec[index] = a->freehead; + a->freehead = index; + return OK; +} + +errtype array_destroy(Array* a) +{ + a->elemsize = 0; + a->vecsize = 0; + a->freehead = FREELIST_EMPTY; + free(a->freevec); + free(a->vec); + return OK; +} diff --git a/engine/src/Libraries/DSTRUCT/Source/array.h b/engine/src/Libraries/DSTRUCT/Source/array.h new file mode 100644 index 0000000..d21af04 --- /dev/null +++ b/engine/src/Libraries/DSTRUCT/Source/array.h @@ -0,0 +1,95 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __ARRAY_H +#define __ARRAY_H + +/* + * $Source: n:/project/lib/src/dstruct/RCS/array.h $ + * $Revision: 1.1 $ + * $Author: mahk $ + * $Date: 1993/04/16 22:09:58 $ + * + * $Log: array.h $ + * Revision 1.1 1993/04/16 22:09:58 mahk + * Initial revision + * + * Revision 1.2 1993/03/22 15:23:41 mahk + * Added prototype for array_destroy. + * + * Revision 1.1 1993/03/22 15:21:34 mahk + * Initial revision + * + * + */ + +// Includes +#include "lg.h" + +// C Library Includes + +// System Library Includes +#include "error.h" + +// Master Game Includes + +// Game Library Includes + +// Game Object Includes + +// ====================== +// ARRAY TYPE +// ====================== +// Here is an implementation of a dynamically-growing array. +// It is intended for used in places where superfluous calls to +// Malloc are not desirable. + +// Defines + +typedef struct _array +{ + int elemsize; // How big is each array element + int vecsize; // How many elements in the vector + int fullness; // How many elements are used. + int freehead; // index to head of the free list. + int *freevec; // free list + char *vec; // the actual vector; +} Array; + + +// Prototypes + + +// Initialize an array. Fill in the structure, allocate the vector and free list. +errtype array_init(Array* toinit, int elemsize, int vecsize); + +// Find a place for a new element of the array, extending the array if necessary. +// returns the new index in *index +errtype array_newelem(Array* a, int* index); + +// Mark an element as unused and eligible for recycling by a subsequent +// array_newelem call. +errtype array_dropelem(Array* a, int index); + +// Destroy an array, deallocating its vec and freevec +errtype array_destroy(Array* a); + + +// Globals + +#endif //__ARRAY_H diff --git a/engine/src/Libraries/DSTRUCT/Source/hash.c b/engine/src/Libraries/DSTRUCT/Source/hash.c new file mode 100644 index 0000000..757b98f --- /dev/null +++ b/engine/src/Libraries/DSTRUCT/Source/hash.c @@ -0,0 +1,291 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/dstruct/RCS/hash.c $ + * $Revision: 1.5 $ + * $Author: mahk $ + * $Date: 1994/01/18 08:16:06 $ + * + * $Log: hash.c $ + * Revision 1.5 1994/01/18 08:16:06 mahk + * Added hash_copy + * + * Revision 1.4 1993/08/09 17:14:59 mahk + * Fixed the libdbg.h thing. + * + * Revision 1.3 1993/07/01 12:16:22 mahk + * changed log2 to hashlog2 to avoid name conflict + * + * Revision 1.2 1993/06/14 21:11:45 xemu + * step func + * + * Revision 1.1 1993/03/25 19:15:09 mahk + * Initial revision + * + * + */ + +#include +#include +#include "hash.h" + +//-------------------- +// Defines +//-------------------- +#define HASH_EMPTY 0 +#define HASH_TOMBSTONE 1 +#define HASH_FULL 2 + +#define INDEX_NOT_FOUND -1 + +#define FULLNESS_THRESHHOLD_PERCENT 80 + +#define ELEM(tbl,i) ((void*)((tbl)->vec + (i)*(tbl)->elemsize)) + +//-------------------- +// Prototypes +//-------------------- +int hashlog2(int x); +int expmod(int b, int e, uint m); +uchar is_fermat_prime(uint n, uint numtests); +static errtype grow(Hashtable* h, int newsize); + +//-------------------- +// Internal Functions +//-------------------- +int hashlog2(int x) +{ + if (x < 2) return 0; + return 1+hashlog2(x/2); +} + +int expmod(int b, int e, uint m) +{ + if (e == 0) return 1; + if (e%2 == 0) + { + int tmp = expmod(b,e/2,m); + return (tmp*tmp)%m; + } + else + { + int tmp = expmod(b,e-1,m); + return (b*tmp)%m; + } + +} + +uchar is_fermat_prime(uint n, uint numtests) +{ + int i; + if (n < 3) return FALSE; + for (i = 0; i < numtests; i++) + { + int a = rand()%(n-2) + 2; + if (expmod(a,n,n) != a) return FALSE; + } + return TRUE; +} + +errtype hash_init(Hashtable* h, int elemsize, int vecsize, Hashfunc hfunc, Equfunc efunc) +{ + int i; +// Spew(DSRC_DSTRUCT_Hash,("hash_init(%x,%d,%d,%x,%x)\n",h,elemsize,vecsize,hfunc,efunc)); + while(!is_fermat_prime(vecsize,2)) vecsize++; + h->elemsize = elemsize; + h->size = vecsize; + h->sizelog2 = hashlog2(vecsize); + h->fullness = 0; + h->hfunc = hfunc; + h->efunc = efunc; + h->statvec = (char*)malloc(vecsize); + if (h->statvec == NULL) return ERR_NOMEM; + for (i = 0; i < vecsize; i++) h->statvec[i] = HASH_EMPTY; + h->vec = (char*)malloc(elemsize*vecsize); + if (h->vec == NULL) return ERR_NOMEM; + return OK; +} + +errtype hash_copy(Hashtable* t, Hashtable* s) +{ + *t = *s; + t->statvec = malloc(t->size); + if (t->statvec == NULL) return ERR_NOMEM; + t->vec = malloc(t->elemsize*t->size); + if (t->vec == NULL) return ERR_NOMEM; + LG_memcpy(t->vec,s->vec,t->size*t->elemsize); + LG_memcpy(t->statvec,s->statvec,t->size); + return OK; +} + +static uchar find_elem(Hashtable* h, void* elem, int* idx) +{ + uchar found = FALSE; + int hash = h->hfunc(elem); + int index,j; + //Spew(DSRC_DSTRUCT_Hash,("find_elem(%x,%x,%x) hash is %d\n",h,elem,idx,hash)); + for (j = 0, index = hash%h->size; j < h->size && h->statvec[index] != HASH_EMPTY; + j++,index = (index + (1 << hash%h->sizelog2)) % h->size) + { + void* myelem = (void*) ELEM(h,index); + if (h->statvec[index] == HASH_FULL && h->efunc(elem,myelem) == 0) + { + found = TRUE; + break; + } + } + *idx = index; + //Spew(DSRC_DSTRUCT_Hash,("find_elem(): index is %d \n",index)); + return found; +} + +static int find_index(Hashtable* h, void* elem) +{ + int hash = h->hfunc(elem); + int j; + int index; +// Spew(DSRC_DSTRUCT_Hash,("find_index(%x,%x) hash is %d\n",h,elem,hash)); + for (j = 0, index = hash%h->size; j < h->size && h->statvec[index] == HASH_FULL; + j++,index = (index + (1 << hash%h->sizelog2)) % h->size) +// Spew(DSRC_DSTRUCT_Hash,("find_index(): found status %d\n",h->statvec[index])); + if (j >= h->size) index = INDEX_NOT_FOUND; +// Spew(DSRC_DSTRUCT_Hash,("find_index(): result is %d\n",index)); + return index; +} + +static errtype grow(Hashtable* h, int newsize) +{ + char* oldvec = h->vec; + char* oldstat = h->statvec; + char *newvec, *newstat; + int oldsize = h->size; + int i; +// Spew(DSRC_DSTRUCT_Hash,("grow(%x,%d)\n",h,newsize)); + for (;!is_fermat_prime(newsize,2);newsize++); + newvec = malloc(newsize*h->elemsize); + if (newvec == NULL) return ERR_NOMEM; + newstat = malloc(newsize); + if (newstat == NULL) + { + free (newvec); + return ERR_NOMEM; + } + h->vec = newvec; + h->statvec = newstat; + h->size = newsize; + h->sizelog2 = hashlog2(newsize); + h->fullness = 0; + for (i = 0; i < newsize; i++) newstat[i] = HASH_EMPTY; + for (i = 0; i < oldsize; i++) + { + if (oldstat[i] == HASH_FULL) + { + hash_insert(h,(void*)(oldvec+i*h->elemsize)); + } + } + free(oldvec); + free(oldstat); + return OK; +} + +errtype hash_set(Hashtable* h, void* elem) +{ + int i; +// Spew(DSRC_DSTRUCT_Hash,("hash_set(%x,%x)\n",h,elem)); + if (h->fullness*100/h->size > FULLNESS_THRESHHOLD_PERCENT) + grow(h,h->size*2); + if (!find_elem(h,elem,&i)) + i = find_index(h,elem); + LG_memcpy(ELEM(h,i),elem,h->elemsize); + h->statvec[i] = HASH_FULL; + h->fullness++; + return OK; +} + +errtype hash_insert(Hashtable* h, void* elem) +{ + int i; +// Spew(DSRC_DSTRUCT_Hash,("hash_insert(%x,%x)\n",h,elem)); + if (h->fullness*100/h->size > FULLNESS_THRESHHOLD_PERCENT) + grow(h,h->size*2); + i = find_index(h,elem); + LG_memcpy(ELEM(h,i),elem,h->elemsize); + h->statvec[i] = HASH_FULL; + h->fullness++; + return OK; +} + + +errtype hash_delete(Hashtable* h, void* elem) +{ + int i; +// Spew(DSRC_DSTRUCT_Hash,("hash_delete(%x,%x)\n",h,elem)); + if (find_elem(h,elem,&i)) + { + h->statvec[i] = HASH_TOMBSTONE; + return OK; + } + return ERR_NOEFFECT; +} + + +errtype hash_lookup(Hashtable* h, void* elem, void** result) +{ + int i; +// Spew(DSRC_DSTRUCT_Hash,("hash_lookup(%x,%x,%x)\n",h,elem,result)); + if (find_elem(h,elem,&i)) + { + *result = ELEM(h,i); + } + else *result = NULL; +// Spew(DSRC_DSTRUCT_Hash,("hash_lookup(): value is %x\n",*result)); + return OK; +} + +errtype hash_iter(Hashtable* h, HashIterFunc ifunc, void* data) +{ + int i; + for (i = 0; i < h->size; i++) + if (h->statvec[i] == HASH_FULL) + if (ifunc(ELEM(h,i),data)) + break; + return OK; +} + +errtype hash_step(Hashtable *h, void **result, int *index) +{ + while ((h->statvec[*index] != HASH_FULL) && (*index < h->size)) + (*index)++; + if (*index == h->size) + *result = NULL; + else + *result = ELEM(h,*index); + (*index)++; + return(OK); +} + +errtype hash_destroy(Hashtable* h) +{ + h->size = 0; + h->fullness = 0; + free(h->statvec); + free(h->vec); + return OK; +} + diff --git a/engine/src/Libraries/DSTRUCT/Source/hash.h b/engine/src/Libraries/DSTRUCT/Source/hash.h new file mode 100644 index 0000000..98a4486 --- /dev/null +++ b/engine/src/Libraries/DSTRUCT/Source/hash.h @@ -0,0 +1,114 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef _HASH_H +#define _HASH_H +#include "lg.h" +#include "error.h" + +/* + * $Source: n:/project/lib/src/dstruct/RCS/hash.h $ + * $Revision: 1.5 $ + * $Author: mahk $ + * $Date: 1994/01/18 08:16:01 $ + * + * $Log: hash.h $ + * Revision 1.5 1994/01/18 08:16:01 mahk + * Added hash_copy + * + * Revision 1.4 1993/08/06 13:46:46 mahk + * changed libdbg.h to _dstruct.h + * + * Revision 1.3 1993/06/14 21:11:50 xemu + * step func + * + * Revision 1.2 1993/03/25 19:55:25 mahk + * Added rep exposure warning. + * + * Revision 1.1 1993/03/25 19:20:09 mahk + * Initial revision + * + * + */ + +// Equivalence function, returns values are as per strcmp +// i.e. 0 if data1 == data2, >0 if data1 > data2, <0 if data1 < data2. +typedef int (*Equfunc)(void* data1,void* data2); + +// hashing function: data1 == data2 ==> hash(data1) == hash(data2) +typedef int (*Hashfunc)(void* data); + + + +typedef struct _hashtable +{ + int size; + int sizelog2; + int elemsize; + int fullness; + Equfunc efunc; + Hashfunc hfunc; + char *statvec; + char *vec; +} Hashtable; + + + +errtype hash_init(Hashtable* h, int elemsize, int vecsize, Hashfunc hfunc, Equfunc efunc); +// initialize a hashtable with the specified hashfunc and equfunc, using elemsize as +// the size of an element, and using vecsize as the initial table size. + +errtype hash_set(Hashtable* h,void* elem); +// insert an element into a hashtable, overwriting any element +// that is equal to it. + +errtype hash_insert(Hashtable* h,void* elem); +// REQUIRES there is no member of h equal to "elem". +// inserts "elem" into hashtable h. Faster than hash_set. + + + +errtype hash_lookup(Hashtable* h, void* elem, void** result); +// Looks up elem in the hashtable, sets *result to point to the +// element in h which is equal to elem. Or NULL if no such element +// exists. +// WARNING WARNING DANGER WILL ROBINSON. HEINOUS REP EXPOSURE. +// MODIFY **RESULT AT YOUR OWN PERIL + +errtype hash_delete(Hashtable* h, void* elem); +// Find and remove the element in h which is equal to elem, +// or do nothing if no such element exists. + + +typedef uchar (*HashIterFunc)(void* elem, void* data); + +errtype hash_iter(Hashtable* h, HashIterFunc ifunc, void* data); +// Applies ifunc(elem,data) to every element of h, one at a time, until +// ifunc returns true. + +errtype hash_copy(Hashtable* t, Hashtable* s); +// Initializes t to be a copy of s + +errtype hash_step(Hashtable *h, void **result, int *index); +// Will step through a hashtable, returning the elements one at a time. + +errtype hash_destroy(Hashtable* h); +// Destroys hashtable h. Does not free h itself, but frees +// subordinate data structures. + +#endif // _HASH_H diff --git a/engine/src/Libraries/DSTRUCT/Source/pqueue.c b/engine/src/Libraries/DSTRUCT/Source/pqueue.c new file mode 100644 index 0000000..ef18d0b --- /dev/null +++ b/engine/src/Libraries/DSTRUCT/Source/pqueue.c @@ -0,0 +1,238 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/dstruct/RCS/pqueue.c $ + * $Revision: 1.1 $ + * $Author: mahk $ + * $Date: 1993/08/09 20:30:58 $ + * + * $Log: pqueue.c $ + * Revision 1.1 1993/08/09 20:30:58 mahk + * Initial revision + * + * + */ + +//#include +#include +#include +#include "pqueue.h" + +#include + +// ------- +// DEFINES +// ------- +#define LCHILD(i) (2*(i)+2) +#define RCHILD(i) (2*(i)+1) +#define PARENT(i) (((i)-1)/2) +#define NTH(pq,n) ((void*)(((pq)->vec)+(n)*((pq)->elemsize))) +#define LESS(pq,i1,i2) ((pq)->comp(NTH(pq,i1),NTH(pq,i2)) < 0) +#define NULL_CHILD 0xFFFFFFFF + +// ------- +// GLOBALS +// ------- +static char* swap_buffer = NULL; +static int swap_bufsize = 0; + +// ------- +// PROTOS +// ------- +void swapelems(PQueue* q,int i, int j); +void re_heapify(PQueue *q); +void double_re_heapify(PQueue *q, int head); + +// --------- +// INTERNALS +// --------- +void swapelems(PQueue* q,int i, int j) +{ + LG_memcpy(swap_buffer,NTH(q,i),q->elemsize); + LG_memcpy(NTH(q,i),NTH(q,j),q->elemsize); + LG_memcpy(NTH(q,j),swap_buffer,q->elemsize); +} + +void re_heapify(PQueue *q) +{ + uint head = 0; + while (head < q->fullness) + { + uint lchild = LCHILD(head); + uint rchild = RCHILD(head); + uint minchild = NULL_CHILD; + if (rchild >= q->fullness) + minchild = lchild; + if (lchild >= q->fullness) + minchild = rchild; + if (minchild == NULL_CHILD) { + if (LESS(q,lchild,rchild)) + { + minchild = lchild; + } + else + { + minchild = rchild; + } + } + if (minchild < q->fullness && LESS(q,minchild,head)) + { + swapelems(q,head,minchild); + head = minchild; + } + else break; + } +} + + +void double_re_heapify(PQueue *q, int head) +{ + uint lchild = LCHILD(head); + uint rchild = RCHILD(head); + uint minchild = NULL_CHILD; + uint maxchild = NULL_CHILD; + if (rchild >= q->fullness) + minchild = lchild; + if (lchild >= q->fullness) + minchild = rchild; + if (minchild == NULL_CHILD) { + if (LESS(q,lchild,rchild)) + { + minchild = lchild; + maxchild = rchild; + } + else + { + minchild = rchild; + maxchild = lchild; + } + } + if (minchild < q->fullness && LESS(q,minchild,head)) + { + swapelems(q,head,minchild); + double_re_heapify(q,minchild); + if (maxchild < q->fullness) + double_re_heapify(q,maxchild); + } +} + +// --------- +// EXTERNALS +// --------- + +errtype pqueue_init(PQueue* q, int size, int elemsize, QueueCompare comp, uchar grow) +{ + if (size < 1) return ERR_RANGE; + q->vec = malloc(elemsize*size); + if (q->vec == NULL) return ERR_NOMEM; + if (elemsize > swap_bufsize) + { + if (swap_buffer == NULL) + swap_buffer = malloc(elemsize); + else + { + free(swap_buffer); + swap_buffer = malloc(elemsize); + } + swap_bufsize = elemsize; + if (swap_buffer == NULL) return ERR_NOMEM; + } + q->size = size; + q->fullness = 0; + q->elemsize = elemsize; + q->comp = comp; + q->grow = grow; + return OK; +} + +errtype pqueue_insert(PQueue* q, void* elem) +{ + int n; + if (!q->grow && q->fullness >= q->size) + return ERR_DOVERFLOW; + while (q->fullness >= q->size) + { + q->vec = realloc(q->vec, q->elemsize*q->size*2); + q->size*=2; + if (q->vec == NULL) return ERR_NOMEM; + } + n = q->fullness++; + memcpy(NTH(q,n),elem,q->elemsize); + while(n > 0) + { + if (LESS(q,PARENT(n),n)) + break; + swapelems(q,n,PARENT(n)); + n = PARENT(n); + } + return OK; +} + +errtype pqueue_extract(PQueue* q, void* elem) +{ + if (q->fullness == 0) return ERR_DUNDERFLOW; + LG_memcpy(elem,NTH(q,0),q->elemsize); + LG_memcpy(NTH(q,0),NTH(q,q->fullness-1),q->elemsize); + q->fullness--; + re_heapify(q); + return OK; +} + +errtype pqueue_least(PQueue* q, void* elem) +{ + if (q->fullness == 0) return ERR_DUNDERFLOW; + LG_memcpy(elem,NTH(q,0),q->elemsize); + return OK; +} + +errtype pqueue_write(PQueue* q, FILE *fd, void (*writefunc)(FILE *fd, void* elem)) +{ + int i; + fwrite((char*)q,1,sizeof(PQueue), fd); + for(i = 0; i < q->fullness; i++) + { + if (writefunc != NULL) + writefunc(fd,NTH(q,i)); + else fwrite((char*)NTH(q,i),1,q->elemsize, fd); + } + return OK; +} + +errtype pqueue_read(PQueue* q, FILE *fd, void (*readfunc)(FILE *fd, void* elem)) +{ + int i; + fread((char*)q,1,sizeof(PQueue), fd); + if (q->grow) q->size = q->fullness; + q->vec = malloc(q->size*q->elemsize); + if (q->vec == NULL) return ERR_NOMEM; + for(i = 0; i < q->fullness; i++) + { + if (readfunc != NULL) + readfunc(fd,NTH(q,i)); + else fread((char*)NTH(q,i),1,q->elemsize, fd); + } + return OK; +} + +errtype pqueue_destroy(PQueue* q) +{ + free(q->vec); + q->fullness = 0; + return OK; +} diff --git a/engine/src/Libraries/DSTRUCT/Source/pqueue.h b/engine/src/Libraries/DSTRUCT/Source/pqueue.h new file mode 100644 index 0000000..3932d15 --- /dev/null +++ b/engine/src/Libraries/DSTRUCT/Source/pqueue.h @@ -0,0 +1,101 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __PQUEUE_H +#define __PQUEUE_H + +/* + * $Source: n:/project/lib/src/dstruct/RCS/pqueue.h $ + * $Revision: 1.1 $ + * $Author: mahk $ + * $Date: 1993/08/09 20:31:11 $ + * + * $Log: pqueue.h $ + * Revision 1.1 1993/08/09 20:31:11 mahk + * Initial revision + * + * + */ + +// ----------------------------------- +// Priority Queue Abstraction +// ----------------------------------- +/* Herein lies a binary heap implementation of a priority queue + The queue can have elements of any size, as the client specifies + the element size and comparison function. */ + + + + +// Includes +#include "lg.h" // every file should have this +#include "error.h" +#include +#include + +// Defines +// Comparson function, works like strcmp +typedef int (*QueueCompare)(void* elem1, void* elem2); + +#pragma pack(push,2) +typedef struct _pqueue +{ + int32_t size; + int32_t fullness; + int32_t elemsize; + uchar grow; + char* vec; + QueueCompare comp; +} PQueue; + +// Prototypes +errtype pqueue_init(PQueue* q, int size, int elemsize, QueueCompare comp, uchar grow); +// Initializes a Priority queue to a particular size, with a +// particular element size and comparison function. + +errtype pqueue_insert(PQueue* q, void* elem); +// Insert an element into the queue (log time) + +errtype pqueue_extract(PQueue* q, void* elem); +// Copies the least element in the queue into *elem, +// and removes that element. (log time) + +errtype pqueue_least(PQueue* q, void* elem); +// Copies the least element into *elem, but does not +// remove it. (constant time) + +errtype pqueue_write(PQueue* q,FILE *fd,void (*writefunc)(FILE *fd,void* elem)); +// Writes out a queue to file number fd, calling writefunc to write out each element. +// If writefunc is NULL, simply writes the literal data in each element. + +errtype pqueue_read(PQueue* q, FILE *fd, void (*readfunc)(FILE *fd, void* elem)); +// Reads in a queue from file number fd, calling readfunc to read each element. +// If readfunc is NULL, reads each element literally. + +errtype pqueue_destroy(PQueue* q); +// Destroys a priority queue. + + + + + +// Globals + +#pragma pack(pop) + +#endif // __PQUEUE_H diff --git a/engine/src/Libraries/DSTRUCT/Source/rect.c b/engine/src/Libraries/DSTRUCT/Source/rect.c new file mode 100644 index 0000000..dfd68ba --- /dev/null +++ b/engine/src/Libraries/DSTRUCT/Source/rect.c @@ -0,0 +1,186 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Rect.C Rectangle-handling routines. +// Rex E. Bradford (REX) +// +// This module implements a bunch of rectangle-manipulation +// routines. Rectangles are defined such that the upper-left +// point is inside the rectangle, and the lower-right point is +// outside. +// +// Thus, if the rect.ul.x == rect.lr.x OR rect.ul.y == rect.lr.y, +// the rectangle is empty. +// +// Also, a rect's width = rect.lr.x - rect.ul.x, +// and its height = rect.lr.y - rect.ul.y. +/* +* $Header: n:/project/lib/src/dstruct/RCS/rect.c 1.3 1994/04/05 04:04:13 dc Exp $ +* $log$ +*/ +#include +#include "rect.h" + +// -------------------------------------------------------- +// +// RectTestSect() tests if two rectangles intersect. +// +// pr1 = ptr to 1st rectangle +// pr2 = ptr to 2nd rectangle +// +// returns: TRUE if rectangles intersect, FALSE if disjoint. + +int RectTestSect(LGRect *pr1, LGRect *pr2) +{ + return(RECT_TEST_SECT(pr1,pr2)); +} + +// -------------------------------------------------------- +// +// RectSect() finds intersection of two rects. +// +// pr1 = ptr to 1st rectangle +// pr2 = ptr to 2nd rectangle +// prsect = ptr to rectangle, filled with intersection of the +// two rects (caution: if no intersection, this rect is undefined!) +// +// returns: TRUE IF rectangles intersect, FALSE if disjoint (in this +// case, *prsect is undefined) + +int RectSect(LGRect *pr1, LGRect *pr2, LGRect *prsect) +{ + if (!RECT_TEST_SECT(pr1, pr2)) + return(false); + + prsect->ul.x = pr1->ul.x > pr2->ul.x ? pr1->ul.x : pr2->ul.x; + prsect->lr.x = pr1->lr.x < pr2->lr.x ? pr1->lr.x : pr2->lr.x; + prsect->ul.y = pr1->ul.y > pr2->ul.y ? pr1->ul.y : pr2->ul.y; + prsect->lr.y = pr1->lr.y < pr2->lr.y ? pr1->lr.y : pr2->lr.y; + + return(true); +} + +// --------------------------------------------------------- +// +// RectUnion() finds union of two rects. +// +// pr1 = ptr to 1st rectangle +// pr2 = ptr to 2nd rectangle +// prunion = ptr to rectangle, filled with union of the two rects + +void RectUnion(LGRect *pr1, LGRect *pr2, LGRect *prunion) +{ + RECT_UNION(pr1, pr2, prunion); +} + +// --------------------------------------------------------- +// +// RectEncloses() tests whether first rect fully encloses second. +// +// pr1 = ptr to 1st rectangle +// pr2 = ptr to 2nd rectangle +// +// returns: TRUE if *pr1 encloses *pr2, FALSE otherwise + +int RectEncloses(LGRect *pr1, LGRect *pr2) +{ + return(RECT_ENCLOSES(pr1, pr2)); +} + +// --------------------------------------------------------- +// +// RectTestPt() tests whether point is inside rect. +// +// prect = ptr to rectangle +// pt = point to be tested +// +// returns: TRUE if point is within rectangle, FALSE if outside + +int RectTestPt(LGRect *prect, LGPoint pt) +{ + return(RECT_TEST_PT(prect, pt)); +} + +// --------------------------------------------------------- +// +// RectMove() moves a rectangle by a delta. +// +// pr = ptr to rectangle +// delta = point to move rectangle by + +void RectMove(LGRect *pr, LGPoint delta) +{ + RECT_MOVE(pr, delta); +} + +// --------------------------------------------------------- +// +// RectOffsettedRect() creates a rectangle, offsetted from another. +// +// pr = ptr to original rect +// delta = pt to offset by +// proff = rectangle to fill in with offsetted rect + +void RectOffsettedRect(LGRect *pr, LGPoint delta, LGRect *proff) +{ + RECT_OFFSETTED_RECT(pr, delta, proff); +} + +// --------------------------------------------------------- +// +// RectClipCode() calculates 4-bit clipcode for pt vs. rect. +// +// prect = ptr to rectangle +// pt = point to be tested +// +// Returns: a 4-bit clipcode, bits set as follows: +// +// 000x: set to 1 if pt.x < rect.ul.x +// 00x0: set to 1 if pt.x >= rect.lr.x +// 0x00: set to 1 if pt.y < rect.ul.y +// x000: set to 1 if pt.y >= rect.lr.y +// +// thus set to 0 if point is inside rect, although a cheaper test can be +// done (via RectTestPt()). + +int RectClipCode(LGRect *prect, LGPoint pt) +{ + short flag; + + flag = 0; + if (pt.x < prect->ul.x) + flag = 1; + if (pt.x >= prect->lr.x) + flag |= 2; + if (pt.y < prect->ul.y) + flag |= 4; + if (pt.y >= prect->lr.y) + flag |= 8; + + return(flag); +} + +// --------------------------------------------------------- +LGPoint MakePoint(short x, short y) +{ + LGPoint pt; + + pt.x = x; + pt.y = y; + return (pt); +} diff --git a/engine/src/Libraries/DSTRUCT/Source/rect.h b/engine/src/Libraries/DSTRUCT/Source/rect.h new file mode 100644 index 0000000..e8fd421 --- /dev/null +++ b/engine/src/Libraries/DSTRUCT/Source/rect.h @@ -0,0 +1,164 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Rect.H Rectangle routines header file +// Rex E. Bradford (REX) +/* +* $Header: n:/project/lib/src/dstruct/RCS/rect.h 1.5 1994/04/05 11:02:15 rex Exp $ +* $Log: rect.h $ + * Revision 1.5 1994/04/05 11:02:15 rex + * One should endeavor to spend less time writing diatribes about the compiler,. + * and more time ensuring the veracity of one's code. + * + * Revision 1.4 1994/04/05 04:04:24 dc + * how about a make point that isnt a function and works better and so on.... + * + * Revision 1.3 1993/11/08 18:53:28 mahk + * WHOOOPS + * + * Revision 1.2 1993/11/08 18:33:47 mahk + * Added RECT_FILL and MakePoint + * + * Revision 1.1 1993/04/22 13:06:17 rex + * Initial revision + * + * Revision 1.2 1993/04/19 18:35:01 rex + * Added macro versions of most functions + * + * Revision 1.1 1992/08/31 17:03:04 unknown + * Initial revision + * +*/ + + +#ifndef RECT_H +#define RECT_H + +// Here are the Point and LGRect structs + +typedef struct { + short x; + short y; +} LGPoint; + +typedef struct { + LGPoint ul; + LGPoint lr; +} LGRect; + +// Point macros + +#define PointsEqual(p1,p2) (*(int32_t*)(&(p1)) == *(int32_t*)(&(p2))) +#define PointSetNull(p) do {(p).x = -1; (p).y = -1;} while (0); +#define PointCheckNull(p) ((p).x == -1 && (p).y == -1) + +// LGRect macros: get width & height + +#define RectWidth(pr) ((pr)->lr.x - (pr)->ul.x) +#define RectHeight(pr) ((pr)->lr.y - (pr)->ul.y) + +// Unwrap macros + +#define RECT_UNWRAP(pr) ((pr)->ul.x),((pr)->ul.y),((pr)->lr.x),((pr)->lr.y) +#define PT_UNWRAP(pt) ((pt).x),((pt).y) + +// These macros are faster, fatter versions of their function counterparts + +#define RECT_TEST_SECT(pr1,pr2) ( \ + ((pr1)->ul.y < (pr2)->lr.y) && \ + ((pr1)->lr.y > (pr2)->ul.y) && \ + ((pr1)->ul.x < (pr2)->lr.x) && \ + ((pr1)->lr.x > (pr2)->ul.x)) + +#define RECT_UNION(pr1,pr2,prunion) { \ + (prunion)->ul.x = (pr1)->ul.x < (pr2)->ul.x ? (pr1)->ul.x : (pr2)->ul.x; \ + (prunion)->lr.x = (pr1)->lr.x > (pr2)->lr.x ? (pr1)->lr.x : (pr2)->lr.x; \ + (prunion)->ul.y = (pr1)->ul.y < (pr2)->ul.y ? (pr1)->ul.y : (pr2)->ul.y; \ + (prunion)->lr.y = (pr1)->lr.y > (pr2)->lr.y ? (pr1)->lr.y : (pr2)->lr.y; \ + } + +#define RECT_ENCLOSES(pr1,pr2) ( \ + ((pr1)->ul.y <= (pr2)->ul.y) && \ + ((pr1)->lr.y >= (pr2)->lr.y) && \ + ((pr1)->ul.x <= (pr2)->ul.x) && \ + ((pr1)->lr.x >= (pr2)->lr.x)) + +#define RECT_TEST_PT(prect,pt) ( \ + ((pt).y >= (prect)->ul.y) && ((pt).y < (prect)->lr.y) && \ + ((pt).x >= (prect)->ul.x) && ((pt).x < (prect)->lr.x)) + +#define RECT_MOVE(prect,pt) { \ + (prect)->ul.x += pt.x; \ + (prect)->ul.y += pt.y; \ + (prect)->lr.x += pt.x; \ + (prect)->lr.y += pt.y; \ + } + +#define RECT_OFFSETTED_RECT(pr1,pt,proff) { \ + (proff)->ul.x = (pr1)->ul.x + (pt).x; \ + (proff)->ul.y = (pr1)->ul.y + (pt).y; \ + (proff)->lr.x = (pr1)->lr.x + (pt).x; \ + (proff)->lr.y = (pr1)->lr.y + (pt).y; \ + } + +#define RECT_FILL(pr,x1,y1,x2,y2) \ + { \ + (pr)->ul.x = (x1); \ + (pr)->ul.y = (y1); \ + (pr)->lr.x = (x2); \ + (pr)->lr.y = (y2); \ + } + +// These are the functional versions of the above macros + +int RectTestSect(LGRect *pr1, LGRect *pr2); +void RectUnion(LGRect *pr1, LGRect *pr2, LGRect *prunion); +int RectEncloses(LGRect *pr1, LGRect *pr2); +int RectTestPt(LGRect *prect, LGPoint pt); +void RectMove(LGRect *pr, LGPoint delta); +void RectOffsettedRect(LGRect *pr, LGPoint delta, LGRect *proff); + +// These functions have no macro counterparts +int RectSect(LGRect *pr1, LGRect *pr2, LGRect *prsect); +int RectClipCode(LGRect *prect, LGPoint pt); + +// guess why this isnt a macro // hah, you cant +//Point MakePoint(short x, short y); // Guess what this does. +//#define MakePoint(x,y) (Point)(((ushort)y<<16)+((ushort)x)) +// oh, doug is mocked, you cant cast to a non-scaler type +LGPoint MakePoint(short x, short y); + +/* +// take this, note ax and bx passed but use whole thing... oooooh +Point MakePointInline(ushort x, ushort y); +#pragma aux MakePointInline = \ + "shl ebx,10H" \ + "and eax,0000ffffH" \ + "add eax,ebx" \ + parm [ax] [bx] \ + modify [eax ebx]; +// and this +#define MakePoint(x,y) MakePointInline((ushort)x,(ushort)y) +// curse you +// note i had to specify ax and bx even though i dont care what is really used +// we would like to say [ax bx cx dx] and then have the code use arg1 and arg2 +// however, we cant, because there is no way of doing that, because lifeispain +// so the compiler generates things like mov eax,ebx;mov ebx,ecx;code as above +// which is dumb, since we could do the above with bx and cx just as well. ick +*/ +#endif diff --git a/engine/src/Libraries/DSTRUCT/Source/slist.h b/engine/src/Libraries/DSTRUCT/Source/slist.h new file mode 100644 index 0000000..7dd6bc2 --- /dev/null +++ b/engine/src/Libraries/DSTRUCT/Source/slist.h @@ -0,0 +1,89 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Slist.H Singly-linked list header file +// Rex E. Bradford (REX) +/* +* $Header: n:/project/lib/src/dstruct/RCS/slist.h 1.1 1993/05/03 10:53:29 rex Exp $ +* $Log: slist.h $ + * Revision 1.1 1993/05/03 10:53:29 rex + * Initial revision + * +*/ + +#ifndef SLIST_H +#define SLIST_H + +//#include "types.h" + +// -------------------------------------------------------------- + +// Slist node + +typedef struct _slist { + struct _slist *psnext; // ptr to next node or NULL if last one + // real data follows, here +} slist; + +// Slist header + +typedef struct _slist_head { + struct _slist *psnext; // ptr to 1st item in list +} slist_head; + +// Initialize an slist header (must be done before use) + +#define slist_init(pslh) { (pslh)->psnext = NULL; } + +// Add a new slist node to head of list + +#define slist_add_head(pslh,psl) { \ + (psl)->psnext = slist_head(pslh); \ + (pslh)->psnext = (slist *) psl; \ + } + +// Insert after specified node + +#define slist_insert_after(psl,pnode) { \ + (psl)->psnext = (pnode)->psnext; \ + (pnode)->psnext = (slist *) psl; \ + } + +// Remove node (must specify prior node) + +#define slist_remove(psl,pslbefore) { (pslbefore)->psnext = (psl)->psnext; } + +// Get ptr to head slist node + +#define slist_head(pslh) (slist *)((pslh)->psnext) + +// Determine if list empty + +#define slist_empty(pslh) (slist_head(pslh) == NULL) + +// Get next node + +#define slist_next(psl) (slist *)((psl)->psnext) // get ptr to next node + +// Iterate across all items + +#define forallinslist(listtype,pslh,psl) for (psl = \ + (listtype *)slist_head(pslh); psl != NULL; psl = (listtype *)slist_next(psl)) + + +#endif diff --git a/engine/src/Libraries/EDMS/Source/MODELS/d_f_2.cc b/engine/src/Libraries/EDMS/Source/MODELS/d_f_2.cc new file mode 100644 index 0000000..2a60cd1 --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/MODELS/d_f_2.cc @@ -0,0 +1,292 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Mechanical model for frame... +// ============================= + +#include "edms_int.h" +#include "edms_mod.h" //State and such... + +// Auto-alignment... +// ----------------- +#include "ss_flet.h" + +// Utilities... +// ============ +static Q n = 0, r = 0, e = 0, mag = 0, roll_delta = 0, kappa = 0, delta = 0, sin_wheel, cos_wheel; + +static Q X[3], XD[3], Z[3], FW[3], D[3]; + +int32_t counter, dummy1, dummy2; + +// Orientation... +// -------------- +static Q e0, e1, e2, e3, ed0, ed1, ed2, ed3; + +// Terrain returns... +// ------------------ +static Q B_C_return[3]; +static Q BC_test = 0; + +// Go for it, just go for it! +// ========================== +void dirac_mechanicals(int object, Q F[3], Q T[3]) { + + void mech_globalize(Q &, Q &, Q &), mech_localize(Q &, Q &, Q &); + + void get_boundary_conditions(int32_t object, Q raduis, Q position[3], Q derivitaves[3]); + + Q kappa, delta, mechanical_drag; + + Q *sc; + + // The points in question, note that the 4th column is steerage... + // =============================================================== + Q structure[6][4] = {{0, 0, 0, 0}, {0, .1, 0, 0}, {-.1, 0, 0, 0}, {0, -.1, 0, 0}, {0, 0, .1, 0}, {0, 0, -.1, 0}}; + + // Get the orientation... + // ---------------------- + e0 = A[object][3][0]; + e1 = A[object][4][0]; + e2 = A[object][5][0]; + e3 = A[object][6][0]; + ed0 = A[object][3][1]; + ed1 = A[object][4][1]; + ed2 = A[object][5][1]; + ed3 = A[object][6][1]; + + // From the actual model... + // ------------------------ + Q beta_dot = 2 * (e0 * ed1 + e3 * ed2 - e2 * ed3 - e1 * ed0); + Q alpha_dot = 2 * (-e3 * ed1 + e0 * ed2 + e1 * ed3 - e2 * ed0); + Q gamma_dot = 2 * (e2 * ed1 - e1 * ed2 + e0 * ed3 - e3 * ed0); + + // Steering... + // ----------- + sincos(0 /*I[object][xxx]*/, &sin_wheel, &cos_wheel); + + int32_t count = 0; + + // Check every @#!$ point... + // ========================= + // for ( counter = 0; counter < 1/*6*/; counter++ ) { + + sc = structure[counter]; // Isn't this clever? + + // Take the structure point and find the terrain intersection... + // ------------------------------------------------------------- + // X[0] = sc[0]; + // X[1] = sc[1]; + // X[2] = sc[2]; + // + // mech_globalize( X[0], X[1], X[2] ); + + X[0] = A[object][0][0]; + X[1] = A[object][1][0]; + X[2] = A[object][2][0]; + + // Now find the moments of the structure elements... + // ------------------------------------------------- + // XD[0] = -XPT_0( sc ); //Fucking angular velocity... + // XD[1] = -XPT_1( sc ); + // XD[2] = -XPT_2( sc ); + + // mech_globalize( XD[0], XD[1], XD[2] ); //Need it at home... + + // Notice notice notice... + // ----------------------- + XD[0] = A[object][0][1]; + XD[1] = A[object][1][1]; + XD[2] = A[object][2][1]; + + B_C_return[0] = B_C_return[1] = B_C_return[2] = 0; + + // Get the terrain information, and project it to useful axes... + // ------------------------------------------------------------- + get_boundary_conditions(object, .15, X, XD); + + FW[0] = B_C_return[0]; // global... + FW[1] = B_C_return[1]; + FW[2] = B_C_return[2]; + + mech_localize(FW[0], FW[1], FW[2]); // now local... + + // Wheels or drag? + // --------------- + // roll_delta = 0;//-( I[object][25] ); + + D[0] = 0; // roll_delta*XD[0]; + D[1] = 0; // roll_delta*XD[1]; + D[2] = 0; // roll_delta*XD[2]; //Oleo damping... + + mech_localize(D[0], D[1], D[2]); + + // Steerable... + // ------------ + // if ( sc[3] == 2 ) { + // D[0] *= sin_wheel; + // D[1] *= cos_wheel; + // } + + // Not... + // ------ + // if ( sc[3] == 1 ) D[0] *= .07; //X direction wheels... + + // D[0] *= .5; + // Z[0] = FW[0] + D[0]; //Temp for torques... + // Z[1] = FW[1] + D[1]; + // Z[2] = FW[2] + D[2]; + + F[0] += FW[0]; // This is local... + F[1] += FW[1]; + F[2] += FW[2]; + + // T[0] -= XP_0( sc, Z ); //Beta, + // T[1] -= XP_1( sc, Z ); //Alpha, + // T[2] -= XP_2( sc, Z ); //Gamma... + + // PRINT3D( T ) + // PRINT3D( F ) + + // Auto alignment... + // ----------------- + if (!(ss_edms_bcd_flags & SS_BCD_CURR_ON)) { + // if ( (EDMS_BCD < 10) || (EDMS_BCD > 27) ) { + + if (FW[0] > 10) + FW[0] = 10; + if (FW[1] > 10) + FW[1] = 10; + if (FW[0] < -10) + FW[0] = -10; + if (FW[1] < -10) + FW[1] = -10; + + T[0] += .6 * FW[0]; + T[0] *= 1 - 2 * (FW[2] <= 0); + + T[1] += -.5 * FW[1]; + T[1] *= 1 - 2 * (FW[2] <= 0); + } + + // } + + // Controls... + // ----------- + F[0] += (1 - BC_test) * I[object][0]; // Control inputs... + + T[0] += I[object][1]; // - .1*FW[1]; + T[1] += -1.5 * gamma_dot + I[object][3]; // + .1*FW[0]; + T[2] += -.8 * I[object][2]; + + // So be it... + // =========== +} + +// We need to transform from global coordinates to the local airplane +// coordinaates, a simple rotation. The order is left up to this routine +// which MODIFIES ITS ARGUMENTS... +// =============================== +void mech_globalize(Q &X, Q &Y, Q &Z) { + + Q x = X, y = Y, z = Z; + + X = x * (e0 * e0 + e1 * e1 - e2 * e2 - e3 * e3) + y * (2 * (e1 * e2 - e0 * e3)) + z * (2 * (e1 * e3 + e0 * e2)); + + Y = x * (2 * (e1 * e2 + e0 * e3)) + y * (e0 * e0 - e1 * e1 + e2 * e2 - e3 * e3) + z * (2 * (e2 * e3 - e0 * e1)); + + Z = x * (2 * (-e0 * e2 + e1 * e3)) + y * (2 * (e2 * e3 + e0 * e1)) + z * (e0 * e0 - e1 * e1 - e2 * e2 + e3 * e3); +} + +// We need to transform from local coordinates back to the global coordinates +// for the actual EDMS model... +// ============================ +void mech_localize(Q &X, Q &Y, Q &Z) { + + Q x = X, y = Y, z = Z; + + X = x * (e0 * e0 + e1 * e1 - e2 * e2 - e3 * e3) + y * (2 * (e1 * e2 + e0 * e3)) + z * (2 * (e1 * e3 - e0 * e2)); + + Y = x * (2 * (e1 * e2 - e0 * e3)) + y * (e0 * e0 - e1 * e1 + e2 * e2 - e3 * e3) + z * (2 * (e2 * e3 + e0 * e1)); + + Z = x * (2 * (e0 * e2 + e1 * e3)) + y * (2 * (e2 * e3 - e0 * e1)) + z * (e0 * e0 - e1 * e1 - e2 * e2 + e3 * e3); +} + +// Get the Real story based on the novella (System shock version), returning +// the result in the B_C_return[3] and BC_test global variables... +// ============================================================== +void get_boundary_conditions(int object, Q radius, Q position[3], Q derivatives[3]) { + + // Schmeck... + // ---------- + Q vec0, vec1, vec2, mul, vv0, vv1, vv2, dmag, kmag; + + // if (position[0] != A[object][0][0] ) mout << position[0] << " : " << A[object][0][0] << "\n"; + // if (position[1] != A[object][0][1] ) mout << position[1] << " : " << A[object][1][0] << "\n"; + // if (position[2] != A[object][0][2] ) mout << position[2] << " : " << A[object][2][0] << "\n"; + + // if (derivatives[0] != A[object][0][1] ) mout << derivatives[0] << " : " << A[object][0][1] << "\n"; + // if (derivatives[1] != A[object][1][1] ) mout << derivatives[1] << " : " << A[object][1][1] << "\n"; + // if (derivatives[2] != A[object][2][1] ) mout << derivatives[2] << " : " << A[object][2][1] << "\n"; + + // Find locations... + // ----------------- + indoor_terrain(position[0], position[1], position[2], radius, + on2ph[object], TFD_FULL); + + // Convert-a-tron... + // ----------------- + vec0.fix_to(terrain_info.fx + terrain_info.cx + terrain_info.wx); + vec1.fix_to(terrain_info.fy + terrain_info.cy + terrain_info.wy); + vec2.fix_to(terrain_info.fz + terrain_info.cz + terrain_info.wz); + + BC_test = sqrt(vec0 * vec0 + vec1 * vec1 + vec2 * vec2); + + if (BC_test > EDMS_DIV_ZERO_TOLERANCE) { + mul = 1 / BC_test; // To get primitive... + BC_test = 1; + } + + else + BC_test = mul = 0; + + // mout << BC_test << "\n"; + + vv0 = mul * vec0; // The primitive V_n... + vv1 = mul * vec1; + vv2 = mul * vec2; + + // "rate" magnitude to all you aero-astro guys... + // ---------------------------------------------- + dmag = I[object][24] * (derivatives[0] * vv0 // Delta_magnitude... + + derivatives[1] * vv1 + derivatives[2] * vv2); + + // PRINT3D( derivatives ); + + B_C_return[0] = -dmag * vv0; // Delta... + B_C_return[1] = -dmag * vv1; + B_C_return[2] = -dmag * vv2; + + kmag = I[object][23]; + + B_C_return[0] += kmag * vec0; // Kappa... + B_C_return[1] += kmag * vec1; + B_C_return[2] += kmag * vec2; + + // PRINT3D( B_C_return ) +} diff --git a/engine/src/Libraries/EDMS/Source/MODELS/d_f_face.cc b/engine/src/Libraries/EDMS/Source/MODELS/d_f_face.cc new file mode 100644 index 0000000..58281f7 --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/MODELS/d_f_face.cc @@ -0,0 +1,326 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Here is the bridge routine for maintenance and upkeep of the dirac frame models... +// ================================================================================== + +#include "fixpp.h" +#include "edms_int.h" + +// This matrix is for Douggie and his magic circus... +// ================================================== +fix Dirac_basis[9]; + +// Here we need include files for each and every model that we'll be using... +// ========================================================================== +#include "d_frame.h" + +// The physics handles definitions... +// ================================== +#include "physhand.h" + +// Data... +// ======= +#include "edms_mod.h" + +// Structs... +// ========== + +// Dirac Frame... +// -------------- +typedef struct { + + fix mass, hardness, roughness, gravity; + + fix corners[10][4]; + +} Dirac_frame; + +// Here we go... +// ============= +#define DIRAC_HARD_FAC 10 + +// Hack hack hack... +// ================= +static Q size = .3; + +// Thank God we have only 16 bits of fraction! +// =========================================== +static Q old_state[7], new_state[7]; + +// We need to link to c... +// ======================= +extern "C" { + +// Here are the bridge routines to the models... +// ============================================= + +// Dirac Frame routines... +// ======================= +physics_handle EDMS_make_Dirac_frame(Dirac_frame *d, State *s) { + + // Variables for tha actual conversion... + // -------------------------------------- + Q params[10], init_state[6][3]; + + Q mass, hardness, gravity, roughness; + + int32_t on = 0; + + physics_handle ph = 0; + + init_state[0][0].fix_to(s->X); + init_state[0][1].fix_to(s->X_dot); + init_state[1][0].fix_to(s->Y); + init_state[1][1].fix_to(s->Y_dot); + init_state[2][0].fix_to(s->Z); + init_state[2][1].fix_to(s->Z_dot); + init_state[3][0].fix_to(s->alpha); + init_state[3][1].fix_to(s->alpha_dot); + init_state[4][0].fix_to(s->beta); + init_state[4][1].fix_to(s->beta_dot); + init_state[5][0].fix_to(s->gamma); + init_state[5][1].fix_to(s->gamma_dot); + + mass.fix_to(d->mass); + roughness.fix_to(d->roughness); + hardness.fix_to(d->hardness); + gravity.fix_to(d->gravity); + + // hardness = hardness*(mass*4/size); + hardness = hardness * (mass * DIRAC_HARD_FAC / size); // Node Size goes here!!! + + params[0] = mass; + params[1] = 1 / mass; + params[2] = 1 / (.4 * mass * size * size); + params[3] = hardness; + params[4] = sqrt(params[3]) * sqrt(mass); + // params[4] = 20*sqrt( params[0] * mass ); + params[5] = roughness; + params[6] = .2; + params[7] = 0; // gravity; + params[8] = 0; + params[9] = 0; + + // Now actulally DO the dirty work... + // ---------------------------------- + on = make_Dirac_frame(init_state, params); + ph = EDMS_bind_object_number(on); + + return ph; +} + +// At some point we need the viewpoint offered by the neck... +// ========================================================== +void EDMS_get_Dirac_frame_viewpoint(physics_handle ph, State *s) { + + // For getting the new basis... + // ---------------------------- + // void render_globalize( Q &X, Q &Y, Q &Z, int ); + void render_localize(Q & X, Q & Y, Q & Z, int); + + // For Euler angle conversion... + // ----------------------------- + Q dirac_temp[9]; + Q alpha, beta, gamma; + + int32_t on = ph2on[ph]; + + Q delta = 0; + + if (I[on][30] == D_FRAME) { + + new_state[0] = (S[on][0][0]); + new_state[1] = (S[on][1][0]); + new_state[2] = (S[on][2][0]); + + new_state[3] = S[on][3][0]; + new_state[4] = S[on][4][0]; + new_state[5] = S[on][5][0]; + new_state[6] = S[on][6][0]; + + delta = (new_state[0] - old_state[0]) * (new_state[0] - old_state[0]) + + (new_state[1] - old_state[1]) * (new_state[1] - old_state[1]) + + (new_state[2] - old_state[2]) * (new_state[2] - old_state[2]) + + (new_state[3] - old_state[3]) * (new_state[3] - old_state[3]) + + (new_state[4] - old_state[4]) * (new_state[4] - old_state[4]) + + (new_state[5] - old_state[5]) * (new_state[5] - old_state[5]) + + (new_state[6] - old_state[6]) * (new_state[6] - old_state[6]); + + if (delta > .00003) { + + old_state[0] = new_state[0]; + old_state[1] = new_state[1]; + old_state[2] = new_state[2]; + + old_state[3] = new_state[3]; + old_state[4] = new_state[4]; + old_state[5] = new_state[5]; + old_state[6] = new_state[6]; + } + + s->X = old_state[0].to_fix(); + s->Y = old_state[1].to_fix(); + s->Z = old_state[2].to_fix(); + + EDMS_get_Euler_angles(alpha, beta, gamma, on); + + s->alpha = -gamma.to_fix(); + s->beta = -alpha.to_fix(); + s->gamma = -beta.to_fix(); + + // Set up global vectors... + // ------------------------ + dirac_temp[0] = dirac_temp[1] = dirac_temp[2] = dirac_temp[3] = dirac_temp[4] = dirac_temp[5] = dirac_temp[6] = + dirac_temp[7] = dirac_temp[8] = 0; + + dirac_temp[0] = 1; + dirac_temp[4] = 1; + dirac_temp[8] = 1; + + // Transform to the new basis... + // ----------------------------- + render_localize(dirac_temp[0], dirac_temp[1], dirac_temp[2], on); + render_localize(dirac_temp[3], dirac_temp[4], dirac_temp[5], on); + render_localize(dirac_temp[6], dirac_temp[7], dirac_temp[8], on); + + // Stuff into Matt's order... + // -------------------------- + /* + Dirac_basis[0] = dirac_temp[0].to_fix(); + Dirac_basis[1] =-dirac_temp[6].to_fix(); + Dirac_basis[2] = dirac_temp[3].to_fix(); + Dirac_basis[3] =-dirac_temp[2].to_fix(); + Dirac_basis[4] = dirac_temp[8].to_fix(); + Dirac_basis[5] =-dirac_temp[5].to_fix(); + Dirac_basis[6] = dirac_temp[1].to_fix(); + Dirac_basis[7] =-dirac_temp[7].to_fix(); + Dirac_basis[8] = dirac_temp[4].to_fix(); + */ + + // Almost... + /* + Dirac_basis[0] = dirac_temp[3].to_fix(); + Dirac_basis[1] =-dirac_temp[6].to_fix(); + Dirac_basis[2] =-dirac_temp[0].to_fix(); + Dirac_basis[3] =-dirac_temp[5].to_fix(); + Dirac_basis[4] = dirac_temp[8].to_fix(); + Dirac_basis[5] = dirac_temp[2].to_fix(); + Dirac_basis[6] = dirac_temp[4].to_fix(); + Dirac_basis[7] =-dirac_temp[7].to_fix(); + Dirac_basis[8] =-dirac_temp[1].to_fix(); + */ + + Dirac_basis[0] = -dirac_temp[3].to_fix(); + Dirac_basis[1] = -dirac_temp[6].to_fix(); + Dirac_basis[2] = dirac_temp[0].to_fix(); + Dirac_basis[3] = dirac_temp[5].to_fix(); + Dirac_basis[4] = dirac_temp[8].to_fix(); + Dirac_basis[5] = -dirac_temp[2].to_fix(); + Dirac_basis[6] = -dirac_temp[4].to_fix(); + Dirac_basis[7] = -dirac_temp[7].to_fix(); + Dirac_basis[8] = dirac_temp[1].to_fix(); + + } // End of check for Dirac_frame or not... +} + +// Utilities for the weak spirited... +// ================================== +void EDMS_set_Dirac_frame_parameters(physics_handle ph, Dirac_frame *d) { + + Q mass, hardness, roughness, gravity; + + mass.fix_to(d->mass); + hardness.fix_to(d->hardness); + gravity.fix_to(d->gravity); + roughness.fix_to(d->roughness); + + int32_t on = physics_handle_to_object_number(ph); + + hardness = hardness * (mass * DIRAC_HARD_FAC / size); + I[on][20] = mass; + I[on][21] = 1 / mass; + I[on][22] = 1 / (.4 * mass * size * size); + I[on][23] = hardness; + I[on][24] = sqrt(I[on][23]) * sqrt(mass); + // I[on][24] = 20*sqrt( I[on][20] * mass ); + I[on][25] = roughness; + I[on][26] = .2; + I[on][27] = 0; // gravity; + I[on][28] = 0; + I[on][29] = 0; + + // Done! + // ----- +} + +// And the weak minded... +// ====================== +void EDMS_get_Dirac_frame_parameters(physics_handle ph, Dirac_frame *d) { + int32_t on = physics_handle_to_object_number(ph); + + d->roughness = (I[on][23] / I[on][26]).to_fix(); + d->hardness = (I[on][26] / I[on][20] * DIRAC_HARD_FAC).to_fix(); + d->mass = I[on][20].to_fix(); + d->gravity = I[on][27].to_fix(); +} + +void EDMS_control_Dirac_frame(physics_handle ph, fix forward, fix pitch, fix yaw, fix roll) { + + int32_t on = ph2on[ph]; + + Q F, P, Y, R; + + F.fix_to(forward); + + // System shock angle order, definition... + // ======================================= + R.fix_to(roll); + P.fix_to(yaw); + Y.fix_to(pitch); + Y = -Y; + + control_dirac_frame(on, F, P, Y, R); +} + +// Access to the Dirac matrix for the main game. +fix *EDMS_Dirac_basis(void) { + return Dirac_basis; +} + +void render_localize(Q &X, Q &Y, Q &Z, int32_t object) { + + Q e0, e1, e2, e3; + + Q x = X, y = Y, z = Z; + + e0 = S[object][3][0]; + e1 = S[object][4][0]; + e2 = S[object][5][0]; + e3 = S[object][6][0]; + + // Go for it, sonny... + // ------------------- + X = x * (e0 * e0 + e1 * e1 - e2 * e2 - e3 * e3) + y * (2 * (e1 * e2 - e0 * e3)) + z * (2 * (e1 * e3 + e0 * e2)); + + Y = x * (2 * (e1 * e2 + e0 * e3)) + y * (e0 * e0 - e1 * e1 + e2 * e2 - e3 * e3) + z * (2 * (e2 * e3 - e0 * e1)); + + Z = x * (2 * (-e0 * e2 + e1 * e3)) + y * (2 * (e2 * e3 + e0 * e1)) + z * (e0 * e0 - e1 * e1 - e2 * e2 + e3 * e3); +} + +} // End of Extern "C"... diff --git a/engine/src/Libraries/EDMS/Source/MODELS/d_frame.cc b/engine/src/Libraries/EDMS/Source/MODELS/d_frame.cc new file mode 100644 index 0000000..11c8543 --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/MODELS/d_frame.cc @@ -0,0 +1,328 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Here is the beginning of the EDMS dirac frame object. Here it is the +// guise of the System shock cyberspace model, or is it??? +// ======================================================= + +// Jon Blackley, May 14, 1994 +// ========================== + +#include "edms_int.h" //This is the model type library. It is universal. +#include "edms_mod.h" + +#define EDMS_CYBER_CURRENT_ALIGN .06 + +// Super secret Church-Blackley Boundary Condition Descriptor (BCD)... +// =================================================================== +#include "idof.h" +#include "ss_flet.h" + +extern Q EDMS_CYBER_FLOW1X; +extern Q EDMS_CYBER_FLOW2X; +extern Q EDMS_CYBER_FLOW3X; + +extern int32_t EDMS_BCD; + +// Here are the internal degrees of freedom. First we get the aerodynamic forces +// from the (external) aero model, then the interactions and solid B/C here... +// =========================================================================== +void dirac_frame_idof(int32_t object) { + + // Here's the real work... + // ----------------------- + extern void dirac_mechanicals(int32_t object, Q F[3], Q T[3]); + extern void shall_we_dance(int32_t object, Q &result0, Q &result1, Q &result2); + + // For alignment... + // ---------------- + extern void mech_localize(Q &X, Q &Y, Q &Z); + extern void mech_globalize(Q &X, Q &Y, Q &Z); + Q F_T[3]; + + Q e0, e1, e2, e3, // For speed plus beauty! + ed0, ed1, ed2, ed3; + + Q collide_x, collide_y, collide_z; + + Q T[3], F[3]; + + // Now deal with the quaternion (2nd order) bullshit... + // ==================================================== + e0 = A[object][3][0]; + e1 = A[object][4][0]; + e2 = A[object][5][0]; + e3 = A[object][6][0]; + ed0 = A[object][3][1]; + ed1 = A[object][4][1]; + ed2 = A[object][5][1]; + ed3 = A[object][6][1]; + + Q beta_dot = 2 * (e0 * ed1 + e3 * ed2 - e2 * ed3 - e1 * ed0); + Q alpha_dot = 2 * (-e3 * ed1 + e0 * ed2 + e1 * ed3 - e2 * ed0); + Q gamma_dot = 2 * (e2 * ed1 - e1 * ed2 + e0 * ed3 - e3 * ed0); + + // Zero the results... + // =================== + F[0] = F[1] = F[2] = T[0] = T[1] = T[2] = 0; + + // Are we hitting anything yet? + // ---------------------------- + shall_we_dance(object, collide_x, collide_y, collide_z); + F[0] += collide_x * I[object][23]; + F[1] += collide_y * I[object][23]; + F[2] += collide_z * I[object][23]; + + // CyberSpace BCD information... + // ----------------------------- + indoor_terrain(A[object][0][0], A[object][1][0], A[object][2][0], + I[object][26], -1, TFD_RCAST); + + if (ss_edms_bcd_flags & SS_BCD_CURR_ON) { + + F_T[0] = F_T[1] = F_T[2] = 0; + + Q current_strength = EDMS_CYBER_FLOW1X; + if ((ss_edms_bcd_flags & SS_BCD_CURR_SPD) == SS_BCD_CURR_MID) + current_strength = EDMS_CYBER_FLOW2X; + if ((ss_edms_bcd_flags & SS_BCD_CURR_SPD) == SS_BCD_CURR_HIGH) + current_strength = EDMS_CYBER_FLOW3X; + + if ((ss_edms_bcd_flags & SS_BCD_REPUL_TYPE) == SS_BCD_REPUL_UP) + F_T[2] = current_strength; + if ((ss_edms_bcd_flags & SS_BCD_REPUL_TYPE) == SS_BCD_REPUL_DOWN) + F_T[2] = -current_strength; + + if (F_T[2] == 0) { + if ((ss_edms_bcd_flags & SS_BCD_CURR_DIR) == SS_BCD_CURR_E) + F_T[0] = current_strength; + if ((ss_edms_bcd_flags & SS_BCD_CURR_DIR) == SS_BCD_CURR_W) + F_T[0] = -current_strength; + if ((ss_edms_bcd_flags & SS_BCD_CURR_DIR) == SS_BCD_CURR_N) + F_T[1] = current_strength; + if ((ss_edms_bcd_flags & SS_BCD_CURR_DIR) == SS_BCD_CURR_S) + F_T[1] = -current_strength; + } + + // Auto alignment... + // ----------------- + mech_localize(F_T[0], F_T[1], F_T[2]); + if (I[object][2] == 0) + T[2] += EDMS_CYBER_CURRENT_ALIGN * F_T[1]; + if (I[object][1] == 0) + T[1] += EDMS_CYBER_CURRENT_ALIGN * F_T[2]; + + F[0] += F_T[0]; // Note that these are locals... + // F[1] += F_T[1]; + // F[2] += F_T[2]; + } + + // Get the mechanical info... + // ========================== + dirac_mechanicals(object, F, T); + + // Jeeeeeezuz... + // ------------- + mech_globalize(F[0], F[1], F[2]); + + // mout << F[2] << "\n"; + + // For now, just pop them in... + // ============================ + S[object][0][2] = I[object][21] * F[0] - .5 * I[object][25] * A[object][0][1]; + S[object][1][2] = I[object][21] * F[1] - .5 * I[object][25] * A[object][1][1]; + S[object][2][2] = I[object][21] * F[2] - .5 * I[object][25] * A[object][2][1] - I[object][27]; + + Q T_alpha_temp = T[0] * I[object][22] - 5 * alpha_dot; + Q T_beta_temp = T[1] * I[object][22] - 5 * beta_dot; + Q T_gamma_temp = T[2] * I[object][22] - 5 * gamma_dot; + + S[object][3][2] = -.5 * (e1 * T_beta_temp + e2 * T_alpha_temp + e3 * T_gamma_temp + ed1 * beta_dot + + ed2 * alpha_dot + ed3 * gamma_dot); + + S[object][4][2] = .5 * (e0 * T_beta_temp + e2 * T_gamma_temp - e3 * T_alpha_temp + ed0 * beta_dot + + ed2 * gamma_dot - ed3 * alpha_dot); + + S[object][5][2] = .5 * (e0 * T_alpha_temp + e3 * T_beta_temp - e1 * T_gamma_temp + ed0 * alpha_dot + + ed3 * beta_dot - ed1 * gamma_dot); + + S[object][6][2] = .5 * (e1 * T_alpha_temp - e2 * T_beta_temp + e0 * T_gamma_temp + ed0 * gamma_dot + + ed1 * alpha_dot - ed2 * beta_dot); + + // mout << "Inside dframe2\n"; + + // That's all, folks. Give 'em some air. Move along... + // ==================================================== +} + +// Control dirac_frame... +// ====================== +void control_dirac_frame(int32_t object, Q forward, Q pitch, Q yaw, Q roll) { + I[object][0] = 3 * forward; + I[object][1] = pitch; + I[object][2] = yaw; + I[object][3] = roll; +} + +// Sets up everything needed to make the Dirac frame object, including conversion +// of angles to spinors and such. Probably should have an external utility for +// resetting these... +// ================== +int32_t make_Dirac_frame(Q init_state[6][3], Q params[10]) { + + // Have some variables... + // ====================== + int32_t object_number = -1, // Three guesses... + error_code = -1; // Guilty until... + + // We need ignorable coordinates... + // ================================ + extern void null_function(int32_t); + + Q sin_alpha = 0, cos_alpha = 0, sin_beta = 0, cos_beta = 0, sin_gamma = 0, cos_gamma = 0; + + // mout << "Making dframe1\n"; + + // First find out which object we're going to be... + // ================================================ + while (S[++object_number][0][0] > END) + ; // Jon's first C trickie... + + // Is it an allowed object number? Are we full? Why are we here? Is there a God? + // ================================================================================ + if (object_number < MAX_OBJ) { + + // Now we can create the frame: first dump the initial state vector... + // ===================================================================== + for (int32_t coord = 0; coord < 3; coord++) { + for (int32_t deriv = 0; deriv < 2; deriv++) { + S[object_number][coord][deriv] = A[object_number][coord][deriv] = + init_state[coord][deriv]; // For collisions... + } + } + + // Now convert the input Euler angles and derivatives into quaternion + // initial conditions... + // ===================== + + // Zeros... + // -------- + sincos(.5 * init_state[3][0], &sin_alpha, &cos_alpha); + sincos(.5 * init_state[4][0], &sin_beta, &cos_beta); + sincos(.5 * init_state[5][0], &sin_gamma, &cos_gamma); + + S[object_number][3][0] = A[object_number][3][0] = + cos_gamma * cos_alpha * cos_beta + sin_gamma * sin_alpha * sin_beta; + + S[object_number][4][0] = A[object_number][4][0] = + cos_gamma * cos_alpha * sin_beta - sin_gamma * sin_alpha * cos_beta; + + S[object_number][5][0] = A[object_number][5][0] = + cos_gamma * sin_alpha * cos_beta + sin_gamma * cos_alpha * sin_beta; + + S[object_number][6][0] = A[object_number][6][0] = + -cos_gamma * sin_alpha * sin_beta + sin_gamma * cos_alpha * cos_beta; + + // Firsts... + // --------- + S[object_number][3][1] = + -.5 * (S[object_number][4][0] * init_state[4][1] + S[object_number][5][0] * init_state[3][1] + + S[object_number][6][0] * init_state[5][1]); + + S[object_number][4][1] = + .5 * (S[object_number][3][0] * init_state[4][1] + S[object_number][5][0] * init_state[5][1] - + S[object_number][6][0] * init_state[3][1]); + + S[object_number][5][1] = + .5 * (S[object_number][3][0] * init_state[3][1] + S[object_number][6][0] * init_state[4][1] - + S[object_number][4][0] * init_state[5][1]); + + S[object_number][6][1] = + .5 * (S[object_number][3][0] * init_state[5][1] + S[object_number][4][0] * init_state[3][1] - + S[object_number][5][0] * init_state[4][1]); + + // mout << "AA: " << S[object_number][3][1] << "\n"; + // mout << "BB: " << S[object_number][4][1] << "\n"; + // mout << "GG: " << S[object_number][5][1] << "\n"; + + // mout << "Making dframe2\n"; + // for ( int ioi = 0; ioi < 7; ioi++ ) { mout << "S[" << object_number << "][" << ioi << "][0]: " << + // S[object_number][ioi][0] << "\n"; } + + // Put in the appropriate parameters... + // ==================================== + for (int copy = 0; copy < 10; copy++) { + I[object_number][copy + 20] = params[copy]; + } + I[object_number][IDOF_MODEL] = D_FRAME; // Hey, you are what you eat. + + // Now tell Soliton where to look for the equations of motion... + // ============================================================= + idof_functions[object_number] = dirac_frame_idof; + + equation_of_motion[object_number][0] = equation_of_motion[object_number][1] = + equation_of_motion[object_number][2] = equation_of_motion[object_number][3] = // Nice symmetries, huh. + equation_of_motion[object_number][4] = equation_of_motion[object_number][5] = + equation_of_motion[object_number][6] = null_function; + + // Put in the collision information... + // =================================== + I[object_number][31] = I[object_number][26]; + I[object_number][32] = I[object_number][33] = I[object_number][34] = I[object_number][35] = 0; + I[object_number][36] = I[object_number][26]; // Shrugoff "mass"... + I[object_number][IDOF_COLLIDE] = -1; + I[object_number][IDOF_AUTODESTRUCT] = 0; // No kill I... + + // Zero the controls... + // ==================== + I[object_number][0] = I[object_number][1] = I[object_number][3] = I[object_number][2] = 0; + + // mout << "Making dframe3\n"; + // for (int tt = 20; tt < 31; tt++) mout << "I[" << tt << "]: " << I[object_number][tt] << "\n"; + + // Wake me up... + // ============= + no_no_not_me[object_number] = 1; + + // Things seem okay... + // =================== + error_code = object_number; + } + + // Inform the caller... + // ==================== + return error_code; +} + +// Nota Bene: Los parametros del model son: +// ========================================= + +// Number | Comment +// -------------------- +// 0 | Mass +// 1 | One over Mass +// 2 | 1/I +// 3 | Kappa +// 4 | Delta +// 5 | Drag +// 6 | Size +// 7 | gravity +// 8 | ??? +// 9 | ??? +// ========================================== +// So there. diff --git a/engine/src/Libraries/EDMS/Source/MODELS/d_frame.h b/engine/src/Libraries/EDMS/Source/MODELS/d_frame.h new file mode 100644 index 0000000..804ce97 --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/MODELS/d_frame.h @@ -0,0 +1,22 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// This seems silly now, but later it will all make sense, sensei... +// ================================================================= +int32_t make_Dirac_frame(Q init_state[6][3], Q params[10]); +void control_dirac_frame(int32_t object, Q forward, Q pitch, Q yaw, Q roll); diff --git a/engine/src/Libraries/EDMS/Source/MODELS/ftl.cc b/engine/src/Libraries/EDMS/Source/MODELS/ftl.cc new file mode 100644 index 0000000..831b2f8 --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/MODELS/ftl.cc @@ -0,0 +1,392 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Many games require objects which travel faster than the renderer can possibly draw. The +// stuff in this file handles these things in various ways. For instance, a laser weapon +// can be raycast instantaneously, dependant only upon the terrain and object models. +// ================================================================================== + +#include "edms_int.h" +#include "idof.h" +//#ifdef EDMS_SHIPPABLE +////#include +//#endif +//#include +//#include <_edms.h> + +#include "physhand.h" + +// extern "C" { +#include "ss_flet.h" +//} + +// Here is some stuff that the line finder needs that is stupid to pass around... +// ============================================================================== +static Q initial_X[3] = {0, 0, 0}, final_X[3] = {0, 0, 0}; + +extern int32_t EDMS_integrating; + +extern int32_t alarm_clock[MAX_OBJ]; + +physics_handle object_check(uint32_t data_word, Q size, Q range, int32_t exclude, int32_t steps, + Q &dist); // Checks for hits... + +// Here is the high velocity weapon primitive... +// ============================================= +physics_handle EDMS_cast_projectile(Q *X, Q D[3], Q kick, Q knock, Q size, Q range, int32_t exclude, int32_t shooter) { + extern Q PELVIS; + int32_t stepper = 0, + max_step = (2 * range / size).to_int(), // samples per meter... + victim_on = 0, shooter_on = 0, object_pointer = 0, i = 0; + + uint32_t must_check_objects[MAX_OBJ]; + + uint32_t test_data; + uint32_t last_test_data = 0; + + physics_handle victim = -1, // It is what is says it is... + return_victim = -1; // The number actually returned... + + // I think now victim and return_victim can be the same thing - DS + + Q iota_c = 0; // Some variable or other... + Q D_old[3]; // Hey, blow blow blow... + + Q dist; // where did we hit the victim? + + // Reset the object collisions... + // ============================== + int32_t no_dont_do_it = 0; + + // Looks at terrain... + // =================== + fix checker = 0; + + // PRINT3D( X ); + // mout << "Max: " << max_step << " :range: " << range << " : size: " << size << ".\n"; + + // Save the initial vectors for the line finder... + // =============================================== + initial_X[0] = X[0]; + initial_X[1] = X[1]; + initial_X[2] = X[2]; + + Q anus = sqrt(D[0] * D[0] + D[1] * D[1] + D[2] * D[2]); + if (anus < .80 || range < .25) { + if (anus < .80) { + D[2] = 1; + D[1] = D[0] = 0; + no_dont_do_it = 1; + } + + //#ifdef EDMS_SHIPPABLE + // mout << "!EDMS: raycast with bad vector: mag = " << anus << ", range = " << range << "\n"; + //#endif + range = .25; + } + + // Rescale direction to 1/3 decimeters... + // ====================================== + D_old[0] = D[0]; + D_old[1] = D[1]; + D_old[2] = D[2]; + + D[0] *= .5 * size; + D[1] *= .5 * size; + D[2] *= .5 * size; + + // Make sure we're ON THE MAP before casting into random memory... + // =============================================================== + if ((X[0] < 0) || (X[1] < 0) || (X[0] > EDMS_DATA_SIZE - 1) || (X[1] > EDMS_DATA_SIZE - 1)) { + checker = 1000; + // mout << "!EDMS: bad raycast start at:\n"; + // PRINT3D( X ) + // mout << "!EDMS: raycast excluded physics handle " << exclude << "\n"; + // flush( mout ); + no_dont_do_it = 1; + } + + // Are we good to go? + // ================== + if (no_dont_do_it == 0) { + // Find impact point... + // ==================== + for (stepper = 0; (stepper < max_step) && (checker == 0); stepper++) { + checker = 0; + + TerrainHit hit = indoor_terrain(X[0], // Get the info... + X[1], X[2], size, -1, TFD_RCAST); + + // Check the terrain... + // ==================== +#ifdef NOT + checker = (terrain_info.cx) | (terrain_info.cy) | (terrain_info.cz) | (terrain_info.fx) | + (terrain_info.fy) | (terrain_info.fz) | (terrain_info.wx) | (terrain_info.wy) | (terrain_info.wz); +#else + checker = (hit == HIT_FACELET); +#endif + + // Check for object collisions... + // ============================== + int32_t hx = floor(hash_scale * X[0]); + int32_t hy = floor(hash_scale * X[1]); + test_data = data[hx][hy]; + + if (test_data != last_test_data && test_data != 0) { + if (object_pointer < MAX_OBJ) { + must_check_objects[object_pointer] = test_data; + object_pointer++; + } + last_test_data = test_data; + } + + // Move the check point... + // ======================= + X[0] += D[0]; + X[1] += D[1]; + X[2] += D[2]; + + if ((X[0] < 0) || (X[1] < 0) || (X[0] > EDMS_DATA_SIZE - 1) || (X[1] > EDMS_DATA_SIZE - 1)) { + checker = 1000; // Get out... + // PRINT3D( X ) + // mout << "!EDMS: Raycast has left map!\n"; + } + } + + // Save the final point of the line segment... + // =========================================== + final_X[0] = X[0]; + final_X[1] = X[1]; + final_X[2] = X[2]; + + // Now we're done following the ray. + + for (i = 0; i < object_pointer; i++) { + victim = object_check(must_check_objects[i], size, range, exclude, stepper, dist); + + if (victim != -1) { + // We hit someone! + return_victim = victim; // return the right guy! + + victim_on = ph2on[victim]; + + Q inv_mass = (I[victim_on][IDOF_MODEL] == ROBOT ? I[victim_on][IDOF_ROBOT_MASS_RECIP] : I[victim_on][36]); + + if (inv_mass > 0.05 && knock * inv_mass > 10.0 / inv_mass) { + // mout << "Clamping knock from " << knock; + + knock = 10.0 / (inv_mass * inv_mass); + + // mout << " to " << knock << "\n"; + } + // FIXME this statement does nothing + if (I[victim_on][IDOF_MODEL] == ROBOT) + iota_c = 200 * inv_mass * inv_mass * knock; + else + iota_c = 200 * inv_mass * inv_mass * knock; + + I[victim_on][32] = D_old[0] * iota_c; // Absolute blows off walls, remember explosions too... + I[victim_on][33] = D_old[1] * iota_c; + I[victim_on][34] = D_old[2] * iota_c; + + I[victim_on][35] = 1; // Deweet! + + if (no_no_not_me[victim_on] == + 0) { // hey folks, if our poor victim is asleep, wake him in the way appropriate to us + if (EDMS_integrating) + alarm_clock[victim_on] = 1; + else + no_no_not_me[victim_on] = 1; // Make sure we're up... a + } + + break; // All done looking! + } + } + + // If we did, in fact, hit a wall, the 3D system precision may be insufficient to sort the hit + // art in front of the wall. Therefore... + // ======================================= + + // The EDMS code used to set the endpoint of the beam to the center of the + // victim if it hit someone. But in reality, bugs in the code made it + // always think at that point that it hadn't hit anyone, so the code was + // never executed. When the bugs were fixed and beams actually started + // hitting centers of objects, people complained. So I am just changing + // back to the old way, which one would think puts the beam way too far + // away since it ignores the position of the victim entirely, but apparently + // due to the way System Shock sorts beams and hits everything works out + // okay in the end. I tried a little to do it correctly for real but I ran + // out of time. - DS + + if (victim > -1) { + // ha ha, the above text lies, it now works + + X[0] = initial_X[0] + D_old[0] * dist; + X[1] = initial_X[1] + D_old[1] * dist; + X[2] = initial_X[2] + D_old[2] * dist; + + // Spew (DSRC_EDMS_Collide, ("vic %f %f %f hit %f %f %f\n", S[victim_on][0][0], S[victim_on][1][0], + // S[victim_on][2][0], X[0], X[1], X[2])); + } else { + // Apparently things are going through walls a little so let's bring + // it even farther back. + + X[0] -= D[0] * 2; + X[1] -= D[1] * 2; + X[2] -= D[2] * 2; + } + + // Did we hit a wall, or did we hit range out? + // =========================================== + if ((stepper == max_step) && (victim == -1)) { + X[0] = X[1] = X[2] = END; + } + + // Do the kickback... + // ================== + if (shooter != -1) { + shooter_on = ph2on[shooter]; + iota_c = I[shooter_on][29] * kick; + + if (I[shooter_on][IDOF_MODEL] == PELVIS) { + I[shooter_on][8] = D_old[0] * iota_c; + I[shooter_on][9] = D_old[1] * iota_c; + } + } + + // Were we good to go? + // =================== + } + + // Hit for now... + // ============== + return return_victim; +} + +//#pragma off (unreferenced) +// Here, since we know the line segment we're interested in, we check to make sure that we +// didn't hit any objects, and return the one we did... +// ==================================================== +physics_handle object_check(uint32_t data_word, Q size, Q range, int32_t exclude, int32_t stepper, Q &dist) { + // General purpose... + // ================== + int32_t object; + physics_handle victim = -1; + + // For the lines... + // ================ + Q a = initial_X[0] - final_X[0], b = initial_X[1] - final_X[1], c = initial_X[2] - final_X[2], top_1 = 0, top_2 = 0, + top_3 = 0, bottom = 0, kill_zone = 0, kzdist = 0, kzdisto = 10000; + + uint32_t bit = 0; // which object bit we're checking + + while (data_word != 0) { + if (data_word & 1) { + // Object bit number 'bit' is on, we must check all objects which have that bit + for (object = bit; object < MAX_OBJ && S[object][0][0] > END; object += NUM_OBJECT_BITS) { + if (object != exclude) { + top_1 = c * (S[object][1][0] - initial_X[1]) - b * (S[object][2][0] - initial_X[2]); + top_1 *= top_1; + + top_2 = a * (S[object][2][0] - initial_X[2]) - c * (S[object][0][0] - initial_X[0]); + top_2 *= top_2; + + top_3 = b * (S[object][0][0] - initial_X[0]) - a * (S[object][1][0] - initial_X[1]); + top_3 *= top_3; + + bottom = a * a + b * b + c * c; + + kill_zone = sqrt((top_1 + top_2 + top_3) / bottom); + + if (kill_zone < (I[object][31] + size)) { + kzdist = sqrt((initial_X[0] - S[object][0][0]) * (initial_X[0] - S[object][0][0]) + + (initial_X[1] - S[object][1][0]) * (initial_X[1] - S[object][1][0]) + + (initial_X[2] - S[object][2][0]) * (initial_X[2] - S[object][2][0])); + + if ((kzdist < .5 * size * stepper) && (kzdist < kzdisto)) { + victim = on2ph[object]; + kzdisto = kzdist; + dist = kzdist - I[object][31]; + + // X[0] = S[object][0][0]; //Provide hit location, naive for + //now... X[1] = S[object][1][0]; X[2] = S[object][2][0]; + } + } + + // Is it a pelvis, and, if so, do I check for your head? (sooooo clean..) + // ======================================================================= + if (I[object][IDOF_MODEL] == PELVIS) { + + Q position[3]; + + Q offset_x = I[object][0] * sin(S[object][4][0]), + offset_y = -1.5 * I[object][0] * sin(S[object][5][0]), + offset_z = I[object][0] * cos(S[object][4][0]) * cos(S[object][5][0]); + + Q sin_alpha = 0, cos_alpha = 0; + + Q final_x = 0, final_y = 0; + + sincos(-S[object][3][0], &sin_alpha, &cos_alpha); + final_x = cos_alpha * offset_x + sin_alpha * offset_y; + final_y = -sin_alpha * offset_x + cos_alpha * offset_y; + + position[0] = S[object][0][0] + final_x; + position[1] = S[object][1][0] + final_y; + position[2] = S[object][2][0] + offset_z; + + top_1 = c * (position[1] - initial_X[1]) - b * (position[2] - initial_X[2]); + top_1 *= top_1; + + top_2 = a * (position[2] - initial_X[2]) - c * (position[0] - initial_X[0]); + top_2 *= top_2; + + top_3 = b * (position[0] - initial_X[0]) - a * (position[1] - initial_X[1]); + top_3 *= top_3; + + bottom = a * a + b * b + c * c; + + kill_zone = sqrt((top_1 + top_2 + top_3) / bottom); + + if (kill_zone < (.75 * I[object][IDOF_PELVIS_RADIUS] + size)) { + kzdist = sqrt((initial_X[0] - position[0]) * (initial_X[0] - position[0]) + + (initial_X[1] - position[1]) * (initial_X[1] - position[1]) + + (initial_X[2] - position[2]) * (initial_X[2] - position[2])); + + // It's a bouncing baby head hit!... + // --------------------------------- + if ((kzdist < .5 * size * stepper) && (kzdist < kzdisto)) { + victim = on2ph[object]; + kzdisto = kzdist; + dist = kzdist - I[object][31]; + } + } + + } // if pelvis + } // if (object != exclude) + } // for (object = bit) + } // if (data_word & 1) + + // Shift over the mask so we're testing the next object bit + data_word >>= 1; + bit++; + } // while (data_word != 0) + + return victim; +} +//#pragma on (unreferenced) diff --git a/engine/src/Libraries/EDMS/Source/MODELS/ftl.h b/engine/src/Libraries/EDMS/Source/MODELS/ftl.h new file mode 100644 index 0000000..cf79740 --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/MODELS/ftl.h @@ -0,0 +1,21 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// This seems silly now, but later it will all make sense, sensei... +// ================================================================= +int32_t EDMS_cast_projectile(Q *X, Q D[3], Q kick, Q knock, Q size, Q range, int32_t exclude, physics_handle shooter); diff --git a/engine/src/Libraries/EDMS/Source/MODELS/ftlface.cc b/engine/src/Libraries/EDMS/Source/MODELS/ftlface.cc new file mode 100644 index 0000000..cf9afc9 --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/MODELS/ftlface.cc @@ -0,0 +1,99 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Here is the bridge routine for maintenance and upkeep of the FTL models... +// ========================================================================== + +#include "fixpp.h" +#include "edms_int.h" + +//#ifdef EDMS_SHIPPABLE +////#include +//#endif + +// Here we need include files for each and every model that we'll be using... +// ========================================================================== +#include "ftl.h" + +// The physics handles definitions... +// ================================== +#include "physhand.h" + +// Data... +// ======= +extern EDMS_Argblock_Pointer A; +extern Q S[MAX_OBJ][7][4], I[MAX_OBJ][DOF_MAX]; + +// Structs... +// ========== +// Nope. + +// We need to link to c... +// ======================= +extern "C" { + +// Here are the bridge routines to the models... +// ============================================= + +// Beam weapon ray caster... +// ========================= +physics_handle EDMS_beam_weapon(fix X[3], fix D[3], fix kick, fix knock, fix size, fix range, physics_handle exclude, + physics_handle shooter) { + + // Return me, baby... + // ------------------ + physics_handle ph = -1; + int32_t EXCLUDE = 0; + + Q DD[3]; + + Q Kick, Knock, Size, Range; + + Q *XX = (Q *)&X[0]; + + DD[0].fix_to(D[0]); + DD[1].fix_to(D[1]); + DD[2].fix_to(D[2]); + + Kick.fix_to(kick); + Knock.fix_to(knock); + Size.fix_to(size); + Range.fix_to(range); + + // Is this a valid physics handle??? + // ================================= + if (exclude > -1) + EXCLUDE = ph2on[exclude]; + // if ( exclude < 0 ) { EXCLUDE = -1; mout << "!EDMS: exclude warning is okay!\n"; } + + // Do it... + // ======== + ph = EDMS_cast_projectile(XX, DD, Kick, Knock, Size, Range, EXCLUDE, shooter); + + // Convert back to the goofbakk fixpoint system... + // ----------------------------------------------- + X[0] = XX[0].to_fix(); + X[1] = XX[1].to_fix(); + X[2] = XX[2].to_fix(); + + // Return a physics handle... + // ========================== + return ph; +} + +} // End of extern "C" for the &^%$@% compiler... diff --git a/engine/src/Libraries/EDMS/Source/MODELS/pelface.cc b/engine/src/Libraries/EDMS/Source/MODELS/pelface.cc new file mode 100644 index 0000000..2c6f7dc --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/MODELS/pelface.cc @@ -0,0 +1,328 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Here is the bridge routine for maintenance and upkeep of the pelvis models... +// ============================================================================= + +////#include +#include "fixpp.h" +#include "edms_int.h" +#include "idof.h" +//#ifdef EDMS_SHIPPABLE +////#include +//#endif + +// Here we need include files for each and every model that we'll be using... +// ========================================================================== +#include "pelvis.h" + +// The physics handles definitions... +// ================================== +#include "physhand.h" + +// Pointers to skeletons (for bipeds, as it were and will be)... +// ============================================================= +extern Q *utility_pointer[MAX_OBJ]; + +// Pelvis... +// --------- +typedef struct { + + fix mass, size, hardness, pep, gravity, height; + + int32_t cyber_space; + +} Pelvis; + +// Here we go... +// ============= +#define HARD_FAC 6 + +// Thatnk God we have only 16 bits of fraction! +// ============================================ +Q old_state[6], new_state[6]; + +// We need to link to c... +// ======================= +extern "C" { + +// Here are the bridge routines to the models... +// ============================================= + +// Pelvis routines... +// ================== +physics_handle EDMS_make_pelvis(Pelvis *p, State *s) { + + Q params[10], init_state[6][3]; + + Q mass, pep, hardness, size, gravity, height; + + int on = 0, cyber_space = 0; + + physics_handle ph = 0; + + init_state[0][0].fix_to(s->X); + init_state[0][1].fix_to(s->X_dot); + init_state[1][0].fix_to(s->Y); + init_state[1][1].fix_to(s->Y_dot); + init_state[2][0].fix_to(s->Z); + init_state[2][1].fix_to(s->Z_dot); + init_state[3][0].fix_to(s->alpha); + init_state[3][1].fix_to(s->alpha_dot); + init_state[4][0].fix_to(s->beta); + init_state[4][1].fix_to(s->beta_dot); + init_state[5][0].fix_to(s->gamma); + init_state[5][1].fix_to(s->gamma_dot); + + mass.fix_to(p->mass); + size.fix_to(p->size); + // if ( size > .45/hash_scale ) size = .45/hash_scale; + hardness.fix_to(p->hardness); + pep.fix_to(p->pep); + gravity.fix_to(p->gravity); + height.fix_to(p->height); + if (height > 3 * size) + height = 3 * size; + + // mout << "Pelvis: \n"; + // mout << " mass: " << mass << "\n"; + // mout << " size: " << size << "\n"; + // mout << " hard: " << hardness << "\n"; + // mout << " pepp: " << pep << "\n"; + // mout << " grav: " << gravity << "\n"; + // mout << " hght: " << height << "\n"; + + // hardness = hardness*(mass*4/size); + hardness = hardness * (mass * HARD_FAC / size); + params[0] = hardness; + params[1] = 3 * sqrt(params[0] * mass); + params[2] = size; + params[3] = pep * mass; + params[4] = 1. / mass; + params[5] = gravity; + params[6] = mass; + params[7] = 1. / (.4 * mass * size * size); + params[8] = 5. * (1. / params[7]); + params[9] = .4 * mass * size * size; + // mout << "I29 from make! " << params[9] << "\n"; + + on = make_pelvis(init_state, params); + + // Turn on Cyber Space... + // ---------------------- + cyber_space = p->cyber_space; + if (cyber_space < 0 || cyber_space > 2) + cyber_space = 0; // Hey, why you do that? + + I[on][10] = cyber_space; + I[on][0] = I[on][6] = height - size; // Ok... + + ph = EDMS_bind_object_number(on); + + return ph; +} + +// This works just like the robot model... +// --------------------------------------- +void EDMS_control_pelvis(physics_handle ph, fix forward, fix turn, fix sidestep, fix lean, fix jump, int32_t crouch) { + + // Silly, no? + Q FF, TT, SS, LL, JJ; + +#ifdef EDMS_SHIPPABLE + if (ph < 0) + mout << "Hey, you are and idiot..."; +#endif + + FF.fix_to(forward); + TT.fix_to(turn); + SS.fix_to(sidestep); + LL.fix_to(lean); + JJ.fix_to(jump); + + int32_t on = physics_handle_to_object_number(ph); + + if (I[on][IDOF_MODEL] == PELVIS) + pelvis_set_control(on, FF, TT, SS, LL, JJ, crouch); +} + +// At some point we need the viewpoint offered by the neck... +// ---------------------------------------------------------- +void EDMS_get_pelvic_viewpoint(physics_handle ph, State *s) { + + int32_t on = ph2on[ph]; + + Q delta = 0; + + if (I[on][IDOF_MODEL] == PELVIS) { + + Q new_neck = I[on][0]; // Rendered height... + + Q offset_x = new_neck * sin(S[on][4][0]), offset_y = -1.5 * new_neck * sin(S[on][5][0]), + offset_z = new_neck * cos((1) * S[on][4][0]) * cos((1) * S[on][5][0]); + // offset_z = new_neck*cos( (.2 + .8*(I[on][10]>0) )*S[on][4][0] )*cos( (.2 + .8*(I[on][10]>0) + //)*S[on][5][0] ); + + Q sin_alpha = 0, cos_alpha = 0; + + Q final_x = 0, final_y = 0; + + sincos(-S[on][3][0], &sin_alpha, &cos_alpha); + final_x = cos_alpha * offset_x + sin_alpha * offset_y; + final_y = -sin_alpha * offset_x + cos_alpha * offset_y; + + new_state[0] = (S[on][0][0] + final_x); + new_state[1] = (S[on][1][0] + final_y); + new_state[2] = (S[on][2][0] + offset_z); + + new_state[3] = S[on][3][0]; + new_state[4] = (.1 /*- .3*(I[on][10]>0)*/) * S[on][4][0]; + new_state[5] = (.03 /*+ 1.6*(I[on][10]>0)*/) * S[on][5][0]; + + // new_state[3] = S[on][3][0]; + // new_state[4] = (.1 - .1*(I[on][10]>0) )*S[on][4][0]; + // new_state[5] = (.03 + 1.6*(I[on][10]>0) )*S[on][5][0]; + + if (I[on][10] == 2) { + new_state[4] = S[on][4][0]; + new_state[5] = S[on][5][0]; + } + + delta = (new_state[0] - old_state[0]) * (new_state[0] - old_state[0]) + + (new_state[1] - old_state[1]) * (new_state[1] - old_state[1]) + + (new_state[2] - old_state[2]) * (new_state[2] - old_state[2]) + + (new_state[3] - old_state[3]) * (new_state[3] - old_state[3]) + + (new_state[4] - old_state[4]) * (new_state[4] - old_state[4]) + + (new_state[5] - old_state[5]) * (new_state[5] - old_state[5]); + + if (delta > .00003) { + + old_state[0] = new_state[0]; + old_state[1] = new_state[1]; + old_state[2] = new_state[2]; + + old_state[3] = new_state[3]; + old_state[4] = new_state[4]; + old_state[5] = new_state[5]; + } + + s->X = old_state[0].to_fix(); + s->Y = old_state[1].to_fix(); + s->Z = old_state[2].to_fix(); + + s->alpha = old_state[3].to_fix(); + s->beta = old_state[4].to_fix(); + s->gamma = old_state[5].to_fix(); + + // if ( delta > 40 ) { + // mout << "Holy cow, batman, delta = " << delta << "\n"; + // getch(); + // } + + } // End of check for pelvis or not... + + //#ifdef EDMS_SHIPPABLE + // else { mout << "Pelvic Viewpoint: physics handle " << ph << ", object #" << on << " isn't a Pelvis + // model!\n"; + // mout << "Is is really a " << I[on][30] << " located at (" << S[on][0][0] << "," << S[on][1][0] << ")!\n"; + //} #endif +} + +// Utilities for the weak spirited... +// ================================== +void EDMS_set_pelvis_parameters(physics_handle ph, Pelvis *p) { + Q mass, hardness, size, pep, height, gravity; + + int32_t cyber_space = 0; + + mass.fix_to(p->mass); + size.fix_to(p->size); + hardness.fix_to(p->hardness); + pep.fix_to(p->pep); + gravity.fix_to(p->gravity); + height.fix_to(p->height); + if (height > 3 * size) + height = 3 * size; + + int32_t on = physics_handle_to_object_number(ph); + + // hardness = hardness*(mass*4/size); + hardness = hardness * (mass * HARD_FAC / size); + I[on][IDOF_PELVIS_K] = hardness; + I[on][IDOF_PELVIS_D] = 3 * sqrt(I[on][IDOF_PELVIS_K] * mass); + I[on][IDOF_PELVIS_RADIUS] = size; + I[on][IDOF_PELVIS_ROLL_DRAG] = pep * mass; + I[on][IDOF_PELVIS_MASS_RECIP] = 1. / mass; + I[on][IDOF_PELVIS_GRAVITY] = gravity; + I[on][IDOF_PELVIS_MASS] = mass; + I[on][IDOF_PELVIS_MOI_RECIP] = 1. / (.4 * mass * size * size); + I[on][IDOF_PELVIS_ROT_DRAG] = 5. * (1. / I[on][IDOF_PELVIS_MOI_RECIP]); + I[on][IDOF_PELVIS_MOI] = .4 * mass * size * size; + // mout << "I29 from set! " << I[on][29] << "\n"; + // if ( I[on][30] != PELVIS ) mout << "!EDMS: You just screwed up the pelvis...\n"; + + cyber_space = p->cyber_space; + + // Turn on Cyber Space... + // ---------------------- + cyber_space = p->cyber_space; + I[on][7] = I[on][15] = 0; + if (cyber_space < 0 || cyber_space > 2) + cyber_space = 0; // Hey, why you do that? + + I[on][10] = cyber_space; + // Won't need to be reset! + // I[on][0] = I[on][6] = height - size; + + // Turn lean control off for skates! + // --------------------------------- + if (I[on][10] > 0) + I[on][15] = I[on][7] = 0; +} + +// And the weak minded... +// ====================== +void EDMS_get_pelvis_parameters(physics_handle ph, Pelvis *p) { + int32_t on = physics_handle_to_object_number(ph); + + p->pep = (I[on][IDOF_PELVIS_ROLL_DRAG] / I[on][IDOF_PELVIS_MASS]).to_fix(); + p->size = I[on][IDOF_PELVIS_RADIUS].to_fix(); + // p -> hardness = ( I[on][20]*I[on][22]/(I[on][26]*4) ).to_fix(); + p->hardness = (I[on][IDOF_PELVIS_K] * I[on][IDOF_PELVIS_RADIUS] / (I[on][IDOF_PELVIS_MASS] * HARD_FAC)).to_fix(); + p->mass = I[on][IDOF_PELVIS_MASS].to_fix(); + p->gravity = I[on][IDOF_PELVIS_GRAVITY].to_fix(); + p->cyber_space = I[on][10].to_int(); + p->height = (I[on][6] + I[on][IDOF_PELVIS_RADIUS]).to_fix(); +} + +// And the compression test for terrain "traps..." +// =============================================== +fix EDMS_get_pelvis_damage(physics_handle ph, fix delta_t) { + int32_t object; + Q worker_bee_buzz_buzz = 0; + + object = ph2on[ph]; // As stupid as it gets... + worker_bee_buzz_buzz = I[object][14]; + + // FIXME What going on there? + I[object][14] = 0; + + return fix_mul(delta_t, I[object][14].to_fix()); +} + +} // End of extern "C" for the &^%$@% compiler... diff --git a/engine/src/Libraries/EDMS/Source/MODELS/pelvis.cc b/engine/src/Libraries/EDMS/Source/MODELS/pelvis.cc new file mode 100644 index 0000000..151e6a0 --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/MODELS/pelvis.cc @@ -0,0 +1,804 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Pelvis.cc is a destroyed biped. R.I.P. Life is pain, and in addition this should make it +// less fun to move in the indoor games. You should probably use the Biped instead unless +// you are a Twinkie and thus more concerned with inventory systems than dynamic fun... + +// Comment rescinded. Pelvis now fun. End of line. +// ================================================= + +// Seamus, Nov 2, 1993... +// ======================== + +#include +////#include +#include "edms_int.h" //This is the object type library. It is universal. +#include "idof.h" + +// Super secret Church-Blackley Boundary Condition Descriptor (BCD)... +// =================================================================== +// extern "C" { + +#include "ss_flet.h" + +Q EDMS_CYBER_FLOW1X = 100; +Q EDMS_CYBER_FLOW2X = 200; +Q EDMS_CYBER_FLOW3X = 270; + +int32_t EDMS_BCD = 0; +bool pelvis_is_climbing = false; +int32_t edms_ss_head_bcd_flags; + +fix hacked_head_bob_1 = fix_make(1, 0); +fix hacked_head_bob_2 = fix_make(1, 0); + +//} + +#define EDMS_DIV_ZERO_TOLERANCE .0005 + +// For lean-o-meter... +// ------------------- +static Q V_ceiling[3], V_floor[3], V_wall[3]; + +// State information and utilities... +// ================================== +extern EDMS_Argblock_Pointer A; +extern Q S[MAX_OBJ][7][4], I[MAX_OBJ][DOF_MAX]; +extern int32_t no_no_not_me[MAX_OBJ]; + +// Functions... +// ============ +extern void (*idof_functions[MAX_OBJ])(int32_t), (*equation_of_motion[MAX_OBJ][7])(int32_t); + +// Callbacks themselves... +// ----------------------- +extern void (*EDMS_wall_contact)(physics_handle caller); + +static Q fix_one = 1., point_five = .5, two_pi = 6.283185; + +// Just a thought... +// ================= +static Q object0, object1, object2, object3, object4, // Howzat?? + object5, object6, object7, object8, object9, object10, object11, object12, object13, object14, object15, object16, + object17, object18, object19, object20, object21, object22; + +static Q sin_alpha = 0, cos_alpha = 0, sin_beta = 0, cos_beta = 0, sin_gamma = 0, cos_gamma = 0, lp_x = 0, lp_y = 0, + lp_z = 0, Fmxm = 0, Fmym = 0, Fmzm = 0, T_beta = 0, T_gamma = 0; + +static Q io17 = 0, io18 = 0, io19 = 0; + +static Q checker = 0, wall_check = 0; + +// Global for head information... +// ============================== +Q head_delta[3], head_kappa[3], body_delta[3], body_kappa[3]; + +// Storage for running feel... +// =========================== +Q bob_arg = 0; + +#pragma require_prototypes off + +// Here are the internal degrees of freedom: +// ========================================= +void pelvis_idof(int32_t object) { + + // attemp to speed up something + Q *i_object = I[object]; + Q temp_Q; + + // To do the head motion, collisions, and climbing... + // -------------------------------------------------- + void get_head_of_death(int32_t), get_body_of_death(int32_t), do_climbing(int32_t object); + + // Call me instead of having special code everywhere... + // ==================================================== + extern void shall_we_dance(int32_t object, Q &result0, Q &result1, Q &result2); + + pelvis_is_climbing = false; + + indoor_terrain(A[object][0][0], // Get the info... + A[object][1][0], A[object][2][0], i_object[22], + on2ph[object], TFD_FULL); + + V_ceiling[0].fix_to(terrain_info.cx); // Put it in... + V_ceiling[1].fix_to(terrain_info.cy); + V_ceiling[2].fix_to(terrain_info.cz); + + V_floor[0].fix_to(terrain_info.fx); + V_floor[1].fix_to(terrain_info.fy); + V_floor[2].fix_to(terrain_info.fz); + + Q mag = i_object[18] * i_object[18] + i_object[19] * i_object[19]; + if (mag < .1 && abs(V_floor[0]) < .05 * i_object[22] && abs(V_floor[1]) < .05 * i_object[22]) { + V_floor[1].val = 0; // Turns on SlopeStand(tm)... + V_floor[0].val = 0; + } + + V_wall[0].fix_to(terrain_info.wx); + V_wall[1].fix_to(terrain_info.wy); + V_wall[2].fix_to(terrain_info.wz); + + object0.val = V_wall[0].val + V_floor[0].val + V_ceiling[0].val; // V_raw... + object1.val = V_wall[1].val + V_floor[1].val + V_ceiling[1].val; + object2.val = V_wall[2].val + V_floor[2].val + V_ceiling[2].val; + + // checker = sqrt(object0*object0 + object1*object1 + object2*object2); + checker.val = fix_sqrt(fix_mul(object0.val, object0.val) + fix_mul(object1.val, object1.val) + + fix_mul(object2.val, object2.val)); + + if (checker > EDMS_DIV_ZERO_TOLERANCE) { // To get primitive... + object3.val = fix_div(fix_one.val, checker.val); + object9.val = fix_make(1, 0); // Are we in the rub??? + } else + checker.val = object9.val = 0; + + if (i_object[10] == 2) + object9.val = fix_make(1, 0); // Cyberspace... + + object4.val = fix_mul(object3.val, object0.val); // The primitive V_n... + object5.val = fix_mul(object3.val, object1.val); + object6.val = fix_mul(object3.val, object2.val); + + object7.val = fix_mul(i_object[21].val, + (fix_mul(A[object][0][1].val, object4.val) // Delta_magnitude... + + fix_mul(A[object][1][1].val, object5.val) + fix_mul(A[object][2][1].val, object6.val))); + + object8.val = i_object[20].val; + + if (i_object[10] > 0) { + object7.val = fix_mul(object7.val, fix_make(2, 0)); + object8.val = fix_mul(object8.val, fix_make(2, 0)); + } + + object4.val = fix_mul(object7.val, object4.val); // Delta... + object5.val = fix_mul(object7.val, object5.val); + object6.val = fix_mul(object7.val, object6.val); + + // CONTROL... + // ========== + + // Head motion for fucking hacking... + // ---------------------------------- + Q x_ease = A[object][0][0] - S[object][0][0]; + Q y_ease = A[object][1][0] - S[object][1][0]; + Q bob_delta = sqrt(x_ease * x_ease + y_ease * y_ease); + Q bob_speed = sqrt(A[object][0][1] * A[object][0][1] + A[object][1][1] * A[object][1][1]); + + bob_arg.val += fix_div(fix_mul(fix_make(5, 0), bob_delta.val), (bob_speed.val + fix_make(1, 0))); + + if (bob_arg > two_pi) + bob_arg.val = bob_arg.val - two_pi.val; + + Q bob_fac = bob_speed * abs(sin(bob_arg)); + +#define EDMS_HEAD_BOB_HEIGHT 2 + + if (bob_fac > EDMS_HEAD_BOB_HEIGHT) + bob_fac = EDMS_HEAD_BOB_HEIGHT; + + bob_fac.val = fix_make(EDMS_HEAD_BOB_HEIGHT, 0) - fix_mul(0x09999, bob_fac.val); // 0x9999 = .6 + + if (i_object[10] > 0) + bob_fac = 1; + + io18.val = fix_mul(i_object[18].val, bob_fac.val); + io19.val = fix_mul(i_object[19].val, bob_fac.val); + io17.val = i_object[17].val; + + // Let's not power through the walls anymore... + // -------------------------------------------- + io18 *= (V_wall[0].val == 0); + io19 *= (V_wall[1].val == 0); + io17 *= (V_ceiling[2].val == 0); + if ((V_floor[2].val == 0) && (io17.val > 0)) + io17.val = 0; + + // Cyberama... + // ----------- + if ((object9.val == 0) && (io17.val >= 0) && (i_object[25].val > 0x08000)) // 0x08000 = .5 + io18.val = io19.val = io17.val = 0; + + // Here are collisions with other objects... + // ========================================= + shall_we_dance(object, object10, object11, object12); + + object10.val = fix_mul(object10.val, i_object[20].val); // More general than it was... + object11.val = fix_mul(object11.val, i_object[20].val); + object12.val = fix_mul(object12.val, i_object[20].val); + + // Let's not power through the walls anymore... + // -------------------------------------------- + // object10 *= ( (V_wall[0].val< 0x028f) && (V_wall[0].val>-0x028f)); // 0x028f = 0.01 + if (!((V_wall[0].val < 0x028f) && (V_wall[0].val > -0x028f))) + object10.val = 0; + // object11 *= ( (V_wall[1].val<0x028f) && (V_wall[1].val>-0x028f)); + if (!((V_wall[1].val < 0x028f) && (V_wall[1].val > -0x028f))) + object11.val = 0; + + // Back to business... + // =================== + sincos(A[object][3][0], &sin_alpha, &cos_alpha); // Positive for local... + sincos(A[object][4][0], &sin_beta, &cos_beta); + sincos(A[object][5][0], &sin_gamma, &cos_gamma); + + // The head... + // =========== + int32_t edms_ss_bcd_flags_save = ss_edms_bcd_flags; // Save off info for after head call... + int32_t edms_ss_param_save = ss_edms_bcd_param; + + Q head_check_x = 0; + Q head_check_y = 0; + + get_head_of_death(object); + + if (terrain_info.wx == 0) + head_check_x = 1; + if (terrain_info.wy == 0) + head_check_y = 1; + + // io18 *= head_check_x; + // io19 *= head_check_y; + + edms_ss_head_bcd_flags = ss_edms_bcd_flags; + if (terrain_info.cz != 0 || head_kappa[2] != 0) + i_object[17] = io17 = 0; + + // The Body... + // =========== + get_body_of_death(object); + // io18 *= ( body_kappa[0] == 0 ); + // io19 *= ( body_kappa[1] == 0 ); + if (body_kappa[0].val != 0) + io18.val = 0; + if (body_kappa[1].val != 0) + io19.val = 0; + + ss_edms_bcd_flags = edms_ss_bcd_flags_save; + ss_edms_bcd_param = edms_ss_param_save; + + // Do climbing... + // ============== + do_climbing(object); + + // Fateful attempt(Jump)... + // ======================== + // object18 = 800*(io17>0)*(object9>0)*( io17 - A[object][2][1] ); //Jump... + object18.val = 0; + if (io17.val > 0 && object9.val > 0) + object18.val = fix_mul(fix_make(800, 0), (io17.val - A[object][2][1].val)); + + // Jump jets... + // ------------ + if ((io17 < 0) && (terrain_info.cz == 0)) + object18 = 800 * (-io17 - A[object][2][1]); // Jump jets... + + // Climbing overriden with repulsors... + // ==================================== + if (ss_edms_bcd_flags & SS_BCD_REPUL_ON) { + + // Get the speed... + Q repulsor_speed = 21; + if ((ss_edms_bcd_flags & SS_BCD_REPUL_SPD) == SS_BCD_REPUL_NORM) + repulsor_speed = 7; + + // Assume we're going up, unless... + if ((ss_edms_bcd_flags & SS_BCD_REPUL_TYPE) == SS_BCD_REPUL_DOWN) + repulsor_speed *= -.5; + + // The parameter should be the desired height.... + Q repul_height; + repul_height.fix_to(ss_edms_bcd_param); + + Q nearness_or_something = repul_height - A[object][2][0]; + if (abs(nearness_or_something) <= .333) + repulsor_speed *= 3 * nearness_or_something; + + io17 = repulsor_speed; + if ((abs(A[object][2][1] - i_object[17]) > .6 * i_object[17]) && (terrain_info.cz == 0) && + (repulsor_speed >= 0)) + io17 += 50 * i_object[17]; + + object18 = i_object[26] * ((io17 - A[object][2][1]) + i_object[25]); + + if (abs(io18) < .01) + io18 = i_object[18] * (V_wall[0] == 0); + if (abs(io19) < .01) + io19 = i_object[19] * (V_wall[1] == 0); + + object9 = 1; + } + + // Do climbing... + // ============== + do_climbing(object); + + // Crouch torso bend thang and boogie boogie boogie... + // =================================================== + if ((i_object[7] > 0.0) || (i_object[0] < i_object[6])) + i_object[0] = i_object[6] * (1 - .636 * abs(S[object][4][0])); // Crouch... + else + i_object[0] = i_object[6]; + + // Cyberspace for real... + // ====================== + Q drug_addict0 = i_object[IDOF_PELVIS_ROLL_DRAG] * A[object][0][1]; + Q drug_addict1 = i_object[IDOF_PELVIS_ROLL_DRAG] * A[object][1][1]; + if (abs(io18) == 0 && i_object[10] > 0) + drug_addict0 *= .2; // Skateware drag reduction... + if (abs(io19) == 0 && i_object[10] > 0) + drug_addict1 *= .2; + + // Pelvis specifics... + // =================== + object20 = object8 * object0 - object4 + head_kappa[0] - head_delta[0] + io18 + body_kappa[0] - + body_delta[0] // F_mxyz... + + object9 * (-drug_addict0) + object10; + + object21 = object8 * object1 - object5 + head_kappa[1] - head_delta[1] + io19 + body_kappa[1] - body_delta[1] + + object9 * (-drug_addict1) + object11; + + object22 = object8 * object2 - (object18 == 0) * object6 + head_kappa[2] - head_delta[2] + object18 + + body_kappa[2] - body_delta[2] + object9 * (-i_object[23] * A[object][2][1]) + object12; + + // Damage control... + // ================= + Q dam0 = object8 * object0 - object4 + head_kappa[0] - head_delta[0]; + Q dam1 = object8 * object1 - object5 + head_kappa[1] - head_delta[1]; + Q dam2 = object8 * object2 - (object18 == 0) * object6 + head_kappa[2] - head_delta[2]; + + i_object[14] = abs(dam0) + abs(dam1) + abs(dam2) - 2 * i_object[26] * i_object[25]; // Damage?? + if (i_object[14] > 0) + i_object[14] *= i_object[IDOF_PELVIS_MASS_RECIP] * (io17 < .5); + else + i_object[14] = 0; + + // Is there a projectile hit? + // ========================== + if (i_object[35] > 0) { + + // Let's not power through the walls anymore... + // -------------------------------------------- + i_object[32] *= ((V_wall[0] < 0.01) && (V_wall[0] > -0.01)); + i_object[33] *= ((V_wall[1] < 0.01) && (V_wall[1] > -0.01)); + i_object[34] *= ((V_ceiling[2] < 0.01) && (V_ceiling[2] > -0.01)); + + object20 += i_object[32]; + object21 += i_object[33]; + object22 += i_object[34]; + + i_object[35] = 0; + i_object[32] = 0; + i_object[33] = 0; + i_object[34] = 0; + } + + Fmxm = object20 * cos_alpha + object21 * sin_alpha; // Locals... + Fmym = -object20 * sin_alpha + object21 * cos_alpha; + + lp_z = -.1 * i_object[0] * cos_beta * cos_gamma; + + Q Head_tau_beta = -.1 * i_object[0] * sin_beta * (head_kappa[2] - head_delta[2]), + Head_tau_gamma = -.1 * i_object[0] * sin_gamma * (head_kappa[2] - head_delta[2]); + + if (((V_wall[1] != 0) && (head_check_y == 0)) || ((V_wall[0] != 0) && (head_check_x == 0))) + i_object[15] = 0; + + T_beta = -(lp_z * Fmxm) + i_object[7] + Head_tau_beta; // Actual torques... + T_gamma = -(-Fmym * lp_z) + .04 * i_object[16] * +Head_tau_gamma; + + // Kickbacks... + // ============ + if (abs(i_object[8]) > 0) { + T_beta -= cos_alpha * i_object[8] + sin_alpha * i_object[9]; + T_gamma = -sin_alpha * i_object[8] + cos_alpha * i_object[9]; + + object20 -= i_object[8]; // For zero g... + object21 -= i_object[9]; + + i_object[8] = i_object[9] = 0; + } + + object17 = i_object[28] * (1 + 1.2 * (i_object[16] == 0)); // 3 is 2 + + // Angular play (citadel) ... + // ========================== + if (S[object][3][0] > two_pi) + S[object][3][0] -= two_pi; + if (S[object][3][0] < -two_pi) + S[object][3][0] += two_pi; + + // Try the equations of motion here for grins... + // ============================================= + S[object][0][2] = i_object[IDOF_PELVIS_MASS_RECIP] * (object20); + S[object][1][2] = i_object[IDOF_PELVIS_MASS_RECIP] * (object21); + S[object][2][2] = i_object[IDOF_PELVIS_MASS_RECIP] * (object22)-i_object[IDOF_PELVIS_GRAVITY]; + S[object][3][2] = i_object[IDOF_PELVIS_MOI_RECIP] * (i_object[16] - object17 * A[object][3][1]); + S[object][4][2] = i_object[IDOF_PELVIS_MOI_RECIP] * (T_beta - 1.5 * i_object[1] * A[object][4][0] /**(1-.5*(i_object[10]==1))*/ + - .8 * i_object[2] * A[object][4][1] /**(1-.5*(i_object[10]==1))*/); + + S[object][5][2] = i_object[IDOF_PELVIS_MOI_RECIP] * (T_gamma - i_object[1] * A[object][5][0] /**(1-.5*(i_object[10]==1))*/ + - .8 * i_object[2] * A[object][5][1] /**(1-.5*(i_object[10]==1))*/ + + i_object[15]); + + // That's all, folks... + // ==================== +} + +// Here we'll get the head information we all want so badly... +// =========================================================== +void get_head_of_death(int32_t object) { + Q *i_object = I[object]; + Q vec0, vec1, vec2, test, mul, vv0, vv1, vv2, dmag, kmag; + + Q offset_x = i_object[0] * sin(A[object][4][0]), offset_y = -1.5 * i_object[0] * sin(A[object][5][0]), + offset_z = i_object[0] * cos(A[object][4][0]) * cos(A[object][5][0]); + + Q sin_alpha = 0, cos_alpha = 0; + + Q final_x = 0, final_y = 0; + + sincos(-A[object][3][0], &sin_alpha, &cos_alpha); + final_x = cos_alpha * offset_x + sin_alpha * offset_y; + final_y = -sin_alpha * offset_x + cos_alpha * offset_y; + + indoor_terrain(A[object][0][0] + final_x, A[object][1][0] + final_y, A[object][2][0] + offset_z, .75 * i_object[22], + -1 /*on2ph[object]*/, TFD_FULL); + + Q mag = i_object[18] * i_object[18] + i_object[19] * i_object[19]; + if (mag < .1 && abs(V_floor[0]) < .05 * i_object[22] && abs(V_floor[1]) < .05 * i_object[22]) { + terrain_info.fx = terrain_info.fy = 0; + } + + vec0.fix_to(terrain_info.fx + terrain_info.cx + terrain_info.wx); + vec1.fix_to(terrain_info.fy + terrain_info.cy + terrain_info.wy); + vec2.fix_to(terrain_info.fz + terrain_info.cz + terrain_info.wz); + + test = sqrt(vec0 * vec0 + vec1 * vec1 + vec2 * vec2); + + if (test > EDMS_DIV_ZERO_TOLERANCE) + mul = fix_one / test; // To get primitive... + else + test = mul = 0; + + vv0 = mul * vec0; // The primitive V_n... + vv1 = mul * vec1; + vv2 = mul * vec2; + + dmag = i_object[IDOF_PELVIS_D] * (A[object][0][1] * vv0 // Delta_magnitude... + + A[object][1][1] * vv1 + A[object][2][1] * vv2); + + head_delta[0] = dmag * vv0; // Delta... + head_delta[1] = dmag * vv1; + head_delta[2] = dmag * vv2; + + // if (test < .5*i_object[22]) kmag = i_object[20]; //Omega_magnitude... + // else kmag = i_object[20]/test; + kmag = i_object[IDOF_PELVIS_K]; + + head_kappa[0] = kmag * vec0; + head_kappa[1] = kmag * vec1; + head_kappa[2] = kmag * vec2; +} + +void get_body_of_death(int32_t object) { + Q *i_object = I[object]; + + Q vec0, vec1, vec2, test, mul, vv0, vv1, vv2, dmag, kmag; + + Q half_height = .5 * i_object[0]; + + Q offset_x = half_height * sin(A[object][4][0]), offset_y = -1.5 * half_height * sin(A[object][5][0]), + offset_z = half_height * cos(A[object][4][0]) * cos(A[object][5][0]); + + Q sin_alpha = 0, cos_alpha = 0; + + Q final_x = 0, final_y = 0; + + sincos(-A[object][3][0], &sin_alpha, &cos_alpha); + final_x = cos_alpha * offset_x + sin_alpha * offset_y; + final_y = -sin_alpha * offset_x + cos_alpha * offset_y; + + indoor_terrain(A[object][0][0] + final_x, A[object][1][0] + final_y, A[object][2][0] + offset_z, .33 * i_object[0], + -1 /*on2ph[object]*/, TFD_FULL); + + // Zero result! + // ============ + body_kappa[0] = body_kappa[1] = body_kappa[2] = 0; + body_delta[0] = body_delta[1] = body_delta[2] = 0; + + // Do ANYTHING? + // ------------ + Q abtotal = abs(terrain_info.fx) + abs(terrain_info.fy) + abs(terrain_info.fz); + abtotal += abs(terrain_info.wx) + abs(terrain_info.wy) + abs(terrain_info.wz); + abtotal += abs(terrain_info.cx) + abs(terrain_info.cy) + abs(terrain_info.cz); + if (abtotal != 0) { + Q mag = i_object[18] * i_object[18] + i_object[19] * i_object[19]; + if (mag < .1 && abs(V_floor[0]) < .05 * i_object[22] && abs(V_floor[1]) < .05 * i_object[22]) + terrain_info.fx = terrain_info.fy = 0; + + vec0.fix_to(terrain_info.fx + terrain_info.cx + terrain_info.wx); + vec1.fix_to(terrain_info.fy + terrain_info.cy + terrain_info.wy); + vec2.fix_to(terrain_info.fz + terrain_info.cz + terrain_info.wz); + + test = sqrt(vec0 * vec0 + vec1 * vec1 + vec2 * vec2); + + if (test > EDMS_DIV_ZERO_TOLERANCE) + mul = fix_one / test; // To get primitive... + else + test = mul = 0; + + vv0 = mul * vec0; // The primitive V_n... + vv1 = mul * vec1; + vv2 = mul * vec2; + + vec2 = vv2 = 0; + + dmag = i_object[IDOF_PELVIS_D] * (A[object][0][1] * vv0 // Delta_magnitude... + + A[object][1][1] * vv1 + A[object][2][1] * vv2); + + body_delta[0] = dmag * vv0; // Delta... + body_delta[1] = dmag * vv1; + body_delta[2] = dmag * vv2; + + kmag = i_object[20]; + + body_kappa[0] = kmag * vec0; + body_kappa[1] = kmag * vec1; + body_kappa[2] = kmag * vec2; + + } // Do NOTHING... +} + +// Climbing stuff also removed for speed of compilations... +// ======================================================== +void do_climbing(int32_t object) { + Q *i_object = I[object]; + + // Hellishness... + // ============== + if ((i_object[17] > 0) && + ((ss_edms_bcd_flags & SS_BCD_MISC_CLIMB) || (edms_ss_head_bcd_flags & SS_BCD_MISC_CLIMB))) { + Q ass = sqrt((.05 * i_object[18]) * i_object[18] + (.05 * i_object[19]) * i_object[19]); + Q ratio = i_object[18] * object0 + i_object[19] * object1; + + if (ratio > 0) + ass = 0; + + pelvis_is_climbing = true; + + if (checker > 0) { + io17 = .02 * ass; // + 100*( .2*i_object[22] - V_[floor][2] ); + if ((terrain_info.cz != 0)) + io17 = 0; + io18 = -.4 * i_object[IDOF_PELVIS_RADIUS] * object0 * object8 / checker + .5 * i_object[18]; + io19 = -.4 * i_object[IDOF_PELVIS_RADIUS] * object1 * object8 / checker + .5 * i_object[19]; + i_object[16] *= .5; + + // Set the mojo... + // =============== + object18 = 800 * (io17 > 0) * (io17 - A[object][2][1]); + } + } + + // AutoClimbing(tm) is for wussies (is superseeded by climbing)... + // =============================================================== + else if ((ss_edms_bcd_flags & SS_BCD_MISC_STAIR) /*&& (i_object[17] == 0) (io17==0) */) { + if ((checker > 0) && (abs(i_object[18]) + abs(i_object[19]) > .01)) { + Q ratio = (i_object[18] + A[object][0][1]) * object0 + (i_object[19] + A[object][1][1]) * object1; + + if (ratio <= 0) { + io17 = .5; + + io18 = -.3 * i_object[IDOF_PELVIS_RADIUS] * object0 * object8 / checker + .2 * i_object[18]; + io19 = -.3 * i_object[IDOF_PELVIS_RADIUS] * object1 * object8 / checker + .2 * i_object[19]; + + // Set the mojo... + // =============== + object18 = 800 * (io17 > 0) * (io17 - A[object][2][1]); + } else { + io18 = i_object[18]; + io19 = i_object[19]; + } + } + } +} // End of climbing nonsense... + +// We might for now want to set some external forces on the pelvis... +// ================================================================== +void pelvis_set_control(int32_t pelvis, Q forward, Q turn, Q sidestep, Q lean, Q jump, int32_t crouch) { + const Q pi_by_two = 1.5707; // Yea, flixpoint... + + sincos(S[pelvis][3][0], &object0, &object1); + + // Get rid of it all... + // -------------------- + I[pelvis][15] = I[pelvis][16] = I[pelvis][17] = I[pelvis][18] = I[pelvis][19] = I[pelvis][7] = 0; + + // Here's the thrust of the situation... + // ------------------------------------- + I[pelvis][18] = forward * object1 * I[pelvis][IDOF_PELVIS_MASS]; + I[pelvis][19] = forward * object0 * I[pelvis][IDOF_PELVIS_MASS]; + + // And the sidestep is off by pi/two... + // ------------------------------------ + sincos((S[pelvis][3][0] - pi_by_two), &object0, &object1); + I[pelvis][18] += sidestep * object1 * I[pelvis][IDOF_PELVIS_MASS]; + I[pelvis][19] += sidestep * object0 * I[pelvis][IDOF_PELVIS_MASS]; + + // And the turn of the... + // ---------------------- + I[pelvis][16] = turn * I[pelvis][IDOF_PELVIS_MOI]; + + // Jump jets of joy... + // ------------------- + if (jump > 0) + I[pelvis][17] = .003 * I[pelvis][IDOF_PELVIS_MASS] * jump; + if (jump < 0) + I[pelvis][17] = .0006 * I[pelvis][IDOF_PELVIS_MASS] * jump; + + // And finally leaning about... + // ---------------------------- + I[pelvis][15] = .04 * lean * I[pelvis][1]; // Exactly the angle! + + // Crouching (overpowers jumping )... + // ---------------------------------- + if (crouch > 0) + I[pelvis][7] = .20 * crouch * I[pelvis][1]; + + // Wake up... + // ========== + no_no_not_me[pelvis] = + (abs(I[pelvis][15]) + abs(I[pelvis][16]) + abs(I[pelvis][17]) + abs(I[pelvis][18]) + abs(I[pelvis][19]) > 0) || + (no_no_not_me[pelvis] == 1); +} + +// Sets up everything needed to manufacture a pelvis with initial state vector +// init_state[][] and EDMS motion parameters params[] into soliton. Returns the +// object number, or else a negative error code (see Soliton.CPP for error handling and codes). +// ============================================================================================ +int32_t make_pelvis(Q init_state[6][3], Q params[10]) { + // Have some variables... + // ====================== + int32_t object_number = -1, // Three guesses... + error_code = -1; // Guilty until... + + // We need ignorable coordinates... + // ================================ + extern void null_function(int); + + // First find out which object we're going to be... + // ================================================ + while (S[++object_number][0][0] > END) + ; // Jon's first C trickie... + + // Is it an allowed object number? Are we full? Why are we here? Is there a God? + // ============================================================================== + if (object_number < MAX_OBJ) { + + // Now we can create the pelvis: first dump the initial state vector... + // ===================================================================== + for (int32_t coord = 0; coord < 6; coord++) { + for (int32_t deriv = 0; deriv < 3; deriv++) { // Has alpha now... + S[object_number][coord][deriv] = A[object_number][coord][deriv] = + init_state[coord][deriv]; // For collisions... + } + } + + // Put in the appropriate pelvis parameters... + // =========================================== + for (int copy = 0; copy < 10; copy++) { + I[object_number][copy + 20] = params[copy]; + } + + I[object_number][IDOF_MODEL] = PELVIS; // Hey, you are what you eat. + + // We need some information that won't fit in the usual areas... + // ============================================================= + // I[object_number][0] = //For reference... + I[object_number][6] = .5 * I[object_number][IDOF_PELVIS_RADIUS]; + I[object_number][1] = 20 * I[object_number][IDOF_PELVIS_MOI]; + I[object_number][2] = 4 * sqrt(I[object_number][IDOF_PELVIS_MOI] * I[object_number][1]); + + // Put in the collision information... + // =================================== + I[object_number][31] = I[object_number][IDOF_PELVIS_RADIUS]; + I[object_number][32] = I[object_number][33] = I[object_number][34] = I[object_number][35] = 0; + I[object_number][36] = I[object_number][IDOF_PELVIS_MASS_RECIP]; // Shrugoff "mass"... + I[object_number][IDOF_COLLIDE] = -1; + I[object_number][IDOF_AUTODESTRUCT] = 0; // No kill I... + + // Zero the control initially... + // ============================= + I[object_number][7] = I[object_number][8] = I[object_number][9] = I[object_number][15] = I[object_number][16] = + I[object_number][18] = I[object_number][19] = I[object_number][17] = 0; + + // Now tell Soliton where to look for the equations of motion... + // ============================================================= + idof_functions[object_number] = pelvis_idof; + + equation_of_motion[object_number][0] = // Nice symmetries, huh. + equation_of_motion[object_number][1] = equation_of_motion[object_number][2] = + equation_of_motion[object_number][3] = equation_of_motion[object_number][4] = + equation_of_motion[object_number][5] = null_function; + + // Wake me up... + // ============= + no_no_not_me[object_number] = 1; + + // Things seem okay... + // =================== + error_code = object_number; + } + + // Inform the caller... + // ==================== + return error_code; +} + +// ATTENZIONE: Los parametros del model son: +// ========================================== + +// Number | Comment +// -------------------- +// 0 | K +// 1 | d +// 2 | Radius +// 3 | Rolling Drag +// 4 | 1/Mass +// 5 | gravity +// 6 | mass +// 7 | 1/moi +// 8 | rotational drag +// 9 | moi +// ========================================== +// So there. + +// For mark... +// ----------- +extern "C" { + +bool EDMS_pelvis_is_climbing() +{ + return pelvis_is_climbing; +} + +void EDMS_lean_o_meter(physics_handle ph, fix &lean, fix &crouch) { + + lean = crouch = 0; + + // Are you for real? + // ----------------- + if (ph > -1) { + + int32_t on = ph2on[ph]; + + // Are you a pelvis... + // ------------------- + if (I[on][IDOF_MODEL] == PELVIS) { + lean = S[on][5][0].to_fix(); + crouch = I[on][0].to_fix() - 3 * V_floor[2].to_fix(); + + } // Pelvis check... + + } // For real... +} + +#pragma require_prototypes on +} diff --git a/engine/src/Libraries/EDMS/Source/MODELS/pelvis.h b/engine/src/Libraries/EDMS/Source/MODELS/pelvis.h new file mode 100644 index 0000000..2bbdbd5 --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/MODELS/pelvis.h @@ -0,0 +1,22 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// This seems silly now, but later it will all make sense, sensei... +// ================================================================= +int32_t make_pelvis(Q init_state[6][3], Q params[10]); +void pelvis_set_control(int32_t pelvis, Q forward, Q turn, Q sidestep, Q lean, Q jump, int32_t crouch); diff --git a/engine/src/Libraries/EDMS/Source/MODELS/robot.cc b/engine/src/Libraries/EDMS/Source/MODELS/robot.cc new file mode 100644 index 0000000..fa09165 --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/MODELS/robot.cc @@ -0,0 +1,547 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Robot.cc is a test object for the Citadel physics system. It uses the Citadel database +// for B/C, and should be fairly simple and robust. Use the vector integrator! +// ============================================================================ + +// Seamus, June 29, 1993... +// ======================== + +#include +//#include +#include "edms_int.h" //This is the object type library. It is universal. +#include "idof.h" +//#ifdef EDMS_SHIPPABLE +////#include +//#endif + +#include "edms_chk.h" + +// extern "C" { +//#include +//#include +#include "ss_flet.h" +//} + +// State information and utilities... +// ================================== +extern EDMS_Argblock_Pointer A; +extern Q S[MAX_OBJ][7][4], I[MAX_OBJ][DOF_MAX]; +extern int32_t no_no_not_me[MAX_OBJ]; + +#define SOLITION_FRAME_CNT + +// Functions... +// ============ +extern void (*idof_functions[MAX_OBJ])(int32_t), (*equation_of_motion[MAX_OBJ][7])(int32_t); + +// Callbacks themselves... +// ----------------------- +extern void (*EDMS_object_collision)(physics_handle caller, physics_handle victim, int32_t badness, int32_t DATA1, + int32_t data2, fix loc[3]), + (*EDMS_wall_contact)(physics_handle caller); + +// extern int are_you_there( int ); //Collisions... +// extern int check_for_hit( int ); + +static Q fix_one = 1., point_five = .5, two_pi = 6.283185; + +// Just a thought... +// ================= +static Q object0, object1, object2, object3, object4, // Howzat?? + object5, object6, object7, object8, object9, object10, object11, object12, object13, object14, object15, object16, + object17, object18, object19; + +// First, here are the equations of motion (outdated!)... +// ====================================================== +int32_t EDMS_robot_global_badness_indicator = 0; + +// Variables that are NOT on the stack... +// ====================================== +static Q A00, A10, A20, A30, A01, A11, A21, A31; + +static Q checker, check0, check1, check2, V_wall0, V_wall1; + +static Q drug, butt; + +const Q wt_pos = 0.001, wt_neg = -wt_pos; + +#pragma require_prototypes off + +// Here are the internal degrees of freedom: +// ========================================= +void robot_idof(int32_t object) { + + // Call me instead of having special code everywhere... + // ==================================================== + extern void shall_we_dance(int object, Q &result0, Q &result1, Q &result2); + + A00 = A[object][0][0]; // Dereference NOW! + A10 = A[object][1][0]; + A20 = A[object][2][0]; + A30 = A[object][3][0]; + A01 = A[object][0][1]; + A11 = A[object][1][1]; + A21 = A[object][2][1]; + A31 = A[object][3][1]; + + I[object][17] = 0; // Make sure we REALLY want to climb... + + EDMS_robot_global_badness_indicator = 0; + + // Get the info... + indoor_terrain(A00, A10, A20, I[object][IDOF_ROBOT_RADIUS], on2ph[object], TFD_FULL); + + // if (EDMS_robot_global_badness_indicator != 0 ) { + // mout << "!Robot.cc: thinks that X: " << A00 << ", Y: " << A10 << ", Z: " + // << A20 << "\n"; mout << "!Robot.cc: has object number " << object << ", + // ph: " << on2ph[object] << "\n"; + // } + + // Boy, will this be faster in the new order... + // ============================================ + Q w0, w1, w2, w3, w4; + w0.fix_to(terrain_info.wx); + w1.fix_to(terrain_info.wy); + w2.fix_to(terrain_info.wz); + + // w3 = sqrt( w0*w0 + w1*w1 + w2*w2 ); + // w4 = 2*( w3 - .5*I[object][22] ); + // if ( w4 < 0 ) w4 = 0; //Ouch! + // w4 = w4 / w3; + // w0 *= w4; w1 *= w4; w2 *= w4; + + if (w0 == 0) + check0 = 1; + else + check0 = 0; + + if (w1 == 0) + check1 = 1; + else + check1 = 0; + + if ((terrain_info.fz == 0) && (terrain_info.cz == 0)) + check2 = 1; + else + check2 = 0; + + object0.fix_to(terrain_info.fx + terrain_info.cx); + object1.fix_to(terrain_info.fy + terrain_info.cy); + object2.fix_to(terrain_info.fz + terrain_info.cz); + + object0 += w0; + object1 += w1; + object2 += w2; + + checker = sqrt(object0 * object0 + object1 * object1 + object2 * object2); + + if (checker > .0005) { + object3 = fix_one / checker; // To get primitive... + // mout << "NZero!! " << checker << "\n"; + } else + checker = object3 = 0; + + object4 = object3 * object0; // The primitive V_n... + object5 = object3 * object1; + object6 = object3 * object2; + + object7 = .75 * I[object][21] * + (A01 * object4 // Delta_magnitude... + + A11 * object5 + A21 * object6); + + object8 = I[object][20]; // Omega_magnitude... + // if( I[object][10]<0 ) object7 *= 4; + // if( I[object][10]<0 ) object8 *= 2; + + // mout << "o7: " << object7 << "\n"; + object4 = object7 * object4; // Delta... + object5 = object7 * object5; + object6 = object7 * object6; + + object9 = ((checker > .001) || (I[object][10] > 0)); // Are we in the rub??? + + // Let's not power through the walls anymore... + // -------------------------------------------- + I[object][18] *= check0; + I[object][19] *= check1; + + // Here are collisions with other objects... + // ========================================= + object10 = object11 = object12 = 0; + + if (I[object][5] == 0) { + shall_we_dance(object, object10, object11, object12); + object10 *= I[object][20] * check0; // More general than it was... + object11 *= I[object][20] * check1; + // object12 *= I[object][20]*check2; + } + + // Climbing overriden with repulsors... + // ==================================== + if (ss_edms_bcd_flags & SS_BCD_REPUL_ON) { + + // Get the speed... + Q repulsor_speed = 21; + if ((ss_edms_bcd_flags & SS_BCD_REPUL_SPD) == SS_BCD_REPUL_NORM) + repulsor_speed = 7; + + // Assume we're going up, unless... + if ((ss_edms_bcd_flags & SS_BCD_REPUL_TYPE) == SS_BCD_REPUL_DOWN) + repulsor_speed *= -.5; + + // The parameter should be the desired height.... + Q repul_height; + repul_height.fix_to(ss_edms_bcd_param); + + Q nearness_or_something = repul_height - A[object][2][0]; + if (abs(nearness_or_something) <= .333) { + repulsor_speed *= 3 * nearness_or_something; + } + + Q io17 = repulsor_speed; + + I[object][17] = I[object][26] * ((io17 - A[object][2][1]) + I[object][25]); + + object9 = 1; + } + + // AutoClimbing(tm) is for wussies (is superseeded by climbing)... + // =============================================================== + if ((ss_edms_bcd_flags & SS_BCD_MISC_STAIR)) { + + Q o1 = 0, o0 = 0; + + if ((checker > 0) && (abs(I[object][18]) + abs(I[object][19]) > .01)) { + + Q ratio = (I[object][18] + A[object][0][1]) * object0 + (I[object][19] + A[object][1][1]) * object1; + + Q io17 = .5; + + if (ratio <= 0) { + o1 = object1; + o0 = object0; + } else + o1 = o0 = io17 = 0; + + I[object][18] = -.3 * I[object][22] * o0 * object8 / checker + .1 * I[object][18]; + I[object][19] = -.3 * I[object][22] * o1 * object8 / checker + .1 * I[object][19]; + // io18 = -.3*I[object][22]*o0*object8/checker + .1*I[object][18]; + // io19 = -.3*I[object][22]*o1*object8/checker + .1*I[object][19]; + + // Set the mojo... + // =============== + I[object][17] = 800 * (io17 - A[object][2][1]); + } + } + + // Angular play (citadel) ... + // ========================== + if (S[object][3][0] > two_pi) + S[object][3][0] -= two_pi; + if (S[object][3][0] < -two_pi) + S[object][3][0] += two_pi; + + // Don't be stupid... + // ------------------ + drug = -object9 * I[object][23]; + // mout << drug << "\n"; + butt = I[object][24]; + + // Try the equations of motion here for grins... + // ============================================= + S[object][2][2] = butt * (object8 * object2 // Elasticity... + - object6 // Drag... + + I[object][17] // Control... + + drug * A21 + object12) + + - I[object][25]; // Grav'ty... + + S[object][0][2] = butt * (object8 * object0 // Elasticity... + - object4 // Drag... + + object9 * I[object][18] // Control... + + drug * A01 // Drag... + + object10); // Collide... + + S[object][1][2] = butt * (object8 * object1 // Elasticity... + - object5 // Drag... + + object9 * I[object][19] // Control... + + drug * A11 // Drag... + + object11); // Collide... + + S[object][3][2] = I[object][27] * (I[object][16] // Control... + - I[object][28] * A31); // Drag... + + // mout << "Butt: " << butt << "\n"; + // mout << "1X: " << object0 << " 1Y: " << object1 << " 1Z: " << object2 << "\n"; + // mout << "VX: " << A01 << " VY: " << A11 << " VZ: " << A21 << "\n"; + // mout << "2X: " << object4 << " 2Y: " << object5 << " 2Z: " << object6 << "\n"; + // mout << "3X: " << object8*object0 << " 3Y: " << object8*object1 << " 3Z: " << object8*object2 << "\n"; + // mout << "FX: " << object8*object0 - object4 << " FY: " << object8*object1 - object5 << " FZ: " << + // object8*object2 - object6 << "\n"; mout << "xx: " << drug*A01 << " yy: " << drug*A11 << " zz: " << + // drug*A21 << "\n"; + // mout << " ZZ: " << S[object][2][2] << " : " << butt*(object8*object2 - object6) - I[object][25] << + // " : " << drug*A21 << " : " << object12 << " : " << I[object][17] << "\n"; + // mout << I[object][17] << " : " << object << "\n"; + + // Damnage... + // ========== + Q dam0 = object8 * object0 - object4; + Q dam1 = object8 * object1 - object5; + Q dam2 = object8 * object2 - object6; + + I[object][14] = I[object][IDOF_ROBOT_MASS_RECIP] * (abs(dam0) + abs(dam1) + abs(dam2)); // Damage?? + + // Is there a projectile hit? + // ========================== + if (I[object][35] > 0) { + + // Let's not power through the walls anymore... + // -------------------------------------------- + // mout << "knock " << I[object][32] << " " << I[object][33] << " " << I[object][34] << ": I[24] " << + // I[object][24] << "\n"; + + // if (I[object][24] > 1.0) + // { + // if ( abs(I[object][32]) > 1000 ) I[object][32] = 1000*(1-2*(I[object][32]<0)); + // if ( abs(I[object][33]) > 1000 ) I[object][33] = 1000*(1-2*(I[object][33]<0)); + // if ( abs(I[object][34]) > 1000 ) I[object][34] = 1000*(1-2*(I[object][34]<0)); + // } + + // mout << "clamp " << I[object][32] << " " << I[object][33] << " " << I[object][34] << "\n"; + + S[object][0][2] += /* I[object][24]* */ I[object][32] * check0; + S[object][1][2] += /* I[object][24]* */ I[object][33] * check1; + S[object][2][2] += /* I[object][24]* */ I[object][34] * check2; + + // mout << " add " << /* I[object][24]* */ I[object][32]*check0 << " " << + // /* I[object][24]* */ I[object][33]*check0 << " " << + // /* I[object][24]* */ I[object][34]*check0 << "\n"; + + // mout << "R: " << object << " K: " << I[object][24]*I[object][32]*check0 << " : " << + // I[object][24]*I[object][33]*check1 << " : " << I[object][24]*I[object][34] << "\n"; + + I[object][35] = 0; + I[object][32] = 0; + I[object][33] = 0; + I[object][34] = 0; + } + + // That's all, folks... + // ==================== +} + +// We might for now want to set some external forces on the robot... +// ================================================================== +void robot_set_control(int32_t robot, Q thrust_lever, Q attitude_jet, Q jump) { + + sincos(S[robot][3][0], &object0, &object1); + +#ifdef EDMS_SHIPPABLE + if (I[robot][30] != ROBOT) + mout << "You are an idiot: I'm not a ROBOT!\n"; +#endif + + // Here's the thrust of the situation... + // ------------------------------------- + I[robot][18] = thrust_lever * object1 * I[robot][IDOF_ROBOT_MASS]; + I[robot][19] = thrust_lever * object0 * I[robot][IDOF_ROBOT_MASS]; + I[robot][17] = I[robot][26] * jump; + + // And the turn of the... + // ---------------------- + I[robot][16] = attitude_jet * I[robot][IDOF_ROBOT_MOI]; + + // Wakee wakee... + // -------------- + no_no_not_me[robot] = (abs(I[robot][18]) + abs(I[robot][19]) + abs(I[robot][16]) + abs(I[robot][17]) > 0); +} + +// Here is a separate control routine for robots under AI domination... +// ==================================================================== +void robot_set_ai_control(int32_t robot, Q desired_heading, Q desired_speed, Q sidestep, Q urgency, Q &there_yet, + Q distance) { + + const Q one_by_pi = 0.31830, pi = 3.14159, two_pi = 6.28318; + +#ifdef EDMS_SHIPPABLE + if (I[robot][30] != ROBOT) + mout << "Hey, don't call control_robot on non-robots!\n"; +#endif + + if (desired_heading > two_pi) + desired_heading -= two_pi; + if (desired_heading < 0) + desired_heading += two_pi; + + // Nota bene: Here the desired heading is specified is in the range + // 0 <= desired_heading < 2pi. Urgency is a number in the range + // 0 <= urgency <= 20. A zero urgency will produce no control input. + // ================================================================== + + // Setup... + // -------- + Q speed = sqrt(S[robot][0][1] * S[robot][0][1] + S[robot][1][1] * S[robot][1][1]), + direction = desired_heading - S[robot][3][0]; + + sincos(S[robot][3][0], &object0, &object1); + + // Heading... + // ---------- + if (direction > pi) + direction = -(direction - pi); + if (direction <= -pi) + direction = -(direction + pi); + + // Inform the caller if we're on course yet... + // ------------------------------------------- + there_yet = one_by_pi * direction * (1 - 2 * (direction < 0)); + + // Set the control... + // ------------------ + I[robot][16] = .1 * urgency * direction * I[robot][29]; + + // Speed... + // -------- + I[robot][17] = urgency * (1 / (10 * there_yet + 5)) * (desired_speed - speed); // temporary... + if (I[robot][17] < 0) + I[robot][17] = 0; + + if (distance < 1) + I[robot][17] *= distance; + + I[robot][18] = object1 * I[robot][17] + object0 * sidestep; + I[robot][19] = object0 * I[robot][17] - object1 * sidestep; + I[robot][17] = 0; // No jumping for AIs + + // Wakee wakee... + // -------------- + if (no_no_not_me[robot] == 0) { + no_no_not_me[robot] = (abs(I[robot][18]) + abs(I[robot][19]) + abs(I[robot][16]) > 0); + // if ( no_no_not_me[robot] != 0 ) mout << "R: " << robot << ", ph= " << on2ph[robot] << " awoken! " << + // no_no_not_me[robot] << "\n"; mout << ( abs( I[robot][18] ) + abs( I[robot][19] ) + 50*abs( + // I[robot][16] ) + abs( I[robot][17] ) ) << "\n"; mout << no_no_not_me[robot] << "\n"; + } + + // mout << "Robot #" << robot << " with: " << ( abs( I[robot][18] ) + abs( I[robot][19] ) + 50*abs( I[robot][16] ) + //+ abs( I[robot][17] ) ) << ".\n";; +} + +int32_t make_robot(Q init_state[6][3], Q params[10]) { + + // Sets up everything needed to manufacture a robot with initial state vector + // init_state[][] and EDMS motion parameters params[] into soliton. Returns the + // object number, or else a negative error code (see Soliton.CPP for error handling and codes). + // ============================================================================================ + + // Have some variables... + // ====================== + int32_t object_number = -1, // Three guesses... + error_code = -1; // Guilty until... + + // We need ignorable coordinates... + // ================================ + extern void null_function(int32_t); + + // First find out which object we're going to be... + // ================================================ + while (S[++object_number][0][0] > END) + ; // Jon's first C trickie... + + // Is it an allowed object number? Are we full? Why are we here? Is there a God? + // ============================================================================== + if (object_number < MAX_OBJ) { + + // Now we can create the robot: first dump the initial state vector... + // ===================================================================== + for (int32_t coord = 0; coord < 6; coord++) { + for (int32_t deriv = 0; deriv < 3; deriv++) { // Has alpha now... + S[object_number][coord][deriv] = A[object_number][coord][deriv] = + init_state[coord][deriv]; // For collisions... + } + } + + // Put in the appropriate robot parameters... + // =========================================== + for (int32_t copy = 0; copy < 10; copy++) { + I[object_number][copy + 20] = params[copy]; + } + I[object_number][IDOF_MODEL] = ROBOT; // Hey, you are what you eat. + + // Put in the collision information... + // =================================== + I[object_number][IDOF_RADIUS] = I[object_number][IDOF_ROBOT_RADIUS]; + I[object_number][32] = I[object_number][33] = I[object_number][34] = I[object_number][35] = 0; + I[object_number][36] = I[object_number][IDOF_ROBOT_MASS_RECIP]; // Shrugoff "mass"... + I[object_number][IDOF_COLLIDE] = -1; + I[object_number][IDOF_AUTODESTRUCT] = 0; // No kill I... + + // Turn ON collisions for this robot... + // ------------------------------------ + I[object_number][5] = 0; // negative values are off... + + // Zero the control initially... + // ============================= + I[object_number][16] = I[object_number][18] = I[object_number][19] = I[object_number][17] = 0; + + // Now tell Soliton where to look for the equations of motion... + // ============================================================= + idof_functions[object_number] = robot_idof; + + equation_of_motion[object_number][0] = equation_of_motion[object_number][1] = + equation_of_motion[object_number][2] = equation_of_motion[object_number][3] = + equation_of_motion[object_number][4] = // Nice symmetries, huh. + equation_of_motion[object_number][5] = null_function; + + // for (int tt = 0; tt < 10; tt++ ) mout << params[tt] << " : "; + // mout << "\n"; + + // Wakee wakee... + // -------------- + no_no_not_me[object_number] = 1; + + // Things seem okay... + // =================== + error_code = object_number; + } + + // Inform the caller... + // ==================== + return error_code; +} + +#pragma require_prototypes on + +// ATTENZIONE: Los parametros del model son: +// ========================================== + +// Number | Comment +// -------------------- +// 0 | K +// 1 | d +// 2 | Radius +// 3 | Rolling Drag +// 4 | 1/Mass +// 5 | gravity +// 6 | mass +// 7 | 1/moi +// 8 | rotational drag +// 9 | moi +// ========================================== +// So there. diff --git a/engine/src/Libraries/EDMS/Source/MODELS/robot.h b/engine/src/Libraries/EDMS/Source/MODELS/robot.h new file mode 100644 index 0000000..665692f --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/MODELS/robot.h @@ -0,0 +1,24 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// This seems silly now, but later it will all make sense, sensei... +// ================================================================= +int32_t make_robot(Q init_state[6][3], Q params[10]); +void robot_set_control(int32_t robot, Q X, Q Y, Q Z); +void robot_set_ai_control(int32_t robot, Q desired_heading, Q desired_speed, Q sidestep, Q urgency, Q &there_yet, + Q distance); diff --git a/engine/src/Libraries/EDMS/Source/collide.cc b/engine/src/Libraries/EDMS/Source/collide.cc new file mode 100644 index 0000000..5178ca0 --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/collide.cc @@ -0,0 +1,250 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Header: n:/project/lib/src/edms/RCS/collide.cc 1.3 1994/04/20 18:44:14 roadkill Exp $ + */ + +// Here are the routines that write object presences to the terrain database. +// Optimally, these will be swapped with the appropriate routine on a project +// to project basis. They are quite simple here. +// ============================================== + +#include "edms_int.h" +#include "idof.h" +#include "lg.h" + +#pragma require_prototypes off // Added by KC for this file for Mac version. + +// The space itself... +// =================== +// +// We divide the playfield into a EDMS_DATA_SIZE by EDMS_DATA_SIZE grid. +// Each bin in the grid contains (possibly) object n if +// data[x][y] & (1 << n) != 0. +// +uint32_t data[EDMS_DATA_SIZE][EDMS_DATA_SIZE]; + +// Constants for the fixpoint... +// ============================= +const int32_t collision_max = EDMS_DATA_SIZE - 1; +extern int32_t EDMS_integrating; + +////////////////////////////// +// +// Okay, here is the generic function that write_object and state_write_object +// will both call. When I can check out the appropriate files, they'll turn +// into macros. +// +void generic_write_object(int32_t object, EDMS_Argblock_Pointer state) { + extern void (*EDMS_off_playfield)(physics_handle caller); + + Q q_hash_x = hash_scale * state[object][DOF_X][0]; + Q q_hash_y = hash_scale * state[object][DOF_Y][0]; + + uint32_t obit = object_bit(object); + + int32_t hash_x = (q_hash_x).to_int(); // The floor should be a function + int32_t hash_y = (q_hash_y).to_int(); // that returns the edges of the ref. squares... + + if ((hash_x > 1) && (hash_y > 1) && (hash_x < collision_max) && (hash_y < collision_max)) { + // We want to write a 2x2 block. We adjust the upper left corner of it + // appropriately given our location in the square. + + if (q_hash_x - hash_x < DELTA_BY_TWO) + hash_x--; + if (q_hash_y - hash_y < DELTA_BY_TWO) + hash_y--; + + write_object_bit(hash_x, hash_y, obit); + write_object_bit(hash_x, hash_y + 1, obit); + write_object_bit(hash_x + 1, hash_y, obit); + write_object_bit(hash_x + 1, hash_y + 1, obit); + } else { + // End of slowness, and inform the world that something stinks in Denmark... + // ------------------------------------------------------------------------- + // mout << "Collide...\n"; + EDMS_off_playfield(on2ph[object]); + } +} + +void write_object(int32_t object) { + // mout << "Write A\n"; + generic_write_object(object, A); +} + +void state_write_object(int32_t object) { + // mout << "Write S\n"; + generic_write_object(object, S); +} + +////////////////////////////// +// +// And here's a generic function for delete_object. +// + +void generic_delete_object(int32_t object, EDMS_Argblock_Pointer state) { + Q q_hash_x = hash_scale * state[object][DOF_X][0]; + Q q_hash_y = hash_scale * state[object][DOF_Y][0]; + + uint32_t obit = object_bit(object); + + int32_t hash_x = (q_hash_x).to_int(); // The floor should be a function + int32_t hash_y = (q_hash_y).to_int(); // that returns the edges of the ref. squares... + + // AAAAAhhhhhhhhh... + // mout << "DA" << object << ": (" << hash_x << ", " << hash_y << ")\n"; + // if ( EDMS_integrating != 1 ) mout << "BADBADBADBADBADBADBADBADBADBADBADBADBADBADBADBBADBADB!!!!\n"; + + // How slow can you go? + // -------------------- + if ((hash_x > 1) && (hash_y > 1) && (hash_x < collision_max) && (hash_y < collision_max)) { + // We want to delete a 2x2 block. We adjust the upper left corner of it + // appropriately given our location in the square. + + if (q_hash_x - hash_x < DELTA_BY_TWO) + hash_x--; + if (q_hash_y - hash_y < DELTA_BY_TWO) + hash_y--; + + delete_object_bit(hash_x, hash_y, obit); + delete_object_bit(hash_x, hash_y + 1, obit); + delete_object_bit(hash_x + 1, hash_y, obit); + delete_object_bit(hash_x + 1, hash_y + 1, obit); + } else { + // End of slowness, and inform the world that something stinks in Denmark... + // ------------------------------------------------------------------------- + // mout << "collide2...\n"; + EDMS_off_playfield(on2ph[object]); + // Spew (DSRC_EDMS_Collide, ("Bounds on %d ph %d hash [%d %d]\n", object, on2ph[object], hash_x, hash_y)); + // Spew (DSRC_EDMS_Collide, ("state x = %8x y = %8x\n", state[object][DOF_X][0], state[object][DOF_Y][0])); + } +} + +void delete_object(int32_t object) { + // mout << "Delete A\n"; + generic_delete_object(object, A); +} + +void state_delete_object(int32_t object) { + // mout << "Delete S\n"; + generic_delete_object(object, S); +} + +////////////////////////////// +// +// Find out whether a given object could actually be found in hash location +// . This is a handy way to find out which of the three objects with +// a given object bit is actually meant. + +bool object_check_hash(int32_t object, int32_t hx, int32_t hy) { + // We use A if we are in the middle of integrating, and the object + // is not asleep. + + EDMS_Argblock_Pointer state = (A_is_active && no_no_not_me[object]) ? A : S; + + int32_t my_hx = (hash_scale * state[object][DOF_X][0]).to_int(); + int32_t my_hy = (hash_scale * state[object][DOF_Y][0]).to_int(); + + return (abs(my_hx - hx) <= 1 && abs(my_hy - hy) <= 1); +} + +// Collision exclusion... +// ====================== + +// Turn it off... +// ============== +void reset_collisions(int32_t object) { + // Are we really inactivated? + // -------------------------- + if (I[object][IDOF_COLLIDE] > -1) { + I[I[object][IDOF_COLLIDE].to_int()][IDOF_COLLIDE] = -1; + I[object][IDOF_COLLIDE] = -1; + } +} + +// Turn it on... +// ============= +void exclude_from_collisions(int32_t guy_1, int32_t guy_2) { + // Are we ready? + // ------------- + if ((I[guy_1][IDOF_COLLIDE] > -1) || (I[guy_2][IDOF_COLLIDE] > -1)) { + reset_collisions(guy_1); + reset_collisions(guy_2); + } + I[guy_1][IDOF_COLLIDE] = guy_2; + I[guy_2][IDOF_COLLIDE] = guy_1; + + // Viola! +} + +// Read'n'... +// ========== + +uint32_t test_bitmask; // used to be clean_test_bit + +// Basically subtract out the bit representing the calling object... +// ================================================================= +int32_t are_you_there(int32_t object) { + return (test_bitmask = + data[(hash_scale * A[object][DOF_X][0]).to_int()][(hash_scale * A[object][DOF_Y][0]).to_int()]); +} + +////////////////////////////// +// +// This is now a macro in edms_int.h called check_for_hit_mac + +#ifdef NOPE +// Subtract out the bit representing the calling object, then compare notes... +// =========================================================================== +int check_for_hit(int other_object) { + // unsigned int test_bit = data[ (hash_scale*A[object][DOF_X][0]).to_int() ][ + // (hash_scale*A[object][DOF_Y][0]).to_int() ]; + + return clean_test_bit & object_bit(other_object); + + // return ( test_bit & ~( object_bit( object ) ) ) + // & check_object( object, other_object ); +} +#endif + +// Won't get compiled in unless you specifically turn it on here +#ifdef DEBUGGING + +extern "C" { + +void spew_collision_table() { + int i, j; + + for (i = 0; i < collision_max; i++) { + for (j = 0; j < collision_max; j++) { + if (data[i][j]) { + int bit, mask; + + for (bit = 0, mask = 1; bit < 32; bit++, mask <<= 1) { + if (data[i][j] & mask) { + // Spew (DSRC_EDMS_Collide, ("[%d %d]: on %d ph %d\n", i, j, bit, on2ph[bit])); + } + } + } + } + } +} +} + +#endif diff --git a/engine/src/Libraries/EDMS/Source/edms.h b/engine/src/Libraries/EDMS/Source/edms.h new file mode 100644 index 0000000..2a8719d --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/edms.h @@ -0,0 +1,297 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Header: r:/prj/lib/src/new_edms/rcs/edms.h 1.2 1994/08/11 18:54:10 dfan Exp $ + */ + +// This is the MAIN header file for the EDMS system. It is used ONLY IN THE MAIN CALLING +// ROUTINE! Just so that functions are handy! +// ========================================== + +// Condom... +// --------- +#ifndef __EDMS_H +#define __EDMS_H + +#include +#include +#include "fix.h" + +// Max and Min +// =========== +#define MAX_OBJ 96 + +// Physics handles handlers... +// =========================== +#include "physhand.h" + +// Stuff that used to be in physhand.h... +// -------------------------------------- +typedef int32_t object_number; +extern int32_t min_physics_handle; + +extern object_number ph2on[MAX_OBJ]; +extern physics_handle on2ph[MAX_OBJ]; + +#define physics_handle_to_object_number(ph) (ph2on[ph]) +#define object_number_to_physics_handle(on) (on2ph[on]) + +// Just in case... +// --------------- +#ifdef __cplusplus +extern "C" { +#endif + +void EDMS_init_handles(void); +physics_handle EDMS_bind_object_number(object_number on); +void EDMS_remap_object_number(object_number old, object_number nu); +physics_handle EDMS_get_free_ph(void); +void EDMS_release_object(physics_handle ph); + +#ifdef __cplusplus +} +#endif + +// Memory conserving stuff... +// ========================== +// typedef Q EDMS_Argument_Block[MAX_OBJ][7][4]; +// typedef Q (*EDMS_Argblock_Pointer)[7][4]; + +// The user must make sure that there's this much space... +// ======================================================= +//#define EDMS_ARGBLOCKSIZE (sizeof(EDMS_Argument_Block)) + +// Soliton the magic Solver... +// =========================== +// void EDMS_soliton( fix timestep ); +void EDMS_soliton_lite(fix timestep); +void EDMS_soliton_vector(fix timestep); +void EDMS_soliton_vector_holistic(fix timestep); + +// Tools... +// ======== +// Here is a routine that will attempt to settle an object to the local b/c. It is NOT intended for +// online use. A negative return value indicates a badly placed or unphysical model... +// ==================================================================================== +int32_t EDMS_settle_object(physics_handle ph); + +void EDMS_mprint_state(physics_handle ph); + +// Prints out state and sleep information on ALL objects. Show sleepers is 1 to display sleeping objects... +// --------------------------------------------------------------------------------------------------------- +void EDMS_inventory_and_statistics(int32_t show_sleepers); + +// Returns TRUE if an object is awake, FLASE otherwise... +// ------------------------------------------------------ +bool EDMS_frere_jaques(physics_handle ph); + +// Checks integrity of EDMS. Returns EDMS error codes, as seen below. +// ------------------------------------------------------------------- +int32_t EDMS_sanity_check(); + +// Here we exclude objects from hitting specific others... +// ------------------------------------------------------- +void EDMS_ignore_collisions(physics_handle ph1, physics_handle ph2); + +// Here we reallow collisions... +// ----------------------------- +void EDMS_obey_collisions(physics_handle ph1); + +// Autodestruct objects kill themselves after the first or second collision callback. This is model specific. +// ----------------------------------------------------------------------------------------------------------- +void EDMS_set_autodestruct(physics_handle ph); +void EDMS_defuse_autodestruct(physics_handle ph); + +// Wake me up no matter what (i.e. terrain is changing, new level, etc.)... +// ------------------------------------------------------------------------ +void EDMS_crystal_meth(physics_handle ph); + +// Structs... +// ========== +typedef struct { + + fix playfield_size; + int32_t min_physics_handle; + + void (*collision_callback)(physics_handle caller, physics_handle victim, int32_t badness, int32_t DATA1, + int32_t DATA2, fix location[3]), + (*autodestruct_callback)(physics_handle caller), (*awol_callback)(physics_handle caller), + (*snooz_callback)(physics_handle caller); + + void *argblock_pointer; + +} EDMS_data; + +typedef struct { + fix X, Y, Z, alpha, beta, gamma; + fix X_dot, Y_dot, Z_dot, alpha_dot, beta_dot, gamma_dot; +} State; + +// Master control... +// ================= +void EDMS_get_state(physics_handle ph, State *s); +void EDMS_startup(EDMS_data *D); +void EDMS_kill_object(physics_handle ph); +void EDMS_holistic_teleport(physics_handle ph, State *s); +void EDMS_mouselook(physics_handle ph, int32_t xlook); + +// Object packages... +// ================== + +// Marble: +// ------- +typedef struct { + fix mass, size, pep; +} Marble; + +physics_handle EDMS_make_marble(Marble *m, State *s); +void EDMS_get_marble_parameters(physics_handle ph, Marble *m); +void EDMS_set_marble_parameters(physics_handle ph, Marble *m); +void EDMS_control_marble(physics_handle ph, fix X, fix Y, fix Z); + +// Robot: +// ------ +typedef struct { + fix mass, size, hardness, pep, gravity; + int cyber_space; +} Robot; + +physics_handle EDMS_make_robot(Robot *m, State *s); +void EDMS_get_robot_parameters(physics_handle ph, Robot *m); +void EDMS_set_robot_parameters(physics_handle ph, Robot *m); +void EDMS_control_robot(physics_handle ph, fix thrust_lever, fix attitude_jets, fix jump_jet); +fix EDMS_get_robot_damage(physics_handle ph); +void EDMS_make_robot_antisocial(physics_handle ph); +void EDMS_make_robot_social(physics_handle ph); + +// Nota bene: Here the desired heading is specified is in the range +// 0 <= desired_heading < 2pi. Urgency is a number in the range +// 0 <= urgency <= 20. A zero urgency will produce no control input. +// ------------------------------------------------------------------ +void EDMS_ai_control_robot(physics_handle ph, fix desired_heading, fix desired_speed, fix sidestep, fix urgency, + fix *are_we_there_yet, fix distance); + +// Pelvis: +// ------ +typedef struct { + fix mass, size, hardness, pep, gravity, height; + int32_t cyber_space; +} Pelvis; + +physics_handle EDMS_make_pelvis(Pelvis *p, State *s); +void EDMS_control_pelvis(physics_handle ph, fix forward, fix turn, fix sidestep, fix lean, fix jump, int32_t crouch); +void EDMS_get_pelvic_viewpoint(physics_handle ph, State *s); +void EDMS_get_pelvis_parameters(physics_handle ph, Pelvis *p); +void EDMS_set_pelvis_parameters(physics_handle ph, Pelvis *p); +fix EDMS_get_pelvis_damage(physics_handle ph, fix delta_t); +bool EDMS_pelvis_is_climbing(void); + +// Death... +// -------- +typedef struct { + + fix mass, size, gravity; + +} Death; + +physics_handle EDMS_make_death(Death *d, State *s); +void EDMS_get_death_parameters(physics_handle ph, Death *d); +void EDMS_set_death_parameters(physics_handle ph, Death *d); + +// Aggregate objects: +// ------------------ +extern int32_t make_jello_cube(fix X, fix Y, fix Z, fix size, int32_t points); +extern int32_t make_octahedron(fix X, fix Y, fix Z, fix size); +extern int32_t make_chair(fix X, fix Y, fix Z, fix size, int32_t points); + +// Bipeds... +// --------- +typedef struct { + fix mass, max_speed, skill, gravity; + fix hip_radius, thigh, shin, torso; + // Arms have 1/2 length segments, i.e. elbow always at 1/2*arms + fix shoulders, arms; +} Biped; + +physics_handle EDMS_make_biped(Biped *b, State *s, fix *skeleton); +void EDMS_make_biped_skeleton(physics_handle ph); +void EDMS_control_biped(physics_handle ph, fix forward, fix side_rotate, int32_t mode); +void EDMS_set_biped_parameters(physics_handle ph, Biped *b); + +// Access to the Dirac basis matrix (used by frsetup). +fix *EDMS_Dirac_basis(void); + +// "Faster than light" objects... Objects that move over their entire trajectory in a frame or two... +// -------------------------------------------------------------------------------------------------- +physics_handle EDMS_beam_weapon(fix X[3], // Location of gun, *returns hit location*... + fix D[3], // Unit vector in direction of barrel... + fix kick, // Art Min requested hacked kickback parameter, no physical meaning... + fix knock, // Art Min requested hacked knockback (see above)... + fix size, // Radius of bullet in meters... + fix range, // Range of weapon in meters... + physics_handle exclude, // A physics object that is immune to hits... + physics_handle shooter); // The physics object who fired... + +// FGREFALL ONLY - this is temporary until terrain functions are unified!!!... +// --------------------------------------------------------------------------- +physics_handle EDMS_FF_beam_weapon(fix X[3], // Location of gun, *returns hit location*... + fix D[3], // Unit vector in direction of barrel... + fix speed, // Speed of bullet in m/s... + fix mass, // Mass of projectile in kilos... + fix size, // Radius of bullet in meters... + fix range, // Range of weapon in meters... + physics_handle exclude, // A physics object that is immune to hits... + physics_handle shooter, // The physics object who fired... + int32_t *w_info, // FF wall information, returned if a wall is hit... + int32_t *g_info, // FF ground information, "" "" "" "" ... + bool *hit); // TRUE for a hit, FALSE if range is exceeded without a hit... + +// Freefall terrain data structures... +// =================================== +typedef struct { + // The ground... + fix g_height, g_dx, g_dy, g_dz; + // Any walls... + fix w_x, w_y, w_z; + // Squishiness, friction, et cetera... + fix terrain_information; + // For terrain return information... + int32_t DATA1, DATA2; + // Who's responsible... + physics_handle caller; +} terrain_ff; + +// The indoor terrain data structures... +// ===================================== +typedef struct { + // Filled by user when Indoor_Terrain is called + fix cx, cy, cz; + fix fx, fy, fz; + fix wx, wy, wz; + +} TerrainData; + +// Finally, EDMS error codes... +// ============================ +#define EDMS_TOO_MANY_OBJECTS 1 +#define EDMS_PHYSICS_HANDLES_CORRUPT 2 +#define EDMS_OBJECT_HANDLES_CORRUPT 3 +#define EDMS_IDOF_POINTERS_CORRUPT 4 + +#endif /* __EDMS_H */ diff --git a/engine/src/Libraries/EDMS/Source/edms_chk.h b/engine/src/Libraries/EDMS/Source/edms_chk.h new file mode 100644 index 0000000..3958883 --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/edms_chk.h @@ -0,0 +1,64 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +// defines for master debug things + +//#define SOLITON_FRAME_CNT + +//#define SOLITON_DO_SL +//#define ROBOT_DO_SL +//#define CHECK_IDOF +#define CHECKING_SOLITON + +// note this requires FRAME_CNT +//#define SOLITON_ALLOC_MONITOR + +// actual macros and vars they set up and use + +#ifdef SOLITON_FRAME_CNT +#ifndef __SOLITON_SRC +//#ifdef __cplusplus +// extern "C" { +// extern int EDMS_pfrm; // for debugging stupidity +//} +//#else +extern int EDMS_pfrm; // for debugging stupidity +//#endif +#else +extern "C" { +int EDMS_pfrm = 0; +} +#endif +#endif + +#ifdef ROBOT_DO_SL +#define rob_sl_at(l, x) (*((uchar *)(0xB0000 + (l * 2))) = x) +#define rob_sl(x) (*((uchar *)(0xB0000 + 158)) = x) +#else +#define rob_sl_at(l, x) +#define rob_sl(x) +#endif + +#ifdef SOLITON_DO_SL +#define sol_sl_at(l, x) (*((uchar *)(0xB0000 + (l * 2))) = x) +#define sol_sl(x) (*((uchar *)(0xB0000 + 158)) = x) +#else +#define sol_sl_at(l, x) +#define sol_sl(x) +#endif diff --git a/engine/src/Libraries/EDMS/Source/edms_int.h b/engine/src/Libraries/EDMS/Source/edms_int.h new file mode 100644 index 0000000..a689d46 --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/edms_int.h @@ -0,0 +1,237 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Header: n:/project/lib/src/edms/RCS/edms_int.h 1.4 1993/05/13 15:46:06 roadkill Exp $ + */ + +// This header file contains the codes for the object types and functions in Soliton. +// ===================================================================== + +#ifndef __EDMS_INT_H +#define __EDMS_INT_H + +// Are we building a shipping version? +// =================================== +//#define EDMS_SHIPPABLE 1 + +// Things like Getch() +// ------------------- +////#include + +//#pragma INLINE_DEPTH 255 +//#pragma INLINE_RECURSION ON + +// Cool math universe... +// --------------------- +#include "fixpp.h" + +// Physics handle typedef +// ====================== +#include "physhand.h" + +#include "ss_flet.h" + +// Actual object types... +// ====================== +extern Q VACUUM, MARBLE, ROBOT, FIELD_POINT, BIPED, PELVIS, DEATH, D_FRAME; + +// Commands for soliton from the state stream (in the Global.cc file)... +// ===================================================================== +extern Q END; + +// Max and Minima... +// ================= +#define MAX_OBJ 96 +//#define DOF_MAX 96 +#define DOF_MAX 40 +#define EDMS_DATA_SIZE 100 + +#define DOF 7 // degrees of freedom +#define DOF_DERIVS 4 // d/dt each dof this-1 times + +// Dan, these are model specific, so we need more general names than below. I suggest +//#define DOF_X 0 +//#define DOF_Y 1 +//#define DOF_Z 2 +//#define DOF_ORIENT_0 3 +//#define DOF_ORIENT_1 4 +//#define DOF_ORIENT_2 5 +//#define DOF_ORIENT_3 6 +// But I can't spend time right now changing the references in your code ;^) ... + +#define DOF_X 0 +#define DOF_Y 1 +#define DOF_Z 2 +#define DOF_ALPHA 3 +#define DOF_BETA 4 +#define DOF_GAMMA 5 +#define DOF_DIRAC 6 + +// Memory conserving stuff... +// ========================== +typedef Q EDMS_Argument_Block[MAX_OBJ][DOF][DOF_DERIVS]; +typedef Q (*EDMS_Argblock_Pointer)[DOF][DOF_DERIVS]; + +// Have some functions... +// ====================== + +// General functions... +// -------------------- +typedef struct { + fix playfield_size; + int32_t min_physics_handle; + void (*collision_callback)(physics_handle caller, physics_handle victim, int32_t badness, int32_t DATA1, + int32_t DATA2, fix location[3]), + (*autodestruct_callback)(physics_handle caller), (*awol_callback)(physics_handle caller), + (*snooz_callback)(physics_handle caller); + void *argblock_pointer; +} EDMS_data; + +// Structs... +// ========== +typedef struct { + fix X, Y, Z, alpha, beta, gamma; + fix X_dot, Y_dot, Z_dot, alpha_dot, beta_dot, gamma_dot; +} State; + +// Stuff that used to be in physhand.h.... +// ======================================= +typedef int32_t object_number; + +#define physics_handle_to_object_number(ph) (ph2on[ph]) +#define object_number_to_physics_handle(on) (on2ph[on]) + +extern "C" { + +void EDMS_init_handles(void); +physics_handle EDMS_bind_object_number(object_number on); +void EDMS_remap_object_number(object_number old, object_number nu); +physics_handle EDMS_get_free_ph(void); +void EDMS_release_object(physics_handle ph); + +} + +// Terrain +// ======= +Q terrain(Q X, Q Y, int32_t deriv); // This calls Terrain() +TerrainHit indoor_terrain(Q X, Q Y, Q Z, Q R, physics_handle ph, TFType type); // Indoor for Citadel, FBO, etc... + +extern "C" { + +fix Terrain(fix X, fix Y, int32_t deriv); // This is provided by the user... +TerrainHit Indoor_Terrain(fix X, fix Y, fix Z, fix R, physics_handle ph, TFType type); // As is this... + +// Here's the actual indoor guy we ask for... +// ------------------------------------------ +typedef struct { + // Filled by user when Indoor_Terrain is called... + fix cx, cy, cz; + fix fx, fy, fz; + fix wx, wy, wz; +} TerrainData; + +extern TerrainData terrain_info; // Struct name EDMS expects... + +// Freefall terrain data structures... +// ----------------------------------- +typedef struct { + // The ground... + fix g_height, g_dx, g_dy, g_dz; + // Any walls... + fix w_x, w_y, w_z; + // Squishiness, friction, et cetera... + fix terrain_information; + // For terrain return information... + int32_t DATA1, DATA2; + // Only needed for "fast" terrain calls + fix my_size; + // Who's responsible... + physics_handle caller; +} terrain_ff; + +bool FF_terrain(fix X, fix Y, fix Z, uchar fast, terrain_ff *TFF); // From Freefall... +bool FF_raycast(fix x, fix y, fix z, fix *vec, fix range, fix *where_hit, terrain_ff *tff); +} + +bool ff_terrain(Q X, Q Y, Q Z, uchar fast, terrain_ff *TFF); // For the refined... +bool ff_raycast(Q x, Q y, Q z, Fixpoint *vec, Q range, Fixpoint *where_hit, terrain_ff *FFT); + +// Motion package functions... +// =========================== + +// Marble... +// --------- +void marble_X(int32_t object); +void marble_Y(int32_t object); +void marble_Z(int32_t object); + +// Robot... +// -------- +void robot_X(int32_t object); +void robot_Y(int32_t object); +void robot_Z(int32_t object); + +// Deformable objects... +// --------------------- +void field_point_X(int32_t object); +void field_point_Y(int32_t object); +void field_point_Z(int32_t object); + +// Have some arrays... +// =================== + +// binary database (collision) operators... +// ======================================== + +// Playfield information and scaling... +// ------------------------------------ +//#define COLLISION_SIZE 100 +#define DELTA_BY_TWO .5 + +#define NUM_OBJECT_BITS 32 + +#define object_bit(n) (1 << (n & 31)) + +// To turn on an element... +// ------------------------ +#define write_object_bit(X, Y, obit) (data[X][Y] |= obit) + +// Turn it off... +// -------------- +#define delete_object_bit(X, Y, obit) (data[X][Y] &= ~(obit)) + +// Test a bit... +// ------------- +#define test_object_bit(X, Y, object) (data[X][Y] & object_bit(object)) + +// Check for a given collision... +// ------------------------------ +#define check_object(caller, looker) \ + (data[(hash_scale * A[caller][DOF_X][0]).to_int()][(hash_scale * A[caller][DOF_Y][0]).to_int()] & \ + object_bit(looker)) + +// This used to be a function in collide.cc +// I had to change the name because Seamus had some files locked out. +#define check_for_hit(other_object) (test_bitmask & object_bit(other_object)) + +// Ta Daa. +// ======= + +#include "externs.h" +#endif // __EDMS_INT_H diff --git a/engine/src/Libraries/EDMS/Source/edms_mod.h b/engine/src/Libraries/EDMS/Source/edms_mod.h new file mode 100644 index 0000000..d4fefd8 --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/edms_mod.h @@ -0,0 +1,53 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Here is an include file for EDMS internal functions. It contains all that +// Stuff that a growing model might need. Also, it's a good idea, dammit! +// ======================================================================= + +// A girl's gotta have some standards. +// =================================== +#define EDMS_DIV_ZERO_TOLERANCE .0005 + +#include +#include "fix.h" + +// State and args... +// ================= +extern EDMS_Argblock_Pointer A; +extern Q S[MAX_OBJ][7][4], I[MAX_OBJ][DOF_MAX]; + +// Functions... +// ============ +extern void (*idof_functions[MAX_OBJ])(int32_t); +extern void (*equation_of_motion[MAX_OBJ][7])(int32_t); + +// Callbacks... +// ------------ +extern void (*EDMS_object_collision)(physics_handle caller, physics_handle victim, int32_t badness, int32_t DATA1, + int32_t DATA2, fix location[3]), + (*EDMS_wall_contact)(physics_handle caller); + +// Collision systems... +// -------------------- +extern int32_t are_you_there(int32_t object); // May not be needed by most models, +// extern int check_for_hit( int ); //due to use of Intrsect.cc + +// Sleepy Snoozy... +// ---------------- +extern int32_t no_no_not_me[MAX_OBJ]; diff --git a/engine/src/Libraries/EDMS/Source/externs.h b/engine/src/Libraries/EDMS/Source/externs.h new file mode 100644 index 0000000..019471b --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/externs.h @@ -0,0 +1,163 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* +** externs.h +** +** extern declarations in EDMS +** +** $Header: r:/prj/lib/src/new_edms/RCS/externs.h 1.8 1994/09/19 14:16:17 dfan Exp $ +** $Log: externs.h $ +* Revision 1.8 1994/09/19 14:16:17 dfan +* A_is_active, object_check_hash +* +* Revision 1.7 1994/08/19 19:32:51 dfan +* declaration of data[][] +* +* Revision 1.6 1994/08/15 18:43:18 roadkill +* *** empty log message *** +* +* Revision 1.5 1994/08/12 17:30:02 dfan +* check_for_hit now macro +* +* Revision 1.4 1994/08/12 15:17:19 roadkill +* *** empty log message *** +* +* Revision 1.3 1994/08/11 15:46:34 dfan +* are_you_there, check_for_hit, +* callback functions +* +* Revision 1.2 1994/08/10 12:08:48 dfan +* k, min_scale_slice +* +* Revision 1.1 1994/08/10 11:37:01 dfan +* Initial revision +* +*/ + +#ifndef __EXTERNS_H +#define __EXTERNS_H + +#include + +#include "fixpp.h" + +// state of each object +extern Q S[MAX_OBJ][DOF][DOF_DERIVS]; + +// This is a copy of S that can be mucked with +extern EDMS_Argblock_Pointer A; + +// internal degrees of freedom for each object +extern Q I[MAX_OBJ][DOF_MAX]; + +// expansion coefficients ?? +extern Q k[4][MAX_OBJ][DOF]; + +// 0 if this object is sleeping ?? +extern int32_t no_no_not_me[MAX_OBJ]; + +// minimum valid physics handle +extern int32_t min_physics_handle; + +// ?? +extern const Q min_scale_slice; + +// does A contain the current state of awake objects, instead of S? +extern bool A_is_active; + +//// interfac.cc + +// tables to go back and forth from physics handle to object number +extern object_number ph2on[MAX_OBJ]; +extern physics_handle on2ph[MAX_OBJ]; + +// callback functions +extern void (*EDMS_object_collision)(physics_handle caller, physics_handle victim, int32_t badness, int32_t DATA1, int32_t DATA2, + fix location[3]); +extern void (*EDMS_autodestruct)(physics_handle caller); +extern void (*EDMS_off_playfield)(physics_handle caller); +extern void (*EDMS_sleepy_snoozy)(physics_handle caller); + +//// collide.cc + +extern uint32_t data[EDMS_DATA_SIZE][EDMS_DATA_SIZE]; + +//// + +// multiply by this to go from physics units to collision bin units +extern Q hash_scale; + +// ?? +extern int32_t EDMS_robot_global_badness_indicator; + +////////////////////////////// functions + +// Killers and snoozers... +// ======================= +void EDMS_initialize(EDMS_data *D); +int32_t EDMS_kill(int32_t object); +void collision_wakeup(int32_t object); + +// Solvers +// ======= +void soliton(Q timestep); +void soliton_lite(Q timestep); +void soliton_lite_holistic(Q timestep); +void soliton_vector(Q timestep); +void soliton_vector_holistic(Q timestep); + +// Tools +// ===== +int32_t settle_object(int32_t object); +void mprint_state(int32_t object); +void inventory_and_statistics(int32_t show_sleepers); +int32_t sanity_check(); + +// Collisions +// ========== +void exclude_from_collisions(int32_t guy_1, int32_t guy_2); +void reset_collisions(int32_t object); + +// EDMS internal testbed wireframe... +// ================================== +void draw_object(int32_t); +void setup_graphics(); +void kill_graphics(); + +// Get the Euler angles we need from the stuff in the state... +// =========================================================== +void EDMS_get_Euler_angles(Q &alpha, Q &beta, Q &gamma, int32_t object); + +////////////////////////////// more stuff + +// Collision handling... +// --------------------- +void write_object(int32_t object); // Write and unwrite to the collision table +void delete_object(int32_t object); // based on arguments... +void state_write_object(int32_t object); // and state. +void state_delete_object(int32_t object); +int are_you_there(int32_t object); +bool object_check_hash(int32_t object, int32_t hx, int32_t hy); + +extern uint32_t test_bitmask; + +// int check_for_hit( int other_object ); // are_you_there must be called first! +// has been turned into a macro in edms_int.h! + +#endif // __EXTERNS_H diff --git a/engine/src/Libraries/EDMS/Source/globals.cc b/engine/src/Libraries/EDMS/Source/globals.cc new file mode 100644 index 0000000..7316e74 --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/globals.cc @@ -0,0 +1,33 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Header: n:/project/lib/src/edms/RCS/globals.cc 1.1 1994/02/28 17:07:14 roadkill Exp $ + */ + +// This is the GLOBALS file for EDMS. It's a girl?! +// ================================================= + +#include "fixpp.h" + +Q END = -9999., VACUUM = 0., MARBLE = 1., FIELD_POINT = 2., ROBOT = 3, BIPED = 4, PELVIS = 5, DEATH = 6, D_FRAME = 7; + +int32_t min_physics_handle = 0; + +// That's it for now. +// ================== diff --git a/engine/src/Libraries/EDMS/Source/idof.h b/engine/src/Libraries/EDMS/Source/idof.h new file mode 100644 index 0000000..71a1cf3 --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/idof.h @@ -0,0 +1,253 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* +** idof.h +** +** #defines for internal degrees of freedom in EDMS +** +** $Header: r:/prj/lib/src/new_edms/RCS/idof.h 1.4 1994/09/07 15:00:54 dfan Exp $ +** $Log: idof.h $ +* Revision 1.4 1994/09/07 15:00:54 dfan +* Beginning to try to figure out robot +* +* Revision 1.3 1994/08/15 18:43:35 roadkill +* *** empty log message *** +* +* Revision 1.2 1994/08/10 16:04:13 dfan +* Blackley/approved names for indices 20/29 +* +* Revision 1.1 1994/08/10 11:37:25 dfan +* Initial revision +* +*/ + +////////////////////////////// +// +// Wherein we discover how I[] is indexed +// +////////////////////////////// + +// marble I[] +// 17 Z control +// 18 X control +// 19 Y control +// +// marble objectX +// +// 0 terrain height +// 1 terrain dz/dx +// 2 terrain dz/dy +// 3 cos thetaZ +// 4 sin thetaX +// 5 sin thetaY +// 6 zeta = height above ground +// 7 zeta < size? +// 8 z moment +// 9 ?? +// 10 proportional to dx (actual compression?) +// 11 proportional to dy +// 12 distance between us and other object +// 13 our radius plus other object's radius +// 14 dx +// 15 dy +// 16 eta: C * mass * penetration +// 17 X drag? +// 18 Y drag? +// 19 pep + +// biped I[] +// +// 0 = -N_r[2], whatever that means +// 1-2 ?? +// 3-8 save_r_w_r, save_r_w_l ?? +// 9 += delta_chi ?? +// 10 ?? +// 11 for computing a radius ?? +// 12 used in computing biped x and y coordinate +// 13 ?? +// 14 ?? +// 15 ?? +// 16 used in knees, right foot gamma? +// 17 left foot gamma? +// 18 ?? +// 19 ?? +// +// biped objectX +// +// 0 x component of wall vector +// 1 y component of wall vector +// 2 ?? +// 3 ?? +// 4 delta x +// 5 delta y +// 6 +// 7 delta magnitude +// 8 omega magnitude + +// pelvis I[] +// +// 0 ?? +// 1-5 ?? +// 6 ?? +// 7 crouch control +// 8 kickback ?? control? +// 9 ?? control? +// 10 cyberspace +// 11-13 ?? +// 14 damage ? +// 15 lean control +// 16 turn control +// 17 jump control +// 18 x control +// 19 y control +// +// pelvis objectX +// +// 0 V_raw x +// 1 V_raw y +// 2 V_raw z +// 3 1/checker? +// 4 normalized 0? +// 5 normalized 1? +// 6 normalized 2? +// 7 delta magnitude +// 8 hardness (I[20]) +// 9 cyberspace? +// 10 result of shall_we_dance ? +// 11 result of shall_we_dance ? +// 12 distance to other object +// 13 sum of our radius plus other object's +// 14 dx +// 15 dy +// 16 dz +// 17 ?? +// 18 jump jets +// 19 ?? +// 20 F_m x? +// 21 F_m y? +// 22 F_m z? + +// robot I[] +// +// +// +// robot objectX +// +// 0 x terrain +// 1 y terrain +// 2 z terrain +// 3 1/terrain distance? +// 4 normalized 0 +// 5 normalized 1 +// 6 normalized 2 +// 7 delta magnitude ?? +// 8 omega magnitude ?? +// 9 + +// 2 means cyberspace ?? +#define IDOF_CYBERSPACE 10 + +// 14 robot damage? + +// 16-19 control? + +// idofs 20-29 are model-specific +#define IDOF_MODEL_OFFSET 20 +#define OFFSET(x) ((x)-IDOF_MODEL_OFFSET) + +#define IDOF_MARBLE_K 20 +#define IDOF_MARBLE_D 21 +#define IDOF_MARBLE_RADIUS 22 +#define IDOF_MARBLE_ROLL_DRAG 23 +#define IDOF_MARBLE_MASS_RECIP 24 +#define IDOF_MARBLE_GRAVITY 25 +#define IDOF_MARBLE_MASS 26 +#define IDOF_MARBLE_27 27 +#define IDOF_MARBLE_28 28 +#define IDOF_MARBLE_29 29 + +#define IDOF_PELVIS_K 20 +#define IDOF_PELVIS_D 21 +#define IDOF_PELVIS_RADIUS 22 +#define IDOF_PELVIS_ROLL_DRAG 23 +#define IDOF_PELVIS_MASS_RECIP 24 +#define IDOF_PELVIS_GRAVITY 25 +#define IDOF_PELVIS_MASS 26 +#define IDOF_PELVIS_MOI_RECIP 27 +#define IDOF_PELVIS_ROT_DRAG 28 +#define IDOF_PELVIS_MOI 29 + +#define IDOF_BIPED_MASS 20 +#define IDOF_BIPED_KAPPA_LEG 21 +#define IDOF_BIPED_DELTA_LEG 22 +#define IDOF_BIPED_L_HIP 23 +#define IDOF_BIPED_L_THIGH 24 +#define IDOF_BIPED_L_SHIN 25 +#define IDOF_BIPED_L_TORSO 26 +#define IDOF_BIPED_M_BAL 27 +#define IDOF_BIPED_SKILL 28 +#define IDOF_BIPED_GRAVITY 29 + +#define IDOF_DEATH_MASS 20 +#define IDOF_DEATH_MASS_RECIP 21 +#define IDOF_DEATH_IALPHA_RECIP 22 +#define IDOF_DEATH_IBETA_RECIP 23 +#define IDOF_DEATH_IGAMMA_RECIP 24 +#define IDOF_DEATH_FLUID_DRAG 25 +#define IDOF_DEATH_GRAVITY 26 +#define IDOF_DEATH_SIZE 27 +#define IDOF_DEATH_28 28 +#define IDOF_DEATH_29 29 + +#define IDOF_ROBOT_K 20 +#define IDOF_ROBOT_D 21 +#define IDOF_ROBOT_RADIUS 22 +#define IDOF_ROBOT_ROLL_DRAG 23 +#define IDOF_ROBOT_MASS_RECIP 24 +#define IDOF_ROBOT_GRAVITY 25 +#define IDOF_ROBOT_MASS 26 +#define IDOF_ROBOT_MOI_RECIP 27 +#define IDOF_ROBOT_ROT_DRAG 28 +#define IDOF_ROBOT_MOI 29 + +// What kind of model? +// See globals.cc +#define IDOF_MODEL 30 + +#define IDOF_RADIUS 31 + +// what are these? +// 32 flag +// 33-35 external force +#define IDOF_32 32 +#define IDOF_33 33 +#define IDOF_34 34 +#define IDOF_35 35 + +// see robot.cc, what is this? +#define IDOF_36 36 + +// -1: can collide with anything +// x: cannot collide with object # x +#define IDOF_COLLIDE 37 + +// -1: about to be autodestructed +// 0: cannot be autodestructed +// 1: can be autodestructed +#define IDOF_AUTODESTRUCT 38 diff --git a/engine/src/Libraries/EDMS/Source/interfac.cc b/engine/src/Libraries/EDMS/Source/interfac.cc new file mode 100644 index 0000000..16e4a69 --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/interfac.cc @@ -0,0 +1,755 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Header: n:/project/lib/src/edms/RCS/interfac.cc 1.11 1994/04/20 18:44:15 roadkill Exp $ + */ + +// State information and utilities... +// ======================== + +#include "fixpp.h" +#include "edms_int.h" +#include "idof.h" + +#pragma require_prototypes off // Added by KC for this file for Mac version. + +#ifdef EDMS_SHIPPABLE +//#include "mout.h" +#endif + +// Here we need include files for each and every model that we'll be using... +// ===================================================== +#include "robot.h" + +// The physics handles definitions... +// ================================== +#include "physhand.h" + +// Sanity... +// --------- +extern int EDMS_integrating; + +// Callbacks... +// ============ +void (*EDMS_object_collision)(physics_handle caller, physics_handle victim, int32_t badness, int32_t DATA1, + int32_t DATA2, fix location[3]) = NULL, + (*EDMS_autodestruct)(physics_handle caller) = NULL, + (*EDMS_off_playfield)(physics_handle caller) = NULL, + (*EDMS_sleepy_snoozy)(physics_handle caller) = NULL; + +// Robots... +// --------- +typedef struct { + fix mass, size, hardness, pep, gravity; + int32_t cyber_space; +} Robot; + +// Conversions... +// ============== +// physics handle to object number... +// ---------------------------------- +object_number ph2on[MAX_OBJ]; + +// Object number to physics handle... +// ---------------------------------- +physics_handle on2ph[MAX_OBJ]; + +// Constants... +// ============ +Q fix_zero = 0.; + +// Bridge routine to the terrain functions. C++ functions are all lower case, C are Capped... +// =========================================================================================== + +// This is outside the extern "C" {} thing +// because it is not called by the user, +// but rather calls the user function Terrain(). +// ============================================= +Q terrain(Q X, Q Y, int32_t deriv) { + Q ans; + + printf("There is no terrain in space!\n"); + // ans.fix_to( Terrain( X.to_fix(), Y.to_fix(), deriv ) ); + return ans; +} + +// Same with Indoors... +// -------------------- +TerrainHit indoor_terrain(Q X, Q Y, Q Z, Q R, physics_handle ph, TFType type) { + if ((X > 1) && (Y > 1) && (X < 64) && (Y < 64)) { + return Indoor_Terrain(X.to_fix(), Y.to_fix(), Z.to_fix(), R.to_fix(), ph, type); + } else { + // EDMS_robot_global_badness_indicator = 1000; + // mout << "!EDMS: integrator = " << EDMS_integrating << " !!\n"; + // mout << "!EDMS: Physics handle: " << ph << " is asking for bad terrain.\n"; + // mout << "!EDMS: Asked for location: (" << X << ", " << Y << ", " << Z << ").\n"; + + if (ph > -1) { + int on = ph2on[ph]; + // mout << "!EDMS: Integration location (should match): (" << A[on][0][0] << ", " << A[on][1][0] << + // ", " << A[on][2][0] << ").\n"; mout << "!EDMS: Object location last frame: (" << S[on][0][0] << + // ", " << S[on][1][0] << ", " << S[on][2][0] << ").\n"; mout << "!EDMS: Sleep: " << + // no_no_not_me[on] << ", EDMS_sanity_check = " << sanity_check() << ".\n"; mout << "!EDMS: object + // number " << on << ", EDMS_type: " << I[on][30] << "\n"; mout << "!EDMS: Calling AWOL + // callback...\n"; mout << "Awol in interfac.cc\n"; + + EDMS_off_playfield(ph); + no_no_not_me[on] = 0; // Safety!!! + } else { + // mout << "!EDMS: No further information is available for physics handle -1. SORRY!\n"; + } + } + // If we're off the map, we should probably terminate the search. + return HIT_FACELET; +} + +// And with Freefall... +// -------------------- +bool ff_terrain(Q X, Q Y, Q Z, uchar fast, terrain_ff *FFT) { + return FF_terrain(X.to_fix(), Y.to_fix(), Z.to_fix(), fast, FFT); +} + +bool ff_raycast(Q x, Q y, Q z, Fixpoint *vec, Q range, Fixpoint *where_hit, terrain_ff *FFT) { + return FF_raycast(x.to_fix(), y.to_fix(), z.to_fix(), (fix *)vec, range.to_fix(), (fix *)where_hit, FFT); +} + +bool FF_terrain(fix X, fix Y, fix Z, uchar fast, terrain_ff *TFF) { return (true); } + +bool FF_raycast(fix x, fix y, fix z, fix *vec, fix range, fix *where_hit, terrain_ff *tff) { return (true); } + +// We need to link to c... +// ======================= +extern "C" { + +// Startup the mighty and perilous EDMS engine... +// ============================================== +void EDMS_startup(EDMS_data *D) { + // Stoke the internals... + // ====================== + EDMS_initialize(D); + + // Get the handles in order... + // =========================== + EDMS_init_handles(); + + // Set the callbacks... + // ==================== + EDMS_object_collision = D->collision_callback; + EDMS_autodestruct = D->autodestruct_callback; + EDMS_off_playfield = D->awol_callback; + EDMS_sleepy_snoozy = D->snooz_callback; + + // Done. + // ===== +} + +////////////////////////////// +// +// Tells EDMS what space to use for A +// +void EDMS_set_workspace(void *place) { A = (EDMS_Argblock_Pointer)place; } + +// Although this seems a very stupid way to do this, it's actually not... +// ====================================================================== + +// Autodestruct gets turned on... +// ============================== +void EDMS_set_autodestruct(physics_handle ph) { + if (ph > -1) { + int32_t object = ph2on[ph]; + I[object][38] = 1; + } +} + +// Autodestruct gets turned off... +// =============================== +void EDMS_defuse_autodestruct(physics_handle ph) { + if (ph > -1) { + int object = ph2on[ph]; + I[object][38] = 0; + } +} + +// I am death incarnate. 666. So there. +// ===================================== +void EDMS_kill_object(physics_handle ph) { + // Are you there, really??? + // ======================== + if (ph > -1) { + int on = physics_handle_to_object_number(ph), i; + + // Do it, just do it... + // -------------------- + EDMS_kill(on); + EDMS_release_object(ph); + + // Simulate the action of soliton packing... + // ----------------------------------------- + for (i = (on + 1); (i < MAX_OBJ); i++) { + EDMS_remap_object_number(i, (i - 1)); + } + + if (EDMS_integrating == 1) { + // mout << "Killed " << on << " while integrating!\n"; + + // You were... + // =========== + } + } +} + +// How you get your damn stuff out... +// ================================== +void EDMS_get_state(physics_handle ph, State *s) { + + int on = physics_handle_to_object_number(ph); + if (on > -1 && on < MAX_OBJ) { + s->X = S[on][0][0].to_fix(); + s->X_dot = S[on][0][1].to_fix(); + s->Y = S[on][1][0].to_fix(); + s->Y_dot = S[on][1][1].to_fix(); + s->Z = S[on][2][0].to_fix(); + s->Z_dot = S[on][2][1].to_fix(); + s->alpha = S[on][3][0].to_fix(); + s->alpha_dot = S[on][3][1].to_fix(); + s->beta = S[on][4][0].to_fix(); + s->beta_dot = S[on][4][1].to_fix(); + s->gamma = S[on][5][0].to_fix(); + s->gamma_dot = S[on][5][1].to_fix(); + } + +#ifdef EDMS_SHIPPABLE + else + cout << "Hey, EDMS_get_state sez: physics_handle " << ph << " is nonexistant!!\n"; +#endif +} + +// This does exactly what it looks like it does. It is up to the caller to make sure +// that the transport coordinate is not dangerous, for now. Later, perhaps, +// it should make sure of this itself, since this is not a real-time kind of function... +// ======================================================== +void EDMS_holistic_teleport(physics_handle ph, State *s) { + if (ph > -1) { + int on = physics_handle_to_object_number(ph); // Who are youuuu... + + // First, get rid of the collision hash reference (in state since frame is over)... + // ===================================================== + state_delete_object(on); + + // Now move the thing... + // ===================== + S[on][0][0].fix_to(s->X); + S[on][0][1].fix_to(s->X_dot); + S[on][1][0].fix_to(s->Y); + S[on][1][1].fix_to(s->Y_dot); + S[on][2][0].fix_to(s->Z); + S[on][2][1].fix_to(s->Z_dot); + + if (I[on][30] != D_FRAME) { + S[on][3][0].fix_to(s->alpha); + S[on][3][1] = fix_zero; + S[on][4][0].fix_to(s->beta); + S[on][4][1] = fix_zero; + S[on][5][0].fix_to(s->gamma); + S[on][5][1] = fix_zero; + } else { + Q alpha, beta, gamma, sin_alpha, cos_alpha, sin_beta, cos_beta, sin_gamma, cos_gamma; + + // alpha.fix_to( s -> alpha); + // beta.fix_to( s -> beta); + // gamma.fix_to( s -> gamma); + + // For shock... + // ------------ + alpha.fix_to(s->beta); + beta.fix_to(s->gamma); + gamma.fix_to(s->alpha); + + alpha = beta = 0; + + sincos(.5 * alpha, &sin_alpha, &cos_alpha); + sincos(.5 * beta, &sin_beta, &cos_beta); + sincos(.5 * gamma, &sin_gamma, &cos_gamma); + + S[on][3][0] = cos_gamma * cos_alpha * cos_beta + sin_gamma * sin_alpha * sin_beta; + S[on][4][0] = cos_gamma * cos_alpha * sin_beta - sin_gamma * sin_alpha * cos_beta; + S[on][5][0] = cos_gamma * sin_alpha * cos_beta + sin_gamma * cos_alpha * sin_beta; + S[on][6][0] = -cos_gamma * sin_alpha * sin_beta + sin_gamma * cos_alpha * cos_beta; + + // Derivatives + S[on][3][1] = 0; + S[on][4][1] = 0; + S[on][5][1] = 0; + S[on][6][1] = 0; + } + + // Restart collisions on it... + // =========================== + state_write_object(on); + + // Gee, I hope that that is a good location, sunny, and free of solid objects... + // ================================================== + } +} + +void EDMS_mouselook(physics_handle ph, int32_t xlook) { + int32_t on; + State current_state; + + on = physics_handle_to_object_number(ph); + EDMS_get_state(ph, ¤t_state); + + S[on][3][0].fix_to(current_state.alpha + xlook); + S[on][3][1] = fix_zero; +} + +// Here we exclude objects from hitting each specific others... +// ============================================================ +void EDMS_ignore_collisions(physics_handle ph1, physics_handle ph2) { + int32_t on1, on2; + + // Safety dance... + // --------------- + if ((ph1 > -1) && (ph2 > -1)) { + on1 = ph2on[ph1]; + on2 = ph2on[ph2]; + + exclude_from_collisions(on1, on2); + } +} + +// Here we reallow collisions... +// ============================= +void EDMS_obey_collisions(physics_handle ph1) { + int32_t on1; + + // Safety ballet... + // --------------- + if (ph1 > -1) { + on1 = ph2on[ph1]; + reset_collisions(on1); + } +} + +// Turns collisions OFF for a given robot, useful in a variety of household chores... +// ================================================================================== +void EDMS_make_robot_antisocial(physics_handle ph) { + int32_t on = 0; + + // Do you suck... + // -------------- + if (ph > -1) { + on = ph2on[ph]; + if (I[on][30] == ROBOT) + I[on][5] = -1; + } // You suck... +} + +// Turns collisions ON for a given robot, useful in a variety of household chores... +// ================================================================================= +void EDMS_make_robot_social(physics_handle ph) { + int32_t on = 0; + + // Do you suck... + // -------------- + if (ph > -1) { + on = ph2on[ph]; + if (I[on][30] == ROBOT) + I[on][5] = 0; + } // You suck... +} + +// Here is a routine that will attempt to settle an object to the local b/c. It is NOT intended for +// online use. A negative return value indicates a badly placed or unphysical model... +// ================================================================= +int32_t EDMS_settle_object(physics_handle ph) { + int32_t on = 0, return_value = -1; + + // Are you really there? + // --------------------- + if (ph > -1) { + on = ph2on[ph]; + return_value = settle_object(on); + } // Happy joy... + + // All done... + // ----------- + return return_value; +} + +// Prints out a state vector... +// ============================ +void EDMS_mprint_state(physics_handle ph) { + int32_t on = ph2on[ph]; + + mprint_state(on); +} + +// Here is the beginning of an EDMS diagnostic statistics tool... +// ============================================================== +void EDMS_inventory_and_statistics(int32_t show_sleepers) { inventory_and_statistics(show_sleepers); } + +// Here is the sanity checker, but you already can read that, can't you... +// ======================================================================= +int32_t EDMS_sanity_check() { return sanity_check(); } + +// Here are the bridge routines to the "default" EDMS models, others are segregates(d)... +// ====================================================================================== + +// Robot routines... +// ================== + +// Hardness scale for robots... +// ---------------------------- +#define ROBOT_HARD_FAC 10 + +// This guy is BROKEN FOR NOW (until we get the params finalized)... +// ------------------------------------------------ +void EDMS_get_robot_parameters(physics_handle ph, Robot *m) { + int32_t on = physics_handle_to_object_number(ph); + +#ifdef EDMS_SHIPPABLE + if (I[on][IDOF_MODEL] != ROBOT) + mout << "You are trying to get ROBOT parameters for an " << I[on][IDOF_MODEL] << "!\n"; +#endif + + // mout << "RD: " << I[on][IDOF_ROBOT_ROLL_DRAG] << " : M:" << I[on][IDOF_ROBOT_MASS] << "\n"; + m->pep = (I[on][IDOF_ROBOT_ROLL_DRAG] / (1.5 * I[on][IDOF_ROBOT_MASS])).to_fix(); + m->size = I[on][IDOF_ROBOT_RADIUS].to_fix(); + m->hardness = (I[on][IDOF_ROBOT_K] * I[on][IDOF_ROBOT_RADIUS] / (I[on][IDOF_ROBOT_MASS] * ROBOT_HARD_FAC)).to_fix(); + m->mass = I[on][IDOF_ROBOT_MASS].to_fix(); + m->gravity = I[on][IDOF_ROBOT_GRAVITY].to_fix(); + m->cyber_space = I[on][IDOF_CYBERSPACE].to_int(); +} + +// And the compression test for terrain "traps..." +// =============================================== +fix EDMS_get_robot_damage(physics_handle ph) { + int32_t object; + + object = ph2on[ph]; // As stupid as it gets... + return (I[object][14]).to_fix(); +} + +// In flux (Thrust, attitude and JumpJets)... +// ========================================== +void EDMS_control_robot(physics_handle ph, fix T, fix A, fix J) { + + Q TT, // thrust + AA, // attitude jets + JJ; // jump jets + +#ifdef EDMS_SHIPPABLE + if (ph < 0) + mout << "Hey, you are an idiot..."; +#endif + + TT.fix_to(T); + AA.fix_to(A); + JJ.fix_to(J); + + int32_t on = physics_handle_to_object_number(ph); + robot_set_control(on, TT, AA, JJ); +} + +// AI control routines... +// ====================== +void EDMS_ai_control_robot(physics_handle ph, fix D_H, fix D_S, fix S_S, fix U, fix *T_Y, fix D) { + Q DH, // desired heading + DS, // desired speed + SS, // sidestep + UU, // urgency + TU, // there yet? + DD; // distance + +#ifdef EDMS_SHIPPABLE + if (ph < 0) + mout << "Hey, you are and idiot..."; +#endif + + DH.fix_to(D_H); + DS.fix_to(D_S); + SS.fix_to(S_S); + UU.fix_to(U); + DD.fix_to(D); + + int32_t on = physics_handle_to_object_number(ph); + robot_set_ai_control(on, DH, DS, SS, UU, TU, DD); + + *T_Y = TU.to_fix(); +} + +// These are different parameters than for the marble now... +// --------------------------------------------------------- +physics_handle EDMS_make_robot(Robot *m, State *s) { + Q params[10], init_state[6][3]; + + Q mass, pep, hardness, size, gravity; + + int32_t cyber_space; + + int32_t on = 0; + physics_handle ph = 0; + + init_state[DOF_X][0].fix_to(s->X); + init_state[DOF_X][1].fix_to(s->X_dot); + init_state[DOF_Y][0].fix_to(s->Y); + init_state[DOF_Y][1].fix_to(s->Y_dot); + init_state[DOF_Z][0].fix_to(s->Z); + init_state[DOF_Z][1].fix_to(s->Z_dot); + init_state[DOF_ALPHA][0].fix_to(s->alpha); + init_state[DOF_ALPHA][1].fix_to(s->alpha_dot); + init_state[DOF_BETA][0] = init_state[DOF_BETA][1] = init_state[DOF_GAMMA][0] = init_state[DOF_GAMMA][1] = END; + + mass.fix_to(m->mass); + size.fix_to(m->size); + // if ( size > .45/hash_scale ) size = .45/hash_scale; + hardness.fix_to(m->hardness); + pep.fix_to(m->pep); + gravity.fix_to(m->gravity); + cyber_space = m->cyber_space; + + // if (hardness > 15) { mout << "Hardness too too too: " << hardness << "\n"; hardness = 15; } + + if (mass < 1) + mass = 1; + if (mass > 30) + mass = 30; + + hardness = hardness * (mass * ROBOT_HARD_FAC / size); + if (hardness > 4000) { + hardness = 4000; + // mout << "Hard cap!\n"; + } + + params[OFFSET(IDOF_ROBOT_K)] = hardness; + params[OFFSET(IDOF_ROBOT_D)] = 1.5 * sqrt(params[OFFSET(IDOF_ROBOT_K)]) * sqrt(mass); + params[OFFSET(IDOF_ROBOT_RADIUS)] = size; + + // mout << params[OFFSET(IDOF_ROBOT_D)] << "\n"; + + params[OFFSET(IDOF_ROBOT_ROLL_DRAG)] = 1.5 * pep * mass; + params[OFFSET(IDOF_ROBOT_MASS_RECIP)] = 1. / mass; + params[OFFSET(IDOF_ROBOT_GRAVITY)] = gravity; + params[OFFSET(IDOF_ROBOT_MASS)] = mass; + // params[7] = 1. / ( .4*mass*size*size ); + // params[8] = 5.*(1. / params[7]); + // params[9] = .4*mass*size*size; + params[OFFSET(IDOF_ROBOT_MOI)] = .4 * mass * size * size; + params[OFFSET(IDOF_ROBOT_ROT_DRAG)] = 5 * params[OFFSET(IDOF_ROBOT_MOI)]; + + if (params[OFFSET(IDOF_ROBOT_MOI)] != 0) + + params[OFFSET(IDOF_ROBOT_MOI_RECIP)] = 1.0 / params[OFFSET(IDOF_ROBOT_MOI)]; + else + params[OFFSET(IDOF_ROBOT_MOI_RECIP)] = 0.0; + + on = make_robot(init_state, params); + + // Here is where cyberspace gets turned on... + // ------------------------------------------ + I[on][IDOF_CYBERSPACE] = (cyber_space > 0); + + ph = EDMS_bind_object_number(on); + +#ifdef EDMS_SHIPPABLE + if (params[OFFSET(IDOF_ROBOT_MOI)] == 0) + mout << "object " << on << " got 0 size or mass\n"; +#endif + + return ph; +} + +void EDMS_set_robot_parameters(physics_handle ph, Robot *m) { + Q mass, hardness, size, pep, gravity; + + int32_t cyber_space; + + mass.fix_to(m->mass); + size.fix_to(m->size); + // if ( size > .45/hash_scale ) size = .45/hash_scale; + hardness.fix_to(m->hardness); + pep.fix_to(m->pep); + gravity.fix_to(m->gravity); + cyber_space = m->cyber_space; + + int32_t on = physics_handle_to_object_number(ph); + +#ifdef EDMS_SHIPPABLE + if (I[on][IDOF_MODEL] != ROBOT) + mout << "You are trying to set ROBOT parameters for an " << I[on][30] << "!\n"; +#endif + + // mout << "Set Robot " << on << "\n"; + // mout << " mass: " << mass << "\n"; + // mout << " size: " << size << "\n"; + // mout << " hard: " << hardness << "\n"; + // mout << " pepp: " << pep << "\n"; + // mout << " grav: " << gravity << "\n"; + + if (mass < 1) + mass = 1; + if (mass > 30) + mass = 30; + + hardness = hardness * (mass * ROBOT_HARD_FAC / size); + + // hardness = hardness*(mass*ROBOT_HARD_FAC/size); + if (hardness > 4000) { + hardness = 4000; + // mout << "Hard cap!\n"; + } + + I[on][IDOF_ROBOT_K] = hardness; + I[on][IDOF_ROBOT_D] = 1.5 * sqrt(I[on][IDOF_ROBOT_K]) * sqrt(mass); + I[on][IDOF_ROBOT_RADIUS] = size; + I[on][IDOF_ROBOT_ROLL_DRAG] = 1.5 * pep * mass; + I[on][IDOF_ROBOT_MASS_RECIP] = 1. / mass; + I[on][IDOF_ROBOT_GRAVITY] = gravity; + I[on][IDOF_ROBOT_MASS] = mass; + I[on][IDOF_ROBOT_MOI_RECIP] = 1. / (.4 * mass * size * size); + I[on][IDOF_ROBOT_ROT_DRAG] = 5. * (1. / I[on][IDOF_ROBOT_MOI_RECIP]); + I[on][IDOF_ROBOT_MOI] = .4 * mass * size * size; + I[on][IDOF_CYBERSPACE] = (cyber_space > 0); + + // mout << "Roll: " << I[on][IDOF_ROBOT_ROLL_DRAG] << "\n"; +} + +// Bridge routines to the solvers!! +// ================================ + +// 4th order and very stable... +// ---------------------------- +void EDMS_soliton(fix timestep) { + Q temp; + temp.fix_to(timestep); + soliton(temp); +} + +// 2nd order and needs some attention... +// ------------------------------------- +void EDMS_soliton_lite(fix timestep) { + Q temp; + temp.fix_to(timestep); + soliton_lite(temp); +} + +// Efficient and unstoppable... +// ---------------------------- +void EDMS_soliton_vector(fix timestep) { + Q temp; + temp.fix_to(timestep); + soliton_vector(temp); +} + +// Won't allow objects to collide w/one another... +// ----------------------------------------------- +void EDMS_soliton_vector_holistic(fix timestep) { + Q temp; + temp.fix_to(timestep); + soliton_vector_holistic(temp); +} + +// This code here handles the mapping between the user's physics handles and the +// dynamically changing intername object numbers. +// ============================================== +// To make a physics handle, edms calls EDMS_bind_object_number to "bind" an internal +// object number to a physics handle. When this object number changes, the function +// EDMS_remap_object_number() needs to be called to map the nu object number to +// the physics number. +// =================== +// The physics handles are valid throughout the life of a physics object; in contrast, +// the object numbers change as the array of objects is compacted. +// =============================================================== +// Make sure the include stuff is included before this. +// ==================================================== + +// Initialization... must call this, man... +// ======================================== +void EDMS_init_handles(void) { + // Fill with the 'end' code... + // --------------------------- + for (int32_t i = 0; i < MAX_OBJ; ++i) + ph2on[i] = on2ph[i] = -1; +} + +// To bind a physics handle to an object number, use this function. +// ================================================================ +physics_handle EDMS_bind_object_number(object_number on) { + physics_handle ph = EDMS_get_free_ph(); + + if (ph == -1) + return -1; + + ph2on[ph] = on; + on2ph[on] = ph; + + return ph; +} + +// EDMS_remap_object_number() remaps object numbers so that the physics_handle that +// used to refer to object number #old will now refer to object number #nu. +// If object #old is not mapped, nothing happens... +// ================================================ + +void EDMS_remap_object_number(object_number old, object_number nu) { + physics_handle ph = on2ph[old]; + + if (ph == -1) + return; + + on2ph[nu] = ph; + on2ph[old] = -1; + ph2on[ph] = nu; +} + +// To release the mapping between physics handle and object number +// (say, when deleting an object)... +// ================================= + +void EDMS_release_object(physics_handle ph) { + object_number on = ph2on[ph]; + + // First, clear out the object number... + // ------------------------------------- + on2ph[on] = -1; + + // Then clear out the physics_handle... + // ------------------------------------ + ph2on[ph] = -1; +} + +// Internal routine to find an unused physics_handle. +// ================================================== + +physics_handle EDMS_get_free_ph(void) { + for (int32_t i = min_physics_handle; i < MAX_OBJ; ++i) { + // Ho, ho... + // --------- + if (ph2on[i] == -1) + return i; + } + // Failed to find one... + // --------------------- + return -1; + // Fun... + // ====== +} + +} // End of extern "C" for the &^%$@% compiler... diff --git a/engine/src/Libraries/EDMS/Source/intrsect.cc b/engine/src/Libraries/EDMS/Source/intrsect.cc new file mode 100644 index 0000000..19b8313 --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/intrsect.cc @@ -0,0 +1,248 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Collision intersection code for EDMS models... +// ============================================== + +// Seamus, "in Prozac we trust," 1994 +// ================================== + +#include "edms_int.h" +#include "idof.h" + +//#ifdef EDMS_SHIPPABLE +////#include +//#endif + +// Collision wakeups go here... +// ---------------------------- +extern int32_t alarm_clock[MAX_OBJ]; +extern int32_t no_no_not_me[MAX_OBJ]; + +bool do_work(int32_t object, int32_t other_object, Q my_rad, Q your_rad, Fixpoint *my_pos, Fixpoint *other_pos, + Q &result0, Q &result1, Q &result2); + +void shall_we_dance(int32_t object, Q &result0, Q &result1, Q &result2); + +// Call me instead of having special code everywhere... +// ==================================================== +void shall_we_dance(int32_t object, Q &result0, Q &result1, Q &result2) { + int32_t other_object; + + Q my_radius, your_radius; + + Q my_position[3], your_position[3]; + + // Collision B/C... + // ---------------- + result0 = result1 = result2 = 0; // B/C... + + // Here we assume that all hits are encompassed by the projection of the + // default radius. If this is not true, then special care must be taken + // in the design of the model... + // ----------------------------- + + // mask contains the bits corresponding to the objects that could be + // intersecting object. + + uint32_t mask = are_you_there(object); + uint32_t bit = 0; // which object bit we're checking + + while (mask != 0) { + if (mask & 1) { + // Object bit number 'bit' is on, we must check all objects which have that bit + for (other_object = bit; other_object < MAX_OBJ && S[other_object][0][0] > END; + other_object += NUM_OBJECT_BITS) { + + if (other_object != object && I[object][IDOF_COLLIDE].to_int() != other_object) { + + // Okay, now we have a confirmed hash hit... + // ----------------------------------------- + + // Do the regular guy, workaday collision... + // ----------------------------------------- + my_position[0] = A[object][0][0]; + my_position[1] = A[object][1][0]; + my_position[2] = A[object][2][0]; + + // if you're asleep, then we have to look at STATE... + // -------------------------------------------------- + if (no_no_not_me[other_object] == 1) { + your_position[0] = A[other_object][0][0]; + your_position[1] = A[other_object][1][0]; + your_position[2] = A[other_object][2][0]; + } else { + your_position[0] = S[other_object][0][0]; + your_position[1] = S[other_object][1][0]; + your_position[2] = S[other_object][2][0]; + } + + my_radius = I[object][IDOF_RADIUS]; + your_radius = I[other_object][IDOF_RADIUS]; + + do_work(object, other_object, my_radius, your_radius, my_position, your_position, result0, result1, + result2); + + int32_t you_are_special = 0, I_am_special = 0; + + // Are YOU special??? + // ------------------ + if (I[other_object][IDOF_MODEL] == PELVIS) { + Q offset_x = I[other_object][0] * sin(A[other_object][4][0]), + offset_y = -1.5 * I[other_object][0] * sin(A[other_object][5][0]), + offset_z = I[other_object][0] * cos(A[other_object][4][0]) * cos(A[other_object][5][0]); + + Q sin_alpha = 0, cos_alpha = 0; + + sincos(-A[other_object][3][0], &sin_alpha, &cos_alpha); + + Q final_x = cos_alpha * offset_x + sin_alpha * offset_y; + Q final_y = -sin_alpha * offset_x + cos_alpha * offset_y; + + your_position[0] = A[other_object][0][0] + final_x; + your_position[1] = A[other_object][1][0] + final_y; + your_position[2] = A[other_object][2][0] + offset_z; + + my_radius = I[object][IDOF_RADIUS]; + your_radius = .75 * I[other_object][IDOF_PELVIS_RADIUS]; + + do_work(object, other_object, my_radius, your_radius, my_position, your_position, result0, + result1, result2); + } // You're not special. + + // Am I special??? + // --------------- + if (I[object][IDOF_MODEL] == PELVIS) { + Q offset_x = I[object][0] * sin(A[object][4][0]), + offset_y = -1.5 * I[object][0] * sin(A[object][5][0]), + offset_z = I[object][0] * cos(A[object][4][0]) * cos(A[object][5][0]); + + Q sin_alpha = 0, cos_alpha = 0; + + sincos(-A[object][3][0], &sin_alpha, &cos_alpha); + + Q final_x = cos_alpha * offset_x + sin_alpha * offset_y; + Q final_y = -sin_alpha * offset_x + cos_alpha * offset_y; + + my_position[0] = A[object][0][0] + final_x; + my_position[1] = A[object][1][0] + final_y; + my_position[2] = A[object][2][0] + offset_z; + + // if you're asleep, then we have to look at STATE... + // -------------------------------------------------- + if (no_no_not_me[other_object] == 1) { + your_position[0] = A[other_object][0][0]; + your_position[1] = A[other_object][1][0]; + your_position[2] = A[other_object][2][0]; + } else { + your_position[0] = S[other_object][0][0]; + your_position[1] = S[other_object][1][0]; + your_position[2] = S[other_object][2][0]; + } + + my_radius = .75 * I[object][IDOF_PELVIS_RADIUS]; + your_radius = I[other_object][IDOF_RADIUS]; + + do_work(object, other_object, my_radius, your_radius, my_position, your_position, result0, + result1, result2); + } // I'm not special... + } // No hash hit... + } + } + + // Shift over the mask so we're testing the next object bit + mask >>= 1; + bit++; + } +} + +Q dx, dy, dz; + +// Here's the meat of the sutuation... +// =================================== +bool do_work(int32_t object, int32_t other_object, Q my_rad, Q your_rad, Fixpoint *my_pos, Fixpoint *other_pos, + Q &result0, Q &result1, Q &result2) { + Q cm_radius = (my_rad + your_rad); + + // First do a preliminary check to avoid overflow. + dx = my_pos[0] - other_pos[0]; + dy = my_pos[1] - other_pos[1]; + dz = my_pos[2] - other_pos[2]; + + if (dx >= cm_radius || dy >= cm_radius || dz >= cm_radius) { + return false; // couldn't possibly collide + } + + // Test for primary collision... + // ============================= + Q test_radius = sqrt(dx * dx + dy * dy + dz * dz); + + if ((test_radius < cm_radius) && (test_radius > 0.0005)) { + + // Is there a problem??? + // --------------------- + if (test_radius < .03) + test_radius = .03; + + // Callback... + // ----------- + physics_handle C = on2ph[object], V = on2ph[other_object]; + + int32_t badness = (20 * (1. - test_radius / cm_radius)).to_int(); + + fix location[3]; + + location[0] = my_pos[0].to_fix(); + location[1] = my_pos[1].to_fix(); + location[2] = my_pos[2].to_fix(); + + EDMS_object_collision(C, V, badness, 0, 0, location); + + Q Eta = (cm_radius - test_radius); // Eta... + + test_radius = 1 / test_radius; + result0 += Eta * dx * test_radius; + result1 += Eta * dy * test_radius; + result2 += Eta * dz * test_radius; + + // God save the Queen... + // --------------------- + if (result0 > my_rad) + result0 = my_rad; + if (result0 < -my_rad) + result0 = -my_rad; + + if (result1 > my_rad) + result1 = my_rad; + if (result1 < -my_rad) + result1 = -my_rad; + + // Wakeup... + // ========= + if (no_no_not_me[other_object] == 0) { + // mout << "Other guy was asleep: " << other_object << "\n"; + // collision_wakeup( other_object ); + alarm_clock[other_object] = 1; + } + + return true; // collision + } // End of radius check... + else { + return false; + } +} diff --git a/engine/src/Libraries/EDMS/Source/phy_tool.cc b/engine/src/Libraries/EDMS/Source/phy_tool.cc new file mode 100644 index 0000000..ffca020 --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/phy_tool.cc @@ -0,0 +1,359 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Here is a box of tools for making physics easier for the programmer... +// =================================================== + +// Well, aren't we snooty... +// ========================= + +// Seamus, Nov 9 1993... +// ===================== + +// Get on with it then... +// ====================== +#include +#include "edms_int.h" //Object types, END conventions, etc. +#include "idof.h" + +#include "physhand.h" + +// extern "C" { //Debugging spew... +//} + +// ============== +// INTERNAL STUFF +// ============== + +void snobby_soliton_lite(Q timestep, int32_t object); + +extern void (*idof_functions[MAX_OBJ])(int), // Pointers to the appropriate places... + (*equation_of_motion[MAX_OBJ][7])(int); // The integer is the object number... + +// Courtesy of C++ and inline fixpoint and such... +// =============================================== +static Q one_sixth = .1666666666667, // Overboard? + point_five = .5, point_one_two_five = .125, two = 2., point_1 = 0.1; + +// Here is a routine that should help in the placement of EDMS objects. 3D+ only! +// =============================================================================== +int32_t settle_object(int32_t object) { + int return_value = -1; // Failure... + + Q nrg = 1000., nrg_min = .1, + pass_time = .01; // Not meta-stable! + + int32_t count = 0, max_count = 1000; + + // First, are you for real? + // ------------------------ + if (S[object][0][0] > END) { + // Ok, now let's actually DO the settling... + // ========================================= + while ((nrg > nrg_min) && (count < max_count)) { + + snobby_soliton_lite(pass_time, object); + + nrg = S[object][DOF_X][1] * S[object][DOF_X][1] + + S[object][DOF_Y][1] * S[object][DOF_Y][1] // Too dangerous for assumptions... + + S[object][DOF_Z][1] * S[object][DOF_Z][1]; // Forget the others... + + count += 1; + } + + // Did we diverge? + // --------------- + if (count < max_count) + return_value = 1; // Success! + + // Yes, you were for real... + // ------------------------- + } + + // All done... + // =========== + return return_value; +} + +// Here is a version of soliton_lite which runs on only one object. This should be useful for +// settling objects at level starts, placing things exactly on the ground, etc... +// ============================================================================== +void snobby_soliton_lite(Q timestep, int32_t object) { + int32_t coord; + + // Copy the state vector initially into the argument vector... + // =========================================================== + state_delete_object(object); // Do collisions, let this guy go free for a while... + + for (coord = 0; coord < DOF && S[object][coord][0] > END; coord++) { + A[object][coord][0] = S[object][coord][0]; + A[object][coord][1] = S[object][coord][1]; + } + + // Here is the leading (order zero) term... + // ======================================== + (*idof_functions[object])(object); + + for (coord = 0; coord < DOF && S[object][coord][0] > END; coord++) { + k[0][object][coord] = timestep * S[object][coord][2]; + } + + // Here is a frobbed term... + // ------------------------- + for (coord = 0; coord < DOF && S[object][coord][0] > END; coord++) { + A[object][coord][0] = S[object][coord][0] + timestep * S[object][coord][1]; + A[object][coord][1] = S[object][coord][1] + k[0][object][coord]; + } + + (*idof_functions[object])(object); + + for (coord = 0; coord < DOF && S[object][coord][0] > END; coord++) { + k[1][object][coord] = timestep * S[object][coord][2]; + } + + // Hey! We're already able to assemble the solution! Wasn't that better than soliton? + // ===--------=======----------------------------------------------------------------- + for (coord = 0; coord < DOF && S[object][coord][0] > END; coord++) { + S[object][coord][0] = S[object][coord][0] + timestep * S[object][coord][1] + + point_five * (timestep * timestep * k[0][object][coord]); + + S[object][coord][1] = S[object][coord][1] + point_five * (k[0][object][coord] + k[1][object][coord]); + } + + state_write_object(object); // Put it back in. Note that IT saw EVERYONE else, but they only see IT now! +} + +// Here is a tool that Marc LeBlanc has requested... +// ================================================= +void mprint_state(int32_t object) { + /* + int coord = 0, + deriv = 0; + + if ( S[object][0][0] > END ) //Are you for real? + { + + for ( coord = 0; (coord < DOF) && (S[object][coord][0] > END); coord++ ) + { + for ( deriv = 0; deriv < 3; deriv++ ) + { + } + } + } + */ +} + +// Here is an inventory of everything in the system... +// =================================================== +void inventory_and_statistics(int32_t show_sleepers) { + int32_t object = 0; + + for (object = 0; object < MAX_OBJ && S[object][0][0] > END; object++) { + if ((no_no_not_me[object] == 1) || show_sleepers == 1) { +#ifdef EDMS_SHIPPABLE + + // mout << object << ".) "; + + // mout << "Physics handle: " << on2ph[object] << " is a " << I[object][IDOF_MODEL] + // << " at X:" << S[object][DOF_X][0] << " Y:" << S[object][DOF_Y][0] << " Z:" << + // S[object][DOF_Z][0] + // << " Sleep: "; + + // if ( no_no_not_me[object] == 0 ) mout << "Y"; + // else mout << "N"; + + // mout << "\n"; + +#endif + } + } // End of sleeper check... + + //#ifdef EDMS_SHIPPABLE + // mout << "There are " << object << " objects currently running.\n"; + //#endif +} + +// This is EDMS' sanity checker. Call it to see what's wrong. Problems +// will return a nonzero result... +// =============================== +int sanity_check() { + // The idof functions... + // ===================== + // extern void biped_idof( int ), + // marble_idof( int ), + // robot_idof( int ), + // pelvis_idof( int ), + // deadly_idof( int ); + + // Have some variables... + // ---------------------- + + int32_t object = 0; + + physics_handle ph = 0; + +// Finally, EDMS error codes, included here only, see EDMS.h +// ========================================================= +#define EDMS_TOO_MANY_OBJECTS 1 +#define EDMS_PHYSICS_HANDLES_CORRUPT 2 +#define EDMS_OBJECT_HANDLES_CORRUPT 3 +#define EDMS_IDOF_POINTERS_CORRUPT 4 + + // Here's the return value... + // -------------------------- + int32_t return_value = 0; // Innocent until... + + // First check the number of active objects... + // =========================================== + for (object = 0; S[object][0][0] > END; object++) + ; + if (object > MAX_OBJ) { + return_value = EDMS_TOO_MANY_OBJECTS; + + //#ifdef EDMS_SHIPPABLE + // mout << "EDMS_sanity_check: too many objects (" << object << ")!\n"; + //#endif + } + + // Now check the physics handle mappings... + // ======================================== + for (object = 0; S[object][0][0] > END; object++) { + if (ph2on[(on2ph[object])] != object) { + return_value = EDMS_PHYSICS_HANDLES_CORRUPT; + //#ifdef EDMS_SHIPPABLE + // mout << "EDMS_sanity_check: physics handles corrupted (object " << object << ")!\n"; + //#endif + } + } + + // Now the object handle mappings... + // ================================= + for (ph = 0; ph < MAX_OBJ; ph++) { + if (ph2on[ph] > 0 && ph2on[ph] < MAX_OBJ) { + if (on2ph[(ph2on[ph])] != ph) { + return_value = EDMS_OBJECT_HANDLES_CORRUPT; + + //#ifdef EDMS_SHIPPABLE + // mout << "EDMS_sanity_check: object handles corrupted (handle " << ph << + //")!\n"; #endif + } + } + } + + // Now we see if the idof pointers are corrupt... + // ============================================== + // for ( object = 0; S[object][0][0] > END; object++ ) { + // if ( idof_functions[object] != biped_idof + // && idof_functions[object] != marble_idof + // && idof_functions[object] != robot_idof + // && idof_functions[object] != pelvis_idof + // && idof_functions[object] != deadly_idof + // ) { + // return_value = EDMS_IDOF_POINTERS_CORRUPT; + // + //#ifdef EDMS_SHIPPABLE + // mout << "EDMS-sanity_check: idof pointers corrupt (object: " << object << " with ph: " << on2ph[object] + //<< ")!\n"; #endif + // + // } + // } + + // Und das ist alles... + // ==================== + return return_value; +} + +// Now that EDMS is no longer using Euler angles to represent object orientation, +// and since space is really 6D + a constraint, and all, and because SU(2) is +// rilly rilly my best friend, and because other people rilly rilly love +// Euler angles, and because using this routine will give them those, and +// because even though the models won't blow up at alpha=2pi this conversion +// becomes degenerate in roll and heading (beta and gamma) and such... +// =================================================================== + +// Seamus says: Use my matrix whenever you can, this conversion incurrs many +// of the same problems that using Euler angles to begin with causes. Come +// talk to me about how YOUR game could benefit from Dirac spinors today! +// ---------------------------------------------------------------------- + +// Get the Euler angles we need from the stuff in the state... +// =========================================================== +void EDMS_get_Euler_angles(Q &alpha, Q &beta, Q &gamma, int32_t object) { + Q e0, e1, e2, e3; + + e0 = S[object][DOF_ALPHA][0]; + e1 = S[object][DOF_BETA][0]; + e2 = S[object][DOF_GAMMA][0]; + e3 = S[object][DOF_DIRAC][0]; + + // Get the trig information we need... + // =================================== + alpha = asin(2 * (e0 * e2 - e1 * e3)); + Q cos_alpha = cos(alpha); + +#define EDMS_EULER_CONVERSION_TRIG_ZERO .0001 + + if (cos_alpha > 0 && cos_alpha < EDMS_EULER_CONVERSION_TRIG_ZERO) + cos_alpha = EDMS_EULER_CONVERSION_TRIG_ZERO; + if (cos_alpha < 0 && cos_alpha > -EDMS_EULER_CONVERSION_TRIG_ZERO) + cos_alpha = -EDMS_EULER_CONVERSION_TRIG_ZERO; + + gamma = acos((e0 * e0 + e1 * e1 - e2 * e2 - e3 * e3) / cos_alpha); + if ((e1 * e2 + e0 * e3) < 0) + gamma *= -1; // sgn... + + beta = acos((e0 * e0 - e1 * e1 - e2 * e2 + e3 * e3) / cos_alpha); + if (e2 * e3 + e0 * e1 < 0) + beta *= -1; + + alpha *= -1; + beta *= -1; + gamma *= -1; +} + +extern "C" { + +#pragma require_prototypes off + +// Call this to see if an object is asleep... +// ========================================== +bool EDMS_frere_jaques(physics_handle ph) { + bool rval = false; + + // condomate... + // ------------ + if (ph > -1) { + if (no_no_not_me[ph2on[ph]] == 1) + rval = true; + } + + return rval; +} + +// Call this to wake an object up... +// ================================= +void EDMS_crystal_meth(physics_handle ph) { + + if (ph > -1) { + no_no_not_me[ph2on[ph]] = 1; + } +} + +#pragma require_prototypes on + +} // End of "Extern "C""... diff --git a/engine/src/Libraries/EDMS/Source/physhand.h b/engine/src/Libraries/EDMS/Source/physhand.h new file mode 100644 index 0000000..ccbebc2 --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/physhand.h @@ -0,0 +1,31 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Header: n:/project/lib/src/edms/RCS/physhand.h 1.1 1994/02/28 17:07:42 roadkill Exp $ + */ + +// These are the physics handle routine stuff things!! +// =================================================== + +#ifndef __PHYSHAND_H +#define __PHYSHAND_H + +typedef int32_t physics_handle; + +#endif /* __PHYSHAND_H */ diff --git a/engine/src/Libraries/EDMS/Source/soliton.cc b/engine/src/Libraries/EDMS/Source/soliton.cc new file mode 100644 index 0000000..76f9bca --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/soliton.cc @@ -0,0 +1,731 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Header: n:/project/lib/src/edms/RCS/soliton.cc 1.6 1994/04/20 18:44:56 roadkill Exp $ + */ + +// Soliton.CPP - the core of EDMS, the Emetic Dynamics Modeling System (tm). + +// "Real time stiff equations are such a joy that hey, let's solve whole systems of them +// at once." -Jon Blackley, prior to choking self on mousepad. + +// Soliton and soliton_lite are heavily modified, tuned, and stoked versions of the previous +// solvers, which are based on MASA internal memo #AF34-DE-3030 1991. It has been modified to +// be faster and better suited to stiff and nonlinear problems. Nota Bene: There IS NO +// SOLUTION ORDER. Which means that all system coordinates are treated as quasi independent +// degrees of freedom of the same well posed problem and are thus approximated simultaneously. +// ===================================================================== + +// Who is responsible: +// =================== +// Jon Blackley, Oct. 25, 1991 + +//#include +#include "edms_int.h" //Object types, END conventions, etc. +#include "idof.h" +#include "physhand.h" +#include "lg.h" + +////#include + +// For convenience +#define HashSpew(a) Spewpp(DSRC_EDMS_Hash, a) + +Q S[MAX_OBJ][7][4]; // State stream... Accessable to all... + +extern "C" { +extern void EDMS_kill_object(physics_handle ph); +} + +// ============== +// INTERNAL STUFF +// ============== + +// Why does this get set to 100 and then immediately reset to .02 +// in soliton_lite? - DS + +extern "C" { +Q snooz_threshold = 100; +} + +int32_t EDMS_integrating = 0; + +EDMS_Argblock_Pointer A; // non-vector type arguments for perturbation... + +Q I[MAX_OBJ][DOF_MAX], // Internal degrees of freedom... + k[4][MAX_OBJ][7]; // expansion coefficients... + +void (*idof_functions[MAX_OBJ])(int32_t), // Pointers to the appropriate places... + (*equation_of_motion[MAX_OBJ][7])(int32_t); // The integer is the object number... + +Q *utility_pointer[MAX_OBJ]; // Biped skeletons, Jello translucencies, etc... + +Q hash_scale = 1.0; // The ratio betwixt coordinate and collision... + +// Courtesy of C++ and inline fixpoint and such... +// =============================================== +const Q one_sixth = .1666666666667, // Overboard? + point_five = .5, point_one_two_five = .125, two = 2., point_1 = 0.1, + min_scale_slice = .03; // �����.03 + +// Sleeping... +// ----------- +int32_t no_no_not_me[MAX_OBJ]; +int32_t alarm_clock[MAX_OBJ]; +int32_t industrial_strength[MAX_OBJ]; + +// *******************HACK*HACK*HACK*HACK*****************************+ +// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// uchar EDMS_Seamus_is_an_asshole[12000]; +// *******************HACK*HACK*HACK*HACK*****************************+ +// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +void soliton(Q /*timestep*/) {} + +// Soliton_Lite (tm)... +// ==================== + +int32_t active_objects = 0; + +// Are non-sleeping objects in the middle of doing their integrating thing, +// and thus using A[][][] instead of S[][][]? +bool A_is_active = false; + +// Here is a slower converging, albeit hopefully equally stable integrator, soliton_light, which +// we'll subject to some speed trials. This could be used for certain applications, such as +// out-of scope models or complex aggregate or articulated objects. Well, let's see... +// ==================================================================================== +void soliton_lite(Q timestep) { + extern void robot_idof(int32_t), pelvis_idof(int32_t); + + int32_t object = 0; + int32_t coord = 0; + Q *S_Object; + + Q frequency_check; + Q average_frequency; + int32_t count = 0; + + // Copy the state vector initially into the argument vector... + // =========================================================== + for (object = 0; S[object][0][0] > END; object++) { + if (no_no_not_me[object] == 1) { + S_Object = (Q *)S[object]; + + state_delete_object(object); + for (coord = 0; coord < 7 && S_Object[coord << 2] > END; coord++) { + A[object][coord][0].val = S_Object[coord << 2].val; + A[object][coord][1].val = S_Object[(coord << 2) + 1].val; + } + state_write_object(object); // Here to initialize new models for sure... + } + } + + // Here is the leading (order zero) term... + // ======================================== + count = 0; + average_frequency = 0; + + for (object = 0; S[object][0][0] > END; object++) { + if (no_no_not_me[object] == 1) { + S_Object = (Q *)S[object]; + + // Are we wasting time... + // ---------------------- + frequency_check = 0; + + // mout << "II0\n"; + (*idof_functions[object])(object); + + for (coord = 0; coord < 7 && S_Object[coord << 2] > END; coord++) { + k[0][object][coord].val = fix_mul(timestep.val, S_Object[(coord << 2) + 2].val); + if (abs(S_Object[(coord << 2) + 2]) > .001) { + // This check makes the function discontinuous (it goes from 10000 down + // to 1000 when S[object][coord][2] reaches 100)... - DS + if (abs(S_Object[(coord << 2) + 2]) < 100) + frequency_check.val += fix_mul(S_Object[(coord << 2) + 2].val, S_Object[(coord << 2) + 2].val); + else + frequency_check.val += fix_make(1000, 0); + } + + if (abs(S_Object[(coord << 2) + 1]) > .001) { + if (abs(S_Object[(coord << 2) + 1]) < 50) + frequency_check.val += fix_div( + fix_mul(S_Object[(coord << 2) + 1].val, S_Object[(coord << 2) + 1].val), I[object][31].val); + else + frequency_check.val += fix_make(1000, 0); + } + + // This is angular velocity I think - DS + if ((I[object][IDOF_MODEL] == ROBOT) && (coord == 3)) + frequency_check += abs(50 * S_Object[(coord << 2) + 2]); + } + + // Are you in stiff and in need of invariant imbedding? + // ---------------------------------------------------- + industrial_strength[object] = 0; // Guilty until... + if (frequency_check > 15) { + // mout << "-"; + industrial_strength[object] = 1; + } + + count += 1; + average_frequency += frequency_check; + + // mout << "f: " << frequency_check << "\n"; + + if (frequency_check < snooz_threshold) { + EDMS_sleepy_snoozy(on2ph[object]); + no_no_not_me[object] = 0; + if (I[object][IDOF_MODEL] == PELVIS) + no_no_not_me[object] = 1; // Hack for now... + if (I[object][IDOF_MODEL] == BIPED) + no_no_not_me[object] = 1; + if (I[object][IDOF_MODEL] == D_FRAME) + no_no_not_me[object] = 1; + // if (no_no_not_me[object] == 0) { mout << "!EDMS: sleeping f = " << frequency_check << "\n";} + } + } // End of object sleep test... + } // End of object... + + // Sleeping... + // ----------- + snooz_threshold = .2; + + // Here is a frobbed term... + // ------------------------- + + // mout << "frob...\n"; + + for (object = 0; S[object][0][0] > END; object++) { + if (no_no_not_me[object] == 1) { + S_Object = (Q *)S[object]; + + state_delete_object(object); // Do collisions... + for (coord = 0; coord < 7 && S_Object[(coord << 2) + 0] > END; coord++) { + A[object][coord][0].val += fix_mul(timestep.val, S_Object[(coord << 2) + 1].val); + A[object][coord][1].val += k[0][object][coord].val; + } + write_object(object); // Do collisions... + } + } + + A_is_active = true; // A is where object's real locations are + + // mout << "NextStep...\n"; + + // Now for the more complicated step... + // ==================================== + for (object = 0; S[object][0][0] > END; object++) { + if (no_no_not_me[object] == 1) { + S_Object = (Q *)S[object]; + + // If we've got a hot one, EDMS now becomes industrial strength (note collisions set above)... + // ------------------------------------------------------------------------------------------- + if (industrial_strength[object] == 1) { + // mout << "IT:"; + + // First order... + // -------------- + delete_object(object); + for (coord = 0; coord < 7 && S_Object[(coord << 2) + 0] > END; coord++) { + A[object][coord][0].val = + S_Object[(coord << 2) + 0].val + + fix_mul(fix_mul(point_five.val, timestep.val), S_Object[(coord << 2) + 1].val) + + fix_mul(fix_mul(point_one_two_five.val, timestep.val), k[0][object][coord].val); + A[object][coord][1].val = + S_Object[(coord << 2) + 1].val + fix_mul(point_five.val, k[0][object][coord].val); + } + write_object(object); + + // mout << "II1\n"; + + (*idof_functions[object])(object); + + for (coord = 0; coord < 7 && S_Object[(coord << 2) + 0] > END; coord++) { + k[1][object][coord].val = fix_mul(timestep.val, S_Object[(coord << 2) + 2].val); + } + + // Second order... + // --------------- + delete_object(object); + for (coord = 0; coord < 7 && S_Object[(coord << 2) + 0] > END; coord++) { + A[object][coord][0].val = + S_Object[(coord << 2) + 0].val + + fix_mul(fix_mul(point_five.val, timestep.val), S_Object[(coord << 2) + 1].val) + + fix_mul(fix_mul(point_one_two_five.val, timestep.val), k[1][object][coord].val); + A[object][coord][1].val = + S_Object[(coord << 2) + 1].val + fix_mul(point_five.val, k[1][object][coord].val); + } + write_object(object); + + // mout << "II2\n"; + (*idof_functions[object])(object); + + for (coord = 0; coord < 7 && S_Object[(coord << 2) + 0] > END; coord++) { + k[2][object][coord].val = fix_mul(timestep.val, S_Object[(coord << 2) + 2].val); + } + + // Third order... + // -------------- + delete_object(object); + for (coord = 0; coord < 7 && S_Object[(coord << 2) + 0] > END; coord++) { + // Different convergence requirement from other terms! + A[object][coord][0].val = S_Object[(coord << 2) + 0].val + + fix_mul(timestep.val, S_Object[(coord << 2) + 1].val) + + fix_mul(fix_mul(point_five.val, timestep.val), k[2][object][coord].val); + A[object][coord][1].val = S_Object[(coord << 2) + 1].val + k[2][object][coord].val; + } + write_object(object); + + // mout << "II3\n"; + (*idof_functions[object])(object); + + for (coord = 0; coord < 7 && S_Object[(coord << 2) + 0] > END; coord++) { + k[3][object][coord].val = fix_mul(timestep.val, S_Object[(coord << 2) + 2].val); + } + } // End of stoked... + else { + // Regular strength... + // =================== + (*idof_functions[object])(object); + + for (coord = 0; coord < 7 && S_Object[(coord << 2) + 0] > END; coord++) { + k[1][object][coord].val = fix_mul(timestep.val, S_Object[(coord << 2) + 2].val); + } + } // End of else for regular guy... + + // Does anybody need to wake up? + // ----------------------------- + for (coord = 0; coord < MAX_OBJ && S[coord][0][0] > END; coord++) + if (alarm_clock[coord] != 0) { + // mout << "Alarm2: " << coord << "\n"; + alarm_clock[coord] = 0; + collision_wakeup(coord); + } + } // End of object... + } + + int32_t total = 0; + + // Hey! We're already able to assemble the solution! Wasn't that better than soliton? + // ===-----======------------------------------------------------- + for (object = 0; S[object][0][0] > END; object++) { + if (no_no_not_me[object] == 1) { + total += 1; + Q anus[7]; + + S_Object = (Q *)S[object]; + + // Calculate the multiplier... + // --------------------------- + if (I[object][IDOF_MODEL] == D_FRAME) { + Q lagrange_multiplier = .5 / timestep; + Q l_m; + Q lagrange; + + lagrange.val = (1 << 16) - (fix_mul(S_Object[(3 << 2) + 0].val, S_Object[(3 << 2) + 0].val) + + fix_mul(S_Object[(4 << 2) + 0].val, S_Object[(4 << 2) + 0].val) + + fix_mul(S_Object[(5 << 2) + 0].val, S_Object[(5 << 2) + 0].val) + + fix_mul(S_Object[(6 << 2) + 0].val, S_Object[(6 << 2) + 0].val)); + l_m.val = fix_mul(lagrange_multiplier.val, lagrange.val); + anus[0].val = anus[1].val = anus[2].val = 0; + anus[3].val = fix_mul(S_Object[(3 << 2) + 0].val, l_m.val); + anus[4].val = fix_mul(S_Object[(4 << 2) + 0].val, l_m.val); + anus[5].val = fix_mul(S_Object[(5 << 2) + 0].val, l_m.val); + anus[6].val = fix_mul(S_Object[(6 << 2) + 0].val, l_m.val); + } // End of calculation... + + // Lupe over coordinates... + // ------------------------ + for (coord = 0; coord < 7 && S_Object[(coord << 2) + 0] > END; coord++) { + if (industrial_strength[object] == 1) { + // These guys need more multiplies... + // ---------------------------------- + // mout << "IS:"; + + // Check for Dirac... + // ------------------ + if (I[object][IDOF_MODEL] == D_FRAME) { + // mout << "D"; + + S_Object[(coord << 2) + 0].val += fix_mul( + timestep.val, (S_Object[(coord << 2) + 1].val + anus[coord].val + + fix_mul(one_sixth.val, (k[0][object][coord].val + k[1][object][coord].val + + k[2][object][coord].val)))); + } else { + // mout << "N"; + + S_Object[(coord << 2) + 0].val += fix_mul( + timestep.val, (S_Object[(coord << 2) + 1].val + + fix_mul(one_sixth.val, (k[0][object][coord].val + k[1][object][coord].val + + k[2][object][coord].val)))); + } + + S_Object[(coord << 2) + 1].val += + fix_mul(one_sixth.val, (k[0][object][coord].val + + fix_mul(two.val, (k[1][object][coord].val + k[2][object][coord].val) + + k[3][object][coord].val))); + } else { + // These guys don't... + // =================== + // mout << "RS:"; + + // Use the multiplier... + // --------------------- + if (I[object][IDOF_MODEL] == D_FRAME) { + S_Object[(coord << 2) + 0].val = + S_Object[(coord << 2) + 0].val + + fix_mul(timestep.val, (S_Object[(coord << 2) + 1].val + anus[coord].val)) + + fix_mul(point_five.val, + (fix_mul(fix_mul(timestep.val, timestep.val), k[0][object][coord].val))); + } else { + S_Object[(coord << 2) + 0].val += fix_mul( + timestep.val, (S_Object[(coord << 2) + 1].val + + fix_mul(fix_mul(point_five.val, timestep.val), k[0][object][coord].val))); + } + + S_Object[(coord << 2) + 1].val += + fix_mul(point_five.val, (k[0][object][coord].val + k[1][object][coord].val)); + } // End of else for regular guys... + } + + // Update the collision table... + // ----------------------------- + delete_object(object); + state_write_object(object); + + if (I[object][IDOF_AUTODESTRUCT] < 0) + EDMS_kill_object(on2ph[object]); // You deserve to die! + } + } + A_is_active = false; +} + +// Soliton_Vector... +// ================= + +// Here is soliton_vector, which is a scalable convergence integrator. In concert with the +// other members of the EDMS integration team, soliton_vector can dramatically increase the +// efficiency of the integration step, while remaining very stable. Details about its use +// will follow when it proves useful... +// ==================================== +void soliton_vector(Q timestep) { + // Here i yam... + // ------------- + EDMS_integrating = 1; + + // timestep = .03; + + int32_t count = 0; + + // for (int test = 0; test < 12000; test++) EDMS_Seamus_is_an_asshole[test] = 243643; + + // Stupid scaling for now, for FF prototype which is very SLOW! + // ----------------------------------------------------------- + while (timestep > min_scale_slice) { + count++; + soliton_lite(min_scale_slice); + timestep -= min_scale_slice; + } + + // Now do the rest... + // ================== + if (timestep > .01 || count == 0) + soliton_lite(timestep); + // else mout << "!EDMS: timestep is small, dt = " << timestep << "\n"; + + // mout << count << "\n"; + + // Here i yam... + // ------------- + EDMS_integrating = 0; +} + +// Here is an integrator that fools the models into not colliding. Hopefully not often used. +// It is a version of Soliton Lite(tm)... +// ====================================== +void soliton_lite_holistic(Q /*timestep*/) {} + +// Here is the holistic vector integrator... +// ========================================= +void soliton_vector_holistic(Q /*timestep*/) {} + +// Have some utility routines... +// ============================= + +// Initialize the state stream and do whatever else deems itself to be essential to +// get soliton running... +// ====================== +void EDMS_initialize(EDMS_data *D) { + extern uint32_t data[EDMS_DATA_SIZE][EDMS_DATA_SIZE]; + int32_t object = 0, coord = 0, deriv = 0; + const Q collision_size = EDMS_DATA_SIZE; + + // Set the starting physics_handle... + // ================================== + min_physics_handle = D->min_physics_handle; + + // Point the argument block to the right place... + // ============================================== + A = (EDMS_Argblock_Pointer)D->argblock_pointer; + + // *******************HACK*HACK*HACK*HACK*****************************+ + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // A = (EDMS_Argblock_Pointer)EDMS_Seamus_is_an_asshole; + // *******************HACK*HACK*HACK*HACK*****************************+ + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + // Set the scale of the playfield and zero the collision data... + // ============================================================= + hash_scale.fix_to(D->playfield_size); + hash_scale = collision_size / hash_scale; + + // printf("hash_scale: %f\n", fix_float(hash_scale.to_fix())); + // printf("collision_size: %f\n", fix_float(collision_size.to_fix())); + + for (coord = 0; coord < EDMS_DATA_SIZE; coord++) { + for (deriv = 0; deriv < EDMS_DATA_SIZE; deriv++) { + data[coord][deriv] = 0; + } + } + + // Most important is setting the END markers, the rest is simply anal... + // ===================================================================== + for (object = 0; object < MAX_OBJ; object++) { + for (coord = 0; coord < 7; coord++) { + for (deriv = 0; deriv < 3; deriv++) { + S[object][coord][deriv] = END; // Set solver bounds... + } + } + + for (coord = 0; coord < DOF_MAX; coord++) { + I[object][coord] = END; + } + + I[object][IDOF_MODEL] = VACUUM; // Look, Ma, no object! + I[object][IDOF_AUTODESTRUCT] = 0; // Don't kill... + } + + // Finally, set all the sleepers to waking states... + // ================================================= + for (object = 0; object < MAX_OBJ; object++) { + no_no_not_me[object] = 1; + alarm_clock[object] = 0; + } +} + +// Kill object number deadguy and perform garbage collection. +// ========================================================== +int32_t EDMS_kill(int32_t deadguy) { + int32_t error_code = -1; + int32_t object; + + // First see if there is, in fact, an object there... + // ================================================== + if (S[deadguy][0][0] > END) { + // if (EDMS_integrating == 1) mout << "Deleting " << deadguy << "\n"; + + // Release from any contractual involvements with other objects... + // =============================================================== + reset_collisions(deadguy); + + // Faster, pussycat, kill kill. First perform the garbage collection... + // ===================================================================== + for (object = deadguy + 1; object < MAX_OBJ && S[object][0][0] > END; object++) { + if (A_is_active && no_no_not_me[(object - 1)] == 1) + delete_object(object - 1); + else + state_delete_object(object - 1); // For collisions... + + idof_functions[object - 1] = idof_functions[object]; + + for (int32_t coord = 0; coord < 7; coord++) { + equation_of_motion[object - 1][coord] = equation_of_motion[object][coord]; + for (int32_t deriv = 0; deriv < 3; deriv++) { + S[(object - 1)][coord][deriv] = S[object][coord][deriv]; + if (A_is_active) + A[(object - 1)][coord][deriv] = A[object][coord][deriv]; + } + + k[0][(object - 1)][coord] = k[0][object][coord]; + k[1][(object - 1)][coord] = k[1][object][coord]; + k[2][(object - 1)][coord] = k[2][object][coord]; + k[3][(object - 1)][coord] = k[3][object][coord]; + } + + for (int32_t number = 0; number < DOF_MAX; number++) { // Copy the parameters... + I[(object - 1)][number] = I[object][number]; + } + + // Fix the excluded collision information... + // ========================================= + if (I[(object - 1)][IDOF_COLLIDE] > -1) + I[I[(object - 1)][IDOF_COLLIDE].to_int()][IDOF_COLLIDE] = object - 1; + + // Utility pointers also need fixing... + // ==================================== + utility_pointer[(object - 1)] = utility_pointer[object]; + + // Frequency checks also... + // ======================== + no_no_not_me[(object - 1)] = no_no_not_me[object]; + alarm_clock[(object - 1)] = alarm_clock[object]; + industrial_strength[(object - 1)] = industrial_strength[object]; + + if (A_is_active && no_no_not_me[(object - 1)] == 1) + write_object(object - 1); + else + state_write_object(object - 1); // For collisions... + } + + // Now kill the old last object... + // =============================== + if (A_is_active && no_no_not_me[(object - 1)] == 1) { + delete_object(object - 1); + } else + state_delete_object(object - 1); // For collisions... + + for (int32_t coord = 0; coord < 7; coord++) { + for (int32_t deriv = 0; deriv < 3; deriv++) { + S[(object - 1)][coord][deriv] = END; + } + } + + for (int32_t number = 0; number < DOF_MAX; number++) { // Kill the parameters... + I[(object - 1)][number] = END; + } + + I[(object - 1)][30] = VACUUM; // Nothing there... + I[(object - 1)][38] = 0; // Don't kill... + + // Points nowhere... + // ================= + idof_functions[object - 1] = NULL; + + // Okay, everything has gone okay... + // ================================= + error_code = 0; + + // Frequency checks also... + // ======================== + no_no_not_me[(object - 1)] = 1; + industrial_strength[(object - 1)] = 0; + alarm_clock[(object - 1)] = 0; + } // Validity... + + return error_code; +} + +// Wow. The following is amazing. An amazing following. +// ====================================================== + +// Collision wakeup... +// =================== +void collision_wakeup(int32_t object) { + Q idof_state[DOF_MAX], state[7][4], arg[7][4]; + + int32_t coord = 0, deriv = 0, new_object = 0; + + physics_handle ph; + + Q *utility_save; + void (*idof_function_save)(int); + + extern void inventory_and_statistics(); + + // Save me, save me... + // =================== + for (coord = 0; coord < 7; coord++) { + for (deriv = 0; deriv < 3; deriv++) { + state[coord][deriv] = S[object][coord][deriv]; + if (no_no_not_me[object]) + arg[coord][deriv] = A[object][coord][deriv]; + else + arg[coord][deriv] = S[object][coord][deriv]; + } + } + + for (coord = 0; coord < DOF_MAX; coord++) { + idof_state[coord] = I[object][coord]; + } + utility_save = utility_pointer[object]; + idof_function_save = idof_functions[object]; + + // Now kill the offender... + // ======================== + EDMS_kill(object); + + // Simulate the action of soliton packing... + // ----------------------------------------- + ph = on2ph[object]; + EDMS_release_object(ph); + for (coord = (object + 1); coord < MAX_OBJ; coord++) + EDMS_remap_object_number(coord, (coord - 1)); + + // Where do I store the copy... + // ============================ + while (S[new_object++][0][0] > END) + ; + new_object -= 1; + + // Create the duplicate... + // ======================= + for (coord = 0; coord < 7; coord++) { + for (deriv = 0; deriv < 3; deriv++) { + A[new_object][coord][deriv] = arg[coord][deriv]; + S[new_object][coord][deriv] = state[coord][deriv]; + } + } + + for (coord = 0; coord < DOF_MAX; coord++) { + I[new_object][coord] = idof_state[coord]; + } + + // Utilities... + // ============ + utility_pointer[new_object] = utility_save; + + // Internals... + // ============ + idof_functions[new_object] = idof_function_save; + + // Set the physics handles right... + // ================================ + on2ph[new_object] = ph; + ph2on[ph] = new_object; + + // Collisions... + // ============= + state_write_object(new_object); // This is ALWAYS DURING integration!! + no_no_not_me[new_object] = 1; // Wake up!!! + industrial_strength[new_object] = 1; // Wake up HOT! + + // Fix the excluded collision information... + // ========================================= + if (I[new_object][IDOF_COLLIDE] > -1) + I[I[new_object][IDOF_COLLIDE].to_int()][IDOF_COLLIDE] = new_object; +} + +#pragma require_prototypes off +// This guy is the thing that models without the full six degrees of freedom get as +// their ignorable coordinates. +// ============================ +void null_function(int32_t /*dummy*/) {} +#pragma require_prototypes on diff --git a/engine/src/Libraries/EDMS/Source/ss_flet.h b/engine/src/Libraries/EDMS/Source/ss_flet.h new file mode 100644 index 0000000..eee9819 --- /dev/null +++ b/engine/src/Libraries/EDMS/Source/ss_flet.h @@ -0,0 +1,142 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/cit/src/inc/RCS/ss_flet.h $ + * $Revision: 1.2 $ + * $Author: dc $ + * $Date: 1994/08/24 05:43:55 $ + */ + +#if !defined(SS_FLET_H) +#define SS_FLET_H + +#if defined(__cplusplus) +extern "C" { +#endif // defined(__cplusplus) + +typedef struct { + fix norm[3]; // unit normal + fix att; // attentuation + fix comp; // compression + int flags; // flag field, BCD, Primary, Which type, so on +} ss_facelet_return; + +// globals +#define SS_MAX_FACELETS 16 +extern int32_t ss_edms_bcd_flags; +extern int32_t ss_edms_bcd_param; + +// bitfield values +// 0bEtttttttRRRRCCCC00FF00MM00TT0PPP + +#define SS_BCD_PRIM_SHF 0 +#define SS_BCD_PRIM_MASK 0x7 +#define SS_BCD_AXIS_MASK 0x6 +#define SS_BCD_PRIM_MULTI 0x0 +#define SS_BCD_PRIM_XAXIS 0x2 +#define SS_BCD_PRIM_YAXIS 0x4 +#define SS_BCD_PRIM_ZAXIS 0x6 +#define SS_BCD_PRIM_NEG 0x1 +#define SS_BCD_PRIM_NEG_X 0x3 +#define SS_BCD_PRIM_NEG_Y 0x5 +#define SS_BCD_PRIM_NEG_Z 0x7 + +#define SS_BCD_TYPE_SHF 4 +#define SS_BCD_TYPE_MASK 0x30 +#define SS_BCD_TYPE_ERR 0x00 +#define SS_BCD_TYPE_FLOOR 0x10 +#define SS_BCD_TYPE_WALL 0x20 +#define SS_BCD_TYPE_CEIL 0x30 + +#define SS_BCD_MISC_SHF 8 +#define SS_BCD_MISC_MASK 0xF00 +#define SS_BCD_MISC_CLIMB 0x100 +#define SS_BCD_MISC_STAIR 0x200 + +#define SS_BCD_FRIC_SHF 12 +#define SS_BCD_FRIC_MASK 0x3000 +#define SS_BCD_FRIC_ZERO 0x0000 +#define SS_BCD_FRIC_LOW 0x1000 +#define SS_BCD_FRIC_NORM 0x2000 +#define SS_BCD_FRIC_HIGH 0x3000 + +#define SS_BCD_CURR_SHF 16 +#define SS_BCD_CURR_ON 0xC0000 // mask with this to see if any current +#define SS_BCD_CURR_DIR 0x30000 // this gives the direction +#define SS_BCD_CURR_N 0x00000 +#define SS_BCD_CURR_E 0x10000 +#define SS_BCD_CURR_S 0x20000 +#define SS_BCD_CURR_W 0x30000 +#define SS_BCD_CURR_SPD 0xC0000 // this gives "speed" +#define SS_BCD_CURR_NULL 0x00000 // null is no current +#define SS_BCD_CURR_LOW 0x40000 // low-high as expected +#define SS_BCD_CURR_MID 0x80000 +#define SS_BCD_CURR_HIGH 0xC0000 + +#define SS_BCD_REPUL_SHF 20 +#define SS_BCD_REPUL_ON 0x700000 +#define SS_BCD_REPUL_TYPE 0x700000 +#define SS_BCD_REPUL_NULL 0x000000 +#define SS_BCD_REPUL_UP 0x100000 +#define SS_BCD_REPUL_DOWN 0x200000 +#define SS_BCD_REPUL_N 0x300000 +#define SS_BCD_REPUL_S 0x400000 +#define SS_BCD_REPUL_E 0x500000 +#define SS_BCD_REPUL_W 0x600000 +#define SS_BCD_REPUL_SPD 0x800000 +#define SS_BCD_REPUL_NORM 0x000000 +#define SS_BCD_REPUL_FAST 0x800000 + +#define SS_BCD_EOF (1 << 31) +#define TF_FLG_HPARAM (1 << 31) + +#define TF_FLG_BOX_MASK 0x30000000 +#define TF_FLG_BOX_NONE 0x00000000 +#define TF_FLG_BOX_LR 0x10000000 +#define TF_FLG_BOX_TB 0x20000000 +#define TF_FLG_BOX_FULL 0x30000000 + +#define TF_FLG_ICHK_MASK 0x0C000000 +#define TF_FLG_ICHK_NONE 0x00000000 +#define TF_FLG_ICHK_INT 0x04000000 +#define TF_FLG_ICHK_EDGE 0x08000000 +#define TF_FLG_ICHK_OUT 0x0C000000 + +#define TF_FLG_3PNT_MASK 0x02000000 + +#define TF_FLG_NHINT_MASK 0x01000000 + +#define TF_FLG_ALL_TERR 0x7F000000 + +typedef enum tagTFType { + TFD_FULL, + TFD_RCAST, + TFD_BCD +} TFType; + +typedef enum tagTerrainHit { + HIT_FACELET, + MISS +} TerrainHit; + +#if defined(__cplusplus) +} +#endif // defined(__cplusplus) + +#endif // !defined(SS_FLET_H) diff --git a/engine/src/Libraries/FIX/Source/MakeTables.c b/engine/src/Libraries/FIX/Source/MakeTables.c new file mode 100644 index 0000000..7f5b557 --- /dev/null +++ b/engine/src/Libraries/FIX/Source/MakeTables.c @@ -0,0 +1,145 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include "fix.h" +#include "trigtab.h" + +// First, the sine table. +// The sine table is indexed by the top 8 bits of a fixang. +// Its units are fix >> 2 (so -1 = 0xc000, 0 = 0x0000, 1 = 0x4000) +// This means that the high bit is almost useless, but otherwise +// we get results like sin(PI/2) = 0.fffe rather than 1.0000. +// +// cos[x] = sin[x + 64]. + +// indexed by fixang +// clang-format off +uint16_t sintab[256+64+1] = { + 000000, 0x0192, 0x0324, 0x04b5, 0x0646, 0x07d6, 0x0964, 0x0af1, + 0x0c7c, 0x0e06, 0x0f8d, 0x1112, 0x1294, 0x1413, 0x1590, 0x1709, + 0x187e, 0x19ef, 0x1b5d, 0x1cc6, 0x1e2b, 0x1f8c, 0x20e7, 0x223d, + 0x238e, 0x24da, 0x2620, 0x2760, 0x289a, 0x29ce, 0x2afb, 0x2c21, + 0x2d41, 0x2e5a, 0x2f6c, 0x3076, 0x3179, 0x3274, 0x3368, 0x3453, + 0x3537, 0x3612, 0x36e5, 0x37b0, 0x3871, 0x392b, 0x39db, 0x3a82, + 0x3b21, 0x3bb6, 0x3c42, 0x3cc5, 0x3d3f, 0x3daf, 0x3e15, 0x3e72, + 0x3ec5, 0x3f0f, 0x3f4f, 0x3f85, 0x3fb1, 0x3fd4, 0x3fec, 0x3ffb, + 0x4000, 0x3ffb, 0x3fec, 0x3fd4, 0x3fb1, 0x3f85, 0x3f4f, 0x3f0f, + 0x3ec5, 0x3e72, 0x3e15, 0x3daf, 0x3d3f, 0x3cc5, 0x3c42, 0x3bb6, + 0x3b21, 0x3a82, 0x39db, 0x392b, 0x3871, 0x37b0, 0x36e5, 0x3612, + 0x3537, 0x3453, 0x3368, 0x3274, 0x3179, 0x3076, 0x2f6c, 0x2e5a, + 0x2d41, 0x2c21, 0x2afb, 0x29ce, 0x289a, 0x2760, 0x2620, 0x24da, + 0x238e, 0x223d, 0x20e7, 0x1f8c, 0x1e2b, 0x1cc6, 0x1b5d, 0x19ef, + 0x187e, 0x1709, 0x1590, 0x1413, 0x1294, 0x1112, 0x0f8d, 0x0e06, + 0x0c7c, 0x0af1, 0x0964, 0x07d6, 0x0646, 0x04b5, 0x0324, 0x0192, + 000000, 0xfe6f, 0xfcdd, 0xfb4c, 0xf9bb, 0xf82b, 0xf69d, 0xf510, + 0xf385, 0xf1fb, 0xf074, 0xeeef, 0xed6d, 0xebee, 0xea71, 0xe8f8, + 0xe783, 0xe612, 0xe4a4, 0xe33b, 0xe1d6, 0xe075, 0xdf1a, 0xddc4, + 0xdc73, 0xdb27, 0xd9e1, 0xd8a1, 0xd767, 0xd633, 0xd506, 0xd3e0, + 0xd2c0, 0xd1a7, 0xd095, 0xcf8b, 0xce88, 0xcd8d, 0xcc99, 0xcbae, + 0xcaca, 0xc9ef, 0xc91c, 0xc851, 0xc790, 0xc6d6, 0xc626, 0xc57f, + 0xc4e0, 0xc44b, 0xc3bf, 0xc33c, 0xc2c2, 0xc252, 0xc1ec, 0xc18f, + 0xc13c, 0xc0f2, 0xc0b2, 0xc07c, 0xc050, 0xc02d, 0xc015, 0xc006, + 0xc001, 0xc006, 0xc015, 0xc02d, 0xc050, 0xc07c, 0xc0b2, 0xc0f2, + 0xc13c, 0xc18f, 0xc1ec, 0xc252, 0xc2c2, 0xc33c, 0xc3bf, 0xc44b, + 0xc4e0, 0xc57f, 0xc626, 0xc6d6, 0xc790, 0xc851, 0xc91c, 0xc9ef, + 0xcaca, 0xcbae, 0xcc99, 0xcd8d, 0xce88, 0xcf8b, 0xd095, 0xd1a7, + 0xd2c0, 0xd3e0, 0xd506, 0xd633, 0xd767, 0xd8a1, 0xd9e1, 0xdb27, + 0xdc73, 0xddc4, 0xdf1a, 0xe075, 0xe1d6, 0xe33b, 0xe4a4, 0xe612, + 0xe783, 0xe8f8, 0xea71, 0xebee, 0xed6d, 0xeeef, 0xf074, 0xf1fb, + 0xf385, 0xf510, 0xf69d, 0xf82b, 0xf9bb, 0xfb4c, 0xfcdd, 0xfe6f, + 000000, 0x0192, 0x0324, 0x04b5, 0x0646, 0x07d6, 0x0964, 0x0af1, + 0x0c7c, 0x0e06, 0x0f8d, 0x1112, 0x1294, 0x1413, 0x1590, 0x1709, + 0x187e, 0x19ef, 0x1b5d, 0x1cc6, 0x1e2b, 0x1f8c, 0x20e7, 0x223d, + 0x238e, 0x24da, 0x2620, 0x2760, 0x289a, 0x29ce, 0x2afb, 0x2c21, + 0x2d41, 0x2e5a, 0x2f6c, 0x3076, 0x3179, 0x3274, 0x3368, 0x3453, + 0x3537, 0x3612, 0x36e5, 0x37b0, 0x3871, 0x392b, 0x39db, 0x3a82, + 0x3b21, 0x3bb6, 0x3c42, 0x3cc5, 0x3d3f, 0x3daf, 0x3e15, 0x3e72, + 0x3ec5, 0x3f0f, 0x3f4f, 0x3f85, 0x3fb1, 0x3fd4, 0x3fec, 0x3ffb, + 0x4000 +}; +// clang-format on + +// Now the arcsin table. +// The arcsin table is indexed by (((fix >> 2) + 0x4000) & 0xffff). +// That means -1 = 0xc000 + 0x4000 = 0x0000, +// 0 = 0x0000 + 0x4000 = 0x4000, +// 1 = 0x4000 + 0x4000 = 0x8000. +// So the high bit is almost useless, but otherwise we have problems +// trying to differentiate between 1 and -1. +// Its units are fixangs. +// +// acos(x) = PI/2 - asin(x). (PI/2 is fixang 0x4000) +// Note that there are 130 entries in the table. The asin and acos functions +// will use the low 8 bits to interpolate between entry i and entry i+1: if the +// parameter is exactly 1, this will be asintab[128] and asintab[129]. + +// indexed by (high 8 bits of (fix >> 2 + 0x4000) +// clang-format off +fixang asintab[128+1+1] = { + 0xc001, 0xc737, 0xca37, 0xcc87, 0xce7c, 0xd037, 0xd1ca, 0xd33d, + 0xd498, 0xd5e0, 0xd716, 0xd840, 0xd95d, 0xda6f, 0xdb78, 0xdc7a, + 0xdd73, 0xde67, 0xdf54, 0xe03c, 0xe11e, 0xe1fd, 0xe2d7, 0xe3ad, + 0xe47f, 0xe54e, 0xe61a, 0xe6e3, 0xe7aa, 0xe86e, 0xe92f, 0xe9ee, + 0xeaac, 0xeb67, 0xec20, 0xecd8, 0xed8e, 0xee42, 0xeef5, 0xefa7, + 0xf058, 0xf107, 0xf1b5, 0xf262, 0xf30e, 0xf3b9, 0xf463, 0xf50d, + 0xf5b5, 0xf65d, 0xf705, 0xf7ab, 0xf852, 0xf8f7, 0xf99d, 0xfa41, + 0xfae6, 0xfb8a, 0xfc2e, 0xfcd1, 0xfd75, 0xfe18, 0xfebb, 0xff5e, + 000000, 0x00a3, 0x0146, 0x01e9, 0x028c, 0x0330, 0x03d3, 0x0477, + 0x051b, 0x05c0, 0x0664, 0x070a, 0x07af, 0x0856, 0x08fc, 0x09a4, + 0x0a4c, 0x0af4, 0x0b9e, 0x0c48, 0x0cf3, 0x0d9f, 0x0e4c, 0x0efa, + 0x0fa9, 0x105a, 0x110c, 0x11bf, 0x1273, 0x1329, 0x13e1, 0x149a, + 0x1555, 0x1613, 0x16d2, 0x1793, 0x1857, 0x191e, 0x19e7, 0x1ab3, + 0x1b82, 0x1c54, 0x1d2a, 0x1e04, 0x1ee3, 0x1fc5, 0x20ad, 0x219a, + 0x228e, 0x2387, 0x2489, 0x2592, 0x26a4, 0x27c1, 0x28eb, 0x2a21, + 0x2b69, 0x2cc4, 0x2e37, 0x2fca, 0x3185, 0x337a, 0x35ca, 0x38ca, + 0x4000, 0x4000 +}; +// clang-format on + +// There are two exp tables. The first is for integer exponents. +// The table only goes from -11 to 11 because that's all that will +// fit in a 16:16 fixed point number. Add INTEGER_EXP_OFFSET to +// your exponent before looking it up in the table. + +#define INTEGER_EXP_OFFSET 11 + +// clang-format off +uint32_t expinttab[INTEGER_EXP_OFFSET*2+1] = { + 0x00000001, 0x00000003, 0x00000008, 0x00000016, + 0x0000003c, 0x000000a2, 0x000001ba, 0x000004b0, + 0x00000cbf, 0x000022a5, 0x00005e2d, 0x00010000, + 0x0002b7e1, 0x00076399, 0x001415e6, 0x00369920, + 0x009469c5, 0x01936dc5, 0x0448a217, 0x0ba4f53f, + 0x1fa7157c, 0x560a773e, 0xe9e22447, +}; +// clang-format on + +// Now for the fractional table, which currently has 16+1 values, +// which should be interpolated between. We can crank up the +// accuracy later if we need it. So this input to this table goes +// from 0 to 1 by sixteenths. + +// clang-format off +uint32_t expfractab[16+1] = { + 0x00010000, 0x00011083, 0x00012216, 0x000134cc, + 0x000148b6, 0x00015de9, 0x0001747a, 0x00018c80, + 0x0001a613, 0x0001c14b, 0x0001de45, 0x0001fd1e, + 0x00021df4, 0x000240e8, 0x0002661d, 0x00028db8, + 0x0002b7e1, +}; +// clang-format on diff --git a/engine/src/Libraries/FIX/Source/f_exp.c b/engine/src/Libraries/FIX/Source/f_exp.c new file mode 100644 index 0000000..c09af20 --- /dev/null +++ b/engine/src/Libraries/FIX/Source/f_exp.c @@ -0,0 +1,62 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* +** f_exp.c +** +** fix_exp +** +** $Header: r:/prj/lib/src/fix/RCS/f_exp.c 1.1 1994/08/11 12:12:16 dfan Exp $ +** $Log: f_exp.c $ +* Revision 1.1 1994/08/11 12:12:16 dfan +* Initial revision +* +*/ + +#include "fix.h" +#include "trigtab.h" + +////////////////////////////// +// +// returns e to the x +// does no range checking whatsoever +// +fix fix_exp(fix x) { + int32_t int_part = fix_int(x); + fix exp_int_part; + int32_t basex, fracx; + fix loy, hiy; + fix exp_frac_part; + + // If our exponent is so small that it goes off the small end of the table, + // just return 0. + + if (int_part + INTEGER_EXP_OFFSET < 0) + return 0; + + exp_int_part = expinttab[int_part + INTEGER_EXP_OFFSET]; + + basex = fix_frac(x) >> 12; + fracx = x & 0x0fff; + + loy = expfractab[basex]; + hiy = expfractab[basex + 1]; + + exp_frac_part = loy + (hiy - loy) * fracx / 0x1000; + return (fix_mul(exp_int_part, exp_frac_part)); +} diff --git a/engine/src/Libraries/FIX/Source/fix.c b/engine/src/Libraries/FIX/Source/fix.c new file mode 100644 index 0000000..b353c59 --- /dev/null +++ b/engine/src/Libraries/FIX/Source/fix.c @@ -0,0 +1,501 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +/* +** fix.c +** +** $Header: r:/prj/lib/src/fix/RCS/fix.c 1.21 1994/08/11 12:11:09 dfan Exp $ +** $Log: fix.c $ +* Revision 1.21 1994/08/11 12:11:09 dfan +* move some stuff out +* +* Revision 1.20 1994/03/16 10:29:08 dfan +* fix_safe_pyth_dist etc. +* +* Revision 1.19 1994/03/01 11:28:44 dfan +* Do it for fix24's too +* +* Revision 1.18 1994/03/01 11:27:58 dfan +* Don't need quadrant/placing code in fix_atan, and besides, +* it's buggy +* +* Revision 1.17 1994/02/16 17:33:47 dfan +* allow arbitrarily small arguments to fix_exp +* +* Revision 1.16 1993/11/30 13:02:42 dfan +* safe fix_mul +* +* Revision 1.15 1993/11/04 11:06:09 rex +* Moved fix_sprintf() stuff out into fixsprnt.c +* +* Revision 1.14 1993/10/20 13:08:22 dfan +* some more fast_pyth_dists +* +* Revision 1.13 1993/09/17 13:03:30 dfan +* fast_pyth_dist +* +* Revision 1.12 1993/07/30 12:42:55 dfan +* fix_exp +* +* Revision 1.11 1993/06/27 03:09:43 dc +* pretty up hexprint formatting, sorry, whoops. etc. +* +* Revision 1.10 1993/06/27 02:30:24 dc +* fix_sprint now returns the buffer string, also added fix_sprint_hex +* +* Revision 1.9 1993/06/01 14:14:43 dfan +* Include trigtab.h, generated by fmaketab +* Lose a bit of precision, but now all trig functions work everywhere +* +* Revision 1.8 1993/04/19 13:31:33 dfan +* individual sin & cos functions +* +* Revision 1.7 1993/03/17 12:46:45 matt +* Removed 'const' from sincos declaration so could be ref'd in fix_asm.asm +* +* Revision 1.6 1993/03/02 10:51:30 dfan +* atan2: comparisons of sin should have been unsigned, not signed +* +* Revision 1.5 1993/02/15 12:15:21 dfan +* more fix24 functions +* +* Revision 1.4 1993/02/15 11:39:57 dfan +* fix24 support, not for all fuctions though +* +* Revision 1.3 1993/01/29 16:37:22 dfan +* maybe we shouldn't printf an error message in a library, what say +* +* Revision 1.2 1993/01/29 11:10:06 dfan +* Changed fix_pyth_dist to be straightforward (and twice as fast) +* The arctrig functions worked all along +* +* Revision 1.1 1993/01/22 09:57:39 dfan +* Initial revision +* +*/ + +#include "fix.h" +#include "lg.h" +#include "trigtab.h" +#include +#include + +fix fix_mul_3_3_3(fix a, fix b) { return (fix)(((int64_t)(a) * (int64_t)(b)) >> 29); } + +fix fix_mul_3_32_16(fix a, fix b) { return (fix)(((int64_t)(a) * (int64_t)(b)) >> 13); } + +fix fix_mul_3_16_20(fix a, fix b) { return (fix)(((int64_t)(a) * (int64_t)(b)) >> 33); } + +fix fix_mul_16_32_20(fix a, fix b) { return (fix)(((int64_t)(a) * (int64_t)(b)) >> 4); } + +fix fix_div_16_16_3(fix a, fix b) { return (fix)(((int64_t)a << 29) / (int64_t)b); } + +int gOVResult; + +fix fix_mul(fix a, fix b) { return (fix)(((int64_t)(a) * (int64_t)(b)) >> 16); } + +fix fast_fix_mul_int(fix a, fix b) { return (fix)(((int64_t)(a) * (int64_t)(b)) >> 32); } + +fix fix_mul_asm_safe(fix a, fix b) { + int64_t intermediate = (int64_t)(a) * (int64_t)(b); + // Apparently PowerPC and Motorola 68000 codes differ here + // PowerPC will check if the lower 32 bits of the intemerdiate result are + // equal to -1 (cmpi 1,r6,-1), while 68K will check if the lower *16* bits of + // the intermediate result are equal to -1 (cmp.w #-1,d2). The result should + // be the same because bits 31:16 of the intermediate result already are + // tested for being equal to -1, but I'm leaving this comment here anyway for + // future reference + // int16_t d2 = intermediate & 0xFFFF; // 68K code + int32_t d2 = intermediate & 0xFFFFFFFF; // PPC code + fix result = (fix)(intermediate >> 16); + if (result == -1 && d2 != -1) { + return 0; + } + return result; +} + +// fix fix_div(fix a, fix b) +fix fix_div(fix a, fix b) { + if (b == 0) { + gOVResult = 2; + fix r = 0x7FFFFFFF; + if (a >= 0) { + return r; + } + return -r; + } + gOVResult = 0; + int64_t r64 = ((int64_t)(a) << 16) / (int64_t)(b); + int32_t r32 = (int32_t)(r64 & 0xFFFFFFFF); + if (r64 != (int64_t)r32) { + gOVResult = 1; + fix r = 0x7FFFFFFF; + if (a >= 0) { + return r; + } + return -r; + } + return r32; +} + +// fix fix_div_int(fix a, fix b) +//{ +// return (fix)(((int64_t)(a) << 16) / (int64_t)(b)); +//} +fix fix_div_int(fix a, fix b) { + int64_t r64 = ((int64_t)(a) << 16) / (int64_t)(b); + int32_t r32 = (int32_t)((r64 >> 16) & 0xFFFFFFFF); + return r32; +} + +// fix fix_div_safe_cint(fix a, fix b) +//{ +// return (fix)(((int64_t)(a) << 16) / (int64_t)(b)); +//} +fix fix_div_safe_cint(fix a, fix b) { + int64_t r64 = ((int64_t)(a) << 16) / (int64_t)(b); + int32_t r32 = (int32_t)((r64 >> 16) & 0xFFFFFFFF); + if ((r64 & 0xFFFF) != 0) { + return r32 + 1; + } + return r32; +} + +//---------------------------------------------------------------------------- +// fix_div: Divide two fixed numbers. +//---------------------------------------------------------------------------- + +fix fix_mul_div(fix m0, fix m1, fix d) { + // return (fix)(((int64_t)(m0) * (int64_t)(m1)) / (int64_t)(d)); + int64_t mr = ((int64_t)(m0) * (int64_t)(m1)); + if (d == 0) { + gOVResult = 2; + fix r = 0x7FFFFFFF; + if (mr >= 0) { + return r; + } + return -r; + } + gOVResult = 0; + int64_t r64 = mr / (int64_t)(d); + int32_t r32 = (int32_t)(r64 & 0xFFFFFFFF); + if (r64 != (int64_t)r32) { + gOVResult = 1; + fix r = 0x7FFFFFFF; + if (mr >= 0) { + return r; + } + return -r; + } + return r32; +} + +//---------------------------------------------------------------------------- +// Returns the distance from (0,0) to (a,b) +//---------------------------------------------------------------------------- +fix fix_pyth_dist(fix a, fix b) { + gOVResult = 100; + + // @@@should check for overflow! + return fix_sqrt(fix_mul(a, a) + fix_mul(b, b)); +} + +//---------------------------------------------------------------------------- +// Returns an approximation to the distance from (0,0) to (a,b) +//---------------------------------------------------------------------------- +fix fix_fast_pyth_dist(fix a, fix b) { + if (a < 0) + a = -a; + if (b < 0) + b = -b; + if (a > b) + return (a + b / 2); + else + return (b + a / 2); +} + +//---------------------------------------------------------------------------- +// We can use the fix function because the difference in scale doesn't matter. +//---------------------------------------------------------------------------- +int long_fast_pyth_dist(int a, int b) { return (fix_fast_pyth_dist(a, b)); } + +//---------------------------------------------------------------------------- +// This function is safer than the other fix_pyth_dist because we don't +// have to worry about overflow. +// +// Uses algorithm from METAFONT involving reflecting (a,b) through +// line from (0,0) to (a,b/2), which keeps a^2+b^2 invariant but +// greatly reduces b. When b reaches 0, a is the distance. +// +// Knuth credits it to Moler & Morrison, IBM Journal of Research and +// Development 27 (1983). Good for them. +//---------------------------------------------------------------------------- +fix fix_safe_pyth_dist(fix a, fix b) { + fix tmp; + + a = abs(a); + b = abs(b); // works fine since they're really longs + if (a < b) { + tmp = a; + a = b; + b = tmp; + } // now 0 <= b <= a + if (a > 0) { + if (a > 0x2fffffff) { + // ssWarning (("Overflow in + // fix_safe_pyth_dist\n")); DebugStr("\pOverflow in fix_safe_pyth_dist"); + DEBUG("%s: Overflow in fix_safe_pyth_dist", __FUNCTION__); + return 0; + } + for (;;) { + // This is a quick way of doing the reflection + tmp = fix_div(b, a); + tmp = fix_mul(tmp, tmp); + if (tmp == 0) + break; + tmp = fix_div(tmp, tmp + fix_make(4, 0)); + a += fix_mul(2 * a, tmp); + b = fix_mul(b, tmp); + } + } + return a; +} + +//---------------------------------------------------------------------------- +// Computes sin and cos of theta +//---------------------------------------------------------------------------- +void fix_sincos(fixang theta, fix *sin, fix *cos) { + uint8_t baseth, fracth; // high and low bytes of the + uint16_t lowsin, lowcos, hisin, hicos; // table lookups + + // divide the angle into high and low bytes + // we will do a table lookup with the high byte and + // interpolate with the low byte + baseth = (uint8_t)(theta >> 8); + fracth = (uint8_t)(theta & 0xff); + + // use the identity [cos x = sin (x + PI/2)] to look up + // cosines in the sine table + lowsin = sintab[baseth]; + hisin = sintab[baseth + 1]; + lowcos = sintab[baseth + 64]; + hicos = sintab[baseth + 65]; + + // interpolate between low___ and hi___ according to fracth + *sin = ((int16_t)(lowsin + ((((int16_t)hisin - (int16_t)lowsin) * fracth) >> 8))) << 2; + *cos = ((int16_t)(lowcos + ((((int16_t)hicos - (int16_t)lowcos) * fracth) >> 8))) << 2; + + return; +} + +//---------------------------------------------------------------------------- +// Computes sin of theta +//---------------------------------------------------------------------------- +fix fix_sin(fixang theta) { + uint8_t baseth, fracth; + uint16_t lowsin, hisin; + + baseth = (uint8_t)(theta >> 8); + fracth = (uint8_t)(theta & 0xff); + lowsin = sintab[baseth]; + hisin = sintab[baseth + 1]; + return ((int16_t)(lowsin + ((((int16_t)hisin - (int16_t)lowsin) * fracth) >> 8))) << 2; +} + +//---------------------------------------------------------------------------- +// Computes cos of theta +//---------------------------------------------------------------------------- +fix fix_cos(fixang theta) { + uint8_t baseth, fracth; + uint16_t lowcos, hicos; + + baseth = (uint8_t)(theta >> 8); + fracth = (uint8_t)(theta & 0xff); + lowcos = sintab[baseth + 64]; + hicos = sintab[baseth + 65]; + return ((int16_t)(lowcos + ((((int16_t)hicos - (int16_t)lowcos) * fracth) >> 8))) << 2; +} + +//---------------------------------------------------------------------------- +// Computes sin and cos of theta +// Faster than fix_sincos() but not as accurate (does not interpolate) +//---------------------------------------------------------------------------- +void fix_fastsincos(fixang theta, fix *sin, fix *cos) { + // use the identity [cos x = sin (x + PI/2)] to look up + // cosines in the sine table + *sin = (((int16_t)(sintab[theta >> 8])) << 2); + *cos = (((int16_t)(sintab[(theta >> 8) + 64])) << 2); + + return; +} + +//---------------------------------------------------------------------------- +// Fast sin of theta +//---------------------------------------------------------------------------- +fix fix_fastsin(fixang theta) { return (((int16_t)(sintab[theta >> 8])) << 2); } + +//---------------------------------------------------------------------------- +// Fast cos of theta +//---------------------------------------------------------------------------- +fix fix_fastcos(fixang theta) { return (((int16_t)(sintab[(theta >> 8) + 64])) << 2); } + +//---------------------------------------------------------------------------- +// Computes the arcsin of x +// Assumes -1 <= x <= 1 +// Returns 0xc000..0x4000 (-PI/2..PI/2) +//---------------------------------------------------------------------------- +fixang fix_asin(fix x) { + uint8_t basex, fracx; // high and low bytes of x + fixang lowy, hiy; // table lookups + + // divide x into high and low bytes + // lookup with the high byte, interpolate with the low + // We shift basex around to make it continuous; see trigtab.h + + basex = (uint8_t)(((x >> 2) >> 8) + 0x40); + fracx = (uint8_t)((x >> 2) & 0xff); + + lowy = asintab[basex]; + hiy = asintab[basex + 1]; + + // interpolate between lowy and hiy according to fracx + return (lowy + ((((int16_t)hiy - (int16_t)lowy) * fracx) >> 8)); +} + +//---------------------------------------------------------------------------- +// Computes the arccos of x +// Returns 0x0000..0x8000 (0..PI) +//---------------------------------------------------------------------------- +fixang fix_acos(fix x) { + uint8_t basex, fracx; + uint16_t lowy, hiy; + fixang asin_answer; + + // acos(x) = PI/2 - asin(x) + + basex = (uint8_t)(((x >> 2) >> 8) + 0x40); + fracx = (uint8_t)((x >> 2) & 0xff); + + lowy = asintab[basex]; + hiy = asintab[basex + 1]; + + asin_answer = (lowy + ((((int16_t)hiy - (int16_t)lowy) * fracx) >> 8)); + return ((fixang)0x4000 - asin_answer); +} + +//---------------------------------------------------------------------------- +// Computes the atan of y/x, in the correct quadrant and everything +//---------------------------------------------------------------------------- +fixang fix_atan2(fix y, fix x) { + fix hyp; // hypotenuse + fix s, c; // sine, cosine + fixang th; // our answer + + // Get special cases out of the way so we don't have to deal + // with things like making sure 1 gets converted to 0x7fff and + // not 0x8000. Note that we grab the y = x = 0 case here + if (y == 0) { + if (x >= 0) + return 0x0000; + else + return 0x8000; + } else if (x == 0) { + if (y >= 0) + return 0x4000; + else + return 0xc000; + } + + if ((hyp = fix_pyth_dist(x, y)) == 0) { + // printf ("hey, dist was 0\n"); + + return 0; + } + + // Use fix_asin or fix_acos depending on where we are. We don't want to use + // fix_asin if the sin is close to 1 or -1 + s = fix_div(y, hyp); + if ((uint32_t)s < 0x00004000 || (uint32_t)s > 0xffffc000) { // range is good, use asin + th = fix_asin(s); + if (x < 0) { + if (th < 0x4000) + th = 0x8000 - th; + else + th = ~th + 0x8000; // that is, 0xffff - th + 0x8000 + } + } else { // use acos instead + c = fix_div(x, hyp); + th = fix_acos(c); + if (y < 0) { + th = ~th; // that is, 0xffff - th + } + } + + // The above (x < 0) and (y < 0) conditionals should take care of placing us + // in the correct quadrant, so we shouldn't need the code below. + // Additionally, the code below can cause rounding errors when (th & 0x3fff + // == 0). So let's try omitting it. + +#ifdef NO_NEED + // set high bits based on what quadrant we are in + th &= 0x3fff; + th |= (y > 0 ? (x > 0 ? 0x0000 : 0x4000) : (x > 0 ? 0xc000 : 0x8000)); +#endif + + return th; +} + +fix24 fix24_mul(fix24 a, fix24 b) { return (fix24)(((int64_t)a * (int64_t)b) >> 8); } + +fix24 fix24_div(fix24 a, fix24 b) { return (fix24)(((int64_t)a << 8) / (int64_t)b); } + +fix fix_pow(fix x, fix y) { + int i; + fix ans; + fix rh, rl; + uint16_t yh, yl; + + ans = FIX_UNIT; + yh = (uint16_t)(fix_int(y)); + yl = fix_frac(y); + rh = rl = x; + + // calculate hi part, leave when done + for (i = 0; i < 16; ++i) { + if (yh & 1) + ans = fix_mul(ans, rh); + if (yh != 0) + rh = fix_mul(rh, rh); + yh = yh >> 1; + if (yl != 0) + rl = fix_sqrt(rl); + if (yl & 0x8000) + ans = fix_mul(ans, rl); + yl = yl << 1; + } + return ans; +} + +int32_t fix64_div(int64_t a, int32_t b) { + return (int32_t) (a / b); +} + +int64_t fix64_mul(int32_t a, int32_t b) { + return (int64_t)a * (int64_t)b; +} diff --git a/engine/src/Libraries/FIX/Source/fix.h b/engine/src/Libraries/FIX/Source/fix.h new file mode 100644 index 0000000..f734f41 --- /dev/null +++ b/engine/src/Libraries/FIX/Source/fix.h @@ -0,0 +1,444 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/fix/RCS/fix.h $ + * $Revision: 1.41 $ + * $Author: jaemz $ + * $Date: 1994/08/18 18:10:21 $ + * + * Code, prototypes and types for fixed-point routines. + * + * $Log: fix.h $ + * Revision 1.41 1994/08/18 18:10:21 jaemz + * Added sloppy_sqrt + * + * Revision 1.40 1994/08/11 12:11:14 dfan + * multiple source directories + * + * Revision 1.39 1994/03/16 10:29:25 lmfeeney + * , + * added extern fn fix_pow + * + * Revision 1.38 1994/03/16 10:29:25 dfan + * fix_safe_pyth_dist etc. + * also conversions to and from degrees + * + * Revision 1.37 1994/01/22 19:05:46 dc + * fast_fix_mul_int + * + * Revision 1.36 1994/01/22 19:04:22 dc + * shrd not shr\shl\or for fix_mul + * neat + * + * Revision 1.34 1993/11/11 13:50:46 rex + * Changed fix_from_float() to much simpler macro, added atofix() and atofix24() + * + * Revision 1.33 1993/11/05 13:56:16 dfan + * 2pi was wrong. + * + * Revision 1.32 1993/11/04 11:13:42 dfan + * long_fast_pyth_dist + * + * Revision 1.31 1993/09/17 13:03:36 dfan + * fast_pyth_dist + * + * Revision 1.30 1993/08/17 15:10:04 kaboom + * Added fix_cint and fix_fint macros. + * + * Revision 1.29 1993/07/30 12:42:58 dfan + * fix_exp + * + * Revision 1.28 1993/07/07 12:26:58 xemu + * fixed a numerical error in fixang_to_fixrad + * + * Revision 1.27 1993/07/02 15:10:01 xemu + * conversion from fixed-point radians to fixangs + * + * Revision 1.26 1993/06/27 02:30:47 dc + * added char return to fix_sprint prototypes, added fix_sprint_hex prototypes + * + * Revision 1.25 1993/06/07 10:29:36 jak + * Reversed #pragma and C decls for some functions + * so that C++ parser will be happy. + * + * Revision 1.24 1993/06/05 07:38:36 mahk + * Added abs, sgn, FIXANG_PI + * + * Revision 1.23 1993/04/19 13:31:42 dfan + * individual sin & cos functions + * + * Revision 1.22 1993/04/14 17:13:23 jaemz + * Added FIX_MIN and FIX_MAX + * + * Revision 1.21 1993/04/14 16:57:23 jaemz + * Added floor and ceil + * + * Revision 1.20 1993/04/07 10:15:06 matt + * Removed include of math.h, which seemed unneccesary. + * + * Revision 1.19 1993/03/15 16:55:45 matt + * Added include of "types.h" + * + * Revision 1.18 1993/03/03 14:46:59 dfan + * fix_from_float: short should have been ushort to prevent nasty sign-extend + * + * Revision 1.17 1993/03/03 11:50:53 dfan + * float conversion + * + * Revision 1.16 1993/02/16 10:44:33 matt + * Added parens around macro args + * + * + * Revision 1.15 1993/02/15 12:15:29 dfan + * more fix24 functions + * + * Revision 1.14 1993/02/15 11:40:22 dfan + * fix24 support + * + * Revision 1.13 1993/02/04 16:25:33 matt + * Added new fix_mul_div() function + * + * Revision 1.12 1993/01/29 11:10:45 dfan + * hey, fix_sqrt returns a 32-bit value + * + * Revision 1.11 1993/01/29 10:42:34 dfan + * type in fix_sqrt pragma + * + * Revision 1.10 1993/01/27 16:47:16 dfan + * sqrt functions + * by the way, the arctrig functions did work after all + * + * Revision 1.9 1993/01/22 15:52:36 dfan + * Asin, acos, and atan2 do work after all + * + * Revision 1.8 1993/01/22 09:57:19 dfan + * Added lots of functions, including trig ones + * Arctrig doesn't work yet + * + * Revision 1.7 1992/10/14 23:37:46 kaboom + * Added fix_rint macro to round & take integer part. + * + * Revision 1.6 1992/10/13 22:34:24 kaboom + * Added #ifdef __FIX_H clause around body of file. + * + * Revision 1.5 1992/09/16 19:52:06 kaboom + * Modified the fix_int macro not to round off before converting fix to int. + * Added fix_trunc to remove fractional part, fix_frac to return fractional + * part. + * + * Revision 1.4 1992/09/16 19:28:26 kaboom + * Changed fix_mul and fix_div to be inline assembly-language functions + * instead of called functions. That makes this the only file in the + * fixed-point math library and fix.asm is obsolete. + * + * Revision 1.3 1992/09/15 14:08:22 kaboom + * Made typedef for fixed point variables. Added fix_make macro. + * + * Revision 1.2 1992/08/24 17:27:17 kaboom + * Added RCS keywords and header at top of file. + * + * Revision 1.1 1992/08/24 17:27:17 kaboom + * Initial revision. + */ + +#ifndef __FIX_H +#define __FIX_H + +#include +#include +#include + +#include "lg_types.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +// Globals +extern int gOVResult; + +/////////////////////////////// +// +// First some math functions that don't use fixes. +// +/* @@@ Change this +// Returns 0 if x < 0 +#pragma aux long_sqrt parm [eax] value [ax] modify [eax ebx ecx edx esi edi] +*/ +int long_sqrt(int x); + +////////////////////////////// +// +// fix.c +// + +int long_fast_pyth_dist(int a, int b); + +//======================================== +// +// Now for fixes themselves. Macros. +// +//======================================== + +/* these functions operate on fixed-point numbers with one bit of sign, 15 + bits of integer, and 16 bits of fraction. thus, a rational number a is + represented as a 32-bit number as a*2^16. */ + +typedef int32_t fix; +typedef fix fix16; + +// define min and max +#define FIX_MAX (0x7fffffff) +#define FIX_MIN (0x80000000) + +// A fixang (fixed-point angle) can be converted to radians by multiplying +// by 2 * PI and dividing by 2^16. +// +// 0x0000 -> 0 +// 0x4000 -> PI/2 +// 0x8000 -> PI +// 0xc000 -> 3PI/2 +// + +#define FIXANG_PI 0x8000 +#define fix_2pi fix_make(6, 18559) // that's 6 + 18559/65536 = 6.28319 + +typedef uint16_t fixang; + +/* makes a fixed point number with integral part a and fractional part b. */ +#define fix_make(a, b) (int32_t)((((uint32_t)(a)) << 16) | (b)) + +#define FIX_UNIT fix_make(1, 0) + +/* lops off the fractional part of a fixed point number. */ +#define fix_trunc(n) ((n)&0xffff0000) + +/* Does a floor */ +#define fix_floor(n) ((n)&0xffff0000) + +/* Does a ceil */ +#define fix_ceil(n) (((n) + 65535) & 0xffff0000) + +/* round a fix to the nearest integer, leaving in fix format. */ +#define fix_round(n) (((n) + 32768) & 0xffff0000) + +/* returns the integral part of a fixed point number. */ +#define fix_int(n) ((int16_t)((n) >> 16)) + +// Absolute value and signum +#define fix_abs(n) (((n) < 0) ? -(n) : (n)) +#define fix_sgn(n) (((n) < 0) ? -FIX_UNIT : (((n) == 0) ? 0 : FIX_UNIT)) + +/* converts the floor of n to an integer. */ +#define fix_fint(n) ((n) >> 16) + +/* converts the ceiling of n to an integer. */ +#define fix_cint(n) (((n) + 0xffff) >> 16) + +/* returns the integral part of a fixed point number rounded up. */ +// #define fix_rint(n) (fix_int (fix_round (n))) +// the following macro does it all explictly to avoid the spurious & in +// fix_round +#define fix_rint(n) (((n) + 0x8000) >> 16) + +/* returns the fractional part of a fixed point number. */ +#define fix_frac(n) ((uint16_t)((n)&0xffff)) + +// fixrad_to_fixang converts a fixed-point in radians to a fixang +// fixang_to_fixrad converts a fixang to a fixed point radians +// degrees_to_fixang converts an integer number of degrees to a fixang +// fixang_to_degrees converts a fixang to an integer number of degrees + +#define fixrad_to_fixang(fixradian) (fix_frac(fix_div((fixradian), fix_2pi))) +#define fixang_to_fixrad(ang) fix_div(fix_mul(ang, fix_2pi), 0x10000) +#define degrees_to_fixang(d) ((fixang)(((d)*FIXANG_PI) / 180)) +#define fixang_to_degrees(ang) (((int)(ang)*180) / FIXANG_PI) + +// turns a fixed point into a float. +#define fix_float(n) ((float)(fix_int(n)) + (float)(fix_frac(n)) / 65536.0) + +// makes a fixed point from a float. +#define fix_from_float(n) ((fix)(65536.0 * (n))) + +//======================================== +// +// Multiplication and division. +// +//======================================== + +// For Mac version: The PowerPC version uses two assembly language routines +// to do the multiply and divide. +fix fix_mul(fix a, fix b); +fix fix_mul_asm_safe(fix a, fix b); +fix fix_div(fix a, fix b); +fix fix_div_int(fix a, fix b); +fix fix_div_safe_cint(fix a, fix b); +fix fix_mul_div(fix m0, fix m1, fix d); +fix fast_fix_mul_int(fix a, fix b); +#define fast_fix_mul fix_mul + +//======================================== +// +// Square rooty kind of stuff. +// +//======================================== + +// Returns sqrt (a^2 + b^2) +fix fix_pyth_dist(fix a, fix b); + +// Returns approximately sqrt (a^2 + b^2) +// Is never off by more than 12% (it's worst at 45 deg) +fix fix_fast_pyth_dist(fix a, fix b); + +// pyth_dist with less fear of overflow. Either number +// can be up to 0x2fffffff. +fix fix_safe_pyth_dist(fix a, fix b); + +// Now in FIX_SQRT.C +// Returns 0 if x < 0 +fix fix_sqrt(fix x); + +int32_t quad_sqrt(int32_t hi, uint32_t lo); + +//======================================== +// +// Trigonometric functions. +// +//======================================== + +// Computes sin and cos of theta +void fix_sincos(fixang theta, fix *sin, fix *cos); + +fix fix_sin(fixang theta); + +fix fix_cos(fixang theta); + +// Computes sin and cos of theta +// Faster than fix_sincos() but not as accurate (does not interpolate) +void fix_fastsincos(fixang theta, fix *sin, fix *cos); + +fix fix_fastsin(fixang theta); + +fix fix_fastcos(fixang theta); + +// Computes the arcsin of x +fixang fix_asin(fix x); + +// Computes the arccos of x +fixang fix_acos(fix x); + +// Computes the atan of y/x, in the correct quadrant and everything +fixang fix_atan2(fix y, fix x); + +#if defined(__cplusplus) +} +#endif + +/* fixpoint x ^ y */ +extern fix fix_pow(fix x, fix y); + +////////////////////////////// +// +// f_exp.c + +// Computes e to the x +// +fix fix_exp(fix x); + +////////////////////////////// +// +// fix24 - 24 bits integer, 8 bits fraction +// + +typedef int32_t fix24; + +#define fix24_make(a, b) ((((int32_t)(a)) << 8) | (b)) +#define fix24_trunc(n) ((n)&0xffffff00) +#define fix24_round(n) (((n) + 128) & 0xffffff00) +#define fix24_int(n) ((n) >> 8) +#define fix24_frac(n) ((n)&0xff) +#define fix24_float(n) ((float)(fix24_int(n)) + (float)(fix24_frac(n)) / 256.0) + +#define fix24_from_fix16(n) ((n) >> 8) +#define fix16_from_fix24(n) ((n) << 8) + +// For Mac version: The PowerPC version uses an assembly language routine +// to do the multiply. +fix24 fix24_mul(fix24 a, fix24 b); +fix24 fix24_div(fix24 a, fix24 b); + +// Wide (64-bit) fix functions + +// 64-bit fix type (high 32 bit - integer, low 32 bit - fractional) +typedef int64_t fix64; + +#define fix64_make(a, b) ((((int64_t)(a)) << 32) | (b)) +#define fix64_int(n) ((int32_t)((n) >> 32)) +#define fix64_frac(n) ((uint32_t)((n) & 0xffffffff)) +#define fix64_to_fix(n) (fix64_int(n) << 16 | fix64_frac(n) >> 16) + +// fix64 algebraic functions +/** + * Divide two numbers. There no divide by zero check! + * @param a dividend + * @param b divisor + * @return int32_t result of division + */ +extern int32_t fix64_div(int64_t a, int32_t b); + +/** + * Multiply two numbers. + * @param a number + * @param b number + * @return result of multiplication + */ +extern int64_t fix64_mul(int32_t a, int32_t b); + +//============================================ +// +// Other multiply/div/add variants used by 2D and 3D. +// +//============================================ +extern fix fix_mul_3_3_3(fix a, fix b); +extern fix fix_mul_3_32_16(fix a, fix b); +extern fix fix_mul_3_16_20(fix a, fix b); +extern fix fix_mul_16_32_20(fix a, fix b); + +extern fix fix_div_16_16_3(fix a, fix b); + +#define fix_mul_div_3_16_16_3 fix_mul_div + +extern fix fix_div_16_16_3(fix a, fix b); +extern fix fix_mul_3_3_3(fix a, fix b); +extern fix fix_mul_3_32_16(fix a, fix b); +extern fix fix_mul_3_16_20(fix a, fix b); +extern fix fix_mul_16_32_20(fix a, fix b); + +#define fix_div_16_3_16 fix_div_16_16_3 +#define fix_div_3_3_16 fix_div +#define fix_mul_3_16_16 fix_mul_3_3_3 + +#define fix_sal(a, b) ((a) << (b)) +#define fix_sar(a, b) ((a) >> (b)) + +#define fix_3_16(a) ((a) >> 13) + +#define FIX_UNIT_3 0x20000000 + +#endif /* !__fix24_H */ diff --git a/engine/src/Libraries/FIX/Source/fix_pow.c b/engine/src/Libraries/FIX/Source/fix_pow.c new file mode 100644 index 0000000..0f5d4cf --- /dev/null +++ b/engine/src/Libraries/FIX/Source/fix_pow.c @@ -0,0 +1,55 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +/* + * $Source: n:/project/lib/src/fix/RCS/fix_pow.c $ + * $Revision: 1.1 $ + * $Author: lmfeeney $ + * $Date: 1994/06/18 03:48:48 $ + */ + +#include "fix.h" + +// returns the fixed point x ^ y, read em and weep +// this can easily overflow so chill +fix fix_pow(fix x, fix y) { + fix ans; + fix rh, rl; + uint16_t yh, yl; + + ans = FIX_UNIT; + yh = (uint16_t)fix_int(y); + yl = fix_frac(y); + rh = rl = x; + + // calculate hi part, leave when done + for (int i = 0; i < 16; ++i) { + if (yh & 1) + ans = fix_mul(ans, rh); + if (yh != 0) + rh = fix_mul(rh, rh); + yh = yh >> 1; + if (yl != 0) + rl = fix_sqrt(rl); + if (yl & 0x8000) + ans = fix_mul(ans, rl); + yl = yl << 1; + } + return ans; +} diff --git a/engine/src/Libraries/FIX/Source/fix_sqrt.c b/engine/src/Libraries/FIX/Source/fix_sqrt.c new file mode 100644 index 0000000..c55bc0d --- /dev/null +++ b/engine/src/Libraries/FIX/Source/fix_sqrt.c @@ -0,0 +1,62 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +//================================================================= +// +// System Shock - ©1994-1995 Looking Glass Technologies, Inc. +// +// FIX_SQRT.c - Square root routine for fixed-point +// numbers. Adapted from 80386 asm. +// +//================================================================= + +//-------------------- +// Includes +//-------------------- +#include "fix.h" +#include "lg.h" +#include + +//----------------------------------------------------------------- +// Calculate the square root of a fixed-point number. +//----------------------------------------------------------------- +fix fix_sqrt(fix num) { + // fix res = long_sqrt(num); + + float f = fix_float(num); + f = sqrtf(f); + + // Make the number a fix and return it + return fix_from_float(f); +} + +//----------------------------------------------------------------- +// Calculate the square root of a long number. +//----------------------------------------------------------------- +int long_sqrt(int num) { + // WH dunno, needed? + if (num == 0) + return (0); + // A bit of error checking. + if (num < 0) { + ERROR("long_sqrt of negative number!"); + return (0); + } + + return (int32_t)sqrt(num); +} diff --git a/engine/src/Libraries/FIX/Source/trigtab.h b/engine/src/Libraries/FIX/Source/trigtab.h new file mode 100644 index 0000000..46eec05 --- /dev/null +++ b/engine/src/Libraries/FIX/Source/trigtab.h @@ -0,0 +1,26 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +extern uint16_t sintab[256 + 64 + 1]; + +extern fixang asintab[128 + 1 + 1]; + +#define INTEGER_EXP_OFFSET 11 +extern uint32_t expinttab[INTEGER_EXP_OFFSET * 2 + 1]; + +extern uint32_t expfractab[16 + 1]; diff --git a/engine/src/Libraries/FIXPP/Source/fixpp.cpp b/engine/src/Libraries/FIXPP/Source/fixpp.cpp new file mode 100644 index 0000000..c5ea936 --- /dev/null +++ b/engine/src/Libraries/FIXPP/Source/fixpp.cpp @@ -0,0 +1,116 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Header: r:/prj/lib/src/fixpp/RCS/fixpp.cc 1.28 1994/08/30 11:32:42 jak Exp $ + */ + +// Ð…Ð…Ð… For now, turn debugging on, so we can run the test programs. +//#define FIXDEBUG 1 + +#ifdef FIXDEBUG + +#include "fixpp.h" +#include + +// ========================================================= +// touch() is a function that does nothing except cause the +// variable passed in to "escape" in the optimization sense, +// so that the compiler cannot optimize it as much. +// ========================================================= + +void touch(Fixpoint &a) { a = a; } + +// =========================================================== +// bitdump() dumps the fixpoint to a string and returns +// the address of the string. +// =========================================================== + +char *bitdump(Fixpoint &a) { + static char string[30]; + + sprintf(string, "[%#lx]", a.val); + + return string; +} + +uint8_t Fixpoint::click_bool = 1; + +uint32_t Fixpoint::constructor_void = 0, Fixpoint::constructor_Fixpoint = 0, Fixpoint::constructor_int = 0, + Fixpoint::constructor_uint = 0, Fixpoint::constructor_lint = 0, Fixpoint::constructor_ulint = 0, + Fixpoint::constructor_double = 0; + +uint32_t Fixpoint::ass_Fixpoint = 0, Fixpoint::ass_int = 0, Fixpoint::ass_lint = 0, Fixpoint::ass_uint = 0, + Fixpoint::ass_ulint = 0, Fixpoint::ass_double = 0; + +uint32_t Fixpoint::binary_add = 0, Fixpoint::binary_div = 0, Fixpoint::binary_sub = 0, Fixpoint::binary_mul = 0; + +uint32_t Fixpoint::add_eq = 0, Fixpoint::sub_eq = 0, Fixpoint::mul_eq = 0, Fixpoint::div_eq = 0; + +uint32_t Fixpoint::unary_minus = 0, Fixpoint::unary_plus = 0; + +uint32_t Fixpoint::cond_l = 0, Fixpoint::cond_g = 0, Fixpoint::cond_le = 0, Fixpoint::cond_ge = 0, + Fixpoint::cond_eq = 0, Fixpoint::cond_neq = 0; + +void Fixpoint::report(void) { report(std::cout); } + +void Fixpoint::report(std::ostream &os) { + os << "Constructor void: " << constructor_void << '\n'; + os << "Constructor Fixpoint: " << constructor_Fixpoint << '\n'; + os << "Constructor int: " << constructor_int << '\n'; + os << "Constructor lint: " << constructor_lint << '\n'; + os << "Constructor uint: " << constructor_uint << '\n'; + os << "Constructor ulint: " << constructor_ulint << '\n'; + os << "Constructor double: " << constructor_double << '\n'; + + os << "Assign to Fixpoint: " << ass_Fixpoint << '\n'; + os << "Assign to int: " << ass_int << '\n'; + os << "Assign to uint: " << ass_uint << '\n'; + os << "Assign to lint: " << ass_lint << '\n'; + os << "Assign to ulint: " << ass_ulint << '\n'; + os << "Assign to double: " << ass_double << '\n'; + + os << "Binary Add: " << binary_add << '\n'; + os << "Binary Sub: " << binary_sub << '\n'; + os << "Binary Div: " << binary_div << '\n'; + os << "Binary Mul: " << binary_mul << '\n'; + + os << "Add-equals " << add_eq << '\n'; + os << "Sub-equals " << sub_eq << '\n'; + os << "Mul-equals " << mul_eq << '\n'; + os << "Div-equals " << div_eq << '\n'; + + os << "Unary minus " << unary_minus << '\n'; + os << "Unary plus " << unary_plus << '\n'; + + os << "< " << cond_l << '\n'; + os << "> " << cond_g << '\n'; + os << "<= " << cond_le << '\n'; + os << ">= " << cond_ge << '\n'; + os << "== " << cond_eq << '\n'; + os << "!= " << cond_neq << '\n'; +} + +void Fixpoint::reset_report(void) { + constructor_void = constructor_Fixpoint = constructor_int = constructor_uint = constructor_lint = + constructor_ulint = constructor_double = ass_Fixpoint = ass_int = ass_uint = ass_lint = ass_ulint = + ass_double = binary_add = binary_sub = binary_div = binary_mul = add_eq = sub_eq = mul_eq = div_eq = + unary_minus = unary_plus = cond_l = cond_g = cond_le = cond_ge = cond_eq = cond_neq = 0; +} + +#endif /* FIXDEBUG */ diff --git a/engine/src/Libraries/FIXPP/Source/fixpp.h b/engine/src/Libraries/FIXPP/Source/fixpp.h new file mode 100644 index 0000000..66946c7 --- /dev/null +++ b/engine/src/Libraries/FIXPP/Source/fixpp.h @@ -0,0 +1,702 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Header: r:/prj/lib/src/fixpp/RCS/fixpp.h 1.45 1994/08/08 18:27:45 ept Exp $ + */ + +/* +I'd like to dedicate this fixpoint library to Dan and Matt for their +inspirational bravery, and to C++ for being such a complex and neurotic +language. +*/ + +#ifndef __FIXPP_H +#define __FIXPP_H + +#include +#include +#include + +extern "C" { +//#include "mprintf.h" +#include "fix.h" // A big thank you to Dan and Matt. +} + +// How many bits to shift an integer up to make it a fixpoint. +// =========================================================== +#ifdef FIXPOINT_SHIFTUP +#define SHIFTUP FIXPOINT_SHIFTUP +#else +#define SHIFTUP 16 // 16:16 default format. +#endif + +#define SHIFTMULTIPLIER (1 << SHIFTUP) + +// Here are some flags for your convenience. +// ========================================= + +// Define this for some misc. debugging things like touch() +// ======================================================== +//#define FIXDEBUG + +#ifdef FIXDEBUG +#define CLICK(c) c += (Fixpoint::click_bool) +#else +#define CLICK(c) +#endif + +// Here is a nice forward declaration. +// =================================== + +class Fixpoint; + +#define Q Fixpoint + +class Fixpoint { + + friend Fixpoint rawConstruct(int32_t l); + + public: + // The data is stored here. + // ======================== + int32_t val; + + // Some invasive functions to get right at the internal rep. + // What? Me not secure? I'm no fascist. + // ========================================================= + + uint32_t bits(); + void setbits(uint32_t ul); + + // Constructors. + // ============= + + Fixpoint(); + + Fixpoint(const Fixpoint &); + + Fixpoint(int32_t i); + + Fixpoint(uint32_t i); + + Fixpoint(double); + + // Conversions. + // ============ + + double to_double() const; + + float to_float() const; + + int32_t to_int() const; + + fix to_fix() const; + + fixang to_fixang() const; + + // Reverse Conversions. + // ==================== + + void fix_to(fix); + + void fixang_to(fixang); + + // Assignments. + // ============ + + // REMOVED!!!! + + // Arithmetic operators (homogeneous)!! + // ==================================== + + Fixpoint &operator+=(Fixpoint); + + Fixpoint &operator-=(Fixpoint); + + Fixpoint &operator*=(Fixpoint); + + Fixpoint &operator/=(Fixpoint); + + Fixpoint &operator<<=(uint32_t n); + + Fixpoint &operator>>=(uint32_t n); + + Fixpoint operator-() const; + + Fixpoint operator+() const; + + int32_t operator<(const Fixpoint &) const; + + int32_t operator>(const Fixpoint &) const; + + int32_t operator<=(const Fixpoint &fp2) const; + + int32_t operator>=(const Fixpoint &fp2) const; + + int32_t operator==(const Fixpoint &fp2) const; + + int32_t operator!=(const Fixpoint &fp2) const; + + // Signed shifts + // ============= + + void shift(int32_t n); + Fixpoint shifted(int32_t n) const; + + // Fast comparisons with zero (maybe... perhaps Q(0) isn't so slow after all) + // (and a trip down memory lane for FORTRAN-ites) + // ==================================== + + int32_t gt_zero() const; + + int32_t ge_zero() const; + + int32_t eq_zero() const; + + int32_t ne_zero() const; + + int32_t le_zero() const; + + int32_t lt_zero() const; + + // Friendly math function declarations. + // ==================================== + + friend inline Fixpoint sqrt(Fixpoint); + friend inline Fixpoint exp(Fixpoint); + friend inline int32_t floor(Fixpoint); + friend inline Fixpoint sin(Fixpoint); + friend inline Fixpoint cos(Fixpoint); + friend inline Fixpoint tan(Fixpoint); + friend inline Fixpoint acos(Fixpoint); + friend inline Fixpoint asin(Fixpoint); + friend inline void sincos(Fixpoint ang, Fixpoint *sn, Fixpoint *cs); + friend inline Fixpoint atan2(Fixpoint, Fixpoint); + friend inline Fixpoint fsin(Fixpoint); + friend inline Fixpoint fcos(Fixpoint); + friend inline void fsincos(Fixpoint ang, Fixpoint *sn, Fixpoint *cs); + friend inline Fixpoint abs(Fixpoint); + +#ifdef FIXDEBUG + + friend char *bitdump(Fixpoint &); + + // Reporting. + // ========== + + static uint8_t click_bool; + + static uint32_t constructor_void, constructor_Fixpoint, constructor_int, constructor_uint, constructor_lint, + constructor_ulint, constructor_double; + + static uint32_t ass_Fixpoint, ass_int, ass_uint, ass_lint, ass_ulint, ass_double; + + static uint32_t binary_add, binary_sub, binary_mul, binary_div; + + static uint32_t add_eq, sub_eq, mul_eq, div_eq; + + static uint32_t unary_minus, unary_plus; + + static uint32_t cond_l, cond_g, cond_le, cond_ge, cond_eq, cond_neq; + + static void report_on(void) { click_bool = 1; } + static void report_off(void) { click_bool = 0; } + + static void report(std::ostream &); + static void report(void); + static void reset_report(void); + +#endif /* FIXDEBUG */ + +} /* Blessed be!! */; + +// Constructors +// ============ + +inline uint32_t Fixpoint::bits() { return (uint32_t)val; } +inline void Fixpoint::setbits(uint32_t ul) { val = ul; } + +inline Fixpoint::Fixpoint() { CLICK(constructor_void); } // Hey, why not define our own.... + +inline Fixpoint::Fixpoint(const Fixpoint &fp) { + CLICK(constructor_Fixpoint); + val = fp.val; +} + +inline Fixpoint::Fixpoint(int32_t i) { + CLICK(constructor_int); + val = i << SHIFTUP; +} + +inline Fixpoint::Fixpoint(uint32_t i) { + CLICK(constructor_uint); + val = i << SHIFTUP; +} + +inline Fixpoint::Fixpoint(double d) { + CLICK(constructor_double); + val = (int32_t)(d * SHIFTMULTIPLIER); +} + +inline Fixpoint rawConstruct(int32_t l) { + Fixpoint f; + f.val = l; + return f; +} +#define f2Fixpoint(x) (rawConstruct((int32_t)((x)*SHIFTMULTIPLIER))) + +// ====================================== +// +// Math functions. +// +// ====================================== + +//////////// +// // +// += // +// // +//////////// +inline Fixpoint &Fixpoint::operator+=(Fixpoint fp2) { + CLICK(add_eq); + + val += fp2.val; + + return *this; +} + +//////////// +// // +// -= // +// // +//////////// +inline Fixpoint &Fixpoint::operator-=(Fixpoint fp2) { + CLICK(sub_eq); + + val -= fp2.val; + return *this; +} + +//////////// +// // +// *= // +// // +//////////// +inline Fixpoint &Fixpoint::operator*=(Fixpoint fp2) { + CLICK(mul_eq); + + val = (int32_t)fix_mul((fix)val, (fix)fp2.val); + return *this; +} + +//////////// +// // +// /= // +// // +//////////// +inline Fixpoint &Fixpoint::operator/=(Fixpoint fp2) { + CLICK(div_eq); + + val = (int32_t)fix_div((fix)val, (fix)fp2.val); + // val = _fix_do_div(val, fp2.val); + return *this; +} + +inline Fixpoint &Fixpoint::operator<<=(uint32_t n) { + val <<= n; + return *this; +} + +inline Fixpoint &Fixpoint::operator>>=(uint32_t n) { + val >>= n; + return *this; +} + +inline Fixpoint operator+(Fixpoint a, Fixpoint b) { + CLICK(Fixpoint::binary_add); + a.val += b.val; + return a; +} + +inline Fixpoint operator-(Fixpoint a, Fixpoint b) { + CLICK(Fixpoint::binary_sub); + a.val -= b.val; + return a; +} + +inline Fixpoint operator*(Fixpoint a, Fixpoint b) { + CLICK(Fixpoint::binary_mul); + a.val = (int32_t)fix_mul((fix)a.val, (fix)b.val); + return a; +} + +inline Fixpoint operator/(Fixpoint a, Fixpoint b) { + CLICK(Fixpoint::binary_div); + a.val = (int32_t)fix_div((fix)a.val, (fix)b.val); + // a.val=_fix_do_div(a.val,b.val); + return a; +} + +/////////// +// // +// - // +// // +/////////// +inline Fixpoint Fixpoint::operator-() const { + Fixpoint ans; + + CLICK(unary_minus); + + ans.val = -this->val; + + return ans; +} + +/////////// +// // +// + // +// // +/////////// +inline Fixpoint Fixpoint::operator+() const { + CLICK(unary_plus); + + return *this; +} + +inline void Fixpoint::shift(int32_t n) { + if (n > 0) + val <<= n; + else if (n < 0) + val >>= (-n); +} + +inline Fixpoint Fixpoint::shifted(int32_t n) const { + Fixpoint r(*this); + if (n > 0) + r.val <<= n; + else if (n < 0) + r.val >>= (-n); + return r; +} + +inline Fixpoint operator<<(Fixpoint p, unsigned int n) { + p.val <<= n; + return p; +} + +inline Fixpoint operator>>(Fixpoint p, unsigned int n) { + p.val >>= n; + return p; +} + +// Conversions. +// ============ + +inline double Fixpoint::to_double() const { return ((double)val) / SHIFTMULTIPLIER; } + +inline float Fixpoint::to_float() const { return ((float)val) / SHIFTMULTIPLIER; } + +inline int32_t Fixpoint::to_int() const { return (int32_t)(val >> SHIFTUP); } + +inline fix Fixpoint::to_fix() const { return (fix)val; } + +inline fixang Fixpoint::to_fixang() const { + Fixpoint temp = *this * f2Fixpoint(0.159154943); + + // for temp, 360 degrees = 1.0. + // The lower 16 bits of the internal rep is the fixang. + + return (uint16_t)temp.val; +} + +inline void Fixpoint::fix_to(fix f) { val = f; } + +inline void Fixpoint::fixang_to(fixang f) { + val = ((int32_t)(int16_t)(f - 1)) + 1; + *this *= f2Fixpoint(6.283185306); +} + +// Comparisons. +// ============ + +/////////// +// // +// < // +// // +/////////// +inline int32_t Fixpoint::operator<(const Fixpoint &fp2) const { + CLICK(cond_l); + + return this->val < fp2.val; +} + +/////////// +// // +// > // +// // +/////////// +inline int32_t Fixpoint::operator>(const Fixpoint &fp2) const { + CLICK(cond_g); + + return this->val > fp2.val; +} + +//////////// +// // +// <= // +// // +//////////// +inline int32_t Fixpoint::operator<=(const Fixpoint &fp2) const { + CLICK(cond_le); + + return this->val <= fp2.val; +} + +//////////// +// // +// >= // +// // +//////////// +inline int32_t Fixpoint::operator>=(const Fixpoint &fp2) const { + CLICK(cond_ge); + + return this->val >= fp2.val; +} + +//////////// +// // +// == // +// // +//////////// +inline int32_t Fixpoint::operator==(const Fixpoint &fp2) const { + CLICK(cond_eq); + + return this->val == fp2.val; +} + +//////////// +// // +// != // +// // +//////////// +inline int32_t Fixpoint::operator!=(const Fixpoint &fp2) const { + CLICK(cond_neq); + + return this->val != fp2.val; +} + +// ====================================== +// +// Comparisons with zero +// +// ====================================== + +inline int32_t Fixpoint::gt_zero() const { return (val > 0); } + +inline int32_t Fixpoint::ge_zero() const { return (val >= 0); } + +inline int32_t Fixpoint::eq_zero() const { return (val == 0); } + +inline int32_t Fixpoint::ne_zero() const { return (val != 0); } + +inline int32_t Fixpoint::le_zero() const { return (val <= 0); } + +inline int32_t Fixpoint::lt_zero() const { return (val < 0); } + +// ====================================== +// +// Mixed math. +// +// ====================================== + +inline Fixpoint operator*(int32_t i, Fixpoint const &fp) { return Fixpoint(i) * fp; } +inline Fixpoint operator*(uint32_t i, Fixpoint const &fp) { return Fixpoint(i) * fp; } +inline Fixpoint operator*(double d, Fixpoint const &fp) { return Fixpoint(d) * fp; } + +inline Fixpoint operator-(int32_t i, Fixpoint const &fp) { return Fixpoint(i) - fp; } +inline Fixpoint operator-(uint32_t i, Fixpoint const &fp) { return Fixpoint(i) - fp; } +inline Fixpoint operator-(double d, Fixpoint const &fp) { return Fixpoint(d) - fp; } + +inline Fixpoint operator+(int32_t i, Fixpoint const &fp) { return Fixpoint(i) + fp; } +inline Fixpoint operator+(uint32_t i, Fixpoint const &fp) { return Fixpoint(i) + fp; } +inline Fixpoint operator+(double d, Fixpoint const &fp) { return Fixpoint(d) + fp; } + +inline Fixpoint operator/(int32_t i, Fixpoint const &fp) { return Fixpoint(i) / fp; } +inline Fixpoint operator/(uint32_t i, Fixpoint const &fp) { return Fixpoint(i) / fp; } +inline Fixpoint operator/(double d, Fixpoint const &fp) { return Fixpoint(d) / fp; } + +#ifdef BADMIX + +inline Fixpoint operator*=(int32_t i, Fixpoint fp) { return Fixpoint(i) *= fp; } +inline Fixpoint operator*=(uint32_t i, Fixpoint fp) { return Fixpoint(i) *= fp; } +inline Fixpoint operator*=(double d, Fixpoint fp) { return Fixpoint(d) *= fp; } + +#endif + +// ====================================== +// +// I/O functions. +// +// ====================================== + +// ====================================== +// +// I/O functions. +// +// ====================================== + +inline std::ostream &operator<<(std::ostream &os, const Fixpoint &fp) { + os << fp.to_double(); + + return os; +} + +inline std::istream &operator>>(std::istream &is, Fixpoint &fp) { + double temp; + + is >> temp; + + fp = temp; + + return is; +} + +// ==================================================== +// +// Math functions. +// +// ==================================================== + +inline Fixpoint mul_div(Fixpoint a, Fixpoint b, Fixpoint c) { + Fixpoint r; + r.val = (int32_t)fix_mul_div((fix)a.val, (fix)b.val, (fix)c.val); + return r; +} + +inline Fixpoint sqrt(Fixpoint a) { + Fixpoint ans; + + ans.val = fix_sqrt(a.val); + + return ans; +} + +inline Fixpoint exp(Fixpoint a) { + Fixpoint ans; + ans.val = fix_exp(a.val); + return ans; +} + +inline int32_t floor(Fixpoint a) { return a.val >> SHIFTUP; } + +inline Fixpoint sin(Fixpoint a) { + Fixpoint ans; + + ans.val = fix_sin(a.to_fixang()); + + return ans; +} + +inline Fixpoint cos(Fixpoint a) { + Fixpoint ans; + + ans.val = fix_cos(a.to_fixang()); + + return ans; +} + +inline Fixpoint tan(Fixpoint a) { + Fixpoint sn, cs; + + sn = sin(a); + cs = cos(a); + if (cs == 0) + return 0; + else + return sn / cs; +} + +inline Fixpoint asin(Fixpoint a) { + Fixpoint ans; + + ans.fixang_to(fix_asin(a.to_fix())); + + return ans; +} + +inline Fixpoint acos(Fixpoint a) { + Fixpoint ans; + + ans.fixang_to(fix_acos(a.to_fix())); + + return ans; +} + +inline void sincos(Fixpoint ang, Fixpoint *sn, Fixpoint *cs) { + fix fsn, fcs; + fix_sincos(ang.to_fixang(), &fsn, &fcs); + sn->val = fsn; + cs->val = fcs; +} + +inline Fixpoint atan2(Fixpoint y, Fixpoint x) { + Fixpoint ans; + + ans.fixang_to(fix_atan2(y.to_fix(), x.to_fix())); + + return ans; +} + +inline Fixpoint fsin(Fixpoint a) { + Fixpoint ans; + + ans.val = fix_fastsin(a.to_fixang()); + + return ans; +} + +inline Fixpoint fcos(Fixpoint a) { + Fixpoint ans; + + ans.val = fix_fastcos(a.to_fixang()); + + return ans; +} + +inline void fsincos(Fixpoint ang, Fixpoint *sn, Fixpoint *cs) { + fix fsn, fcs; + fix_fastsincos(ang.to_fixang(), &fsn, &fcs); + sn->val = fsn; + cs->val = fcs; +} + +inline Fixpoint abs(Fixpoint fp) { + Fixpoint ans; + + ans.val = labs(fp.val); + + return ans; +} + +#ifdef FIXDEBUG + +void touch(Fixpoint &); + +#endif /* FIXDEBUG */ + +#endif /* !__FIXPP_H */ diff --git a/engine/src/Libraries/H/2dres.h b/engine/src/Libraries/H/2dres.h new file mode 100644 index 0000000..dd8ca01 --- /dev/null +++ b/engine/src/Libraries/H/2dres.h @@ -0,0 +1,149 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Header: r:/prj/lib/src/h/RCS/2dres.h 1.6 1994/07/06 18:38:48 jaemz Exp $ + * + * Macros to use 2d calls with resources + * + * $Log: 2dres.h $ + * Revision 1.6 1994/07/06 18:38:48 jaemz + * Added fields to cylbm frame + * + * Revision 1.5 1994/06/15 11:51:49 jaemz + * Added cylindrical bitmap object data types + * + * Revision 1.4 1994/01/27 13:21:19 eric + * Added gr_cpy_pal_image to copy image pallette from + * REFerence to a pallette in memory. + * + * Revision 1.3 1993/09/28 01:12:21 kaboom + * Converted #include "xxx" to #include for watcom. + * + * Revision 1.2 1993/08/24 20:04:52 rex + * Turned framedesc's updateArea into union of updateArea, anchorArea, anchorPt + * + * Revision 1.1 1993/04/27 12:06:52 rex + * Initial revision + */ + +#ifndef _2DRES_H +#define _2DRES_H + +#include "../2D/Source/2d.h" +#include "../RES/Source/res.h" +#include "../DSTRUCT/Source/rect.h" + +#pragma pack(push,2) + +// A Ref in a resource gets you a Frame Descriptor: + +typedef struct { + grs_bitmap bm; // embedded bitmap, bm.bits set to NULL + union { + LGRect updateArea; // update area (for anims) + LGRect anchorArea; // area to anchor sub-bitmap + LGPoint anchorPt; // point to anchor from + }; + int32_t pallOff; // offset to pallette + // bitmap's bits follow immediately +} FrameDesc; + +// On-disc layout of a FrameDesc. +extern const ResLayout FrameDescLayout; +extern const ResourceFormat FrameDescFormat; +#define FORMAT_FRAMEDESC (&FrameDescFormat) + +// These are the ref-based 2d macros. They have the goofy do/while(0) +// so that you may use them in an if statement without C whining. + +// Draw a bitmap, given its ref (unclipped) + +#define gr_ubitmap_ref(ref,x,y) do { \ + FrameDesc *pfd = RefGet(ref); \ + pfd->bm.bits = (uchar *)(pfd + 1); \ + gr_ubitmap(&pfd->bm, x, y); \ + } while (0) + +// Draw a bitmap, given its ref (clipped) + +#define gr_bitmap_ref(ref,x,y) do { \ + FrameDesc *pfd = RefGet(ref); \ + pfd->bm.bits = (uchar *)(pfd + 1); \ + gr_bitmap(&pfd->bm, x, y); \ + } while(0) + +// Draw a scaled bitmap, given its ref (unclipped) + +#define gr_scale_ubitmap_ref(ref,x,y,w,h) do { \ + FrameDesc *pfd = RefGet(ref); \ + pfd->bm.bits = (uchar *)(pfd + 1); \ + gr_scale_ubitmap(&pfd->bm, x, y, w, h); \ + } while(0) + +// Draw a scaled bitmap, given its ref (clipped) + +#define gr_scale_bitmap_ref(ref,x,y,w,h) do { \ + FrameDesc *pfd = RefGet(ref); \ + pfd->bm.bits = (uchar *)(pfd + 1); \ + gr_scale_bitmap(&pfd->bm, x, y, w, h); \ + } while(0) + +// Set an image's associated (partial) palette, if any + +#define gr_set_pal_imgref(ref) do { \ + FrameDesc *pfd = RefGet(ref); \ + if (pfd->pallOff) { \ + short *p = (short *)((uchar *) ResGet(REFID(ref)) + pfd->pallOff); \ + gr_set_pal(*p, *(p+1), (uchar *)(p+2)); \ + } \ + } while(0) + +// Copy an image's associated (partial) palette to memory +// (palp is a pointer to start of destination pallette) + +#define gr_cpy_pal_imgref(ref, palp) do { \ + FrameDesc *pfd = RefGet(ref); \ + if (pfd->pallOff) { \ + short *p = (short *)((uchar *) ResGet(REFID(ref)) + pfd->pallOff); \ + LG_memcpy((uchar *)(palp + (*p * 3)), (uchar *)(p+2), *(p+1) * 3 ); \ + } \ + } while(0) + +// Data types for a cylindrical bitmap object +// eventually we'll want one for a full 3d one + +typedef struct { + int nviews; // number of views + fix ppu; // pixels per unit + uchar bisym; // bilateral symmetry or not (means its a mirror) + int off[1]; // offsets, in reality there should be off[nview] of them +} CylBMObj; // cylindrical 3d bitmap object + +typedef struct { + grs_bitmap bm; + byte u1,v1; // anchor point 1 + byte u2,v2; // anchor point 2 + fix vper1; // v1 / (v2-v1) + fix vper2; // (h-v2) / (v2-v1) + fix uper; // (w - u) / u +} CylBMFrame; // one frame of the cylindrical bm object. Always put the bits after this. S + +#pragma pack(pop) + +#endif diff --git a/engine/src/Libraries/H/error.h b/engine/src/Libraries/H/error.h new file mode 100644 index 0000000..31c180a --- /dev/null +++ b/engine/src/Libraries/H/error.h @@ -0,0 +1,70 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/h/RCS/error.h $ + * $Revision: 1.8 $ + * $Author: kaboom $ + * $Date: 1993/09/28 01:12:45 $ + * + * $Log: error.h $ + * Revision 1.8 1993/09/28 01:12:45 kaboom + * Converted #include "xxx" to #include for watcom. + * + * Revision 1.7 1993/06/03 06:52:13 mahk + * Added file errors + * + * Revision 1.6 1993/03/22 11:44:05 matt + * Moved home of this file to the \project\lib\src\h (from src\lg). + * + * Revision 1.5 1993/03/21 01:51:17 mahk + * Added ERR_NOMEM + * + * Revision 1.4 1993/03/19 18:44:05 mahk + * Added ERR_NULL and ERR_NOEFFECT, and an RCS header. + * + * Revision 1.3 1993/03/19 17:09:35 mahk + * added ERR_NULL. + * + * Revision 1.2 1993/03/15 18:17:27 mahk + * cast each error value explicitly + * + * Revision 1.1 1993/02/25 12:52:09 rex + * Initial revision + */ + +#ifndef __ERROR_H +#define __ERROR_H + +typedef short errtype; + +#define OK ((errtype)0) // Normal execution +#define ERR_NODEV ((errtype)1) // No such device +#define ERR_DUNDERFLOW ((errtype)2) // Data underflow (stack, queue, etc) +#define ERR_DOVERFLOW ((errtype)3) // Data overflow (stack, queue, etc) +#define ERR_RANGE ((errtype)4) // Arg out of range +#define ERR_NULL ((errtype)5) // Unexpected NULL pointer +#define ERR_NOEFFECT ((errtype)6) // Operation had no effect +#define ERR_NOMEM ((errtype)7) // Not enough memory +#define ERR_FOPEN ((errtype)8) // Error opening file +#define ERR_FCLOSE ((errtype)9) // Error closing file +#define ERR_FREAD ((errtype)10) // Error reading file +#define ERR_FWRITE ((errtype)11) // Error writing file +// more here + +#endif // __ERROR_H diff --git a/engine/src/Libraries/H/keydefs.h b/engine/src/Libraries/H/keydefs.h new file mode 100644 index 0000000..bc0c39f --- /dev/null +++ b/engine/src/Libraries/H/keydefs.h @@ -0,0 +1,49 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#define KEY_DEL 0x08 +#define KEY_BS 0x08 +#define KEY_TAB 0x09 +#define KEY_ENTER 0x0d +#define KEY_UP 0x1e +#define KEY_DOWN 0x1f +#define KEY_LEFT 0x1c +#define KEY_RIGHT 0x1d +#define KEY_HOME 0x01 +#define KEY_END 0x04 +#define KEY_PGUP 0x0b +#define KEY_PGDN 0x0c +#define KEY_ESC 0x1b +#define KEY_PAUSE 0x1b +#define KEY_SPACE 0x20 + +#define KEY_F1 128 + 0 +#define KEY_F2 128 + 1 +#define KEY_F3 128 + 2 +#define KEY_F4 128 + 3 +#define KEY_F5 128 + 4 +#define KEY_F6 128 + 5 +#define KEY_F7 128 + 6 +#define KEY_F8 128 + 7 +#define KEY_F9 128 + 8 +#define KEY_F10 128 + 9 +#define KEY_F11 128 + 10 +#define KEY_F12 128 + 11 +#define KEY_F13 128 + 12 +#define KEY_F14 128 + 13 +#define KEY_F15 128 + 14 diff --git a/engine/src/Libraries/H/lg_types.h b/engine/src/Libraries/H/lg_types.h new file mode 100644 index 0000000..56a6b06 --- /dev/null +++ b/engine/src/Libraries/H/lg_types.h @@ -0,0 +1,65 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/h/RCS/types.h $ + * $Revision: 1.2 $ + * $Author: kaboom $ + * $Date: 1993/09/28 01:12:47 $ + * + * extra typedefs and macros for use by all code. + * + * $Log: types.h $ + * Revision 1.2 1993/09/28 01:12:47 kaboom + * Converted #include "xxx" to #include for watcom. + * + * Revision 1.1 1993/03/19 18:19:27 matt + * Initial revision + */ + +#ifndef __TYPES_H +#define __TYPES_H + +#ifndef _H2INC //don't redefine byte in assembly header +/* this is a signed byte */ +typedef signed char byte; +#endif /* !_H2INC */ + +/* these are convenience typedefs so we don't always have to keep typing + `unsigned.' */ +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef unsigned char ubyte; + +//typedef unsigned char bool; + +#ifndef NULL +#define NULL 0 +#endif /* !NULL */ + +#ifndef TRUE +#define TRUE 1 +#endif /* !TRUE */ + +#ifndef FALSE +#define FALSE 0 +#endif /* !FALSE */ + +#endif /* !__TYPES_H */ diff --git a/engine/src/Libraries/H/types.inc b/engine/src/Libraries/H/types.inc new file mode 100644 index 0000000..3cb711e --- /dev/null +++ b/engine/src/Libraries/H/types.inc @@ -0,0 +1,65 @@ +; +; Copyright (C) 2015-2018 Night Dive Studios, LLC. +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . +; + +option expr32 +option casemap:none + +IFNDEF types_inc +types_inc EQU 1 + +; Begin of file types.h +; +; * $Source: n:/project/lib/src/h/RCS/types.h $ +; * $Revision: 1.2 $ +; * $Author: kaboom $ +; * $Date: 1993/09/28 01:12:47 $ +; * +; * extra typedefs and macros for use by all code. +; * +; * $Log: types.h $ +; * Revision 1.2 1993/09/28 01:12:47 kaboom +; * Converted #include "xxx" to #include for watcom. +; * +; * Revision 1.1 1993/03/19 18:19:27 matt +; * Initial revision +; +; don't redefine byte in assembly header +; these are convenience typedefs so we don't always have to keep typing +; `unsigned.' +uchar TYPEDEF BYTE + +ushort TYPEDEF WORD + +uint TYPEDEF WORD + +ulong TYPEDEF DWORD + +bool TYPEDEF BYTE + +ubyte TYPEDEF BYTE + +NULL EQU 0t +; !NULL +TRUE EQU 1t +; !TRUE +FALSE EQU 0t +; !FALSE +; !__TYPES_H +; End of file types.h + +ENDIF + diff --git a/engine/src/Libraries/INPUT/Source/kb.h b/engine/src/Libraries/INPUT/Source/kb.h new file mode 100644 index 0000000..b1fe2e9 --- /dev/null +++ b/engine/src/Libraries/INPUT/Source/kb.h @@ -0,0 +1,147 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2019 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __KB_H +#define __KB_H +/* + * $Source: n:/project/lib/src/input/RCS/kbs.h $ + * $Revision: 1.1 $ + * $Author: kaboom $ + * $Date: 1994/02/12 18:28:21 $ + * + * Types for keyboard system. + * + * This file is part of the input library. + */ + +#ifndef __KBS_H +#define __KBS_H +typedef struct { + uchar code; + uchar state; + uchar ascii; // Added for Mac version + uchar modifiers; // " " " " +} kbs_event; +#endif /* !__KBS_H */ + +/* + * $Source: n:/project/lib/src/input/RCS/kbdecl.h $ + * $Revision: 1.4 $ + * $Author: kaboom $ + * $Date: 1994/02/12 18:21:29 $ + * + * Declarations for keyboard library. + * + * $Log: kbdecl.h $ + * Revision 1.4 1994/02/12 18:21:29 kaboom + * Moved event structure. + * + * Revision 1.3 1993/04/29 17:19:59 mahk + * added kb_get_cooked + * + * Revision 1.2 1993/04/28 17:01:48 mahk + * Added kb_flush_bios + * + * Revision 1.1 1993/03/10 17:16:41 kaboom + * Initial revision + * + */ + +#ifdef __INLINE_FUNCTIONS__ +#define kb_state(code) (kbd_lowmem_start[KBD_ARRAY_START + code] & KBA_STATE) +#else +extern uchar kb_state(uchar code); +#endif + +#define kb_init kb_startup +#define kb_close kb_shutdown +extern int kb_startup(void *init_buf); +extern int kb_shutdown(void); + +extern kbs_event kb_next(void); +extern kbs_event kb_look_next(void); +extern void kb_flush(void); +extern uchar kb_get_state(uchar kb_code); +extern void kb_clear_state(uchar kb_code, uchar bits); +extern void kb_set_state(uchar kb_code, uchar bits); +extern void kb_set_signal(uchar code, uchar int_no); +extern int kb_get_flags(); +extern void kb_set_flags(int flags); +extern void kb_generate(kbs_event e); +// extern void kb_flush_bios(void); // For Mac version +#define kb_flush_bios kb_flush +extern uchar kb_get_cooked(ushort *key); +#define KBA_STATE (1) +#define KBA_REPEAT (2) +#define KBA_SIGNAL (4) +#define __KBD_INC (1) +extern char *kbd_lowmem_start; +#define __KBERR_INC (1) +#define KBE_ALLOC_LOWMEM (0) +#define KBE_FREE_LOWMEM (1) +#define KBE_MEM_THRASHED (2) +#define KBE_REAL_HANDLER (3) +#define KBE_PROT_HANDLER (4) +#define KBE_MEM_LOCK (5) +#define KBE_MEM_UNLOCK (6) +#define KBF_BLOCK (1) +#define KBF_CHAIN (2) +#define KBF_SIGNAL (4) +#define KBD_GLOBAL_START (0) +#define KBD_QUEUE_HEAD (KBD_GLOBAL_START) +#define KBD_LAST_CODES (KBD_QUEUE_HEAD + 4) +#define KBD_OLD_REAL_HANDLER (KBD_LAST_CODES + 4) +#define KBD_STATUS_FLAGS (KBD_OLD_REAL_HANDLER + 4) +#define KBD_GLOBAL_SIZE (KBD_STATUS_FLAGS + 4) +#define KBD_QUEUE_START (KBD_GLOBAL_SIZE) +#define KBD_QUEUE_SIZE (1024) +#define KBD_QUEUE_END (KBD_QUEUE_START + KBD_QUEUE_SIZE) +#define KBD_ARRAY_START (KBD_QUEUE_END) +#define KBD_ARRAY_SIZE (256) +#define KBD_ARRAY_END (KBD_ARRAY_START + KBD_ARRAY_SIZE) +#define KBD_SIGLIST_START (KBD_ARRAY_END) +#define KBD_SIGLIST_SIZE (256) +#define KBD_SIGLIST_END (KBD_SIGLIST_START + KBD_SIGLIST_SIZE) +#define KBD_LOWBUF_SIZE (KBD_SIGLIST_END) +#define KBD_HANDLER_START (KBD_LOWBUF_SIZE) +#define KBC_SHIFT_PREFIX (0x0e0) +#define KBC_PAUSE_PREFIX (0x0e1) +#define KBC_PAUSE_DOWN (0x0e11d) +#define KBC_PAUSE_UP (0x0e19d) +#define KBC_PRSCR_DOWN (0x02a) +#define KBC_PRSCR_UP (0x0aa) +#define KBC_PAUSE (0x07f) +#define KBC_NONE (0x0ff) +#define KBS_UP (0) +#define KBS_DOWN (1) + +// DG: constants for values of kbs_event::modifiers, also used in sshockKeyStates[] +// (those constants are based on the values that were hardcoded in kb_cook()) +#define KB_MOD_CTRL (0x01) +#define KB_MOD_SHIFT (0x04) +#define KB_MOD_ALT (0x08) + +// currente state of the keys, based on the SystemShock/Mac Keycodes (sshockKeyStates[keyCode] has the state for that +// key) +extern uchar sshockKeyStates[256]; +// this one is only used in sshockKeyStates[], it's set if a button is pressed +// (together with the CTRL/SHIFT/ALT modifiers, if they were pressed as while the key was pressed) +#define KB_MOD_PRESSED (0x10) + +#endif /* !__KB_H */ diff --git a/engine/src/Libraries/INPUT/Source/kbcook.c b/engine/src/Libraries/INPUT/Source/kbcook.c new file mode 100644 index 0000000..eb9f6af --- /dev/null +++ b/engine/src/Libraries/INPUT/Source/kbcook.c @@ -0,0 +1,162 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2019 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/input/RCS/kbcook.c $ + * $Revision: 1.5 $ + * $Author: kaboom $ + * $Date: 1994/08/15 16:35:59 $ + * + * Routines to convert raw scan codes to more useful cooked codes. + * + * This file is part of the input library. + */ +#include "lg.h" +#include "kbcook.h" +#include "keydefs.h" +//#include +//#include + +//---------------------------------------------------------------------------- +// This cooks kbc codes into ui codes which include ascii stuff. +//---------------------------------------------------------------------------- +// For Mac version, replace the whole thing, because the "cooked" info +// already exists in the kbs_event record. So just format the results +// as expected. +//---------------------------------------------------------------------------- +errtype kb_cook(kbs_event ev, ushort *cooked, uchar *results) { + // On the Mac, since modifiers by themselves don't produce an event, + // you always have a "cooked" result. + + *results = TRUE; + *cooked = ev.ascii; + + if (ev.ascii == 0) { + // FIXME: Why don't the arrow keys translate properly? + if (ev.code == 125) + *cooked = KEY_DOWN; + else if (ev.code == 126) + *cooked = KEY_UP; + else if (ev.code == 123) + *cooked = KEY_LEFT; + else if (ev.code == 124) + *cooked = KEY_RIGHT; + else if (ev.code == 13) + *cooked = KEY_ENTER; + } + + *cooked |= (short)ev.state << KB_DOWN_SHF; // Add in the key-down state. + + // text input events are used to get printable characters (see sdl_events.c) + // note that text input events don't work for ctrl'd or alt'd keys + + if (ev.modifiers & KB_MOD_CTRL) // If command-key was down, + *cooked |= KB_FLAG_CTRL; // simulate a control key + + if (ev.modifiers & KB_MOD_SHIFT) // If shift-key was down + *cooked |= KB_FLAG_SHIFT; + + if (ev.modifiers & KB_MOD_ALT) // If option-key was down, + *cooked |= KB_FLAG_ALT; // simulate an alt key. + + return OK; + /* + ushort flags = KB_CNV(ev.code,0); + uchar shifted = 1 & ((kbd_modifier_state >> KBM_SHIFT_SHF) + | (kbd_modifier_state >> (KBM_SHIFT_SHF+1))); + uchar capslock = 1 & (flags >> CNV_CAPS_SHF) + & (kbd_modifier_state >> KBM_CAPS_SHF); + ushort cnv = KB_CNV(ev.code,shifted ^ capslock); + int old_mods = kbd_modifier_state; + + *cooked = cnv & (CNV_SPECIAL|CNV_2ND|0xFF) ; + *results = FALSE; + + // if an up event, use negative logic. Wacky + if (ev.state == KBS_UP) kbd_modifier_state = ~kbd_modifier_state; + switch(ev.code) // check for modifiers + { + case 0x7a: return 0; break; + case KBC_LSHIFT: + kbd_modifier_state |= KBM_LSHIFT; + break; + case KBC_RSHIFT: + kbd_modifier_state |= KBM_RSHIFT; + break; + case KBC_LCTRL: + kbd_modifier_state |= KBM_LCTRL; + break; + case KBC_RCTRL: + kbd_modifier_state |= KBM_RCTRL; + break; + case KBC_CAPS: + if (ev.state == KBS_DOWN) + kbd_modifier_state ^= KBM_CAPS; + break; + case KBC_NUM: + if (ev.state == KBS_DOWN) + kbd_modifier_state ^= KBM_NUM; + break; + case KBC_SCROLL: + if (ev.state == KBS_DOWN) + kbd_modifier_state ^= KBM_SCROLL; + break; + case KBC_LALT: + kbd_modifier_state |= KBM_LALT; + break; + case KBC_RALT: + kbd_modifier_state |= KBM_RALT; + break; + default: + *results = TRUE; // Not a modifier key, we must translate. + break; + } + if (ev.state == KBS_UP) kbd_modifier_state = ~kbd_modifier_state; + if ((kbd_modifier_state&KBM_LED_MASK) != (old_mods&KBM_LED_MASK)) + kb_set_leds(kbd_modifier_state&KBM_LED_MASK); + if (!*results) return OK; + + if ((cnv & CNV_NUM) && !(kbd_modifier_state & KBM_NUM)) + *cooked = ev.code|KB_FLAG_SPECIAL; + + *cooked |= (short)ev.state << KB_DOWN_SHF; + + *cooked |= (((kbd_modifier_state << (KB_CTRL_SHF - KBM_CTRL_SHF)) + | (kbd_modifier_state << (KB_CTRL_SHF - KBM_CTRL_SHF-1))) + & KB_FLAG_CTRL) & cnv; + + *cooked |= (((kbd_modifier_state << (KB_ALT_SHF - KBM_ALT_SHF)) + | (kbd_modifier_state << (KB_ALT_SHF - KBM_ALT_SHF-1))) + & KB_FLAG_ALT) & cnv; + + // if KB_FLAG_SPECIAL is set, then let set the shifted + // flag according to shifted + *cooked |= (shifted << KB_SHIFT_SHF) & cnv; + return OK; + */ +} + +uchar kb_get_cooked(ushort *key) { + uchar res = FALSE; + kbs_event ev = kb_next(); + if (ev.code == KBC_NONE) + return res; + kb_cook(ev, key, &res); + return res; +} diff --git a/engine/src/Libraries/INPUT/Source/kbcook.h b/engine/src/Libraries/INPUT/Source/kbcook.h new file mode 100644 index 0000000..41facec --- /dev/null +++ b/engine/src/Libraries/INPUT/Source/kbcook.h @@ -0,0 +1,101 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2019 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/input/RCS/kbcook.h $ + * $Revision: 1.3 $ + * $Author: kaboom $ + * $Date: 1994/08/15 16:36:40 $ + * + * Constants for cooked keyboard event codes. + * + * This file is part of the input library. + */ + +#ifndef __KBCOOK_H +#define __KBCOOK_H + +#include +//#include "error.h" +#include "../../H/error.h" +#include "kb.h" + +//#include + +/* +#define KB_CNV_SHIFT 1 +#define KB_CNV_NOSHIFT 0 +#define KB_CNV_TBLSIZE 0xE0 + +extern ushort kb_cnv_table[KB_CNV_TBLSIZE][2]; + +#ifdef DEBUG_ON +#define KB_CNV(scan,shift) (((scan) >= KB_CNV_TBLSIZE) ? 0 : kb_cnv_table[scan][shift]) +#else +#define KB_CNV(scan,shift) (kb_cnv_table[scan][shift]) +#endif +*/ + +#define CNV_CTRL (1 << (1 + 8)) // KB_FLAG_CTRL can be set +#define CNV_ALT (1 << (2 + 8)) // KB_FLAG_SHIFT can be set +#define CNV_SPECIAL (1 << (3 + 8)) // KB_FLAG_SPECIAL is set +#define CNV_SHIFT (1 << (4 + 8)) // KB_FLAG_SHIFT can be set +#define CNV_2ND (1 << (5 + 8)) // KB_FLAG_2ND is set +#define CNV_NUM (1 << (6 + 8)) // affected by numlock +#define CNV_CAPS (1 << (7 + 8)) // affected by capslock + +#define CNV_CAPS_SHF (7 + 8) + +#define KB_FLAG_DOWN (1 << (0 + 8)) +#define KB_FLAG_CTRL CNV_CTRL +#define KB_FLAG_ALT CNV_ALT +#define KB_FLAG_SPECIAL CNV_SPECIAL +#define KB_FLAG_SHIFT CNV_SHIFT +#define KB_FLAG_2ND CNV_2ND + +#define KB_DOWN_SHF 8 +#define KB_CTRL_SHF 9 +#define KB_ALT_SHF 10 +#define KB_SPECIAL_SHF 11 +#define KB_SHIFT_SHF 12 +#define KB_2ND_SHF 13 + +#define KBC_EXTENDED 0x80 + +errtype kb_cook(kbs_event code, ushort *cooked, uchar *results); +// "cooks" kb event "code." If cooking generates a cooked code, sets +// *results to true and puts the result in *cooked. Otherwise, *results = false. + +#define kb2ascii(x) (((x)&KB_FLAG_SPECIAL) ? 0 : (x)&0xFF) + +#define kb_isalnum(i) isalnum(kb2ascii(i)) +#define kb_isalpha(i) isalpha(kb2ascii(i)) +#define kb_iscntrl(i) ((i)&KB_FLAG_CTRL) +#define kb_isdigit(i) isdigit(kb2ascii(i)) +#define kb_isgraph(i) isgraph(kb2ascii(i)) +#define kb_islower(i) islower(kb2ascii(i)) +#define kb_isprint(i) isprint(kb2ascii(i)) +#define kb_ispunct(i) ispunct(kb2ascii(i)) +#define kb_isspace(i) isspace(kb2ascii(i)) +#define kb_isupper(i) isupper(kb2ascii(i)) +#define kb_isxdigit(i) isxdigit(kb2ascii(i)) +#define kb_tolower(i) (((i)&KB_FLAG_SPECIAL) ? (i) : ((i)&0xFF00) | tolower((i)&0xFF)) +#define kb_toupper(i) (((i)&KB_FLAG_SPECIAL) ? (i) : ((i)&0xFF00) | toupper((i)&0xFF)) + +#endif /* __KBCOOK_H */ diff --git a/engine/src/Libraries/INPUT/Source/mouse.c b/engine/src/Libraries/INPUT/Source/mouse.c new file mode 100644 index 0000000..094ba19 --- /dev/null +++ b/engine/src/Libraries/INPUT/Source/mouse.c @@ -0,0 +1,759 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2019 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/input/RCS/mouse.c $ + * $Revision: 1.15 $ + * $Author: mahk $ + * $Date: 1994/06/21 06:16:42 $ + * + * Mouse handler adapted from Rex Bradford's Freefall mouse code. + * + * This file is part of the input library. + */ + +// This file only vaguely resembles the freefall mouse code from +// which it descended. It supports some very-low-level input routines +// for the mouse. It provides an interrupt-driven event queue, polling, +// and callbacks from the interrupt handler. + +// --------------------------------------------------------- +// 6/21/94 ML Added mouse velocity support in mousevel.h so that we can emulate the mouse +// using other devices. +// --------------------------------------------------------- +// For the Mac version I use a TimeManager task to poll the mouse for mouse +// movement callback routines. Mouse click events will be handled throught the normal +// Macintosh event queue. Most of the stuff in this file will go away. +// ¥¥¥Note: The mouse position will always be returned in *local* coordinates, +// that is, local to the main game window. + +#include + +#include "error.h" +#include "mouse.h" +#include "tickcount.h" + +typedef struct _mouse_state { + short x, y; + short butts; +} mouse_state; + +/* +#define DEFAULT_XRATE 16 // Default mouse sensitivity parameters +#define DEFAULT_YRATE 8 +#define DEFAULT_ACCEL 100 +#define LO_RES_SCREEN_WIDTH 320 +#define HIRES_XRATE 2 +#define HIRES_YRATE 2 + +// These are global for fast access from interrupt routine & others + +short gMouseCritical; // in critical region? +*/ +#define NUM_MOUSEEVENTS 32 +short mouseQueueSize = NUM_MOUSEEVENTS; +volatile short mouseQueueIn; // back of event queue +volatile short mouseQueueOut; // front of event queue +ss_mouse_event mouseQueue[NUM_MOUSEEVENTS]; // array of events + +short mouseInstantX; // instantaneous mouse xpos (int-based) +short mouseInstantY; // instantaneous mouse ypos (int-based) +short mouseInstantButts; +/* +short mouseButtMask; // amt to mask to get buttons. +ubyte mouseXshift = 0; // Extra bits of mouse resolution +ubyte mouseYshift = 1; +*/ +ubyte mouseMask = 0xFF; // mask of events to put in the queue. +/* +uchar mouseLefty = FALSE; // is the user left-handed? +*/ +#define NUM_MOUSE_CALLBACKS 16 +mouse_callfunc mouseCall[NUM_MOUSE_CALLBACKS]; +void *mouseCallData[NUM_MOUSE_CALLBACKS]; +short mouseCalls = 0; // current number of mouse calls. +short mouseCallSize = sizeof(mouse_callfunc); +/* +uchar mouse_installed = FALSE; // was mouse found? + +ulong default_mouse_ticks = 0; +ulong volatile *mouse_ticks = &default_mouse_ticks; // Place to get mouse timestamps. + +// MOUSE VELOCITY STUFF + +int mouseVelX = 0, mouseVelY = 0; +int mouseVelXmax = 0x7FFFFFFF; +int mouseVelYmax = 0x7FFFFFFF; +int mouseVelXmin = 0x80000000; +int mouseVelYmin = 0x80000000; + +// Macros & defines + +#define MOUSECRITON() (gMouseCritical++) +#define MOUSECRITOFF() (gMouseCritical--) + +#define INT_MOUSE 0x33 // mouse software interrupt vector + +#define ERRET 1 + + + +extern void MouseHandler(void); +extern ulong mouseHandlerSize; +*/ + +// extern short gActiveLeft, gActiveTop; +// bool gRBtnWasDown = true; +extern uchar pKbdGetKeys[16]; + +//---------------- +// Internal Prototypes +//---------------- +static void ReadMouseState(mouse_state *pMouseState); + +#if __profile__ +#pragma profile off +#endif +//--------------------------------------------------------------- +// The following section is the time manager task for handling mouse movement. +//--------------------------------------------------------------- +#pragma require_prototypes off + +// KLC - try calling this from the main timer task. +//--------------------------------------------------------------- +void MousePollProc(void) { + // TODO: is this even still needed? if so, can it be replaced by setting mouseInstant* in pump_events() ? + // if the callbacks from mouseCall[] are still needed, could they also be called in pump_events() ? + // if not, could they be the only thing called here, while mouseInstant* is still set in pump_events() ? + + extern ss_mouse_event latestMouseEvent; + mouseInstantButts = latestMouseEvent.buttons; + + if (mouseInstantX != latestMouseEvent.x || mouseInstantY != latestMouseEvent.y) // If different + { + mouseInstantX = latestMouseEvent.x; // save the position + mouseInstantY = latestMouseEvent.y; + + ss_mouse_event e = latestMouseEvent; + e.type = MOUSE_MOTION; + + for (uint16_t i = 0; i < mouseCalls; i++) + if (mouseCall[i] != NULL) + mouseCall[i](&e, mouseCallData[i]); + + if (mouseMask & MOUSE_MOTION) // Add a mouse-moved event + { // to the internal queue. + short newin = mouseQueueIn, newout = mouseQueueOut; + short in = newin; + mouseQueue[newin] = e; + newin = (newin + 1 < mouseQueueSize) ? newin + 1 : 0; + if (newin == mouseQueueOut) + newout = (newout + 1 < mouseQueueSize) ? newout + 1 : 0; + mouseQueueOut = newout; + mouseQueueIn = newin; + } + } + + /*Point mp; + short i; + mouse_event e; + + mp = *(Point *)0x830; // Get mouse location from low +memory. mp.h -= gActiveLeft; +// Convert to "local" screen coordinates. mp.v -= gActiveTop; + + GetKeys((UInt32 *)pKbdGetKeys); // Check keys to see if our +simulated + // right button is down. + if (Button()) // See if the mouse button is +down + { + mouseInstantButts = 1; + if ((pKbdGetKeys[0x3A>>3] >> (0x3A & 7)) & 1) // If the option key is down also, + mouseInstantButts = 2; // then it's really a right-button +click. + } + if( ((pKbdGetKeys[0x31>>3] >> (0x31 & 7)) & 1) || // If space, enter, or return are down, + ((pKbdGetKeys[0x4C>>3] >> (0x4C & 7)) & 1) || // then pretend right-button is down. + ((pKbdGetKeys[0x24>>3] >> (0x24 & 7)) & 1) ) + mouseInstantButts |= 2; + + if (mouseInstantX != mp.h || mouseInstantY != mp.v) // If different + { + mouseInstantX = mp.h; // save the +position mouseInstantY = mp.v; + + e.x = mp.h; // and inform the callback +routines e.y = mp.v; e.type = MOUSE_MOTION; e.buttons = mouseInstantButts; for (i = 0; i < mouseCalls; i++) + if(mouseCall[i] !=NULL) + mouseCall[i](&e,mouseCallData[i]); + + if (mouseMask & MOUSE_MOTION) // Add a mouse-moved +event + { // to the internal +queue. short newin = mouseQueueIn, newout = mouseQueueOut; short in = newin; mouseQueue[newin] = e; newin = (newin + 1 +< mouseQueueSize) ? newin + 1 : 0; if (newin == mouseQueueOut) newout = (newout + 1 < mouseQueueSize) ? newout + 1 : 0; + mouseQueueOut = newout; + mouseQueueIn = newin; + } + } +// PrimeTime((QElemPtr)tmTaskPtr, 50); // Check 20 times a second. + +*/ +} + +#pragma require_prototypes on +#if __profile__ +#pragma profile on +#endif + +// --------------------------------------------------------- +// mouse_shutdown() terminates mouse handler. +// --------------------------------------------------------- +// For Mac version: do nothing. + +errtype mouse_shutdown(void) { + /* union REGS regs; + struct SREGS segregs; + + Spew(DSRC_MOUSE_Shutdown,("entering mouse_shutdown()\n")); + + + // Shut down Microsoft mouse driver + + if (mouse_installed) + { + regs.x.eax = 0x000C; + regs.x.ecx = 0; + regs.x.edx = 0; + segregs.es = 0; + segregs.ds = 0; + int386x(INT_MOUSE, ®s, ®s, &segregs); + + dpmi_unlock_lin_region(MouseHandler,mouseHandlerSize); + } */ + // RmvTime((QElemPtr)&pMousePollTask); // Stop the mouse polling + //task DisposeRoutineDescriptor(pMousePollPtr); // Dispose its UPP + + return OK; +} + +// --------------------------------------------------------- +// mouse_init() initializes mouse handler. It does the following: +// +// 1. Microsoft mouse driver initialized +// 2. Customizes mouse driver & handler based on display mode +// 3. Initializes mouse handler state variables +// --------------------------------------------------------- +// For Mac version: ignore sizes (mouse is already set up). + +errtype mouse_init(short mone, short mtwo) { + mouse_state mstate; + /* + union REGS regs; + struct SREGS segregs; + Spew(DSRC_MOUSE_Init,("Entering mouse_init()\n")); + + // Initialize Microsoft mouse driver + + regs.x.eax = 0x0000; + int386(INT_MOUSE, ®s, ®s); + mouse_installed = (regs.w.ax != 0); + + // If mouse found, do more initialization + + if (mouse_installed) + { + + DBG(DSRC_MOUSE_Init, + { if (!mouse_installed) + Warning(("mouse_init(): Mouse not installed\n")); + }) + */ + // Initialize mouse state variables + + extern void sdl_mouse_init(void); + sdl_mouse_init(); + + mouseQueueIn = 0; + mouseQueueOut = 0; + + mouseCalls = 0; + + ReadMouseState(&mstate); + mouseInstantX = mstate.x; + mouseInstantY = mstate.y; + mouseInstantButts = mstate.butts; + /* + // Set up mouse interrupt handler + + dpmi_lock_lin_region(MouseHandler,mouseHandlerSize); + + regs.x.eax = 0x000C; + regs.w.cx = 0xFF; // take all mouse events + regs.x.edx = FP_OFF(MouseHandler); + segregs.es = FP_SEG(MouseHandler); + segregs.ds = segregs.es; + int386x(INT_MOUSE, ®s, ®s, &segregs); + + // Do the sensitivity scaling thang. + mouse_set_screensize(xsize,ysize); + } + + AtExit(mouse_shutdown); + + // Return whether or not mouse found + + return(mouse_installed ? OK : ERR_NODEV); + */ + + /* + pMousePollPtr = NewTimerProc(MousePollProc); // Make a UPP for the TM task + + pMousePollTask.task.tmAddr = pMousePollPtr; // Insert the mouse polling TM task + pMousePollTask.task.tmWakeUp = 0; + pMousePollTask.task.tmReserved = 0; + #ifndef __powerc + pMousePollTask.appA5 = SetCurrentA5(); + #endif + InsTime((QElemPtr)&pMousePollTask); + PrimeTime((QElemPtr)&pMousePollTask, 50); // Check 20 times a second + */ + return (OK); +} +/* +// --------------------------------------------------------- +// mouse_set_screensize() sets the screen size, scaling mouse sensitivity. +errtype mouse_set_screensize(short x, short y) +{ + short xrate = DEFAULT_XRATE,yrate = DEFAULT_YRATE,t = DEFAULT_ACCEL; + if (x > LO_RES_SCREEN_WIDTH) + { + xrate = HIRES_XRATE; + yrate = HIRES_YRATE; + mouseXshift = 3; + mouseYshift = 3; + } + else + { + xrate /= 2; + mouseXshift = 1; + mouseYshift = 0; + } + mouse_set_rate(xrate,yrate,t); + mouse_constrain_xy(0,0,x-1,y-1); + mouse_put_xy(mouseInstantX,mouseInstantY); + return OK; +} +*/ +/* +//--------------------------------------------------------- +// _mouse_update_vel() updates coordinates based on mouse +// velocity. Generates a motion event if there's any change to position. + +void _mouse_update_vel(void) +{ + static ulong last_ticks = 0; + + ulong ticks = *mouse_ticks; + + if (ticks != last_ticks && (mouseVelX != 0 || mouseVelY != 0)) + { + short newx = mouseInstantX; + short newy = mouseInstantY; + ulong dt = ticks - last_ticks; + short dx = (mouseVelX*dt) >> MOUSE_VEL_UNIT_SHF; + short dy = (mouseVelY*dt) >> MOUSE_VEL_UNIT_SHF; + + mouse_put_xy(newx+dx,newy+dy); + } + last_ticks = ticks; +} +*/ + +// -------------------------------------------------------- +// mouse_check_btn checks button state. +// res = ptr to result +// button = button number 0-2 +// --------------------------------------------------------- +// For Mac version: Basically just return true or false right now. +// WH: no use +#if 0 +errtype mouse_check_btn(short button, bool *res) { + + if (button == 1) { + *res = SDL_BUTTON(SDL_BUTTON_LEFT); + } else if (button == 2) { + *res = SDL_BUTTON(SDL_BUTTON_RIGHT); + } + /* if (!mouse_installed) + { + Warning(("mouse_get_xy(): mouse not installed.\n")); + return ERR_NODEV; + } + *res = (mouseInstantButts >> button) & 1; + Spew(DSRC_MOUSE_CheckBtn,("mouse_check_btn(%d,%x) *res = %d\n",button,res,*res)); */ + return OK; +} +#endif +// --------------------------------------------------------- +// mouse_look_next gets the event in front the event queue, +// but does not remove the event from the queue. +// res = ptr to event to be filled. +// --------------------------------------------------------- +// For Mac version: Check the normal Mac event queue for mouse events. The events +// looked for depend on the 'mouseMask' setting. +// WH: no use +#if 0 +errtype mouse_look_next(ss_mouse_event *res) { + printf("mouse_look_next not implemented.\n"); + + /*if (OSEventAvail(eventMask, &theEvent)) // If there is an event, + { + GlobalToLocal(&theEvent.where); + res->x = theEvent.where.h; // fill in the mouse_event + record. res->y = theEvent.where.v; res->timestamp = theEvent.when; + if (theEvent.modifiers & optionKey) // If the option keys is down, send back + a + { // right-button + event. if (theEvent.what == mouseDown) res->type = MOUSE_RDOWN; else if (theEvent.what == mouseUp) res->type = + MOUSE_RUP; res->buttons = 2; res->modifiers = 0; + } + else // Otherwise it's a left-button + event. + { + if (theEvent.what == mouseDown) + res->type = MOUSE_LDOWN; + else if (theEvent.what == mouseUp) + res->type = MOUSE_LUP; + res->buttons = 1; + res->modifiers = (uchar)(theEvent.modifiers >> 8); + } + }*/ + + // If there's not a mouse click event, check the internal queue for mouse + // movement events. + /*else if (mouseMask & MOUSE_MOTION) + { + if (mouseQueueOut == mouseQueueIn) // If no motion events, return an error. + return ERR_NODEV; + else + *res = mouseQueue[mouseQueueOut]; // Return the event. + } + + // If there are no events at all, return an error. + else + return ERR_NODEV;*/ + + /* + Spew(DSRC_MOUSE_LookNext,("entering mouse_look_next()\n")); + if (mouseQueueOut == mouseQueueIn) + _mouse_update_vel(); + if (mouseQueueOut == mouseQueueIn) + { + Spew(DSRC_MOUSE_LookNext,("mouse_look_next(): Queue Underflow.\n")); + return ERR_NODEV; + } + *res = mouseQueue[mouseQueueOut]; + */ + return OK; +} +#endif + +/* +// ------------------------------------------------------- +// +// mouse_generate() adds an event to the back of the +// mouse event queue. If this overflows the queue, + +errtype mouse_generate(mouse_event e) +{ + short newin = mouseQueueIn, newout = mouseQueueOut; + short in = newin; + int i; + errtype result = OK; + Spew(DSRC_MOUSE_Generate,("Entering mouse_generate()\n")); + mouseQueue[newin] = e; + newin = (newin + 1 < mouseQueueSize) ? newin + 1 : 0; + if (newin == mouseQueueOut) + { + newout = (newout + 1 < mouseQueueSize) ? newout + 1 : 0; + Spew(DSRC_MOUSE_Generate,("mouse_generate(): Queue Overflow.\n")); + result = ERR_DUNDERFLOW; + } + + mouseQueueOut = newout; + mouseQueueIn = newin; + mouseInstantX = e.x; + mouseInstantY = e.y; + mouseInstantButts = e.buttons; + for (i = 0; i < mouseCalls; i++) + if(mouseCall[i] !=NULL) + mouseCall[i](&mouseQueue[in],mouseCallData[i]); + return result; +} +*/ + +// ------------------------------------------------------ +// +// mouse_set_callback() registers a callback with the interrupt handler +// f = func to be called back. +// data = data to be given to the func when called +// *id = set to a unique id of the callback. + +errtype mouse_set_callback(mouse_callfunc f, void *data, int *id) { + // Spew(DSRC_MOUSE_SetCallback,("entering mouse_set_callback(%x,%x,%x)\n",f,data,id)); + for (*id = 0; *id < mouseCalls; ++*id) + if (mouseCall[*id] == NULL) + break; + if (*id == NUM_MOUSE_CALLBACKS) { + // Spew(DSRC_MOUSE_SetCallback,("mouse_set_callback(): Table Overflow.\n")); + return ERR_DOVERFLOW; + } + if (*id == mouseCalls) + mouseCalls++; + // Spew(DSRC_MOUSE_SetCallback,("mouse_set_callback(): *id = %d, mouseCalls = %d\n",*id,mouseCalls)); + mouseCall[*id] = f; + mouseCallData[*id] = data; + return OK; +} + +// ------------------------------------------------------- +// +// mouse_unset_callback() un-registers a callback function +// id = unique id of function to unset + +errtype mouse_unset_callback(int id) { + // Spew(DSRC_MOUSE_UnsetCallback,("entering mouse_unset_callback(%d)\n",id)); + if (id >= mouseCalls || id < 0) { + // Spew(DSRC_MOUSE_UnsetCallback,("mouse_unset_callback(): id out of range \n")); + return ERR_RANGE; + } + mouseCall[id] = NULL; + while (mouseCalls > 0 && mouseCall[mouseCalls - 1] == NULL) + mouseCalls--; + return OK; +} + +// -------------------------------------------------------- +// +// mouse_constrain_xy() defines min/max coords +// ¥¥¥ don't do anything for now. Will need to implement some day. +errtype mouse_constrain_xy(short xl, short yl, short xh, short yh) { + /* + union REGS regs; + Spew(DSRC_MOUSE_ConstrainXY,("mouse_constrain_xy(%d,%d,%d,%d)\n",xl,yl,xh,yh)); + if (!mouse_installed) + { + Warning(("mouse_constrain_xy(): mouse not installed.\n")); + return ERR_NODEV; + } + regs.x.eax = 0x0007; + regs.x.ecx = xl << mouseXshift; + regs.x.edx = xh << mouseXshift; + int386(INT_MOUSE,®s,®s); + regs.x.eax = 0x0008; + regs.x.ecx = yl << mouseYshift; + regs.x.edx = yh << mouseYshift; + int386(INT_MOUSE,®s,®s); + */ + return OK; +} + +/* +// -------------------------------------------------------- +// +// mouse_set_rate() sets mouse rate, doubling threshhold + +errtype mouse_set_rate(short xr, short yr, short thold) +{ + union REGS regs; + Spew(DSRC_MOUSE_SetRate,("mouse_set_rate(%d,%d,%d)\n",xr,yr,thold)); + if (!mouse_installed) + { + Warning(("mouse_set_rate(): mouse not installed.\n")); + return ERR_NODEV; + } +// if (mouseXshift > 0) xr = xr / mouseXshift; // why are we dividing? Because shifting is too extreme +// if (mouseYshift > 0) yr = yr / mouseYshift; + regs.x.eax = 0x000F; + regs.x.ecx = max(1,xr); + regs.x.edx = max(1,yr); + int386(INT_MOUSE,®s,®s); + regs.x.eax = 0x0013; + regs.x.edx = max(1,thold); + int386(INT_MOUSE,®s,®s); + return OK; +} + +// -------------------------------------------------- +// +// mouse_get_rate() gets current sensitivity values + +errtype mouse_get_rate(short* xr, short* yr, short* thold) +{ + union REGS regs; + regs.x.eax = 0x001B; + int386(INT_MOUSE,®s,®s); + *xr = regs.x.ebx; + *yr = regs.x.ecx; + *thold = regs.x.edx; +// if (mouseXshift > 0) *xr *= mouseXshift; // why are we multiplying? Because shifting is too extreme +// if (mouseYshift > 0) *yr *= mouseYshift; + return OK; +} + + +// -------------------------------------------------------- +// +// mouse_set_timestamp_register() tells the mouse library where to get +// timestamps. + +errtype mouse_set_timestamp_register(ulong* tstamp) +{ + mouse_ticks = tstamp; + return OK; +} +*/ + +// -------------------------------------------------------- +// mouse_get_time() returns the current mouse timestamp +// -------------------------------------------------------- +// For Mac version: Just return TickCount(). + +uint32_t mouse_get_time(void) { + return TickCount(); +} + +// -------------------------------------------------------- +// ReadMouseState() reads current state of mouse. +// +// pMouseState = ptr to mouse state struct, filled in by routine +// -------------------------------------------------------- +// For Mac version: Use Mac routines to get mouse position and state. + +static void ReadMouseState(mouse_state *pMouseState) { + int mouse_x; + int mouse_y; + + uint mouse_state = SDL_GetMouseState(&mouse_x, &mouse_y); + pMouseState->x = mouse_x; + pMouseState->y = mouse_y; + pMouseState->butts = 0; + + if (mouse_state & SDL_BUTTON(SDL_BUTTON_LEFT)) { + pMouseState->butts = 1; + } + + if (mouse_state & SDL_BUTTON(SDL_BUTTON_RIGHT)) { + pMouseState->butts = 2; + } + + /* union REGS regs; + + regs.x.eax = 0x0003; + int386(INT_MOUSE, ®s, ®s); + + pMouseState->x = regs.w.cx; + pMouseState->y = regs.w.dx; + pMouseState->butts = regs.w.bx; */ +} + +/* +// --------------------------------------------------- +// +// mouse_extremes() finds the min and max "virtual" coordinates of the mouse position + +errtype mouse_extremes( short *xmin, short *ymin, short *xmax, short *ymax ) +{ + union REGS regs; + + regs.x.eax = 0x31; + int386( INT_MOUSE, ®s, ®s ); + + *xmin = regs.w.ax >> mouseXshift; + *ymin = regs.w.bx >> mouseYshift; + *xmax = regs.w.cx >> mouseXshift; + *ymax = regs.w.dx >> mouseYshift; + + Spew( DSRC_MOUSE_Extremes, ("mouse_extremes(): <%d %d> to <%d %d>\n", *xmin, *ymin, *xmax, *ymax )); + + return OK; +} + + +// ------------------------------------------------------ +// +// mouse_set_lefty() sets mouse handedness + +#define SHIFTDIFF 1 + +static short shifted_button_state(short bstate) +{ + short tmp = (bstate & MOUSE_RBUTTON) >> SHIFTDIFF; + tmp |= (bstate & MOUSE_LBUTTON) << SHIFTDIFF; + tmp |= bstate & MOUSE_CBUTTON; + return tmp; +} + +errtype mouse_set_lefty(uchar lefty) +{ + if (lefty == mouseLefty) return ERR_NOEFFECT; + mouseInstantButts = shifted_button_state(mouseInstantButts); + mouseLefty = lefty; + return OK; +} + + + + +// --------------------------------------------------- +// +// mouse_set_velocity_range() sets the range of valid mouse pointer velocities. + +errtype mouse_set_velocity_range(int xl, int yl, int xh, int yh) +{ + mouseVelXmin = xl; + mouseVelYmin = yl; + mouseVelXmax = xh; + mouseVelYmax = yh; + return OK; +} + +errtype mouse_set_velocity(int x, int y) +{ + mouseVelX = max(mouseVelXmin,min(x,mouseVelXmax)); + mouseVelY = max(mouseVelYmin,min(y,mouseVelYmax)); + if (mouseVelX != x || mouseVelY != y) + return ERR_RANGE; + return OK; +} + +errtype mouse_add_velocity(int x, int y) +{ + return mouse_set_velocity(mouseVelX + x, mouseVelY + y); +} + +errtype mouse_get_velocity(int* x, int* y) +{ + *x = mouseVelX; + *y = mouseVelY; + return OK; +} +*/ diff --git a/engine/src/Libraries/INPUT/Source/mouse.h b/engine/src/Libraries/INPUT/Source/mouse.h new file mode 100644 index 0000000..2aa25b9 --- /dev/null +++ b/engine/src/Libraries/INPUT/Source/mouse.h @@ -0,0 +1,161 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2019 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Mouse.H Mouse library header file +// MAHK Leblanc 2/19/93 +/* + * $Source: n:/project/lib/src/input/RCS/mouse.h $ + * $Revision: 1.11 $ + * $Author: unknown $ + * $Date: 1993/09/01 00:19:18 $ + * + * $Log: mouse.h $ + * Revision 1.11 1993/09/01 00:19:18 unknown + * Changed left-handedness api + * + * Revision 1.10 1993/08/29 03:12:35 mahk + * Added mousemask and lefty support. + * + * Revision 1.9 1993/08/27 14:05:06 mahk + * Added shift factors + * + * Revision 1.8 1993/07/28 18:15:58 jak + * Added mouse_extremes() function + * + * Revision 1.7 1993/06/28 02:04:59 mahk + * Bug fixes for the new regime + * + * Revision 1.6 1993/06/27 22:17:32 mahk + * Added timestamps and button state to the mouse event structure. + * + * Revision 1.5 1993/05/04 14:34:27 mahk + * mouse_init no longer takes a screen mode argument. + * + * Revision 1.4 1993/04/14 12:08:46 mahk + * Hey, I got my mouse ups and downs backwards. + * + * Revision 1.3 1993/03/19 18:46:57 mahk + * Added RCS header + * + * + */ + +#ifndef MOUSE_H +#define MOUSE_H + +#include "lg.h" +#include "error.h" +#include +#include +//#include + +typedef struct _mouse_event { + short x; // position + short y; + uint16_t type; // Event mask, bits defined below + uint32_t timestamp; + uchar buttons; + uchar modifiers; // Added for Mac version + char pad[4]; // pad to sixteen bytes +} ss_mouse_event; + +#define MOUSE_MOTION 1u // Event mask bits +#define MOUSE_LDOWN 2u +#define MOUSE_LUP 4u +#define MOUSE_RDOWN 8u +#define MOUSE_RUP 16u +#define MOUSE_CDOWN 32u +#define MOUSE_CUP 64u +// bits 7..9 are used for double click events, see UI/Source/event.h +#define MOUSE_WHEELUP (1 << 10) +#define MOUSE_WHEELDN (1 << 11) + +// Mask of events that are allowed into the queue. +extern ubyte mouseMask; + +// type of mouse interrupt callback func +typedef void (*mouse_callfunc)(ss_mouse_event *e, void *data); + +#define NUM_MOUSE_BTNS 3 +#define MOUSE_LBUTTON 0 +#define MOUSE_RBUTTON 1 +#define MOUSE_CBUTTON 2 + +#define MOUSE_BTN2DOWN(num) (1 << (1 + 2 * (num))) +#define MOUSE_BTN2UP(num) (1 << (2 + 2 * (num))) + +// Initialize the mouse, specifying screen size. +errtype mouse_init(short xsize, short ysize); + +// shutdown mouse system +errtype mouse_shutdown(void); + +// Tell the mouse library where to get timestamps from. +// errtype mouse_set_timestamp_register(ulong *tstamp); + +// Get the current mouse timestamp +uint32_t mouse_get_time(void); + +// Get the mouse position +errtype mouse_get_xy(short *x, short *y); + +// Set the mouse position +errtype mouse_put_xy(short x, short y); + +// Check the state of a mouse button +// errtype mouse_check_btn(short button, bool *result); + +// look at the next mouse event. +// errtype mouse_look_next(ss_mouse_event *result); + +// get & pop the next mouse event +errtype mouse_next(ss_mouse_event *result); + +// Flush the mouse queue +errtype mouse_flush(void); + +// Add an event to the back of the mouse queue +errtype mouse_generate(ss_mouse_event e); + +// Set up an interrupt callback +errtype mouse_set_callback(mouse_callfunc f, void *data, int *id); + +// Remove an interrupt callback +errtype mouse_unset_callback(int id); + +// Constrain the mouse coordinates +errtype mouse_constrain_xy(short xl, short yl, short xh, short yh); + +// Set the mouse rate and accelleration threshhold +// errtype mouse_set_rate(short xr, short yr, short thold); + +// Get the mouse rate and accelleration threshhold +// errtype mouse_get_rate(short *xr, short *yr, short *thold); + +// Sets the mouse coordinate bounds to (0,0) - (x-1,y-1), +// and scales the current values of the mouse sensitivity accordingly. +// errtype mouse_set_screensize(short x, short y); + +// Find the min and max "virtual" coordinates of the mouse position +// errtype mouse_extremes(short *xmin, short *ymin, short *xmax, short *ymax); + +// Sets mouse handedness (true for left-handed) +// errtype mouse_set_lefty(uchar lefty); + +#endif // _MOUSE_H diff --git a/engine/src/Libraries/INPUT/Source/sdl_events.c b/engine/src/Libraries/INPUT/Source/sdl_events.c new file mode 100644 index 0000000..3907b6c --- /dev/null +++ b/engine/src/Libraries/INPUT/Source/sdl_events.c @@ -0,0 +1,970 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2019 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +// +// DG 2018: (eventually) SDL versions of the functions previously in kbMac.c, mouse.c and kbcook.c +// + +#include "lg.h" +#include "kb.h" +#include "mouse.h" +#include +#include +#include + +extern SDL_Window *window; +extern SDL_Renderer *renderer; + +bool fullscreenActive = false; + +static void toggleFullScreen() { + fullscreenActive = !fullscreenActive; + SDL_SetWindowFullscreen(window, fullscreenActive ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + + if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_MAXIMIZED)) + SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); +} + +// current state of the keys, based on the SystemShock/Mac Keycodes (sshockKeyStates[keyCode] has the state for that +// key) set at the beginning of each frame in pump_events() +uchar sshockKeyStates[256]; + +enum { kNumKBevents = 128, kNumMouseEvents = 128 }; + +// queue keyboard events, created in pump_events(), consumed by kb_next() +static kbs_event kbEvents[kNumKBevents]; +static int nextKBevent = 0; // where next to insert (also, if 0 there are no events) + +static void addKBevent(const kbs_event *ev) { + if (nextKBevent < kNumKBevents) { + kbEvents[nextKBevent] = *ev; + ++nextKBevent; + } else { + // printf("WTF, the kbEvents queue is full?!"); + // drop the oldest event + memmove(&kbEvents[0], &kbEvents[1], sizeof(kbs_event) * (kNumKBevents - 1)); + kbEvents[kNumKBevents - 1] = *ev; + } +} + +// same for mouse events, also created in pump_events(), consumed by mouse_next() +static ss_mouse_event mouseEvents[kNumMouseEvents]; +static int nextMouseEvent = 0; + +// latest mouse state as input for MousePollProc() in mouse.c +ss_mouse_event latestMouseEvent; + +static void addMouseEvent(const ss_mouse_event *ev) { + latestMouseEvent = *ev; + + if (nextMouseEvent < kNumMouseEvents) { + mouseEvents[nextMouseEvent] = latestMouseEvent; + ++nextMouseEvent; + } else { + // printf("WTF, the mouseEvents queue is full?!"); + // drop the oldest event + memmove(&mouseEvents[0], &mouseEvents[1], sizeof(ss_mouse_event) * (kNumMouseEvents - 1)); + mouseEvents[kNumMouseEvents - 1] = latestMouseEvent; + } +} + +static uchar sdlKeyCodeToSSHOCKkeyCode(SDL_Keycode kc) { + // apparently System Shock uses the same keycodes as Mac + // which are luckily documented, see + // see http://snipplr.com/view/42797/ + // and https://stackoverflow.com/a/16125341 + // see also GameSrc/movekeys.c for a very short list + + // printf("sdlKeyCodeToSSHOCKkeyCode: %x\n", kc); + + switch (kc) { + case SDLK_a: + return 0x00; // kVK_ANSI_A = 0x00, + case SDLK_s: + return 0x01; // kVK_ANSI_S = 0x01, + case SDLK_d: + return 0x02; // kVK_ANSI_D = 0x02, + case SDLK_f: + return 0x03; // kVK_ANSI_F = 0x03, + case SDLK_h: + return 0x04; // kVK_ANSI_H = 0x04, + case SDLK_g: + return 0x05; // kVK_ANSI_G = 0x05, + case SDLK_z: + return 0x06; // kVK_ANSI_Z = 0x06, + case SDLK_x: + return 0x07; // kVK_ANSI_X = 0x07, + case SDLK_c: + return 0x08; // kVK_ANSI_C = 0x08, + case SDLK_v: + return 0x09; // kVK_ANSI_V = 0x09, + case SDLK_b: + return 0x0B; // kVK_ANSI_B = 0x0B, + case SDLK_q: + return 0x0C; // kVK_ANSI_Q = 0x0C, + case SDLK_w: + return 0x0D; // kVK_ANSI_W = 0x0D, + case SDLK_e: + return 0x0E; // kVK_ANSI_E = 0x0E, + case SDLK_r: + return 0x0F; // kVK_ANSI_R = 0x0F, + case SDLK_y: + return 0x10; // kVK_ANSI_Y = 0x10, + case SDLK_t: + return 0x11; // kVK_ANSI_T = 0x11, + case SDLK_1: + return 0x12; // kVK_ANSI_1 = 0x12, + case SDLK_2: + return 0x13; // kVK_ANSI_2 = 0x13, + case SDLK_3: + return 0x14; // kVK_ANSI_3 = 0x14, + case SDLK_4: + return 0x15; // kVK_ANSI_4 = 0x15, + case SDLK_6: + return 0x16; // kVK_ANSI_6 = 0x16, + case SDLK_5: + return 0x17; // kVK_ANSI_5 = 0x17, + case SDLK_EQUALS: + return 0x18; // kVK_ANSI_Equal = 0x18, + case SDLK_9: + return 0x19; // kVK_ANSI_9 = 0x19, + case SDLK_7: + return 0x1A; // kVK_ANSI_7 = 0x1A, + case SDLK_MINUS: + return 0x1B; // kVK_ANSI_Minus = 0x1B, + case SDLK_8: + return 0x1C; // kVK_ANSI_8 = 0x1C, + case SDLK_0: + return 0x1D; // kVK_ANSI_0 = 0x1D, + case SDLK_RIGHTBRACKET: + return 0x1E; // kVK_ANSI_RightBracket = 0x1E, + case SDLK_o: + return 0x1F; // kVK_ANSI_O = 0x1F, + case SDLK_u: + return 0x20; // kVK_ANSI_U = 0x20, + case SDLK_LEFTBRACKET: + return 0x21; // kVK_ANSI_LeftBracket = 0x21, + case SDLK_i: + return 0x22; // kVK_ANSI_I = 0x22, + case SDLK_p: + return 0x23; // kVK_ANSI_P = 0x23, + case SDLK_l: + return 0x25; // kVK_ANSI_L = 0x25, + case SDLK_j: + return 0x26; // kVK_ANSI_J = 0x26, + case SDLK_QUOTE: + return 0x27; // kVK_ANSI_Quote = 0x27, // TODO: or QUOTEDBL ? + case SDLK_k: + return 0x28; // kVK_ANSI_K = 0x28, + case SDLK_SEMICOLON: + return 0x29; // kVK_ANSI_Semicolon = 0x29, + case SDLK_BACKSLASH: + return 0x2A; // kVK_ANSI_Backslash = 0x2A, + case SDLK_COMMA: + return 0x2B; // kVK_ANSI_Comma = 0x2B, + case SDLK_SLASH: + return 0x2C; // kVK_ANSI_Slash = 0x2C, + case SDLK_n: + return 0x2D; // kVK_ANSI_N = 0x2D, + case SDLK_m: + return 0x2E; // kVK_ANSI_M = 0x2E, + case SDLK_PERIOD: + return 0x2F; // kVK_ANSI_Period = 0x2F, + case SDLK_BACKQUOTE: + return 0x32; // kVK_ANSI_Grave = 0x32, // TODO: really? + case SDLK_KP_DECIMAL: + return 0x41; // kVK_ANSI_KeypadDecimal = 0x41, + case SDLK_KP_MULTIPLY: + return 0x43; // kVK_ANSI_KeypadMultiply = 0x43, + case SDLK_KP_PLUS: + return 0x45; // kVK_ANSI_KeypadPlus = 0x45, + case SDLK_KP_CLEAR: + return 0x47; // kVK_ANSI_KeypadClear = 0x47, + case SDLK_KP_DIVIDE: + return 0x4B; // kVK_ANSI_KeypadDivide = 0x4B, + case SDLK_KP_ENTER: + return 0x4C; // kVK_ANSI_KeypadEnter = 0x4C, aka _ENTER2_ + case SDLK_KP_MINUS: + return 0x4E; // kVK_ANSI_KeypadMinus = 0x4E, + case SDLK_KP_EQUALS: + return 0x51; // kVK_ANSI_KeypadEquals = 0x51, + case SDLK_KP_0: + return 0x52; // kVK_ANSI_Keypad0 = 0x52, + case SDLK_KP_1: + return 0x53; // kVK_ANSI_Keypad1 = 0x53, aka _END2_ + case SDLK_KP_2: + return 0x54; // kVK_ANSI_Keypad2 = 0x54, aka _DOWN2_ + case SDLK_KP_3: + return 0x55; // kVK_ANSI_Keypad3 = 0x55, aka _PGDN2_ + case SDLK_KP_4: + return 0x56; // kVK_ANSI_Keypad4 = 0x56, aka _LEFT2_ + case SDLK_KP_5: + return 0x57; // kVK_ANSI_Keypad5 = 0x57, aka _PAD5_ + case SDLK_KP_6: + return 0x58; // kVK_ANSI_Keypad6 = 0x58, aka _RIGHT2_ + case SDLK_KP_7: + return 0x59; // kVK_ANSI_Keypad7 = 0x59, aka _HOME2_ + case SDLK_KP_8: + return 0x5B; // kVK_ANSI_Keypad8 = 0x5B, aka _UP2_ + case SDLK_KP_9: + return 0x5C; // kVK_ANSI_Keypad9 = 0x5C, aka _PGUP2_ + + // keycodes for keys that are independent of keyboard layout + case SDLK_RETURN: + return 0x24; // kVK_Return = 0x24, + case SDLK_TAB: + return 0x30; // kVK_Tab = 0x30, + case SDLK_SPACE: + return 0x31; // kVK_Space = 0x31, + case SDLK_DELETE: + return 0x33; // kVK_Delete = 0x33, + case SDLK_BACKSPACE: + return 0x33; // kVK_Delete = 0x33, + case SDLK_ESCAPE: + return 0x35; // kVK_Escape = 0x35, + + // returning these is unnecessary and can cause keypresses to be missed + // (esp keys with modifiers) + // case SDLK_LGUI : // fall-through + // case SDLK_RGUI : return 0x37; // kVK_Command = 0x37, // FIXME: I think command is the windows/meta key? + // case SDLK_LSHIFT : return 0x38; // kVK_Shift = 0x38, + // case SDLK_CAPSLOCK : return 0x39; // kVK_CapsLock= 0x39, + // case SDLK_LALT : return 0x3A; // kVK_Option = 0x3A, Option == Aalt + // case SDLK_LCTRL : return 0x3B; // kVK_Control = 0x3B, + // case SDLK_RSHIFT : return 0x3C; // kVK_RightShift = 0x3C, + // case SDLK_RALT : return 0x3D; // kVK_RightOption = 0x3D, + // case SDLK_RCTRL : return 0x3E; // kVK_RightControl = 0x3E, + + // case SDLK_ : return 0x3F; // kVK_Function = 0x3F, // TODO: what's this? + case SDLK_F17: + return 0x40; // kVK_F17 = 0x40, + case SDLK_VOLUMEUP: + return 0x48; // kVK_VolumeUp = 0x48, + case SDLK_VOLUMEDOWN: + return 0x49; // kVK_VolumeDown = 0x49, + case SDLK_MUTE: + return 0x4A; // kVK_Mute = 0x4A, + case SDLK_F18: + return 0x4F; // kVK_F18 = 0x4F, + case SDLK_F19: + return 0x50; // kVK_F19 = 0x50, + case SDLK_F20: + return 0x5A; // kVK_F20 = 0x5A, + case SDLK_F5: + return 0x60; // kVK_F5 = 0x60, + case SDLK_F6: + return 0x61; // kVK_F6 = 0x61, + case SDLK_F7: + return 0x62; // kVK_F7 = 0x62, + case SDLK_F3: + return 0x63; // kVK_F3 = 0x63, + case SDLK_F8: + return 0x64; // kVK_F8 = 0x64, + case SDLK_F9: + return 0x65; // kVK_F9 = 0x65, + case SDLK_F11: + return 0x67; // kVK_F11 = 0x67, + case SDLK_F13: + return 0x69; // kVK_F13 = 0x69, + case SDLK_F16: + return 0x6A; // kVK_F16 = 0x6A, + case SDLK_F14: + return 0x6B; // kVK_F14 = 0x6B, + case SDLK_F10: + return 0x6D; // kVK_F10 = 0x6D, + case SDLK_F12: + return 0x6F; // kVK_F12 = 0x6F, + case SDLK_F15: + return 0x71; // kVK_F15 = 0x71, + case SDLK_HELP: + return 0x72; // kVK_Help = 0x72, + case SDLK_HOME: + return 0x73; // kVK_Home = 0x73, + case SDLK_PAGEUP: + return 0x74; // kVK_PageUp = 0x74, + // case SDLK_ : return 0x75; // kVK_ForwardDelete = 0x75, // TODO: what's this? + case SDLK_F4: + return 0x76; // kVK_F4 = 0x76, + case SDLK_END: + return 0x77; // kVK_End = 0x77, + case SDLK_F2: + return 0x78; // kVK_F2 = 0x78, + case SDLK_PAGEDOWN: + return 0x79; // kVK_PageDown = 0x79, + case SDLK_F1: + return 0x7A; // kVK_F1 = 0x7A, + case SDLK_LEFT: + return 0x7B; // kVK_LeftArrow = 0x7B, aka _LEFT_ + case SDLK_RIGHT: + return 0x7C; // kVK_RightArrow = 0x7C, aka _RIGHT + case SDLK_DOWN: + return 0x7D; // kVK_DownArrow = 0x7D, aka _DOWN_ + case SDLK_UP: + return 0x7E; // kVK_UpArrow = 0x7E, aka _UP_ + default: + return KBC_NONE; + } +} + +int MouseX; +int MouseY; + +int MouseChaosX; +int MouseChaosY; + +extern bool MouseCaptured; + +void SetMouseXY(int mx, int my) { + int physical_width, physical_height; + SDL_GetWindowSize(window, &physical_width, &physical_height); + + int w, h; + SDL_RenderGetLogicalSize(renderer, &w, &h); + + float scale_x = (float)physical_width / w; + float scale_y = (float)physical_height / h; + + int x, y; + if (scale_x >= scale_y) { + x = (physical_width - w * scale_x) / 2; + y = 0; + } else { + x = 0; + y = (physical_height - h * scale_y) / 2; + } + + bool inside = (mx >= x && mx < x + w && my >= y && my < y + h); + bool focus = (SDL_GetWindowFlags(window) & SDL_WINDOW_INPUT_FOCUS); //checking mouse focus isn't what we want here + + if (!inside && focus) { + if (mx < x) + mx = x; + if (mx > x + w - 1) + mx = x + w - 1; + if (my < y) + my = y; + if (my > y + h - 1) + my = y + h - 1; + } + + if (focus) { + MouseX = mx; + MouseY = my; + } + + SDL_ShowCursor((!focus || (!inside && !MouseCaptured)) ? SDL_ENABLE : SDL_DISABLE); +} + +void get_mouselook_vel(int *vx, int *vy); + +extern bool TriggerRelMouseMode; + +static SDL_bool saved_rel_mouse = FALSE; + +// same codes as returned by sdlKeyCodeToSSHOCKkeyCode() +uchar Ascii2Code[95] = { + 0x31, // space + 0x12, // ! + 0x27, // " + 0x14, // # + 0x15, // $ + 0x17, // % + 0x1A, // & + 0x27, // ' + 0x19, // ( + 0x1D, // ) + 0x1C, // * + 0x18, // + + 0x2B, // , + 0x1B, // - + 0x2F, // . + 0x2C, // / + 0x1D, // 0 + 0x12, // 1 + 0x13, // 2 + 0x14, // 3 + 0x15, // 4 + 0x17, // 5 + 0x16, // 6 + 0x1A, // 7 + 0x1C, // 8 + 0x19, // 9 + 0x29, // : + 0x29, // ; + 0x2B, // < + 0x18, // = + 0x2F, // > + 0x2C, // ? + 0x13, // @ + 0x00, // A + 0x0B, // B + 0x08, // C + 0x02, // D + 0x0E, // E + 0x03, // F + 0x05, // G + 0x04, // H + 0x22, // I + 0x26, // J + 0x28, // K + 0x25, // L + 0x2E, // M + 0x2D, // N + 0x1F, // O + 0x23, // P + 0x0C, // Q + 0x0F, // R + 0x01, // S + 0x11, // T + 0x20, // U + 0x09, // V + 0x0D, // W + 0x07, // X + 0x10, // Y + 0x06, // Z + 0x21, // [ + 0x2A, // backslash + 0x1E, // ] + 0x16, // ^ + 0x1B, // _ + 0x32, // ` + 0x00, // a + 0x0B, // b + 0x08, // c + 0x02, // d + 0x0E, // e + 0x03, // f + 0x05, // g + 0x04, // h + 0x22, // i + 0x26, // j + 0x28, // k + 0x25, // l + 0x2E, // m + 0x2D, // n + 0x1F, // o + 0x23, // p + 0x0C, // q + 0x0F, // r + 0x01, // s + 0x11, // t + 0x20, // u + 0x09, // v + 0x0D, // w + 0x07, // x + 0x10, // y + 0x06, // z + 0x21, // { + 0x2A, // | + 0x1E, // } + 0x32 // ~ +}; + +void pump_events(void) { + SDL_Event ev; + + while (SDL_PollEvent(&ev)) { + switch (ev.type) { + case SDL_QUIT: + // a bit hacky at this place, but this would allow exiting the game via the window's [x] button + exit(0); // TODO: I guess there is a better way. + break; + + // TODO: really also handle key up here? the mac code apparently didn't, but where else do + // kbs_events with .state == KBS_UP come from? + case SDL_KEYUP: + case SDL_KEYDOWN: { + uchar c = sdlKeyCodeToSSHOCKkeyCode(ev.key.keysym.sym); + if (c != KBC_NONE) { + kbs_event keyEvent = {0}; + + keyEvent.code = c; + keyEvent.ascii = 0; + keyEvent.modifiers = 0; + + // https://wiki.libsdl.org/SDLKeycodeLookup + // Keycodes for keys with printable characters are represented by the + // character byte in parentheses. Keycodes without character representations + // are determined by their scancode bitwise OR-ed with 1<<30 (0x40000000). + + if (ev.key.keysym.sym >= 0x08 && ev.key.keysym.sym <= 127) + keyEvent.ascii = ev.key.keysym.sym; + else { + // use these invented "ascii" codes for hotkey system + // see MacSrc/Prefs.c + switch (ev.key.keysym.sym) { + case SDLK_F1: + keyEvent.ascii = 128 + 0; + break; + case SDLK_F2: + keyEvent.ascii = 128 + 1; + break; + case SDLK_F3: + keyEvent.ascii = 128 + 2; + break; + case SDLK_F4: + keyEvent.ascii = 128 + 3; + break; + case SDLK_F5: + keyEvent.ascii = 128 + 4; + break; + case SDLK_F6: + keyEvent.ascii = 128 + 5; + break; + case SDLK_F7: + keyEvent.ascii = 128 + 6; + break; + case SDLK_F8: + keyEvent.ascii = 128 + 7; + break; + case SDLK_F9: + keyEvent.ascii = 128 + 8; + break; + case SDLK_F10: + keyEvent.ascii = 128 + 9; + break; + case SDLK_F11: + keyEvent.ascii = 128 + 10; + break; + case SDLK_F12: + keyEvent.ascii = 128 + 11; + break; + case SDLK_KP_DIVIDE: + keyEvent.ascii = 128 + 12; + break; + case SDLK_KP_MULTIPLY: + keyEvent.ascii = 128 + 13; + break; + case SDLK_KP_MINUS: + keyEvent.ascii = 128 + 14; + break; + case SDLK_KP_PLUS: + keyEvent.ascii = 128 + 15; + break; + case SDLK_KP_ENTER: + keyEvent.ascii = 128 + 16; + break; + case SDLK_KP_DECIMAL: + keyEvent.ascii = 128 + 17; + break; + case SDLK_KP_0: + keyEvent.ascii = 128 + 18; + break; + } + } + + Uint16 mod = ev.key.keysym.mod; + + if (mod & KMOD_SHIFT) + keyEvent.modifiers |= KB_MOD_SHIFT; + if (mod & KMOD_CTRL) + keyEvent.modifiers |= KB_MOD_CTRL; + if (mod & KMOD_ALT) + keyEvent.modifiers |= KB_MOD_ALT; + + if (ev.key.state == SDL_PRESSED) { + if (ev.key.keysym.sym == SDLK_RETURN && mod & KMOD_ALT) { + toggleFullScreen(); + break; + } + + // handle non-printable or ctrl'd or alt'd keys here + // other cases are handled by text input event below + if (ev.key.keysym.sym < 32 || ev.key.keysym.sym > 126 || (mod & KMOD_CTRL) || (mod & KMOD_ALT)) { + keyEvent.state = KBS_DOWN; + addKBevent(&keyEvent); + + sshockKeyStates[c] = keyEvent.modifiers | KB_MOD_PRESSED; + } + } else { + // key up following text input event case below is handled here + + keyEvent.state = KBS_UP; + addKBevent(&keyEvent); + + sshockKeyStates[c] = 0; + } + } + + // hack to allow pressing shift after move key + // sets all current shock states in array to shifted or non-shifted + if (ev.key.keysym.sym == SDLK_LSHIFT || ev.key.keysym.sym == SDLK_RSHIFT) { + for (int i = 0; i < 256; i++) + if (sshockKeyStates[i]) { + if (ev.key.state == SDL_PRESSED) + sshockKeyStates[i] |= KB_MOD_SHIFT; + else + sshockKeyStates[i] &= ~KB_MOD_SHIFT; + } + } + } break; + + case SDL_TEXTINPUT: { + uint32_t len = strlen(ev.text.text); + + // for every utf8 char in null-terminated string + for (uint32_t i = 0; i < len; i++) { + int ch = ev.text.text[i]; + + // ignore if non-printable key + if (!isprint(ch)) + continue; + + kbs_event keyEvent = {0}; + + keyEvent.modifiers = 0; + + // if uppercase, lower it and set shift modifier + if (isupper(ch)) { + ch = tolower(ch); + keyEvent.modifiers |= KB_MOD_SHIFT; + } + + // get code for this printable ascii key + int c = Ascii2Code[ch - 32]; + + keyEvent.code = c; + keyEvent.ascii = ch; + + // this is a key down event; key up will be handled in event case above + keyEvent.state = KBS_DOWN; + addKBevent(&keyEvent); + + sshockKeyStates[c] = keyEvent.modifiers | KB_MOD_PRESSED; + } + } break; + + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: { + bool down = (ev.button.state == SDL_PRESSED); + ss_mouse_event mouseEvent = {0}; + mouseEvent.type = 0; + + // TODO: the old mac code used to emulate right mouse clicks if space, enter, or return + // was pressed at the same time - do the same? (=> could check sshockKeyStates[]) + + mouseEvent.buttons = 0; + + switch (ev.button.button) { + case SDL_BUTTON_LEFT: + mouseEvent.type = down ? MOUSE_LDOWN : MOUSE_LUP; + mouseEvent.buttons |= down ? (1 << MOUSE_LBUTTON) : 0; + break; + + case SDL_BUTTON_RIGHT: + mouseEvent.type = down ? MOUSE_RDOWN : MOUSE_RUP; + mouseEvent.buttons |= down ? (1 << MOUSE_RBUTTON) : 0; + break; + + // case SDL_BUTTON_MIDDLE: // TODO: is this MOUSE_CDOWN/UP ? + // break; + } + + if (mouseEvent.type != 0) { + bool shifted = ((SDL_GetModState() & KMOD_SHIFT) != 0); + + mouseEvent.x = MouseX; + mouseEvent.y = MouseY; + mouseEvent.timestamp = mouse_get_time(); + mouseEvent.modifiers = (shifted ? 1 : 0); + addMouseEvent(&mouseEvent); + } + } break; + + case SDL_MOUSEMOTION: { + // call this first; it sets MouseX and MouseY + if (SDL_GetRelativeMouseMode() == SDL_TRUE) + SetMouseXY(MouseX + ev.motion.xrel, MouseY + ev.motion.yrel); + else + SetMouseXY(ev.motion.x, ev.motion.y); + + ss_mouse_event mouseEvent = {0}; + mouseEvent.type = MOUSE_MOTION; + mouseEvent.x = MouseX; + mouseEvent.y = MouseY; + mouseEvent.buttons = 0; + if (ev.motion.state & SDL_BUTTON_LMASK) + mouseEvent.buttons |= (1 << MOUSE_LBUTTON); + if (ev.motion.state & SDL_BUTTON_RMASK) + mouseEvent.buttons |= (1 << MOUSE_RBUTTON); + mouseEvent.timestamp = mouse_get_time(); + addMouseEvent(&mouseEvent); + + if (TriggerRelMouseMode) { + TriggerRelMouseMode = FALSE; + + SDL_SetRelativeMouseMode(SDL_TRUE); + // throw away this first relative mouse reading + int mvelx, mvely; + get_mouselook_vel(&mvelx, &mvely); + } + } break; + + case SDL_MOUSEWHEEL: + if (ev.wheel.y != 0) { + ss_mouse_event mouseEvent = {0}; + mouseEvent.type = ev.wheel.y < 0 ? MOUSE_WHEELDN : MOUSE_WHEELUP; + mouseEvent.x = MouseX; + mouseEvent.y = MouseY; + mouseEvent.buttons = 0; + mouseEvent.timestamp = mouse_get_time(); + addMouseEvent(&mouseEvent); + } + break; + + case SDL_WINDOWEVENT: + switch (ev.window.event) { + case SDL_WINDOWEVENT_SIZE_CHANGED: + if (can_use_opengl()) + opengl_resize(ev.window.data1, ev.window.data2); + break; + + case SDL_WINDOWEVENT_MOVED: + case SDL_WINDOWEVENT_RESIZED: + break; + + case SDL_WINDOWEVENT_FOCUS_GAINED: + SDL_SetRelativeMouseMode(saved_rel_mouse); + if (saved_rel_mouse == SDL_TRUE) { + // throw away this first relative mouse reading + int mvelx, mvely; + get_mouselook_vel(&mvelx, &mvely); + } + SDL_ShowCursor(SDL_DISABLE); + break; + + case SDL_WINDOWEVENT_FOCUS_LOST: + saved_rel_mouse = SDL_GetRelativeMouseMode(); + SDL_SetRelativeMouseMode(SDL_FALSE); + SDL_ShowCursor(SDL_ENABLE); + break; + } + break; + } + } +} + +//=============================================================== +// +// This section is adapted from: +// kbMac.c - All the keyboard handling routines that are specific to the Macintosh. +// +//=============================================================== + +//------------------ +// Globals +//------------------ +int pKbdStatusFlags; + +//--------------------------------------------------------------- +// Startup and keyboard handlers and initialize globals. Shutdown follows. +//--------------------------------------------------------------- +int kb_startup(void *v) { + pKbdStatusFlags = 0; + + memset(sshockKeyStates, 0, sizeof(sshockKeyStates)); + nextKBevent = 0; + + return (0); +} + +int kb_shutdown(void) { return (0); } + +//--------------------------------------------------------------- +// Get and set the global flags. +//--------------------------------------------------------------- +int kb_get_flags() { return (pKbdStatusFlags); } + +void kb_set_flags(int flags) { pKbdStatusFlags = flags; } + +//--------------------------------------------------------------- +// Get the next available key from the event queue. +//--------------------------------------------------------------- +kbs_event kb_next(void) { + kbs_event retEvent = kb_look_next(); + // kb_look_next() doesn't remove events from the queue, this function does, + // right here (but only if there actually was an event in the queue, of course): + if (nextKBevent > 0) { + --nextKBevent; + memmove(&kbEvents[0], &kbEvents[1], sizeof(kbs_event) * (kNumKBevents - 1)); + } + return retEvent; + +#if 0 + bool gotKey = FALSE; + EventRecord theEvent; + while(!gotKey) + { + gotKey = GetOSEvent(keyDownMask | autoKeyMask, &theEvent); // Get a key + if (gotKey) + { + retEvent.code = (uchar)(theEvent.message >> 8); // keyCodeMask == 0x0000FF00 + retEvent.state = KBS_DOWN; + retEvent.ascii = (uchar)(theEvent.message & charCodeMask); + retEvent.modifiers = (uchar)(theEvent.modifiers >> 8); + } + else if ((flags & KBF_BLOCK) == 0) // If there was no key and we're + return (retEvent); // not blocking, then return. + } + return (retEvent); +#endif +} + +//--------------------------------------------------------------- +// See if there is a key waiting in the queue. +//--------------------------------------------------------------- +kbs_event kb_look_next(void) { + kbs_event retEvent = {0xFF, 0x00}; + + int flags = kb_get_flags(); + if (flags & KBF_BLOCK) { + while (nextKBevent == 0) { + pump_events(); + } + } + + if (nextKBevent > 0) { + retEvent = kbEvents[0]; + } + return retEvent; + +#if 0 + bool gotKey = FALSE; + EventRecord theEvent; + while(!gotKey) + { + gotKey = OSEventAvail(keyDownMask | autoKeyMask, &theEvent); // Get a key + if (gotKey) + { + retEvent.code = (uchar)(theEvent.message >> 8); + retEvent.state = KBS_DOWN; + retEvent.ascii = (uchar)(theEvent.message & charCodeMask); + retEvent.modifiers = (uchar)(theEvent.modifiers >> 8); + } + else if (flags & KBF_BLOCK == 0) // If there was no key and we're + return (retEvent); // not blocking, then return. + } + return (retEvent); +#endif +} + +//--------------------------------------------------------------- +// Flush keyboard events from the event queue. +//--------------------------------------------------------------- +void kb_flush(void) { + // http://mirror.informatimago.com/next/developer.apple.com/documentation/Carbon/Reference/Event_Manager/event_mgr_ref/function_group_5.html#//apple_ref/c/func/FlushEvents + // FlushEvents(keyDownMask | autoKeyMask, 0); + + SDL_FlushEvents(SDL_KEYDOWN, SDL_KEYUP); // Note: that's a range! + + nextKBevent = 0; // this flushes the keyboard events already buffered - TODO is that desirable? +} + +//--------------------------------------------------------------- +// Return the state of the indicated key (scan code). +//--------------------------------------------------------------- + +uchar kb_state(uchar code) { + // see + // http://mirror.informatimago.com/next/developer.apple.com/documentation/Carbon/Reference/Event_Manager/event_mgr_ref/function_group_4.html#//apple_ref/c/func/GetKeys + // GetKeys((UInt32 *) pKbdGetKeys); + // return ((pKbdGetKeys[code>>3] >> (code & 7)) & 1); + + return sshockKeyStates[code] != 0; +} + +//--------------------------- +// +// MOUSE STUFF +// +//--------------------------- + +// --------------------------------------------------------- +// mouse_next gets the event in the front event queue, +// and removes the event from the queue. +// res = ptr to event to be filled. +// --------------------------------------------------------- +// For Mac version: Get event from the normal Mac event queue for mouse events. +// The events looked for depend on the 'mouseMask' setting. + +uchar btn_left = FALSE; +uchar btn_right = FALSE; +errtype mouse_next(ss_mouse_event *res) { + if (nextMouseEvent <= 0) + return ERR_DUNDERFLOW; + + *res = mouseEvents[0]; + + --nextMouseEvent; + memmove(&mouseEvents[0], &mouseEvents[1], sizeof(ss_mouse_event) * (kNumMouseEvents - 1)); + + return OK; +} + +errtype mouse_flush(void) { + // FlushEvents(mouseDown | mouseUp, 0); + // Spew(DSRC_MOUSE_Flush,("Entering mouse_flush()\n")); + // mouseQueueIn = mouseQueueOut = 0; + nextMouseEvent = 0; + // TODO: anything else? + return OK; +} + +errtype mouse_get_xy(short *x, short *y) { + *x = MouseX; + *y = MouseY; + + return OK; +} + +void middleize_mouse(void) { + int w, h; + SDL_RenderGetLogicalSize(renderer, &w, &h); + + MouseX = latestMouseEvent.x = w / 2; + MouseY = latestMouseEvent.y = h / 2; +} + +void get_mouselook_vel(int *vx, int *vy) { + if (SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE) + *vx = *vy = 0; + else { + SDL_GetRelativeMouseState(vx, vy); + + *vx += MouseChaosX; + MouseChaosX = 0; + *vy += MouseChaosY; + MouseChaosY = 0; + } +} + +errtype mouse_put_xy(short x, short y) { + MouseX = x; + MouseY = y; + + return OK; +} + +void set_mouse_chaos(short dx, short dy) { + MouseChaosX = dx; + MouseChaosY = dy; +} + +void sdl_mouse_init(void) { nextMouseEvent = 0; } diff --git a/engine/src/Libraries/INPUT/Source/sdl_events.h b/engine/src/Libraries/INPUT/Source/sdl_events.h new file mode 100644 index 0000000..e6c8ced --- /dev/null +++ b/engine/src/Libraries/INPUT/Source/sdl_events.h @@ -0,0 +1,27 @@ +/* + +Copyright (C) 2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef SDL_EVENT_H +#define SDL_EVENT_H + +// Ingest SDL events into internal Event system +void pump_events(void); +void set_mouse_chaos(short dx, short dy); + +#endif diff --git a/engine/src/Libraries/LG/Source/LOG/LICENSE b/engine/src/Libraries/LG/Source/LOG/LICENSE new file mode 100644 index 0000000..7e3bf17 --- /dev/null +++ b/engine/src/Libraries/LG/Source/LOG/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2017 rxi + +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. diff --git a/engine/src/Libraries/LG/Source/LOG/README.md b/engine/src/Libraries/LG/Source/LOG/README.md new file mode 100644 index 0000000..a5b7e88 --- /dev/null +++ b/engine/src/Libraries/LG/Source/LOG/README.md @@ -0,0 +1,70 @@ +# log.c +A simple logging library implemented in C99 + +![screenshot](https://cloud.githubusercontent.com/assets/3920290/23831970/a2415e96-0723-11e7-9886-f8f5d2de60fe.png) + + +## Usage +**[log.c](src/log.c?raw=1)** and **[log.h](src/log.h?raw=1)** should be dropped +into an existing project and compiled along with it. The library provides 6 +function-like macros for logging: + +```c +log_trace(const char *fmt, ...); +log_debug(const char *fmt, ...); +log_info(const char *fmt, ...); +log_warn(const char *fmt, ...); +log_error(const char *fmt, ...); +log_fatal(const char *fmt, ...); +``` + +Each function takes a printf format string followed by additional arguments: + +```c +log_trace("Hello %s", "world") +``` + +Resulting in a line with the given format printed to stderr: + +``` +20:18:26 TRACE src/main.c:11: Hello world +``` + + +#### log_set_quiet(int enable) +Quiet-mode can be enabled by passing `1` to the `log_set_quiet()` function. +While this mode is enabled the library will not output anything to stderr, but +will continue to write to the file if one is set. + + +#### log_set_level(int level) +The current logging level can be set by using the `log_set_level()` function. +All logs below the given level will be ignored. By default the level is +`LOG_TRACE`, such that nothing is ignored. + + +#### log_set_fp(FILE *fp) +A file pointer where the log should be written can be provided to the library by +using the `log_set_fp()` function. The data written to the file output is +of the following format: + +``` +2047-03-11 20:18:26 TRACE src/main.c:11: Hello world +``` + + +#### log_set_lock(log_LockFn fn) +If the log will be written to from multiple threads a lock function can be set. +The function is passed a `udata` value (set by `log_set_udata()`) and the +integer `1` if the lock should be acquired or `0` if the lock should be +released. + + +#### LOG_USE_COLOR +If the library is compiled with `-DLOG_USE_COLOR` ANSI color escape codes will +be used when printing. + + +## License +This library is free software; you can redistribute it and/or modify it under +the terms of the MIT license. See [LICENSE](LICENSE) for details. diff --git a/engine/src/Libraries/LG/Source/LOG/src/log.c b/engine/src/Libraries/LG/Source/LOG/src/log.c new file mode 100644 index 0000000..7c74ec0 --- /dev/null +++ b/engine/src/Libraries/LG/Source/LOG/src/log.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2017 rxi + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include "log.h" + +static struct { + void *udata; + log_LockFn lock; + FILE *fp; + int level; + int quiet; +} L; + + +static const char *level_names[] = { + "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" +}; + +#ifdef LOG_USE_COLOR +static const char *level_colors[] = { + "\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m" +}; +#endif + + +static void lock(void) { + if (L.lock) { + L.lock(L.udata, 1); + } +} + + +static void unlock(void) { + if (L.lock) { + L.lock(L.udata, 0); + } +} + + +void log_set_udata(void *udata) { + L.udata = udata; +} + + +void log_set_lock(log_LockFn fn) { + L.lock = fn; +} + + +void log_set_fp(FILE *fp) { + L.fp = fp; +} + + +void log_set_level(int level) { + L.level = level; +} + + +void log_set_quiet(int enable) { + L.quiet = enable ? 1 : 0; +} + + +void log_log(int level, const char *file, int line, const char *fmt, ...) { + if (level < L.level) { + return; + } + + /* Acquire lock */ + lock(); + + /* Get current time */ + time_t t = time(NULL); + struct tm *lt = localtime(&t); + + /* Log to stderr */ + if (!L.quiet) { + va_list args; + char buf[16]; + buf[strftime(buf, sizeof(buf), "%H:%M:%S", lt)] = '\0'; +#ifdef LOG_USE_COLOR + fprintf( + stderr, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", + buf, level_colors[level], level_names[level], file, line); +#else + fprintf(stderr, "%s %-5s %s:%d: ", buf, level_names[level], file, line); +#endif + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); + } + + /* Log to file */ + if (L.fp) { + va_list args; + char buf[32]; + buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt)] = '\0'; + fprintf(L.fp, "%s %-5s %s:%d: ", buf, level_names[level], file, line); + va_start(args, fmt); + vfprintf(L.fp, fmt, args); + va_end(args); + fprintf(L.fp, "\n"); + } + + /* Release lock */ + unlock(); +} diff --git a/engine/src/Libraries/LG/Source/LOG/src/log.h b/engine/src/Libraries/LG/Source/LOG/src/log.h new file mode 100644 index 0000000..b3df494 --- /dev/null +++ b/engine/src/Libraries/LG/Source/LOG/src/log.h @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2017 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See `log.c` for details. + */ + +#ifndef LOG_H +#define LOG_H + +#include +#include + +#define LOG_VERSION "0.1.0" + +typedef void (*log_LockFn)(void *udata, int lock); + +enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; + +#define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__) +#define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) +#define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) +#define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__) +#define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__) +#define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__) + +void log_set_udata(void *udata); +void log_set_lock(log_LockFn fn); +void log_set_fp(FILE *fp); +void log_set_level(int level); +void log_set_quiet(int enable); + +void log_log(int level, const char *file, int line, const char *fmt, ...); + +#endif diff --git a/engine/src/Libraries/LG/Source/dbg.h b/engine/src/Libraries/LG/Source/dbg.h new file mode 100644 index 0000000..c1bab25 --- /dev/null +++ b/engine/src/Libraries/LG/Source/dbg.h @@ -0,0 +1,398 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// DBG.H Error/debug system +// Rex E. Bradford (REX) +/* +* $Header: r:/prj/lib/src/lg/rcs/dbg.h 1.24 1994/08/12 17:03:24 jak Exp $ +* $Log: dbg.h $ + * Revision 1.24 1994/08/12 17:03:24 jak + * Split DbgHandleC() off DbgHandle() + * Externed some things for use by dbgpp.cc + * Split up decl of DbgSetReportRoutine() to make + * C++ parser happy + * + * Revision 1.23 1994/08/11 10:32:00 dfan + * Make C++ compatible + * + * Revision 1.22 1994/03/11 11:32:52 dfan + * DBGS shouldn't have been do/whiled + * + * Revision 1.21 1994/03/10 15:26:52 eric + * Fixed bug in Spew -- wasn't wrapped in do {} while (0), so statements like + * if (condition) Spew(("Hello\n")); else Error(1, "Bye\n"); + * would be evaluated incorrectly. (else would be bound to wrong if() ). + * + * Revision 1.20 1993/09/22 19:05:42 jak + * Oops. Wrong # of args for Assrt(). + * + * Revision 1.19 1993/09/22 18:51:34 jak + * Added null 'Assrt()' macro, + * + * Revision 1.18 1993/09/16 10:02:35 dfan + * When warnings and spews not on, replace them by do{}while(0), not nothing + * Otherwise strange things happen, for instance in conditionals + * + * Revision 1.17 1993/08/11 14:54:23 dfan + * There was no DBGS macro if DBG_ON wasn't defined + * Removed some sarcastic comments in the interest of promoting love and harmony + * + * Revision 1.16 1993/08/10 22:44:44 dc + * move around stuff to get stuff working for assembler code + * + * Revision 1.15 1993/08/10 21:36:19 dc + * attempt to fix broken ifdef nesting from r1.13 on the 9th of July + * when dbg macros for asm source files were broken + * but i cant test it since i need to make install to really do an h2i + * so we will see what happens + * + * Revision 1.14 1993/07/26 10:27:45 jak + * Modified Assert() macro to have an ELSE clause so that it does not + * swallow up an ELSE clause in the caller's code. + * Added Assrt() macro to call Assert() with a default message + * for the lazy among us. + * + * Revision 1.13 1993/07/09 09:32:56 rex + * Added Assert(), made dummy macro set when DBG_ON is not defined + * + * Revision 1.12 1993/04/22 13:58:55 rex + * Changed mono config key install thingy from flag to func ptr + * + * Revision 1.11 1993/04/22 11:39:23 rex + * Added macro DbgUseKblib() + * + * Revision 1.10 1993/03/25 10:50:41 rex + * Made AtExit() into a macro, instead of function. + * + * Revision 1.9 1993/03/24 12:19:53 matt + * Fixed another stupid bug. You would think I would test these files + * before I checked them in. + * + * Revision 1.8 1993/03/24 12:15:37 matt + * Fixed stupid mistake + * + * Revision 1.7 1993/03/24 12:12:25 matt + * Added include for assembly macros + * + * Revision 1.6 1993/03/04 11:51:41 rex + * Fixed macros: DbgSetDbg(), DbgSetMono(), DbgSetFunc() + * + * Revision 1.5 1993/02/25 12:51:30 rex + * Changed exit-handling functions + * + * Revision 1.4 1993/02/17 11:17:49 matt + * Added new macro DBGS(), like DBG(), but based on spew flags + * + * Revision 1.3 1993/02/04 20:04:32 rex + * Changed DbgExit() to Exit(), etc. + * + * Revision 1.2 1993/01/29 17:30:25 rex + * Added arg to Error() + * + * Revision 1.1 1993/01/29 09:47:52 rex + * Initial revision + * +*/ + +#include + +#include "log.h" + +// Relative path workaround +#define THIS_FILE ((strrchr(__FILE__, '/') ?: __FILE__ - 1) + 1) + +/** + * Main function for logging, and helper functions + * @param level log level (LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL) + * @param string printf-like string with format + * @example LOGGER(LOG_TRACE, "My name is %s", name); +**/ +#define LOGGER(level, ...) log_log(level, THIS_FILE, __LINE__, __VA_ARGS__) +#define TRACE(...) log_log(LOG_TRACE, THIS_FILE, __LINE__, __VA_ARGS__) +#define DEBUG(...) log_log(LOG_DEBUG, THIS_FILE, __LINE__, __VA_ARGS__) +#define INFO(...) log_log(LOG_INFO, THIS_FILE, __LINE__, __VA_ARGS__) +#define WARN(...) log_log(LOG_WARN, THIS_FILE, __LINE__, __VA_ARGS__) +#define ERROR(...) log_log(LOG_ERROR, THIS_FILE, __LINE__, __VA_ARGS__) + +#ifdef SPEW_ON +#define Spew(src,msg) { printf(src); printf(": "); printf(msg); } +#define SpewArgs(src,msg,args) { printf(src); printf(": ", args); printf(msg); } +#else +#define Spew(src,msg) {} +#define SpewArgs(src,msg,args) {} +#endif + +// Define the only warning I want to do right now - KC +/* +#ifdef DBG_ON +#define Warning(msg) DoWarningMsg msg +void DoWarningMsg(char *msg); +#else +#define Warning(msg) do {} while (0) +#endif +*/ +#define Warning(msg) DoWarningMsg msg +void DoWarningMsg(char *msg); + +void DebugString(char* msg); +//void DebugString(char* msg); + +/* +#include +#include "types.h" + +// The 4 levels of reporting + +#define DBG_ERROR 3 // report & go to DOS +#define DBG_WARNUSER 2 // alert user somehow +#define DBG_WARNING 1 // warn developer +#define DBG_SPEW 0 // be happy + +// 'sources' combine a bank (0-31 in high 5 bits) and one or many of 27 lower +// 'slot' bits. You can make your own sources with DBGSRC() macro. +// The bank portion of a source can be extracted using DBGBANK(). + +#define NUM_DBG_BANKS 32 +#define NUM_DBG_SLOTS 27 +#define DBG_SLOT_MASK 0x07FFFFFF + +#define DBGSRC(bank,bits) (((bank)<>NUM_DBG_SLOTS) + +// If DBG_ON is defined, debug system is defined, else macros +// are used to compile calls and macros out + +// The DbgBank structure maintains most information about the +// run-time debugging desires of banks & slots. + +#define MAX_BANKNAMELEN 8 +#define MAX_SLOTNAMELEN 8 + +#define DG_DBG 0 +#define DG_MONO 1 +#define DG_FUNC 2 +#define DG_FILE 3 +#define DG_NUM 4 + +typedef struct { + ulong gate[DG_NUM]; // gates for: dbg, spew, warn, mono + uchar file_index[NUM_DBG_SLOTS]; // which log file for each slot + char bank_name[MAX_BANKNAMELEN+1]; // bank's name + char **ppBankSlotNames; // ptr to bank slot names + char pad[8]; // padding +} DbgBank; // 64 bytes each + +#ifdef DBG_ON +// this is here for C, and in ASM we have it hand coded into +// the dbgmacro.inc, because h2inc is so cool +extern DbgBank dbgBank[NUM_DBG_BANKS]; // 2K total bank storage + +// These macros set & get gates. + +#define DbgSetDbg(bank,slots) dbgBank[bank].gate[DG_DBG] = ((slots) & DBG_SLOT_MASK) +#define DbgGetDbg(bank) (dbgBank[bank].gate[DG_DBG]) +#define DbgSetMono(bank,slots) dbgBank[bank].gate[DG_MONO] = ((slots) & DBG_SLOT_MASK) +#define DbgGetMono(bank) (dbgBank[bank].gate[DG_MONO]) +#define DbgSetFunc(bank,slots) dbgBank[bank].gate[DG_FUNC] = ((slots) & DBG_SLOT_MASK) +#define DbgGetFunc(bank) (dbgBank[bank].gate[DG_FUNC]) + +// Each bank can be given a name, and an array of slot names also. +// This is used by human-operated config routines. + +#define DbgSetBankName(bank,name) strncpy(dbgBank[bank].bank_name,name,MAX_BANKNAMELEN); +void DbgSetSlotNames(int bank, char **namelist); +void DbgSetSlotName(int bank, int slot, char *name); +int DbgFindBankName(char *name); +int DbgFindSlotName(int bank, char *name); + +// Debug info can be directed to one of several files. This data structure +// keeps track of the debug files which are in use. All logfiles must be +// in the same directory. + +#define MAX_DBG_LOGFILENAME 12 + +typedef struct { + char name[MAX_DBG_LOGFILENAME+1]; + FILE *fp; + short refCount; +} DbgLogFile; + +#define NUM_DBG_LOGFILES 16 +extern DbgLogFile dbgLogFile[NUM_DBG_LOGFILES]; +extern char dbgLogPath[128]; +extern int errErrCode; + +// The DBG() macro is used to conditionally compile debugging code, +// based on DBG_ON, which we checked at the top of the file. +#define DBG(src,stuff) if ((dbgBank[DBGBANK(src)].gate[DG_DBG]&(src))==((src)&DBG_SLOT_MASK)) stuff + +// This macro is like DBG(), in that it conditionally compiles code, except +// it is based on the spew flag. It is useful, for example, if you have a +// for loop to print out an array. + +#ifdef SPEW_ON +#define DBGS(src,stuff) if (DbgSpewTest(src)) stuff +#else +#define DBGS(src,stuff) +#endif + +// The important macros: +// +// Error(char *msg, ...) - Fatal error +// WarnUser(char *msg, ...) - Warn User +// Warning(char *msg, ...) - Warn developer +// Assert(expr, char *msg, ...) - Test expression, warn if false +// Spew(flags, char *msg, ...) - Spew message + +#define Error DbgReportError +#define WarnUser DbgReportWarnUser + +#ifdef WARN_ON +#define Warning(msg) DbgReportWarning msg +#define Assert(expr,msg) if (!(expr)) DbgReportWarning msg ; else +#else +#define Warning(msg) do {} while (0) +#define Assert(expr,msg) do {} while (0) +#endif + +#define Assrt(expr) Assert(expr,("Assert in %s at line %d in %s\n", #expr, __LINE__, __FILE__)) + +#ifdef SPEW_ON +#define Spew(src,msg) do { if (DbgSpewTest(src)) DbgDoSpew msg; } while (0) +#else +#define Spew(src,msg) do {} while (0) +#endif + +// These are prototypes for the 4 reporting routines, which should be +// called via the macros given above, and not directly. + +#ifdef __cplusplus +extern "C" { +#endif + +void DbgReportError(int errcode, char *msg, ...); +void DbgReportWarnUser(char *msg, ...); +void DbgReportWarning(char *msg, ...); +uchar DbgSpewTest(ulong src); +void DbgDoSpew(char *msg, ...); + +// Set debug config screen to use function for getting keys + +extern int (*f_getch)(); +#define DbgInstallGetch(f) (f_getch = (f)) + +// All logfiles are written to the same directory, which defaults to +// the current directory, but can be changed. + +void DbgSetLogPath(char *path); + +// This routine sets the log file associated with a source. +// Opening, writing, and closing of the log file is automatic, as +// is sharing of files with the same name. + +uchar DbgSetLogFile(ulong src, char *name); + +// These are really internal things + +uchar DbgOpenLogFile(int index); +void DbgCloseLogFiles(); +void DbgHandle(int reportType, ulong src, char *buff); +extern char *dbgTags[]; + +// Set a routine to be called to present reports. + +// C++ doesn't like the following: +// +// void DbgSetReportRoutine(void (*f_warn)(int reportType, char *msg)); +// +// so instead we break it up into two parts: +// + +typedef void ReportRoutine(int reportType, char *msg); +void DbgSetReportRoutine(ReportRoutine *); + +// Allows user to configure debug system + +void DbgInit(); // auto-loads settings from "debug.dbg" +void DbgMonoConfig(); // let operator config on mono screen +uchar DbgAddConfigPath(char *path); // add path for finding config files +int DbgLoadConfig(char *fname); // load config file +int DbgSaveConfig(char *fname); // save config file + +#ifdef __cplusplus +} +#endif + +// note this is the else DBG_ON from the top of the file, sitting here all +// alone and lonely in the middle of the file +#else + +// If DBG_ON not defined, most macros and functions are macro'ed to +// nothing or (0). A few functions remain + +#define DbgSetDbg(bank,slots) +#define DbgGetDbg(bank) (0) +#define DbgSetMono(bank,slots) +#define DbgGetMono(bank) (0) +#define DbgSetFunc(bank,slots) +#define DbgGetFunc(bank) (0) +#define DbgSetBankName(bank,name) +#define DbgSetSlotNames(bank, namelist) +#define DbgSetSlotName(bank, slot, name) +#define DbgFindBankName(name) (0) +#define DbgFindSlotName(bank, name) (0) +#define DBG(src,stuff) +#define DBGS(src,stuff) +#define Error DbgReportError +#define WarnUser DbgReportWarnUser +#define Warning(msg) +#define Assert(expr,msg) +#define Assrt(expr) +#define Spew(src,msg) +void DbgReportError(int errcode, char *msg, ...); +void DbgReportWarnUser(char *msg, ...); +#define DbgInstallGetch(f) +#define DbgSetLogPath(path) +#define DbgSetLogFile(src, name) (0) +#define DbgOpenLogFile(index) (0) +#define DbgCloseLogFiles() +//#define DbgHandle(reportType,src,buff) +typedef void ReportRoutine(int reportType, char *msg); +void DbgSetReportRoutine(ReportRoutine *); +#define DbgInit() +#define DbgMonoConfig() +#define DbgAddConfigPath(path) (0) +#define DbgLoadConfig(fname) (0) +#define DbgSaveConfig(fname) (0) + +#endif + +// look ma, an important thing to do +#ifdef _H2INC //include assembly macros +#include "dbgmacro.h" //dummy file; converts to 'include dbgmacro.inc' +#endif + +// These routines are in exit.c, and handle exit functions. + +void Exit(int errcode, char *msg); // shut down with msg +#define AtExit(func) atexit(func); // add func to atexit list +void PrintExitMsg(); // prints exit message + +#define SetExitMsg(str) pExitMsg=str +extern char *pExitMsg; +*/ diff --git a/engine/src/Libraries/LG/Source/lg.h b/engine/src/Libraries/LG/Source/lg.h new file mode 100644 index 0000000..a0305b1 --- /dev/null +++ b/engine/src/Libraries/LG/Source/lg.h @@ -0,0 +1,93 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// LG.H Looking Glass Over-Arching Master Control Header File +// +/* +* $Header: n:/project/lib/src/lg/rcs/lg.h 1.3 1993/08/06 11:00:11 rex Exp $ +* $Log: lg.h $ + * Revision 1.3 1993/08/06 11:00:11 rex + * Removed libdbg.h + * + * Revision 1.2 1993/03/19 18:20:22 rex + * Added RCS header + * +*/ + +#ifndef __TYPES_H +#include "lg_types.h" +#endif + +#include +#include "log.h" + +// Relative path workaround +#define THIS_FILE ((strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') : __FILE__ - 1) + 1) + +/** + * Main function for logging, and helper functions + * @param level log level (LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL) + * @param string printf-like string with format + * @example LOGGER(LOG_TRACE, "My name is %s", name); +**/ +#define LOGGER(level, ...) log_log(level, THIS_FILE, __LINE__, __VA_ARGS__) +#define TRACE(...) log_log(LOG_TRACE, THIS_FILE, __LINE__, __VA_ARGS__) +#define DEBUG(...) log_log(LOG_DEBUG, THIS_FILE, __LINE__, __VA_ARGS__) +#define INFO(...) log_log(LOG_INFO, THIS_FILE, __LINE__, __VA_ARGS__) +#define WARN(...) log_log(LOG_WARN, THIS_FILE, __LINE__, __VA_ARGS__) +#define ERROR(...) log_log(LOG_ERROR, THIS_FILE, __LINE__, __VA_ARGS__) + +// DG: helpful for seeing which stubbed out things are even used +// prints a stub message (incl. containing function) +#define STUB(msg) \ + printf("STUB: %s() %s\n", __FUNCTION__, msg); + +// prints a stub message (incl. containing function) only the first time it's called +#define STUB_ONCE(msg) do { \ + static int show=1; \ + if(show) { \ + show = 0; \ + printf("STUB: %s() %s\n", __FUNCTION__, msg); \ + } \ +} while(0); + +// For mac version. +#define lg_max(a,b) (((a) > (b)) ? (a) : (b)) +#define lg_min(a,b) (((a) < (b)) ? (a) : (b)) + +#define LG_memset memset +#define LG_memcpy memcpy +#define LG_memmove memmove +//#define BlockMove(src, dest, num) LG_memmove(src, dest, num); + +#define GAMEONLY 1 +#define SVGA_SUPPORT 1 +#define USE_STEALTH 1 +#define USE_PFIELD 1 +#define DISTANCE_AI_KILL 1 +#define TEXTURE_SELECTION 1 +#define NO_HELP_STRINGS 1 +#define NO_CORRUPT_SAVES 1 +#define MAP_RESHIFTING 1 +#define DIRAC_EDMS 1 +#define NO_ANTIGRAV_CRATES 1 +#define DOOM_EMULATION_MODE 1 +#define EDMS_SAFETY_NET 1 +#define AUDIOLOGS 1 +#define SVGA_CUTSCENES 1 +#define LOST_TREASURES_OF_MFD_GAMES 1 diff --git a/engine/src/Libraries/LG/Source/memall.c b/engine/src/Libraries/LG/Source/memall.c new file mode 100644 index 0000000..1e7df4d --- /dev/null +++ b/engine/src/Libraries/LG/Source/memall.c @@ -0,0 +1,395 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Memall.C Memory Allocation module +// Rex E. Bradford (REX) +// +// Memall provides a very simple mechanism for "installable memory +// allocators". The default allocator is malloc()/realloc()/free(). +// Clients may install and de-install their own handlers, in a pushdown +// stack so that old allocators can be re-activated when the new allocator +// is no longer needed. +// +// To install a new allocator set, call: +// +// MemPushAllocator(f_malloc, f_realloc, f_free); +// +// To deinstall an allocator set, call: +// +// MemPopAllocator(); +// +// A set of allocators is provided which features automatic checking for +// allocation failure. A convenience routine is provided to install +// these: +// +// MemCheckOn(uchar hard); // will Error() or Warn() based on flag +// +// To turn checking off, use: +// +// MemCheckOff(); +// +// Also, a set of routines to malloc, realloc, and free conventional +// (below 1 Mb) memory blocks is provided. Since the regular memory +// allocation system can usurp conventional memory, systems which need +// conventional memory should allocate it early on in a program. + +/* +* $Header: n:/project/lib/src/lg/rcs/memall.c 1.7 1993/08/11 18:43:55 rex Exp $ +* $log$ +*/ + +#include +//#include +#include "memall.h" +#include +//#include "_lg.h" + +// Allocator set structure + +typedef struct { + void *(*func_malloc)(size_t size); // allocator func + void *(*func_realloc)(void *p, size_t size); // realloc func + void (*func_free)(void *p); // de-allocator func +} MemAllocSet; + +// Allocator set stack + +#define MAX_ALLOCATORS 4 +static MemAllocSet memAllocStack[MAX_ALLOCATORS] = { + malloc, realloc, free, +}; +static int memIndexAllocStack = 0; + +// Current allocator ptrs + +void *(*f_malloc)(size_t size) = malloc; +void *(*f_realloc)(void *p, size_t size) = realloc; +void (*f_free)(void *p) = free; + +// Miscellaneous + +static uchar memHardCheck; // if checking on, use Error or Warning? + +#define INT_DPMI 0x31 // intr for Dos Protected Mode Interface + +// Internal prototypes + +void *MallocChecked(size_t size); +void *ReallocChecked(void *p, size_t size); + +// -------------------------------------------------------------- +// SETTING, PUSHING, & POPPING ALLOCATOR SETS +// -------------------------------------------------------------- +// +// MemSetAllocator() sets the current allocator set. +// +// fm = ptr to allocator function +// ff = ptr to free function +// fr = ptr to realloc function + +void MemSetAllocator(void *(*fm)(size_t size), + void *(fr)(void *p, size_t size), void (*ff)(void *p)) +{ + MemAllocSet *pmas; + + pmas = &memAllocStack[memIndexAllocStack]; + f_malloc = pmas->func_malloc = fm; + f_realloc = pmas->func_realloc = fr; + f_free = pmas->func_free = ff; +} + +// -------------------------------------------------------------- +// +// MemPushAllocator() pushes old allocators, sets new one. +// +// fm = ptr to allocator function +// ff = ptr to free function +// fr = ptr to realloc function +// +// Returns: 0 if successful, -1 if allocations stack full + +int MemPushAllocator(void *(*fm)(size_t size), + void *(fr)(void *p, size_t size), void (*ff)(void *p)) +{ + if (memIndexAllocStack >= (MAX_ALLOCATORS - 1)) + return(-1); + + ++memIndexAllocStack; + MemSetAllocator(fm, fr, ff); + return(0); +} + +// --------------------------------------------------------------- +// +// MemPopAllocator() pops most recent allocator. +// +// Returns: 0 if successful, -1 if allocations stack underflow + +int MemPopAllocator() +{ + MemAllocSet *pmas; + + if (memIndexAllocStack <= 0) + return(-1); + + --memIndexAllocStack; + pmas = &memAllocStack[memIndexAllocStack]; + f_malloc = pmas->func_malloc; + f_realloc = pmas->func_realloc; + f_free = pmas->func_free; + return(0); +} + +// --------------------------------------------------------------- +// CALLOC - NONDEBUG VERSION +// --------------------------------------------------------------- +// +// CallocNorm() allocates with Malloc(), then clears to 0. +// +// size = # bytes to allocate and clear + +void *CallocNorm(size_t size) +{ + void *p = (*f_malloc)(size); + if (p) + memset(p, 0, size); + return(p); +} + +// --------------------------------------------------------------- +// SPEW VERSIONS +// --------------------------------------------------------------- +// +// MallocSpew() does Malloc() and spews. + +#ifdef DBG_ON + +void *MallocSpew(size_t size, char *file, int line) +{ + void *p = (*f_malloc)(size); + Spew(DSRC_LG_Memall, ("Malloc: p: 0x%x size: %d (file: %s line: %d)\n", + p, size, file, line)); + return(p); +} + +// --------------------------------------------------------------- +// +// ReallocSpew() does Realloc() and spews. + +void *ReallocSpew(void *p, size_t size, char *file, int line) +{ + void *pnew = (*f_realloc)(p,size); + Spew(DSRC_LG_Memall, ("Realloc: p: 0x%x pold: 0x%x size: %d (file: %s line: %d)\n", + pnew, p, size, file, line)); + return(pnew); +} + +// --------------------------------------------------------------- +// +// FreeSpew() does Free() and spews. + +void FreeSpew(void *p, char *file, int line) +{ + (*f_free)(p); + Spew(DSRC_LG_Memall, ("Free: p: 0x%x (file: %s line: %d)\n", + p, file, line)); +} + +// --------------------------------------------------------------- +// +// CallocSpew() does Calloc() and spews. + +void *CallocSpew(size_t size, char *file, int line) +{ + void *p = (*f_malloc)(size); + if (p) + memset(p, 0, size); + Spew(DSRC_LG_Memall, ("Calloc: p: 0x%x size: %d (file: %s line: %d)\n", + p, size, file, line)); + return(p); +} + +#endif + +// --------------------------------------------------------------- +// CHECKED ALLOCATION +// --------------------------------------------------------------- +// +// MemCheckOn() turns on memory checking. +// +// hard = if TRUE, do hard error on alloc fail, else do warning + +void MemCheckOn(uchar hard) +{ + MemPushAllocator(MallocChecked, ReallocChecked, f_free); + memHardCheck = hard; +} + +// --------------------------------------------------------------- +// +// MemCheckOff() turns off memory checking. + +void MemCheckOff() +{ + MemPopAllocator(); +} + +// ---------------------------------------------------------- +// CONVENTIONAL MEMORY ALLOCATION +// ---------------------------------------------------------- +// +// MallocConvMemBlock() allocates conventional memory. It +// returns a protected mode ptr as well as filling in a useful +// structure, or returns NULL if unable to get the memory. +// +// size = size of memory block in bytes +// pcmb = ptr to ConvMemBlock structure (see res.h) +// +// Returns: far ptr to block in low memory, or NULL + +/*void *MallocConvMemBlock(ushort size, ConvMemBlock *pcmb) +{ + //union REGS regs; + +// Use DPMI to get the memory + + regs.x.eax = 0x0100; + regs.x.ebx = (size + 15) >> 4; + int386(INT_DPMI, ®s, ®s); + if (regs.x.cflag) + return(NULL); + +// Fill in our ConvMemBlock struct, return protected ptr + + pcmb->realSeg = regs.w.ax; + pcmb->protSel = regs.w.dx; +// pcmb->protPtr = MK_FP(pcmb->protSel, 0); // this is the non-flat memory way + pcmb->protPtr = (void *)(pcmb->realSeg << 4); + return(pcmb->protPtr); +}*/ + +// ---------------------------------------------------------- +// +// ReallocConvMemBlock() resizes a conventional memory block. +// +// pcmb = ptr to ConvMemBlock structure (see res.h) +// newsize = new size in bytes +// +// Returns: far ptr to realloc'ed block + +/*void *ReallocConvMemBlock(ConvMemBlock *pcmb, ushort newsize) +{ + //union REGS regs; + long realAddr; + + regs.x.eax = 0x0102; + regs.w.bx = (newsize + 15) >> 4; + regs.w.dx = pcmb->protSel; + int386(INT_DPMI, ®s, ®s); + if (regs.x.cflag) + return(NULL); + + regs.x.eax = 0x0006; + regs.w.bx = pcmb->protSel; + int386(INT_DPMI, ®s, ®s); + realAddr = (((long) regs.w.cx) << 16) + regs.w.dx; + pcmb->realSeg = realAddr >> 4; +// pcmb->protPtr = MK_FP(pcmb->protSel, 0); // this is the non-flat memory way + pcmb->protPtr = (void *)(pcmb->realSeg << 4); + + malloc() + + return(pcmb->protPtr); +}*/ + +// --------------------------------------------------------- +// +// FreeConvMemBlock() frees a conventional memory block. +// +// pcmb = ptr to ConvMemBlock structure (see res.h) +// +// Returns: 0 if successful, -1 if free failed + +/*int FreeConvMemBlock(ConvMemBlock *pcmb) +{ + //union REGS regs; + + regs.x.eax = 0x0101; + regs.w.dx = pcmb->protSel; + int386(INT_DPMI, ®s, ®s); + if (regs.x.cflag) + return(-1); + return(0); +}*/ + +// --------------------------------------------------------------- +// INTERNAL ROUTINES +// --------------------------------------------------------------- +// +// MallocChecked() calls the previously installed allocator, and +// checks for NULL. If underlying malloc failed, does hard error. +// +// size = # bytes to allocate +// +// Returns: ptr to memory block. + +#define MallocPrev(size) (memIndexAllocStack >= 0 ? (*memAllocStack[memIndexAllocStack-1].func_malloc)(size) : NULL) + +void *MallocChecked(size_t size) +{ + void *p; + + p = MallocPrev(size); + /*if (p == NULL) + { + if (memHardCheck) + Error(1, "MallocChecked: out of memory allocating %d bytes\n", size); + else + Warning(("MallocChecked: returning NULL (%d bytes requested)\n", size)); + }*/ + + return(p); +} + +// ---------------------------------------------------------------- +// +// ReallocChecked() calls the previously installed allocator, and +// checks for NULL. If underlying realloc failed, does hard error. +// +// p = ptr to existing block +// size = new size +// +// Returns: ptr to realloc'ed block. + +#define ReallocPrev(p,size) (memIndexAllocStack >= 0 ? (*memAllocStack[memIndexAllocStack-1].func_realloc)(p,size) : NULL) + +void *ReallocChecked(void *p, size_t size) +{ + void *pnew; + + pnew = ReallocPrev(p, size); + /*if (pnew == NULL) + { + if (memHardCheck) + Error(1, "ReallocChecked: out of memory reallocing %d bytes\n", size); + else + Warning(("ReallocChecked: returning NULL (%d bytes requested)\n", size)); + }*/ + + return(pnew); +} diff --git a/engine/src/Libraries/LG/Source/memall.h b/engine/src/Libraries/LG/Source/memall.h new file mode 100644 index 0000000..f38bd17 --- /dev/null +++ b/engine/src/Libraries/LG/Source/memall.h @@ -0,0 +1,183 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Memall.H Memory allocator +// Rex E. Bradford (REX) +/* +* $Header: n:/project/lib/src/lg/rcs/memall.h 1.12 1993/12/20 13:40:05 ept Exp $ +* $Log: memall.h $ + * Revision 1.12 1993/12/20 13:40:05 ept + * Added MemStackRealloc. + * + * Revision 1.11 1993/09/30 18:36:51 rex + * Added prototypes for memgrow.c stuff (heap grow, lock, unlock) + * + * Revision 1.10 1993/09/13 12:40:12 dfan + * ptr and size were reserved words in assembler + * + * Revision 1.9 1993/09/13 11:11:13 dfan + * Add memstack stuff + * + * Revision 1.8 1993/08/11 18:44:12 rex + * Changed Calloc() to macro, so can do spew + * + * Revision 1.7 1993/08/11 17:30:40 rex + * Added Spew() versions of malloc/realloc/free, and made uppercase macro + * versions point to them when DBG is on + * + * Revision 1.6 1993/04/16 12:59:55 matt + * Added some void's to functions that took no args to get around h2inc bug. + * + * Revision 1.5 1993/04/13 16:05:22 rex + * Added prototypes for MemCheckOn() and MemCheckOff() + * + * Revision 1.4 1993/03/24 12:02:44 matt + * More asm header junk + * + * Revision 1.3 1993/03/16 15:12:39 matt + * Added junk for H2INC translation to assembly header + * + * Revision 1.2 1993/02/05 17:38:07 rex + * Added Calloc() prototype + * + * Revision 1.1 1993/01/29 09:47:55 rex + * Initial revision + * + * Revision 1.3 1993/01/18 12:18:38 rex + * Changed interface to new standard we all agreed on + * + * Revision 1.2 1993/01/14 09:41:02 rex + * Decided Malloc() should never fail, changed interface. + * + * Revision 1.1 1993/01/12 17:54:54 rex + * Initial revision + * +*/ + + +#ifndef MEMALL_H +#define MEMALL_H + + +//#include +#include +#include "lg_types.h" + +/* +// Setting, pushing, & popping allocator sets + +void MemSetAllocator(void *(*fm)(size_t size), + void *(*fr)(void *p, size_t size), void (*ff)(void *p)); +int MemPushAllocator(void *(*fm)(size_t size), + void *(*fr)(void *p, size_t size), void (*ff)()); +int MemPopAllocator(void); + +// Allocating, reallocating, & freeing memory + +extern void *(*f_malloc)(size_t size); +extern void *(*f_realloc)(void *p, size_t size); +extern void (*f_free)(void *p); + +#ifdef DBG_ON + +void *MallocSpew(size_t size, char *file, int line); +void *ReallocSpew(void *p, size_t size, char *file, int line); +void FreeSpew(void *p, char *file, int line); +void *CallocSpew(size_t size, char *file, int line); + +#define Malloc(size) MallocSpew(size,__FILE__,__LINE__) +#define Realloc(p,size) ReallocSpew(p,size,__FILE__,__LINE__) +#define Free(p) FreeSpew(p,__FILE__,__LINE__) +#define Calloc(size) CallocSpew(size,__FILE__,__LINE__) + +#else + +void *CallocNorm(size_t size); + +#define Malloc(size) (*f_malloc)(size) +#define Realloc(p,size) (*f_realloc)(p,size) +#define Free(p) (*f_free)(p) +#define Calloc(size) CallocNorm(size) + +#endif + +#ifdef _H2INC //if translating, include assembly macros +#include "memmacro.h" //this will translate to 'include memmacro.inc' +#endif + +// Memory checking + +void MemCheckOn(uchar hard); +void MemCheckOff(void); + +// Heap management (memgrow.c) + +int MemGrowHeap(int wantK); +void MemLockHeap(); +void MemUnlockHeap(); + +// Calling previous (underlying) allocators (only from top level!) + +#define MallocPrev(size) (memIndexAllocStack >= 0 ? (*memAllocStack[memIndexAllocStack-1].func_malloc)(size) : NULL) +#define ReallocPrev(p,size) (memIndexAllocStack >= 0 ? (*memAllocStack[memIndexAllocStack-1].func_realloc)(p,size) : NULL) +#define FreePrev(p) (if (memIndexAllocStack >= 0) (*memAllocStack[memIndexAllocStack-1].func_free)(p)) + +// Allocating conventional memory +// Caveat: since Malloc() can grab conventional memory, necessary +// conventional memory blocks should be grabbed early in program. + +typedef struct { + ushort realSeg; // real mode segment to conventional mem block + ushort protSel; // protected mode selector for conv mem block + void far *protPtr; // protected mode ptr to mem block +} ConvMemBlock; + +void far *MallocConvMemBlock(ushort size, ConvMemBlock *pcmb); // alloc +void far *ReallocConvMemBlock(ConvMemBlock *pcmb, ushort newsize); // resize +int FreeConvMemBlock(ConvMemBlock *pcmb); // free low memory block +*/ + +////////////////////////////// +// +// Dealing with a large block of memory as a stack for easy allocation +// +// Rationale: often, routines want some large amount of memory, and know they +// will throw it away when they're done. If you use a MemStack for this memory, +// you don't have to worry about fragmenting the heap. +// +// To use, declare a MemStack. Malloc n bytes of memory, put the resulting +// pointer in baseptr and n in size, and call MemStackInit(). Then use +// MemStackAlloc() and MemStackFree() to grab and release memory. You must +// free memory in the reverse order of allocating it, as this is a stack. +// Note that you can have multiple MemStacks if you feel like it. + +typedef struct +{ + void *baseptr; // pointer to bottom of stack + long sz; // size of stack in bytes + void *topptr; // pointer to current top of stack (next free byte) +} +MemStack; + +void MemStackInit (MemStack *ms); +void *MemStackAlloc (MemStack *ms, long size); +void *MemStackRealloc (MemStack *ms, void *ptr, long newsize); +uchar MemStackFree (MemStack *ms, void *ptr); + + +#endif diff --git a/engine/src/Libraries/LG/Source/stack.c b/engine/src/Libraries/LG/Source/stack.c new file mode 100644 index 0000000..ca99bf1 --- /dev/null +++ b/engine/src/Libraries/LG/Source/stack.c @@ -0,0 +1,105 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* +** stack.c +** +** Routines for controlling user-defined stacks of large memory +** +** $Header: n:/project/lib/src/lg/rcs/stack.c 1.3 1993/12/20 13:40:17 ept Exp $ +** $Log: stack.c $ + * Revision 1.3 1993/12/20 13:40:17 ept + * Added MemStackRealloc. + * + * Revision 1.2 1993/09/13 12:40:25 dfan + * ptr and size were reserved words in assembler + * Warning if MemStackAlloc returns NULL + * + * Revision 1.1 1993/09/13 11:10:56 dfan + * Initial revision + * +*/ + +#include "lg.h" +#include "memall.h" + +////////////////////////////// +// +// Initializes a MemStack. The user must already have allocated the memory +// himself, and have set baseptr and size in the MemStack structure. +// +void MemStackInit (MemStack *ms) +{ + ms->topptr = ms->baseptr; +} + +////////////////////////////// +// +// Allocates size bytes of memory from the MemStack. +// +void *MemStackAlloc (MemStack *ms, long size) +{ + char *newptr = (char *) ms->topptr + size; + char *oldptr = (char *) ms->topptr; + + if (newptr > (char *) ms->baseptr + ms->sz) + { + WARN("%s: can't alloc", __FUNCTION__); + return NULL; + } + + ms->topptr = (void *) newptr; + return oldptr; +} + +////////////////////////////// +// +// Change the size of the ptr. Note that if this is not the last on +// the stack bad things will occur! +// +void *MemStackRealloc (MemStack *ms, void *ptr, long newsize) +{ + char *newptr = (char *)ptr + newsize; + + if (newptr > (char *)ms->baseptr + ms->sz) + { + WARN("%s: can't realloc", __FUNCTION__); + return NULL; + } + ms->topptr = (char *)ptr + newsize; + return ptr; +} + +////////////////////////////// +// +// Frees memory allocated with MemStackAlloc(). You must free memory in +// reverse order from allocating it - this is a stack. Violations may +// not be caught right away. +// +uchar MemStackFree (MemStack *ms, void *ptr) +{ + if (ms->topptr < ptr) + { + WARN("%s: freed in wrong order", __FUNCTION__); + return FALSE; + } + + // Return ms->topptr to where it was when we allocated ptr: namely, ptr + ms->topptr = ptr; + return TRUE; +} diff --git a/engine/src/Libraries/LG/Source/tmpalloc.c b/engine/src/Libraries/LG/Source/tmpalloc.c new file mode 100644 index 0000000..d806ac1 --- /dev/null +++ b/engine/src/Libraries/LG/Source/tmpalloc.c @@ -0,0 +1,168 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/lg/rcs/tmpalloc.c $ + * $Revision: 1.4 $ + * $Author: kaboom $ + * $Date: 1994/08/03 23:09:14 $ + * + * Routines for controlling temporary memory buffer. + * + * This file is part of the 2d library. + */ + +#include "lg.h" +#include "memall.h" +#include "tmpalloc.h" +//#include <_lg.h> + +/* arbitrary size for buffer. used if a buffer isn't explicitly set. */ +#define TEMP_BUF_SIZE 16384 + +/* memstack to use for temporary memory requests. */ +static MemStack *temp_mem_stack=NULL; + +/* TRUE if buffer is allocated by temp_mem_init. */ +static uchar stack_dynamic=FALSE; + +MemStack *temp_mem_get_stack(void) +{ + return temp_mem_stack; +} + +/* sets the memstack to be used by the temporary memory routines to ms. + if ms is NULL, it attempts to allocate a dynamic buffer of size given + by TEMP_BUF_SIZE. returns 0 if all is well, nonzero if there is an + error. */ +int temp_mem_init(MemStack *ms) +{ + if (ms==NULL) { + /* allocate memstack struct and buffer dynamically. */ +// Spew(DSRC_LG_Tempmem, +// ("TempMemInit: dynamically allocating stack of %d bytes\n", +// TEMP_BUF_SIZE)); +// if ((ms=(MemStack *)Malloc(sizeof(MemStack)+TEMP_BUF_SIZE))==NULL) { + if ((ms=(MemStack *)malloc(sizeof(MemStack)+TEMP_BUF_SIZE))==NULL) { + WARN("%s: can't allocate dynamic buffer.", __FUNCTION__); + return -1; + } + stack_dynamic=TRUE; + ms->baseptr=(void *)(ms+1); + ms->sz=TEMP_BUF_SIZE; + MemStackInit(ms); + temp_mem_stack=ms; /* save pointer to temp memstack */ + return 0; + } else { + /* use passed in memstack. */ + temp_mem_stack=ms; + return 0; + } +} + +/* sets the memstack used by the temporary memory routines to NULL. + if the buffer was allocated dynamically, it's freed. */ +int temp_mem_uninit(void) +{ + if (stack_dynamic==TRUE) { +// Spew(DSRC_LG_Tempmem, +// ("TempMemUninit: freeing dynamically allocated stack\n")); + free(temp_mem_stack); +// free((Ptr)temp_mem_stack); + stack_dynamic=FALSE; + } + temp_mem_stack=NULL; + return 0; +} + +/* allocate a temporary buffer of size n from temp_mem_stack. */ +void *temp_malloc(long n) +{ + if (temp_mem_stack==NULL) + if (temp_mem_init(NULL)!=0) + return NULL; + return MemStackAlloc(temp_mem_stack,n); +} + +/* resize temporary buffer pointed to by p to be new size n. */ +void *temp_realloc(void *p,long n) +{ + return MemStackRealloc(temp_mem_stack,p,n); +} + +/* free temporary buffer pointed to by p. */ +int temp_free(void *p) +{ + return MemStackFree(temp_mem_stack,p)==FALSE; +} + +#ifdef DBG_ON +/* the spewing versions of the temporary memory routines print out + additional information about the call to the real routine, including + the file name and line number where the call was made. */ +int temp_spew_mem_init(MemStack *ms,char *file,int line) +{ + int r; + r=temp_mem_init(ms); + Spew(DSRC_LG_Tempmem, + ("TempMemInit: stack: %p rval: %d (file: %s line: %d)\n", + temp_mem_stack,r,file,line)); + return r; +} + +int temp_spew_mem_uninit(char *file,int line) +{ + int r; + + r=temp_mem_uninit(); + Spew(DSRC_LG_Tempmem, + ("TempMemUninit rval: %d (file: %s line: %s)\n", + r,file,line)); + return r; +} + +void *temp_spew_malloc(long size,char *file,int line) +{ + void *p; + p=temp_malloc(size); + Spew(DSRC_LG_Tempmem, + ("TempMalloc: p: 0x%x size: %d (file: %s line: %d)\n", + p,size,file,line)); + return p; +} + +void *temp_spew_realloc(void *ptr,long size,char *file,int line) +{ + void *p; + p=temp_realloc(ptr,size); + Spew(DSRC_LG_Tempmem, + ("TempRealloc: p: 0x%x pold: 0x%x size: %d (file: %s line: %d)\n", + p,ptr,size,file,line)); + return p; +} + +int temp_spew_free(void *ptr,char *file,int line) +{ + int r; + r=temp_free(ptr); + Spew(DSRC_LG_Tempmem, + ("TempFree: p: 0x%x (file: %s line: %d)\n", + ptr,file,line)); + return r; +} +#endif /* DBG_ON */ diff --git a/engine/src/Libraries/LG/Source/tmpalloc.h b/engine/src/Libraries/LG/Source/tmpalloc.h new file mode 100644 index 0000000..179a631 --- /dev/null +++ b/engine/src/Libraries/LG/Source/tmpalloc.h @@ -0,0 +1,55 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/lg/rcs/tmpalloc.h $ + * $Revision: 1.4 $ + * $Author: kaboom $ + * $Date: 1994/08/03 23:09:27 $ + * + * Header for routines for controlling temporary stacks of big_buffer + * + * This file is part of the 2d library. + */ + +extern MemStack *temp_mem_get_stack(void); +extern int temp_mem_init(MemStack *ms); +extern int temp_mem_uninit(void); +extern void *temp_malloc(long n); +extern void *temp_realloc(void *p,long n); +extern int temp_free(void *p); + +#ifdef DBG_ON +extern int temp_spew_mem_init(MemStack *ms,char *file,int line); +extern int temp_spew_mem_uninit(char *file,int line); +extern void *temp_spew_malloc(long n,char *file,int line); +extern void *temp_spew_realloc(void *p,long n,char *file,int line); +extern int temp_spew_free(void *p,char *file,int line); + +#define TempMemInit(ms) temp_spew_mem_init(ms,__FILE__,__LINE__) +#define TempMemUninit() temp_spew_mem_uninit(__FILE__,__LINE__) +#define TempMalloc(n) temp_spew_malloc(n,__FILE__,__LINE__) +#define TempRealloc(p,n) temp_spew_realloc(p,n,__FILE__,__LINE__) +#define TempFree(p) temp_spew_free(p,__FILE__,__LINE__) +#else /* !DBG_ON */ +#define TempMemInit temp_mem_init +#define TempMemUninit temp_mem_uninit +#define TempMalloc temp_malloc +#define TempRealloc temp_realloc +#define TempFree temp_free +#endif /* DBG_ON */ diff --git a/engine/src/Libraries/PALETTE/Source/palette.c b/engine/src/Libraries/PALETTE/Source/palette.c new file mode 100644 index 0000000..955fc56 --- /dev/null +++ b/engine/src/Libraries/PALETTE/Source/palette.c @@ -0,0 +1,613 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/palette/RCS/palette.c $ + * $Revision: 1.34 $ + * $Author: minman $ + * $Date: 1994/08/15 04:49:03 $ + * + * + */ + +#include "palette.h" + +/* + * GLOBAL VARIABLES + * + * Palette_Effects_Table holds entries for different types of + * palette effects. Its size is user defined when the initializing + * routine is called. + * + * Shadow_Fixed_Cmap is a shadow colormap in fixed point, used to keep a + * tighter approximation in palette shifts. (Integer divides would make + * successive steps jerky). Delta_Cmap keeps fixed point track + * of the delta which should be added successively to shifting entries. + * + * Timestamps per step, also user-defined at initialization time, + * defines how how many timestamp increments should pass before a + * palette change is required. + */ + +PAL_TABLE_ENTRY *Palette_Effects_Table = NULL; +short Palette_Effects_Table_Size = 0; + +fix Shadow_Fixed_Cmap[768]; // r,g,b for each entry in colormap +fix Delta_Cmap[768]; // r,g,b delta shifts for same +uchar local_smap[768]; + +short timestamps_per_step; // How many ts units = 1 palette step +long last_timestamp = 0L; +short num_active_effects = 0; + +byte num_installed_shifts = 0; + +#ifndef REAL_PAL_SWAP_SHAD +// should really do a warning here for so people to understand a problem, she has happened +#define palette_swap_shadow(s,n,d) +#endif + +/* + * ROUTINES + */ + +/* + * ADVANCE ROUTINES + * + * The first routine advances a palette effect by the appropriate + * approximate delta, or increments a delay field for the entry. + * This routine is responsible for figuring out whether change + * should occur, and if so, how much change. It also executes + * that change. + * + * Note that for palette shifts, this routine will also notice if + * the effect is completed and will remove the effect from the + * effect table at that time. + * + * The second routine is the master routine, which should be called + * in every pass of the calling application's main loop. It + * figures out which effects are active, if any, and advances + * them as neccessary. The application programmer should never + * have to explicitly call the former advance routine: he will + * only screw up this library's internal bookkeeping. To + * advance an effect, install it and let the master "Advance All" + * routine take care of the execution. + */ + +void palette_advance_all_fx(long timestamp) +{ + static int ts_remainder = 0; + int i, time_diff; + short c1, c2, t; + int steps_to_do; + div_t result; + + // Figure out how many steps of work need to be done + // while updating timestamp info + + if (timestamps_per_step <= 0) return; // dont do a div by 0 + + time_diff = (int) (timestamp - last_timestamp); + last_timestamp = timestamp; + + result = div(time_diff, timestamps_per_step); + steps_to_do = result.quot; + ts_remainder += result.rem; + + if (ts_remainder > timestamps_per_step) + { + ts_remainder -= timestamps_per_step; + steps_to_do++; + } + + // Inform all active palette effects in the table how many + // steps of work they need to do. + + if (steps_to_do == 0) return; + + gr_get_pal(0, 256, local_smap); + c1 = 255; + c2 = 0; + for (i = 0; i < Palette_Effects_Table_Size; i++) + { + if (Palette_Effects_Table[i].status == ACTIVE) + { + t = Palette_Effects_Table[i].entry_1; + if (t < c1) c1 = t; + t = Palette_Effects_Table[i].entry_n; + if (t > c2) c2 = t; + + palette_advance_effect(i, steps_to_do); + } + } + if (Palette_Effects_Table[0].effect == CBANK) { + gr_set_pal((int)c1, (int)(c2 - c1 +1), &local_smap[c1*3]); + } + + // CC: refresh the whole palette + gr_set_pal(0, 256, local_smap); +} + +uchar c_off_stack[3]; + +void palette_advance_effect(byte id, int steps) +{ + short e1, en, er; + short i, j; + fix x; + div_t dv; + uchar *t, *v; + short m, n; +// uchar c[3]; + int add_to_color, add_to_delay, a; + short s1, sn, sr; + + if (Palette_Effects_Table[id].mode == STEADY) steps = 1; + + e1 = Palette_Effects_Table[id].entry_1; + en = Palette_Effects_Table[id].entry_n; + er = Palette_Effects_Table[id].range; + t = Palette_Effects_Table[id].from_pal; // "Colors" array for cycle + v = Palette_Effects_Table[id].to_pal; + + // We need to figure out how many color steps to advance, + // and how much delay to increment. So, divide "steps to do" + // by delay + 1, which is a complete cycle of effect operation. + // The quotient will tell us how many colors to increment, + // the modulus how many delay elements to add. + + dv = div(steps, (int) (Palette_Effects_Table[id].dsteps + 1)); + + add_to_color = dv.quot; + add_to_delay = dv.rem; + + // Then, if adding to delay puts us through to another color, + // update accordingly. + + Palette_Effects_Table[id].curr_dstep += add_to_delay; + if (Palette_Effects_Table[id].curr_dstep > + Palette_Effects_Table[id].dsteps) + { + Palette_Effects_Table[id].curr_dstep -= + Palette_Effects_Table[id].dsteps; + + add_to_color++; + } + + if (Palette_Effects_Table[id].effect != SHIFT) add_to_color %= er; + else if ((Palette_Effects_Table[id].curr_stage + add_to_color) > 256) + add_to_color %= 256; + + // If we've advanced exactly 0 or 1 times the entire range + // of colors, we don't really need to do anything. + + if (add_to_color == 0) return; + + switch(Palette_Effects_Table[id].effect) { + + case SHIFT: + + // If we're about to first start the shift, and the source + // palette segment is not the exact same as the real colormap + // segment corresponding, then go immediately to that + // source colormap and load it into the real colormap. + + if ((Palette_Effects_Table[id].curr_stage == -1) && + (&grd_pal[e1*3] != &t[e1*3])) { + gr_set_pal((int)e1, (int)er, &t[e1*3]); + Palette_Effects_Table[id].curr_stage = 0; + } + + // If adding the # of steps to do to our current number of + // steps pushes us up to or past the total number of steps + // to do, then jump to the final palette and remove the + // entry. + + Palette_Effects_Table[id].curr_stage += add_to_color; + + if (Palette_Effects_Table[id].curr_stage >= + Palette_Effects_Table[id].stages) + { + gr_set_pal((int) e1, (int) er, &v[e1*3]); + palette_remove_effect(id); + } + + // Otherwise, we jump our palette shift ahead by "steps" + // steps, taking care to update both the real colormap + // and our shadow fixed point map. + + else { + + x = fix_make(add_to_color, 0); + + gr_get_pal(0, 256, local_smap); + + for (i = e1; i <= en; i++) + { + j = i - e1; + + Shadow_Fixed_Cmap[3*i] += fix_mul(x,Delta_Cmap[3*j]); + Shadow_Fixed_Cmap[3*i+1] += fix_mul(x,Delta_Cmap[3*j+1]); + Shadow_Fixed_Cmap[3*i+2] += fix_mul(x,Delta_Cmap[3*j+2]); + + local_smap[i*3] = (uchar) fix_rint(Shadow_Fixed_Cmap[3*i]); + local_smap[i*3+1] = (uchar) fix_rint(Shadow_Fixed_Cmap[3*i+1]); + local_smap[i*3+2] = (uchar) fix_rint(Shadow_Fixed_Cmap[3*i+2]); + } + } + + gr_set_pal(0, 256, local_smap); + + break; + + case CYCLE: + + // If the data segments here dont make sense, realize that + // for cycles, the "entry_n" portion is the current color, + // and "range" is the cycle size. + // We're seeing if we've gone past the end of the colors array. + + Palette_Effects_Table[id].entry_n += add_to_color; + if (Palette_Effects_Table[id].entry_n > er) + Palette_Effects_Table[id].entry_n -= er; + + m = Palette_Effects_Table[id].entry_n; // "Curr_color" for cycle + n = e1; // "Cmap_Index" for cycle + + // Update colormap and shadow fixed map + + if (m < 0) break; // Rare initial case w/fast frame rate + + gr_set_pal((int) n, 1, &t[3*m]); + + Shadow_Fixed_Cmap[3*n] = fix_make(t[3*m],0); + Shadow_Fixed_Cmap[3*n+1] = fix_make(t[3*m+1],0); + Shadow_Fixed_Cmap[3*n+2] = fix_make(t[3*m+2],0); + + break; + + case CBANK: + + a = add_to_color; + + // Shift the colormap by using a uchar *shadowmap: + // Write from a->end of colormap to start of shadowmap, + // then finish shadowmap with 0->a. Then copy the whole + // thing back. + + gr_get_pal((int) e1+a, (int) er-a, &local_smap[e1*3]); + gr_get_pal((int) e1, (int) a, &local_smap[(e1+er-a)*3]); + +// KLC gr_set_pal((int) e1, (int) er, &local_smap[e1*3]); +// Now does it once after checking all CBANK effects. + + // NOT ONLY THE COLORMAP must be swapped: the delta and + // Shadow-fixed arrays must be swapped, 3 elements at + // a time. But this is only if there is a SHIFT + // effect active. If 1 SHIFT is active, we check for + // overlap. If more are active, we just swap the whole + // cycling bank's palette segment worth. (huh?) + + if (num_installed_shifts == 0) break; + else if (num_installed_shifts != 1) palette_swap_shadow((int)e1, (int)er, (int)a); + + else { + + for (i = 0; i < Palette_Effects_Table_Size; i++) + if (Palette_Effects_Table[i].effect == SHIFT) break; + + s1 = Palette_Effects_Table[i].entry_1; + sn = Palette_Effects_Table[i].entry_n; + sr = sn - s1 + 1; + + if ((s1 >= e1) && (sn <= en)) palette_swap_shadow((int)s1, (int)sr, (int)a); + else if (s1 > e1) palette_swap_shadow((int)s1, (int)(en - s1 + 1), (int)a); + else if (sn < en) palette_swap_shadow((int)e1, (int)(sn - e1 + 1), (int)a); + + else palette_swap_shadow((int)e1, (int)er, (int)a); + } + + break; + } + + return; +} + +/* + * INSTALL and REMOVE ROUTINES + * + * These either remove a palette change (easy) or install + * a new one and return an id handle (slightly more difficult). + * + * Remove routine returns ERR_NOEFFECT if the entry was already + * empty, OK otherwise. Install routine returns -1 if there + * were no available entries or if they could not install the + * palette change, and return an id handle otherwise. + * + * Also, there's not very much sanity checking. Install assumes + * legitimate arguments passed to it (as do most of the other routines + * in this library) + */ + +byte palette_install_effect(PAL_TYPE type, PAL_MODE mode, + short b1, short b2, short b3, short b4, + uchar *ptr1, uchar *ptr2) +{ + byte i; + + // Find an available slot for installation? + + for (i = 0; i < Palette_Effects_Table_Size; i++) + if (Palette_Effects_Table[i].status == EMPTY) break; + + if (i == Palette_Effects_Table_Size) return -1; // didn't find anything + + // We're psyched. Let's install the standard stuff 1st... + + Palette_Effects_Table[i].status = ACTIVE; + Palette_Effects_Table[i].effect = type; + Palette_Effects_Table[i].mode = mode; + + // ...and then, the type-specific stuff + + switch(type) { + + case SHIFT: + + Palette_Effects_Table[i].entry_1 = b1; + Palette_Effects_Table[i].entry_n = b2; + Palette_Effects_Table[i].range = b2 - b1 + 1; + Palette_Effects_Table[i].from_pal = ptr1; + Palette_Effects_Table[i].to_pal = ptr2; + Palette_Effects_Table[i].dsteps = b3; + Palette_Effects_Table[i].stages = b4; + Palette_Effects_Table[i].curr_dstep = 0; + Palette_Effects_Table[i].curr_stage = -1; + + palette_init_smap(b1, b2, ptr1, ptr2, b4); // reserve smap + deltas + + num_installed_shifts++; + + break; + + case CYCLE: + + Palette_Effects_Table[i].entry_1 = b1; + Palette_Effects_Table[i].range = b2; + Palette_Effects_Table[i].from_pal = ptr1; + Palette_Effects_Table[i].entry_n = -1; // Haven't begun yet + Palette_Effects_Table[i].dsteps = b3; + Palette_Effects_Table[i].curr_dstep = 0; // Not currently delayed + + break; + + case CBANK: + + Palette_Effects_Table[i].entry_1 = b1; + Palette_Effects_Table[i].entry_n = b2; + Palette_Effects_Table[i].dsteps = b3; + Palette_Effects_Table[i].range = b2 - b1 + 1; + Palette_Effects_Table[i].curr_dstep = 0; // Not currently delayed + + break; + + } + + // If this is the only active effect currently installed, lets + // reset the timestamp and count from now + + num_active_effects++; // Announce our presence, return handle + + return i; + +} + +errtype palette_remove_effect(byte id) +{ + if (Palette_Effects_Table[id].status == EMPTY) return ERR_NOEFFECT; + + Palette_Effects_Table[id].status = EMPTY; + + num_active_effects--; + + if (Palette_Effects_Table[id].effect == SHIFT) num_installed_shifts--; + + return OK; +} + +/* + * FREEZE and UNFREEZE ROUTINES + * + * These change the status of a table entry to "frozen". + * The return code is ERR_RANGE if the entry was empty, + * ERR_NOEFFECT if the entry was already frozen, and OK + * if the table entry freeze went alright. + * + * Similarly for the UNFREEZE routines, except they also set the + * timestamp to the time of unfreezing. + */ + +errtype palette_freeze_effect(byte id) +{ + if (Palette_Effects_Table[id].status == EMPTY) return ERR_RANGE; + if (Palette_Effects_Table[id].status == FROZEN) return ERR_NOEFFECT; + Palette_Effects_Table[id].status = FROZEN; + + num_active_effects--; + + return OK; +} + +errtype palette_unfreeze_effect(byte id) +{ + if (Palette_Effects_Table[id].status == EMPTY) return ERR_RANGE; + if (Palette_Effects_Table[id].status != FROZEN) return ERR_NOEFFECT; + + Palette_Effects_Table[id].status = ACTIVE; + + num_active_effects++; + + return OK; +} + +/* + * QUERY and CHANGE_DELAY + * + * The former lets you query the status of an effect, the latter + * lets you change its delay (but only for cycling effects!) + */ + +PAL_STATUS palette_query_effect(byte id) +{ + return Palette_Effects_Table[id].status; +} + +void palette_change_delay(byte id, short delay) +{ + if (Palette_Effects_Table[id].status != EMPTY) + Palette_Effects_Table[id].dsteps = delay; + + return; +} + +/* Palette INITIALIZE and SHUTDOWN routines + * + * DESCRIPTION: (1) Initializes the data structures internal to the palette + * library. It also takes an argument specifying how many timestamp increments + * equal one step. Passing values <= 0 can be hazardous to your health. + * + * DESCRIPTION: (2) Shutdown frees malloc'd memory. + */ + +void palette_set_rate(short ts) +{ + timestamps_per_step = ts; +} + +void palette_initialize(short tbl_size) +{ + int i; + + Palette_Effects_Table_Size = tbl_size; + + // Malloc the table + + Palette_Effects_Table = (PAL_TABLE_ENTRY *) + malloc((int) tbl_size * sizeof(PAL_TABLE_ENTRY)); +//¥¥¥No error check here + + // Initialize Table + + for (i = 0; i < Palette_Effects_Table_Size; i++) + Palette_Effects_Table[i].status = EMPTY; + + palette_set_rate(1); + + return; +} + +/* + * Palette_shutdown() + * + * Call this at the end of your program to free up the effects table. + */ + +void palette_shutdown() +{ + free(Palette_Effects_Table); + + return; +} + +/* + * SHADOWMAP and DELTA ARRAY routines + * + * The initializer reserves a specified portion of the shadow map for + * a single palette shift effect, and sets its entries to fixed point + * mockups of the "from" colormap. It also calculates what the delta + * array for that portion will be, based on the source and destination + * palettes, and the number of steps. + * + * Woe to the programmer who overlaps palette segments with multiple + * shift effects! + */ + +void palette_init_smap(short first, short last, uchar *from, uchar *to, + short num_steps) +{ + int i, j; + fix x0, x1, x2, y; + + for (i = first; i <= last; i++) { + + j = i - first; + + Shadow_Fixed_Cmap[3*i] = fix_make(from[3*j],0); + Shadow_Fixed_Cmap[3*i+1] = fix_make(from[3*j+1],0); + Shadow_Fixed_Cmap[3*i+2] = fix_make(from[3*j+2],0); + + // Get fixed point difference for r,g,b between src/dest palettes + + x0 = fix_make(to[3*j],0) - Shadow_Fixed_Cmap[3*i]; + x1 = fix_make(to[3*j+1],0) - Shadow_Fixed_Cmap[3*i+1]; + x2 = fix_make(to[3*j+2],0) - Shadow_Fixed_Cmap[3*i+2]; + + // Now divide the r,g,b diffs by #steps to figure out fixed deltas + + y = fix_make(num_steps,0); + + Delta_Cmap[3*i] = fix_div(x0, y); + Delta_Cmap[3*i+1] = fix_div(x1, y); + Delta_Cmap[3*i+2] = fix_div(x2, y); + } +} + +/* + * Palette_Swap_shadow() + * + * Does a cycle bank on the shadow map and the delta map. + */ + +#ifdef REAL_PAL_SWAP_SHAD +void palette_swap_shadow(int s, int n, int d) +{ + // used to be static, too big, what to do, what to do.... what to do... + fix Shadow_smap[768]; + fix Shadow_dmap[768]; + int i; + + // Copy the originals to the shadow maps + + for (i = 3*s; i < (s+d)*3; i++) { + Shadow_smap[i] = Shadow_Fixed_Cmap[i]; + Shadow_dmap[i] = Delta_Cmap[i]; + } + + for (i = 3*s; i < (s+n-d)*3; i++) { + Shadow_Fixed_Cmap[i] = Shadow_Fixed_Cmap[(d*3)+i]; + Delta_Cmap[i] = Delta_Cmap[(d*3)+i]; + } + + for (i = (s+n-d)*3; i < (s+n)*3; i++) { + Shadow_Fixed_Cmap[i] = Shadow_smap[i-((n-d)*3)]; + Delta_Cmap[i] = Shadow_dmap[i-((n-d)*3)]; + } + + return; +} +#endif diff --git a/engine/src/Libraries/PALETTE/Source/palette.h b/engine/src/Libraries/PALETTE/Source/palette.h new file mode 100644 index 0000000..1359f23 --- /dev/null +++ b/engine/src/Libraries/PALETTE/Source/palette.h @@ -0,0 +1,125 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __PALETTE_H +#define __PALETTE_H + +/* + * $Source: r:/prj/lib/src/palette/RCS/palette.h $ + * $Revision: 1.16 $ + * $Author: minman $ + * $Date: 1994/08/15 04:51:11 $ + * + * + */ + +// Do yourself a favor and quickly read the docs, found in +// n:\project\lib\docs\palette.txt, before you use the palette +// library. It may save you some confusion. + +#include "lg.h" +#include "fix.h" +#include "error.h" +#include "2d.h" +#include + +/* + * STRUCTS and TYPEDEFS + */ + +typedef enum { // Different palette f(x) types: + SHIFT, CYCLE, CBANK // shift, single color cycle, +} PAL_TYPE; // color banking + +typedef enum { // Status of loaded palette f(x)'s + EMPTY, ACTIVE, DELAYED, FROZEN +} PAL_STATUS; + +typedef enum { // Different cycling/banking modes + REAL_TIME, STEADY +} PAL_MODE; + +// This is the structure for an entry in the Palette Special Effects table. +// It supports different types of effects, and will hold different bits +// of information according to what type of entry it is. + +typedef struct { + PAL_STATUS status; + PAL_TYPE effect; + PAL_MODE mode; + short dsteps; // Holds delay + short stages; // Holds steps just for shift + short curr_dstep; // Holds where we are in delay + short curr_stage; // Holds where we are in shift + short entry_1; // Doubles to hold cmap_index for Cycles + short entry_n; // Doubles to hold curr_color for Cycles + short range; // Cmap range used (shift/bank)/#cols (cycle) + uchar *from_pal; // Doubles to hold colors array for Cycles + uchar *to_pal; // sometimes useless +} PAL_TABLE_ENTRY; + +/* + * MACROS + */ + +#define palette_install_fade(m, i, j, d, s, p, q) palette_install_effect(SHIFT, m, i, j, d, s, p, q) +#define palette_install_cycle(m, i, nc, d, c) palette_install_effect(CYCLE, m, i, nc, d, 0, c, NULL) +#define palette_install_cbank(m, i, j, d) palette_install_effect(CBANK, m, i, j, d, 0, NULL, NULL) + +/* + * ROUTINE PROTOTYPES + */ + +extern void palette_initialize(short table_size); +extern void palette_set_rate(short time_units_per_step); +extern void palette_shutdown(); +extern void palette_init_smap(short first, short last, uchar *from, uchar *to, + short num_steps); + +extern byte palette_install_effect(PAL_TYPE type, + PAL_MODE mode, // SHIFT CYCLE CBANK + short b1, // first index first + short b2, // last #cols last + short b3, // delay delay delay + short b4, // #steps -- -- + uchar *ptr1, // from colors -- + uchar *ptr2); // to -- -- + +extern errtype palette_remove_effect(byte id); +extern errtype palette_freeze_effect(byte id); +extern errtype palette_unfreeze_effect(byte id); +extern void palette_advance_effect(byte id, int steps); // DON'T call this +extern void palette_advance_all_fx(long timestamp); // Call this, rather... +extern PAL_STATUS palette_query_effect(byte id); +extern void palette_change_delay(byte id, short delay); +extern void palette_swap_shadow(int s, int n, int d); + +extern void palette_print_table(); + +extern byte num_installed_shifts; + +#endif // __PALETTE_H + + + + + + + + + diff --git a/engine/src/Libraries/RES/Source/caseless.c b/engine/src/Libraries/RES/Source/caseless.c new file mode 100644 index 0000000..ca5227f --- /dev/null +++ b/engine/src/Libraries/RES/Source/caseless.c @@ -0,0 +1,274 @@ +/* + +Copyright (C) 2018 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +// DG 2018: a case-insensitive fopen() wrapper, and functions used by it + +#include +#include +#include +#include +#ifdef __APPLE__ +#include +#endif + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +size_t DG_strlcpy(char *dst, const char *src, size_t dstsize) { + assert(src && dst && "Don't call strlcpy with NULL arguments!"); + size_t srclen = strlen(src); + + if (dstsize != 0) { + size_t numchars = dstsize - 1; + + if (srclen < numchars) + numchars = srclen; + + memcpy(dst, src, numchars); + dst[numchars] = '\0'; + } + return srclen; +} + +size_t DG_strlcat(char *dst, const char *src, size_t dstsize) { + assert(src && dst && "Don't call strlcat with NULL arguments!"); + + size_t dstlen = strnlen(dst, dstsize); + size_t srclen = strlen(src); + + assert(dstlen != dstsize && "dst must contain null-terminated data with strlen < dstsize!"); + + // TODO: dst[dstsize-1] = '\0' to ensure null-termination and make wrong dstsize more obvious? + + if (dstsize > 1 && dstlen < dstsize - 1) { + size_t numchars = dstsize - dstlen - 1; + + if (srclen < numchars) + numchars = srclen; + + memcpy(dst + dstlen, src, numchars); + dst[dstlen + numchars] = '\0'; + } + + return dstlen + srclen; +} + +#ifndef _WIN32 + +#include +#include + +static int check_and_append_pathelem(char dirbuf[PATH_MAX], const char *elem) { + DIR *basedir = opendir(dirbuf); + int ret = 0; + if (basedir != NULL) { + struct dirent *entry; + for (entry = readdir(basedir); entry != NULL; entry = readdir(basedir)) { + if (strcasecmp(entry->d_name, elem) == 0) { + size_t dblen = strlen(dirbuf); + if (dirbuf[dblen - 1] != '/') { + dirbuf[dblen] = '/'; + dirbuf[dblen + 1] = '\0'; + } + DG_strlcat(dirbuf, entry->d_name, PATH_MAX); + + ret = 1; + break; + } + } + + closedir(basedir); + } + return ret; +} +#endif // not _WIN32 + +// checks if a version of file with path inpath with different case exists. +// if so, the corrected version is copied to outpath. +// => outpath must be able to hold at least strlen(inpath)+2 chars. +// if wantdir is 1, the path must lead to a directory +// if it's 0, it must be a file +// if it's -1 it can be either (unless inpath ends with '/' then it must be a directory) +// returns 1 if the file (or directory) could be found, 0 if not +int caselesspath(const char *inpath, char *outpath, int wantdir) { + size_t inlen = strlen(inpath); + +#ifdef _WIN32 + + // windows is case insensitive, just do a stat() + struct _stat statBuf; + int isdir = 0; + + outpath[0] = '\0'; + + if (inlen == 0) + return 0; + + if (inpath[inlen - 1] == '/' || inpath[inlen - 1] == '\\') { + if (wantdir == 0) + return 0; // if it ends with a /, it's no file + else + wantdir = 1; + } + + if (_stat(inpath, &statBuf) != 0) + return 0; + + isdir = (statBuf.st_mode & _S_IFDIR) != 0; + if (wantdir == -1 || isdir == wantdir) { + DG_strlcpy(outpath, inpath, inlen + 1); + return 1; + } + return 0; + +#else // not Windows - more complicated + + // anyway, first do the cheap check with a stat(), maybe the case already is correct + struct stat statBuf; + + outpath[0] = '\0'; + + if (inlen == 0) + return 0; + + if (inpath[inlen - 1] == '/') { + if (wantdir == 0) + return 0; // if it ends with a /, it's no file + else + wantdir = 1; + } + + if (stat(inpath, &statBuf) == 0) { + // the file exists, now we only need to make sure it's a directory + // or not, depending on isdir + int isdir = ((statBuf.st_mode & S_IFDIR) != 0); + if (wantdir == -1 || isdir == wantdir) { + DG_strlcpy(outpath, inpath, inlen + 1); + return 1; + } + return 0; + } else // not found with stat, do it the hard way + { + char *curdirtok = NULL; + char *strtokctx = NULL; + const char *orig_inpath = inpath; + + char dirbuf[PATH_MAX] = {0}; + char inpathcpy[PATH_MAX] = {0}; + + outpath[0] = '\0'; + + if (inpath[0] == '/') { + dirbuf[0] = '/'; + dirbuf[1] = '\0'; + ++inpath; + } else if (inpath[0] == '.' && inpath[1] == '.') { + if (inpath[2] != '/') { + return 0; // malformed path, starting with .. but not ../ + } + DG_strlcpy(dirbuf, "..", 3); + inpath += 2; + } else { + dirbuf[0] = '.'; + dirbuf[1] = '/'; + //++inpath; + } + + if (DG_strlcpy(inpathcpy, inpath, sizeof(inpathcpy)) >= sizeof(inpathcpy)) { + // sorry, path too long + return 0; + } + + for (curdirtok = strtok_r(inpathcpy, "/", &strtokctx); curdirtok != NULL; + curdirtok = strtok_r(NULL, "/", &strtokctx)) { + // if the path contained /./ just ignore that + if (curdirtok[0] == '.' && curdirtok[1] == '\0') + continue; + + if (!check_and_append_pathelem(dirbuf, curdirtok)) { + // ok, that element couldn't be found + return 0; + } + } + + // now do a stat() to make sure the whole thing matches wantdir + // FIXME: somehow the stat() destroys dirbuf(), even though that really shouldn't happen.. + // if(stat(dirbuf, &statBuf) == 0) + { + // the file exists, now we only need to make sure it's a directory + // or not, depending on isdir + // int isdir = ((statBuf.st_mode & S_IFDIR) != 0); + // if(wantdir != -1 && isdir != wantdir) return 0; + + if (dirbuf[0] == '/') { + assert(strlen(dirbuf) <= inlen && "the output string shouldn't be longer than input!"); + DG_strlcpy(outpath, dirbuf, inlen + 1); + } else { + size_t outoffset = 0; + // assert(strlen(dirbuf+outoffset) <= inlen && "the output string shouldn't be longer than input!"); + // if the orig string didn't start with "./", skip that for the output as well + if (orig_inpath[0] != '.' || orig_inpath[1] != '/') + outoffset = 2; + DG_strlcpy(outpath, dirbuf + outoffset, inlen + 1); + } + if (orig_inpath[inlen - 1] == '/') { + // restore the trailing '/' that has been eaten by strtok_r() + DG_strlcat(outpath, "/", inlen + 1); + } + + return 1; + } + + return 0; + } + +#endif // not Windows +} + +FILE *fopen_caseless(const char *path, const char *mode) { + FILE *ret = NULL; + + if (path == NULL || mode == NULL) + return NULL; + +#ifdef __APPLE__ + char *macpath; + const char * prefix = SDL_GetPrefPath("Interrupt", "SystemShock"); + macpath = (char *)malloc(strlen(prefix) + strlen(path) + 1); + strcpy(macpath, prefix); + strcpy(&macpath[strlen(prefix)], path); + ret = fopen(macpath, mode); +#else + ret = fopen(path, mode); +#endif + +#ifndef _WIN32 // not windows + if (ret == NULL) { + char fixedpath[PATH_MAX]; + size_t pathlen = strlen(path); + + if (pathlen < sizeof(fixedpath) && caselesspath(path, fixedpath, 0)) { + ret = fopen(fixedpath, mode); + } + } +#endif // not windows + + return ret; +} diff --git a/engine/src/Libraries/RES/Source/lzw.c b/engine/src/Libraries/RES/Source/lzw.c new file mode 100644 index 0000000..7dbea8b --- /dev/null +++ b/engine/src/Libraries/RES/Source/lzw.c @@ -0,0 +1,716 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// clang-format off +// LZW.C New improved super-duper LZW compressor & decompressor +// This module by Greg Travis and Rex Bradford +// +// This module implements LZW-based compression and decompression. +// It has flexible control over the source and destination of the +// data it uses. Data is read from a "source" and written to a +// "destination". In the case of compression, the source is uncompressed +// and the destination is compressed; for expansion the reverse is true. +// Sources and destinations deal in byte values, even though the LZW +// routine works in 14-bit compression codes. +// +// Sources may be one of the following standard types: +// +// BUFF A memory block +// FD A file descriptor (fd = open()), already positioned with seek() +// FP A file ptr (fp = fopen()), already positioned with lseek() +// USER A user-supplied function +// +// Destinations may be any of the above 4 types, plus additionally: +// +// NULL The bit-bucket. Used to determine the size of destination +// data without putting it anywhere. +// +// The LZW module consists of these public routines: +// +// LzwInit() - Just sets LzwTerm() to be called on exit. +// +// LzwTerm() - Just calls LzwFreeBuffer(), to free lzw buffer if it +// has been malloc'ed. +// +// LzwSetBuffer() - Sets buffer for lzw compression & expansion routines +// to use. The buffer must be at least LZW_BUFF_SIZE +// in size, which for 14-bit lzw is about 91K. +// +// LzwMallocBuffer() - Allocates buffer for lzw compression & expansion +// routines to use. This routine will be called auto- +// matically the first time LzwCompress() or LzwExpand() +// is used if a buffer has not been set or allocated. +// +// LzwFreeBuffer() - Frees current buffer if allocated. +// +// LzwCompress() - Compresses data, reading from an uncompressed source +// and writing compressed bytes to a destination. Returns +// the size of the compressed data. A maximum destination +// size may be specified, in which case a destination which +// is about to exceed this will be aborted, returning -1. +// +// LzwExpand() - Expands data, reading from a compressed source and +// writing decompressed bytes to a destination. Returns the +// size of the decompressed data. Parameters may be used +// to capture a subsection of the uncompressed stream (by +// skipping the first n1 destination bytes and then taking +// the next n2). +// +// Lzw.h supplies a large set of macros of the form: +// +// LzwCompressSrc2Dest(...) and LzweExpandSrc2Dest(...) +// +// which implement all combinations of source and destination types, +// such as buffer->buffer, fd->buffer, buffer->null, fp->user, etc. +// +// User types are handy when there is a need to transform the data +// on its way to or from compression (to enhance the compression, or +// just to massage the data into a usable form). For example, a map +// may want to transform elevations to delta format on the way to +// and from compression in order to enhance the compression. +// +// User sources supply two functions of the form: +// +// void f_SrcCtrl(long srcLoc, LzwCtrl ctrl); +// uchar f_SrcGet(); +// +// The control function is used to set up and tear down the Get() +// function, which is used to supply the next byte of data. Before +// any compression or decompression begins, the SrcCtrl() function +// is called with the srcLoc argument (supplied at the call to +// LzwCompress or LzwExpand, its meaning is user-defined), and the +// ctrl argument set to BEGIN. After all compression and decompression +// is done, cleanup is invoked by calling SrcCtrl() with ctrl equal +// to END. Between BEGIN and END, the SrcGet() function is called +// repeatedly to get the next byte from the user input stream. +// +// User destinations work similarly. Again, two functions: +// +// void f_DestCtrl(long destLoc, LzwCtrl ctrl); +// void f_DestPut(uchar byte); +// +// The control function is called with BEGIN and END just like the +// source function. The DestPut() function is called repeatedly to +// put the next byte to the user output stream. +// +// Note that user sources can be used for both compression (source +// of uncompressed bytes) and expansion (source of compressed bytes). +// Similarly, user destinations can be used for both compression +// (destination of compressed bytes) and expansion (destination of +// uncompressed bytes). This is true of standard sources and +// destinations as well, of course. +// clang-format on +/* + * $Header: n:/project/lib/src/res/rcs/lzw.c 1.4 1994/02/17 11:24:13 rex Exp $ + * $log$ + */ + +// ------------------------------------------------------------ +// HEADER SECTION +// ------------------------------------------------------------ + +#include +#include +#include +#include + +#ifndef __LZW_H +#include "lzw.h" +#endif + +// Important constants + +#define MAX_VALUE ((1 << LZW_BITS) - 1) // end-of-compress-data code +#define MAX_CODE (MAX_VALUE - 2) // maximum real code allows +#define FLUSH_CODE (MAX_VALUE - 1) // code to lzw string table +#define HASHING_SHIFT (LZW_BITS - 8) // # bits to shift when hashing +#define FLUSH_PAUSE 1000 // wait on full table before flush + +// Overall lzw buffer info + +void *lzwBuffer; // total buffer +uint8_t lzwBufferMalloced; // buffer malloced? + +// Global tables used for compression & expansion + +int16_t *lzwCodeValue; // code value array +uint16_t *lzwPrefixCode; // prefix code array +uint8_t *lzwAppendChar; // appended chars array +uint8_t *lzwDecodeStack; // decoded string + +uint8_t *lzwFdReadBuff; // buffer for file descriptor source +uint8_t *lzwFdWriteBuff; // buffer for file descriptor dest + +// Prototypes of internal routines + +int32_t LzwFindMatch(int32_t hash_prefix, uint32_t hash_character); +uint8_t *LzwDecodeString(uint8_t *buffer, uint32_t code); + +// -------------------------------------------------------- +// INITIALIZATION AND TERMINATION +// -------------------------------------------------------- +// +// LzwInit() needs to be called once before any of the compression +// routines are used. + +void LzwInit(void) { atexit(LzwTerm); } + +// ------------------------------------------------------------ +// +// LzwTerm() needs to be called once when the lzw compression +// routines are no longer needed. + +void LzwTerm(void) { LzwFreeBuffer(); } + +// ------------------------------------------------------------ +// BUFFER SETTING +// -------------------------------------------------------- +// +// LzwSetBuffer() inits and sets buffer to use. +// +// Returns: 0 if ok, -1 if buffer not ok + +int32_t LzwSetBuffer(void *buff, int32_t buffSize) { + // Check buffer size + + if (buffSize < LZW_BUFF_SIZE) { + // Warning(("LzwSetBuffer: buffer too small!\n")); + return (-1); + } + + // De-allocate current buffer if malloced + + LzwTerm(); + + // Set buffer pointers + + lzwBuffer = buff; + lzwDecodeStack = lzwBuffer; + lzwFdReadBuff = (lzwDecodeStack) + LZW_DECODE_STACK_SIZE; + lzwFdWriteBuff = (lzwFdWriteBuff) + LZW_FD_READ_BUFF_SIZE; + lzwCodeValue = (int16_t *)((lzwDecodeStack) + LZW_FD_WRITE_BUFF_SIZE); + lzwPrefixCode = (uint16_t *)(((uint8_t *)lzwCodeValue) + (LZW_TABLE_SIZE * sizeof(uint16_t))); + lzwAppendChar = ((uint8_t *)lzwPrefixCode) + (LZW_TABLE_SIZE * sizeof(uint16_t)); + lzwBufferMalloced = false; + return (0); +} + +// ------------------------------------------------------------ +// +// LzwMallocBuffer() allocates buffer with Malloc. +// +// Returns: 0 if success, -1 if error. + +int32_t LzwMallocBuffer() { + void *buff; + + if ((lzwBuffer == NULL) || (!lzwBufferMalloced)) { + buff = malloc(LZW_BUFF_SIZE); + if (buff == NULL) { + // Warning(("LzwMallocBuffer: failed to allocate buffers\n")); + return (-1); + } else { + LzwSetBuffer(buff, LZW_BUFF_SIZE); + lzwBufferMalloced = true; + } + } + return (0); +} + +// ------------------------------------------------------------ +// +// LzwFreeBuffer() frees buffer. + +void LzwFreeBuffer() { + if (lzwBufferMalloced) { + free(lzwBuffer); + lzwBuffer = NULL; + lzwBufferMalloced = false; + } +} +// clang-format off +// ------------------------------------------------------------ +// COMPRESSION +// ------------------------------------------------------------ +// +// LzwCompress() does lzw compression. It reads uncompressed bytes +// from an input source and outputs compressed bytes to an output +// destination. It returns the number of bytes the compressed data +// took up, or -1 if the compressed data size exceeds the allowed space. +// +// f_ScrCtrl = routine to call to control source data stream +// f_SrcGet = routine to call to get next input data byte +// srcLoc = source data "location", actual type undefined +// srcSize = size of source (input) data +// f_DestCtrl = routine to call to control destination data stream +// f_DestPut = routine to call to put next output data byte +// destLoc = dest data "location", actual type undefined +// destSizeMax = maximum allowed size of output data +// +// Returns: actual output compressed size, or -1 if exceeded outputSizeMax +// (in which case compression has been aborted) + +// This macro is used to accumulate output codes into a bit buffer +// and call the destination put routine whenever more than 8 bits +// are available. If the output data size ever exceeds the alloted +// size, the source and destination are shut down and -1 is returned. +// clang-format on + +typedef struct { + uint32_t next_code; // next available string code + uint32_t character; // current character read from source + uint32_t string_code; // current string compress code + uint32_t index; // index into string table + int32_t lzwInputCharCount; // input character count + int32_t lzwOutputSize; // current size of output + int32_t lzwOutputBitCount; // current bit location in output + uint32_t lzwOutputBitBuffer; // 32-bit buffer holding output bits +} LzwC; + +LzwC lzwc; // current compress state + +#define LzwOutputCode(code) \ + { \ + lzwc.lzwOutputBitBuffer |= ((uint32_t)code) << (32 - LZW_BITS - lzwc.lzwOutputBitCount); \ + lzwc.lzwOutputBitCount += LZW_BITS; \ + while (lzwc.lzwOutputBitCount >= 8) { \ + (*f_DestPut)(lzwc.lzwOutputBitBuffer >> 24); \ + if (++lzwc.lzwOutputSize > destSizeMax) { \ + (*f_SrcCtrl)(srcLoc, END); \ + (*f_DestCtrl)(destLoc, END); \ + return -1L; \ + } \ + lzwc.lzwOutputBitBuffer <<= 8; \ + lzwc.lzwOutputBitCount -= 8; \ + } \ + } + +int32_t LzwCompress(void (*f_SrcCtrl)(intptr_t srcLoc, LzwCtrl ctrl), // func to control source + uint8_t (*f_SrcGet)(), // func to get bytes from source + intptr_t srcLoc, // source "location" (ptr, FILE *, etc.) + int32_t srcSize, // size of source in bytes + void (*f_DestCtrl)(intptr_t destLoc, LzwCtrl ctrl), // func to control dest + void (*f_DestPut)(uint8_t byte), // func to put bytes to dest + intptr_t destLoc, // dest "location" (ptr, FILE *, etc.) + int32_t destSizeMax // max size of dest (or LZW_MAXSIZE) +) { + + // If not already initialized, do it + if (lzwBuffer == NULL) { + if (LzwMallocBuffer() < 0) + return (0); + } + + // Set up for compress loop + lzwc.next_code = 256; // skip over real 256 char values + memset(lzwCodeValue, -1, sizeof(int16_t) * LZW_TABLE_SIZE); + + lzwc.lzwOutputSize = 0; + lzwc.lzwOutputBitCount = 0; + lzwc.lzwOutputBitBuffer = 0; + + (*f_SrcCtrl)(srcLoc, BEGIN); + (*f_DestCtrl)(destLoc, BEGIN); + + lzwc.string_code = (*f_SrcGet)(); + lzwc.lzwInputCharCount = 1; + + // This is the main loop where it all happens. This loop runs until all of + // the input has been exhausted. Note that it stops adding codes to the + // table after all of the possible codes have been defined. + + while (lzwc.lzwInputCharCount < srcSize) { + // Get next input char, if read all data then exit loop + lzwc.character = (*f_SrcGet)(); + lzwc.lzwInputCharCount++; + + // See if string is in string table. If it is, get the code value. + lzwc.index = LzwFindMatch(lzwc.string_code, lzwc.character); + if (lzwCodeValue[lzwc.index] != -1) + lzwc.string_code = lzwCodeValue[lzwc.index]; + + // Else if string not in string table, try to add it. + else { + if (lzwc.next_code <= MAX_CODE) { + lzwCodeValue[lzwc.index] = lzwc.next_code++; + lzwPrefixCode[lzwc.index] = lzwc.string_code; + lzwAppendChar[lzwc.index] = lzwc.character; + LzwOutputCode(lzwc.string_code); + lzwc.string_code = lzwc.character; + } + // Else if table is full and has been for a while, flush it, and + // drain the code value table too. + else if (lzwc.next_code > MAX_CODE + FLUSH_PAUSE) { + LzwOutputCode(lzwc.string_code); + LzwOutputCode(FLUSH_CODE); + memset(lzwCodeValue, -1, sizeof(int16_t) * LZW_TABLE_SIZE); + lzwc.string_code = lzwc.character; + lzwc.next_code = 256; + } + // Else if can't add but table not full, just output the code. + else { + lzwc.next_code++; + LzwOutputCode(lzwc.string_code); + lzwc.string_code = lzwc.character; + } + } + } + + // Done with processing loop, output current code, end-of-data code, + // and a final 0 to flush the buffer. + + LzwOutputCode(lzwc.string_code); + LzwOutputCode(MAX_VALUE); + LzwOutputCode(0); + + // Shut down source and destination and return size of output + + (*f_SrcCtrl)(srcLoc, END); + (*f_DestCtrl)(destLoc, END); + + return (lzwc.lzwOutputSize); +} + +// clang-format off +// ----------------------------------------------------------- +// EXPANSION +// ----------------------------------------------------------- +// +// LzwExpand() does lzw expansion. It reads compressed bytes +// from an input source and outputs uncompressed bytes to an output +// destination. It returns the number of bytes the uncompressed data +// took up. +// +// f_ScrCtrl = routine to call to control source data stream +// f_SrcGet = routine to call to get next input data byte +// srcLoc = source data "location", actual type undefined +// f_DestCtrl = routine to call to control destination data stream +// f_DestPut = routine to call to put next output data byte +// destLoc = dest data "location", actual type undefined +// destSkip = # bytes of output to skip over before storing +// destSize = # bytes of output to store (if 0, everything) +// +// Returns: # bytes in uncompressed output +// clang-format on + +typedef struct { + int32_t lzwInputBitCount; + uint32_t lzwInputBitBuffer; + uint32_t next_code; // next available string code + uint32_t new_code; // next code from source + uint32_t old_code; // last code gotten from source + uint32_t character; // current char for string stack + uint8_t *string; // used to output string in reverse order + int32_t outputSize; // size of uncompressed data + int32_t destSkip; // # bytes to skip over + int32_t destSize; // destination size +} LzwE; + +LzwE lzwe; // current expand state + +static uint32_t LzwInputCode(uint8_t (*f_SrcGet)()) { + uint32_t return_value; + + while (lzwe.lzwInputBitCount <= 24) { + lzwe.lzwInputBitBuffer |= ((uint32_t)(*f_SrcGet)()) << (24 - lzwe.lzwInputBitCount); + lzwe.lzwInputBitCount += 8; + } + return_value = lzwe.lzwInputBitBuffer >> (32 - LZW_BITS); + + lzwe.lzwInputBitBuffer <<= LZW_BITS; + lzwe.lzwInputBitCount -= LZW_BITS; + + return (return_value); +} + +int32_t LzwExpand(void (*f_SrcCtrl)(intptr_t srcLoc, LzwCtrl ctrl), // func to control source + uint8_t (*f_SrcGet)(), // func to get bytes from source + intptr_t srcLoc, // source "location" (ptr, FILE *, etc.) + void (*f_DestCtrl)(intptr_t destLoc, LzwCtrl ctrl), // func to control dest + void (*f_DestPut)(uint8_t byte), // func to put bytes to dest + intptr_t destLoc, // dest "location" (ptr, FILE *, etc.) + int32_t destSkip, // # dest bytes to skip over (or 0) + int32_t destSize // # dest bytes to capture (if 0, all) +) { + // If not already initialized, do it + if (lzwBuffer == NULL) { + if (LzwMallocBuffer() < 0) + return (0); + } + // Set up for expansion loop + + lzwe.lzwInputBitCount = 0; + lzwe.lzwInputBitBuffer = 0; + lzwe.next_code = 256; // next available char after regular 256 chars + lzwe.outputSize = 0; + lzwe.destSkip = destSkip; + lzwe.destSize = destSize ? destSize : LZW_MAXSIZE; + + // Notify the control routines + (*f_SrcCtrl)(srcLoc, BEGIN); + (*f_DestCtrl)(destLoc, BEGIN); + + // Get first code & output it. + lzwe.old_code = LzwInputCode(f_SrcGet); + lzwe.character = lzwe.old_code; + + if (--lzwe.destSkip < 0) { + (*f_DestPut)(lzwe.old_code); + lzwe.outputSize++; + } + + // This is the expansion loop. It reads in codes from the source until + // it sees the special end-of-data code. + while ((lzwe.new_code = LzwInputCode(f_SrcGet)) != MAX_VALUE) { + + // If flush code, flush the string table & restart from top of loop + if (lzwe.new_code == FLUSH_CODE) { + lzwe.next_code = 256; + lzwe.old_code = LzwInputCode(f_SrcGet); + lzwe.character = lzwe.old_code; + if (--lzwe.destSkip < 0) { + if (lzwe.outputSize++ >= lzwe.destSize) + break; + (*f_DestPut)(lzwe.old_code); + } + continue; + } + + // Check for the special STRING+CHARACTER+STRING+CHARACTER+STRING, which + // generates an undefined code. Handle it by decoding the last code, + // adding a single character to the end of the decode string. + + if (lzwe.new_code >= lzwe.next_code) { + *lzwDecodeStack = lzwe.character; + lzwe.string = LzwDecodeString(lzwDecodeStack + 1, lzwe.old_code); + } + + // Otherwise we do a straight decode of the new code. + else { + lzwe.string = LzwDecodeString(lzwDecodeStack, lzwe.new_code); + } + + // Output the decode string to the destination, in reverse order. + lzwe.character = *lzwe.string; + while (lzwe.string >= lzwDecodeStack) { + if (--lzwe.destSkip < 0) { + if (lzwe.outputSize++ >= lzwe.destSize) + goto DONE_EXPAND; + (*f_DestPut)(*lzwe.string); + } + --lzwe.string; + } + + // If possible, add a new code to the string table. + if (lzwe.next_code <= MAX_CODE) { + lzwPrefixCode[lzwe.next_code] = lzwe.old_code; + lzwAppendChar[lzwe.next_code] = lzwe.character; + lzwe.next_code++; + } + lzwe.old_code = lzwe.new_code; + } + + // When break out of expansion loop, shut down source & dest & return size. + +DONE_EXPAND: + + (*f_SrcCtrl)(srcLoc, END); + (*f_DestCtrl)(destLoc, END); + + return (lzwe.outputSize); +} + +// -------------------------------------------------------------- +// STANDARD INPUT SOURCES +// -------------------------------------------------------------- +// +// LzwBuffSrcCtrl() and LzwBuffSrcGet() implement a memory buffer +// source for lzw compression and expansion. + +static uint8_t *lzwBuffSrcPtr; + +void LzwBuffSrcCtrl(intptr_t srcLoc, LzwCtrl ctrl) { + if (ctrl == BEGIN) + lzwBuffSrcPtr = (uint8_t *)srcLoc; +} + +uint8_t LzwBuffSrcGet() { return (*lzwBuffSrcPtr++); } + +// --------------------------------------------------------------- +// +// LzwFdSrcCtrl() and LzwFdSrcGet() implement a file-descriptor +// source (fd = open()) for lzw compression and expansion. + +static FILE *lzwFdSrc; +static int32_t lzwReadBuffIndex; + +void LzwFdSrcCtrl(intptr_t srcLoc, LzwCtrl ctrl) { + if (ctrl == BEGIN) { + lzwFdSrc = (FILE *)srcLoc; + lzwReadBuffIndex = LZW_FD_READ_BUFF_SIZE; + } +} + +uint8_t LzwFdSrcGet() { + if (lzwReadBuffIndex == LZW_FD_READ_BUFF_SIZE) { + fread(lzwFdReadBuff, LZW_FD_READ_BUFF_SIZE, 1, lzwFdSrc); + lzwReadBuffIndex = 0; + } + return (lzwFdReadBuff[lzwReadBuffIndex++]); +} + +// --------------------------------------------------------------- +// +// LzwFpSrcCtrl() and LzwFpSrcGet() implement a file-ptr source +// (fp = fopen()) for lzw compression and expansion. + +static FILE *lzwFpSrc; + +void LzwFpSrcCtrl(intptr_t srcLoc, LzwCtrl ctrl) { + if (ctrl == BEGIN) + lzwFpSrc = (FILE *)srcLoc; +} + +uint8_t LzwFpSrcGet() { return (fgetc(lzwFpSrc)); } + +// --------------------------------------------------------------- +// STANDARD OUTPUT SOURCES +// --------------------------------------------------------------- +// +// LzwBuffDestCtrl() and LzwBuffDestPut() implement a memory +// buffer destination for lzw compression and expansion. + +static uint8_t *lzwBuffDestPtr; + +void LzwBuffDestCtrl(intptr_t destLoc, LzwCtrl ctrl) { + if (ctrl == BEGIN) + lzwBuffDestPtr = (uint8_t *)destLoc; +} + +void LzwBuffDestPut(uint8_t byte) { *lzwBuffDestPtr++ = byte; } + +// --------------------------------------------------------------- +// +// LzwFdDestCtrl() and LzwFdDestPut() implement a file-descriptor +// destination (fd = open()) for lzw compression and expansion. + +static FILE *lzwFdDest; +static int32_t lzwWriteBuffIndex; + +void LzwFdDestCtrl(intptr_t destLoc, LzwCtrl ctrl) { + if (ctrl == BEGIN) { + lzwFdDest = (FILE *)destLoc; + lzwWriteBuffIndex = 0; + } else if (ctrl == END) { + if (lzwWriteBuffIndex) + fwrite(lzwFdWriteBuff, lzwWriteBuffIndex, 1, lzwFdDest); + } +} + +void LzwFdDestPut(uint8_t byte) { + lzwFdWriteBuff[lzwWriteBuffIndex++] = byte; + if (lzwWriteBuffIndex == LZW_FD_WRITE_BUFF_SIZE) { + fwrite(lzwFdWriteBuff, LZW_FD_WRITE_BUFF_SIZE, 1, lzwFdDest); + lzwWriteBuffIndex = 0; + } +} + +// --------------------------------------------------------------- +// +// LzwFpDestCtrl() and LzwFpDestPut() implement a file-ptr destination +// (fp = fopen()) for lzw compression and expansion. + +static FILE *lzwFpDest; + +void LzwFpDestCtrl(intptr_t destLoc, LzwCtrl ctrl) { + if (ctrl == BEGIN) + lzwFpDest = (FILE *)destLoc; +} + +void LzwFpDestPut(uint8_t byte) { fputc(byte, lzwFpDest); } + +// --------------------------------------------------------------- +// +// LzwNullDestCtrl() and LzwNullDestPut() implement a bit-bucket +// destination for lzw compression and expansion. Used to size +// results of compression or expansion. + +void LzwNullDestCtrl(int32_t destLoc, LzwCtrl ctrl) {} + +void LzwNullDestPut(uint8_t byte) {} + +// ----------------------------------------------------------- +// INTERNAL ROUTINES - COMPRESSION +// ----------------------------------------------------------- +// +// LzwFindMatch() is the hashing routine. It tries to find a match +// for the prefix+char string in the string table. If it finds it, +// the index is returned. If the string is not found, the first available +// index in the string table is returned instead. +// +// hash_prefix = prefix to this code +// hash_character = new character +// +// Returns: string table index + +int32_t LzwFindMatch(int32_t hash_prefix, uint32_t hash_character) { + int32_t index; + int32_t offset; + + index = (hash_character << HASHING_SHIFT) ^ hash_prefix; + if (index == 0) + offset = 1; + else + offset = LZW_TABLE_SIZE - index; + while (1) { + if (lzwCodeValue[index] == -1) + return (index); + if ((lzwPrefixCode[index] == hash_prefix) && (lzwAppendChar[index] == hash_character)) + return (index); + index -= offset; + if (index < 0) + index += LZW_TABLE_SIZE; + } +} + +// ------------------------------------------------------------ +// INTERNAL ROUTINES - EXPANSION +// ------------------------------------------------------------ +// +// LzwDecodeString() decodes a string from the string table, +// storing it in a buffer. The buffer can then be output in +// reverse order by the expansion program. + +uint8_t *LzwDecodeString(uint8_t *buffer, uint32_t code) { +#ifdef DBG_ON + int32_t i = 0; +#endif + + while (code > 255) { + *buffer++ = lzwAppendChar[code]; + code = lzwPrefixCode[code]; + +#ifdef DBG_ON + if (i++ >= 4094) + Warning(("LzwDecodeString: Fatal error during code expansion\n")); +#endif + } + + *buffer = code; + return (buffer); +} diff --git a/engine/src/Libraries/RES/Source/lzw.h b/engine/src/Libraries/RES/Source/lzw.h new file mode 100644 index 0000000..6907041 --- /dev/null +++ b/engine/src/Libraries/RES/Source/lzw.h @@ -0,0 +1,334 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// LZW.H Header file for LZW compressor/expander (see lzw.c for info) +// Rex E. Bradford (REX) +/* + * $Header: r:/prj/lib/src/res/rcs/lzw.h 1.5 1994/09/21 09:34:59 rex Exp $ + * $Log: lzw.h $ + * Revision 1.5 1994/09/21 09:34:59 rex + * Added optional optimized hard-coded lzw expand fd 2 buff + * + * Revision 1.4 1994/02/17 11:24:29 rex + * Changed #ifdef to use double-underscores + * + * Revision 1.3 1993/08/17 17:55:31 rex + * Added buffer-management routines + * + * Revision 1.2 1993/03/22 10:29:30 rex + * Revamped LZW module to handle sources & destinations + * + * Revision 1.1 1993/03/04 18:47:54 rex + * Initial revision + * + * Revision 1.1 1993/01/12 18:07:18 rex + * Initial revision + * + */ + +#ifndef __LZW_H +#define __LZW_H + +#include + +// Options + +//#define OPTIMIZED_LZW_EXPAND_FD2BUFF // uncomment for hard-coded +// routine + +// Initialization and shutdown + +void LzwInit(void); // Justs sets AtExit routine (LzwTerm) +void LzwTerm(void); // Calls LzwFreeBuffer() + +// Lzw buffer management (if lzw compress/expand routine called and no +// buffer has been set or allocated, one will automatically be allocated). + +int32_t LzwSetBuffer(void *buff, int32_t buffSize); // Set buffer for lzw use +int32_t LzwMallocBuffer(); // Malloc buffer for lzw use +void LzwFreeBuffer(); // free alloced buffer if any + +// Sizing constants (just needed to define LZW_BUFF_SIZE) + +#define LZW_BITS 14 // # bits in compress codes (12,13,14) + +#if LZW_BITS == 14 +#define LZW_TABLE_SIZE 18041 /* The string table size needs to be a */ +#endif /* prime number that is somwhat larger */ +#if LZW_BITS == 13 /* than 2**BITS. */ +#define LZW_TABLE_SIZE 9029 +#endif +#if LZW_BITS <= 12 +#define LZW_TABLE_SIZE 5021 +#endif + +#define LZW_FD_READ_BUFF_SIZE 512 +#define LZW_FD_WRITE_BUFF_SIZE 512 +#define LZW_DECODE_STACK_SIZE 4000 + +// LzwSetBuffer() requires a buffer of at least this size: + +#define LZW_BUFF_SIZE \ + (LZW_DECODE_STACK_SIZE + LZW_FD_READ_BUFF_SIZE + LZW_FD_WRITE_BUFF_SIZE + \ + (LZW_TABLE_SIZE * (sizeof(int16_t) + sizeof(uint16_t) + sizeof(uint8_t)))) + +// Other constants + +typedef enum { + BEGIN, + END +} LzwCtrl; // LzwCtrl's used to start/stop lzw + // data sources and destinations + +#define LZW_MAXSIZE 0x7FFFFFFFL // maximum output size + +// The Ginzo compression knife + +int32_t LzwCompress(void (*f_SrcCtrl)(intptr_t srcLoc, LzwCtrl ctrl), // func to control source + uint8_t (*f_SrcGet)(), // func to get bytes from source + intptr_t srcLoc, // source "location" (ptr, FILE *, etc.) + int32_t srcSize, // size of source in bytes + void (*f_DestCtrl)(intptr_t destLoc, LzwCtrl ctrl), // func to control dest + void (*f_DestPut)(uint8_t byte), // func to put bytes to dest + intptr_t destLoc, // dest "location" (ptr, FILE *, etc.) + int32_t destSizeMax // max size of dest (or LZW_MAXSIZE) +); + +// And its expansion counterpart, both for $19.95 while supplies last + +int32_t LzwExpand(void (*f_SrcCtrl)(intptr_t srcLoc, LzwCtrl ctrl), // func to control source + uint8_t (*f_SrcGet)(), // func to get bytes from source + intptr_t srcLoc, // source "location" (ptr, FILE *, etc.) + void (*f_DestCtrl)(intptr_t destLoc, LzwCtrl ctrl), // func to control dest + void (*f_DestPut)(uint8_t byte), // func to put bytes to dest + intptr_t destLoc, // dest "location" (ptr, FILE *, etc.) + int32_t destSkip, // # dest bytes to skip over (or 0) + int32_t destSize // # dest bytes to capture (if 0, all) +); + +// clang-format off +// Macros which implement all the varied compression forms, using the +// standard supplied sources and destinations, or user-supplied ones. +// +// LzwCompressBuff2Buff - src is memory block, dest is memory block +// LzwCompressBuff2Fd - src is memory block, dest is file desc (int fd) +// LzwCompressBuff2Fp - src is memory block, dest is file ptr (FILE *fp) +// LzwCompressBuff2Null - src is memory block, no dest (used to find size) +// LzwCompressBuff2User - src is memory buffer, dest is user-supplied +// LzwCompressFd2Buff - src is file desc (int fd), dest is memory block +// LzwCompressFd2Fd - src is file desc, dest is file desc +// LzwCompressFd2Fp - src is file desc, dest is file ptr +// LzwCompressFd2Null - src is file desc, no dest (used to find size) +// LzwCompressFd2User - src is file desc, dest is user-supplied +// LzwCompressFp2Buff - src is file ptr (FILE *fp), dest is memory block +// LzwCompressFp2Fd - src is file ptr, dest is file desc +// LzwCompressFp2Fp - src is file ptr, dest is file ptr +// LzwCompressFp2Null - src is file ptr, no dest (used to find size) +// LzwCompressFp2User - src is file ptr, dest is user-supplied +// LzwCompressUser2Buff - src is user-supplied, dest is memory block +// LzwCompressUser2Fd - src is user-supplied, dest is file desc (int fd) +// LzwCompressUser2Fp - src is user-supplied, dest is file ptr (FILE *fp) +// LzwCompressUser2Null - src is user-supplied, no dest (used to find size) +// LzwCompressUser2User - src is user-supplied, dest is user-supplied +// clang-format on + +#define LzwCompressBuff2Buff(psrc, srcSize, pdest, destSizeMax) \ + LzwCompress(LzwBuffSrcC(psrc, srcSize), LzwBuffDestC(pdest, destSizeMax)) + +#define LzwCompressBuff2Fd(psrc, srcSize, fdDest) LzwCompress(LzwBuffSrcC(psrc, srcSize), LzwFdDestC(fdDest)) + +#define LzwCompressBuff2Fp(psrc, srcSize, fpDest) LzwCompress(LzwBuffSrcC(psrc, srcSize), LzwFpDestC(fpDest)) + +#define LzwCompressBuff2Null(psrc, srcSize) LzwCompress(LzwBuffSrcC(psrc, srcSize), LzwNullDestC()) + +#define LzwCompressBuff2User(psrc, srcSize, f_destCtrl, f_destPut, destLoc, destSizeMax) \ + LzwCompress(LzwBuffSrcC(psrc, srcSize), f_destCtrl, f_destPut, destLoc, destSizeMax) + +#define LzwCompressFd2Buff(fdSrc, srcSize, pdest, destSizeMax) \ + LzwCompress(LzwFdSrcC(fdSrc, srcSize), LzwBuffDestC(pdest, destSizeMax)) + +#define LzwCompressFd2Fd(fdSrc, srcSize, fdDest) LzwCompress(LzwFdSrcC(fdSrc, srcSize), LzwFdDestC(fdDest)) + +#define LzwCompressFd2Fp(fdSrc, srcSize, fpDest) LzwCompress(LzwFdSrcC(fdSrc, srcSize), LzwFpDestC(fpDest)) + +#define LzwCompressFd2Null(fdSrc, srcSize) LzwCompress(LzwFdSrcC(fdSrc, srcSize), LzwNullDestC()) + +#define LzwCompressFd2User(fdSrc, srcSize, f_destCtrl, f_destPut, destLoc, destSizeMax) \ + LzwCompress(LzwFdSrcC(fdSrc, srcSize), f_destCtrl, f_destPut, destLoc, destSizeMax) + +#define LzwCompressFp2Buff(fpSrc, srcSize, pdest, destSizeMax) \ + LzwCompress(LzwFpSrcC(fpSrc, srcSize), LzwBuffDestC(pdest, destSizeMax)) + +#define LzwCompressFp2Fd(fpSrc, srcSize, fdDest) LzwCompress(LzwFpSrcC(fpSrc, srcSize), LzwFdDestC(fdDest)) + +#define LzwCompressFp2Fp(fpSrc, srcSize, fpDest) LzwCompress(LzwFpSrcC(fpSrc, srcSize), LzwFpDestC(fpDest)) + +#define LzwCompressFp2Null(fpSrc, srcSize) LzwCompress(LzwFpSrcC(fpSrc, srcSize), LzwNullDestC()) + +#define LzwCompressFp2User(fpSrc, srcSize, f_destCtrl, f_destPut, destLoc, destSizeMax) \ + LzwCompress(LzwFpSrcC(fpSrc, srcSize), f_destCtrl, f_destPut, destLoc, destSizeMax) + +#define LzwCompressUser2Buff(f_SrcCtrl, f_SrcGet, srcLoc, srcSize, pdest, destSizeMax) \ + LzwCompress(f_SrcCtrl, f_SrcGet, srcLoc, srcSize, LzwBuffDestC(pdest, destSizeMax)) + +#define LzwCompressUser2Fd(f_SrcCtrl, f_SrcGet, srcLoc, srcSize, fdDest) \ + LzwCompress(f_SrcCtrl, f_SrcGet, srcLoc, srcSize, LzwFdDestC(fdDest)) + +#define LzwCompressUser2Fp(f_SrcCtrl, f_SrcGet, srcLoc, srcSize, fpDest) \ + LzwCompress(f_SrcCtrl, f_SrcGet, srcLoc, srcSize, LzwFpDestC(fpDest)) + +#define LzwCompressUser2Null(f_SrcCtrl, f_SrcGet, srcLoc, srcSize) \ + LzwCompress(f_SrcCtrl, f_SrcGet, srcLoc, srcSize, LzwNullDestC()) + +#define LzwCompressUser2User(f_SrcCtrl, f_SrcGet, srcLoc, srcSize, f_DestCtrl, f_DestPut, destLoc, destSizeMax) \ + LzwCompress(f_SrcCtrl, f_SrcGet, srcLoc, srcSize, f_DestCtrl, f_DestPut, destLoc, destSizeMax) + +// These macros are used to help implement the compression macros + +#define LzwBuffSrcC(psrc, srcSize) LzwBuffSrcCtrl, LzwBuffSrcGet, (intptr_t)psrc, srcSize +#define LzwFdSrcC(fdSrc, srcSize) LzwFdSrcCtrl, LzwFdSrcGet, (intptr_t)fdSrc, srcSize +#define LzwFpSrcC(fpSrc, srcSize) LzwFpSrcCtrl, LzwFpSrcGet, (intptr_t)fpSrc, srcSize + +#define LzwBuffDestC(pdest, destSizeMax) LzwBuffDestCtrl, LzwBuffDestPut, (intptr_t)pdest, destSizeMax +#define LzwFdDestC(fdDest) LzwFdDestCtrl, LzwFdDestPut, (intptr_t)fdDest, LZW_MAXSIZE +#define LzwFpDestC(fpDest) LzwFpDestCtrl, LzwFpDestPut, (intptr_t)fpDest, LZW_MAXSIZE +#define LzwNullDestC() LzwNullDestCtrl, LzwNullDestPut, NULL, LZW_MAXSIZE + +// clang-format off +// Macros which implement all the varied expansionn forms, using the +// standard supplied sources and destinations, or user-supplied ones. +// +// LzwExpandBuff2Buff - src is memory block, dest is memory block +// LzwExpandBuff2Fd - src is memory block, dest is file desc (int fd) +// LzwExpandBuff2Fp - src is memory block, dest is file ptr (FILE *fp) +// LzwExpandBuff2Null - src is memory block, no dest (used to find size) +// LzwExpandBuff2User - src is memory buffer, dest is user-supplied +// LzwExpandFd2Buff - src is file desc (int fd), dest is memory block +// LzwExpandFd2Fd - src is file desc, dest is file desc +// LzwExpandFd2Fp - src is file desc, dest is file ptr +// LzwExpandFd2Null - src is file desc, no dest (used to find size) +// LzwExpandFd2User - src is file desc, dest is user-supplied +// LzwExpandFp2Buff - src is file ptr (FILE *fp), dest is memory block +// LzwExpandFp2Fd - src is file ptr, dest is file desc +// LzwExpandFp2Fp - src is file ptr, dest is file ptr +// LzwExpandFp2Null - src is file ptr, no dest (used to find size) +// LzwExpandFp2User - src is file ptr, dest is user-supplied +// LzwExpandUser2Buff - src is user-supplied, dest is memory block +// LzwExpandUser2Fd - src is user-supplied, dest is file desc (int fd) +// LzwExpandUser2Fp - src is user-supplied, dest is file ptr (FILE *fp) +// LzwExpandUser2Null - src is user-supplied, no dest (used to find size) +// LzwExpandUser2User - src is user-supplied, dest is user-supplied +// clang-format on + +#define LzwExpandBuff2Buff(psrc, pdest, destSkip, destSize) \ + LzwExpand(LzwBuffSrcE(psrc), LzwBuffDestE(pdest, destSkip, destSize)) + +#define LzwExpandBuff2Fd(psrc, fdDest, destSkip, destSize) \ + LzwExpand(LzwBuffSrcE(psrc), LzwFdDestE(fdDest, destSkip, destSize)) + +#define LzwExpandBuff2Fp(psrc, fpDest, destSkip, destSize) \ + LzwExpand(LzwBuffSrcE(psrc), LzwFpDestE(fpDest, destSkip, destSize)) + +#define LzwExpandBuff2Null(psrc, destSkip, destSize) LzwExpand(LzwBuffSrcE(psrc), LzwNullDestE(destSkip, destSize)) + +#define LzwExpandBuff2User(psrc, f_destCtrl, f_destPut, destLoc, destSkip, destSize) \ + LzwExpand(LzwBuffSrcE(psrc), f_destCtrl, f_destPut, destLoc, destSkip, destSize) + +#ifdef OPTIMIZED_LZW_EXPAND_FD2BUFF + +int32_t LzwExpandFd2Buff(int32_t fdSrc, uint8_t *pdest, int32_t destSkip, int32_t destSize); + +#else + +#define LzwExpandFd2Buff(fdSrc, pdest, destSkip, destSize) \ + LzwExpand(LzwFdSrcE(fdSrc), LzwBuffDestE(pdest, destSkip, destSize)) + +#endif + +#define LzwExpandFd2Fd(fdSrc, fdDest, destSkip, destSize) \ + LzwExpand(LzwFdSrcE(fdSrc), LzwFdDestE(fdDest, destSkip, destSize)) + +#define LzwExpandFd2Fp(fdSrc, fpDest, destSkip, destSize) \ + LzwExpand(LzwFdSrcE(fdSrc), LzwFpDestE(fpDest, destSkip, destSize)) + +#define LzwExpandFd2Null(fdSrc, destSkip, destSize) LzwExpand(LzwFdSrcE(fdSrc), LzwNullDestE(destSkip, destSize)) + +#define LzwExpandFd2User(fdSrc, f_destCtrl, f_destPut, destLoc, destSkip, destSize) \ + LzwExpand(LzwFdSrcE(fdSrc), f_destCtrl, f_destPut, destLoc, destSkip, destSize) + +#define LzwExpandFp2Buff(fpSrc, pdest, destSkip, destSize) \ + LzwExpand(LzwFpSrcE(fpSrc), LzwBuffDestE(pdest, destSkip, destSize)) + +#define LzwExpandFp2Fd(fpSrc, fdDest, destSkip, destSize) \ + LzwExpand(LzwFpSrcE(fpSrc), LzwFdDestE(fdDest, destSkip, destSize)) + +#define LzwExpandFp2Fp(fpSrc, fpDest, destSkip, destSize) \ + LzwExpand(LzwFpSrcE(fpSrc), LzwFpDestE(fpDest, destSkip, destSize)) + +#define LzwExpandFp2Null(fpSrc, destSkip, destSize) LzwExpand(LzwFpSrcE(fpSrc), LzwNullDestE(destSkip, destSize)) + +#define LzwExpandFp2User(fpSrc, f_destCtrl, f_destPut, destLoc, destSkip, destSize) \ + LzwExpand(LzwFpSrcE(fpSrc), f_destCtrl, f_destPut, destLoc, destSkip, destSize) + +#define LzwExpandUser2Buff(f_SrcCtrl, f_SrcGet, srcLoc, pdest, destSkip, destSize) \ + LzwExpand(f_SrcCtrl, f_SrcGet, srcLoc, LzwBuffDestE(pdest, destSkip, destSize)) + +#define LzwExpandUser2Fd(f_SrcCtrl, f_SrcGet, srcLoc, fdDest, destSkip, destSize) \ + LzwExpand(f_SrcCtrl, f_SrcGet, srcLoc, LzwFdDestE(fdDest, destSkip, destSize)) + +#define LzwExpandUser2Fp(f_SrcCtrl, f_SrcGet, srcLoc, fpDest, destSkip, destSize) \ + LzwExpand(f_SrcCtrl, f_SrcGet, srcLoc, LzwFpDestE(fpDest, destSkip, destSize)) + +#define LzwExpandUser2Null(f_SrcCtrl, f_SrcGet, srcLoc, destSkip, destSize) \ + LzwExpand(f_SrcCtrl, f_SrcGet, srcLoc, LzwNullDestE(destSkip, destSize)) + +#define LzwExpandUserUser(f_SrcCtrl, f_SrcGet, srcLoc, f_DestCtrl, f_DestPut, destLoc, destSkip, destSize) \ + LzwExpand(f_SrcCtrl, f_SrcGet, srcLoc, f_DestCtrl, f_DestPut, destLoc, destSkip, destSize) + +// These macros are used to help implement the expansion macros + +#define LzwBuffSrcE(psrc) LzwBuffSrcCtrl, LzwBuffSrcGet, (intptr_t)psrc +#define LzwFdSrcE(fdSrc) LzwFdSrcCtrl, LzwFdSrcGet, (intptr_t)fdSrc +#define LzwFpSrcE(fpSrc) LzwFpSrcCtrl, LzwFpSrcGet, (intptr_t)fpSrc + +#define LzwBuffDestE(pdest, destSkip, destSize) LzwBuffDestCtrl, LzwBuffDestPut, (intptr_t)pdest, destSkip, destSize +#define LzwFdDestE(fdDest, destSkip, destSize) LzwFdDestCtrl, LzwFdDestPut, (intptr_t)fdDest, destSkip, destSize +#define LzwFpDestE(fpDest, destSkip, destSize) LzwFpDestCtrl, LzwFpDestPut, (intptr_t)fpDest, destSkip, destSize +#define LzwNullDestE(destSkip, destSize) LzwNullDestCtrl, LzwNullDestPut, NULL, destSkip, destSize + +// Prototypes of standard sources + +void LzwBuffSrcCtrl(intptr_t srcLoc, LzwCtrl ctrl); +uint8_t LzwBuffSrcGet(); +void LzwFdSrcCtrl(intptr_t srcLoc, LzwCtrl ctrl); +uint8_t LzwFdSrcGet(); +void LzwFpSrcCtrl(intptr_t srcLoc, LzwCtrl ctrl); +uint8_t LzwFpSrcGet(); + +// Prototypes of standard destinations + +void LzwBuffDestCtrl(intptr_t destLoc, LzwCtrl ctrl); +void LzwBuffDestPut(uint8_t byte); +void LzwFdDestCtrl(intptr_t destLoc, LzwCtrl ctrl); +void LzwFdDestPut(uint8_t byte); +void LzwFpDestCtrl(intptr_t destLoc, LzwCtrl ctrl); +void LzwFpDestPut(uint8_t byte); +void LzwNullDestCtrl(int32_t destLoc, LzwCtrl ctrl); +void LzwNullDestPut(uint8_t byte); + +#endif diff --git a/engine/src/Libraries/RES/Source/refacc.c b/engine/src/Libraries/RES/Source/refacc.c new file mode 100644 index 0000000..40e2b8b --- /dev/null +++ b/engine/src/Libraries/RES/Source/refacc.c @@ -0,0 +1,463 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// RefAcc.c Resource reference access +// Rex E. Bradford +/* + * $Header: r:/prj/lib/src/res/rcs/refacc.c 1.4 1994/08/30 15:18:38 rex Exp $ + * $Log: refacc.c $ + * Revision 1.4 1994/08/30 15:18:38 rex + * Made sure RefGet() returns NULL if ResLoadResource() did + * + * Revision 1.3 1994/08/30 15:15:22 rex + * Put in check for NULL return from ResLoadResource + * + * Revision 1.2 1994/06/16 11:05:17 rex + * Modified RefGet() to handle LRU list better (keep locked guys out) + * + * Revision 1.1 1994/02/17 11:23:16 rex + * Initial revision + * + */ +#include +#include // malloc + +//#include +//#include +#include "lzw.h" +#include "res.h" +#include "res_.h" + +const ResourceFormat *RefLookUpFormat(Id id); + +const ResourceFormat RefTableFormat = { ResDecodeRefTable, + NULL, + 0, + ResFreeRefTable }; +// --------------------------------------------------------- +// +// RefLock() locks a compound resource and returns ptr to item. +// +// ref = resource reference +// +// Returns: ptr to item within locked compound resource. +// --------------------------------------------------------- +// For Mac version: Change 'ptr' refs to 'hdl', lock resource handle and +// return ptr. + +void *RefLock(Ref ref) { + Id id = REFID(ref); + ResDesc *prd; + RefTable *prt; + RefIndex index; + + if (!RefCheckRef(ref)) { + ERROR("%s: Bad ref ID!", __FUNCTION__); + return NULL; + } + + // Load block if not in RAM + prd = RESDESC(id); + + if (prd->ptr == NULL) { + if (ResLoadResource(id, NULL) == NULL) { + return (NULL); + } + } + + if (prd->lock == 0) + ResRemoveFromLRU(prd); + + if (prd->lock == RES_MAXLOCK) + prd->lock--; + + prd->lock++; + + // Index into ref table + prt = (RefTable *)prd->ptr; + index = REFINDEX(ref); + + // printf("Loading ref %x %x\n", REFID(ref), index); + + // Return ptr + // We had better loaded the whole thing and not just the reftable. + assert(prt->raw_data != NULL); + if (!RefIndexValid(prt, index)) { + ERROR("%s: Invalid Index %x", __FUNCTION__, ref); + return (NULL); + } + const ResourceFormat *format = RefLookUpFormat(id); + if (format == NULL) { + ERROR("%s: Unknown format %d", __FUNCTION__, RESDESC2(id)->type); + return NULL; + } + assert(format->freer == NULL); // not supporting custom free functions yet + const ResDecodeFunc decoder = format->decoder; + const UserDecodeData data = format->data; + + void *raw = ((uint8_t*)(prt->raw_data)) + prt->entries[index].offset; + if (decoder != NULL) { + if (prt->entries[index].decoded_data == NULL) { + size_t size = prt->entries[index].size; + prt->entries[index].decoded_data = decoder(raw, &size, data); + } + return prt->entries[index].decoded_data; + } else { + return raw; + } +} + +// --------------------------------------------------------- +// +// RefGet() gets a ptr to an item in a compound resource (ref). +// +// ref = resource reference +// +// Returns: ptr to item (ptr only guaranteed until next Malloc(), +// Lock(), Get(), etc. +// --------------------------------------------------------- +// For Mac version: Lose debug and stats. Change 'ptr' refs to 'hdl'. Locks +// the resource handle before returning the ref ptr. + +void *RefGet(Ref ref) { + Id id = REFID(ref); + ResDesc *prd; + RefTable *prt; + RefIndex index; + + // Check for valid ref + if (RefCheckRef(ref) != true) { + ERROR("%s: No valid ref!", __FUNCTION__); + return NULL; + } + + // Get hold of ref + prd = RESDESC(id); + if (prd->ptr == NULL) { + if (ResLoadResource(REFID(ref), NULL) == NULL) { + ERROR("%s: RefID %x == NULL!", __FUNCTION__, ref); + return (NULL); + } + ResAddToTail(prd); + } else if (prd->lock == 0) { + ResMoveToTail(prd); + } + + // Index into ref table + prt = (RefTable *)prd->ptr; + index = REFINDEX(ref); + + // Return ptr + // We had better loaded the whole thing and not just the reftable. + assert(prt->raw_data != NULL); + if (!RefIndexValid(prt, index)) { + ERROR("%s: Invalid Index %x", __FUNCTION__, ref); + return (NULL); + } + + const ResourceFormat *format = RefLookUpFormat(id); + if (format == NULL) { + ERROR("%s: Unknown format %d", __FUNCTION__, RESDESC2(id)->type); + return NULL; + } + const ResDecodeFunc decoder = format->decoder; + const UserDecodeData data = format->data; + assert(format->freer == NULL); // custom free not yet supported + + void *raw = ((uint8_t*)(prt->raw_data)) + prt->entries[index].offset; + if (decoder != NULL) { + if (prt->entries[index].decoded_data == NULL) { + size_t size = prt->entries[index].size; + prt->entries[index].decoded_data = decoder(raw, &size, data); + } + return prt->entries[index].decoded_data; + } else { + return raw; + } +} + +// Read ref table entries (only; no data) from file. File pointer is expected to +// point at the offsets. prt must have the requisite size and have its 'numRefs' +// field set. +void readRefTableEntries(RefTable *prt, FILE *fd) { + // Temporary buffer for raw offsets. + // FIXME assumes little-endian architecture locally. + const size_t sizeOffsets = (prt->numRefs + 1) * sizeof(int32_t); + uint32_t* offsets = malloc(sizeOffsets); + int i; + const size_t offsetBase = sizeof(RefIndex) + sizeOffsets; + fread(offsets, sizeof(int32_t), prt->numRefs + 1, fd); + for (i = 0; i < prt->numRefs; ++i) { + prt->entries[i].size = offsets[i+1] - offsets[i]; + prt->entries[i].offset = offsets[i] - offsetBase; + prt->entries[i].decoded_data = NULL; + } + free(offsets); +} + +// --------------------------------------------------------- +// +// ResReadRefTable() reads a compound resource's ref table. +// +// id = id of compound resource +// +// Returns: ptr to reftable allocated with Malloc(), or NULL +// --------------------------------------------------------- +// For Mac version: Use "ReadPartialResource" to mimic this code's +// functionality. + +RefTable *ResReadRefTable(Id id) { + ResDesc *prd; + RefIndex numRefs; + RefTable *prt; + FILE *fd; + + if (!ResCheckId(id)) + return (NULL); + + prd = RESDESC(id); + fd = resFile[prd->filenum].fd; + + if (fd == NULL) { + ERROR("%s: id $%x doesn't exist", __FUNCTION__, id); + return (NULL); + } + + if (ResIsCompound(id) == 0) { + ERROR("%s: id $%x is not compound", __FUNCTION__, id); + return (NULL); + } + + // Seek to data, read numrefs, allocate table, read in offsets + + fseek(fd, RES_OFFSET_DESC2REAL(prd->offset), SEEK_SET); + fread(&numRefs, sizeof(RefIndex), 1, fd); + prt = malloc(REFTABLESIZE(numRefs)); + prt->numRefs = numRefs; + prt->raw_data = NULL; + readRefTableEntries(prt, fd); + + return (prt); +} + +// --------------------------------------------------------- +// +// ResExtractRefTable() extracts a compound res's ref table. +// +// id = id of compound resource +// prt = ptr to ref table +// size = size of ref table in bytes +// +// Returns: 0 if ok, -1 if error + +int32_t ResExtractRefTable(Id id, RefTable *prt, int32_t size) { + ResDesc *prd; + FILE *fd; + + // Check id and file number and make sure compound + if (!ResCheckId(id)) + return (-1); + + prd = RESDESC(id); + fd = resFile[prd->filenum].fd; + if (fd == NULL) { + ERROR("%s: id $%x doesn't exist", __FUNCTION__, id); + return (-1); + } + if (ResIsCompound(id) == 0) { + ERROR("%s: id $%x is not compound", __FUNCTION__, id); + return (-1); + } + + // Seek to data, read numrefs, check table size, read in offsets + fseek(fd, RES_OFFSET_DESC2REAL(prd->offset), SEEK_SET); + fread(&prt->numRefs, sizeof(RefIndex), 1, fd); + if (REFTABLESIZE(prt->numRefs) > size) { + ERROR("%s: ref table too large for buffer", __FUNCTION__); + return (-1); + } + readRefTableEntries(prt, fd); + + return (0); +} + +void *ResDecodeRefTable(void *raw, size_t *size, UserDecodeData data) { + RefIndex i; + uint32_t offset; + // First grab the table size. We'll be pulling stuff in bytewise because it + // doesn't hurt to proof the code against alignment issues on less lenient + // processors than x86, so we'll correct endianness while we're at it. + uint8_t *rp = raw; + uint16_t numRefs = (uint16_t)*rp | ((uint16_t)rp[1] << 8); + // Offset to first item in raw data. + size_t startOffset = sizeof(RefIndex) + (numRefs+1) * sizeof(uint32_t); + rp += 2; + // Allocate a directory for it. + RefTable *prt = malloc(REFTABLESIZE(numRefs)); + prt->numRefs = numRefs; + // Copy the raw data out of the original resource (it'll be deleted once + // decoding is done). + size_t rawSize = *size - startOffset; + prt->raw_data = malloc(rawSize); + memcpy(prt->raw_data, (uint8_t*)raw + startOffset, rawSize); + offset = (uint32_t)*rp | ((uint32_t)rp[1] << 8) | ((uint32_t)rp[2] << 16) | + ((uint32_t)rp[3] << 24); + rp += 4; + for (i = 0; i < numRefs; ++i) { + uint32_t next = (uint32_t)*rp | ((uint32_t)rp[1] << 8) | + ((uint32_t)rp[2] << 16) | ((uint32_t)rp[3] << 24); + rp += 4; + prt->entries[i].size = next - offset; + prt->entries[i].offset = offset - startOffset; + prt->entries[i].decoded_data = NULL; + offset = next; + } + *size = 0; // not used for compound resource. + return prt; +} + +void ResFreeRefTable(void *ptr) { + RefTable *prt = ptr; + RefIndex i; + for (i = 0; i < prt->numRefs; ++i) { + free(prt->entries[i].decoded_data); + } + free(prt->raw_data); + free(prt); +} + +// --------------------------------------------------------- +// +// return number of refs, or -1 if error +// +// --------------------------------------------------------- +int32_t ResNumRefs(Id id) { + ResDesc *prd; + + // Check id and file number and make sure compound + if (!ResCheckId(id)) + return (-1); + if (ResIsCompound(id) == 0) { + ERROR("%s: id $%x is not compound", __FUNCTION__, id); + return (-1); + } + prd = RESDESC(id); + if (prd->ptr != NULL) { + return ((RefTable *)prd->ptr)->numRefs; + } else { + FILE *fd = resFile[prd->filenum].fd; + RefIndex result; + if (fd == NULL) { + ERROR("%s: id $%x doesn't exist", __FUNCTION__, id); + return (-1); + } + fseek(fd, RES_OFFSET_DESC2REAL(prd->offset), SEEK_SET); + fread(&result, sizeof(RefIndex), 1, fd); + return result; + } +} + +/* +int32_t RefInject(RefTable *prt, Ref ref, void *buff) +{ + RefIndex index; + ResDesc *prd; + int32_t fd; + int32_t refsize; + RefIndex numrefs; + int32_t offset; + +// Check id, get file number + + if (ResFlags(REFID(ref)) & RDF_LZW) + { + return 0; + } + + + prd = RESDESC(REFID(ref)); + fd = resFile[prd->filenum].fd; + index = REFINDEX(ref); + + // get reftable date from rt or by seeking. + if (prt != NULL) + { + refsize = RefSize(prt,index); + numrefs = prt->numRefs; + offset = prt->offset[index]; + } + else + { + // seek into the file and find the stuff. + lseek(fd, RES_OFFSET_DESC2REAL(prd->offset), SEEK_SET); + read(fd, &numrefs, sizeof(RefIndex)); + lseek(fd, index*sizeof(int32_t), SEEK_CUR); + read(fd,&offset,sizeof(int32_t)); + read(fd,&refsize,sizeof(int32_t)); + refsize -= offset; + Warning(("Null reftable size = %d offset = %d numrefs = +%d\n",refsize,offset,numrefs)); + } + DBG(DSRC_RES_ChkIdRef, {if (!RefCheckRef(ref)) return(NULL);}); + DBG(DSRC_RES_ChkIdRef, {if (index >= numrefs) { \ + Warning(("RefExtract: ref $%x index too large\n", ref)); \ + return(NULL); \ + }}); + +// Add to cumulative stats + + CUMSTATS(REFID(ref),numExtracts); + +// Seek to start of all data in compound resource + + lseek(fd, RES_OFFSET_DESC2REAL(prd->offset) + REFTABLESIZE(numrefs), + SEEK_SET); + + + lseek(fd, offset - REFTABLESIZE(numrefs), SEEK_CUR); + return write(fd, buff, refsize); + +} +*/ + +// --------------------------------------------------------- +// INTERNAL ROUTINES +// --------------------------------------------------------- +// +// RefCheckRef() checks if ref valid. +// +// ref = ref to be checked +// +// Returns: true if ref ok, false if invalid & prints warning + +bool RefCheckRef(Ref ref) { + Id id; + + id = REFID(ref); + if (!ResCheckId(id)) { + WARN("%s: id $%x is bad\n", __FUNCTION__, id); + return false; + } + + if (ResIsCompound(id) == 0) { + WARN("%s: id $%x is not a compound resource", __FUNCTION__, id); + return false; + } + + return true; +} diff --git a/engine/src/Libraries/RES/Source/res.c b/engine/src/Libraries/RES/Source/res.c new file mode 100644 index 0000000..7e0c791 --- /dev/null +++ b/engine/src/Libraries/RES/Source/res.c @@ -0,0 +1,227 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Res.C Resource Manager primary access routines +// Rex E. Bradford (REX) +// +// See the doc RESOURCE.DOC for information. +/* + * $Header: r:/prj/lib/src/res/rcs/res.c 1.24 1994/07/15 18:19:33 xemu Exp $ + * $Log: res.c $ + * Revision 1.24 1994/07/15 18:19:33 xemu + * added ResShrinkResDescTable + * + * Revision 1.23 1994/05/26 13:51:55 rex + * Added ResInstallPager(ResDefaultPager) to ResInit() + * + * Revision 1.22 1994/02/17 11:24:51 rex + * Moved most funcs out into other .c files + * + */ + +//#include +//#include +#include + +#include +#include "lzw.h" +#include "res.h" +//#include +//#include <_res.h> + +// The resource descriptor table + +ResDesc *gResDesc; // ptr to array of resource descriptors +ResDesc2 *gResDesc2; // secondary array, shared buff with resdesc +Id resDescMax; // max id in res desc +// default max resource id +#define DEFAULT_RESMAX 32767 +// grow by blocks of 1024 resources must be power of 2! +#define DEFAULT_RESGROW 32768 + +// Some variables +/* +ResStat resStat; // stats held +here static bool resPushedAllocators; // did we push our allocators? +*/ + +// --------------------------------------------------------- +// INITIALIZATION AND TERMINATION +// --------------------------------------------------------- +// +// ResInit() initializes resource manager. + +void ResInit() { + int32_t i; + + // We must exit cleanly + atexit(ResTerm); + + // init LZW system + LzwInit(); + + // Allocate initial resource descriptor table, default size (can't fail) + TRACE("%s: RES system initialization", __FUNCTION__); + + resDescMax = DEFAULT_RESMAX; + gResDesc = (ResDesc *)calloc(DEFAULT_RESMAX + 1, sizeof(ResDesc) + sizeof(ResDesc2)); + if (gResDesc == NULL) + ERROR("ResInit: Can't allocate the global resource descriptor table."); + gResDesc2 = (ResDesc2 *)(gResDesc + (DEFAULT_RESMAX + 1)); + gResDesc[ID_HEAD].prev = 0; + gResDesc[ID_HEAD].next = ID_TAIL; + gResDesc[ID_TAIL].prev = ID_HEAD; + gResDesc[ID_TAIL].next = 0; + + // Clear file descriptor array + + for (i = 0; i <= MAX_RESFILENUM; i++) + resFile[i].fd = NULL; + + // Add directory pointed to by RES env var to search path + + /* + p = getenv("RES"); + if (p) + ResAddPath(p); + */ + + TRACE("%s: RES system initialized", __FUNCTION__); + + // Install default pager + // ResInstallPager(ResDefaultPager); +} + +// --------------------------------------------------------- +// +// ResTerm() terminates resource manager. + +void ResTerm() { + int32_t i; + // Close all open resource files + for (i = 0; i <= MAX_RESFILENUM; i++) { + if (resFile[i].fd >= 0) + ResCloseFile(i); + } + + // Free up resource descriptor table + + if (gResDesc) { + free(gResDesc); + gResDesc = NULL; + gResDesc2 = NULL; + resDescMax = 0; + } + // We're outta here + TRACE("%s: RES system terminated", __FUNCTION__); +} + +// --------------------------------------------------------- +// +// ResGrowResDescTable() grows resource descriptor table to +// handle a new id. +// +// This routine is normally called internally, but a client +// program may call it directly too. +// +// id = id + +void ResGrowResDescTable(Id id) { + int32_t newAmt, currAmt; + ResDesc2 *pNewResDesc2; + + // Calculate size of new table and size of current + + newAmt = (id + DEFAULT_RESGROW) & ~(DEFAULT_RESGROW - 1); + currAmt = resDescMax + 1; + + // If need to grow, do it, clearing new entries + + if (newAmt > currAmt) { + WARN("%s: extending to $%x entries", __FUNCTION__, newAmt); + + // Realloc double-array buffer and check for error + gResDesc = (ResDesc *)realloc(gResDesc, newAmt * (sizeof(ResDesc) + sizeof(ResDesc2))); + if (gResDesc == NULL) { + ERROR("%s: RES DESCRIPTOR TABLE BAD!!!", __FUNCTION__); + return; + } + + // Compute new location for gResDesc2[] array at top of buffer, + // and move the gResDesc2[] array up there + + gResDesc2 = (ResDesc2 *)(gResDesc + currAmt); + pNewResDesc2 = (ResDesc2 *)(gResDesc + newAmt); + memmove(pNewResDesc2, gResDesc2, currAmt * sizeof(ResDesc2)); + gResDesc2 = pNewResDesc2; + + // Clear extra entries in both tables + + memset(gResDesc + currAmt, 0, (newAmt - currAmt) * sizeof(ResDesc)); + memset(gResDesc2 + currAmt, 0, (newAmt - currAmt) * sizeof(ResDesc2)); + + // Set new max id limit + + resDescMax = newAmt - 1; + + + } +} + +// --------------------------------------------------------- +// +// ResShrinkResDescTable() resizes the descriptor table to be +// the minimum allowable size with the currently in-use resources. +// +/* +void ResShrinkResDescTable() +{ + int32_t newAmt,currAmt; + // id is the largest used ID + Id id; + +// Calculate largest used ID + id = resDescMax; + while ((id > ID_MIN) && (!ResInUse(id))) + id--; +// Spew(DSRC_RES_General, ("largest ID in use is %x.\n",id)); + +// Calculate size of new table and size of current + + newAmt = (id + DEFAULT_RESGROW) & ~(DEFAULT_RESGROW - 1); + currAmt = resDescMax + 1; + +// If need to shrink do it +// note that we don't increase the stat table + + if (currAmt > newAmt) + { +// Spew(DSRC_RES_General, +// ("ResGrowResDescTable: extending to $%x entries\n", +newAmt)); + + SetPtrSize(gResDesc, newAmt * sizeof(ResDesc)); + if (MemError() != noErr) + { +//��� Warning(("ResGrowDescTable: RES DESCRIPTOR TABLE +BAD!!!\n")); return; + } + resDescMax = newAmt - 1; + } +} +*/ diff --git a/engine/src/Libraries/RES/Source/res.h b/engine/src/Libraries/RES/Source/res.h new file mode 100644 index 0000000..ecfb2d4 --- /dev/null +++ b/engine/src/Libraries/RES/Source/res.h @@ -0,0 +1,392 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Res.H Resource Manager header file +// Rex E. Bradford (REX) +/* + * $Header: n:/project/lib/src/res/rcs/res.h 1.9 1994/06/16 11:56:34 rex Exp $ + * $Log: res.h $ + * Revision 1.9 1994/06/16 11:56:34 rex + * Got rid of RDF_NODROP + * + * Revision 1.8 1994/05/26 13:54:27 rex + * Added prototype ResInstallPager() + * + * Revision 1.7 1994/03/09 19:31:48 jak + * Res\RefExtractInBlocks transfers a variable/length + * block of data in each pass. The user/defined function + * returns the amount that should be passed in NEXT time. + * + * Revision 1.6 1994/02/17 11:25:02 rex + * Massive overhaul, moved some private stuff out to res_.h + * + * Revision 1.5 1993/09/01 16:02:10 rex + * Added prototype for ResExtractRefTable(). + * + * Revision 1.4 1993/05/13 10:38:44 rex + * Added prototype for ResUnmake() + * + * Revision 1.3 1993/05/13 10:30:56 rex + * Added Extract routines and macros + * + * Revision 1.2 1993/03/08 10:06:12 rex + * Changed resource directory entry format (reduced from 12 to 10 bytes) + * + * Revision 1.1 1993/03/04 18:47:58 rex + * Initial revision + * + * Revision 1.6 1993/03/02 18:42:21 rex + * Major revision, new system + * + */ + +#ifndef __RES_H +#define __RES_H + +//��� For now +//#define DBG_ON 1 + +#include +#include +#include +#include // for FILE* +#include + +#include "lg.h" + +//#ifndef DATAPATH_H +//#include +//#endif + +#ifndef __RESTYPES_H +#include "restypes.h" +#endif + +#include "resformat.h" + +#pragma pack(push,2) + +// --------------------------------------------------------- +// ID AND REF DEFINITIONS AND MACROS +// --------------------------------------------------------- + +// Id's refer to resources, Ref's refer to items in compound resources + +typedef uint16_t Id; // ID of a resource +typedef uint32_t Ref; // high word is ID, low word is index +typedef uint16_t RefIndex; // index part of ref + +// Here's how you get parts of a ref, or make a ref + +#define REFID(ref) (uint16_t)((ref) >> 16u) // get id from ref +#define REFINDEX(ref) (uint16_t)((ref) & 0xFFFFu) // get index from ref +#define MKREF(id, index) ((((uint32_t)id) << 16u) | (uint16_t)(index)) // make ref + +#define ID_NULL 0 // null resource id +#define ID_HEAD 1 // holds head ptr for LRU chain +#define ID_TAIL 2 // holds tail ptr for LRU chain +#define ID_MIN 3 // id's from 3 and up are valid + +// --------------------------------------------------------- +// ACCESS TO RESOURCES (ID'S) (resacc.c) +// --------------------------------------------------------- + +void *ResLock(Id id); // lock resource & get ptr +void ResUnlock(Id id); // unlock resource +void *ResGet(Id id); // get ptr to resource (dangerous!) +void *ResExtract(Id id, const ResourceFormat *format, void *buffer); // extract resource into buffer +void ResDrop(Id id); // drop resource from immediate use +void ResDelete(Id id); // delete resource forever + +// ------------------------------------------------------------ +// ACCESS TO ITEMS IN COMPOUND RESOURCES (REF'S) (refacc.c) +// ------------------------------------------------------------ + +// Each compound resource starts with a Ref Table. When loaded into memory, a +// ref table entry has a size, an offset into the raw resource (if loaded) and a +// pointer to the decoded ref (freed when the compound resource is unloaded). +typedef struct { + uint32_t size; + uint32_t offset; + void *decoded_data; +} RefTableEntry; + +typedef struct { + RefIndex numRefs; // # items in compound resource + void *raw_data; // resource data if loaded. + RefTableEntry entries[]; // numRefs table entries +} RefTable; + +// Decode raw ref table from file and return a RefTable. +void *ResDecodeRefTable(void *raw, size_t *size, UserDecodeData); + +void *RefLock(Ref ref); // lock compound res, get ptr to item +#define RefUnlock(ref) ResUnlock(REFID(ref)) // unlock compound res item +void *RefGet(Ref ref); // get ptr to item in comp. res (dangerous!) + +RefTable *ResReadRefTable(Id id); // alloc & read ref table +void ResFreeRefTable(void *ptr); // free ref table +int32_t ResExtractRefTable(Id id, RefTable *prt, + int32_t size); // extract reftable + +#define RefIndexValid(prt, index) ((index) < (prt)->numRefs) + +// returns the number of refs in a resource, extracting if necessary. +int32_t ResNumRefs(Id id); + +#define REFTABLESIZE(numrefs) (offsetof(RefTable, entries) + ((numrefs) * sizeof(RefTableEntry))) + +/* +// ----------------------------------------------------------- +// BLOCK-AT-A-TIME ACCESS TO RESOURCES (resexblk.c) +// ----------------------------------------------------------- + +void ResExtractInBlocks(Id id, void *buff, int32_t blockSize, + int32_t (*f_ProcBlock)(void *buff, int32_t numBytes, int32_t iblock)); +void RefExtractInBlocks(RefTable *prt, Ref ref, void *buff, int32_t blockSize, + int32_t (*f_ProcBlock)(void *buff, int32_t numBytes, int32_t iblock)); + +#define REBF_FIRST 0x01 // set for 1st block passed to f_ProcBlock +#define REBF_LAST 0x02 // set for last block (may also be first!) +*/ + +// ----------------------------------------------------------- +// IN-MEMORY RESOURCE DESCRIPTORS, AND INFORMATION ROUTINES +// ----------------------------------------------------------- +// For Mac version, keep a handle (rather than ptr). Most ResDesc info not +// needed because the Mac Resource Mgr takes care of it. + +// Each resource id gets one of these resource descriptors + +/*typedef struct +{ + //Handle hdl; // Mac resource handle. +NULL if not in memory (on disk) int32_t filenum; // Mac +resource file number uint8_t lock; // lock count uint8_t +flags; // misc flags (RDF_XXX, see below) uint8_t type; +// resource type (RTYPE_XXX, see restypes.h) uint32_t offset; uint32_t +size; + + void* ptr; + Id next; + Id prev; +} ResDesc;*/ + +typedef struct { + void *ptr; // ptr to resource in memory + uint32_t lock; // lock count + uint32_t fsize; // size of resource in bytes (1 Mb max) + uint32_t msize; // size in memory (where used; not for compound) + uint32_t filenum; // file number 0-31 + uint32_t offset; // offset in file + Id next; // next resource in LRU order + Id prev; // previous resource in LRU order + //uint32_t flags; // resource management flags + /*uint16_t type : 8;*/ // resource type (RTYPE_XXX, see restypes.h) + const ResourceFormat *format;// format of resource +} ResDesc; + +typedef struct { + uint16_t flags : 8; // misc flags (RDF_XXX, see below) + uint16_t type : 8; // resource type (RTYPE_XXX, see restypes.h) +} ResDesc2; + +#define RESDESC(id) (&gResDesc[id]) // convert id to resource desc ptr +#define RESDESC_ID(prd) ((prd)-gResDesc) // convert resdesc ptr to id + +#define RESDESC2(id) (&gResDesc2[id]) // convert id to rd2 ptr +#define RESDESC2_ID(prd) ((prd)-gResDesc2) // convert rd2 ptr to id + +#define RDF_LZW 0x01 // if 1, LZW compressed +#define RDF_COMPOUND 0x02 // if 1, compound resource +#define RDF_RESERVED 0x04 // reserved +#define RDF_LOADONOPEN 0x08 // if 1, load block when open file + +#define RES_MAXLOCK 255 // max locks on a resource + +// ptr to big array of ResDesc's +extern ResDesc *gResDesc; +// ptr to array of ResDesc2 (shared buff with resdesc) +extern ResDesc2 *gResDesc2; + +extern Id resDescMax; // max id in res desc + +// Information about resources +#define ResInUse(id) (gResDesc[id].offset) +#define ResPtr(id) (gResDesc[id].ptr) +#define ResSize(id) (gResDesc[id].msize) +#define ResLocked(id) (gResDesc[id].lock) +#define ResFilenum(id) (gResDesc[id].filenum) +#define ResType(id) (gResDesc2[id].type) +#define ResFlags(id) (gResDesc2[id].flags) +#define ResCompressed(id) (gResDesc2[id].flags & RDF_LZW) +#define ResIsCompound(id) (gResDesc2[id].flags & RDF_COMPOUND) +//#define ResZipped(id) (gResDesc2[id].flags & RDF_PKZIP) + +//#define MaxSizeRsrc(theResource) GetMaxResourceSize(theResource) + +// ------------------------------------------------------------ +// RESOURCE MANAGER GENERAL ROUTINES (res.c) +// ------------------------------------------------------------ + +void ResInit(); // init Res, allocate initial ResDesc[] +void ResTerm(); // term Res (done auto via atexit) + +// ------------------------------------------------------------ +// RESOURCE FILE ACCESS (resfile.c) +// ------------------------------------------------------------ + +typedef enum { + ROM_READ, // open for reading only + ROM_EDIT, // open for editing (r/w) only + ROM_EDITCREATE, // open for editing, create if not found + ROM_CREATE // open for creation (deletes existing) +} ResOpenMode; + +void ResAddPath(char *path); // add search path for resfiles +int32_t ResOpenResFile(const char *fname, ResOpenMode mode, bool auxinfo); +void ResCloseFile(int32_t filenum); // close res file + +#define ResOpenFile(fname) ResOpenResFile(fname, ROM_READ, FALSE) +#define ResEditFile(fname, creat) ResOpenResFile(fname, (creat) ? ROM_EDITCREATE : ROM_EDIT, TRUE) +#define ResCreateFile(fname) ResOpenResFile(fname, ROM_CREATE, TRUE) + +#define MAX_RESFILENUM 31 // maximum file number + +// extern Datapath gDatapath; // res system's datapath (others may use) +/* +// --------------------------------------------------------- +// RESOURCE MEMORY MANAGMENT ROUTINES (resmem.c) +// --------------------------------------------------------- + +void *ResMalloc(size_t size); +void *ResRealloc(void *p, size_t newsize); +void ResFree(void *p); +void *ResPage(int32_t size); + +void ResInstallPager(void *f(int32_t size)); + +// --------------------------------------------------------- +// RESOURCE STATS - ACCESSIBLE AT ANY TIME +// --------------------------------------------------------- + +typedef struct { + uint16_t numLoaded; // # resources +loaded in ram uint16_t numLocked; // # +resources locked int32_t totMemAlloc; // total +memory alloted to resources } ResStat; + +extern ResStat resStat; // stats computed if +proper DBG bit set +*/ + +// ---------------------------------------------------------- +// PUBLIC INTERFACE FOR CREATORS OF RESOURCES +// ---------------------------------------------------------- + +// ---------------------------------------------------------- +// RESOURCE MAKING (resmake.c) +// ---------------------------------------------------------- + +// make resource from data block +void ResMake(Id id, void *ptr, size_t size, uint8_t type, int32_t filenum, uint8_t flags, const ResourceFormat *format); +// make empty compound resource +void ResMakeCompound(Id id, uint8_t type, int32_t filenum, uint8_t flags); +// add item to compound +void ResAddRef(Ref ref, void *pitem, int32_t itemSize); +// unmake a resource +void ResUnmake(Id id); + +// ---------------------------------------------------------- +// RESOURCE FILE LAYOUT +// ---------------------------------------------------------- + +// Resource-file disk format: header, data, dir + +typedef struct { + char signature[16]; // "LG ResFile v2.0\n", + char comment[96]; // user comment, terminated with '\z' + uint8_t reserved[12]; // reserved for future use, must be 0 + int32_t dirOffset; // file offset of directory +} ResFileHeader; // total 128 bytes (why not?) + +typedef struct { + uint16_t numEntries; // # items referred to by directory + int32_t dataOffset; // file offset at which data resides + // directory entries follow immediately + // (numEntries of them) +} ResDirHeader; + +typedef struct { + Id id; // resource id (if 0, entry is deleted) + uint32_t size : 24; // uncompressed size (size in ram) + uint32_t flags : 8; // resource flags (RDF_XXX) + uint32_t csize : 24; // compressed size (size on disk) + // (this size is valid disk size even if not comp.) + uint32_t type : 8; // resource type +} ResDirEntry; + +// Active resource file table + +typedef struct { + uint16_t flags; // RFF_XXX + ResFileHeader hdr; // file header + ResDirHeader *pdir; // ptr to resource directory + uint16_t numAllocDir; // # dir entries allocated + int32_t currDataOffset; // current data offset in file +} ResEditInfo; + +typedef struct { + FILE *fd; // file descriptor (from open()) + ResEditInfo *pedit; // editing info, or NULL if read-only file +} ResFile; + +#define RFF_NEEDSPACK 0x0001 // resfile has holes, needs packing +#define RFF_AUTOPACK 0x0002 // resfile auto-packs (default TRUE) + +extern ResFile resFile[MAX_RESFILENUM + 1]; + +// Macros to get ptr to resfile's directory, & iterate across entries + +#define RESFILE_HASDIR(filenum) (resFile[filenum].pedit) +#define RESFILE_DIRPTR(filenum) (resFile[filenum].pedit->pdir) +#define RESFILE_DIRENTRY(pdir, n) ((ResDirEntry *)((pdir) + 1) + (n)) +#define RESFILE_FORALLINDIR(pdir, pde) \ + for (pde = RESFILE_DIRENTRY(pdir, 0); pde < RESFILE_DIRENTRY(pdir, pdir->numEntries); pde++) + +extern char resFileSignature[16]; // magic header + +// -------------------------------------------------------- +// RESOURCE FILE BUILDING (resbuild.c) +// -------------------------------------------------------- + +void ResSetComment(int32_t filenum, char *comment); // set comment +int32_t ResWrite(Id id); // write resource to file +void ResKill(Id id); // delete resource & remove from file +int32_t ResPack(int32_t filenum); // remove empty entries + +//#define ResAutoPackOn(filenum) (resFile[filenum].pedit->flags |= RFF_AUTOPACK) +//#define ResAutoPackOff(filenum) (resFile[filenum].pedit->flags &= +//~RFF_AUTOPACK) #define ResNeedsPacking(filenum) (resFile[filenum].pedit->flags +//& RFF_NEEDSPACK) +// DG: a case-insensitive fopen()-wrapper (see resfile.c) +extern FILE *fopen_caseless(const char *path, const char *mode); + +#pragma pack(pop) + +#endif diff --git a/engine/src/Libraries/RES/Source/res_.h b/engine/src/Libraries/RES/Source/res_.h new file mode 100644 index 0000000..dcb26d2 --- /dev/null +++ b/engine/src/Libraries/RES/Source/res_.h @@ -0,0 +1,120 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// RES_.H Resource System internal header file +// Rex E. Bradford +/* + * $Header: n:/project/lib/src/res/rcs/res_.h 1.2 1994/05/26 13:54:52 rex Exp $ + * $Log: res_.h $ + * Revision 1.2 1994/05/26 13:54:52 rex + * Added stuff for installable & default pager + * + * Revision 1.1 1994/02/17 11:22:56 rex + * Initial revision + * + */ + +#ifndef __RES__H +#define __RES__H + +#ifndef __RES_H +#include "res.h" +#endif +//#ifndef ___RES_H +//#include "_res.h" +//#endif + +// ---------------------------------------------------------- +// FOR RESOURCE SYSTEM INTERNAL USE - DON'T BE BAD! +// ---------------------------------------------------------- + +// Checking id's and ref's (resacc.c and refacc.c) + +bool ResCheckId(Id id); // returns TRUE if id ok, else FALSE + warns +bool RefCheckRef(Ref ref); // returns TRUE if ref ok, else FALSE & warns + +// Size of a ref on disc (not necessarily in memory). +#define RefSize(prt, index) (prt->entries[index].size) + +// Resource loading (resload.c) + +void *ResLoadResource(Id id, const ResourceFormat *format); +bool ResRetrieve(Id id, void *buffer); + +/* +// Resource paging (resmem.c) + +void *ResDefaultPager(int32_t size); +extern void *(*f_pager)(int32_t size); +extern Id idBeingLoaded; +#define RES_PAGER(size) (*f_pager)(size) +*/ + +// Grow descriptor table (res.c) + +void ResGrowResDescTable(Id id); + +#define ResExtendDesc(id) \ + { \ + if ((id) > resDescMax) \ + ResGrowResDescTable(id); \ + } + +// (Private) access to resource size in the file. +#define ResFSize(id) (gResDesc[id].fsize) + +#define DEFAULT_RES_GROWDIRENTRIES 128 // must be power of 2 + +// Data alignment aids + +#define RES_OFFSET_ALIGN(offset) (((offset) + 3) & 0xFFFFFFFCL) +#define RES_OFFSET_PADBYTES(size) ((4 - (size)) & 3) + +#define RES_OFFSET_REAL2DESC(offset) (offset) +#define RES_OFFSET_DESC2REAL(offset) (offset) + +//#define RES_OFFSET_REAL2DESC(offset) ((offset)>>2) +//#define RES_OFFSET_DESC2REAL(offset) ((offset)<<2) + +#define RES_OFFSET_PENDING 1 // offset of resource not yet written + +// LRU chain link management macros + +#define ResRemoveFromLRU(prd) \ + { \ + gResDesc[(prd)->next].prev = (prd)->prev; \ + gResDesc[(prd)->prev].next = (prd)->next; \ + } + +#define ResAddToTail(prd) \ + { \ + (prd)->prev = gResDesc[ID_TAIL].prev; \ + (prd)->next = ID_TAIL; \ + gResDesc[(prd)->prev].next = RESDESC_ID(prd); \ + gResDesc[ID_TAIL].prev = RESDESC_ID(prd); \ + } + +#define ResMoveToTail(prd) \ + { \ + if ((prd)->next != ID_TAIL) { \ + ResRemoveFromLRU(prd); \ + ResAddToTail(prd); \ + } \ + } + +#endif diff --git a/engine/src/Libraries/RES/Source/resacc.c b/engine/src/Libraries/RES/Source/resacc.c new file mode 100644 index 0000000..4b90042 --- /dev/null +++ b/engine/src/Libraries/RES/Source/resacc.c @@ -0,0 +1,422 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// ResAcc.c Resource access +// Rex E. Bradford +/* + * $Header: r:/prj/lib/src/res/rcs/resacc.c 1.4 1994/08/30 15:18:20 rex Exp $ + * $Log: resacc.c $ + * Revision 1.4 1994/08/30 15:18:20 rex + * Made sure ResGet() returns NULL if ResLoadResource() did + * + * Revision 1.3 1994/08/30 15:14:32 rex + * Put in check for NULL return from ResLoadResource + * + * Revision 1.2 1994/06/16 11:06:05 rex + * Modified routines to handle LRU list better (keep locked and nodrop stuff + * out) + * + * Revision 1.1 1994/02/17 11:23:31 rex + * Initial revision + * + */ + +#include "res.h" +#include "res_.h" +#include "lg.h" + +#include +#include // free() +#include + +// An empty ResourceFormat struct means no translation is needed. +const ResourceFormat RawFormat = { NULL, NULL, 0, NULL }; + +void *ResDecode(void *raw, size_t *size, UserDecodeData ud) +{ + // Layout. + const ResLayout *layout = (const ResLayout*)ud; + // Working pointer into the raw data. + uchar *rp = raw; + // Number of entries, if it's an array. + int nentries = (layout->flags & LAYOUT_FLAG_ARRAY) ? *size / layout->dsize : 1; + // Total size of the decoded data. + size_t bufsize = layout->msize * nentries; + if (layout->flags & LAYOUT_FLAG_RAW_DATA_FOLLOWS) { + // Additional raw data follows; add its size to the buffer. + assert(nentries == 1); + bufsize += *size - layout->dsize; + } + void *buff = malloc(bufsize); + int i; + for (i = 0; i < nentries; ++i) { + uchar *b = ((uchar *)buff) + i * layout->msize; + uchar *bp; + const ResField *field = layout->fields; + + while (field->type != RFFT_END) { + bp = b + field->offset; + if (field->type > RFFT_BIN_BASE) { + // Fixed size binary data, treated as flat byte array. + int binsize = field->type - RFFT_BIN_BASE; + memcpy(bp, rp, binsize); + rp += binsize; + } else switch (field->type) { + case RFFT_PAD: + rp += field->offset; + break; + case RFFT_UINT8: + *bp = *rp++; + break; + case RFFT_UINT16: + *(uint16_t*)bp = (uint16_t)rp[0] | ((uint16_t)rp[1] << 8); + rp += 2; + break; + case RFFT_UINT32: + *(uint32_t*)bp = (uint32_t)rp[0] | ((uint32_t)rp[1] << 8) | + ((uint32_t)rp[2] << 16) | ((uint32_t)rp[3] << 24); + rp += 4; + break; + case RFFT_INTPTR: + // These occupy 32 bits in file but expand to pointer size in memory. + *(uintptr_t*)bp = (uint32_t)rp[0] | ((uint32_t)rp[1] << 8) | + ((uint32_t)rp[2] << 16) | ((uint32_t)rp[3] << 24); + rp += 4; + break; + case RFFT_RAW: // should be last entry + memcpy(bp, rp, *size - (rp-(uchar*)raw)); + break; + default: + assert(!"Invalid resource field type"); + } + ++field; + } + } + // Update size with the decoded data size. + *size = bufsize; + return buff; +} + +void *ResEncode(void *cooked, size_t *size, UserDecodeData ud) +{ + // Layout. + const ResLayout *layout = (const ResLayout*)ud; + // Number of entries, if it's an array. + int nentries = (layout->flags & LAYOUT_FLAG_ARRAY) ? *size / layout->msize : 1; + // Total size of the data on disc. + size_t bufsize = layout->dsize * nentries; + if (layout->flags & LAYOUT_FLAG_RAW_DATA_FOLLOWS) { + // Additional raw data follows; add its size to the buffer. + assert(nentries == 1); + bufsize += *size - layout->msize; + } + void *buff = malloc(bufsize); + // Working pointer into the "raw" data. + uchar *rp = buff; + int i; + for (i = 0; i < nentries; ++i) { + uchar *b = ((uchar *)cooked) + i * layout->msize; + uchar *bp; + const ResField *field = layout->fields; + + while (field->type != RFFT_END) { + bp = b + field->offset; + if (field->type > RFFT_BIN_BASE) { + // Fixed size binary data, treated as flat byte array. + int binsize = field->type - RFFT_BIN_BASE; + memcpy(rp, bp, binsize); + rp += binsize; + } else switch (field->type) { + case RFFT_PAD: + rp += field->offset; + break; + case RFFT_UINT8: + *rp++ = *bp; + break; + case RFFT_UINT16: + *rp++ = (*(uint16_t*)bp) & 0xff; + *rp++ = (*(uint16_t*)bp) >> 8; + break; + case RFFT_UINT32: + *rp++ = (*(uint32_t*)bp) & 0xff; + *rp++ = ((*(uint32_t*)bp) >> 8) & 0xff; + *rp++ = ((*(uint32_t*)bp) >> 16) & 0xff; + *rp++ = ((*(uint32_t*)bp) >> 24) & 0xff; + break; + case RFFT_INTPTR: + // These occupy 32 bits in file but expand to pointer size in memory. + *rp++ = (*(uintptr_t*)bp) & 0xff; + *rp++ = ((*(uintptr_t*)bp) >> 8) & 0xff; + *rp++ = ((*(uintptr_t*)bp) >> 16) & 0xff; + *rp++ = ((*(uintptr_t*)bp) >> 24) & 0xff; + break; + case RFFT_RAW: // should be last entry + memcpy(rp, bp, bufsize - (rp-(uchar*)buff)); + break; + default: + assert(!"Invalid resource field type"); + } + ++field; + } + } + // Return the size of the 'raw' data. + *size = bufsize; + return buff; +} + +// --------------------------------------------------------- +// +// ResLock() locks a resource and returns ptr. +// +// id = resource id +// +// Returns: ptr to locked resource +// --------------------------------------------------------- +void *ResLock(Id id) { + ResDesc *prd; + + // Check if valid id + // DBG(DSRC_RES_ChkIdRef, {if (!ResCheckId(id)) return NULL;}); + + + prd = RESDESC(id); + + // CC: If already loaded, use the existing bytes + if (prd->ptr != NULL) { + prd->lock++; + return prd->ptr; + } + + // If resource not loaded, load it now + if (ResLoadResource(id, NULL) == NULL) { + ERROR("ResLock: Could not load %x", id); + return (NULL); + } else if (prd->lock == 0) + ResRemoveFromLRU(prd); + + prd->lock++; + + // Return ptr + return prd->ptr; +} + +// --------------------------------------------------------- +// +// ResUnlock() unlocks a resource. +// +// id = resource id +// --------------------------------------------------------- +void ResUnlock(Id id) { + ResDesc *prd; + + // Check if valid id + if (!ResCheckId(id)) + return; + + // Check for under-lock + prd = RESDESC(id); + + if (prd->lock == 0) { + DEBUG("%s: id $%x already unlocked", __FUNCTION__, id); + return; + } + + // Else decrement lock, if 0 move to tail and tally stats + if (prd->lock > 0) + prd->lock--; + + if (prd->lock == 0) { + // CC: Should we free the prd ptr here? + ResAddToTail(prd); + } +} + +// ------------------------------------------------------------- +// +// ResGet() gets a ptr to a resource +// +// id = resource id +// +// Returns: ptr to resource (ptr only guaranteed until next Malloc(), +// Lock(), Get(), etc. +// --------------------------------------------------------- + +void *ResGet(Id id) { + ResDesc *prd; + // Check if valid id + // ValidateRes(id); + + if (!ResCheckId(id)) + return NULL; + + // Load resource or move to tail + prd = RESDESC(id); + if (prd->ptr == NULL) { + if (ResLoadResource(id, NULL) == NULL) { + return (NULL); + } + ResAddToTail(prd); + } else if (prd->lock == 0) { + ResMoveToTail(prd); + } + + // ValidateRes(id); + + // Return ptr + return (prd->ptr); +} + +// --------------------------------------------------------- +// +// ResExtract() extracts a resource from an open resource file. +// +// id = id +// buff = ptr to buffer +// +// Returns: ptr to supplied buffer, or NULL if problem +// --------------------------------------------------------- +// For Mac version: Copies information from resource handle into the buffer. + +void *ResExtract(Id id, const ResourceFormat *format, void *buffer) { + ResDecodeFunc decoder = format->decoder; + ResDesc *prd = RESDESC(id); + if (decoder != NULL) { + // Get the raw data into a temporary buffer. + size_t size = prd->fsize; + void *tbuf = malloc(size); + if (ResRetrieve(id, tbuf)) { + void *dbuf = decoder(tbuf, &size, format->data); + memcpy(buffer, dbuf, size); + prd->msize = size; + if (format->freer != NULL) { + format->freer(dbuf); + } else { + free(dbuf); + } + free(tbuf); + return buffer; + } + free(tbuf); + } else { + // Retrieve the data into the buffer, please + if (ResRetrieve(id, buffer)) { + prd->msize = prd->fsize; + return (buffer); + } + } + + ERROR("%s: failed for %x", __FUNCTION__, id); + // If ResRetreive failed, return NULL ptr + return (NULL); +} + +// ---------------------------------------------------------- +// +// ResDrop() drops a resource from memory for awhile. +// +// id = resource id +// ---------------------------------------------------------- +void ResDrop(Id id) { + ResDesc *prd; + + if (!ResCheckId(id)) + return; + + prd = RESDESC(id); + if (prd->lock) + WARN("%s: Block $%x is locked, dropping anyway", __FUNCTION__, id); + + // Remove from LRU chain + if (prd->lock == 0) + ResRemoveFromLRU(prd); + + // Free memory and set ptr to NULL + + if (prd->ptr == NULL) { + TRACE("%s: Block $%x not in memory, ignoring request\n", __FUNCTION__, id); + return; + } + + if (prd->lock != 0) { + TRACE("%s: Dropping resource 0x%x that's in use.", __FUNCTION__, id); + prd->lock = 0; + } + + // Free the raw data. + if (prd->ptr != NULL) { + assert(prd->format != NULL); + if (prd->format->freer != NULL) { + prd->format->freer(prd->ptr); + } + else { + free(prd->ptr); + } + prd->ptr = NULL; + } +} + +// ------------------------------------------------------- +// +// ResDelete() deletes a resource forever. +// +// Id = id of resource +// ------------------------------------------------------- +// For Mac version: Call ReleaseResource on the handle and set its ref to +// null. The next ResLoadResource on the resource will load it back in. + +void ResDelete(Id id) { + ResDesc *prd; + + // If locked, issue warning + if (!ResCheckId(id)) + return; + + prd = RESDESC(id); + + // If in use: if in ram, free memory & LRU, then in any case zap entry + if (prd->offset) { + if (prd->ptr) { + if (prd->lock == 0) + ResRemoveFromLRU(prd); + ResDrop(id); + } + memset(prd, 0, sizeof(ResDesc)); + } +} + +// -------------------------------------------------------- +// INTERNAL ROUTINES +// -------------------------------------------------------- +// +// ResCheckId() checks if id valid. +// +// id = id to be checked +// +// Returns: TRUE if id ok, FALSE if invalid & prints warning + +bool ResCheckId(Id id) { + if (id < ID_MIN) { + DEBUG("%s: id $%x invalid", __FUNCTION__, id); + return false; + } + if (id > resDescMax) { + DEBUG("%s: id $%x exceeds table", __FUNCTION__, id); + return false; + } + return true; +} diff --git a/engine/src/Libraries/RES/Source/resbuild.c b/engine/src/Libraries/RES/Source/resbuild.c new file mode 100644 index 0000000..84872dc --- /dev/null +++ b/engine/src/Libraries/RES/Source/resbuild.c @@ -0,0 +1,379 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// RESBUILD.C Resource-file building routines +// Rex E. Bradford (REX) +/* + * $Header: n:/project/lib/src/res/rcs/resbuild.c 1.10 1994/06/16 11:06:30 rex + * Exp $ + * $Log: resbuild.c $ + * Revision 1.10 1994/06/16 11:06:30 rex + * Got rid of RDF_NODROP flag + * + * Revision 1.9 1994/02/17 11:25:32 rex + * Moved some stuff out to resmake.c and resfile.c + * + */ + +#include +#include +#if defined(_MSC_VER) +#include // SetFilePointer / SetEndOfFile +#else +#include // ftruncate +#endif + +#include "lg.h" +#include "lzw.h" +#include "res.h" +#include "res_.h" + +#include + +// make sure comment ends with one, so can type a file +#define CTRL_Z 26 + +bool ResEraseIfInFile(Id id); + +// Internal prototypes +static void ResCopyBytes(FILE *fd, int32_t writePos, int32_t readPos, int32_t size); + +// ------------------------------------------------------- +// +// ResSetComment() sets comment in res header. +// ------------------------------------------------------- +// For Mac version: Does nothing. May go back later and add comment via +// the +// desktop database, maybe. + +void ResSetComment(int32_t filenum, char *comment) { + + ResFileHeader *phead; + if (resFile[filenum].pedit == NULL) { + WARN("%s: file %d not open for writing", __FUNCTION__, filenum); + return; + } + TRACE("%s: setting comment for filenum %d to: %s", __FUNCTION__, filenum, comment); + + + phead = &resFile[filenum].pedit->hdr; + memset(phead->comment, 0, sizeof(phead->comment)); + strncpy(phead->comment, comment, sizeof(phead->comment) - 2); + phead->comment[strlen(phead->comment)] = CTRL_Z; +} + +// ------------------------------------------------------- +// +// ResWrite() writes a resource to an open resource file. +// This routine assumes that the file position is already set to +// the current data position. +// Returns the total number of bytes written out, or -1 if there +// was a writing error. +// +// id = id to write +// ------------------------------------------------------- +#define EXTRA 250 + +int32_t ResWrite(Id id) { + static uint8_t pad[] = {0, 0, 0, 0, 0, 0, 0, 0}; + ResDesc *prd; + ResDesc2 *prd2; + ResFile *prf; + ResDirEntry *pDirEntry; + uint8_t *p; + size_t size, sizeTable; + void *pcompbuff; + int32_t compsize, padBytes; + + TRACE("%s: writing", __FUNCTION__); + if (!ResCheckId(id)) + return -1; + + prd = RESDESC(id); + prf = &resFile[prd->filenum]; + + if (prf->pedit == NULL) { + ERROR("%s: file %i not open for writing!", __FUNCTION__, prd->filenum); + return -1; + } + //}); + + // Check if item already in directory, if so erase it + ResEraseIfInFile(id); + + // If directory full, grow it + if (prf->pedit->pdir->numEntries == prf->pedit->numAllocDir) { + TRACE("%s: growing directory of filenum %d", __FUNCTION__, prd->filenum); + + prf->pedit->numAllocDir += DEFAULT_RES_GROWDIRENTRIES; + prf->pedit->pdir = + realloc(prf->pedit->pdir, sizeof(ResDirHeader) + (sizeof(ResDirEntry) * prf->pedit->numAllocDir)); + } + + // Set resource's file offset + prd->offset = RES_OFFSET_REAL2DESC(prf->pedit->currDataOffset); + + // See if it needs encoding. + assert(prd->format != NULL); + size = prd->msize; + if (prd->format->encoder != NULL) { + p = prd->format->encoder(prd->ptr, &size, prd->format->data); + } else { + p = prd->ptr; + } + prd->fsize = size; + + // Fill in directory entry + pDirEntry = ((ResDirEntry *)(prf->pedit->pdir + 1)) + prf->pedit->pdir->numEntries; + + pDirEntry->id = id; + prd2 = RESDESC2(id); + pDirEntry->flags = prd2->flags; + pDirEntry->type = prd2->type; + pDirEntry->size = size; + + TRACE("%s: writing $%x\n", __FUNCTION__, id); + + // If compound, write out reftable without compression + fseek(prf->fd, prf->pedit->currDataOffset, SEEK_SET); + sizeTable = 0; + + // Body (post compound res header) if compound, else main pointer. + uint8_t *body = p; + // FIXME will need rework for compound refs if we reinstate, e.g. if we want + // to integrate a resource/level editor. + if (prd2->flags & RDF_COMPOUND) { + sizeTable = REFTABLESIZE(((RefTable *)p)->numRefs); + fwrite(p, sizeTable, 1, prf->fd); + body = p + sizeTable; + size -= sizeTable; + } + + // If compression, try it (may not work out) + if (pDirEntry->flags & RDF_LZW) { + pcompbuff = malloc(size + EXTRA); + compsize = LzwCompressBuff2Buff(p, size, pcompbuff, size); + if (compsize < 0) { + pDirEntry->flags &= ~RDF_LZW; + } else { + pDirEntry->csize = sizeTable + compsize; + fwrite(pcompbuff, compsize, 1, prf->fd); + } + free(pcompbuff); + } + + // If no compress (or failed to compress well), just write out + if (!(pDirEntry->flags & RDF_LZW)) { + pDirEntry->csize = size; + fwrite(body, size, 1, prf->fd); + } + + // Pad to align on data boundary + padBytes = RES_OFFSET_PADBYTES(pDirEntry->csize); + if (padBytes) + fwrite(pad, padBytes, 1, prf->fd); + + // FIXME Error handling + // if (ftell(prf->fd) & 3) + // Warning(("ResWrite: misaligned writing!\n")); + + // If we encoded it, free the encode buffer. + if (prd->format->encoder) { + free(p); + } + // Advance dir num entries, current data offset + prf->pedit->pdir->numEntries++; + prf->pedit->currDataOffset = RES_OFFSET_ALIGN(prf->pedit->currDataOffset + pDirEntry->csize); + + return 0; +} + +// ------------------------------------------------------------- +// +// ResKill() not only deletes a resource from memory, it removes it +// from the file too. +// ------------------------------------------------------------- +// For Mac version: Use Resource Manager to remove resource from file. Have +// to do +// our own thing (instead of calling ResDelete()) because RmveResource turns +// the resource handle into a normal handle. + +void ResKill(Id id) { + ResDesc *prd = RESDESC(id); + + if (prd->ptr) { + if (prd->lock == 0) + ResRemoveFromLRU(prd); + } + memset(prd, 0, sizeof(ResDesc)); + + // Check for valid id + if (!ResCheckId(id)) + return; + TRACE("%s: killing $%x\n", __FUNCTION__, id); + + // Delete it + ResDelete(id); + + // Make sure file is writeable + prd = RESDESC(id); + if (resFile[prd->filenum].pedit == NULL) { + WARN("%s: file %d not open for writing", __FUNCTION__, prd->filenum); + return; + } + + // If so, erase it + ResEraseIfInFile(id); +} + +// ------------------------------------------------------------- +// +// ResPack() removes holes from a resource file. +// +// filenum = resource filenum (must already be open for +// create/edit) +// +// Returns: # bytes reclaimed + +int32_t ResPack(int32_t filenum) { + ResFile *prf; + ResDirEntry *pDirEntry; + int32_t numReclaimed, sizeReclaimed; + int32_t dataRead, dataWrite; + int32_t i; + ResDirEntry *peWrite; + + // Check for errors + prf = &resFile[filenum]; + if (prf->pedit == NULL) { + ERROR("%s: filenum %d not open for editing", __FUNCTION__, filenum); + return (0); + } + + // Set up + sizeReclaimed = numReclaimed = 0; + dataRead = dataWrite = prf->pedit->pdir->dataOffset; + + // Scan thru directory, copying over all empty entries + pDirEntry = (ResDirEntry *)(prf->pedit->pdir + 1); + for (i = 0; i < prf->pedit->pdir->numEntries; i++) { + if (pDirEntry->id == 0) { + numReclaimed++; + sizeReclaimed += pDirEntry->csize; + } else { + if (gResDesc[pDirEntry->id].offset > RES_OFFSET_PENDING) + gResDesc[pDirEntry->id].offset = RES_OFFSET_REAL2DESC(dataWrite); + if (dataRead != dataWrite) + ResCopyBytes(prf->fd, dataWrite, dataRead, pDirEntry->csize); + dataWrite = RES_OFFSET_ALIGN(dataWrite + pDirEntry->csize); + } + dataRead = RES_OFFSET_ALIGN(dataRead + pDirEntry->csize); + pDirEntry++; + } + + // Now pack directory itself + pDirEntry = (ResDirEntry *)(prf->pedit->pdir + 1); + peWrite = pDirEntry; + for (i = 0; i < prf->pedit->pdir->numEntries; i++) { + if (pDirEntry->id) { + if (pDirEntry != peWrite) + *peWrite = *pDirEntry; + peWrite++; + } + pDirEntry++; + } + prf->pedit->pdir->numEntries -= numReclaimed; + + // Set new current data offset + prf->pedit->currDataOffset = dataWrite; + fseek(prf->fd, dataWrite, SEEK_SET); + prf->pedit->flags &= ~RFF_NEEDSPACK; + + // Truncate file to just header & data (will be extended later when + // write directory on closing) + + // FIXME Non-portable +#ifndef _MSC_VER + ftruncate(fileno(prf->fd), dataWrite); +#else // So much for POSIX. + SetFilePointer(fileno(prf->fd), dataWrite, NULL, FILE_BEGIN); + SetEndOfFile(fileno(prf->fd)); +#endif + + // Return # bytes reclaimed + TRACE("%s: reclaimed %d bytes", __FUNCTION__, sizeReclaimed); + + return (sizeReclaimed); +} + +#define SIZE_RESCOPY 32768 + +static void ResCopyBytes(FILE *fd, int32_t writePos, int32_t readPos, int32_t size) { + int32_t sizeCopy; + uint8_t *buff; + + buff = malloc(SIZE_RESCOPY); + + while (size > 0) { + sizeCopy = lg_min(SIZE_RESCOPY, size); + fseek(fd, readPos, SEEK_SET); + fread(buff, sizeCopy, 1, fd); + fseek(fd, writePos, SEEK_SET); + fwrite(buff, sizeCopy, 1, fd); + readPos += sizeCopy; + writePos += sizeCopy; + size -= sizeCopy; + } + + free(buff); +} + +// -------------------------------------------------------- +// INTERNAL ROUTINES +// -------------------------------------------------------- +// +// ResEraseIfInFile() erases a resource if it's in a file's directory. +// +// id = id of item +// +// Returns: TRUE if found & erased, FALSE otherwise + +bool ResEraseIfInFile(Id id) { + ResDesc *prd; + ResFile *prf; + ResDirEntry *pDirEntry; + int32_t i; + + prd = RESDESC(id); + prf = &resFile[prd->filenum]; + pDirEntry = (ResDirEntry *)(prf->pedit->pdir + 1); + + for (i = 0; i < prf->pedit->pdir->numEntries; i++) { + if (id == pDirEntry->id) { + TRACE("%s: $%x being erased\n", __FUNCTION__, id); + pDirEntry->id = 0; + prf->pedit->flags |= RFF_NEEDSPACK; + if (prf->pedit->flags & RFF_AUTOPACK) + ResPack(prd->filenum); + return true; + } + pDirEntry++; + } + + return false; +} diff --git a/engine/src/Libraries/RES/Source/resfile.c b/engine/src/Libraries/RES/Source/resfile.c new file mode 100644 index 0000000..11d9560 --- /dev/null +++ b/engine/src/Libraries/RES/Source/resfile.c @@ -0,0 +1,487 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// ResFile.C Resource Manager file access +// Rex E. Bradford (REX) +/* + * $Header: r:/prj/lib/src/res/rcs/resfile.c 1.5 1994/11/30 20:40:43 xemu Exp $ + * $Log: resfile.c $ + * Revision 1.5 1994/11/30 20:40:43 xemu + * cd spoofing support + * + * Revision 1.4 1994/09/22 10:48:32 rex + * Modified access to resdesc flags and type, which have moved + * + * Revision 1.3 1994/08/07 20:17:31 xemu + * generate a warning on resource collision + * + * Revision 1.2 1994/06/16 11:07:04 rex + * Added item to tail of LRU when loadonopen + * + * Revision 1.1 1994/02/17 11:23:23 rex + * Initial revision + * + */ +#include +#include +#include + +#include "lg.h" +#include "res.h" +#include "res_.h" + +// Resource files start with this signature + +char resFileSignature[16] = {'L', 'G', ' ', 'R', 'e', 's', ' ', 'F', 'i', 'l', 'e', ' ', 'v', '2', 13, 10}; + +// The active resource file info table + +ResFile resFile[MAX_RESFILENUM + 1]; + +// Global datapath for res files, other data modules may piggyback + +// Datapath gDatapath; + +int32_t ResFindFreeFilenum(); + +void ResReadDirEntries(int32_t filenum, ResDirHeader *pDirHead); +void ResProcDirEntry(ResDirEntry *pDirEntry, int32_t filenum, int32_t dataOffset); + +void ResReadEditInfo(ResFile *prf); +void ResReadDir(ResFile *prf, int32_t filenum); +void ResCreateEditInfo(ResFile *prf, int32_t filenum); +void ResCreateDir(ResFile *prf); +void ResWriteDir(int32_t filenum); +void ResWriteHeader(int32_t filenum); + +// --------------------------------------------------------- +// +// ResAddPath() adds a path to the resource manager's list. +// +// path = name of directory to add +/* +void ResAddPath(char *path) +{ + DatapathAdd(&gDatapath, path); + + Spew(DSRC_RES_General, ("ResAddPath: added %s\n", path)); +} +*/ + +// --------------------------------------------------------- +// +// ResOpenResFile() opens for read/edit/create. +// +// fname = ptr to filename +// mode = ROM_XXX (see res.h) +// auxinfo = if TRUE, allocate aux info, including directory +// (applies to mode 0, other modes automatically +// get it) +// +// Returns: +// +// -1 = couldn't find free filenum +// -2 = couldn't open, edit, or create file +// -3 = invalid resource file +// -4 = memory allocation failure + +int32_t ResOpenResFile(const char *fname, ResOpenMode mode, bool auxinfo) { + int32_t filenum; + FILE *fd; + ResFile *prf; + ResFileHeader fileHead; + ResDirHeader dirHead; + // uint8_t cd_spoof = FALSE; + + // Find free file number, else return -1 + + filenum = ResFindFreeFilenum(); + if (filenum < 0) { + WARN("%s: no free filenum for: %s", __FUNCTION__, fname); + return (-1); + } + + // If any mode but create, open along datapath. If can't open, + // return error except if mode 2 (edit/create), in which case + // drop thru to create case by faking mode 3. + + TRACE("%s: %s", __FUNCTION__, fname); + + if (mode != ROM_CREATE) { + // fd = DatapathFDOpen(&gDatapath, fname, openMode[mode]); + + if (mode == ROM_READ) + fd = fopen_caseless(fname, "rb"); + else + fd = fopen_caseless(fname, "rb+"); + + if (fd != NULL) { + fread(&fileHead, sizeof(ResFileHeader), 1, fd); + if (strncmp(fileHead.signature, resFileSignature, sizeof(resFileSignature)) != 0) { + fclose(fd); + WARN("%s: %s is not valid resource file", __FUNCTION__, fname); + return (-3); + } + } else { + if (mode == ROM_EDITCREATE) + mode = ROM_CREATE; + else { + WARN("%s: can't open file: %s", __FUNCTION__, fname); + return (-2); + } + } + } + + // If create mode, or edit/create failed, try to open file for creation. + + if (mode == ROM_CREATE) { + fd = fopen_caseless(fname, "wb"); + if (fd == NULL) { + WARN("%s: Can't create file: %s", __FUNCTION__, fname); + return (-2); + } + } + + // If aux info, allocate space for it + + prf = &resFile[filenum]; + prf->pedit = NULL; + if (mode || auxinfo) { + prf->pedit = (ResEditInfo *)malloc(sizeof(ResEditInfo)); + if (prf->pedit == NULL) { + // Warning(("ResOpenResFile: unable to allocate ResEditInfo\n")); + fclose(fd); + return (-4); + } + } + + // Record resFile[] file descriptor + prf->fd = fd; + TRACE("%s: opening: %s at filenum %d",__FUNCTION__, fname, filenum); + + // Switch based on mode + switch (mode) { + // If open existing file, read directory into edit info & process, or + // if no edit info then process piecemeal. + case ROM_READ: + case ROM_EDIT: + case ROM_EDITCREATE: + if (prf->pedit) { + ResReadEditInfo(prf); + ResReadDir(prf, filenum); + + // Bugfix for save games growing the entries until crashing + ResPack(filenum); + } else { + fseek(fd, fileHead.dirOffset, SEEK_SET); + fread(&dirHead, 1, sizeof(ResDirHeader), fd); + ResReadDirEntries(filenum, &dirHead); + } + break; + + // If open for create, initialize header & dir + case ROM_CREATE: + ResCreateEditInfo(prf, filenum); + ResCreateDir(prf); + break; + } + + // Return filenum + return (filenum); +} + +// --------------------------------------------------------- +// +// ResCloseFile() closes an open resource file. +// +// filenum = file number used when opening file + +void ResCloseFile(int32_t filenum) { + Id id; + + // Make sure file is open + if (resFile[filenum].fd == NULL) { + WARN("%s: filenum %d not in use", __FUNCTION__, filenum); + return; + } + + // If file being created, flush it + TRACE("%s: closing %d", __FUNCTION__, filenum); + if (resFile[filenum].pedit) { + ResWriteDir(filenum); + ResWriteHeader(filenum); + } + + // Scan object list, delete any blocks associated with this file + for (id = ID_MIN; id <= resDescMax; id++) { + if (ResInUse(id) && (ResFilenum(id) == filenum)) + ResDelete(id); + } + + // Free up memory + if (resFile[filenum].pedit) { + if (resFile[filenum].pedit->pdir) + free(resFile[filenum].pedit->pdir); + free(resFile[filenum].pedit); + } + + // Close file + fclose(resFile[filenum].fd); + resFile[filenum].fd = NULL; +} + +// -------------------------------------------------------------- +// INTERNAL ROUTINES +// --------------------------------------------------------- +// +// ResFindFreeFilenum() finds free file number + +int32_t ResFindFreeFilenum() { + int32_t filenum; + + for (filenum = 1; filenum <= MAX_RESFILENUM; filenum++) { + if (resFile[filenum].fd == NULL) + return (filenum); + } + return (-1); +} + +// ---------------------------------------------------------- +// +// ResReadDirEntries() reads in entries in a directory. +// (file seek should be set to 1st directory entry) +// +// filenum = file number +// pDirHead = ptr to directory header +// add_flags = additional flags to OR into RDF flags for all +// resources in this file. + +void ResReadDirEntries(int32_t filenum, ResDirHeader *pDirHead) { +#define NUM_DIRENTRY_BLOCK 64 // (12 bytes each) + FILE *fd; + int32_t entry; + int32_t dataOffset; + ResDirEntry *pDirEntry; + ResDirEntry dirEntries[NUM_DIRENTRY_BLOCK]; + + // Set up + pDirEntry = &dirEntries[NUM_DIRENTRY_BLOCK]; // no dir entries read + dataOffset = pDirHead->dataOffset; // mark starting offset + fd = resFile[filenum].fd; + + // Scan directory: + for (entry = 0; entry < pDirHead->numEntries; entry++) { + // If reached end of local directory buffer, refill it + if (pDirEntry >= &dirEntries[NUM_DIRENTRY_BLOCK]) { + // read(fd, dirEntries, sizeof(ResDirEntry) * NUM_DIRENTRY_BLOCK); + fread(dirEntries, sizeof(ResDirEntry) * NUM_DIRENTRY_BLOCK, 1, fd); + pDirEntry = &dirEntries[0]; + } + + // Process entry + ResProcDirEntry(pDirEntry, filenum, dataOffset); + + // Advance file offset and get next + dataOffset = RES_OFFSET_ALIGN(dataOffset + pDirEntry->csize); + pDirEntry++; + } +} + +// ----------------------------------------------------------- +// +// ResProcDirEntry() processes directory entry, sets res desc. +// +// pDirEntry = ptr to directory entry +// filenum = file number +// dataOffset = offset in file where data lives +// add_flags = additional flags to OR into RDF flags for all +// resources in this file. + +void ResProcDirEntry(ResDirEntry *pDirEntry, int32_t filenum, int32_t dataOffset) { + ResDesc *prd; + ResDesc2 *prd2; + int32_t currOffset; + + // Grow table if need to + ResExtendDesc(pDirEntry->id); + + //TRACE("id %x", pDirEntry->id); + + // If already a resource at this id, warning + prd = RESDESC(pDirEntry->id); + prd2 = RESDESC2(pDirEntry->id); + if (prd->ptr) { + WARN("%s, RESOURCE ID COLLISION AT ID %x!!", __FUNCTION__, pDirEntry->id); + ResDelete(pDirEntry->id); + } + + // Fill in resource descriptor + prd->ptr = NULL; + prd->fsize = pDirEntry->size; + prd->msize = 0; // not decoded yet + prd->filenum = filenum; + prd->lock = 0; + prd->offset = RES_OFFSET_REAL2DESC(dataOffset); + prd2->flags = pDirEntry->flags; + prd2->type = pDirEntry->type; + prd->next = 0; + prd->prev = 0; + + //TRACE("Found id: %x of type %x", pDirEntry->id, pDirEntry->type); + + // If loadonopen flag set, load resource + + if (pDirEntry->flags & RDF_LOADONOPEN) { + currOffset = ftell(resFile[filenum].fd); + // Preload raw data, subsequent Lock() or Get() calls will decode if + // the caller so wishes. + ResLoadResource(pDirEntry->id, FORMAT_RAW); + ResAddToTail(prd); + fseek(resFile[filenum].fd, currOffset, SEEK_SET); + } +} + +// -------------------------------------------------------------- +// +// ResReadEditInfo() reads edit info from file. + +void ResReadEditInfo(ResFile *prf) { + ResEditInfo *pedit = prf->pedit; + + // Init flags to no autopack or anything else + pedit->flags = 0; + + // Seek to start of file, read in header + fseek(prf->fd, 0L, SEEK_SET); + fread(&pedit->hdr, sizeof(pedit->hdr), 1, prf->fd); + + // Set no directory (yet, anyway) + pedit->pdir = NULL; + pedit->numAllocDir = 0; + pedit->currDataOffset = 0L; +} + +// --------------------------------------------------------------- +// +// ResReadDir() reads directory for a file. + +void ResReadDir(ResFile *prf, int32_t filenum) { + ResEditInfo *pedit; + ResFileHeader *phead; + ResDirHeader *pdir; + ResDirEntry *pDirEntry; + ResDirHeader dirHead; + + // Read directory header + + pedit = prf->pedit; + phead = &pedit->hdr; + fseek(prf->fd, phead->dirOffset, SEEK_SET); + fread(&dirHead, sizeof(ResDirHeader), 1, prf->fd); + + // Allocate space for directory, copy directory header into it + + pedit->numAllocDir = (dirHead.numEntries + DEFAULT_RES_GROWDIRENTRIES) & ~(DEFAULT_RES_GROWDIRENTRIES - 1); + pdir = pedit->pdir = malloc(sizeof(ResDirHeader) + (sizeof(ResDirEntry) * pedit->numAllocDir)); + *pdir = dirHead; + + // Read in directory into allocated space (past header) + fread(RESFILE_DIRENTRY(pdir, 0), dirHead.numEntries * sizeof(ResDirEntry), 1, prf->fd); + + // Scan directory, setting resource descriptors & counting data bytes + pedit->currDataOffset = pdir->dataOffset; + + RESFILE_FORALLINDIR(pdir, pDirEntry) { + if (pDirEntry->id == 0) + pedit->flags |= RFF_NEEDSPACK; + else + ResProcDirEntry(pDirEntry, filenum, pedit->currDataOffset); + pedit->currDataOffset = RES_OFFSET_ALIGN(pedit->currDataOffset + pDirEntry->csize); + } + + // Seek to current data location + fseek(prf->fd, pedit->currDataOffset, SEEK_SET); +} + +// -------------------------------------------------------------- +// +// ResCreateEditInfo() creates new empty edit info. + +void ResCreateEditInfo(ResFile *prf, int32_t filenum) { + ResEditInfo *pedit = prf->pedit; + + pedit->flags = RFF_AUTOPACK; + memcpy(pedit->hdr.signature, resFileSignature, sizeof(resFileSignature)); + ResSetComment(filenum, ""); + memset(pedit->hdr.reserved, 0, sizeof(pedit->hdr.reserved)); +} + +// -------------------------------------------------------------- +// +// ResCreateDir() creates empty dir. + +void ResCreateDir(ResFile *prf) { + ResEditInfo *pedit = prf->pedit; + + pedit->hdr.dirOffset = 0; + pedit->numAllocDir = DEFAULT_RES_GROWDIRENTRIES; + pedit->pdir = malloc(sizeof(ResDirHeader) + (sizeof(ResDirEntry) * pedit->numAllocDir)); + pedit->pdir->numEntries = 0; + pedit->currDataOffset = pedit->pdir->dataOffset = sizeof(ResFileHeader); + fseek(prf->fd, pedit->currDataOffset, SEEK_SET); +} + +// ------------------------------------------------------------- +// +// ResWriteDir() writes directory to resource file. + +void ResWriteDir(int32_t filenum) { + ResFile *prf; + + if (resFile[filenum].pedit == NULL) { + WARN("%s: file %d not open for writing", __FUNCTION__, filenum); + return; + } + + TRACE("%s: writing directory for filenum %d", __FUNCTION__, filenum); + + prf = &resFile[filenum]; + fseek(prf->fd, prf->pedit->currDataOffset, SEEK_SET); + fwrite(prf->pedit->pdir, sizeof(ResDirHeader) + (prf->pedit->pdir->numEntries * sizeof(ResDirEntry)), 1, prf->fd); +} + +// -------------------------------------------------------- +// +// ResWriteHeader() writes header to resource file. + +void ResWriteHeader(int32_t filenum) { + ResFile *prf; + + if (resFile[filenum].pedit == NULL) { + WARN("%s: file %d not open for writing", __FUNCTION__, filenum); + return; + } + + TRACE("%s: writing header for filenum %d", __FUNCTION__, filenum); + + prf = &resFile[filenum]; + prf->pedit->hdr.dirOffset = prf->pedit->currDataOffset; + + fseek(prf->fd, 0, SEEK_SET); + fwrite(&prf->pedit->hdr, sizeof(ResFileHeader), 1, prf->fd); +} diff --git a/engine/src/Libraries/RES/Source/resformat.c b/engine/src/Libraries/RES/Source/resformat.c new file mode 100644 index 0000000..f6250bb --- /dev/null +++ b/engine/src/Libraries/RES/Source/resformat.c @@ -0,0 +1,134 @@ +/* + +Copyright (C) 2019 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include "res.h" + +#include "2dres.h" +#include "grs.h" // grs_font + +// Defines the layout of a FrameDesc within a resource file. +const ResLayout FrameDescLayout = { + 28, // on-disc size + sizeof(FrameDesc), // in-memory size + LAYOUT_FLAG_RAW_DATA_FOLLOWS, // flags + { + { RFFT_PAD, 4 }, // skip placeholder bits pointer + { RFFT_UINT8, offsetof(FrameDesc,bm.type) }, + { RFFT_UINT8, offsetof(FrameDesc,bm.align) }, + { RFFT_UINT16, offsetof(FrameDesc,bm.flags) }, + { RFFT_UINT16, offsetof(FrameDesc,bm.w) }, + { RFFT_UINT16, offsetof(FrameDesc,bm.h) }, + { RFFT_UINT16, offsetof(FrameDesc,bm.row) }, + { RFFT_UINT8, offsetof(FrameDesc,bm.wlog) }, + { RFFT_UINT8, offsetof(FrameDesc,bm.hlog) }, + { RFFT_UINT16, offsetof(FrameDesc,updateArea.ul.x) }, + { RFFT_UINT16, offsetof(FrameDesc,updateArea.ul.y) }, + { RFFT_UINT16, offsetof(FrameDesc,updateArea.lr.x) }, + { RFFT_UINT16, offsetof(FrameDesc,updateArea.lr.y) }, + { RFFT_UINT32, offsetof(FrameDesc,pallOff) }, + { RFFT_RAW, sizeof(FrameDesc) }, // raw bitmap data follows + { RFFT_END, 0 } + } +}; + +// Decoder function for frames: decodes using the layout in the normal way and +// then updates the bits pointer. +void *FrameDecode(void *raw, size_t *size, UserDecodeData layout) { + FrameDesc *f = ResDecode(raw, size, layout); + f->bm.bits = (uchar *)(f + 1); + return f; +} +const ResourceFormat FrameDescFormat = { + FrameDecode, ResEncode, (UserDecodeData)&FrameDescLayout, NULL }; + +// Describe a font. +// FIXME treats the offsets table as raw, should be decoded also. +const ResLayout FontLayout = { + 84, // size on disc (header only) + offsetof(grs_font, off_tab), // size in memory (header only) + LAYOUT_FLAG_RAW_DATA_FOLLOWS, // flags + { + { RFFT_UINT16, offsetof(grs_font, id) }, + { RFFT_PAD, 34 }, // dummy1 + { RFFT_UINT16, offsetof(grs_font, min) }, + { RFFT_UINT16, offsetof(grs_font, max) }, + { RFFT_PAD, 32 }, // dummy2 + { RFFT_UINT32, offsetof(grs_font, cotptr) }, + { RFFT_UINT32, offsetof(grs_font, buf) }, + { RFFT_UINT16, offsetof(grs_font, w) }, + { RFFT_UINT16, offsetof(grs_font, h) }, + { RFFT_RAW, offsetof(grs_font, off_tab) }, + { RFFT_END, 0 } + // offsets table follows, then bitmap data + } +}; + +const ResourceFormat FontFormat = RES_FORMAT(FontLayout); + +// Table of "well-known" resource formats, indexed by resource type. +#define MAX_SUPPORTED_TYPE RTYPE_MOVIE +const ResourceFormat *ResTypeLayout[MAX_SUPPORTED_TYPE + 1] = { + NULL, // FIXME RTYPE_UNKNOWN (actually palette) + &RawFormat, // RTYPE_STRING (needs no translation) + &FrameDescFormat, // RTYPE_BITMAP + &FontFormat, // RTYPE_FONT + &RawFormat, // FIXME RTYPE_ANIM + NULL, // FIXME RTYPE_PALL + NULL, // FIXME RTYPE_SHADTAB + &RawFormat, // RTYPE_VOC (not translated) + NULL, // FIXME RTYPE_SHAPE + NULL, // FIXME RTYPE_PICT + NULL, // RTYPE_B2EXTERN (not used) + NULL, // RTYPE_B2RELOC (I think these are to do with the BABL + NULL, // RTYPE_B2CODE conversation engine used by the + NULL, // RTYPE_B2HEADER Underworld games. Not used by System + NULL, // RTYPE_B2RESRVD Shock.) + // OBJ3D types should have at least some translation, but looking at the + // way the format is actually handled, I'm adopting the strategy of "back + // away slowly, smiling but not showing your teeth" at the moment. + &RawFormat, // FIXME RTYPE_OBJ3D + NULL, // FIXME RTYPE_STENCIL + &RawFormat, // FIXME RTYPE_MOVIE +}; + +const ResourceFormat *ResLookUpFormat(Id id) { + ResDesc2 *prd2 = RESDESC2(id); + // If it's a compound resource, decode the ref table. + if (prd2->flags & RDF_COMPOUND) { + return FORMAT_REFTABLE; + } + // If it's a known resource type, return that format. + if (prd2->type <= MAX_SUPPORTED_TYPE) { + return ResTypeLayout[prd2->type]; + } + return NULL; +} + +// Find the appropriate format for refs (compound resources). +const ResourceFormat *RefLookUpFormat(Id id) { + ResDesc2 *prd2 = RESDESC2(id); + // Shouldn't be used for non-compound resources. + if (!(prd2->flags & RDF_COMPOUND)) { + return NULL; + } + // If it's a known resource type, return that format. + if (prd2->type <= MAX_SUPPORTED_TYPE) { + return ResTypeLayout[prd2->type]; + } + return NULL; +} diff --git a/engine/src/Libraries/RES/Source/resformat.h b/engine/src/Libraries/RES/Source/resformat.h new file mode 100644 index 0000000..e5ef1d8 --- /dev/null +++ b/engine/src/Libraries/RES/Source/resformat.h @@ -0,0 +1,94 @@ +/* + +Copyright (C) 2019 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#if !defined(RESFORMAT_H) +#define RESFORMAT_H + +// Types of resource field within a resfile. +typedef int ResFileFieldType; +#define RFFT_PAD 0 // not decoded, skip 'offset' bytes +#define RFFT_UINT8 1 // 8-bit integer +#define RFFT_UINT16 2 // 16-bit integer +#define RFFT_UINT32 3 // 32-bit integer +#define RFFT_INTPTR 4 // 32 bits on disc, 32 or 64 bits in memory. +#define RFFT_RAW 5 // raw data, copy 'offset' bytes or rest of resource if 0 +#define RFFT_END 6 // mark end of table +#define RFFT_BIN_BASE 0x100 + +#define RFFT_BIN(x) (RFFT_BIN_BASE+(x)) + +// Describes the layout of a resource structure. +typedef struct { + ResFileFieldType type; // type of field + size_t offset; // offset in memory +} ResField; + +typedef struct { + size_t dsize; // size of resource on disc + size_t msize; // size of resource in memory + uint32_t flags; // misc. info. + ResField fields[]; +} ResLayout; + +// Indicates that multiple records exist within a resource, each of 'dsize' +// bytes, up to the resource size. +#define LAYOUT_FLAG_ARRAY 0x01 +// Indicates that raw data (e.g. bitmap data) follows a header in the resource. +// The header is decoded according to the layout and the raw data is copied to +// immediately following it. +#define LAYOUT_FLAG_RAW_DATA_FOLLOWS 0x02 + +// User data for a resource decoding function. +typedef intptr_t UserDecodeData; +// Function to decode a resource loaded from disc. Takes raw data, size of raw +// data and user data. Returns decoded data. Default is to free decoded data +// using free() but the caller can supply a custom free function. +typedef void *(*ResDecodeFunc)(void*, size_t*, UserDecodeData); +// Encoder function. Same signature as decoder; takes cooked data and returns +// raw data for file, violating the laws of thermodynamics but allowing portable +// saving. Encoded data is always freed using free(). +typedef void *(*ResEncodeFunc)(void*, size_t*, UserDecodeData); +// Function to free decoded data, if free() won't cut it. +typedef void (*ResFreeFunc)(void*); + +// Decode a resource using a ResLayout. Prototyped as a decode function. +void *ResDecode(void *raw, size_t *size, UserDecodeData layout); +// Encode a resource using a ResLayout. +void *ResEncode(void *raw, size_t *size, UserDecodeData layout); + +// Describes the format of a resource for serialisation and deserialisation to +// and from disc file. +typedef struct { + ResDecodeFunc decoder; // deserialise data from disc. + ResEncodeFunc encoder; // serialise data to disc. + UserDecodeData data; // aux data, typically a pointer to a layout struct. + ResFreeFunc freer; // free cooked (only) data. +} ResourceFormat; + +// An empty ResourceFormat struct means no translation is needed. +extern const ResourceFormat RawFormat; +#define FORMAT_RAW (&RawFormat) + +// Make a format out of a layout. +#define RES_FORMAT(layout) \ + { ResDecode, ResEncode, (UserDecodeData)&layout, NULL } + +extern const ResourceFormat RefTableFormat; +#define FORMAT_REFTABLE (&RefTableFormat) + +#endif // !defined(RESFORMAT_H) diff --git a/engine/src/Libraries/RES/Source/resload.c b/engine/src/Libraries/RES/Source/resload.c new file mode 100644 index 0000000..bfdd971 --- /dev/null +++ b/engine/src/Libraries/RES/Source/resload.c @@ -0,0 +1,171 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// ResLoad.c Load resource from resfile +// Rex E. Bradford +/* + * $Header: n:/project/lib/src/res/rcs/resload.c 1.5 1994/06/16 11:07:44 rex Exp + * $ $Log: resload.c $ Revision 1.5 1994/06/16 11:07:44 rex Took LRU list + * adding out of ResLoadResource() + * + * Revision 1.4 1994/05/26 13:52:32 rex + * Surrounded Malloc() for loading resource with setting of idBeingLoaded, + * so installable pager can make use of this. + * + * Revision 1.3 1994/04/19 16:40:28 rex + * Added check for 0-size resource + * + * Revision 1.2 1994/03/14 16:10:47 rex + * Added id to spew in ResLoadResource() + * + * Revision 1.1 1994/02/17 11:23:39 rex + * Initial revision + * + */ + +#include +#include + +#include "lzw.h" +#include "res.h" +#include "res_.h" + +//------------------------------- +// Private Prototypes +//------------------------------- + +// Defined in resformat.c +extern const ResourceFormat *ResLookUpFormat(Id id); + +// ----------------------------------------------------------- +// +// ResLoadResource() loads a resource object, decompressing it if it is +// compressed. +// +// id = resource id +// ----------------------------------------------------------- + +void *ResLoadResource(Id id, const ResourceFormat *format) { + ResDesc *prd; + + // If doesn't exist, forget it + if (!ResInUse(id)) + return NULL; + if (!ResCheckId(id)) + return NULL; + + TRACE("%s loading $%x", __FUNCTION__, id); + + prd = RESDESC(id); + + if (prd->fsize == 0) { + return NULL; + } + // Format. If not specified, see if we can find a known format for the + // resource type. + if (format == NULL) { + format = ResLookUpFormat(id); + } + // No format isn't allowed at this stage. + if (format == NULL) { + ERROR("ResLoadResource(): unknown resource format %d", RESDESC2(id)->type); + return NULL; + } + + // Should not be called if resource is already loaded. + if (prd->ptr == NULL) { + // Allocate memory, setting magic id so pager can tell who it is if need be. + prd->ptr = malloc(prd->fsize); + if (prd->ptr == NULL) + return (NULL); + // Load from disk + ResRetrieve(id, prd->ptr); + } else { + assert(format->decoder != NULL); + } + // Set resource format. + prd->format = format; + // Decode if a decoder was supplied. + size_t size = prd->fsize; + if (format->decoder != NULL) { + void *decoded = format->decoder(prd->ptr, &size, format->data); + free (prd->ptr); + prd->ptr = decoded; + } + prd->msize = size; + + // Return ptr + return (prd->ptr); +} + +// --------------------------------------------------------- +// +// ResRetrieve() retrieves a resource from disk. +// +// id = id of resource +// buffer = ptr to buffer to load into (must be big enough) +// +// Returns: TRUE if retrieved, FALSE if problem + +bool ResRetrieve(Id id, void *buffer) { + ResDesc *prd; + ResDesc2 *prd2; + FILE *fd; + uint8_t *p; + int32_t size; + RefIndex numRefs; + + // Check id and file number + if (!ResCheckId(id)) { + TRACE("%s: failed ResCheckId! %x\n", __FUNCTION__, id); + return false; + } + + prd = RESDESC(id); + prd2 = RESDESC2(id); + fd = resFile[prd->filenum].fd; + + if (fd == NULL) { + WARN("%s: id $%x doesn't exist", __FUNCTION__, id); + return false; + } + + // Seek to data, set up + fseek(fd, RES_OFFSET_DESC2REAL(prd->offset), SEEK_SET); + p = (uint8_t *)buffer; + size = prd->fsize; + + // If compound, read in ref table + if (prd2->flags & RDF_COMPOUND) { + fread(p, sizeof(int16_t), 1, fd); + numRefs = *(uint16_t *)p; + p += sizeof(int16_t); + fread(p, sizeof(int32_t), (numRefs + 1), fd); + p += sizeof(int32_t) * (numRefs + 1); + size -= (p - (uint8_t*)buffer); + } + + // Read in data + if (prd2->flags & RDF_LZW) { + LzwExpandFp2Buff(fd, p, 0, 0); + } else { + fread(p, size, 1, fd); + } + + return true; +} diff --git a/engine/src/Libraries/RES/Source/resmake.c b/engine/src/Libraries/RES/Source/resmake.c new file mode 100644 index 0000000..26a4342 --- /dev/null +++ b/engine/src/Libraries/RES/Source/resmake.c @@ -0,0 +1,239 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// ResMake.c Resource making +// Rex E. Bradford +/* + * $Header: n:/project/lib/src/res/rcs/resmake.c 1.2 1994/06/16 11:08:04 rex Exp + * $ + * $Log: resmake.c $ + * Revision 1.2 1994/06/16 11:08:04 rex + * Modified LRU list handling, lock resource made with ResMake() instead of + * setting RDF_NODROP flag + * + * Revision 1.1 1994/02/17 11:23:57 rex + * Initial revision + * + */ + +#include +#include + +#include "res.h" +#include "res_.h" + +#define REFPTR(prt,index) (((char*)(prt)->raw_data)+(prt)->entries[index].offset) + +// -------------------------------------------------------- +// +// ResMake() makes a resource from a data block. +// +// Id = id of resource +// ptr = ptr to memory block (resource is not copied; this +// should point to storage where the resource can live +// indefinitely) +// size = size of resource in bytes +// type = resource type (RTYPE_XXX) +// filenum = file number +// flags = flags (RDF_XXX) +// -------------------------------------------------------- +// For Mac version, use Resource Manager to add the resource to indicated res +// file. + +void ResMake(Id id, void *ptr, size_t size, uint8_t type, int32_t filenum, uint8_t flags, const ResourceFormat *format) { + + TRACE("%s: Making id $%x", __FUNCTION__, id); + ResDesc *prd; + ResDesc2 *prd2; + + ResExtendDesc(id); + // Check for resource at that id. If the handle exists, then just change the + // handle (adjusting for size if needed, of course). + + // Extend res desc table if need to + prd = RESDESC(id); + prd2 = RESDESC2(id); + // If resource has id, delete it + if (prd->offset) { + ResDelete(id); + } + + // Add us to the soup, set lock so doesn't get swapped out + prd->ptr = ptr; + prd->format = format; + prd->fsize = 0; // not encoded yet + prd->msize = size; + prd->filenum = filenum; + prd->lock = 1; + prd->offset = RES_OFFSET_PENDING; + prd2->flags = flags; + prd2->type = type; +} + +#if 0 // not used by game code +// --------------------------------------------------------------- +// +// ResMakeCompound() makes an empty compound resource +// +// id = id of resource +// type = resource type (RTYPE_XXX) +// filenum = file number +// flags = flags (RDF_XXX, RDF_COMPOUND automatically added) + +void ResMakeCompound(Id id, uint8_t type, int32_t filenum, uint8_t flags) { + RefTable *prt; + int32_t sizeTable; + + // Build empty compound resource in allocated memory + TRACE("%s: making compound resource $%x", __FUNCTION__, id); + + sizeTable = REFTABLESIZE(0); + prt = (RefTable *)malloc(sizeTable); + prt->numRefs = 0; + prt->raw_data = ((uint8_t*)prt) + sizeTable; + + // Make a resource out of it + ResMake(id, prt, sizeTable, type, filenum, flags | RDF_COMPOUND); +} + +// --------------------------------------------------------------- +// +// ResAddRef() adds an item to a compound resource. +// +// ref = reference +// pitem = ptr to item's data (copied from here, unlike simple +// resource) +// itemSize = size of item +// --------------------------------------------------------------- +// FIXME needs rework to add or replace a decoded item. + +void ResAddRef(Ref ref, void *pitem, int32_t itemSize) { + ResDesc *prd; + RefTable *prt; + RefIndex index, i; + int32_t sizeEntries, oldSize, sizeDiff; + + // Error check + if (!RefCheckRef(ref)) + return; + + // Get vital info (and get into memory if not already) + TRACE("%s: adding ref $%x\n", __FUNCTION__, ref); + + prd = RESDESC(REFID(ref)); + + prt = (RefTable *)prd->ptr; + if (prt == NULL) { + prt = (RefTable *)ResGet(ref); + } + + // If index within current range of compound resource, replace or insert + index = REFINDEX(ref); + if (index < prt->numRefs) { + oldSize = RefSize(prt, index); + + // If same size, just copy in + if (itemSize == oldSize) { + // Spew(DSRC_RES_Make, ("ResAddRef: replacing same size ref\n")); + memcpy(REFPTR(prt, index), pitem, itemSize); + } + // Else if new item smaller, reduce offsets, shift data, insert new data + else if (itemSize < oldSize) { + // Spew(DSRC_RES_Make, ("ResAddRef: replacing larger ref\n")); + sizeDiff = oldSize - itemSize; + + for (i = index + 1; i <= prt->numRefs; i++) + prt->entries[i].offset -= sizeDiff; + prd->size -= sizeDiff; + memmove(REFPTR(prt, index + 1), REFPTR(prt, index + 1) + sizeDiff, + prt->entries[prt->numRefs].offset - prt->entries[index + 1].offset); + memcpy(REFPTR(prt, index), pitem, itemSize); + prt = realloc(prt, prd->size); + prd->ptr = prt; + prt->raw_data = (void*)((char*)prt + REFTABLESIZE(prt->numRefs)); + } else { + // New item is larger. + // Spew(DSRC_RES_Make, ("ResAddRef: replacing smaller ref\n")); + sizeDiff = itemSize - oldSize; + + prd->size += sizeDiff; + prt = realloc(prt, prd->size); + prd->ptr = prt; + prt->raw_data = (void*)((char*)prt + REFTABLESIZE(prt->numRefs)); + memmove(REFPTR(prt, index + 1) + sizeDiff, REFPTR(prt, index + 1), + prt->entries[prt->numRefs].offset - prt->entries[index + 1].offset); + + for (i = index + 1; i <= prt->numRefs; i++) + prt->entries[i].offset += sizeDiff; + memcpy(REFPTR(prt, index), pitem, itemSize); + } + // Update the size in the table. + prt->entries[index].size = itemSize; + } else { + // Else if index exceeds current range, expand + // Spew(DSRC_RES_Make, ("ResAddRef: extending compound resource\n")); + + // Extend resource for new offset(s) and data item + sizeEntries = sizeof(RefTableEntry) * ((index + 1) - prt->numRefs); + prd->size += sizeEntries + itemSize; + prd->ptr = realloc(prd->ptr, prd->size); + prt = (RefTable *)prd->ptr; + + // Shift data upwards to make room for new offset(s) + memmove(REFPTR(prt, 0) + sizeEntries, REFPTR(prt, 0), prd->size - REFTABLESIZE(index + 1)); + prt->raw_data = (void*)((char*)prt) + REFTABLESIZE(index); + + // Advance old offsets, set new ones + // No need to update offsets, they are relative to the raw_data pointer + // which we've already updated. + // for (i = 0; i <= prt->numRefs; i++) { + // prt->offset[i] += sizeItemOffsets; + // } + + for (i = prt->numRefs; i < index; i++) { + prt->entries[i].offset = prt->entries[prt->numRefs-1].offset; + } + // Save size of whole dir entry + // prt->offset[index + 1] = prt->offset[index] + itemSize; + + // Copy data into place, set new numRefs + memcpy(REFPTR(prt, index), pitem, itemSize); + prt->entries[index].size = itemSize; + prt->numRefs = index + 1; + } +} +#endif + +// ------------------------------------------------------------- +// +// ResUnmake() removes a resource from the LRU list and sets its +// ptr to NULL. In this way, a program may take over management +// of the resource data, and the RES system forgets about it. +// This is typically done when user-managed data needs to be +// written to a resource file, using ResMake(), ResWrite(), +// ResUnmake(). +// +// id = id of resource to unmake +// -------------------------------------------------------- +// For Mac version: use ReleaseResource to free the handle (the pointer that +// the handle was made from will still be around). + +void ResUnmake(Id id) { + ResDesc *prd = RESDESC(id); + memset(prd, 0, sizeof(ResDesc)); +} diff --git a/engine/src/Libraries/RES/Source/restypes.c b/engine/src/Libraries/RES/Source/restypes.c new file mode 100644 index 0000000..519c18c --- /dev/null +++ b/engine/src/Libraries/RES/Source/restypes.c @@ -0,0 +1,84 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// ResTypes.C Resource type names +// Rex E. Bradford (REX) +// +// See the doc RESOURCE.DOC for information. +/* + * $Header: r:/prj/lib/src/res/rcs/restypes.c 1.11 1994/11/18 13:43:54 mahk Exp + * $ $log: $ + */ + +#include "res.h" + +// Resource type names + +char *resTypeNames[NUM_RESTYPENAMES] = { + "UNKNOWN", // RTYPE_UNKNOWN (aka BIN) + "STRING", // RTYPE_STRING + "IMAGE", // RTYPE_IMAGE (aka IMG) + "FONT", // RTYPE_FONT + "ANIM", // RTYPE_ANIM + "PALL", // RTYPE_PALL + "SHADTAB", // RTYPE_SHADTAB + "VOC", // RTYPE_VOC + "SHAPE", // RTYPE_SHAPE + "PICT", // RTYPE_PICT + "B2EXTERN", // RTYPE_B2EXTERN + "B2RELOC", // RTYPE_B2RELOC + "B2CODE", // RTYPE_B2CODE + "B2HEADER", // RTYPE_B2HEADER + "hey!", // RTYPE_B2RESRVD + "OBJ3D", // RTYPE_OBJ3D + "STENCIL", // RTYPE_STENCIL + "MOVIE", // RTYPE_MOVIE + "RECT", // 18 + "", // 19 + "", // 20 + "", // 21 + "", // 22 + "", // 23 + "", // 24 + "", // 25 + "", // 26 + "", // 27 + "", // 28 + "", // 29 + "", // 30 + "", // 31 + "", // 32 + "", // 33 + "", // 34 + "", // 35 + "", // 36 + "", // 37 + "", // 38 + "", // 39 + "", // 40 + "", // 41 + "", // 42 + "", // 43 + "", // 44 + "", // 45 + "", // 46 + "", // 47 + "APP1", // RTYPE_APP + "APP2", "APP3", "APP4", "APP5", "APP6", "APP7", "APP8", "APP9", + "APP10", "APP11", "APP12", "APP13", "APP14", "APP15", "APP16", +}; diff --git a/engine/src/Libraries/RES/Source/restypes.h b/engine/src/Libraries/RES/Source/restypes.h new file mode 100644 index 0000000..4e0a4f1 --- /dev/null +++ b/engine/src/Libraries/RES/Source/restypes.h @@ -0,0 +1,99 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Restypes.H Resource types +// Rex E. Bradford (REX) + +/* + * $Header: r:/prj/lib/src/res/rcs/restypes.h 1.12 1994/12/07 14:19:07 mahk Exp + * $ $Log: restypes.h $ Revision 1.12 1994/12/07 14:19:07 mahk Added rect + * resource type. + * + * Revision 1.11 1994/09/01 12:00:22 rex + * Added RTYPE_MOVIE + * + * Revision 1.10 1994/05/02 17:47:02 rex + * Added RTYPE_STENCIL + * + * Revision 1.9 1994/05/02 14:49:23 rex + * Added RTYPE_SHADTAB + * + * Revision 1.8 1994/02/17 11:26:54 rex + * Changed #ifdef to use double-underscores + * + * Revision 1.7 1993/09/23 16:43:50 rex + * Added RTYPE_OBJ3D + * + * Revision 1.6 1993/07/23 16:41:17 rex + * Added RTYPE_B2HEADER + * + * Revision 1.5 1993/07/22 19:17:30 rex + * Added B2xxx types + * + * Revision 1.4 1993/06/17 10:00:41 rex + * Added RTYPE_PICT + * + * Revision 1.3 1993/06/02 13:50:42 dfan + * Added SHAPE types + * + * Revision 1.2 1993/05/11 18:51:02 rex + * Added RTYPE_VOC + * + * Revision 1.1 1993/03/04 18:48:04 rex + * Initial revision + * + * Revision 1.1 1993/03/02 18:42:05 rex + * Initial revision + * + */ + +#ifndef __RESTYPES_H +#define __RESTYPES_H + +#define NUM_RESTYPENAMES 64 + +// Resource types 0-47 are reserved for system-wide use + +#define RTYPE_UNKNOWN 0 // unknown resource type, or mix in compound +#define RTYPE_STRING 1 // string (usually in compound table) +#define RTYPE_IMAGE 2 // bitmapped image (usually in compound table) +#define RTYPE_FONT 3 // bitmapped font (usually non-compound) +#define RTYPE_ANIM 4 // animation script (usually in compound table) +#define RTYPE_PALL 5 // color pallette (usually non-compound) +#define RTYPE_SHADTAB 6 // shading table (usually non-compound) +#define RTYPE_VOC 7 // sound .voc file (usually non-compound) +#define RTYPE_SHAPE 8 // shape (usually compound) +#define RTYPE_PICT 9 // picture (usually compound) +#define RTYPE_B2EXTERN 10 // BABL2 extern records (always compound) +#define RTYPE_B2RELOC 11 // BABL2 relocation records (always non-compound) +#define RTYPE_B2CODE 12 // BABL2 object code (always compound) +#define RTYPE_B2HEADER 13 // BABL2 linked resource header +#define RTYPE_B2RESRVD 14 // BABL2 reserved +#define RTYPE_OBJ3D 15 // 3d object (always compound) +#define RTYPE_STENCIL 16 // stencil, with offsets (usually non-compound) +#define RTYPE_MOVIE 17 // movie (lg .mov format) +#define RTYPE_RECT \ + 18 // list of bounding rects for images (usually in + // compound table) +// Resource types 48-63 are application-specific +#define RTYPE_APP 48 // 16 application-specific resource types + +// Type names can be found thru this array (array kept in res.c) +extern char *resTypeNames[NUM_RESTYPENAMES]; + +#endif diff --git a/engine/src/Libraries/RND/Source/rnd.c b/engine/src/Libraries/RND/Source/rnd.c new file mode 100644 index 0000000..ef75a40 --- /dev/null +++ b/engine/src/Libraries/RND/Source/rnd.c @@ -0,0 +1,263 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Rnd.C Random stream implementation +// Rex E. Bradford (REX) +// +// INTRODUCTION +// +// Random streams constitute an interface for the functionality +// of a set of deterministic "random number" streams. These have +// the property of producing an unvarying set of values given the +// same starting "seed". This file includes implementations for +// several random number algorithms, which vary in their speed of +// calculation and "randomness" of their output. +// +// Using random streams over ad-hoc random number approaches has +// the following advantages. +// +// 1. Switching to a new, better random number algorithm involves +// only changing the random stream's declaration, not any calls +// to get new random values. +// +// 2. Several independent random streams may be operating concurrently, +// so that a module which needs a deterministic flow of random +// values will not be disturbed by other modules' needs. +// +// 3. By saving and restoring a random stream's seed, a repeatable +// flow of random values can be guaranteed. Each stream is +// independently controllable in this way. +// +// 4. Handy macros and functions are available to get a random +// value scaled into a range, converted to fixed-point format, etc. +// +// USING RANDOM STREAMS +// +// To use a random stream, first declare it, for instance: +// +// static RNDSTREAM_LC16(myRs); // usually static or global, but not necc. +// +// The random stream must be seeded! The declaration sets the seed +// value to 0, which may be inappropriate for some random streams, +// especially those which transform the seed into an alternate range +// or use seed time to create helper tables. You may reseed at any +// time: +// +// RndSeed(&myRs,savedSeed); // any ulong value will do +// +// You can get the next random value produced by the stream via a variety +// of macros and functions, depending on the type and range of random +// value you want: +// +// ulong rval = Rnd(&myRs); // get next value (16-bit generators +// // use high 16 bits, low 16 bits set to 0) +// +// long rval = RndRange(&myRs,low,high); // get next value scaled into +// // range from low to high, inclusive +// +// fix rval = RndFix(&myRs); // get next value as fix, range 0 to .9999 +// +// fix rval = RndRangeFix(&myRs,low,high); // get next value as fix, +// // scaled into range from low to high +// +// CREATING A NEW RANDOM STREAM CLASS +// +// To create a new random stream class, you only need to define one +// macro (in rnd.h) and two functions (in rnd.c, prototyped in rnd.h). +// For example: +// +// (in rnd.h): +// +// #define RNDSTREAM_WHIZ(name) RndStream name = {0,RndWhiz,RndWhizSeed}; +// ulong RndWhiz(RndStream *prs); +// void RndWhizSeed(RndStream *prs, ulong seed); +// +// (in rnd.c): +// +// ulong RndWhiz(RndStream *prs) +// { +// return(.....); +// } +// +// void RndWhizSeed(RndStream *prs, ulong seed) +// { +// ..... (any 32-bit value should be acceptable, transform if needed) +// prs->curr = ...; +// } +// +// If your random number generator normally works in 32-bit values, fine. +// If it works in values less than 32 bits wide, you must ensure that +// the values returned by your generator move those bits into the high +// bits of the ulong. Generators which use more than 32 bits are not +// currently supported. +/* +* $Header: n:/project/lib/src/rnd/RCS/rnd.c 1.2 1993/06/01 10:59:38 rex Exp $ +* $Log: rnd.c $ + * Revision 1.2 1993/06/01 10:59:38 rex + * Turned stack checking off + * + * Revision 1.1 1993/04/06 09:56:44 rex + * Initial revision + * +*/ + +#include +#include "lg.h" +#include "rnd.h" + +// For gruesome interrupt routines, let 'em have their way: + +//¥¥¥#pragma off(check_stack); + + +// --------------------------------------------------------------- +// ROUTINES WHICH SCALE RNUMS INTO RANGE +// --------------------------------------------------------------- +// +// RndRange() returns the next random value, scaled into an integer range. +// +// prs = ptr to random stream +// low = low value of range +// high = high value of range +// +// Returns: next random value scaled into range low->high, inclusive + +long RndRange(RndStream *prs, long low, long high) +{ + // HAX HAX HAX should use this library instead of using the std lib random + return (rand() % (high - low + 1)) + low; +} + +// ---------------------------------------------------------------- +// +// RndRangeFix() returns the next random value, scaled into fixed-point range. +// +// prs = ptr to random stream +// low = low value of fixed-point range +// high = high value of fixed-point range +// +// Returns: next random value scaled into range low->high + +fix RndRangeFix(RndStream *prs, fix low, fix high) +{ + // HAX HAX HAX should use this library instead of using the std lib random + float flow = fix_float(low); + float fhigh = fix_float(high); + return fix_from_float((float)rand()/(float)(RAND_MAX/(fhigh - flow)) + flow); +} + +// ----------------------------------------------------------------- +// RANDOM GENERATORS +// ----------------------------------------------------------------- +// +// RndLc16() uses a 16-bit linear conguential method. + +#define LC16_MULT 2053 +#define LC16_ADD 13849 + +ulong RndLc16(RndStream *prs) +{ + prs->curr = (prs->curr * LC16_MULT) + LC16_ADD; // only low 16 bits matter + return(prs->curr << 16); // move them to high 16 +} + +void RndLc16Seed(RndStream *prs, ulong seed) +{ + prs->curr = seed ^ (seed >> 16); // make sure something in low 16 bits +} + +// ----------------------------------------------------------------- +// +// RndGauss16() uses multiple passes of a linear conguential method to +// generate a random number with gaussian distribution. It is slow. + +#define NUM_PASSES + +ulong RndGauss16(RndStream *prs) +{ + long gauss; + ushort rnum; + int i; + + gauss = 0; + rnum = prs->curr; // prs->curr uses only low 16 bits + + for (i = 0; i < 6; i++) // add 6 rnums & subtract 6 + { + rnum = (rnum * LC16_MULT) + LC16_ADD; + gauss += rnum; + rnum = (rnum * LC16_MULT) + LC16_ADD; + gauss -= rnum; + } + + gauss /= 12; // scale by 12 since we did 12 rnums's + if (gauss < -32768) // and clamp to 16-bit short range + gauss = -32768; + else if (gauss > 32767) + gauss = 32767; + + prs->curr = gauss + 32768; // set our current rnum as ushort + + return(prs->curr << 16); // return in high 16 bits +} + +void RndGauss16Seed(RndStream *prs, ulong seed) +{ + prs->curr = seed ^ (seed >> 16); // make sure something in low 16 bits +} + +// ---------------------------------------------------------------- +// +// RndGauss16Fast() creates a gaussian distribution table the first +// time it is called, and then does a single lc random number to +// look up into the table with. + +#define NUM_GAUSSBITS 13 // we'll use 13 bits of our 16 bit rnums +#define SIZE_GAUSSTABLE (1<curr = (prs->curr * LC16_MULT) + LC16_ADD; + return((ulong)(gaussTable[prs->curr & MASK_GAUSSTABLE]) << 16); +} + +void RndGauss16FastSeed(RndStream *prs, ulong seed) +{ + int i; + ushort *pg; + +// If table not allocated, allocate it and fill it using the RndGauss16 +// generator. + + if (gaussTable == NULL) + { + pg = gaussTable = (ushort *)malloc(SIZE_GAUSSTABLE * sizeof(short)); + for (i = 0; i < SIZE_GAUSSTABLE; i++) + { + prs->curr = i; + *pg++ = RndGauss16(prs) >> 16; + } + } + + prs->curr = seed ^ (seed >> 16); // make sure something in low 16 bits +} diff --git a/engine/src/Libraries/RND/Source/rnd.h b/engine/src/Libraries/RND/Source/rnd.h new file mode 100644 index 0000000..518a388 --- /dev/null +++ b/engine/src/Libraries/RND/Source/rnd.h @@ -0,0 +1,95 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Rnd.H Random stream header file (see rnd.c for more info) +// Rex E. Bradford (REX) +/* +* $Header: n:/project/lib/src/rnd/RCS/rnd.h 1.2 1993/04/06 10:33:57 rex Exp $ +* $Log: rnd.h $ + * Revision 1.2 1993/04/06 10:33:57 rex + * Fixed RndSeed() macro to pass seed! + * + * Revision 1.1 1993/04/06 09:56:35 rex + * Initial revision + * +*/ + +#ifndef RND_H +#define RND_H + +#include "lg_types.h" +#include "fix.h" + +// A random stream + +typedef struct RndStream_ { + ulong curr; + ulong (*f_Next)(struct RndStream_ *prs); + void (*f_Seed)(struct RndStream_ *prs, ulong seed); +} RndStream; + +// To use a random stream, instantiate one (usually statically), +// seed it, and then make calls to get rnums, like so: +// +// static RNDSTREAM_STD(rs); // declare a stream +// RndSeed(&rs,22); // or maybe 23 +// rval = Rnd(&rs); // get any old rnum +// rval = RndRange(&rs,1,6); // throw dice +// rfix = RndFix(&rs); // maybe you'd like 0 to .9999 +// rfix = RndRangeFix(&rs,fl,fh); // or fixed point in a range + +// Here are the random stream type declaration macros + +#define RNDSTREAM_LC16(name) RndStream name = {0,RndLc16,RndLc16Seed} +#define RNDSTREAM_GAUSS16(name) RndStream name = {0,RndGauss16,RndGauss16Seed} +#define RNDSTREAM_GAUSS16FAST(name) RndStream name = {0,RndGauss16Fast,RndGauss16FastSeed} + +#define RNDSTREAM_STD(name) RNDSTREAM_LC16(name) + +// Seed a random stream + +#define RndSeed(prs,seed) ((prs)->f_Seed(prs,seed)) + +// Get next random # + +#define Rnd(prs) ((prs)->f_Next(prs)) + +// Get next random # and scale into fixed range 0.0 to .9999 + +#define RndFix(prs) (fix_make(0,Rnd(prs)>>16)) + +// Get next random # and scale into low->high range (high value included) + +long RndRange(RndStream *prs, long low, long high); + +// Get next random # and scale into low->high range + +fix RndRangeFix(RndStream *prs, fix low, fix high); + +// Prototypes for current set of random stream classes + +ulong RndLc16(RndStream *prs); +void RndLc16Seed(RndStream *prs, ulong seed); + +ulong RndGauss16(RndStream *prs); +void RndGauss16Seed(RndStream *prs, ulong seed); + +ulong RndGauss16Fast(RndStream *prs); +void RndGauss16FastSeed(RndStream *prs, ulong seed); + +#endif diff --git a/engine/src/Libraries/SND/Source/lgsndx.h b/engine/src/Libraries/SND/Source/lgsndx.h new file mode 100644 index 0000000..8a8bf86 --- /dev/null +++ b/engine/src/Libraries/SND/Source/lgsndx.h @@ -0,0 +1,193 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +//=================================================================== +// $Source: r:/prj/lib/src/snd/RCS/lgsndx.h $ +// $Revision: 1.1 $ +// $Author: dc $ +// $Date: 1994/12/01 02:06:32 $ +// +// header for new simpleotron sound library +//=================================================================== + +#ifndef __LGSNDX_H +#define __LGSNDX_H + +#include + +//#include +//#include + +//#include + +#include "lg.h" + +//-------------------------- +// Types +//-------------------------- + +typedef struct snd_digi_parms +{ + //SndChannelPtr sndChan; // Ptr to Mac sound channel. + uchar pan; + uchar pri; + ushort vol; + uchar flags; + int snd_ref; + int loops; + //Handle sample; // Handle to Mac 'snd ' resource. + uintptr_t data; + int len; +} snd_digi_parms; + +//typedef struct snd_digi_parms snd_digi_parms; + +typedef struct +{ + uchar pan; + uchar vol; + uchar pri; + uchar flags; + int snd_ref; + void *data; + int seq_num; + void *internal_form; +} snd_midi_parms; + +//typedef void LG_SND_DRIVER; + +//-------------------------- +// Globals +//-------------------------- +//extern LG_SND_DRIVER *snd_digi; +//extern LG_SND_DRIVER *snd_midi; +extern uchar snd_digi_vol; +extern uchar snd_midi_vol; +//extern void cdecl (*snd_update)(snd_digi_parms *dprm); +extern void (*snd_finish)(struct snd_digi_parms *dprm); +//extern void cdecl (*snd_nblock)(snd_digi_parms *dprm); +extern void (*seq_finish)(long seq_ind); +//extern void cdecl (*seq_miditrig)(snd_midi_parms *mprm, int trig_value); +extern int snd_error; +//extern int snd_genmidi_or_not; +//extern uchar snd_stereo_reverse; + +//-------------------------- +// Defines +//-------------------------- +//#define snd_sample_ptr_from_parms(ps) ((SAMPLE *)ps->internal_form) +//#define snd_sequence_ptr_from_parms(ps) ((SEQUENCE *)ps->internal_form) +//#define snd_sample_ptr_from_id(pid) ((SAMPLE *)snd_get_sample(pid)) +#define snd_sequence_ptr_from_id(pid) ((SEQUENCE *)snd_get_sequence(pid)) + +//-------------------------- +// Prototypes +//-------------------------- +void snd_startup(void); +void snd_setup(void *d_path, char *prefix); +void snd_shutdown(void); +int snd_set_midi_sequences(int chan_cnt); +int snd_start_digital(void); +int snd_stop_digital(void); +int snd_set_digital_channels(int chan_cnt); +int snd_start_midi(void); +int snd_stop_midi(void); + +int snd_sample_play(int snd_ref, int len, uchar *smp, struct snd_digi_parms *dprm); +int snd_alog_play(int snd_ref, int len, Uint8 *smp, struct snd_digi_parms *dprm); +void snd_end_sample(int hnd_id); +bool snd_sample_playing(int hnd_id); +struct snd_digi_parms *snd_sample_parms(int hnd_id); +//void *snd_get_sample(int hnd_id); +void snd_kill_all_samples(void); +void snd_sample_reload_parms(struct snd_digi_parms *sdp); + +int snd_find_free_sequence(void); +//int snd_sequence_play(int snd_ref, uchar *seq_dat, int seq_num, snd_midi_parms *mparm); +//snd_midi_parms *snd_sequence_parms(int hnd_id); + +typedef struct { + char unused; +} TunePlayer; // DG: hack so compiler shuts up about the two functions using this + +TunePlayer snd_get_sequence(int seq_id); +void snd_end_sequence(int seq_id); +void snd_kill_all_sequences(void); + +//char *snd_load_raw(char *fname, int *ldat); +// Mac only routines. +//OSErr snd_load_theme(FSSpec *specPtr, TunePlayer thePlayer); +void snd_release_current_theme(void); + +//-------------------------- +// Parm + flag defines for digital +//-------------------------- +#define SND_DEF_PRI 0x3f +#define SND_PARM_NULL 0xff +#define SND_DEF_PAN 64 + +#define SND_FLG_DOUBLE_BUFFER 0x01 +#define SND_FLG_INUSE 0x08 +#define SND_FLG_RAWDATA 0x80 +#define SND_FLG_RAWD_STEREO 0x40 +#define SND_FLG_SPEED 0x30 +#define SND_FLG_RAWMASK 0xF0 + +//-------------------------- +// Size/scale defines +//-------------------------- +#define SND_MAX_SAMPLES 12 // For Mac version. +#define SND_MAX_SEQUENCES 8 //¥¥¥ Can I really handle this many? + +//-------------------------- +// Misc defines +//-------------------------- +#define SND_HND_FIELD 0 // where in user data handle is stored + +//-------------------------- +// Capabilities field +//-------------------------- +#define SND_CAP_DIGI 1 +#define SND_CAP_MIDI 2 +#define SND_CAP_GENMIDI 4 +#define SND_CAP_STEREO 8 +#define SND_CAP_GAIN 16 + +//-------------------------- +// Return/Error codes +//-------------------------- +#define SND_OK 0x0000 + +// error codes +#define SND_GENERIC_ERROR 0x0001 +#define SND_OUT_OF_MEMORY 0x0002 +#define SND_NO_DRIVER 0x0003 +#define SND_NOT_SUPPORTED 0x0004 + +// specialized errors +#define SND_DRIVER_ALREADY 0x0100 +#define SND_NO_DRIVER_NAME 0x0101 +#define SND_CANT_INIT_DRIVER 0x0102 +#define SND_CANT_FIND_CARD 0x0103 + +#define SND_NO_HANDLE 0x0200 + +// to use with functions which want to return 0->n values +#define SND_PERROR -1 + +#endif // __LGSNDX_H diff --git a/engine/src/Libraries/UI/Source/curdat.h b/engine/src/Libraries/UI/Source/curdat.h new file mode 100644 index 0000000..b387b3a --- /dev/null +++ b/engine/src/Libraries/UI/Source/curdat.h @@ -0,0 +1,49 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/ui/RCS/curdat.h $ + * $Revision: 1.2 $ + * $Author: mahk $ + * $Date: 1994/02/07 16:14:57 $ + * + * Declarations for cursor globals. + * + * $Log: curdat.h $ + * Revision 1.2 1994/02/07 16:14:57 mahk + * Changed canvas variables. + * + * Revision 1.1 1993/12/16 07:46:35 kaboom + * Initial revision + * + */ + +#include "curtyp.h" + +extern int MouseLock; +extern LGRegion* CursorRegion; +extern LGCursor* CurrentCursor; +extern LGCursor* LastCursor; +extern LGRegion* LastCursorRegion; +extern LGPoint LastCursorPos; +extern LGRect* HideRect; +extern int curhiderect; +extern grs_canvas* CursorCanvas; +extern grs_canvas DefaultCursorCanvas; +extern struct _cursor_saveunder SaveUnder; + diff --git a/engine/src/Libraries/UI/Source/curdrw.c b/engine/src/Libraries/UI/Source/curdrw.c new file mode 100644 index 0000000..1f0b334 --- /dev/null +++ b/engine/src/Libraries/UI/Source/curdrw.c @@ -0,0 +1,166 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/ui/RCS/curdrw.c $ + * $Revision: 1.4 $ + * $Author: mahk $ + * $Date: 1994/04/05 00:19:46 $ + * + * Cursor drawing routines called from the interrupt handler. + * + * $Log: curdrw.c $ + * Revision 1.4 1994/04/05 00:19:46 mahk + * Added hflipped cursors. Wacky. + * + * Revision 1.3 1994/02/07 15:58:02 mahk + * now we use clipped get_bitmap for saveunder. + * + * Revision 1.2 1994/02/07 16:14:09 mahk + * Changed the canvas to be more renderer/compatible. + * + * Revision 1.1 1993/12/16 07:45:47 kaboom + * Initial revision + * + */ + +#include + +#include "lg.h" +#include "2d.h" +#include "cursors.h" +#include "curdat.h" +#include "string.h" + +#define BMT_SAVEUNDER BMT_FLAT8 + +#pragma require_prototypes off + +//----------------------------------------------------------- +void cursor_draw_callback(ss_mouse_event* e, void* data) +{ + LGPoint pos; +#ifndef NO_DUMMIES + void *dummy; dummy = (void *)e; dummy = data; +#endif + + if (MouseLock > 0) return; + MouseLock++; + pos.x = e->x; + pos.y = e->y; + if (CurrentCursor != NULL) + { + pos.x -= CurrentCursor->hotspot.x; + pos.y -= CurrentCursor->hotspot.y; + } + if (LastCursor != NULL) + { + if (abs(LastCursorPos.x - pos.x) + abs(LastCursorPos.y - pos.y) <= CursorMoveTolerance) + goto out; + LastCursor->func(CURSOR_UNDRAW,LastCursorRegion,LastCursor,LastCursorPos); + } + if (CurrentCursor != NULL) + { + LGRect cr; + cr.ul = pos; + cr.lr.x = pos.x + CurrentCursor->w; + cr.lr.y = pos.y + CurrentCursor->h; + if (RECT_TEST_SECT(&HideRect[curhiderect],&cr)) + { + goto out; + } + CurrentCursor->func(CURSOR_DRAW,CursorRegion,CurrentCursor,pos); + LastCursor = CurrentCursor; + LastCursorPos = pos; + LastCursorRegion = CursorRegion; + } + out: + MouseLock--; +} + +// --------------------- +// BITMAP CURSOR SUPPORT +// --------------------- + +#define GR_BITMAP gr_bitmap +#define GR_GET_BITMAP gr_get_bitmap +#define GR_HFLIP_BITMAP_IN_PLACE(x) + +static grs_canvas* old_canvas = NULL; +bool doubleUndraw = FALSE; + +//----------------------------------------------------------- +void bitmap_cursor_drawfunc(int cmd, LGRegion* r, LGCursor* c, LGPoint pos) +{ + grs_bitmap* bm = (grs_bitmap*)(c->state); +#ifndef NO_DUMMIES + LGRegion *dummy; dummy = r; +#endif + + // set up screen canvas + old_canvas = grd_canvas; + gr_set_canvas(CursorCanvas); + switch(cmd) + { + case CURSOR_UNDRAW: + if (doubleUndraw) + { + grs_bitmap temp; + gr_init_sub_bitmap(&SaveUnder.bm, &temp, 0, 0, SaveUnder.bm.w >> 1, SaveUnder.bm.h >> 1); + gr_scale_bitmap(&temp, pos.x, pos.y, SaveUnder.bm.w, SaveUnder.bm.h); + } + else + GR_BITMAP(&SaveUnder.bm,pos.x,pos.y); + break; + + case CURSOR_DRAW: + // Get saveunder + gr_init_bm(&SaveUnder.bm,SaveUnder.bm.bits,BMT_SAVEUNDER,0,bm->w,bm->h); + GR_GET_BITMAP(&SaveUnder.bm,pos.x,pos.y); + // Blit over the save under + GR_BITMAP(bm,pos.x,pos.y); + doubleUndraw = FALSE; + break; + + case CURSOR_DRAW_HFLIP: + pos.x -= bm->w-1; + // Get saveunder + gr_init_bm(&SaveUnder.bm,SaveUnder.bm.bits,BMT_SAVEUNDER,0,bm->w,bm->h); +// GR_HFLIP_BITMAP_IN_PLACE(&SaveUnder.bm); + GR_GET_BITMAP(&SaveUnder.bm,pos.x,pos.y); +// GR_HFLIP_BITMAP_IN_PLACE(&SaveUnder.bm); + // Blit over the save under + GR_BITMAP(bm,pos.x,pos.y); +// gr_hflip_bitmap(bm,pos.x,pos.y); +// doubleUndraw = FALSE; + break; + + case 3: // Scale cursor down half-size. + gr_init_bm(&SaveUnder.bm,SaveUnder.bm.bits,BMT_SAVEUNDER,0,bm->w,bm->h); + GR_GET_BITMAP(&SaveUnder.bm,pos.x,pos.y); + gr_scale_bitmap(bm, pos.x, pos.y, (bm->w >> 1), (bm->h >> 1)); + doubleUndraw = TRUE; + break; + + case 4: // Scale cursor down half-size, don't save the background. + gr_scale_bitmap(bm, pos.x, pos.y, (bm->w >> 1), (bm->h >> 1)); + doubleUndraw = FALSE; + break; + } + gr_set_canvas(old_canvas); +} diff --git a/engine/src/Libraries/UI/Source/cursors.c b/engine/src/Libraries/UI/Source/cursors.c new file mode 100644 index 0000000..c366872 --- /dev/null +++ b/engine/src/Libraries/UI/Source/cursors.c @@ -0,0 +1,780 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/ui/RCS/cursors.c $ + * $Revision: 1.24 $ + * $Author: mahk $ + * $Date: 1994/08/24 08:55:41 $ + * + * $Log: cursors.c $ + * Revision 1.24 1994/08/24 08:55:41 mahk + * Cursor stacks and invisible regions. + * + * Revision 1.23 1994/08/22 04:19:08 mahk + * Made real anal cursor_stack spew. + * + * Revision 1.22 1994/02/10 08:15:25 mahk + * Actually caused uiSetDefaultSlabCursor or whatever to actually work as specified instead + * of obliterating the top of the cursor stack. + * + * Revision 1.21 1994/02/07 16:14:43 mahk + * Changed the canvas to be more renderer/compatible. + * + * Revision 1.20 1994/02/06 06:05:03 mahk + * Fixed stupid uiPopSlabCursor bug. + * + * Revision 1.19 1994/01/19 20:52:11 mahk + * Fixed black cursor bug yay. + * + * Revision 1.18 1994/01/16 04:37:40 mahk + * Fixed hide/show logic + * + * Revision 1.17 1993/12/16 07:44:07 kaboom + * Moved code actually called from interrupt handler to another + * file and made relevant statics public. + * + * Revision 1.16 1993/10/20 05:13:00 mahk + * No longer do we push a canvas in an interrupt. + * + * Revision 1.15 1993/10/11 20:26:32 dc + * Angle is fun, fun fun fun + * + * Revision 1.14 1993/09/09 19:20:10 mahk + * Fixed heap trashage. + * + * Revision 1.13 1993/08/16 18:24:28 xemu + * fixed some spew + * + * Revision 1.12 1993/08/02 14:21:10 mahk + * Save under now grows whenever a bitmap cursor is made + * + * Revision 1.11 1993/06/08 23:59:36 mahk + * Cursors now clip. + * + * Revision 1.10 1993/05/27 12:49:01 mahk + * Lock out reentrance in interrupt mouse drawing. + * Mouse rectangle stack grows in size if necessary. + * + * Revision 1.9 1993/05/26 16:33:55 mahk + * Added REAL mouse tolerance. + * + * Revision 1.8 1993/05/26 03:21:54 mahk + * Added way-cool rectangle protection for mousehide, and pixel move tolerance for cursor + * draw. + * + * Revision 1.7 1993/05/25 19:55:14 mahk + * Fixed mousehide and show to not leave droppings. + * + * Revision 1.6 1993/04/28 15:59:23 mahk + * conversion to libdbg + * + * Revision 1.5 1993/04/28 14:39:52 mahk + * Preparing for second exodus + * + * Revision 1.4 1993/04/13 23:18:35 mahk + * Added lots of debugging spews. + * + * Revision 1.3 1993/04/08 17:52:07 mahk + * The interrupt handler callback, now, in fact, no longer draws the + * mouse cursor when it is hidden. Go Figure. + * + * Revision 1.2 1993/04/05 23:36:01 unknown + * Added hide/show and slab support. + * + * Revision 1.1 1993/03/31 23:17:24 mahk + * Initial revision + */ + +#include +#include + +#include "lg.h" +#include "2d.h" +#include "cursors.h" +#include "curtyp.h" +//#include +#include "vmouse.h" +#include // printf() + +#define SPEW_ANAL Spew + +#define BMT_SAVEUNDER BMT_FLAT8 +#define MAPSIZE(x,y) ((x)*(y)) +#define CURSOR_STACKSIZE 5 +#define STARTING_SAVEUNDER_WD 16 +#define STARTING_SAVEUNDER_HT 16 + +// ------------------- +// DEFINES AND GLOBALS +// ------------------- + +// Global: the saveunder for bitmap cursors +struct _cursor_saveunder SaveUnder; + +extern uiSlab* uiCurrentSlab; +#define RootCursorRegion (uiCurrentSlab->creg) + +// The region currently occupied by the cursor, and the current cursor to be drawn + +LGRegion* CursorRegion = NULL; +LGCursor* CurrentCursor = NULL; + + +// The last cursor region, for when we undraw. + +LGPoint LastCursorPos; +LGRegion* LastCursorRegion = NULL; +LGCursor* LastCursor = NULL; + + +// A semaphore which tells the mouse interrupt handler that the mouse is hidden +int MouseLock = 0; + +// A protected rectangle stack for mouse hide/show +#define INITIAL_RECT_STACK 5 +LGRect* HideRect; +int numhiderects = INITIAL_RECT_STACK; +int curhiderect = 0; + + +// The canvas used by cursors +grs_canvas DefaultCursorCanvas; +grs_canvas* CursorCanvas = &DefaultCursorCanvas; + + +// Number of pixels to move before interrupt handler draws +int CursorMoveTolerance = 0; + + +// ------------------ +// INTERNAL PROTOTYPES +// ------------------ +typedef struct _cursor_callback_state +{ + LGCursor** out; + LGRegion** reg; +} cstate; + + + +// ------------------ +// INTERNAL FUNCTIONS +// ------------------ + +// KLC - now just allocates a bitmap at the beginning and never actually grows it. +static errtype grow_save_under(short x, short y) +{ + int sz = MAPSIZE(x,y); + if (SaveUnder.mapsize >= sz) return ERR_NOEFFECT; + + // Clear out old SaveUnder + if(SaveUnder.mapsize > 0) { + free(SaveUnder.bm.bits); + } + + // Grow bigger than we actually need so that this doesn't happen all the time + int newsize = sz * 2; + SaveUnder.bm.bits = (uchar *)malloc(newsize); + SaveUnder.mapsize = newsize; + return OK; +} + + +uchar cursor_get_callback(LGRegion* reg, LGRect* rect, void *vp) +{ + cstate *s = (cstate *)vp; + cursor_stack* cs = (cursor_stack*)(reg->cursors); + uchar anal = FALSE; + //DBG(DSRC_UI_Anal, { anal = TRUE;}); + //if (anal) SPEW_ANAL(DSRC_UI_Cursor_Stack,("cursor_get_callback(%x,%x,%x)\n",reg,rect,s)); + if (cs == NULL) *(s->out) = NULL; + else + { + *(s->out) = cs->stack[cs->fullness-1]; + *(s->reg) = reg; + } + if (*(s->out) == NULL && uiCurrentSlab != NULL) + { + //if (anal) SPEW_ANAL(DSRC_UI_Cursor_Stack,("cursor_get_callback(): using global default\n")); + *(s->out) = uiCurrentSlab->cstack.stack[0]; + } + return *(s->out) != NULL; +} + +#define cstack_init uiMakeCursorStack + +errtype uiMakeCursorStack(cursor_stack* res) +{ + // Spew(DSRC_UI_Cursor_Stack,("cstack_init(%x)\n",res)); + if (res == NULL) + return ERR_NULL; + res->size = CURSOR_STACKSIZE; + res->stack = (LGCursor**)malloc(sizeof(LGCursor*) * res->size); + if (res->stack == NULL) + { + free(res); + return ERR_NOMEM; + } + res->fullness = 1; + res->stack[0] = NULL; + return OK; +} + +#define ui_destroy_cursor_stack uiDestroyCursorStack + + +errtype uiDestroyCursorStack(cursor_stack* cstack) +{ + // Spew(DSRC_UI_Cursor_Stack,("ui_destroy_cursor_stack(%x) \n",cstack)); + if (cstack == NULL) return ERR_NULL; + if (cstack->size== 0) return ERR_NOEFFECT; + cstack->size = 0; + free(cstack->stack); + return OK; +} + + + +errtype uiSetRegionCursorStack(LGRegion* r, uiCursorStack* cs) +{ + if (r == NULL) return ERR_NULL; + r->cursors = cs; + return OK; +} + +errtype uiGetSlabCursorStack(uiSlab* slab, uiCursorStack** cs) +{ + if (slab == NULL) return ERR_NULL; + *cs = &slab->cstack; + return OK; +} + +errtype uiSetDefaultCursor(uiCursorStack* cs, LGCursor* c) +{ + if (cs == NULL) return ERR_NULL; + cs->stack[0] = c; + return OK; +} + + +errtype uiGetDefaultCursor(uiCursorStack* cs, LGCursor** c) +{ + if (cs == NULL) return ERR_NULL; + *c = cs->stack[0]; + return OK; +} + + +#define cs_push uiPushCursor + +errtype uiPushCursor(cursor_stack* cs, LGCursor* c) +{ + if (cs == NULL) return ERR_NULL; + if (cs->fullness >= cs->size) + { + LGCursor** tmp = (LGCursor**)malloc(cs->size*2*sizeof(LGCursor*)); + //SPEW_ANAL(DSRC_UI_Cursor_Stack,("cs_push(%x,%x), growing stack\n",cs,c)); + if (tmp == NULL) return ERR_NOMEM; + LG_memcpy(tmp,cs->stack,cs->size*sizeof(LGCursor*)); + free(cs->stack); + cs->stack = tmp; + cs->size *= 2; + } + cs->stack[cs->fullness++] = c; + return OK; +} + + +errtype uiPopCursor(uiCursorStack* cs) +{ + if (cs == NULL) return ERR_NULL; + if (cs->fullness <= 1) return ERR_DUNDERFLOW; + cs->fullness--; + return OK; +} + +errtype uiGetTopCursor(uiCursorStack* cs, LGCursor** c) +{ + if (cs == NULL) return ERR_NULL; + if (cs->fullness <= 1) + return ERR_DUNDERFLOW; + *c = cs->stack[cs->fullness-1]; + return OK; +} + + +errtype uiPushCursorOnce(uiCursorStack* cs, LGCursor* c) +{ + LGCursor* top = NULL; + errtype err = uiGetTopCursor(cs,&top); + if (err == ERR_DUNDERFLOW) top = NULL; + else if (err != OK) return err; + if (top != c) + err = uiPushCursor(cs,c); + return err; +} + +// I wish I had time to implement a non-boneheaded recursive version. +errtype uiPopCursorEvery(uiCursorStack* cs, LGCursor* c) +{ + LGCursor* top = NULL; + errtype err = uiGetTopCursor(cs,&top); + if (err == ERR_DUNDERFLOW) return OK; + else if (err != OK) return err; + uiPopCursor(cs); + err = uiPopCursorEvery(cs,c); + if (top != c) + { + errtype newerr = uiPushCursor(cs,top); + if (newerr != OK) return newerr; + } + return err; +} + + +#define get_region_stack uiGetRegionCursorStack + +errtype get_region_stack(LGRegion*r, cursor_stack** cs) +{ + cursor_stack* res = (cursor_stack*)(r->cursors); + if (res == NULL) + { + errtype err; + //SPEW_ANAL(DSRC_UI_Cursor_Stack,("get_region_stack(%x,%x), creating stack\n",r,cs)); + r->cursors = res = (cursor_stack *)malloc(sizeof(cursor_stack)); + if (res == NULL) + return ERR_NOMEM; + err = cstack_init(res); + if (err != OK) return err; + } + *cs = res; + return OK; +} + +// -------------------- +// UI Toolkit internals +// -------------------- + + +static int uiCursorCallbackId; + +errtype ui_init_cursor_stack(uiSlab* slab, LGCursor* default_cursor) +{ + errtype err = cstack_init(&slab->cstack); + // Spew(DSRC_UI_Cursor_Stack,("ui_init_cursor_stack(%x,%x) err = %d\n",slab,default_cursor,err)); + if (err != OK) return err; + slab->cstack.stack[0] = default_cursor; + return OK; +} + +extern void cursor_draw_callback(ss_mouse_event* e, void* data); +extern void bitmap_cursor_drawfunc(int cmd, LGRegion* r, LGCursor* c, LGPoint pos); + +errtype ui_init_cursors(void) +{ + errtype err; + // Spew(DSRC_UI_Cursors ,("ui_init_cursors()\n")); + grow_save_under(STARTING_SAVEUNDER_WD,STARTING_SAVEUNDER_HT); + // KLC - just initalize it to a sizeable bitmap, and leave it that way. + //SaveUnder.bm.bits = (uchar *)malloc(6144); + //SaveUnder.mapsize = 6144; + + LastCursor = NULL; + MouseLock = 0; + + gr_init_sub_canvas(grd_scr_canv,&DefaultCursorCanvas,0,0,grd_cap->w,grd_cap->h); + gr_cset_cliprect(&DefaultCursorCanvas, + 0,0,grd_cap->w,grd_cap->h); + err = mouse_set_callback(cursor_draw_callback,NULL,&uiCursorCallbackId); + if (err != OK) return err; + HideRect = (LGRect *)malloc(sizeof(LGRect)*INITIAL_RECT_STACK); + HideRect[0].ul.x = -32768; + HideRect[0].ul.y = -32768; + HideRect[0].lr = HideRect[0].ul; + return OK; +} + +errtype uiUpdateScreenSize(LGPoint size) +{ + short w = size.x; + short h = size.y; + if (size.x == UI_DETECT_SCREEN_SIZE.x) + w = grd_screen_canvas->bm.w; + if (size.y == UI_DETECT_SCREEN_SIZE.y) + h = grd_screen_canvas->bm.h; + + gr_init_sub_canvas(grd_scr_canv,&DefaultCursorCanvas,0,0,w,h); + gr_cset_cliprect(&DefaultCursorCanvas,0,0,w,h); +// mouse_set_screensize(w,h); +// mouse_constrain_xy(0,0,w,h); + return(OK); +} + +errtype ui_shutdown_cursors(void) +{ + errtype err; + // Spew(DSRC_UI_Cursors,("ui_shutdown_cursors()\n")); + free(SaveUnder.bm.bits); + err = mouse_unset_callback(uiCursorCallbackId); + return err; +} + +uchar ui_set_current_cursor(LGPoint pos) +{ + cstate s; + uchar result = FALSE; + + ui_mouse_do_conversion(&(pos.x),&(pos.y),TRUE); + // Spew(DSRC_UI_Cursors,("ui_set_current_cursor(<%d,%d>)\n",pos.x,pos.y)); + if (uiCurrentSlab == NULL) + { + //SPEW_ANAL(DSRC_UI_Cursors,("ui_set_current_cursor(): no current slab\n")); + result = FALSE; + goto out; + } + if (uiCurrentSlab->cstack.fullness > 1) + { + CurrentCursor = uiCurrentSlab->cstack.stack[uiCurrentSlab->cstack.fullness-1]; + CursorRegion = (uiCurrentSlab->creg); + result = CurrentCursor != NULL; + goto out; + } + if (RootCursorRegion == NULL) + { + //SPEW_ANAL(DSRC_UI_Cursors,("ui_set_current_cursor(): no root region\n")); + result = FALSE; + goto out; + } + s.out = &CurrentCursor; + s.reg = &CursorRegion; + + result = region_traverse_point(RootCursorRegion,pos,cursor_get_callback,TOP_TO_BOTTOM,&s); + out: + // Spew(DSRC_UI_Cursors,("ui_set_current_cursor(): current cursor = %x\n",CurrentCursor)); + return result; +} + +void ui_update_cursor(LGPoint pos) +{ + uchar show = ui_set_current_cursor(pos); +// ui_mouse_do_conversion(&(pos.x),&(pos.y),FALSE); + if (show && LastCursor != NULL && !PointsEqual(pos,LastCursorPos)) + { + MouseLock++; + LastCursor->func(CURSOR_UNDRAW,LastCursorRegion,LastCursor,LastCursorPos); + LastCursorPos = pos; + LastCursorPos.x -= CurrentCursor->hotspot.x; + LastCursorPos.y -= CurrentCursor->hotspot.y; + CurrentCursor->func(CURSOR_DRAW, + CursorRegion, + CurrentCursor, + LastCursorPos); + LastCursor = CurrentCursor; + LastCursorRegion = CursorRegion; + MouseLock--; + } +} + + +// ------------- +// API FUNCTIONS +// ------------- + + +errtype uiSetCursor(void) +{ + LGPoint pos; + uchar show = MouseLock == 0; + errtype retval = OK; + // Spew(DSRC_UI_Cursors,("uiSetCursor(), MouseLock = %d\n",MouseLock)); + if (!ui_set_current_cursor(pos)) + { + retval = ERR_NULL; + } + if (MouseLock > 0) // KLC - added to keep MouseLock from going negative. + MouseLock--; + if (show && CurrentCursor != LastCursor) + { + uiShowMouse(NULL); + } + return(retval); +} + +errtype uiSetRegionDefaultCursor(LGRegion* r, LGCursor* c) +{ + cursor_stack* cs; + errtype err = get_region_stack(r,&cs); + // Spew(DSRC_UI_Cursor_Stack,("uiSetRegionDefaultCursor(%x,%x)\n",r,c)); + if (err != OK) return err; + cs->stack[0] = c; + uiSetCursor(); + return OK; +} + +errtype uiPushRegionCursor(LGRegion* r, LGCursor* c) +{ + cursor_stack* cs; + errtype err = get_region_stack(r,&cs); + // Spew(DSRC_UI_Cursor_Stack,("uiPushRegionCursor(%x,%x)\n",r,c)); + if (err != OK) return err; + err = cs_push(cs,c); + if (err != OK) return err; + return uiSetCursor(); +} + + +errtype uiPopRegionCursor(LGRegion* r) +{ + cursor_stack *cs; + if (r == NULL) return ERR_NULL; + cs = (cursor_stack*)(r->cursors); + if (cs == NULL) + return ERR_DUNDERFLOW; + else + { + //Spew(DSRC_UI_Cursor_Stack,("uiPopRegionCursor(%x)\n",r)); + if (cs->fullness <= 1) return ERR_DUNDERFLOW; + cs->fullness--; + uiSetCursor(); + } + return OK; +} + +errtype uiGetRegionCursor(LGRegion* r,LGCursor** c) +{ + cursor_stack *cs; + if (r == NULL) return ERR_NULL; + cs = (cursor_stack*)(r->cursors); + if (cs == NULL) + { + *c = NULL; + } + else + { + //Spew(DSRC_UI_Cursor_Stack,("uiGetRegionCursor(%x,%x)\n",r,c)); + *c = cs->stack[cs->fullness-1]; + } + return OK; +} + + +errtype uiShutdownRegionCursors(LGRegion* r) +{ + cursor_stack* cs = (cursor_stack*)(r->cursors); + // Spew(DSRC_UI_Cursor_Stack,("uiShutdownRegionCursors(%x)\n",r)); + if (cs == NULL) return ERR_NOEFFECT; + free(cs->stack); + free(cs); + r->cursors = NULL; + uiSetCursor(); + return OK; +} + +errtype uiSetSlabDefaultCursor(uiSlab* slab, LGCursor* c) +{ + // Spew(DSRC_UI_Cursor_Stack,("uiSetSlabDefaultCursor(%x,%x)\n",slab,c)); + if (slab == NULL) return ERR_NULL; + slab->cstack.stack[0] = c; + uiSetCursor(); + return OK; +} + +errtype uiSetGlobalDefaultCursor(LGCursor* c) +{ + return uiSetSlabDefaultCursor(uiCurrentSlab,c); +} + +errtype uiPushSlabCursor(uiSlab* slab, LGCursor* c) +{ + errtype err; + // Spew(DSRC_UI_Cursor_Stack,("uiPushSlabCursor(%x,%x)\n",slab,c)); + if (slab == NULL) return ERR_NULL; + err = cs_push(&slab->cstack,c); + uiSetCursor(); + return err; +} + +errtype uiPushGlobalCursor(LGCursor* c) +{ + return uiPushSlabCursor(uiCurrentSlab,c); +} + +errtype uiPopSlabCursor(uiSlab* slab) +{ + // Spew(DSRC_UI_Cursor_Stack,("uiPopSlabCursor(%x)\n",slab)); + if (slab == NULL) return ERR_NULL; + if (slab->cstack.fullness <= 1) + return ERR_DUNDERFLOW; + slab->cstack.fullness--; + uiSetCursor(); + return OK; +} + +errtype uiPopGlobalCursor(void) +{ + return uiPopSlabCursor(uiCurrentSlab); +} + +errtype uiGetSlabCursor(uiSlab* slab, LGCursor** c) +{ + // Spew(DSRC_UI_Cursor_Stack,("uiGetSlabCursor(%x,%x)\n",slab,c)); + if (slab == NULL) return ERR_NULL; + *c = slab->cstack.stack[slab->cstack.fullness-1]; + uiSetCursor(); + return OK; +} + +errtype uiGetGlobalCursor(LGCursor** c) +{ + return uiGetSlabCursor(uiCurrentSlab,c); +} + +errtype uiHideMouse(LGRect* r) +{ + LGRect mr; + uchar hide = r == NULL || LastCursor == NULL; + MouseLock++; // hey, don't move the mouse while we're doing this. + if (!hide) + { + mr.ul = LastCursorPos; + ui_mouse_do_conversion(&(mr.ul.x),&(mr.ul.y),TRUE); + if (LastCursor != NULL) + { + mr.lr.x = LastCursorPos.x + LastCursor->w; + mr.lr.y = LastCursorPos.y + LastCursor->h; + ui_mouse_do_conversion(&mr.lr.x,&mr.lr.y,TRUE); + } + else mr.lr = mr.ul; + curhiderect++; + if (curhiderect >= numhiderects) + { + LGRect* tmp = HideRect; + HideRect = (LGRect *)malloc(numhiderects*2*sizeof(LGRect)); + memcpy(HideRect,tmp,numhiderects*sizeof(LGRect)); + numhiderects *= 2; + free(tmp); + } + if (curhiderect == 1) HideRect[curhiderect] = *r; + else RECT_UNION(&HideRect[curhiderect-1],r,&HideRect[curhiderect]); + hide = RECT_TEST_SECT(&HideRect[curhiderect],&mr); + } + // Undraw the mouse. + if (hide) + { + if (LastCursor != NULL) + { + LastCursor->func(CURSOR_UNDRAW,LastCursorRegion,LastCursor,LastCursorPos); + } + LastCursor = NULL; + } + +//#define FREEZE_ON_HIDE +#ifndef FREEZE_ON_HIDE + else + { + MouseLock--; + return ERR_NOEFFECT; + } +#endif + return OK; +} + +errtype uiShowMouse(LGRect* r) +{ + errtype ret; + LGRect mr; + uchar show = LastCursor == NULL || r == NULL; + MouseLock++; + if (!show) + { + mr.ul = LastCursorPos; + ui_mouse_do_conversion(&(mr.ul.x),&(mr.ul.y),TRUE); + if (LastCursor != NULL) + { + mr.lr.x = LastCursorPos.x + LastCursor->w; + mr.lr.y = LastCursorPos.y + LastCursor->h; + ui_mouse_do_conversion(&mr.lr.x,&mr.lr.y,TRUE); + } + else mr.lr = mr.ul; + show = RECT_TEST_SECT(r,&mr); + } + if (show) + { + if (MouseLock <= 2) + { + MouseLock = 2; + if (LastCursor != NULL) + { + LastCursor->func(CURSOR_UNDRAW,LastCursorRegion,LastCursor,LastCursorPos); + } + mouse_get_xy(&LastCursorPos.x,&LastCursorPos.y); + if (ui_set_current_cursor(LastCursorPos)) + { + LastCursorPos.x -= CurrentCursor->hotspot.x; + LastCursorPos.y -= CurrentCursor->hotspot.y; + CurrentCursor->func(CURSOR_DRAW, + CursorRegion, + CurrentCursor, + LastCursorPos); + LastCursor = CurrentCursor; + LastCursorRegion = CursorRegion; + } + else LastCursor = NULL; + } + MouseLock--; + ret = OK; + } +#ifndef FREEZE_ON_HIDE + else + { + ret = ERR_NOEFFECT; + } +#else + else + { + if (MouseLock <= 2) MouseLock = 2; + MouseLock--; + } +#endif + if (--curhiderect < 0) curhiderect = 0; + MouseLock--; + +// KLC - I have no idea what MouseLock is doing. I'll just set it to zero - shown - duh! +MouseLock = 0; + + return ret; +} + +errtype uiMakeBitmapCursor(LGCursor* c,grs_bitmap* bm, LGPoint hotspot) +{ + // Spew(DSRC_UI_Cursors,("uiMakeBitmapCursor(%x,%x,<%d %d>)\n",c,bm,hotspot.x,hotspot.y)); + + if(c == NULL) { + printf("FIXME uiMakeBitmapCursor tried to make a null cursor!\n"); + return ERR_NOEFFECT; + } + + grow_save_under(bm->w,bm->h); + c->func = bitmap_cursor_drawfunc; + c->state = bm; + c->hotspot = hotspot; + c->w = bm->w; + c->h = bm->h; + return OK; +} diff --git a/engine/src/Libraries/UI/Source/cursors.h b/engine/src/Libraries/UI/Source/cursors.h new file mode 100644 index 0000000..b0327e0 --- /dev/null +++ b/engine/src/Libraries/UI/Source/cursors.h @@ -0,0 +1,260 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __CURSORS_H +#define __CURSORS_H + +/* + * $Source: r:/prj/lib/src/ui/RCS/cursors.h $ + * $Revision: 1.10 $ + * $Author: mahk $ + * $Date: 1994/12/02 07:13:30 $ + * + * $Log: cursors.h $ + * Revision 1.10 1994/12/02 07:13:30 mahk + * c++ compatibility + * + * Revision 1.9 1994/11/18 12:13:30 mahk + * uiUpdateScreenSize or whatever. + * + * Revision 1.8 1994/08/24 08:55:55 mahk + * Cursor stacks and invisible regions. + * + * Revision 1.7 1994/04/05 00:19:32 mahk + * Added hflipped cursors. Wacky. + * + * Revision 1.6 1993/10/11 20:27:12 dc + * Angle is fun, fun fun fun + * + * Revision 1.5 1993/05/26 16:33:39 mahk + * Added REAL mouse tolerance. + * + * Revision 1.4 1993/04/28 14:40:14 mahk + * Preparing for second exodus + * + * Revision 1.3 1993/04/27 16:36:44 xemu + * rip out base + * + * Revision 1.2 1993/04/05 23:42:58 mahk + * Added mouse hide/show and slab support. + * + * Revision 1.1 1993/03/31 23:22:11 mahk + * Initial revision + * + * + */ + +// Includes +#include "lg.h" // every file should have this +#include "2d.h" +//#include +#include "rect.h" +#include "region.h" +#include "mouse.h" +#include "error.h" +#include "array.h" + +// Defines + +struct _cursor; + +typedef void (*CursorDrawFunc)(int cmd, LGRegion* r, struct _cursor* c, LGPoint pos); +// A cursor drawfunc executes the command specified by cmd to draw and undraw cursor +// c at point pos. R is the region in which c was found. + +// The commands are as follows: +#define CURSOR_DRAW 0 +#define CURSOR_UNDRAW 1 +#define CURSOR_DRAW_HFLIP 2 // draw horizontally flipped. Go figure. + +typedef struct _cursor +{ + CursorDrawFunc func; + void* state; + LGPoint hotspot; + short w,h; +} LGCursor; + +// Every region has a cursor stack. +typedef struct _cursorstack +{ + int size; + int fullness; + LGCursor** stack; +} cursor_stack; + + +typedef cursor_stack uiCursorStack; + + +typedef struct _ui_slab +{ + LGRegion* creg; // cursor region. + struct _focus_chain + { + Array chain; + int curfocus; + } fchain; // focus chain + cursor_stack cstack; +} uiSlab; + + + +// Prototypes +errtype uiMakeBitmapCursor(LGCursor* c, grs_bitmap* bm, LGPoint hotspot); +// Initializez *c to a bitmap cursor whose bitmap is bm, with the specified hotspot + +errtype uiSetRegionDefaultCursor(LGRegion* r, LGCursor* c); +// Sets the default cursor to be used when the cursor is in region r and no cursor has been +// pushed to r's cursor stack + +errtype uiPushRegionCursor(LGRegion* r, LGCursor* c); +// Pushes c to r's regional cursor stack. When the mouse is in region r, +// the top of r's cursor stack will be displayed. + +errtype uiPopRegionCursor(LGRegion* r); +// Pops the top cursor off of r's cursor stack + +errtype uiGetRegionCursor(LGRegion* r, LGCursor** c); +// Gets the current cursor for region r. *c will be NULL if +// there is no default cursor for r, and no cursors on the +// r's cursor stack. + +errtype uiShutdownRegionCursors(LGRegion* r); +// Deletes the cursor stack and default cursor for region r. + +errtype uiSetGlobalDefaultCursor(LGCursor* c); +// Sets the default cursor for the currently active slab. + +errtype uiPushGlobalCursor(LGCursor* c); +// Pushes a cursor to the active slab's global cursor stack. + +errtype uiPopGlobalCursor(void); +// Pops the top cursor off of the active slab's global cursor stack. + +errtype uiGetGlobalCursor(LGCursor** c); +// Gets the cursor on top of the active slab's global cursor stack, +// or the global default cursor if the stack is empty. + +errtype uiSetSlabDefaultCursor(uiSlab* slab, LGCursor* c); +// Sets the default cursor for the specified slab. + +errtype uiPushSlabCursor(uiSlab* slab, LGCursor* c); +// Pushes a cursor to the specified slab's global cursor stack. + +errtype uiPopSlabCursor(uiSlab* slab); +// Pops the top cursor off of the specified slab's global cursor stack. + +errtype uiGetSlabCursor(uiSlab* slab, LGCursor** c); +// Gets the cursor on top of the specified slab's global cursor stack, +// or the global default cursor if the stack is empty. + +errtype uiHideMouse(LGRect* r); +// Hides the mouse if it intersects r. + +errtype uiShowMouse(LGRect* r); +// Shows the mouse if it intersects r. + +errtype uiSetCursor(void); +// Recomputes and redraws the current cursor based on the position of the +// mouse. + +// ------------------ +// SCREEN_MODE_UPDATE +// ------------------ + +#define UI_DETECT_SCREEN_SIZE (MakePoint(-1,-1)) + + +// resizes the ui coordinate space. +// if UI_DETECT_SCREEN_SIZE, detect the screen size +extern errtype uiUpdateScreenSize(LGPoint size); + + +// Globals + +extern int CursorMoveTolerance; +// Number of pixels of movement the interrupt handler will "tolerate" before +// redrawing the cursor. + + + + + +// ---------------------- +// CURSOR-STACK-BASED API +// + +/* + Routines for manipulating cursor stacks. This is kind of an + afterthought, but it's a good one, and in the future, when we have + light without heat and travel to the stars, all good ui clients will use it. + + The idea here is to *expose* the notion of cursor stack to the client, so it can do + clever things like have regions/slabs share cursor stacks. In addition to this, we + implement the push-one and pop-every operations, which will only operate on cursor stacks + so that the API does not explode with element of { slab, region, stack} x { once, always} etc. + +*/ + +extern errtype uiMakeCursorStack(uiCursorStack* cs); +// initializes cs to an empty cursor stack + +extern errtype uiDestroyCursorStack(uiCursorStack* cs); +// destroys a cursor stack. + +extern errtype uiGetRegionCursorStack(LGRegion* reg, uiCursorStack** cs); +// points *cs to reg's cursor stack. If reg has no cursor stack, +// creates one. + +extern errtype uiSetRegionCursorStack(LGRegion* reg, uiCursorStack* cs); +// Sets reg's cursor stack to cs. + +extern errtype uiGetSlabCursorStack(uiSlab* slab, uiCursorStack** cs); +// points *cs to slab's cursor stack. If slab has no cursor stack, sets *cs to NULL +// and returns ERR_NULL; + +// note that there is not currently a uiSetSlabCursorStack + +extern errtype uiSetDefaultCursor(uiCursorStack* cs, LGCursor* c); +// sets cs' default cursor to c. if cs is the cursor stack of a slab, +// c will become the default global cursor when that slab is the current slab. If +// cs is the cursor stack of a region, c will become the default cursor for the region. + +extern errtype uiGetDefaultCursor(uiCursorStack* cs, LGCursor** c); +// sets *c to the default cursor for cs, or NULL if there is none. + +extern errtype uiPushCursor(uiCursorStack* cs, LGCursor* c); +// pushes cursor c onto cursor stack cs + +extern errtype uiPopCursor(uiCursorStack* cs); +// pops the top cursor off of cs. + +extern errtype uiGetTopCursor(uiCursorStack* cs, LGCursor** c); +// Points *c to the top cursor on cs; or NULL if there is no +// top cursor. will NOT set *c to the default cursor. + +extern errtype uiPushCursorOnce(uiCursorStack* cs, LGCursor* c); +// pushes cursor c to the top of cs ONLY IF c is not already +// on the top of cs. + +extern errtype uiPopCursorEvery(uiCursorStack* cs, LGCursor* c); +// deletes every instance of c from cs. + +#endif // __CURSORS_H + diff --git a/engine/src/Libraries/UI/Source/curtyp.h b/engine/src/Libraries/UI/Source/curtyp.h new file mode 100644 index 0000000..7dbcfa2 --- /dev/null +++ b/engine/src/Libraries/UI/Source/curtyp.h @@ -0,0 +1,37 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/ui/RCS/curtyp.h $ + * $Revision: 1.1 $ + * $Author: kaboom $ + * $Date: 1993/12/16 07:46:45 $ + * + * Declarations for cursor types. + * + * $Log: curtyp.h $ + * Revision 1.1 1993/12/16 07:46:45 kaboom + * Initial revision + * + */ + +/* the saveunder for bitmap cursors */ +struct _cursor_saveunder { + grs_bitmap bm; + int mapsize; +}; diff --git a/engine/src/Libraries/UI/Source/event.c b/engine/src/Libraries/UI/Source/event.c new file mode 100644 index 0000000..14f7093 --- /dev/null +++ b/engine/src/Libraries/UI/Source/event.c @@ -0,0 +1,1048 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include +#include +#include + +#include "lg.h" +#include "mouse.h" +#include "kb.h" +#include "kbcook.h" +#include "array.h" +#include "rect.h" +#include "slab.h" +#include "event.h" +#include "vmouse.h" + + +// --------------------- +// INTERNAL PROTOTYPES +// --------------------- +void event_queue_add(uiEvent* e); +uchar event_queue_next(uiEvent** e); +uchar region_check_opacity(LGRegion* reg, ulong evmask); +uchar event_dispatch_callback(LGRegion* reg, LGRect* r, void* v); +void ui_set_last_mouse_region(LGRegion* reg, uiEvent* ev); +uchar ui_try_region(LGRegion* reg, LGPoint pos, uiEvent* ev); +uchar ui_traverse_point(LGRegion* reg, LGPoint pos, uiEvent* data); +uchar send_event_to_region(LGRegion* r, uiEvent* ev); +void ui_purge_mouse_events(void); +void ui_flush_mouse_events(ulong timestamp, LGPoint pos); +void ui_dispatch_mouse_event(uiEvent* mout); +void ui_poll_keyboard(void); +void ui_pop_up_keys(void); +LGPoint ui_poll_mouse_position(void); +errtype ui_init_focus_chain(uiSlab* slab); + + +// --------------------- +// HANDLER CHAIN DEFINES +// --------------------- + +typedef struct _ui_event_handler +{ + ulong typemask; // Which event types does this handle? + /* handler proc: called when a specific event is received */ + uiHandlerProc proc; + intptr_t state; // handler-specific state data + int next; // used for chaining handlers. +} uiEventHandler; + +typedef struct _handler_chain +{ + Array chain; + int front; + ulong opacity; +} handler_chain; + + +#define INITIAL_CHAINSIZE 4 +#define INITIAL_FOCUSES 5 +#define CHAIN_END -1 + +ulong uiGlobalEventMask = ALL_EVENTS; + +ulong last_mouse_draw_time = 0; + +// ---------------------------- +// HANDLER CHAIN IMPLEMENTATION +// ---------------------------- + +errtype uiInstallRegionHandler(LGRegion* r, uint32_t evmask, uiHandlerProc callback, intptr_t state, int* id) +{ + handler_chain *ch; + uiEventHandler* eh; + int i; + errtype err; + // Spew(DSRC_UI_Handlers,("uiInstallRegionhandler(%x,%x,%x,%x,%x)\n",r,evmask,callback,state,id)); + if (callback == NULL || r == NULL || evmask == 0) return ERR_NULL; + ch = (handler_chain*) r->handler; + if (ch == NULL) + { + // Spew(DSRC_UI_Handlers,("uiInstallRegionHandler(): creating new handler chain\n")); + ch = (handler_chain *)malloc(sizeof(handler_chain)); + if (ch == NULL) + { + // Spew(DSRC_UI_Handlers,("uiInstallRegionHandler: out of memory\n")); + return ERR_NOMEM; + } + array_init(&ch->chain,sizeof(uiEventHandler),INITIAL_CHAINSIZE); + ch->front = CHAIN_END; + r->handler = (void*)ch; + ch->opacity = uiDefaultRegionOpacity; + } + err = array_newelem(&ch->chain,&i); + if (err != OK) + { + // Spew(DSRC_UI_Handlers,("uiInstallRegionHandler(): array_newelem returned %d\n",err)); + return err; + } + eh = &((uiEventHandler*)(ch->chain.vec))[i]; + eh->next = ch->front; + eh->typemask = evmask; + eh->proc = callback; + eh->state = state; + ch->front = i; + ch->opacity &= ~evmask; + *id = i; + // Spew(DSRC_UI_Handlers,("exit uiInstallRegionHandler(): *id = %d\n",*id)); + return OK; +} + +errtype uiRemoveRegionHandler(LGRegion* r, int id) +{ + errtype err; + handler_chain* ch; + uiEventHandler* handlers; + int i; + + // Spew(DSRC_UI_Handlers,("uiRemoveRegionHandler(%x,%d)\n",r,id)); + if (r == NULL) return ERR_NULL; + ch = (handler_chain*)r->handler; + if (ch == NULL || id < 0) return ERR_RANGE; + handlers = (uiEventHandler*)(ch->chain.vec); + if (id == ch->front) + { + int next = handlers[id].next; + err = array_dropelem(&ch->chain,id); + if (err != OK) return err; + ch->front = next; + return OK; + } + for (i = ch->front; handlers[i].next != CHAIN_END; i = handlers[i].next) + { + if (handlers[i].next == id) + { + errtype err = array_dropelem(&ch->chain,id); + if (err != OK) return err; + handlers[i].next = handlers[id].next; + return OK; + } + } + return ERR_NOEFFECT; +} + + +errtype uiSetRegionHandlerMask(LGRegion* r, int id, int evmask) +{ + handler_chain *ch; + uiEventHandler* handlers; + // Spew(DSRC_UI_Handlers,("uiSetRegionHandlerMask(%x,%d,%x)\n",r,id,evmask)); + if (r == NULL) return ERR_NULL; + ch = (handler_chain*)r->handler; + if (ch == NULL || id >= ch->chain.fullness || id < 0) return ERR_RANGE; + handlers = (uiEventHandler*)(ch->chain.vec); + handlers[id].typemask = evmask; + return OK; +} + +// ------- +// OPACITY +// ------- + +ulong uiDefaultRegionOpacity = 0; + +ulong uiGetRegionOpacity(LGRegion* reg) +{ + handler_chain *ch = (handler_chain*)(reg->handler); + if (ch == NULL) + { + return uiDefaultRegionOpacity; + } + else + return ch->opacity; +} + +errtype uiSetRegionOpacity(LGRegion* reg,ulong mask) +{ + handler_chain *ch = (handler_chain*)(reg->handler); + if (ch == NULL) + { + // Spew(DSRC_UI_Handlers,("uiSetRegionOpacity(): creating new handler chain\n")); + ch = (handler_chain *)malloc(sizeof(handler_chain)); + if (ch == NULL) + { + // Spew(DSRC_UI_Handlers,("uiSetRegionOpacity: out of memory\n")); + return ERR_NOMEM; + } + array_init(&ch->chain,sizeof(uiEventHandler),INITIAL_CHAINSIZE); + ch->front = CHAIN_END; + reg->handler = (void*)ch; + ch->opacity = mask; + } + else + ch->opacity = mask; + return OK; +} + +// ------------------- +// FOCUS CHAIN DEFINES +// ------------------- + +typedef struct _focus_link +{ + LGRegion* reg; + ulong evmask; + int next; +} focus_link; + +extern uiSlab* uiCurrentSlab; +#define FocusChain (uiCurrentSlab->fchain.chain) +#define CurFocus (uiCurrentSlab->fchain.curfocus) +#define FCHAIN ((focus_link*)(uiCurrentSlab->fchain.chain.vec)) + + +// ---------------- +// FOCUS CHAIN CODE +// ---------------- + + +errtype uiGrabSlabFocus(uiSlab* slab, LGRegion* r, ulong evmask) +{ + int i; + errtype err; + focus_link* fchain = (focus_link*) slab->fchain.chain.vec; + // Spew(DSRC_UI_Slab,("uiGrabSlabFocus(%x,%x,%x)\n",slab,r,evmask)); + if (r == NULL) return ERR_NULL; + if (evmask == 0) return ERR_NOEFFECT; + err = array_newelem(&slab->fchain.chain,&i); + if (err != OK) return err; + fchain[i].reg = r; + fchain[i].evmask = evmask; + fchain[i].next = slab->fchain.curfocus; + // Spew(DSRC_UI_Slab,("uiGrabSlabFocus(): old focus = %d new focus = %d\n",slab->fchain.curfocus,i)); + slab->fchain.curfocus = i; + return OK; +} + + +errtype uiGrabFocus(LGRegion* r, ulong evmask) +{ + return uiGrabSlabFocus(uiCurrentSlab,r,evmask); +} + +errtype uiReleaseSlabFocus(uiSlab* slab, LGRegion* r, ulong evmask) +{ + errtype retval = ERR_NOEFFECT; + focus_link* fchain = (focus_link*)slab->fchain.chain.vec; + focus_link *l = &fchain[CurFocus]; + // Spew(DSRC_UI_Slab,("uiReleaseSlabFocus(%x,%x,%x)\n",slab,r,evmask)); + if (r == NULL) return ERR_NULL; + if (l->reg == r) + { + ulong tmpmask = l->evmask & evmask; + l->evmask &= ~evmask; + evmask &= ~tmpmask; + if (l->evmask == 0) + { + int tmp = slab->fchain.curfocus; + slab->fchain.curfocus = l->next; + // Spew(DSRC_UI_Slab,("uiReleaseSlabFocus(): CurFocus = %d\n",slab->fchain.curfocus)); + array_dropelem(&slab->fchain.chain,tmp); + } + if (evmask == 0) return OK; + retval = OK; + + } + for(; l->next != CHAIN_END; l = &fchain[l->next]) + { + focus_link* thenext = &fchain[l->next]; + if (thenext->reg == r) + { + ulong tmpmask = l->evmask & evmask; + thenext->evmask &= ~evmask; + evmask &= ~tmpmask; + if (thenext->evmask == 0) + { + int tmp = l->next; + l->next = thenext->next; + array_dropelem(&slab->fchain.chain,tmp); + } + if (evmask == 0) + return OK; + retval = OK; + } + } + return retval; +} + +errtype uiReleaseFocus(LGRegion* r, ulong evmask) +{ + return uiReleaseSlabFocus(uiCurrentSlab,r,evmask); +} + + +// ----------------------- +// POLLING AND DISPATCHING +// ----------------------- + +#define INITIAL_QUEUE_SIZE 32 +#define DEFAULT_DBLCLICKTIME 0 +#define DEFAULT_DBLCLICKDELAY 0 + +ushort uiDoubleClickTime = DEFAULT_DBLCLICKTIME; +ushort uiDoubleClickDelay = DEFAULT_DBLCLICKDELAY; +uchar uiDoubleClicksOn[NUM_MOUSE_BTNS] = { FALSE, FALSE, FALSE } ; +uchar uiAltDoubleClick = FALSE; +ushort uiDoubleClickTolerance = 5; +static uchar poll_mouse_motion = FALSE; +static uiEvent last_down_events[NUM_MOUSE_BTNS]; +static uiEvent last_up_events[NUM_MOUSE_BTNS]; + +static struct _eventqueue +{ + int in, out; + int size; + uiEvent* vec; +} EventQueue; + +void event_queue_add(uiEvent* e) +{ + if ((EventQueue.in + 1)%EventQueue.size == EventQueue.out) + { + // Queue is full, grow it. + int i; + int out = EventQueue.out; + int newsize = EventQueue.size * 2; + uiEvent *newvec = (uiEvent *)malloc(sizeof(uiEvent)*newsize); + for(i = 0; out != EventQueue.in; i++, out = (out+1)%EventQueue.size) + newvec[i] = EventQueue.vec[out]; + free(EventQueue.vec); + EventQueue.vec = newvec; + EventQueue.size = newsize; + EventQueue.in = i; + EventQueue.out = 0; + } + EventQueue.vec[EventQueue.in] = *e; + EventQueue.in++; + if (EventQueue.in >= EventQueue.size) EventQueue.in = 0; +} + +uchar event_queue_next(uiEvent** e) +{ + if (EventQueue.in != EventQueue.out) + { + *e = &EventQueue.vec[EventQueue.out++]; + if (EventQueue.out >= EventQueue.size) + EventQueue.out = 0; + return TRUE; + } + return FALSE; +} + + +// TRUE we are opaque to this mask. +uchar region_check_opacity(LGRegion* reg, ulong evmask) +{ + return (evmask & uiGetRegionOpacity(reg)) != 0; +} + +uchar event_dispatch_callback(LGRegion* reg, LGRect* rect, void* v) +{ + uiEvent* ev = (uiEvent*)v; + handler_chain *ch = (handler_chain*)(reg->handler); + int i,next; + + if(ch == NULL) { + //printf("WARNING! handler_chain is NULL in event_dispatch_callback\n"); + return FALSE; + } + + uiEventHandler* handlers = (uiEventHandler*)(ch->chain.vec); + // Spew(DSRC_UI_Dispatch,("event_dispatch_callback(%x,%x,%x) event type %x\n",reg,r,v,ev->type)); +/* +if (ev->type == UI_EVENT_KBD_COOKED) +{ + char buff[100]; + sprintf(buff+1, "event_dispatch_callback(%x,%x,%x) event type %x\0",reg,r,v,ev->type); + buff[0] = strlen(buff+1); + DebugString((uchar *)buff); +} +*/ + if (ch == NULL || handlers == NULL) + { + // Spew(DSRC_UI_Dispatch,("event_dispatch_callback(): no handler chain ch = %x handlers = %d\n",ch,handlers)); + return FALSE; + } + for (i = ch->front; i != CHAIN_END; i = next) + { + next = handlers[i].next; + if ((handlers[i].typemask & ev->type) + && (handlers[i].proc)(ev,reg,handlers[i].state)) + { + // Spew(DSRC_UI_Dispatch,("Caught by handler %d\n",i)); + return TRUE; + } + } + // Spew(DSRC_UI_Dispatch,("Event Rejected\n")); + return FALSE; +} + +// ui_traverse_point return values: +#define TRAVERSE_HIT 0 +#define TRAVERSE_MISS 1 +#define TRAVERSE_OPAQUE 2 + +LGRegion* uiLastMouseRegion[NUM_MOUSE_BTNS]; + + +void ui_set_last_mouse_region(LGRegion* reg, uiEvent* ev) +{ + int i; + if (ev->type != UI_EVENT_MOUSE) + return; + for (i = 0; i < NUM_MOUSE_BTNS; i++) + { + if ((ev->mouse_data.action & MOUSE_BTN2DOWN(i)) != 0 || + (ev->mouse_data.action & UI_MOUSE_BTN2DOUBLE(i))) + uiLastMouseRegion[i] = reg; + if (ev->mouse_data.action & MOUSE_BTN2UP(i)) + uiLastMouseRegion[i] = NULL; + } +} + +uchar ui_try_region(LGRegion* reg, LGPoint pos, uiEvent* ev) +{ + LGRect cbr; + uchar retval = TRAVERSE_MISS; + + cbr.ul = pos; + cbr.lr = pos; + if (region_check_opacity(reg,ev->type)) retval = TRAVERSE_OPAQUE; + else if (event_dispatch_callback(reg,&cbr,ev)) retval = TRAVERSE_HIT; + else return retval; + ui_set_last_mouse_region(reg, ev); + return retval; +} + +uchar ui_traverse_point(LGRegion* reg, LGPoint pos, uiEvent* data) +{ + uchar retval = TRAVERSE_MISS; + LGPoint rel; + LGRegion* child; + + rel = pos; + rel.x -= reg->abs_x; + rel.y -= reg->abs_y; + + if ((reg->status_flags & INVISIBLE_FLAG) != 0) + return retval; + + if (reg->event_order) + { + retval = ui_try_region(reg,pos,data); + if (retval != TRAVERSE_MISS) return retval; + } + for (child = reg->sub_region; child != NULL; child = child->next_region) + if (RECT_TEST_PT(child->r,rel)) + { + retval = ui_traverse_point(child,pos,data); + if (retval != TRAVERSE_MISS) return retval; + break; + } + if (!reg->event_order) + { + retval = ui_try_region(reg,pos,data); + if (retval != TRAVERSE_MISS) return retval; + } + return TRAVERSE_MISS; +} + +uchar send_event_to_region(LGRegion* r, uiEvent* ev) +{ + // Spew(DSRC_UI_Dispatch,("send_event_to_region(%x,%x)\n",r,ev)); + return ui_traverse_point(r,ev->pos,ev) == TRAVERSE_HIT; +} + +uchar uiDispatchEventToRegion(uiEvent* ev, LGRegion* reg) +{ + LGPoint pos; + uiEvent nev = *ev; + + ui_mouse_do_conversion(&(nev.pos.x),&(nev.pos.y),TRUE); + pos = nev.pos; + pos.x += reg->r->ul.x - reg->abs_x; + pos.y += reg->r->ul.y - reg->abs_y; + + if (!RECT_TEST_PT(reg->r,pos)) + { + LGRect r; + r.ul = nev.pos; + r.lr.x = nev.pos.x+1; + r.lr.y = nev.pos.y+1; + return event_dispatch_callback(reg,&r,&nev); + } + return ui_traverse_point(reg,nev.pos,&nev) == TRAVERSE_HIT; +} + + +uchar uiDispatchEvent(uiEvent* ev) +{ + int i; + // Spew(DSRC_UI_Dispatch,("dispatch_event(%x), CurFocus = %d\n",ev,CurFocus)); + if (!(ev->type & uiGlobalEventMask)) return FALSE; + for (i = CurFocus; i != CHAIN_END; i = FCHAIN[i].next) + { + // Spew(DSRC_UI_Dispatch,("dispatch_event(): checking focus chain element %d\n",i)); + if (FCHAIN[i].evmask & ev->type) + if (uiDispatchEventToRegion(ev,FCHAIN[i].reg)) return TRUE; + } + return FALSE; +} + +errtype uiQueueEvent(uiEvent* ev) +{ + // if this is a keyboard event, queue up earlier events. + if (ev->type == UI_EVENT_KBD_RAW || ev->type == UI_EVENT_KBD_COOKED) + { + kbs_event kbe; + for(kbe = kb_next(); kbe.code != KBC_NONE; kbe = kb_next()) + { + uiEvent out; + mouse_get_xy(&out.pos.x,&out.pos.y); + out.type = UI_EVENT_KBD_RAW; + out.raw_key_data.scancode = kbe.code; + out.raw_key_data.action = kbe.state; + event_queue_add(&out); + } + } + if (ev->type == UI_EVENT_MOUSE || ev->type == UI_EVENT_MOUSE_MOVE) + { + ss_mouse_event mse; + errtype err = mouse_next(&mse); + for(;err == OK; err = mouse_next(&mse)) + { + uiEvent out; + out.pos.x = mse.x; + out.pos.y = mse.y; + out.type = (mse.type == MOUSE_MOTION) ? UI_EVENT_MOUSE_MOVE : UI_EVENT_MOUSE; + out.mouse_data.action = mse.type; + out.mouse_data.modifiers = mse.modifiers; + event_queue_add((uiEvent*)&out); + } + } + event_queue_add(ev); + return OK; +} + +#define MOUSE_EVENT_FLUSHED UI_EVENT_MOUSE_MOVE + +void ui_purge_mouse_events(void) +{ + int i; + for (i = 0; i < NUM_MOUSE_BTNS; i++) + { + last_down_events[i].type = UI_EVENT_NULL; + last_down_events[i].mouse_data.tstamp = 0; + last_up_events[i].type = UI_EVENT_NULL; + last_up_events[i].mouse_data.tstamp = 0; + } +} + +void ui_flush_mouse_events(ulong timestamp, LGPoint pos) +{ + int i; + for (i = 0; i < NUM_MOUSE_BTNS; i++) + { + + if (uiDoubleClicksOn[i] && + last_down_events[i].type != UI_EVENT_NULL) + { + int crit = uiDoubleClickDelay * 5; + ulong timediff = timestamp - last_down_events[i].mouse_data.tstamp; + LGPoint downpos = last_down_events[i].pos; + uchar out = (abs(pos.x - downpos.x) > uiDoubleClickTolerance || + abs(pos.y - downpos.y) > uiDoubleClickTolerance); + + // OK, if we've waited DoubleClickDelay after a down event, send it out. + if (out || timediff >= crit) + { + uiEvent ev; + //Spew(DSRC_UI_Polling,("flushing old clicks: crit = %d timediff = %d\n",crit,timediff)); + if (last_down_events[i].type != MOUSE_EVENT_FLUSHED) + { + ev = last_down_events[i]; + last_down_events[i].type = MOUSE_EVENT_FLUSHED; + uiDispatchEvent(&ev); + } + if (last_up_events[i].type != MOUSE_EVENT_FLUSHED) + { + ev = last_up_events[i]; + last_up_events[i].type = MOUSE_EVENT_FLUSHED; + uiDispatchEvent(&ev); + } + } + + // This is where we do our flushing + if (last_up_events[i].type != UI_EVENT_NULL) + { + crit = uiDoubleClickTime; + timediff = timestamp - last_up_events[i].mouse_data.tstamp; + if (out || timediff >= crit) + { + last_down_events[i].type = UI_EVENT_NULL; + last_up_events[i].type = UI_EVENT_NULL; + } + } + } + } +} + +void ui_dispatch_mouse_event(uiEvent* mout) +{ + int i; + uchar eaten = FALSE; + + bool altDown = (SDL_GetModState() & KMOD_ALT) != 0; + +// ui_mouse_do_conversion(&(mout->pos.x),&(mout->pos.y),TRUE); + ui_flush_mouse_events(mout->mouse_data.tstamp, mout->pos); + for (i = 0; i < NUM_MOUSE_BTNS; i++) + { + if (!(uiDoubleClicksOn[i])) + continue; + + //printf("Checking double click! %i\n", mout->action & MOUSE_BTN2DOWN(i)); + if (uiAltDoubleClick && altDown) + { + if (mout->mouse_data.action & MOUSE_BTN2DOWN(i)) + { + mout->mouse_data.action &= ~MOUSE_BTN2DOWN(i); + mout->mouse_data.action |= UI_MOUSE_BTN2DOUBLE(i); + continue; + } + } + if (last_down_events[i].type != UI_EVENT_NULL) + { + if (mout->mouse_data.action & MOUSE_BTN2DOWN(i)) + { + // Spew(DSRC_UI_Polling,("double click down\n")); + // make a double click event. + mout->mouse_data.action &= ~MOUSE_BTN2DOWN(i); + mout->mouse_data.action |= UI_MOUSE_BTN2DOUBLE(i); + + last_down_events[i].type = UI_EVENT_NULL; + last_up_events[i].type = UI_EVENT_NULL; + } + if (mout->mouse_data.action & MOUSE_BTN2UP(i)) + { + // Spew(DSRC_UI_Polling,("up in time %d\n",mout->tstamp - last_down_events[i].tstamp)); + last_up_events[i] = *mout; + eaten = TRUE; + } + } + else if (mout->mouse_data.action & MOUSE_BTN2DOWN(i)) + { + // Spew(DSRC_UI_Polling,("saving the down\n")); + last_down_events[i] = *mout; + eaten = TRUE; + } + } + if (!eaten) + uiDispatchEvent(mout); +} + +// ---------------------- +// KEYBOARD POLLING SETUP +// ---------------------- + +uchar* ui_poll_keys = NULL; + +errtype uiSetKeyboardPolling(ubyte* codes) +{ + ui_poll_keys = codes; + return OK; +} + +extern uchar sshockKeyStates[256]; + +static ushort inputModToUImod(uchar mod) +{ + ushort ret = 0; + if(mod & KB_MOD_CTRL) + ret |= KB_FLAG_CTRL; + if(mod & KB_MOD_SHIFT) + ret |= KB_FLAG_SHIFT; + // TODO: what's 0x04 ? windows key? + if(mod & KB_MOD_ALT) + ret |= KB_FLAG_ALT; + // Note: KB_MOD_PRESSED doesn't matter here + + return ret; +} + +// KLC - For Mac version, call GetKeys once at the beginning, then check +// the results in the loop. Fill in the "mods" field (ready for cooking) +// before dispatching an event. +void ui_poll_keyboard(void) +{ + int numKeys = 0; + + for(uchar* key = ui_poll_keys; *key != KBC_NONE; key++) + { + if(sshockKeyStates[*key] != 0) + { + uiEvent ev; + ev.type = UI_EVENT_KBD_POLL; + ev.pos.x = 0; + ev.pos.y = 0; + ev.poll_key_data.action = KBS_DOWN; + ev.poll_key_data.scancode = *key; + ev.poll_key_data.mods = inputModToUImod(sshockKeyStates[*key]); + + uiDispatchEvent(&ev); + } + // *key is a System Shock/Mac keycode + } + +#if 0 + extern uchar pKbdGetKeys[16]; + long *keys = (long *)pKbdGetKeys; + GetKeys((UInt32 *)keys); + + uchar *key; + for (key = ui_poll_keys; *key != KBC_NONE; key++) + if((pKbdGetKeys[*key>>3] >> (*key & 7)) & 1) + { + uiPollKeyEvent ev; + ev.type = UI_EVENT_KBD_POLL; + ev.pos.x = 0; + ev.pos.y = 0; + ev.action = KBS_DOWN; + ev.scancode = *key; + ev.mods = 0; + if ((keys[1] & 0x00000001) != 0L) // Shift key + ev.mods |= KB_FLAG_SHIFT; + if ((keys[1] & 0x00008000) != 0L) // Cmd key + ev.mods |= KB_FLAG_CTRL; + if ((keys[1] & 0x00000004) != 0L) // Option key + ev.mods |= KB_FLAG_ALT; + + uiDispatchEvent((uiEvent*)&ev); + } +#endif +} + +void ui_pop_up_keys(void) +{ +/*¥¥¥ serve any purpose now? + if (ui_poll_keys != NULL) + { + uchar* key; + for (key = ui_poll_keys; *key != KBC_NONE; key++) + { + kb_clear_state(*key,KBA_STATE); + } + } +*/ +} + +errtype uiMakeMotionEvent(uiEvent* ev) +{ + // haha, this is the super secret mouse library variable of the + // current button state. + extern short mouseInstantButts; + mouse_get_xy(&ev->pos.x,&ev->pos.y); + ev->type = UI_EVENT_MOUSE_MOVE; // must get past event mask + ev->mouse_data.action = MOUSE_MOTION; + ev->mouse_data.tstamp = mouse_get_time(); + ev->mouse_data.buttons = (ubyte)mouseInstantButts; + return OK; +} + +// Generate a fake mouse motion event and send it along... +LGPoint ui_poll_mouse_position(void) +{ + uiEvent ev; + uiMakeMotionEvent(&ev); + uiDispatchEvent(&ev); + return ev.pos; +} + + +errtype uiPoll(void) +{ + static LGPoint last_mouse = { -1, -1 }; + errtype err; + uiEvent out,*ev; + uchar kbdone = FALSE; + uchar msdone = FALSE; + LGPoint mousepos = last_mouse; + extern LGPoint LastCursorPos; + extern struct _cursor* LastCursor; + extern void ui_update_cursor(LGPoint pos); + +#define BURN_QUEUE +#ifdef BURN_QUEUE + // burn through queue + while(event_queue_next(&ev)) + { + uchar result = TRUE; +// ui_mouse_do_conversion(&(ev->pos.x),&(ev->pos.y),TRUE); + if (ev->type == UI_EVENT_MOUSE) + ui_dispatch_mouse_event(ev); + else result = uiDispatchEvent(ev); + if (!result && ev->type == UI_EVENT_KBD_RAW) + { + ushort cooked; + kbs_event kbe; + kbe.code = ev->raw_key_data.scancode; + kbe.state = ev->raw_key_data.action; + err = kb_cook(kbe,&cooked,&result); + if (err != OK) return err; + if (result) + { + out.subtype = cooked; + out.type = UI_EVENT_KBD_COOKED; + uiDispatchEvent(ev); + } + } + } +#endif // BURN_QUEUE + +// ui_mouse_get_xy(&mousepos.x,&mousepos.y); + + mouse_get_xy(&mousepos.x,&mousepos.y); + + while(!kbdone || !msdone) + { + if (!kbdone) + { + kbs_event kbe = kb_next(); + if (kbe.code != KBC_NONE) + { + uchar eaten; + // Spew(DSRC_UI_Polling,("uiPoll(): got a keyboard event: <%d,%x>\n",kbe.state,kbe.code)); + out.pos = mousepos; + out.type = UI_EVENT_KBD_RAW; + out.raw_key_data.scancode = kbe.code; + out.raw_key_data.action = kbe.state; + eaten = uiDispatchEvent(&out); + if (!eaten) + { + ushort cooked; + uchar result; + // Spew(DSRC_UI_Polling,("uiPoll(): cooking keyboard event: <%d,%x>\n",kbe.state,kbe.code)); + err = kb_cook(kbe,&cooked,&result); + if (err != OK) return err; + if (result) + { + out.subtype = cooked; + out.type = UI_EVENT_KBD_COOKED; + eaten = uiDispatchEvent(&out); + } + } +// if (eaten) +// { +// kb_clear_state(kbe.code,KBA_STATE); +// } + } + else kbdone = TRUE; + } + if (!msdone) + { + ss_mouse_event mse; + errtype err = mouse_next(&mse); + /*if (poll_mouse_motion) + while (mse.type == MOUSE_MOTION && err == OK) + { + err = mouse_next(&mse); + }*/ + + if (err == OK) + { + out.pos.x = mse.x; + out.pos.y = mse.y; + // note that the equality operator here means that motion-only + // events are MOUSE_MOVE, and others are MOUSE events. + out.type = (mse.type == MOUSE_MOTION) ? UI_EVENT_MOUSE_MOVE : UI_EVENT_MOUSE; + out.subtype = mse.type; + out.mouse_data.tstamp = mse.timestamp; + out.mouse_data.buttons = mse.buttons; + out.mouse_data.modifiers = mse.modifiers; + ui_dispatch_mouse_event(&out); +// uiDispatchEvent((uiEvent*)mout); + } + else msdone = TRUE; + } + } + + if (poll_mouse_motion) + { + mousepos = ui_poll_mouse_position(); + } + if (ui_poll_keys != NULL && (uiGlobalEventMask & UI_EVENT_KBD_POLL)) + ui_poll_keyboard(); + ui_flush_mouse_events(mouse_get_time(),mousepos); + + // CC: Make sure the attack cursor doesn't display forever! + int diff = mouse_get_time() - last_mouse_draw_time; + + if (!PointsEqual(mousepos,last_mouse) || diff > uiDoubleClickDelay * 5) + { + ui_update_cursor(mousepos); + last_mouse = mousepos; + last_mouse_draw_time = mouse_get_time(); + } + + return OK; +} + +errtype uiSetMouseMotionPolling(uchar poll) +{ + if (poll) mouseMask &= ~MOUSE_MOTION; + else mouseMask |= MOUSE_MOTION; + poll_mouse_motion = poll; + return OK; +} + + + +errtype uiFlush(void) +{ + uiEvent* e; + kbs_event kbe = kb_next(); + mouse_flush(); + + while (kbe.code != KBC_NONE) + { + ushort dummy; + uchar result; + kb_cook(kbe,&dummy,&result); + kbe = kb_next(); + } + while(event_queue_next(&e)); + ui_pop_up_keys(); + ui_purge_mouse_events(); + return OK; +} + +uchar uiCheckInput(void) +{ + kbs_event kbe; + ss_mouse_event mse; + kbe = kb_next(); + if (kbe.code != KBC_NONE) + { + ushort cooked; + uchar res; + kb_cook(kbe,&cooked,&res); + if (kbe.state == KBS_DOWN) + { + ui_pop_up_keys(); + return TRUE; + } + } + if (mouse_next(&mse) == OK) + { + int i; + for (i = 0; i < NUM_MOUSE_BTNS; i++) + { + if ((mse.type & MOUSE_BTN2DOWN(i) ) != 0) + return TRUE; + } + } + return FALSE; +} + +// --------------------------- +// INITIALIZATION AND SHUTDOWN +// --------------------------- + +//char keybuf[512]; + +errtype uiInit(uiSlab* slab) +{ + int i; + errtype err; + + uiSetCurrentSlab(slab); +//KLC - moved to main program mouse_init(grd_cap->w,grd_cap->h); +//KLC - moved to main program kb_init(NULL); + // initialize the event queue; + EventQueue.in = EventQueue.out = 0; + EventQueue.size = INITIAL_QUEUE_SIZE; + EventQueue.vec = (uiEvent *)malloc(sizeof(uiEvent)*INITIAL_QUEUE_SIZE); + for (i = 0; i < NUM_MOUSE_BTNS; i++) + last_down_events[i].type = UI_EVENT_NULL; +//KLC - done in main program now. err = ui_init_cursors(); + if (err != OK) return err; +//KLC - AtExit(uiShutdown); + return OK; +} + +void uiShutdown(void) +{ + extern errtype ui_shutdown_cursors(void); + ui_shutdown_cursors(); + mouse_shutdown(); + kb_close(); +} + + +errtype uiShutdownRegionHandlers(LGRegion* r) +{ + errtype err = OK; + handler_chain *ch = (handler_chain*)(r->handler); + if (ch == NULL) return ERR_NOEFFECT; + err = array_destroy(&ch->chain); + free(ch); + return err; +} + +errtype ui_init_focus_chain(uiSlab* slab) +{ + errtype err = array_init(&slab->fchain.chain,sizeof(focus_link),INITIAL_FOCUSES); + if (err != OK) return err; + slab->fchain.curfocus = CHAIN_END; + return OK; +} + + + + + + + + + + + + + diff --git a/engine/src/Libraries/UI/Source/event.h b/engine/src/Libraries/UI/Source/event.h new file mode 100644 index 0000000..97b4ff6 --- /dev/null +++ b/engine/src/Libraries/UI/Source/event.h @@ -0,0 +1,300 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef _EVENT_H +#define _EVENT_H +#include "lg.h" +#include "error.h" +#include "slab.h" +#include "region.h" +#include "mouse.h" + +// --------------- +// INPUT EVENTS +// --------------- + +// Type-specific data for a raw key event. +typedef struct +{ + short scancode; // subtype + uchar action; // KBS_UP or _DOWN + ushort mods; // KLC - modifiers added for Mac version +} uiRawKeyData; + +// Poll key event is the same structure. +typedef uiRawKeyData uiPollKeyData; + + +// Cooked key events +typedef struct +{ + short code; /* cooked keycode, chock full o' stuff */ +} uiCookedKeyData; + +// mouse events +typedef struct +{ + short action; /* mouse event type, as per mouse library */ + uint32_t tstamp; + ubyte buttons; + uchar modifiers; +} uiMouseData; + +// joystick events +typedef struct +{ + short action; /* joystick event subtype, as defined below */ + uchar joynum; /* joystick number */ + LGPoint joypos; /* joystick position */ +} uiJoyData; + +// User-defined event can hold a pointer or integer. +typedef struct +{ + short subtype; + intptr_t data; +} uiUserData; + +// Generalized input event struct +typedef struct _ui_event +{ + LGPoint pos; + uint32_t type; + union + { + short subtype; + uiRawKeyData raw_key_data; + uiPollKeyData poll_key_data; + uiCookedKeyData cooked_key_data; + uiMouseData mouse_data; + uiJoyData joystick_data; + uiUserData user_data; + }; +} uiEvent; + + +// Type field values +#define UI_EVENT_NULL 0x00000000 +#define UI_EVENT_KBD_RAW 0x00000001 +#define UI_EVENT_KBD_COOKED 0x00000002 +#define UI_EVENT_KBD_POLL 0x00000020 +#define UI_EVENT_MOUSE 0x00000004 +#define UI_EVENT_MOUSE_MOVE 0x00000008 +#define UI_EVENT_JOY 0x00000010 +#define UI_EVENT_MIDI 0x10000000 // Hey, gotta be ready for the future. +#define UI_EVENT_USER_DEFINED 0x80000000 +#define ALL_EVENTS 0xFFFFFFFF + +// extended mouse event types (double clicks) +#define UI_MOUSE_LDOUBLE (1 << 7) +#define UI_MOUSE_RDOUBLE (1 << 8) +#define UI_MOUSE_CDOUBLE (1 << 9) +#define UI_MOUSE_BTN2DOUBLE(i) (128 << (i)) + +// The "first" events are generated for the first half of a +// double click, so you can start reacting to the first click +// without waiting for the second. + +#define UI_MOUSE_FIRST_LDOWN (1 << 10) +#define UI_MOUSE_FIRST_RDOWN (1 << 11) +#define UI_MOUSE_FIRST_CDOWN (1 << 12) +#define UI_MOUSE_BTN2FIRST_DOWN(i) (1 << ((i)+10)) + +#define UI_MOUSE_FIRST_LUP (1 << 13) +#define UI_MOUSE_FIRST_RUP (1 << 14) +#define UI_MOUSE_FIRST_CUP (1 << 15) +#define UI_MOUSE_BTN2FIRST_UP(i) (1 << ((i)+13)) + + +#define UI_JOY_MOTION 0 +#define UI_JOY_BUTTON1UP 1 +#define UI_JOY_BUTTON2UP 2 +#define UI_JOY_BUTTON1DOWN 3 +#define UI_JOY_BUTTON2DOWN 4 + +// ---------------- +// EVENT HANDLERS +// ---------------- + +/* Event handlers are installed by the client to receive callbacks when events + happen. Event handlers are called when the ui toolkit is + polled. Interrupt-driven phenomena such as mouse cursors will, in + general, be internal to the ui-toolkit. An event handler is installed + on a region, and will receive events when the mouse is in that region. + It is possible to chain event handlers within a region. In + this case, an event is "offered" to each event handler, in order, + one at a time until an event handler chooses to accept it. */ + + +typedef uchar (*uiHandlerProc)(uiEvent* e, LGRegion* r, intptr_t state); + +// If an event-handler's proc returns true, it has accepted the event, and no +// other event handler will see the event. An event handler will only be +// offered those events specified by its typemask; it automatically rejects +// any other events. + +errtype uiInstallRegionHandler(LGRegion* v, uint32_t evmask, uiHandlerProc proc, intptr_t state, int* id); +// installs an event handler at the front of r's handler chain. The event handler +// will call "proc" with the event, the region "v", and the value of "state" +// whenever "v" receives any event whose type bit is set in evmask. +// sets *id to an id for that event handler. + + +errtype uiRemoveRegionHandler(LGRegion* v, int id); +// Removes the event handler with the specified id from a region's handler chain + +errtype uiSetRegionHandlerMask(LGRegion* r, int id, int evmask); +// Changes the event mask for handler #id in region r. + +errtype uiShutdownRegionHandlers(LGRegion* r); +// Shut down and destroy all handlers for a region. + + +// -------------- +// REGION OPACITY +// -------------- + +// The opacity of a region is the mask of event types that cannot pass through the +// region. Set bits in the opacity mask indicate that events of that type will be +// automatically rejected if they reach that region, and will not be offered to any other region. + +// Se uiDefaultRegionOpacity in the globals section + +errtype uiSetRegionOpacity(LGRegion* r, ulong opacity); +// Sets the opacity of a region. + +ulong uiGetRegionOpacity(LGRegion* r); +// Gets the opacity mask of the region. + + +// ----------- +// INPUT FOCUS +// ----------- + +errtype uiGrabFocus(LGRegion* r, ulong evmask); +// grabs input focus on the active slab for region r for events specified by evmask + +errtype uiReleaseFocus(LGRegion* r, ulong evmask); +// If r has the current input focus in the active slab, then releases r's +// focus on the events specified by evmask, and restores the previous focus. Else does nothing. + +errtype uiGrabSlabFocus(uiSlab* slab, LGRegion* r, ulong evmask); +// Grabs focus for region r on the specified slab. + +errtype uiReleaseSlabFocus(uiSlab* slab, LGRegion* r, ulong evmask); +// If r has the current input focus in the specified slab, then releases r's +// focus on the events specified by evmask, and restores the previous focus. +// Else does nothing. + + +// ----------------------- +// POLLING AND DISPATCHING +// ----------------------- + +errtype uiPoll(void); +// polls the ui toolkit, dispatching all events. + + +errtype uiQueueEvent(uiEvent* ev); +// adds an event to the ui event queue. The event will be dispatched at the next uiPoll() call + +uchar uiDispatchEvent(uiEvent* ev); +// Dispatches an event right away, without queueing. Returns +// Whether or not the event was accepted by a handler. + +uchar uiDispatchEventToRegion(uiEvent* ev, LGRegion* r); +// Like uiDispatchEvent, but dispatches an event to a +// specific region's event handlers. + +errtype uiSetMouseMotionPolling(uchar poll); +// Iff poll is true, exactly one mouse motion event will be generated +// per call to uiPoll, the motion event will be generated by polling the +// mouse position. Otherwise, all motion events generated by the interrupt handler will +// be dispatched, and thus no motion event will be dispatched if the mouse has not moved. +// Defaults to FALSE + +errtype uiMakeMotionEvent(uiEvent* ev); +// Fills *ev with a mouse motion event reflecting the current mouse position. + +errtype uiSetKeyboardPolling(uchar* codes); +// Codes is a KBC_NONE terminated array of scancodes to be polled by the system. +// if a code is in the list, the specified key will generate one keyboard polling event +// (type UI_EVENT_KBD_POLL) per call to uiPoll. otherwise, no such event will be generated +// for this key. +// defaults to NULL. + +errtype uiFlush(void); +// Flushes all ui system input events. + +uchar uiCheckInput(void); +// reads through the input queue, returning true if there +// is a key or mouse button up event, false otherwise. + +// --------------------------- +// INITIALIZATION AND SHUTDOWN +// --------------------------- + +errtype uiInit(uiSlab* slab); +// Initialize the ui toolkit. +// Sets the current slab. + +void uiShutdown(void); +// shuts down the ui toolkit. + + + +// ---------------- +// GLOBALS +// ---------------- + +extern ushort uiDoubleClickTime; +// The maximum time separation between individual clicks of a double click + +extern ushort uiDoubleClickDelay; +// The maximum allowed time between the first down and up event in a double click + +extern uchar uiDoubleClicksOn[NUM_MOUSE_BTNS]; +// are double clicks allowed for the specified button? +// defaults to FALSE + +extern uchar uiAltDoubleClick; +// Whether alt-click should emulate double click. +// Defaults to FALSE; + +extern LGRegion* uiLastMouseRegion[NUM_MOUSE_BTNS]; +// Stores a pointer to the region that accepted the +// last down event for each button. + +extern ushort uiDoubleClickTolerance; +// How much mouse motion will we tolerate before discarding +// a potiential double click. Defaults to 5. + +extern ulong uiGlobalEventMask; +// Global mask of what events are to be dispatched. +// initially set to ALL_EVENTS + +extern ulong uiDefaultRegionOpacity; +// The initial value of the opacity of a region, used until +// an opacity is set for the region. +// Defaults to zero. +// When a handler is added to a region, the opacity is set to the +// value of uiDefaultRegionOpacity. + + + +#endif // _EVENT_H diff --git a/engine/src/Libraries/UI/Source/hotkey.c b/engine/src/Libraries/UI/Source/hotkey.c new file mode 100644 index 0000000..b43a3b4 --- /dev/null +++ b/engine/src/Libraries/UI/Source/hotkey.c @@ -0,0 +1,264 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#define __HOTKEY_SRC +#include "hotkey.h" +#include "hash.h" +//#include <_ui.h> + +#ifdef HOTKEY_HELP +#include +#endif + +#define CHAIN_LENGTH 2 +#define CHAIN_END -1 + +ulong HotkeyContext = 0xFFFFFFFF; + + +#pragma require_prototypes off +int hotkey_hash_func(void* v) +{ + hotkey_entry* e = (hotkey_entry*)v; + return e->key; +} + +int hotkey_equ_func(void* v1, void* v2) +{ + return ((hotkey_entry*)v1)->key - ((hotkey_entry*)v2)->key; +} +#pragma require_prototypes on + + +errtype hotkey_init(int tblsize) +{ + return hash_init(&hotkey_table,sizeof(hotkey_entry),tblsize,hotkey_hash_func,hotkey_equ_func); +} + +errtype hotkey_add(ushort keycode, uint32_t contexts, hotkey_callback func, intptr_t state) +{ +#ifdef HOTKEY_HELP + return(hotkey_add_help(keycode,contexts,func,state,NULL)); +} + +errtype hotkey_add_help(ushort keycode, uint32_t contexts, hotkey_callback func, intptr_t state, char * help_text) +{ +#endif + hotkey_entry e,*ch; + errtype err; + int i; + hotkey_link *chain; + e.key = keycode; + err = hash_lookup(&hotkey_table,&e,(void **)&ch); + if (err != OK) return err; + if (ch == NULL) + { +// Spew(DSRC_UI_Hotkey,("Creating new hotkey chain\n")); + err = hash_insert(&hotkey_table,&e); + if (err != OK) return err; + hash_lookup(&hotkey_table,&e,(void **)&ch); + array_init(&ch->keychain,sizeof(hotkey_link),CHAIN_LENGTH); + ch->first = CHAIN_END; + } + err = array_newelem(&ch->keychain,&i); + if (err != OK) return err; + chain = (hotkey_link*)ch->keychain.vec; + chain[i].context = contexts; + chain[i].func = func; + chain[i].state = state; +#ifdef HOTKEY_HELP +// chain[i].help_text = malloc(strlen(help_text)+1); +// strcpy(chain[i].help_text,help_text); +#endif + chain[i].next = ch->first; + ch->first = i; + return OK; +} + +/* KLC - not used +#ifdef HOTKEY_HELP +char *hotkey_help_text(short keycode, ulong contexts, hotkey_callback func) +{ + hotkey_entry *ch; + errtype err; + int i; + hotkey_link *chain; + err = hash_lookup(&hotkey_table,(hotkey_entry*)&keycode,(void **)&ch); + if (err != OK) return(NULL) ; + if (ch == NULL) return(NULL); + chain = (hotkey_link*)ch->keychain.vec; + for (i = ch->first; chain[i].func == func;) + { + chain[i].context &= ~contexts; + if (chain[i].context == 0) + { + return(chain[i].help_text); + } + } + for(i = ch->first; chain[i].next != CHAIN_END; i = chain[i].next) + { + int n = chain[i].next; + if (chain[n].func == func) + { + chain[n].context &= ~contexts; + if (chain[n].context == 0) + { + return(chain[n].help_text); + } + } + } + return(NULL); +} +#endif +*/ + +errtype hotkey_remove(short keycode, ulong contexts, hotkey_callback func) +{ + hotkey_entry *ch; + errtype err; + int i; + hotkey_link *chain; + err = hash_lookup(&hotkey_table,(hotkey_entry*)&keycode,(void **)&ch); + if (err != OK) return err; + if (ch == NULL) return ERR_NOEFFECT; + chain = (hotkey_link*)ch->keychain.vec; + for (i = ch->first; chain[i].func == func;) + { + chain[i].context &= ~contexts; + if (chain[i].context == 0) + { + ch->first = chain[i].next; +#ifdef HOTKEY_HELP +// free(chain[i].help_text); +#endif // HOTKEY_HELP + array_dropelem(&ch->keychain,i); + i = ch->first; + } + } + for(i = ch->first; chain[i].next != CHAIN_END; i = chain[i].next) + { + int n = chain[i].next; + if (chain[n].func == func) + { + chain[n].context &= ~contexts; + if (chain[n].context == 0) + { + chain[i].next = chain[n].next; +#ifdef HOTKEY_HELP +// free(chain[i].help_text); +#endif // HOTKEY_HELP + array_dropelem(&ch->keychain,n); + } + } + } + return OK; +} + + +errtype hotkey_dispatch(short keycode) +{ + hotkey_entry *ch; + errtype err; + int i; + hotkey_link *chain; + err = hash_lookup(&hotkey_table,(hotkey_entry*)&keycode,(void **)&ch); + if (err != OK) return err; + if (ch == NULL) return ERR_NOEFFECT; + chain = (hotkey_link*)ch->keychain.vec; + for (i = ch->first; i != CHAIN_END; i = chain[i].next) + { +// Spew(DSRC_UI_Hotkey,("checking link %d \n",i)); + if (chain[i].context & HotkeyContext) + { +// Spew(DSRC_UI_Hotkey,("Succeeded context test %d\n",chain[i].context)); + if (chain[i].func(keycode,HotkeyContext,chain[i].state)) + return OK; + } + } + return ERR_NOEFFECT; +} + +static uchar shutdown_iter_func(void* elem, void* data) +{ +#ifndef NO_DUMMIES + void *dummy = data; +#endif // NO_DUMMIES + hotkey_entry* ch = (hotkey_entry*)elem; +/* KLC +#ifdef HOTKEY_HELP + int i; + hotkey_link *chain = (hotkey_link*)(ch->keychain.vec); + + if (ch == NULL) return FALSE; + for (i = ch->first; i != CHAIN_END; i = chain[i].next) + { + free(chain[i].help_text); + } +#endif // HOTKEY_HELP +*/ +#ifndef NO_DUMMIES + data = dummy; +#endif // NO_DUMMIES + array_destroy(&ch->keychain); + return FALSE; +} + +errtype hotkey_shutdown(void) +{ + hash_iter(&hotkey_table,shutdown_iter_func,NULL); + hash_destroy(&hotkey_table); + return OK; +} + +int list_index = 0; + +#ifdef GODDAMN_THIS_MESS_IS_IMPOSSIBLE +uchar hotkey_list(char **item, int sort_type) +{ + void *res; + hotkey_entry* ch; + hotkey_link *chain; + int i; + + hash_step(&hotkey_table, &res, &list_index); + ch = (hotkey_entry *)res; + if (ch == NULL) return ERR_NOEFFECT; + chain = (hotkey_link*)ch->keychain.vec; + strcpy(*item, ""); + for (i = ch->first; i != CHAIN_END; i = chain[i].next) + { + strcat(*item, + if (chain[i].context & HotkeyContext) + { + Spew(DSRC_UI_Hotkey,("Succeeded context test %d\n",chain[i].context)); + if (chain[i].func(keycode,HotkeyContext,chain[i].state)) + return OK; + } + } + strcpy(*item, +} + +errtype hotkey_list_clear() +{ + list_index = 0; +} + +#endif + + + diff --git a/engine/src/Libraries/UI/Source/hotkey.h b/engine/src/Libraries/UI/Source/hotkey.h new file mode 100644 index 0000000..ba1cb53 --- /dev/null +++ b/engine/src/Libraries/UI/Source/hotkey.h @@ -0,0 +1,148 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __HOTKEY_H +#define __HOTKEY_H + +/* + * $Source: n:/project/lib/src/ui/RCS/hotkey.h $ + * $Revision: 1.6 $ + * $Author: dc $ + * $Date: 1993/10/11 20:27:20 $ + * + * $Log: hotkey.h $ + * Revision 1.6 1993/10/11 20:27:20 dc + * Angle is fun, fun fun fun + * + * Revision 1.5 1993/06/14 21:50:15 xemu + * export structures + * + * Revision 1.4 1993/06/14 21:12:08 xemu + * failed list + * + * Revision 1.3 1993/05/17 15:52:21 xemu + * help text + * + * Revision 1.2 1993/04/28 14:40:17 mahk + * Preparing for second exodus + * + * Revision 1.1 1993/03/26 21:50:33 mahk + * Initial revision + * + * + */ + +// Includes +#include "lg.h" // every file should have this + +// C Library Includes + +// System Library Includes +#include +#include "error.h" +#include "hash.h" +#include "kbcook.h" +#include "array.h" + +// Master Game Includes + +// Game Library Includes + +// Game Object Includes + + +// Defines + +#define HOTKEY_HELP 1 + +#define HKSORT_NONE 0 +#define HKSORT_KEYCODE 1 +#define HKSORT_ASCII 2 + +typedef uchar (*hotkey_callback)(ushort keycode, uint32_t context, intptr_t state); + +typedef struct _hotkey_entry +{ + ushort key; + Array keychain; + int first; +} hotkey_entry; + +typedef struct _hotkey_link +{ + uint32_t context; +#ifdef HOTKEY_HELP + hotkey_callback func; +#endif + intptr_t state; + char *help_text; + int next; +} hotkey_link; + +#ifdef __HOTKEY_SRC +Hashtable hotkey_table; +#else +extern Hashtable hotkey_table; +#endif + +// Prototypes + +errtype hotkey_init(int tblsize); +// Initialize hotkey table, giving an initial context and table size. + +errtype hotkey_add(ushort keycode, uint32_t context_mask, hotkey_callback func, intptr_t state); +// installs a hotkey handler for a specific cooked keycode in the set of contexts described by context_mask. +// This handler will take precidence over previously-installed handlers. + +#ifdef HOTKEY_HELP +errtype hotkey_add_help(ushort keycode, uint32_t context_mask, hotkey_callback func, intptr_t state, char *help_text); +// like hotkey_add, but also takes a help string which it stores +// for later reference. + +char *hotkey_help_text(short keycode, ulong contexts, hotkey_callback func); +// looks up the help string for a given hotkey + +#endif + +errtype hotkey_remove(short keycode, ulong context_mask, hotkey_callback func); +// delete all hotkey handlers with the specified keycode and callback function +// from the contexts specified by the context_mask. + +errtype hotkey_dispatch(short keycode); +// dispatches the keycode to the highest-priority key handler for that +// keycode that has any set bits in common with HotkeyContext. + +errtype hotkey_shutdown(void); +// shut down the hotkey system. + +#ifdef GODDAMN_THIS_MESS_IS_IMPOSSIBLE +uchar hotkey_list(char **item, int sort_type); +// stores in item a string that is the next hotkey string off of the +// list, along with it's help text. Returns whether or not there +// are more hotkeys to list out. sort_type determines what sorting +// method is used. + +errtype hotkey_list_clear(); +// Starts hotkey listing at the beginning. +#endif + +// Globals + +extern ulong HotkeyContext; + +#endif // __HOTKEY_H diff --git a/engine/src/Libraries/UI/Source/region.c b/engine/src/Libraries/UI/Source/region.c new file mode 100644 index 0000000..6f4920e --- /dev/null +++ b/engine/src/Libraries/UI/Source/region.c @@ -0,0 +1,973 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Source Code for the LGRegion library + +#include + +#include "cursors.h" +#include "region.h" +#include "slist.h" + + +typedef struct { + struct _slist *psnext; + LGRegion *reg; + LGRect exp_rect; +} Region_Sequence_Element; + + +/* Prototypes */ + +LGRegion *trav_get_first(LGRegion *reg, int order); +LGRegion *trav_get_next(LGRegion *curp, int order); +errtype region_place(LGRegion *reg); +errtype region_remove(LGRegion *reg, uchar draw); +void region_moverect(LGRegion *reg, int delta_x, int delta_y, int move_rel); +void region_propagate_callback(LGRegion *reg, ulong callback_code, LGRect *arg_rect); +uchar reg_exp_CB(LGRegion *reg, LGRect *rc, void *data); +errtype region_expose_absolute(LGRegion *reg, LGRect *newr); +errtype region_manage_place(LGRegion *reg); +errtype region_manage_remove(LGRegion *reg); +int region_convert_tochild(LGRegion *from_reg, LGRect *orig, LGRect *conv); +errtype region_abs_rect(LGRegion *reg, LGRect *orig_rect, LGRect *conv); +errtype region_set_moving(LGRegion *reg, int val); +int region_convert_tochild(LGRegion *from_reg, LGRect *orig, LGRect *conv); +int region_convert_toparent(LGRegion *from_reg, LGRect *orig, LGRect *conv); +uchar is_child(LGRegion *poss_parent, LGRegion *child); +uchar region_obscured_callback(LGRegion *reg, LGRect *r, void *data); +Region_Sequence_Element *get_rse_from_pool(); +errtype return_rse_to_pool(Region_Sequence_Element *rse); +errtype region_add_sequence_expose(LGRegion *reg, LGRect exp_rect); + +/* Globals */ + +int region_in_sequence = 0; +uchar region_system_init = FALSE, region_found; +slist_head sequence_header; +LGRegion *obsc_region, *current_expose_region; + +/* API FUNCTIONS */ + +errtype init_rse_pool(); + +errtype region_init() +{ + region_system_init = TRUE; + slist_init(&sequence_header); + init_rse_pool(); + return(OK); +} + +errtype region_create(LGRegion *parent, LGRegion *ret, LGRect *r, int z, int event_order, + ulong status, RectCallback expose, + RectCallback save_under, RectCallback replace, void *user_data) +{ + LGRegion *curp, *lastp; + + if (!region_system_init) + region_init(); + + /* Plug in parameters */ + ret->real_rect = *r; + ret->r = &(ret->real_rect); + ret->z = z; + ret->event_order = event_order; + ret->status_flags = status; + ret->expose = expose; + ret->save_under = save_under; + ret->replace = replace; + ret->user_data = user_data; + ret->parent = parent; + + /* Compute initial values for other things */ + ret->handler = NULL; + ret->cursors = NULL; + ret->moving = 0; + ret->sub_region = NULL; + ret->next_region = NULL; + ret->device_type = -1; + ret->abs_x = r->ul.x; // If in root region, rel = abs + ret->abs_y = r->ul.y; // otherwise, is base for real abs + if (ret->parent != NULL) + { + /* Inherit device_type value */ + ret->device_type = (ret->parent)->device_type; + ret->abs_x += (ret->parent)->abs_x; + ret->abs_y += (ret->parent)->abs_y; + curp = (ret->parent)->sub_region; + // Spew(DSRC_UI_Initialization, ("parent loop: reg = %s(%d) ",GD_NAME(ret), ret->z)); + if (curp == NULL) + { + // If we are first child of parent... + (ret->parent)->sub_region = ret; + } else { + // Otherwise put ourselves in proper place in z-sorted chain + lastp = NULL; + while ((curp != NULL) && (curp->z > ret->z)) { + lastp = curp; + curp = curp->next_region; + } + /* + if (lastp != NULL) + Spew(DSRC_UI_Initialization, ("lastp = %s(%d) ",GD_NAME(lastp),lastp->z)); + if (curp != NULL) + Spew(DSRC_UI_Initialization, ("curp = %s(%d) ",GD_NAME(curp),curp->z)); + Spew(DSRC_UI_Initialization, ("!\n")); + */ + ret->next_region = curp; + if (lastp != NULL) + lastp->next_region = ret; + else + (ret->parent)->sub_region = ret; + } + } + + if (ret->status_flags & DISPLAY_ON_CREATION) + { + // Spew(DSRC_UI_Initialization, ("right before region_place: abs_x = %d, abs_y = %d\n",ret->abs_x, ret->abs_y)); + region_place(ret); + } + + return(OK); +} + +errtype region_destroy(LGRegion *reg, uchar draw) +{ + LGRegion *curp, *lastp, *nextp; + extern errtype uiShutdownRegionHandlers(LGRegion* r); + + // Make us disappear + region_remove(reg,draw); + + // First, we kill our children...all of them!! + curp = reg->sub_region; + while (curp != NULL) + { + nextp = curp->next_region;; + region_destroy(curp,draw); + curp = nextp; + } + + // Then, we make our siblings / parent forget about us! + if (reg->parent != NULL) + { + curp = (reg->parent)->sub_region; + lastp = NULL; + while (curp != reg) + { + lastp = curp; + curp = curp->next_region; + } + if (lastp != NULL) + lastp->next_region = curp->next_region; + else + (reg->parent)->sub_region = curp->next_region; + } + + // Shutdown handlers + uiShutdownRegionHandlers(reg); + + // Then, we kill OURSELVES!!!!!!!!!!!! + if (AUTODESTROY_FLAG & reg->status_flags) + { + free(reg->r); + free(reg); + } + return(OK); +} + +errtype region_move(LGRegion *reg, int new_x, int new_y, int new_z) +{ + int delta_x, delta_y; + LGRegion *lastp, *curp; + + /* trigger the "picking up" callbacks */ + region_remove(reg,TRUE); + + // Spew(DSRC_UI_Region_Manipulation, ("after region_remove call...")); + /* Update the database numbers */ + delta_x = new_x - (reg->r)->ul.x; + delta_y = new_y - (reg->r)->ul.y; + region_moverect(reg, delta_x, delta_y, 1); + // Spew(DSRC_UI_Region_Manipulation, ("after region_moverect...")); + if (reg->z != new_z) + { + // disconnect us from our previous position + if (reg->parent != NULL) + { + curp = (reg->parent)->sub_region; + lastp = NULL; + while (curp != reg) + { + lastp = curp; + curp = curp->next_region; + } + if (lastp != NULL) + lastp->next_region = curp->next_region; + else + (reg->parent)->sub_region = curp->next_region; + + // Spew(DSRC_UI_Region_Manipulation, ("After disconnection...")); + + // set new value + reg->z = new_z; + + // reconnect us in correct position + curp = (reg->parent)->sub_region; + // Spew(DSRC_UI_Region_Manipulation, ("reg = %s(%d) ",GD_NAME(reg), reg->z)); + if (curp == NULL) + { + // If we are first child of parent... + (reg->parent)->sub_region = reg; + } else { + // Otherwise put ourselves in proper place in z-sorted chain + lastp = NULL; + while ((curp != NULL) && (curp->z > reg->z)) { + lastp = curp; + curp = curp->next_region; + } + /* + if (lastp != NULL) + Spew(DSRC_UI_Utilities, ("lastp = %s(%d) ",GD_NAME(lastp),lastp->z)); + if (curp != NULL) + Spew(DSRC_UI_Utilities, ("curp = %s(%d) ",GD_NAME(curp),curp->z)); + Spew(DSRC_UI_Utilities, ("!\n")); + */ + reg->next_region = curp; + if (lastp != NULL) + lastp->next_region = reg; + else + (reg->parent)->sub_region = reg; + } + // Spew(DSRC_UI_Region_Manipulation, ("After reconnection...")); + } + + } + + /* trigger the "placing down" callbacks */ + region_place(reg); + return(OK); +} + +errtype region_resize(LGRegion *reg, int new_x_size, int new_y_size) +{ + int delta_x, delta_y; + + delta_x = new_x_size - RectWidth(reg->r); + delta_y = new_y_size - RectHeight(reg->r); + + (reg->r)->lr.x += delta_x; + (reg->r)->lr.y += delta_y; + + region_place(reg); + return(OK); +} + +int region_traverse_point(LGRegion *reg, LGPoint target, TravRectCallback fn, int order, void *data) +{ + LGRect inter, newtarget; + LGRegion *curp; + int retval = 0, iflag = 0; + +// Spew(DSRC_UI_Traversal, ("r_t_point -- target = (%d, %d) %s->r = (%d, %d) - (%d, %d)\n", +// target.x, target.y, GD_NAME(reg), RECT_PRINT_ARGS(reg->r))); + + inter.ul = target; + inter.lr = target; + if ((reg->status_flags & INVISIBLE_FLAG) != 0) + return FALSE; + if (RectTestPt(reg->r, target)) + iflag = 1; + if ((order == BOTTOM_TO_TOP) && iflag) + { +// Spew(DSRC_UI_Traversal, ("BOTTOM_TO_TOP, root case, target = (%d,%d)\n",target.x, target.y)); + retval = fn(reg, &inter, data); + } + curp = trav_get_first(reg, order); +// Spew(DSRC_UI_Traversal, ("before while, curp = ")); +// if (curp) +// Spew(DSRC_UI_Traversal, ("%s\n",GD_NAME(curp))); +// else +// Spew(DSRC_UI_Traversal, ("NULL\n")); + while (!retval && (curp != NULL)) + { + region_convert_tochild(reg, &inter, &newtarget); +// Spew(DSRC_UI_Traversal, ("while case, newtarget.ul = (%d,%d)\n",newtarget.ul.x, newtarget.ul.y)); + retval = region_traverse_point(curp, newtarget.ul, fn, order, data); + curp = trav_get_next(curp, order); + } + if ((order == TOP_TO_BOTTOM) && (iflag) && (!retval)) + { +// Spew(DSRC_UI_Traversal, ("TOP_TO_BOTTOM, root case\n")); + retval = fn(reg, &inter, data); + } + return (retval); +} + +int region_traverse_rect(LGRegion *reg, LGRect *target, TravRectCallback fn, int order, + void *data) +{ + LGRect inter, newtarget; + LGRegion *curp; + int retval = 0, iflag = 0; + + // Spew(DSRC_UI_Traversal, ("r_t_r -- target = (%d, %d) - (%d, %d) %s->r = (%d, %d) - (%d, %d)\n", + // RECT_PRINT_ARGS(target), GD_NAME(reg), RECT_PRINT_ARGS(reg->r))); + if ((reg->status_flags & INVISIBLE_FLAG) != 0) + return FALSE; + if (RectTestSect(reg->r, target)) { + RectSect(reg->r, target, &inter); + iflag = 1; + } + if ((order == BOTTOM_TO_TOP) && iflag) + { + // Spew(DSRC_UI_Traversal, ("BOTTOM_TO_TOP, root case, target = (%d,%d)(%d,%d)\n",RECT_EXPAND_ARGS(target))); + retval = fn(reg, &inter, data); + } + curp = trav_get_first(reg, order); +/* + if (curp) + { + Spew(DSRC_UI_Traversal,("before while, curp = %s\n",GD_NAME(curp))); + } + else + { + Spew(DSRC_UI_Traversal,("before while, curp = NULL\n")); + } +*/ + while (!retval && curp) + { + region_convert_tochild(reg, target, &newtarget); + // Spew(DSRC_UI_Traversal, ("while case, newtarget = (%d,%d)(%d,%d)\n",RECT_EXPAND_ARGS(&newtarget))); + retval = region_traverse_rect(curp, &newtarget, fn, order, data); + curp = trav_get_next(curp,order); + } + if ((order == TOP_TO_BOTTOM) && (iflag) && (!retval)) + { + // Spew (DSRC_UI_Traversal, ("TOP_TO_BOTTOM, root case\n")); + retval = fn(reg, &inter, data); + } + return (retval); +} + +int region_traverse(LGRegion *reg, TravCallback fn, int order, void *data) +{ + LGRegion *curp; + int retval = 0; + + // Spew(DSRC_UI_Traversal, ("r_traverse -- %s\n",GD_NAME(reg))); + if ((reg->status_flags & INVISIBLE_FLAG) != 0) + return FALSE; + if (order == BOTTOM_TO_TOP) + { + // Spew (DSRC_UI_Traversal, ("BOTTOM_TO_TOP, root case\n")); + retval = fn(reg, data); + } + curp = trav_get_first(reg, order); +/* + if (curp) + { + Spew(DSRC_UI_Traversal,("before while, curp = %s\n",GD_NAME(curp))); + } + else + { + Spew(DSRC_UI_Traversal,("before while, curp = NULL\n")); + } +*/ + while (!retval && curp) + { + retval = region_traverse(curp, fn, order, data); + curp = trav_get_next(curp,order); + } + if ((order == TOP_TO_BOTTOM) && (!retval)) + { + // Spew (DSRC_UI_Traversal, ("TOP_TO_BOTTOM, root case\n")); + retval = fn(reg, data); + } + return (retval); +} + +LGRegion *trav_get_first(LGRegion *reg, int order) +{ + LGRegion *ptr, *retval; + + if (order == TOP_TO_BOTTOM) + return(reg->sub_region); + else + { + ptr = reg->sub_region; + retval = NULL; + while (ptr != NULL) + { + retval = ptr; + ptr = ptr->next_region; + } + return(retval); + } +} + +LGRegion *trav_get_next(LGRegion *curp, int order) +{ + LGRegion *retval, *ptr; + + if (order == TOP_TO_BOTTOM) + { + retval = curp->next_region; + } + else + { + if (curp->parent != NULL) + ptr = curp->parent->sub_region; + else + ptr = curp; + retval = NULL; + // if (ptr != NULL) + // Spew(DSRC_UI_Traversal, ("ptr = %s\n",GD_NAME(ptr))); + // else + // Spew(DSRC_UI_Traversal, ("ptr = NULL!\n")); + while (ptr != curp) + { + retval = ptr; + ptr = ptr->next_region; +// if (ptr != NULL) +// Spew(DSRC_UI_Traversal, ("ptr = %s\n",GD_NAME(ptr))); +// else +// Spew(DSRC_UI_Traversal, ("ptr = NULL!\n")); + } + } +/* + // Spew(DSRC_UI_Traversal, ("order = %d ",order)); + if (!curp) + { + // Spew(DSRC_UI_Traversal, ("curp = NULL ")); + } + else + { + // Spew(DSRC_UI_Traversal, ("curp = %s ",GD_NAME(curp))); + } + if (!retval) + { + // Spew(DSRC_UI_Traversal, ("next = NULL \n")); + } + else + { + // Spew(DSRC_UI_Traversal, ("next = %s \n",GD_NAME(retval))); + } + // Spew(DSRC_UI_Traversal, ("!!\n")); +*/ + return(retval); +} + +/* INTERNAL FUNCTIONS */ + +/* Call appropriate callbacks and automanaging functions for + slapping a region down onto momma region. Assumes the DB + contains the new location for the thing. */ + +errtype region_place(LGRegion *reg) +{ + // NOTE: Assumes that the DB already has the correct values about you... + + +#ifdef UI_LINKED + /* If appropriate, do automanaging things */ + if (reg->status_flags & AUTOMANAGE_FLAG) + { + region_manage_place(reg); + } +#endif // UI_LINKED + + /* Dispatch appropriate callbacks */ + if (reg->parent != NULL) + region_propagate_callback(reg, SAVEUNDER_CB, reg->r); + + region_expose(reg, reg->r); + return(OK); +} + +errtype region_remove(LGRegion *reg, uchar draw) +{ + // This is currently a very stupid algorithm with lots of flicker and wasted effort + // Needs to be made better! + +#ifdef UI_LINKED + /* If appropriate, do automanaging things */ + if (reg->status_flags & AUTOMANAGE_FLAG) + { + region_manage_remove(reg); + } +#endif // UI_LINKED + + if (reg->parent != NULL) + region_propagate_callback(reg, REPLACE_CB, reg->r); + region_set_moving(reg,1); + // Spew(DSRC_UI_Callbacks, ("Removing %s\n",GD_NAME(reg))); + if (draw && (reg->parent != NULL)) + { + // First off, if we have a parent, expose that area of parent to fill + // in gap we leave behind. + // Spew(DSRC_UI_Callbacks, ("parent exposure of %s, (%d,%d)(%d,%d)!\n",GD_NAME(reg),RECT_EXPAND_ARGS(reg->r))); + region_expose(reg, reg->r); + } + region_set_moving(reg,0); + return(OK); +} + +void region_moverect(LGRegion *reg, int delta_x, int delta_y, int move_rel) +{ + LGRegion *curp; + + // Spew(DSRC_UI_Region_Manipulation, ("starting region moverect for (%d, %d)(%d, %d)\n",RECT_EXPAND_ARGS(reg->r))); + /* Move us */ + if (move_rel) + { + (reg->r)->ul.x += delta_x; + (reg->r)->ul.y += delta_y; + (reg->r)->lr.x += delta_x; + (reg->r)->lr.y += delta_y; + } + reg->abs_x += delta_x; + reg->abs_y += delta_y; +/* if (reg->parent != NULL) + { + reg->abs_x += (reg->parent)->abs_x; + reg->abs_y += (reg->parent)->abs_y; + } */ + + /* Since our kids are conveniently in OUR frame of reference, + we only need to update their absolute coords */ + + /* For our kids */ + curp = reg->sub_region; + while (curp != NULL) + { + curp->abs_x += delta_x; + curp->abs_y += delta_y; + curp = curp->next_region; + } + + // Spew(DSRC_UI_Region_Manipulation, ("ending region moverect for (%d, %d) - (%d, %d)\n", + // RECT_PRINT_ARGS(reg->r))); +} + +void region_propagate_callback(LGRegion *reg, ulong callback_code, LGRect *arg_rect) +{ + LGRegion *curp; + RectCallback fn; + LGRect new_rect,abs_rect; + extern errtype uiHideMouse(LGRect* r), uiShowMouse(LGRect* r); + + switch(callback_code) + { + case SAVEUNDER_CB: + fn = reg->save_under; + break; + case REPLACE_CB: + fn = reg->replace; + break; + } + if ((reg->status_flags & callback_code) && (fn != NULL) && !(reg->moving)) + { + // Spew(DSRC_UI_Callbacks, ("CB %d sent , arg (%d,%d)-(%d,%d)\n",callback_code, + // RECT_PRINT_ARGS(arg_rect))); + abs_rect.ul.x = reg->abs_x; + abs_rect.ul.y = reg->abs_y; + abs_rect.lr.x = reg->abs_x + RectWidth(reg->r); + abs_rect.lr.y = reg->abs_y + RectHeight(reg->r); + uiHideMouse(&abs_rect); + if (!fn(reg, arg_rect)) + { + region_convert_tochild(reg, arg_rect, &new_rect); + curp = reg->sub_region; + while (curp != NULL) + { + region_propagate_callback(curp, callback_code, &new_rect); + curp = curp->next_region; + } + } + uiShowMouse(&abs_rect); + } +} + +uchar reg_exp_CB(LGRegion *reg, LGRect *rc, void *data) +{ + uchar *dbp; + dbp = (uchar *)data; + if ((!(*dbp)) && !(current_expose_region->moving)) + { + if (reg == current_expose_region) + *dbp = TRUE; + else + { + // Spew(DSRC_UI_Callbacks, ("Did not display region %s (vs. %s) (%d,%d)(%d,%d)\n",GD_NAME(reg),GD_NAME(current_expose_region), + // RECT_EXPAND_ARGS(rc))); + return(FALSE); + } + } + + if ((reg->status_flags & EXPOSE_CB) && (reg->expose != NULL) && (!reg->moving)) + { + // Spew(DSRC_UI_Callbacks, ("Expose CB sent! (%d,%d)(%d,%d)\n", RECT_EXPAND_ARGS(rc))); + reg->expose(reg, rc); + } + return (FALSE); +} + +errtype region_expose_absolute(LGRegion *reg, LGRect *newr) +{ + LGRect absr, dummy_rect; + uchar draw_beneath = TRUE; + LGRegion *par; + + absr.ul.x = reg->abs_x; absr.ul.y = reg->abs_y; + absr.lr.x = absr.ul.x + RectWidth(reg->r); + absr.lr.y = absr.ul.y + RectHeight(reg->r); + region_convert_to_root(reg, &par, newr, &dummy_rect); + + // Spew(DSRC_UI_Callbacks, ("Expose of %s (%d,%d)(%d,%d) -- newr = (%d,%d)(%d,%d)\n",GD_NAME(reg), + // RECT_EXPAND_ARGS(&absr), RECT_EXPAND_ARGS(newr))); + if (RECT_ENCLOSES(&absr, newr)) + draw_beneath = FALSE; + current_expose_region = reg; + uiHideMouse(&absr); + region_traverse_rect(par, newr, ®_exp_CB, BOTTOM_TO_TOP, &draw_beneath); + uiShowMouse(&absr); + + return (OK); +} + +errtype region_expose(LGRegion *reg, LGRect *exp_rect) +{ + LGRegion *par; + LGRect newr; + region_convert_to_root(reg, &par, exp_rect, &newr); + if (region_in_sequence) + { + region_add_sequence_expose(reg, newr); + return(OK); + } + return(region_expose_absolute(reg, &newr)); +} + +errtype region_set_moving(LGRegion *reg, int val) +{ + LGRegion *curp; + + reg->moving = val; + curp = reg->sub_region; + while (curp != NULL) + { + region_set_moving(curp, val); + curp = curp->next_region; + } + return(OK); +} + +// Converts a rectangle from from_reg's frame of reference to that of one of it's children + +int region_convert_tochild(LGRegion *from_reg, LGRect *orig, LGRect *conv) +{ + LGPoint delta_pt; + int retval = 1; + + *conv = *orig; + + delta_pt.x = (from_reg->r)->ul.x * -1; + delta_pt.y = (from_reg->r)->ul.y * -1; + RectMove(conv,delta_pt); + // Spew(DSRC_UI_Conversion, ("c_conv = (%d, %d) - (%d, %d)\n", RECT_PRINT_ARGS(conv))); + return(retval); +} + +// Converts a rectangle from from_reg's frame of reference to that of it's parent + +int region_convert_toparent(LGRegion *from_reg, LGRect *orig, LGRect *conv) +{ + LGPoint delta_pt; + int retval = 1; + + *conv = *orig; + + delta_pt.x = (from_reg->parent->r)->ul.x; + delta_pt.y = (from_reg->parent->r)->ul.y; + RectMove(conv,delta_pt); + // Spew(DSRC_UI_Conversion, ("p_conv = (%d, %d) - (%d, %d)\n", RECT_PRINT_ARGS(conv))); + return(retval); +} + +// Converts a rectangle within a region to the absolute coords for that region, not relative +errtype region_abs_rect(LGRegion *reg, LGRect *orig_rect, LGRect *conv) +{ + conv->ul.x = orig_rect->ul.x; + conv->ul.y = orig_rect->ul.y; + conv->lr.x = orig_rect->lr.x; + conv->lr.y = orig_rect->lr.y; + if (reg->parent != NULL) + { + conv->ul.x += reg->parent->abs_x; + conv->ul.y += reg->parent->abs_y; + conv->lr.x += reg->parent->abs_x; + conv->lr.y += reg->parent->abs_y; + } + return((errtype)OK); +} + +uchar is_child(LGRegion *poss_parent, LGRegion *child) +{ + uchar retval = FALSE; + LGRegion *curp; + + if (child == poss_parent) + { + return(TRUE); + } + curp = poss_parent->sub_region; + while (curp != NULL) + { + retval = is_child(curp, child); + if (retval) + { + return(TRUE); + } + curp = curp->next_region; + } + return(FALSE); +} + +uchar ignore_children; + +uchar region_obscured_callback(LGRegion *reg, LGRect *r, void *data) +{ + int *ival; + LGRect ar1, ar2; + if (!region_found) + { + if (reg == obsc_region) + region_found = TRUE; + return(FALSE); + } + if (ignore_children) + { + if (is_child(obsc_region,reg)) + { + return(FALSE); + } + } + if (reg->moving) + return(FALSE); + ival = (int *)data; + region_abs_rect(reg, r, &ar1); + region_abs_rect(obsc_region, obsc_region->r, &ar2); + // Spew(DSRC_UI_Utilities, ("Obscured callback on %s obsc_region->r = (%d,%d)(%d,%d) r = (%d,%d)(%d,%d)\n",GD_NAME(reg), + // RECT_EXPAND_ARGS(&ar2), RECT_EXPAND_ARGS(&ar1))); + if (*ival != COMPLETELY_OBSCURED) + { + if (RECT_ENCLOSES(&ar1, &ar2)) + *ival = COMPLETELY_OBSCURED; + else + *ival = PARTIALLY_OBSCURED; + } + // Spew(DSRC_UI_Utilities, ("*ival = %d\n",*ival)); + return(FALSE); +} + +int region_obscured(LGRegion *reg, LGRect *obs_rect) +{ + int retval = UNOBSCURED; + LGRect newr; + LGRegion *rr; + + obsc_region = reg; + region_found = FALSE; + region_convert_to_root(reg, &rr, obs_rect, &newr); + ignore_children = FALSE; + if (reg != NULL) + region_traverse_rect(rr, &newr, ®ion_obscured_callback, BOTTOM_TO_TOP, &retval); + return(retval); +} + +int foreign_region_obscured(LGRegion *reg, LGRect *obs_rect) +{ + int retval = UNOBSCURED; + LGRect newr; + LGRegion *rr; + + obsc_region = reg; + region_found = FALSE; + region_convert_to_root(reg, &rr, obs_rect, &newr); + ignore_children = TRUE; + if (reg != NULL) + region_traverse_rect(rr, &newr, ®ion_obscured_callback, BOTTOM_TO_TOP, &retval); + return(retval); +} + +errtype region_begin_sequence() +{ + region_in_sequence += 1; + // Spew(DSRC_UI_Utilities, ("Beginning sequence...\n")); + return(OK); +} + +#define RSE_POOL_SIZE 40 +Region_Sequence_Element rse_pool[RSE_POOL_SIZE]; + +Region_Sequence_Element *get_rse_from_pool() +{ + int i = 0; + while ((i < RSE_POOL_SIZE) && (rse_pool[i].reg != NULL)) + i++; + if (i < RSE_POOL_SIZE) + return(&rse_pool[i]); + return(NULL); +} + +errtype return_rse_to_pool(Region_Sequence_Element *rse) +{ + int i; + for (i=0; i < RSE_POOL_SIZE; i++) + { + if (&rse_pool[i] == rse) + { + rse_pool[i].reg = NULL; + return(OK); + } + } + return(ERR_NOEFFECT); +} + +errtype init_rse_pool() +{ + int i; + for (i=0; i < RSE_POOL_SIZE; i++) + rse_pool[i].reg = NULL; + return(OK); +} + +errtype region_end_sequence(uchar replay) +{ + Region_Sequence_Element *pnode,*pnode_prior,*pnode_next; + + // Spew(DSRC_UI_Utilities, ("Ending sequence...\n")); + + region_in_sequence -= 1; + if (!region_in_sequence) + { + pnode = (Region_Sequence_Element *)slist_head(&sequence_header); + pnode_prior = (Region_Sequence_Element *)(&sequence_header); + while (pnode != NULL) + { + pnode_next = (Region_Sequence_Element *)slist_next(pnode); + if (replay) + region_expose_absolute(pnode->reg, &(pnode->exp_rect)); + slist_remove(pnode, pnode_prior); + return_rse_to_pool(pnode); +// pnode_prior = pnode; // unless deleted, then keep same + pnode = pnode_next; + } + } + return(OK); +} + +errtype region_add_sequence_expose(LGRegion *reg, LGRect exp_rect) +{ + Region_Sequence_Element *rse, *pnode; + uchar add_flag = TRUE; + rse = get_rse_from_pool(); + if (rse == NULL) + { + WARN("%s: No available RSE's in pool!", __FUNCTION__); + return(ERR_NOMEM); + } + rse->reg = reg; + rse->exp_rect = exp_rect; + forallinslist(Region_Sequence_Element, &sequence_header, pnode) + { +// if (add_flag && (pnode->reg == reg) && PointsEqual(pnode->exp_rect.ul,exp_rect.ul) +// && PointsEqual(pnode->exp_rect.lr, exp_rect.lr)) + + if (add_flag && (pnode->reg == reg) && RECT_ENCLOSES(&(exp_rect),&(pnode->exp_rect))) + add_flag = FALSE; + } + if (add_flag) + { + // Spew(DSRC_UI_Utilities, ("Adding RSE for %s (%d,%d)\n",GD_NAME(reg),RECT_EXPAND_ARGS(&exp_rect))); + slist_add_head(&sequence_header, rse); + } + else + { + return_rse_to_pool(rse); + // Spew(DSRC_UI_Utilities, ("RSE not added -- duplicate!\n")); + } + return(OK); +} + +errtype region_convert_to_root(LGRegion *reg, LGRegion **root_reg, LGRect *rect, LGRect *conv) +{ + LGRect oldr,newr; + + oldr = *rect; + if (reg->parent != NULL) + region_convert_toparent(reg, &oldr, &newr); + else + newr = oldr; + *root_reg = reg; + while ((*root_reg)->parent != NULL) + { + region_convert_toparent(*root_reg, &oldr, &newr); + *root_reg = (*root_reg)->parent; + oldr = newr; + } + *conv = newr; + return(OK); +} + +#ifdef UI_LINKED +errtype region_manage_place(LGRegion *reg) +{ + LGRegion *dummy; + dummy = reg; + + return(OK); +} + +errtype region_manage_remove(LGRegion *reg) +{ + LGRegion *dummy; + dummy = reg; + + return(OK); +} + +errtype region_set_invisible(LGRegion* reg, uchar invis) +{ + if (invis) + reg->status_flags |= INVISIBLE_FLAG; + else + reg->status_flags &= ~INVISIBLE_FLAG; + return OK; +} + +errtype region_get_invisible(LGRegion* reg, uchar* invis) +{ + *invis = (reg->status_flags & INVISIBLE_FLAG) != 0; + return OK; +} + +#endif // UI_LINKED + diff --git a/engine/src/Libraries/UI/Source/region.h b/engine/src/Libraries/UI/Source/region.h new file mode 100644 index 0000000..40492c2 --- /dev/null +++ b/engine/src/Libraries/UI/Source/region.h @@ -0,0 +1,186 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// Header file for the Region library + +#ifndef __REGION_H +#define __REGION_H + +#include "lg.h" +#include "rect.h" +#include "error.h" + +#define INVISIBLE_FLAG 0x80000000 + + +#define EVENT_CB 0x0001UL +#define EXPOSE_CB 0x0002UL +#define SAVEUNDER_CB 0x0004UL +#define REPLACE_CB 0x0008UL + +#define AUTOMANAGE_FLAG 0x0100 +#define DISPLAY_ON_CREATION 0x0200 +#define STENCIL_CLIPPING 0x0400 +#define OBSCURATION_CHECK 0x0800 +#define AUTODESTROY_FLAG 0x1000 + +#define UI_LINKED 1 + +typedef struct _Region +{ + int abs_x, abs_y; // upper left in absolute coords + LGRect *r; // rectangle covered by this region, in coord frame of parent. + int z; // z-coordinate to determine stacking + int moving; + uchar (*expose)(struct _Region *reg, LGRect *r); // function to draw a given rectangle + uchar (*save_under)(struct _Region *reg, LGRect *r); // function to save under a given rectangle + uchar (*replace)(struct _Region *reg, LGRect *r); + ulong status_flags; + int device_type; + void *handler; + void *cursors; + void *user_data; // user-provided region information for callback use + int event_order; + struct _Region *sub_region; // Head of children regions + struct _Region *next_region; // next region at same level + struct _Region *parent; // parent of this region + LGRect real_rect; +} LGRegion; + +typedef uchar (*RectCallback)(LGRegion *reg, LGRect *r); // all in relative coords +typedef uchar (*TravRectCallback)(LGRegion *reg, LGRect *r, void *data); +typedef uchar (*TravCallback)(LGRegion *reg, void *data); + + +// If the callback returns non-zero, then it indicates that the callback triggering +// should not propagate further downwards. Otherwise, the callback will go +// further, with parents getting callbacks before their children. + +// Initialize the region system. Note that this gets called automatically +// the first time you try to create a region, if you haven't done so already. +errtype region_init(); + +// Register a region with the UI manager, geometry described by r, and as a +// subregion of the parent region. When the passed +// events are not filtered out by emask, and callbacks are allowed by +// cb mask, callback will be called. cbmask can be used to prevent +// any of expose, save_under, or event callbacks from happening. +// Returns a pointer to the newly-defined region. Depending +// on the value of the z coordinate, the new region will be +// "above" or "below" other overlapping regions. Any covered region +// will be given a saveunder callback if it's mask allows. Whether or +// not the parent handles the events before it's children is dependant +// on the event_order parameter. The user_data parameter is simply +// stored and can be used by callbacks to determine information about +// what is contained in the region. The status parameter determines the degree of +// control that the region system has over the newly created region, as well as +// defining the callback mask. + +// Note that when creating the root region, you must set the device_type by hand -- this +// will then be inherited by each subregion as they are created. + +errtype region_create(LGRegion *parent, LGRegion *ret, LGRect *r, int z, int event_order, + ulong status, RectCallback expose, + RectCallback save_under, RectCallback replace, void *user_data); + +#define REG_USER_CONTROLLED EVENT_CB | EXPOSE_CB | SAVEUNDER_CB | REPLACE_CB +#define REG_NORMAL EVENT_CB | EXPOSE_CB | SAVEUNDER_CB | REPLACE_CB | AUTOMANAGE_FLAG | DISPLAY_ON_CREATION +#define REG_AUTOMATIC AUTOMANAGE_FLAG | EXPOSE_CB | EVENT_CB | DISPLAY_ON_CREATION +#define REG_TRANSPARENT EVENT_CB // Dunno whether this will actually work... +#define REG_NONEXISTANT 0x0000 + +// Removes a region from the system, along with all it's children. +// Returns whether or not the operation was successful. Any newly exposed +// areas will recieve expose callbacks if their masks allow. + +errtype region_destroy(LGRegion *reg, uchar draw); + +// Move a region to a new set of coordinates. Expose and saveunder +// callbacks are dished out for the original area and any newly covered +// area. As usual, these coords are relative.... + +errtype region_move(LGRegion *reg, int new_x, int new_y, int new_z); + +// Change the size of a region. Appropriate callbacks are +// triggered if the regions masks allow. + +errtype region_resize(LGRegion *reg, int new_x_size, int new_y_size); + +errtype region_expose(LGRegion *reg, LGRect *exp_rect); + +// Traverse the region stack by calling fn on every Region that is +// intersected by the target rectangle. The order parameter determines +// whether traversal is front to back or back to front. If the callback +// function ever returns a non-zero value, the traversal stops. Returns +// true if non-zero value was returned during traversal. + +int region_traverse_rect(LGRegion *reg, LGRect *target, TravRectCallback fn, int order, void *data); +int region_traverse_point(LGRegion *reg, LGPoint target, TravRectCallback fn, int order, void *data); +int region_traverse(LGRegion *reg, TravCallback fn, int order, void *data); + +// Converts a rectangle from a given region's coordinate system to the frame of a child of that +// coordinate system. +int region_convert_rect(LGRegion *from_reg, LGRect *conv); + +// Converts a rectangle within a region to the absolute coords for that region, not relative +errtype region_abs_rect(LGRegion *reg, LGRect *orig_rect, LGRect *conv); + +// Converts a rectangle within a region to it's root coordinates, as well as return a pointer +// to that root region. +errtype region_convert_to_root(LGRegion *reg, LGRegion **root_reg, LGRect *rect, LGRect *conv); + +// Returns whether or not a particular rectangle within a region is obscured by anything or +// not. The coordinates of the rectangle are local coords. region_foreign_obscured is +// like region_obscured but ignores children for purposes of obscuration. +int region_obscured(LGRegion *reg, LGRect *obs_rect); +int foreign_region_obscured(LGRegion *reg, LGRect *obs_rect); + +// These functions control whether or not the region library thinks the application is +// in the middle of a sequence which will generate multiple, probably duplicate, expose events. +// While a sequence is active, it captures all the expose events, and saves them until the +// sequence has ended, a which point it lets the exposes get through, after filtering out +// all the duplicate exposes. +errtype region_begin_sequence(); +errtype region_end_sequence(uchar replay); + + +// An _invisible_ region is not detected through any kind of traversal, does not +// receive mouse events and does not change the cursor. + +errtype region_set_invisible(LGRegion* reg, uchar invis); +// Sets whether or not a region is invisible + +errtype region_get_invisible(LGRegion* reg, uchar* invis); +// determines whether a region is currently invisible. + +#define UNOBSCURED 0 +#define PARTIALLY_OBSCURED 1 +#define COMPLETELY_OBSCURED 2 + +#define TOP_TO_BOTTOM 0 +#define BOTTOM_TO_TOP 1 + +#define RECT_EXPAND_ARGS(pr) (pr)->ul.x,(pr)->ul.y,(pr)->lr.x,(pr)->lr.y +#define RECT_PRINT_ARGS(pr) RECT_EXPAND_ARGS(pr) +#define RECT_MULTIPLY(rc,factor) { (rc)->ul.x = (rc)->ul.x * (factor); \ + (rc)->ul.y = (rc)->ul.y * (factor); (rc)->lr.x = (rc)->lr.x * (factor); (rc)->lr.y = (rc)->lr.y * (factor); } +#define POINT_MULTIPLY(pt,factor) (pt).x = (pt).x * factor; (pt).y = (pt).y * (factor) +#define SCALE_RECT(rc, scale_pt) { (rc)->ul.x = (rc)->ul.x * (scale_pt).x; (rc)->lr.x = (rc)->lr.x * (scale_pt).x ;\ + (rc)->ul.y = (rc)->ul.y * (scale_pt).y; (rc)->lr.y = (rc)->lr.y * (scale_pt).y; } + +#endif // __REGION_H diff --git a/engine/src/Libraries/UI/Source/resgadg.h b/engine/src/Libraries/UI/Source/resgadg.h new file mode 100644 index 0000000..4b3ffff --- /dev/null +++ b/engine/src/Libraries/UI/Source/resgadg.h @@ -0,0 +1,40 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* This file created by RESTOOL */ + +#define RES_gadgetGfx 500 // (0x1f4) +#define REF_IMG_bmGadgetBackground 0x1f40000 +#define REF_IMG_bmCursorMid 0x1f40001 +#define REF_IMG_bmCursorUp 0x1f40002 +#define REF_IMG_bmCursorDown 0x1f40003 +#define REF_IMG_bmCursorLeft 0x1f40004 +#define REF_IMG_bmCursorRight 0x1f40005 +#define REF_IMG_bmCursorRotLeft 0x1f40006 +#define REF_IMG_bmCursorRotRight 0x1f40007 +#define REF_IMG_bmCursorUpLeft 0x1f40008 +#define REF_IMG_bmCursorUpRight 0x1f40009 +#define REF_IMG_bmCursorDownLeft 0x1f4000a +#define REF_IMG_bmCursorDownRight 0x1f4000b +#define REF_IMG_bmFriendPBA 0x1f4000c +#define REF_IMG_bmEnemyPBA 0x1f4000d +#define REF_IMG_bmDeadPBA 0x1f4000e +#define REF_IMG_bmGuardTower 0x1f4000f +#define REF_IMG_bmHelmBigHead 0x1f40010 +#define REF_IMG_bmHelmMiniHead 0x1f40011 +#define REF_IMG_bmHelmTinyHead 0x1f40012 diff --git a/engine/src/Libraries/UI/Source/slab.c b/engine/src/Libraries/UI/Source/slab.c new file mode 100644 index 0000000..47ab34d --- /dev/null +++ b/engine/src/Libraries/UI/Source/slab.c @@ -0,0 +1,102 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include "lg.h" +#include "slab.h" + +/* + * $Source: r:/prj/lib/src/ui/RCS/slab.c $ + * $Revision: 1.4 $ + * $Author: mahk $ + * $Date: 1994/08/24 08:55:51 $ + * + * $Log: slab.c $ + * Revision 1.4 1994/08/24 08:55:51 mahk + * Cursor stacks and invisible regions. + * + * Revision 1.3 1993/10/11 20:26:47 dc + * Angle is fun, fun fun fun + * + * Revision 1.2 1993/04/28 14:40:01 mahk + * Preparing for second exodus + * + * Revision 1.1 1993/04/05 23:40:58 mahk + * Initial revision + * + * + */ + + + +// ------------------- +// Defines and Globals +// ------------------- +errtype ui_init_slabs(void); + +uiSlab* uiCurrentSlab = NULL; + +// --------- +// INTERNALS +// --------- + +errtype ui_init_slabs(void) +{ + uiCurrentSlab = NULL; + return OK; +} + + +// ------------- +// API FUNCTIONS +// ------------- + +errtype uiMakeSlab(uiSlab* slab, LGRegion* cursor_reg, LGCursor* default_cursor) +{ + errtype err; + extern errtype ui_init_focus_chain(uiSlab* slab); + extern errtype ui_init_cursor_stack(uiSlab* slab, LGCursor* default_cursor); + + slab->creg = cursor_reg; + err = ui_init_focus_chain(slab); + if (err != OK) return err; + err = ui_init_cursor_stack(slab,default_cursor); + if (err != OK) return err; + return OK; +} + +errtype uiDestroySlab(uiSlab* slab) +{ + slab->creg = NULL; + uiDestroyCursorStack(&slab->cstack); + array_destroy(&slab->fchain.chain); + return OK; +} + +errtype uiSetCurrentSlab(uiSlab* slab) +{ + uiCurrentSlab = slab; + return OK; +} + +errtype uiGetCurrentSlab(uiSlab** slab) +{ + *slab = uiCurrentSlab; + return OK; +} + + diff --git a/engine/src/Libraries/UI/Source/slab.h b/engine/src/Libraries/UI/Source/slab.h new file mode 100644 index 0000000..27547b9 --- /dev/null +++ b/engine/src/Libraries/UI/Source/slab.h @@ -0,0 +1,94 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __SLAB_H +#define __SLAB_H + +/* + * $Source: n:/project/lib/src/ui/RCS/slab.h $ + * $Revision: 1.3 $ + * $Author: dc $ + * $Date: 1993/10/11 20:27:32 $ + * + * $Log: slab.h $ + * Revision 1.3 1993/10/11 20:27:32 dc + * Angle is fun, fun fun fun + * + * Revision 1.2 1993/04/28 14:40:21 mahk + * Preparing for second exodus + * + * Revision 1.1 1993/04/05 23:43:26 mahk + * Initial revision + * + * + */ + +// A slab is a collection of information about where to send input +// events, and where to look for mouse cursors. Every slab comes complete with: +// 1) a root cursor region, which will be traversed upon to +// find regional mouse cursors. +// 2) A default cursor stack. If the mouse is in a region +// which doesn't specify a mouse cursor, the top of the slab's cursor stack +// is used instead. +// 3) A focus chain. This determines which regions have input focus. + +// Only one slab is active at one time, and that slabbed is looked at +// by uiPoll. + + + +// Includes +#include "lg.h" // every file should have this +#include "error.h" +#include "array.h" +#include "region.h" +#include "cursors.h" + +// Defines + +/*typedef struct _ui_slab +{ + LGRegion* creg; // cursor region. + struct _focus_chain + { + Array chain; + int curfocus; + } fchain; // focus chain + cursor_stack cstack; +} uiSlab;*/ + + +// Prototypes + +errtype uiMakeSlab(uiSlab* slab,LGRegion* cursor_reg, LGCursor* default_cursor); +// Initialize a region with the specified cursor region, default cursor. +// the initial focus is usually the root region. + +errtype uiSetCurrentSlab(uiSlab* slab); +// Sets the current active slab. + +errtype uiGetCurrentSlab(uiSlab** slab); +// Gets the current active slab; + +errtype uiDestroySlab(uiSlab* slab); +// shuts down a slab, freeing any satellite data. + + +// Globals + +#endif // __SLAB_H diff --git a/engine/src/Libraries/UI/Source/vmouse.c b/engine/src/Libraries/UI/Source/vmouse.c new file mode 100644 index 0000000..b8acf97 --- /dev/null +++ b/engine/src/Libraries/UI/Source/vmouse.c @@ -0,0 +1,66 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#include "lg.h" +#include "error.h" +#include "mouse.h" +#include "vmouse.h" +//#include + +void (*ui_mouse_convert)(short *px, short *py, uchar down) = NULL; +void (*ui_mouse_convert_round)(short *px, short *py, uchar down) = NULL; + +errtype ui_mouse_do_conversion(short *pmx, short *pmy, uchar down) +{ + if (ui_mouse_convert != NULL) + ui_mouse_convert(pmx,pmy,down); + return(OK); +} + +errtype ui_mouse_get_xy(short *pmx, short *pmy) +{ + errtype retval; + retval = mouse_get_xy(pmx,pmy); + ui_mouse_do_conversion(pmx,pmy,TRUE); + return(retval); +} + +errtype ui_mouse_put_xy(short pmx, short pmy) +{ + errtype retval; + ui_mouse_do_conversion(&pmx,&pmy,FALSE); + retval = mouse_put_xy(pmx,pmy); + return(retval); +} + +errtype ui_mouse_constrain_xy(short xl, short yl, short xh, short yh) +{ + if (ui_mouse_convert == NULL) + return(mouse_constrain_xy(xl,yl,xh,yh)); + else + { + short uxl,uyl,uxh,uyh; + uxl=xl; + uyl=yl; + uxh=xh; + uyh=yh; + ui_mouse_convert_round(&uxl,&uyl,FALSE); + ui_mouse_convert_round(&uxh,&uyh,FALSE); + return(mouse_constrain_xy(uxl,uyl,uxh,uyh)); + } +} diff --git a/engine/src/Libraries/UI/Source/vmouse.h b/engine/src/Libraries/UI/Source/vmouse.h new file mode 100644 index 0000000..5069692 --- /dev/null +++ b/engine/src/Libraries/UI/Source/vmouse.h @@ -0,0 +1,23 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// virtualized mouse support +extern errtype ui_mouse_get_xy(short *pmx, short *pmy); +extern errtype ui_mouse_put_xy(short pmx, short pmy); +extern errtype ui_mouse_constrain_xy(short xl, short yl, short xh, short yh); +extern errtype ui_mouse_do_conversion(short *pmx, short *pmy, uchar down); diff --git a/engine/src/Libraries/VOX/Source/vox.h b/engine/src/Libraries/VOX/Source/vox.h new file mode 100644 index 0000000..f823157 --- /dev/null +++ b/engine/src/Libraries/VOX/Source/vox.h @@ -0,0 +1,60 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +#ifndef __VOX_H +#define __VOX_H + +#include "2d.h" + +// Voxel structure +typedef struct { + fix pix_dist; // space between pixels in 3d units + fix pix_size; // size of pixels in 3d units + int w,h,d; // size of voxel in source pixels + // any pointer set to Null is not rendered + grs_bitmap *col; // Pointers to color maps + grs_bitmap *ht; // Pointers to height maps +} vxs_vox; + + +// Startup for the voxel system, it needs to allocate +// space for the incremental multiplication tables +// pass it the maximum pixel dimension of any of the +// voxel objects you anticipate drawing +// returns TRUE for success, FALSE if unable to allocate +uchar vx_init(int max_depth); + +// Close the voxel system +void vx_close(); + +// Initialize a voxel +// Pass it pointer to the voxel structure +// 3d distance between the pixels in the bitmaps +// 2d size of each pixel at a distance of 1 3d unit +// pointer to colormap +// pointer to height map +void vx_init_vox(vxs_vox *v,fix pix_dist,fix pix_size,int depth,grs_bitmap *col,grs_bitmap *ht); + +// Render voxel object v, after calling g3_start_object +// don't forget to call g3_end_object afterwards +void vx_render(vxs_vox *v); + + +#endif /* !__VOX_H */ + + diff --git a/engine/src/Libraries/VOX/Source/vox2d.c b/engine/src/Libraries/VOX/Source/vox2d.c new file mode 100644 index 0000000..e4ec1f9 --- /dev/null +++ b/engine/src/Libraries/VOX/Source/vox2d.c @@ -0,0 +1,223 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/vox/RCS/vox2d.c $ + * $Revision: 1.6 $ + * $Author: jaemz $ + * $Date: 1994/08/16 05:45:28 $ + * + * 2d Interface to the voxel library + * This file is part of the vox library + * + * $Log: vox2d.c $ + * Revision 1.6 1994/08/16 05:45:28 jaemz + * Found fix overflow problem and fixed it + * + * Revision 1.5 1994/08/11 22:52:55 jaemz + * Made fill mode work and put pix_size in real 3d points. + * + * Revision 1.4 1994/07/15 21:13:12 jaemz + * Added self clipping + * + * Revision 1.3 1994/05/25 23:14:53 jaemz + * Added more debug checking for out of bounds squares + * + * Revision 1.2 1994/04/21 12:00:24 jaemz + * Added bounds checking to the debug version + * + * Revision 1.1 1994/04/21 10:52:33 jaemz + * Initial revision + * + */ + +#include "2d.h" +//#include +#include "vox.h" + +// static area for multiplication tables + +extern fix *zdxdz; +extern fix *zdydz; + +// maximum depth allocated for mult tables +// used for bounds checking in debug version +#ifdef DBG_ON +extern int vxd_maxd; +#endif + +void vmap_dot(fix x0, fix y0, fix dxdu, fix dydu, fix dxdv, fix dydv, fix dxdz, fix dydz, + int near_ver,vxs_vox *vx,int dotw,int doth,uchar clip); + +// We calculate fdxdu (where uv goes across bitmap) +// and xy goes across screen +// x,y seem to be nearest vertex coordinates +// dz is the amount +void vmap_dot(fix x0, fix y0, fix dxdu, fix dydu, fix dxdv, fix dydv, fix dxdz, fix dydz, + int near_ver,vxs_vox *vx,int dotw,int doth,uchar clip) +{ + int du,dv,initu,endu,initv,endv; + int i,j; + int c; + int crow,hrow; + int dcrow,dhrow; + ubyte *crow2,*hrow2; + long z; + int far_ver; + grs_bitmap *col; + grs_bitmap *ht; + int (* rect) (short x1,short y1,short x2,short y2); + fix xp,yp; + fix oldcurx; + fix oldcury; + fix curx,cury; + #ifdef DBG_ON + short xl,yt,xr,yb; + #endif + + col = vx->col; + ht = vx->ht; + + if (clip) + rect = gr_rect; + else + rect = (int (*)(short, short, short, short)) gr_urect; + + far_ver = (near_ver+2)%4; + + //How to scan given near_ver, and this should be actually computed some day + if (near_ver == 0 || near_ver == 3) { + initu = col->w -1; + du = -1; + endu = -1; + } + else { + initu = 0; + du = 1; + endu = col->w; + } + if (near_ver < 2) { + initv = col->h-1; + dv = -1; + endv = -1; + } + else { + initv = 0; + dv = 1; + endv = col->h; + } + + oldcurx = curx = x0 + dxdu*initu + dxdv*initv; + oldcury = cury = y0 + dydu*initu + dydv*initv; + + dxdu *= du; + dydu *= du; + dxdv *= dv; + dydv *= dv; + + // fill tables with values so we don't have to multiply in the inner loop + xp = 0; + yp = 0; + + #ifdef DBG_ON + if (vx->d > vxd_maxd) { + mprintf("voxel object depth z=%d\n",vx->d); + mprintf("greater than max = %d\n",vxd_maxd); + mprintf("voxel at %ld\n",vx); + mprintf("color map at %ld htmap at %ld\n",col,ht); + return; + } + #endif + + for (i=0;id;++i) { + zdxdz[i] = xp; + zdydz[i] = yp; + xp += dxdz; + yp += dydz; + } + + dcrow = dv*col->row; + dhrow = dv*ht->row; + + crow = initv*col->row; + hrow = initv*ht->row; + + for(j=initv;j!=endv;j+=dv) { + crow2 = col->bits + crow + initu; + hrow2 = ht->bits + hrow + initu; + + for(i=initu;i!=endu;i+=du) { + c = *crow2; + if (c != 0) { + z = *hrow2; + + #ifdef DBG_ON + if ((z>=vxd_maxd) || (z<0) ) { + mprintf("voxel object depth (%d,%d)=%d\n",i,j,(char)z); + mprintf("out of bounds [0,%d]\n",vxd_maxd); + mprintf("color map at %ld htmap at %ld\n\n",col,ht); + return; + } + #endif + + xp = (curx + zdxdz[z])>>16; + yp = (cury + zdydz[z])>>16; + + #ifdef DBG_ON + if (!clip) { + xl = xp; + xr = xp+dotw; + yt = yp; + yb = yp+doth; + if (gr_clip_rect(&xl,&yt,&xr,&yb) != 0) { + mprintf("vox: Thair's a rectungle oot uf elaignment, lahd.\n"); + mprintf("vox: best tell Jaeeemz, thut ruscal!\n"); + exit(1); + return; + } + } + #endif + + // call the box routine only if its bigger than a point + // until gr_point supports fill modes, do boxes always. + if (doth>1) { + gr_set_fcolor(c); + rect(xp,yp,xp+dotw,yp+doth); + } else { + gr_set_fcolor(c); + if (clip) gr_point(xp,yp); + else gr_upoint(xp,yp); + //*(grd_bm.bits + yp*grd_bm.row + xp) = c; + } + + } + curx += dxdu; + cury += dydu; + + crow2 += du; + hrow2 += du; + } + curx = (oldcurx += dxdv); + cury = (oldcury += dydv); + + crow += dcrow; + hrow += dhrow; + } +} + + diff --git a/engine/src/Libraries/VOX/Source/vox3d.c b/engine/src/Libraries/VOX/Source/vox3d.c new file mode 100644 index 0000000..18c8a47 --- /dev/null +++ b/engine/src/Libraries/VOX/Source/vox3d.c @@ -0,0 +1,284 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: r:/prj/lib/src/vox/RCS/vox3d.c $ + * $Revision: 1.5 $ + * $Author: jaemz $ + * $Date: 1994/08/16 05:45:42 $ + * + * 3d Interface to the voxel library + * This file is part of the vox library + * + * $Log: vox3d.c $ + * Revision 1.5 1994/08/16 05:45:42 jaemz + * Found fix overflow problem and fixed it + * + * Revision 1.4 1994/08/11 22:53:20 jaemz + * Made fill mode work and put pix_size in real 3d points. + * + * Revision 1.3 1994/07/15 21:13:20 jaemz + * Added self clipping + * + * Revision 1.2 1994/04/21 12:00:36 jaemz + * Changed interface to vox2d to facilitate bounds checking in + * the debug version. + * + * Revision 1.1 1994/04/21 10:52:42 jaemz + * Initial revision + * + */ + + +#include +#include "vox.h" +#include "fix.h" +#include "3d.h" +//#include + +//void vmap_rgbg(fix x[4],fix y[4],fix dz[3],int near_ver,grs_bitmap *col,grs_bitmap *ht); +//void vmap_poly(fix x[4],fix y[4],fix dz[3],int near_ver,grs_bitmap *col,grs_bitmap *ht); +void vmap_dot(fix x0, fix y0, fix dxdu, fix dydu, fix dxdv, fix dydv, fix dxdz, fix dydz, int near_ver,vxs_vox *vx,int dotw,int doth,uchar clip); + +// The four vertices of every face +//static int faces[6][4] = { {0,1,5,4},{1,2,6,5},{2,3,7,6} +// ,{3,0,4,7},{3,2,1,0},{4,5,6,7}}; + +// The dz direction of every face +//static int dzs[6][3] = { {0,0,1},{-1,0,0},{0,0,-1},{1,0,0},{0,1,0},{0,-1,0} }; + +// The coordinates of every vertex +// static int ver[8][3] = { {-1,-1,-1},{1,-1,-1},{1,-1,1},{-1,-1,1} +// ,{-1,1,-1},{1,1,-1},{1,1,1},{-1,1,1} }; + +// gives which visible faces for dominant vertex +// static int corners[8][3] = { {0,3,4}, {0,1,4}, {1,2,4}, {2,3,4} +// ,{3,0,5}, {0,1,5}, {1,2,5}, {2,3,5} }; + + +//#define CIRCLES +//#define BMAPS +//#define BBOX +//#define STATS + +void vx_render(vxs_vox *vx) +{ + fix a,b; + //int f; + int near_ver; //which vertex is nearest + g3s_vector p[4]; + g3s_phandle tmp[4]; + int i; + int psx,psy; + fix maxdx; + fix maxdy; + uchar clip; + fix tx,ty,tz; + fix min_z; + + + // static int f=0; + + #ifdef BBOX + // for debugging + fix x[4],y[4]; + #endif + + // spot to render on screen + p[0].gX = p[0].gY = p[0].gZ = 0; + // dx's and dy's and stuff + p[1].gX = vx->pix_dist; p[1].gY = 0; p[1].gZ = 0; + p[2].gX = 0; p[2].gY = vx->pix_dist; p[2].gZ = 0; + p[3].gX = 0; p[3].gY = 0; p[3].gZ = vx->pix_dist; + + g3_alloc_list(4,tmp); + g3_transform_list(4,tmp,p); + + // assuming face zero, all relative to p[0] + // tmp[1] contains dxdu dydu + // tmp[2] contains dxdv dydv + // tmp[3] contains dxdz dydz + + // return if it's behind you + if (tmp[0]->gZ < 0) { + g3_free_list(4,tmp); + return; + } + + // find max and min x and y + // note that maxdx and maxdy are in fix + //mprintf("tmp[0]->sx,sy = %x %x\n",tmp[0]->sx,tmp[0]->sy); + // mprintf("tmp[1]->sx,sy = %x %x\n",tmp[1]->sx,tmp[1]->sy); + //mprintf("tmp[2]->sx,sy = %x %x\n",tmp[2]->sx,tmp[2]->sy); + //mprintf("tmp[3]->sx,sy = %x %x\n",tmp[3]->sx,tmp[3]->sy); + + // turn vectors into deltas + for (i=1;i<4;++i) { + tmp[i]->sx -= tmp[0]->sx; + tmp[i]->sy -= tmp[0]->sy; + } + + a = tmp[0]->sx - ((vx->w*tmp[1]->sx+vx->h*tmp[2]->sx+vx->d*tmp[3]->sx)>>1); + b = tmp[0]->sy - ((vx->w*tmp[1]->sy+vx->h*tmp[2]->sy+vx->d*tmp[3]->sy)>>1); + +#ifdef CIRCLES + // Top line from vertex 0 to 1 just for debugging + gr_set_fcolor(255); + gr_int_disk(fix_rint(tmp[0]->sx),fix_rint(tmp[0]->sy),5); +#endif + +#ifdef BMAPS + gr_bitmap(vx->col,fix_rint(tmp[0]->sx),fix_rint(tmp[0]->sy)); + gr_bitmap(vx->ht,fix_rint(tmp[0]->sx)-vx->w,fix_rint(tmp[0]->sy)-vx->h); +#endif + + //calculate pixel size + psy = fix_div(grd_bm.w * vx->pix_size,tmp[0]->gZ)>>1; + psx = fix_mul(psy,grd_cap->aspect); + +// mprintf("psx = %x psy = %x\n",psx,psy); + + // make them at least one pixel + if (psyw*fix_abs(tmp[1]->sx) + vx->h*fix_abs(tmp[2]->sx) + vx->d*fix_abs(tmp[3]->sx)); + maxdy = (psy<<1) + (vx->w*fix_abs(tmp[1]->sy) + vx->h*fix_abs(tmp[2]->sy) + vx->d*fix_abs(tmp[3]->sy)); + //mprintf("maxdx = %x maxdy = %x\n",maxdx,maxdy); + + // these can overflow and become negative in extreme situations causing it not to clip + if ((maxdy < 0) || (maxdx < 0)) { + g3_free_list(4,tmp); + return; + } + + maxdx = maxdx >> 1; + maxdy = maxdy >> 1; + + tx = fix_abs(tmp[0]->gX); + ty = fix_abs(tmp[0]->gY); + tz = tmp[0]->gZ; + + // clip if it SEEMS to be out of bounds, or if psx is bigger than 10. Kind of a hack to compensate + // for weirdo fixed point saturation. + clip = ((tmp[0]->sx - maxdx) < 0) || ((tmp[0]->sx + maxdx) > fix_make(grd_bm.w,0)) || + ((tmp[0]->sy - maxdy) < 0) || ((tmp[0]->sy + maxdy) > fix_make(grd_bm.h,0)) || (psx > fix_make(10,0)); + + #ifdef STATS + mprintf("vx: tx = %g ty = %g tz = %g\n",(float)tx/65536.0,(float)ty/65536.0,(float)tz/65536.0); + mprintf("pd = %g vx->w = %d vx->h %d\n",(float)vx->pix_dist/65536.0,vx->w,vx->h); + mprintf("c = %d tx/tz = %g ty/tz = %g\n",clip,(float)fix_div(tx,tz)/65536.0,(float)fix_div(ty,tz)/65536.0); + mprintf("minus tx/tz = %g ty/tz = %g\n",(float)fix_div(tx- vx->pix_dist * vx->w,tz)/65536.0, + (float)fix_div(ty- vx->pix_dist * vx->h,tz)/65536.0); + #endif + + if ( (tx-(vx->pix_dist * vx->w) > tz ) || (ty-(vx->pix_dist * vx->h) > tz)) { + #ifdef BBOX + mprintf("vox: punting due to out of view cone\n"); + #endif + g3_free_list(4,tmp); + return; + } + +#ifdef BBOX + // different color when clipping + gr_set_fcolor(0x4c+clip*0x10); + x[0] = a; + y[0] = b; + x[1] = a+(vx->w)*(tmp[1]->sx); + y[1] = b+(vx->w)*(tmp[1]->sy); + x[2] = x[1]+(vx->h)*(tmp[2]->sx); + y[2] = y[1]+(vx->h)*(tmp[2]->sy); + x[3] = a+(vx->h)*(tmp[2]->sx); + y[3] = b+(vx->h)*(tmp[2]->sy); + + gr_fix_line(x[0],y[0],x[1],y[1]); + gr_fix_line(x[1],y[1],x[2],y[2]); + gr_fix_line(x[2],y[2],x[3],y[3]); + gr_fix_line(x[3],y[3],x[0],y[0]); + + for (i=0;i<4;++i) { + x[i] += (vx->d)*(tmp[3]->sx); + y[i] += (vx->d)*(tmp[3]->sy); + } + + gr_fix_line(x[0],y[0],x[1],y[1]); + gr_fix_line(x[1],y[1],x[2],y[2]); + gr_fix_line(x[2],y[2],x[3],y[3]); + gr_fix_line(x[3],y[3],x[0],y[0]); +#endif + + //mprintf("f = %d clip = %d\n",f++,clip); + + // if (f==97) + // mprintf("uh oh\n"); + + // x[0] = tmp[0]->sx + maxdx; + // x[1] = tmp[0]->sx - maxdx; + // y[0] = tmp[0]->sy + maxdy; + // y[1] = tmp[0]->sy - maxdy; + // gr_fix_line(x[0],y[0],x[1],y[0]); + // gr_fix_line(x[1],y[0],x[1],y[1]); + // gr_fix_line(x[1],y[1],x[0],y[1]); + // gr_fix_line(x[0],y[1],x[0],y[0]); + + // find near vertex + near_ver = 0; + min_z = tmp[0]->gZ; + + // check for vertex 1 + if (tmp[1]->gZ < min_z) { + near_ver = 1; + min_z = tmp[1]->gZ; + } + if (tmp[1]->gZ + tmp[2]->gZ < min_z) { + near_ver = 2; + min_z = tmp[1]->gZ + tmp[2]->gZ; + } + if (tmp[2]->gZ < min_z) { + near_ver = 3; + min_z = tmp[2]->gZ; + } + + //mprintf("near_ver = %d\n",near_ver); + + // calculate face 1 + // for(i=0;i<1;++i) { + // // defines which faces visible at that vertex + // f = corners[near_ver][i]; + // f = 0; + // for(j=0;j<4;++j) { + // // faces gives vertices at each face + // x[j] = a+v[faces[f][j]] [0]; + // y[j] = b+v[faces[f][j]] [1]; + // } + + // find out which one of the vertices is the near_ver + // faces looks for all the vertices there + //for(j=0;j<4;++j) { + // if (near_ver == faces[f][j]) break; + //} + // f= 0; + + //mprintf("clip = %d\n",clip); + //mprintf("tmp[0]->gZ = %x\n",tmp[0]->gZ); + + vmap_dot(a,b,tmp[1]->sx,tmp[1]->sy,tmp[2]->sx,tmp[2]->sy,tmp[3]->sx,tmp[3]->sy,near_ver,vx,fix_rint(psx),fix_rint(psy),clip); + + g3_free_list(4,tmp); +} diff --git a/engine/src/Libraries/VOX/Source/voxinit.c b/engine/src/Libraries/VOX/Source/voxinit.c new file mode 100644 index 0000000..dcf0878 --- /dev/null +++ b/engine/src/Libraries/VOX/Source/voxinit.c @@ -0,0 +1,87 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +/* + * $Source: n:/project/lib/src/vox/RCS/voxinit.c $ + * $Revision: 1.2 $ + * $Author: jaemz $ + * $Date: 1994/04/21 12:00:59 $ + * + * Voxel initialization routines + * This file is part of the vox library + * + * $Log: voxinit.c $ + * Revision 1.2 1994/04/21 12:00:59 jaemz + * Added vxd_maxd to facilitate bounds checking in debug version + * + * Revision 1.1 1994/04/21 10:52:54 jaemz + * Initial revision + * + */ + +#include +#include "lg.h" +#include "vox.h" + +// pointers to arrays for multiplication tables +fix *zdxdz; +fix *zdydz; + +#ifdef DBG_ON +// maximal dimension use for bounds checking +int vxd_maxd; +#endif + +// Startup for the voxel system, it needs to allocate +// space for the incremental multiplication tables +// pass it the maximum pixel dimension of any of the +// voxel objects you anticipate drawing +// returns TRUE for success, FALSE if unable to allocate +uchar vx_init(int max_depth) +{ + zdxdz = (fix *)malloc(2 * max_depth * sizeof(fix)); + zdydz = zdxdz + max_depth; + + #ifdef DBG_ON + vxd_maxd = max_depth; + #endif + + if (zdxdz == NULL) return FALSE; + return TRUE; +} + +void vx_close() +{ + free(zdxdz); +} + +// Der, this could be a macro, and +// maybe should be +void vx_init_vox(vxs_vox *v,fix pix_dist,fix pix_size,int depth,grs_bitmap *col,grs_bitmap *ht) +{ + v->pix_dist = pix_dist; + v->pix_size = pix_size; + + v->col = col; + v->ht = ht; + + v->w = col->w; + v->h = col->h; + v->d = depth; +} + diff --git a/engine/src/Libraries/adlmidi/adldata.cpp b/engine/src/Libraries/adlmidi/adldata.cpp new file mode 100644 index 0000000..13260ae --- /dev/null +++ b/engine/src/Libraries/adlmidi/adldata.cpp @@ -0,0 +1,10883 @@ +#include "adldata.hh" + +/* THIS OPL-3 FM INSTRUMENT DATA IS AUTOMATICALLY GENERATED + * FROM A NUMBER OF SOURCES, MOSTLY PC GAMES. + * PREPROCESSED, CONVERTED, AND POSTPROCESSED OFF-SCREEN. + */ +const adldata adl[4537] = +{ // ,---------+-------- Wave select settings + // | ,-------ч-+------ Sustain/release rates + // | | ,-----ч-ч-+---- Attack/decay rates + // | | | ,---ч-ч-ч-+-- AM/VIB/EG/KSR/Multiple bits + // | | | | | | | | + // | | | | | | | | ,----+-- KSL/attenuation settings + // | | | | | | | | | | ,----- Feedback/connection bits + // | | | | | | | | | | | ,----- Fine tune + + // | | | | | | | | | | | | + // | | | | | | | | | | | | + { 0x0F4F201,0x0F7F201, 0x8F,0x06, 0x8, +0 }, + { 0x0F4F201,0x0F7F201, 0x4B,0x00, 0x8, +0 }, + { 0x0F4F201,0x0F6F201, 0x49,0x00, 0x8, +0 }, + { 0x0F7F281,0x0F7F241, 0x12,0x00, 0x6, +0 }, + { 0x0F7F101,0x0F7F201, 0x57,0x00, 0x0, +0 }, + { 0x0F7F101,0x0F7F201, 0x93,0x00, 0x0, +0 }, + { 0x0F2A101,0x0F5F216, 0x80,0x0E, 0x8, +0 }, + { 0x0F8C201,0x0F8C201, 0x92,0x00, 0xA, +0 }, + { 0x0F4F60C,0x0F5F381, 0x5C,0x00, 0x0, +0 }, + { 0x0F2F307,0x0F1F211, 0x97,0x80, 0x2, +0 }, + { 0x0F45417,0x0F4F401, 0x21,0x00, 0x2, +0 }, + { 0x0F6F398,0x0F6F281, 0x62,0x00, 0x0, +0 }, + { 0x0F6F618,0x0F7E701, 0x23,0x00, 0x0, +0 }, + { 0x0F6F615,0x0F6F601, 0x91,0x00, 0x4, +0 }, + { 0x0F3D345,0x0F3A381, 0x59,0x80, 0xC, +0 }, + { 0x1F57503,0x0F5B581, 0x49,0x80, 0x4, +0 }, + { 0x014F671,0x007F131, 0x92,0x00, 0x2, +0 }, + { 0x058C772,0x008C730, 0x14,0x00, 0x2, +0 }, + { 0x018AA70,0x0088AB1, 0x44,0x00, 0x4, +0 }, + { 0x1239723,0x01455B1, 0x93,0x00, 0x4, +0 }, + { 0x1049761,0x00455B1, 0x13,0x80, 0x0, +0 }, + { 0x12A9824,0x01A46B1, 0x48,0x00, 0xC, +0 }, + { 0x1069161,0x0076121, 0x13,0x00, 0xA, +0 }, + { 0x0067121,0x00761A1, 0x13,0x89, 0x6, +0 }, + { 0x194F302,0x0C8F341, 0x9C,0x80, 0xC, +0 }, + { 0x19AF303,0x0E7F111, 0x54,0x00, 0xC, +0 }, + { 0x03AF123,0x0F8F221, 0x5F,0x00, 0x0, +0 }, + { 0x122F603,0x0F8F321, 0x87,0x80, 0x6, +0 }, + { 0x054F903,0x03AF621, 0x47,0x00, 0x0, +0 }, + { 0x1419123,0x0198421, 0x4A,0x05, 0x8, +0 }, + { 0x1199523,0x0199421, 0x4A,0x00, 0x8, +0 }, + { 0x04F2009,0x0F8D184, 0xA1,0x80, 0x8, +0 }, + { 0x0069421,0x0A6C3A2, 0x1E,0x00, 0x2, +0 }, + { 0x028F131,0x018F131, 0x12,0x00, 0xA, +0 }, + { 0x0E8F131,0x078F131, 0x8D,0x00, 0xA, +0 }, + { 0x0285131,0x0487132, 0x5B,0x00, 0xC, +0 }, + { 0x09AA101,0x0DFF221, 0x8B,0x40, 0x8, +0 }, + { 0x016A221,0x0DFA121, 0x8B,0x08, 0x8, +0 }, + { 0x0E8F431,0x078F131, 0x8B,0x00, 0xA, +0 }, + { 0x113DD31,0x0265621, 0x15,0x00, 0x8, +0 }, + { 0x113DD31,0x0066621, 0x16,0x00, 0x8, +0 }, + { 0x11CD171,0x00C6131, 0x49,0x00, 0x8, +0 }, + { 0x1127121,0x0067223, 0x4D,0x80, 0x2, +0 }, + { 0x121F1F1,0x0166FE1, 0x40,0x00, 0x2, +0 }, + { 0x175F502,0x0358501, 0x1A,0x80, 0x0, +0 }, + { 0x175F502,0x0F4F301, 0x1D,0x80, 0x0, +0 }, + { 0x105F510,0x0C3F211, 0x41,0x00, 0x2, +0 }, + { 0x125B121,0x00872A2, 0x9B,0x01, 0xE, +0 }, + { 0x1037FA1,0x1073F21, 0x98,0x00, 0x0, +0 }, + { 0x012C1A1,0x0054F61, 0x93,0x00, 0xA, +0 }, + { 0x022C121,0x0054F61, 0x18,0x00, 0xC, +0 }, + { 0x015F431,0x0058A72, 0x5B,0x83, 0x0, +0 }, + { 0x03974A1,0x0677161, 0x90,0x00, 0x0, +0 }, + { 0x0055471,0x0057A72, 0x57,0x00, 0xC, +0 }, + { 0x0635490,0x045A541, 0x00,0x00, 0x8, +0 }, + { 0x0178521,0x0098F21, 0x92,0x01, 0xC, +0 }, + { 0x0177521,0x0098F21, 0x94,0x05, 0xC, +0 }, + { 0x0157621,0x0378261, 0x94,0x00, 0xC, +0 }, + { 0x1179E31,0x12C6221, 0x43,0x00, 0x2, +0 }, + { 0x06A6121,0x00A7F21, 0x9B,0x00, 0x2, +0 }, + { 0x01F7561,0x00F7422, 0x8A,0x06, 0x8, +0 }, + { 0x15572A1,0x0187121, 0x86,0x83, 0x0, +0 }, + { 0x03C5421,0x01CA621, 0x4D,0x00, 0x8, +0 }, + { 0x1029331,0x00B7261, 0x8F,0x00, 0x8, +0 }, + { 0x1039331,0x0097261, 0x8E,0x00, 0x8, +0 }, + { 0x1039331,0x0098261, 0x91,0x00, 0xA, +0 }, + { 0x10F9331,0x00F7261, 0x8E,0x00, 0xA, +0 }, + { 0x116AA21,0x00A8F21, 0x4B,0x00, 0x8, +0 }, + { 0x1177E31,0x10C8B21, 0x90,0x00, 0x6, +0 }, + { 0x1197531,0x0196132, 0x81,0x00, 0x0, +0 }, + { 0x0219B32,0x0177221, 0x90,0x00, 0x4, +0 }, + { 0x05F85E1,0x01A65E1, 0x1F,0x00, 0x0, +0 }, + { 0x05F88E1,0x01A65E1, 0x46,0x00, 0x0, +0 }, + { 0x01F75A1,0x00A7521, 0x9C,0x00, 0x2, +0 }, + { 0x0588431,0x01A6521, 0x8B,0x00, 0x0, +0 }, + { 0x05666E1,0x02665A1, 0x4C,0x00, 0x0, +0 }, + { 0x0467662,0x03655A1, 0xCB,0x00, 0x0, +0 }, + { 0x0075762,0x00756A1, 0x99,0x00, 0xB, +0 }, + { 0x0077762,0x00776A1, 0x93,0x00, 0xB, +0 }, + { 0x203FF22,0x00FFF21, 0x59,0x00, 0x0, +0 }, + { 0x10FFF21,0x10FFF21, 0x0E,0x00, 0x0, +0 }, + { 0x0558622,0x0186421, 0x46,0x80, 0x0, +0 }, + { 0x0126621,0x00A96A1, 0x45,0x00, 0x0, +0 }, + { 0x12A9221,0x02A9122, 0x8B,0x00, 0x0, +0 }, + { 0x005DFA2,0x0076F61, 0x9E,0x40, 0x2, +0 }, + { 0x001EF20,0x2068F60, 0x1A,0x00, 0x0, +0 }, + { 0x029F121,0x009F421, 0x8F,0x80, 0xA, +0 }, + { 0x0945377,0x005A0A1, 0xA5,0x00, 0x2, +0 }, + { 0x011A861,0x00325B1, 0x1F,0x80, 0xA, +0 }, + { 0x0349161,0x0165561, 0x17,0x00, 0xC, +0 }, + { 0x0015471,0x0036A72, 0x5D,0x00, 0x0, +0 }, + { 0x0432121,0x03542A2, 0x97,0x00, 0x8, +0 }, + { 0x177A1A1,0x1473121, 0x1C,0x00, 0x0, +0 }, + { 0x0331121,0x0254261, 0x89,0x03, 0xA, +0 }, + { 0x14711A1,0x007CF21, 0x15,0x00, 0x0, +0 }, + { 0x0F6F83A,0x0028651, 0xCE,0x00, 0x2, +0 }, + { 0x1232121,0x0134121, 0x15,0x00, 0x0, +0 }, + { 0x0957406,0x072A501, 0x5B,0x00, 0x0, +0 }, + { 0x081B122,0x026F261, 0x92,0x83, 0xC, +0 }, + { 0x151F141,0x0F5F242, 0x4D,0x00, 0x0, +0 }, + { 0x1511161,0x01311A3, 0x94,0x80, 0x6, +0 }, + { 0x0311161,0x0031DA1, 0x8C,0x80, 0x6, +0 }, + { 0x173F3A4,0x0238161, 0x4C,0x00, 0x4, +0 }, + { 0x053D202,0x1F6F207, 0x85,0x03, 0x0, +0 }, + { 0x111A311,0x0E5A213, 0x0C,0x80, 0x0, +0 }, + { 0x141F611,0x2E6F211, 0x06,0x00, 0x4, +0 }, + { 0x032D493,0x111EB91, 0x91,0x00, 0x8, +0 }, + { 0x056FA04,0x005C201, 0x4F,0x00, 0xC, +0 }, + { 0x0207C21,0x10C6F22, 0x49,0x00, 0x6, +0 }, + { 0x133DD31,0x0165621, 0x85,0x00, 0xA, +0 }, + { 0x205DA20,0x00B8F21, 0x04,0x81, 0x6, +0 }, + { 0x0E5F105,0x0E5C303, 0x6A,0x80, 0x6, +0 }, + { 0x026EC07,0x016F802, 0x15,0x00, 0xA, +0 }, + { 0x0356705,0x005DF01, 0x9D,0x00, 0x8, +0 }, + { 0x028FA18,0x0E5F812, 0x96,0x00, 0xA, +0 }, + { 0x007A810,0x003FA00, 0x86,0x03, 0x6, +0 }, + { 0x247F811,0x003F310, 0x41,0x03, 0x4, +0 }, + { 0x206F101,0x002F310, 0x8E,0x00, 0xE, +0 }, + { 0x0001F0E,0x3FF1FC0, 0x00,0x00, 0xE, +0 }, + { 0x024F806,0x2845603, 0x80,0x88, 0xE, +0 }, + { 0x000F80E,0x30434D0, 0x00,0x05, 0xE, +0 }, + { 0x000F60E,0x3021FC0, 0x00,0x00, 0xE, +0 }, + { 0x0A337D5,0x03756DA, 0x95,0x40, 0x0, +0 }, + { 0x261B235,0x015F414, 0x5C,0x08, 0xA, +0 }, + { 0x000F60E,0x3F54FD0, 0x00,0x00, 0xE, +0 }, + { 0x001FF26,0x11612E4, 0x00,0x00, 0xE, +0 }, + { 0x0F0F300,0x2C9F600, 0x00,0x00, 0xE, +0 }, + { 0x277F810,0x006F311, 0x44,0x00, 0x8, +0 }, + { 0x0FFF902,0x0FFF811, 0x07,0x00, 0x8, +0 }, + { 0x205FC00,0x017FA00, 0x00,0x00, 0xE, +0 }, + { 0x007FF00,0x008FF01, 0x02,0x00, 0x0, +0 }, + { 0x00CF600,0x006F600, 0x00,0x00, 0x4, +0 }, + { 0x008F60C,0x247FB12, 0x00,0x00, 0xA, +0 }, + { 0x008F60C,0x2477B12, 0x00,0x05, 0xA, +0 }, + { 0x002F60C,0x243CB12, 0x00,0x00, 0xA, +0 }, + { 0x000F60E,0x3029FD0, 0x00,0x00, 0xE, +0 }, + { 0x042F80E,0x3E4F407, 0x08,0x4A, 0xE, +0 }, + { 0x030F50E,0x0029FD0, 0x00,0x0A, 0xE, +0 }, + { 0x3E4E40E,0x1E5F507, 0x0A,0x5D, 0x6, +0 }, + { 0x004B402,0x0F79705, 0x03,0x0A, 0xE, +0 }, + { 0x000F64E,0x3029F9E, 0x00,0x00, 0xE, +0 }, + { 0x237F811,0x005F310, 0x45,0x08, 0x8, +0 }, + { 0x303FF80,0x014FF10, 0x00,0x0D, 0xC, +0 }, + { 0x00CF506,0x008F502, 0x0B,0x00, 0x6, +0 }, + { 0x0BFFA01,0x097C802, 0x00,0x00, 0x7, +0 }, + { 0x087FA01,0x0B7FA01, 0x51,0x00, 0x6, +0 }, + { 0x08DFA01,0x0B8F802, 0x54,0x00, 0x6, +0 }, + { 0x088FA01,0x0B6F802, 0x59,0x00, 0x6, +0 }, + { 0x30AF901,0x006FA00, 0x00,0x00, 0xE, +0 }, + { 0x389F900,0x06CF600, 0x80,0x00, 0xE, +0 }, + { 0x388F803,0x0B6F60C, 0x80,0x08, 0xF, +0 }, + { 0x388F803,0x0B6F60C, 0x85,0x00, 0xF, +0 }, + { 0x04F760E,0x2187700, 0x40,0x08, 0xE, +0 }, + { 0x049C80E,0x2699B03, 0x40,0x00, 0xE, +0 }, + { 0x305ADD7,0x0058DC7, 0xDC,0x00, 0xE, +0 }, + { 0x304A8D7,0x00488C7, 0xDC,0x00, 0xE, +0 }, + { 0x306F680,0x3176711, 0x00,0x00, 0xE, +0 }, + { 0x205F580,0x3164611, 0x00,0x09, 0xE, +0 }, + { 0x0F40006,0x0F5F715, 0x3F,0x00, 0x1, +0 }, + { 0x3F40006,0x0F5F712, 0x3F,0x00, 0x0, +0 }, + { 0x0F40006,0x0F5F712, 0x3F,0x00, 0x1, +0 }, + { 0x0E76701,0x0077502, 0x58,0x00, 0x0, +0 }, + { 0x048F841,0x0057542, 0x45,0x08, 0x0, +0 }, + { 0x3F0E00A,0x005FF1E, 0x40,0x4E, 0x8, +0 }, + { 0x3F0E00A,0x002FF1E, 0x7C,0x52, 0x8, +0 }, + { 0x04A7A0E,0x21B7B00, 0x40,0x08, 0xE, +0 }, + { 0x3E4E40E,0x1395507, 0x0A,0x40, 0x6, +0 }, + { 0x332F905,0x0A5D604, 0x05,0x40, 0xE, +0 }, + { 0x3F30002,0x0F5F715, 0x3F,0x00, 0x8, +0 }, + { 0x08DFA01,0x0B5F802, 0x4F,0x00, 0x7, +0 }, + { 0x054F231,0x056F221, 0x4B,0x00, 0x8, +0 }, + { 0x03BF2B1,0x00BF361, 0x0E,0x00, 0x6, +0 }, + { 0x0E7F21C,0x0B8F201, 0x6F,0x80, 0xC, +0 }, + { 0x0E5B111,0x0B8F211, 0x9C,0x80, 0xD, +0 }, + { 0x0E7C21C,0x0B8F301, 0x3A,0x80, 0x0, +0 }, + { 0x0F5B111,0x0D8F211, 0x1B,0x80, 0x1, +0 }, + { 0x031F031,0x037F234, 0x90,0x9F, 0x8, +0 }, + { 0x451F324,0x497F211, 0x1C,0x00, 0x8, +0 }, + { 0x010A831,0x1B9D234, 0x0A,0x03, 0x6, +0 }, + { 0x0E6CE02,0x0E6F401, 0x25,0x00, 0x0, +0 }, + { 0x0E6F507,0x0E5F341, 0xA1,0x00, 0x1, +0 }, + { 0x0045617,0x004F601, 0x21,0x00, 0x2, +0 }, + { 0x055F718,0x0D8E521, 0x23,0x00, 0x0, +0 }, + { 0x0D6F90A,0x0D6F784, 0x53,0x80, 0xA, +0 }, + { 0x0A6F615,0x0E6F601, 0x91,0x00, 0xB, +0 }, + { 0x0B3D441,0x0B4C280, 0x8A,0x13, 0x4, +0 }, + { 0x082D345,0x0E3A381, 0x59,0x80, 0x5, +0 }, + { 0x1557403,0x005B381, 0x49,0x80, 0x4, +0 }, + { 0x02FA2A0,0x02FA522, 0x85,0x9E, 0x7, +0 }, + { 0x02FA5A2,0x02FA128, 0x83,0x95, 0x7, +0 }, + { 0x02A91A0,0x03AC821, 0x85,0x0B, 0x7, +0 }, + { 0x03AC620,0x05AF621, 0x81,0x80, 0x7, +0 }, + { 0x12AA6E3,0x00AAF61, 0x56,0x83, 0x8, -12 }, + { 0x00AAFE1,0x00AAF62, 0x91,0x83, 0x9, -12 }, + { 0x10BF024,0x20B5030, 0x12,0x00, 0x1, +0 }, + { 0x71A7223,0x02A7221, 0xAC,0x83, 0x0, +0 }, + { 0x41A6223,0x02A62A1, 0x22,0x00, 0x1, +0 }, + { 0x006FF25,0x005FF23, 0xA1,0x2F, 0xA, +0 }, + { 0x405FFA1,0x0096F22, 0x1F,0x80, 0xA, +0 }, + { 0x11A6223,0x02A7221, 0x19,0x80, 0xC, +0 }, + { 0x41A6223,0x02A7222, 0x1E,0x83, 0xD, +0 }, + { 0x074F302,0x0B8F341, 0x9C,0x80, 0xA, +0 }, + { 0x274D302,0x0B8D382, 0xA5,0x40, 0xB, +0 }, + { 0x2F6F234,0x0F7F231, 0x5B,0x9E, 0xC, +0 }, + { 0x0F7F223,0x0E7F111, 0xAB,0x00, 0xC, +0 }, + { 0x0FAF322,0x0FAF223, 0x53,0x66, 0xA, +0 }, + { 0x0FAC221,0x0F7C221, 0xA7,0x00, 0xA, +0 }, + { 0x022FA02,0x0F3F301, 0x4C,0x97, 0x8, +0 }, + { 0x1F3C204,0x0F7C111, 0x9D,0x00, 0x8, +0 }, + { 0x0AFC711,0x0F8F501, 0x87,0x00, 0x8, +0 }, + { 0x098C301,0x0F8C302, 0x18,0x00, 0x9, +0 }, + { 0x0F2B913,0x0119102, 0x0D,0x1A, 0xA, +0 }, + { 0x74A9221,0x02A9122, 0x8F,0x00, 0xA, +0 }, + { 0x103FF80,0x3FFF021, 0x01,0x00, 0x8, +0 }, + { 0x04F2009,0x0F8D104, 0xA1,0x80, 0x8, +0 }, + { 0x2F8F802,0x0F8F602, 0x87,0x00, 0x9, +0 }, + { 0x015A701,0x0C8A301, 0x4D,0x00, 0x2, +0 }, + { 0x0317101,0x0C87301, 0x93,0x00, 0x3, +0 }, + { 0x0E5F111,0x0E5F312, 0xA8,0x57, 0x4, +0 }, + { 0x0E5E111,0x0E6E111, 0x97,0x00, 0x4, +0 }, + { 0x0C7F001,0x027F101, 0xB3,0x16, 0x6, +0 }, + { 0x027F101,0x028F101, 0x16,0x00, 0x6, +0 }, + { 0x00C0300,0x024FA20, 0x30,0x03, 0x6, +12 }, + { 0x024F820,0x056F510, 0x12,0x00, 0x6, +0 }, + { 0x0EBF431,0x07AF131, 0x8B,0x00, 0xA, +0 }, + { 0x1C8F621,0x0C8F101, 0x1C,0x1F, 0xA, +0 }, + { 0x0425401,0x0C8F201, 0x12,0x00, 0xA, +0 }, + { 0x0035131,0x0675461, 0x1C,0x80, 0xE, +0 }, + { 0x21351A0,0x2275360, 0x98,0x01, 0xE, +0 }, + { 0x513DD31,0x0265621, 0x95,0x00, 0x8, +0 }, + { 0x1038D13,0x0866605, 0x95,0x8C, 0x9, +0 }, + { 0x243CC70,0x21774A0, 0x92,0x03, 0xE, +0 }, + { 0x007BF21,0x1076F21, 0x95,0x00, 0xF, +0 }, + { 0x515C261,0x0056FA1, 0x97,0x00, 0x6, +0 }, + { 0x08FB563,0x08FB5A5, 0x13,0x94, 0x7, +0 }, + { 0x0848523,0x0748212, 0xA7,0xA4, 0xE, +0 }, + { 0x0748202,0x0358511, 0x27,0x00, 0xE, +0 }, + { 0x0748202,0x0338411, 0x27,0x00, 0xE, +0 }, + { 0x005F511,0x0C3F212, 0x01,0x1E, 0x3, +0 }, + { 0x2036130,0x21764A0, 0x98,0x03, 0xE, +0 }, + { 0x1176561,0x0176521, 0x92,0x00, 0xF, +0 }, + { 0x2234130,0x2174460, 0x98,0x01, 0xE, +0 }, + { 0x1037FA1,0x1073F21, 0x98,0x00, 0xF, +0 }, + { 0x125B121,0x0087262, 0x9B,0x01, 0xE, +0 }, + { 0x001D3E1,0x0396262, 0xCA,0x83, 0x6, +0 }, + { 0x2197320,0x0297563, 0x22,0x02, 0xE, +0 }, + { 0x2686500,0x613C500, 0x00,0x00, 0xB, +0 }, + { 0x606C800,0x3077400, 0x00,0x00, 0xB, +0 }, + { 0x0157620,0x0378261, 0x94,0x00, 0xC, +12 }, + { 0x02661B1,0x0266171, 0xD3,0x80, 0xD, +0 }, + { 0x00B5131,0x13BB261, 0x1C,0x00, 0xE, +0 }, + { 0x0265121,0x007F021, 0x18,0x00, 0xA, +0 }, + { 0x0257221,0x00A7F21, 0x16,0x05, 0xC, +0 }, + { 0x0357A21,0x03A7A21, 0x1D,0x09, 0xD, +0 }, + { 0x035C221,0x00ACF61, 0x16,0x09, 0xE, +0 }, + { 0x04574A1,0x0087F21, 0x8A,0x00, 0xF, +0 }, + { 0x01A52A1,0x01B8F61, 0x97,0x00, 0xC, +0 }, + { 0x01A7521,0x01B8F21, 0xA1,0x00, 0xD, +0 }, + { 0x20F9331,0x00F72A1, 0x96,0x00, 0x8, +0 }, + { 0x0078521,0x1278431, 0x96,0x00, 0x9, +0 }, + { 0x1039331,0x00972A1, 0x8E,0x00, 0x8, +0 }, + { 0x006C524,0x1276431, 0xA1,0x00, 0x9, +0 }, + { 0x10693B1,0x0067271, 0x8E,0x00, 0xA, +0 }, + { 0x0088521,0x02884B1, 0x5D,0x00, 0xB, +0 }, + { 0x10F9331,0x00F7272, 0x93,0x00, 0xC, +0 }, + { 0x0068522,0x01684B1, 0x61,0x00, 0xD, +0 }, + { 0x02AA961,0x036A863, 0xA3,0x52, 0x8, +0 }, + { 0x016AA61,0x00A8F61, 0x94,0x80, 0x8, +0 }, + { 0x0297721,0x1267A33, 0x21,0x55, 0x2, +0 }, + { 0x0167AA1,0x0197A22, 0x93,0x00, 0x2, +0 }, + { 0x1077B21,0x0007F22, 0x2B,0x57, 0xA, +0 }, + { 0x0197531,0x0196172, 0x51,0x00, 0xA, +0 }, + { 0x0219B32,0x0177221, 0x90,0x00, 0x8, +0 }, + { 0x0219B32,0x0177221, 0x90,0x13, 0x9, +0 }, + { 0x029C9A4,0x0086F21, 0xA2,0x80, 0xC, +0 }, + { 0x015CAA2,0x0086F21, 0xAA,0x00, 0xD, +0 }, + { 0x0AA7724,0x0173431, 0x5B,0x00, 0xE, +0 }, + { 0x0C676A1,0x0868726, 0x0D,0x59, 0xF, +0 }, + { 0x0566622,0x02665A1, 0x56,0x00, 0xE, +0 }, + { 0x0019F26,0x0487664, 0x00,0x25, 0xE, +0 }, + { 0x0465622,0x03645A1, 0xCB,0x00, 0xF, +0 }, + { 0x11467E1,0x0175461, 0x67,0x00, 0xC, +0 }, + { 0x1146721,0x0164421, 0x6D,0x00, 0xD, +0 }, + { 0x00F4032,0x0097021, 0xDF,0x00, 0x0, +0 }, + { 0x00FFF21,0x00FFF21, 0x35,0xB7, 0x4, +0 }, + { 0x00FFF21,0x60FFF21, 0xB9,0x80, 0x4, +0 }, + { 0x00FFF21,0x00FFF21, 0x36,0x1B, 0xA, +0 }, + { 0x00FFF21,0x409CF61, 0x1D,0x00, 0xA, +0 }, + { 0x0658722,0x0186421, 0x46,0x80, 0x0, +0 }, + { 0x4F2B912,0x0119101, 0x0D,0x1A, 0xA, +0 }, + { 0x12A9221,0x02A9122, 0x99,0x00, 0xA, +0 }, + { 0x0157D61,0x01572B1, 0x40,0xA3, 0xE, +0 }, + { 0x005DFA2,0x0077F61, 0x5D,0x40, 0xF, +0 }, + { 0x001FF20,0x4068F61, 0x36,0x00, 0x8, +0 }, + { 0x00FFF21,0x4078F61, 0x27,0x00, 0x9, +0 }, + { 0x1035317,0x004F608, 0x1A,0x0D, 0x2, +0 }, + { 0x03241A1,0x0156161, 0x9D,0x00, 0x3, +0 }, + { 0x031A181,0x0032571, 0xA1,0x00, 0xB, +0 }, + { 0x0141161,0x0165561, 0x17,0x00, 0xC, +0 }, + { 0x445C361,0x025C361, 0x14,0x00, 0xD, +0 }, + { 0x021542A,0x0136A27, 0x80,0xA6, 0xE, +0 }, + { 0x0015431,0x0036A72, 0x5D,0x00, 0xF, +0 }, + { 0x0331121,0x02542A1, 0x89,0x03, 0xA, +0 }, + { 0x1471161,0x007CF21, 0x15,0x00, 0x0, +0 }, + { 0x1B1F2DE,0x0B281D1, 0x57,0x0A, 0xE, +0 }, + { 0x2322121,0x0133220, 0x8C,0x97, 0x6, +0 }, + { 0x1031121,0x0133121, 0x0E,0x00, 0x7, +0 }, + { 0x0F6F358,0x0F6F241, 0x62,0x00, 0x0, +0 }, + { 0x00F5F00,0x005FF00, 0x00,0x00, 0x0, +0 }, + { 0x03111A1,0x0031D61, 0x8C,0x80, 0x6, +0 }, + { 0x173F364,0x02381A1, 0x4C,0x00, 0x4, +0 }, + { 0x053F301,0x1F6F101, 0x46,0x80, 0x0, +0 }, + { 0x053F201,0x0F6F208, 0x43,0x40, 0x1, +0 }, + { 0x135A511,0x133A517, 0x10,0xA4, 0x0, +0 }, + { 0x141F611,0x2E5F211, 0x0D,0x00, 0x0, +0 }, + { 0x0F8F755,0x1E4F752, 0x92,0x9F, 0xE, +0 }, + { 0x0E4F341,0x1E5F351, 0x13,0x00, 0xE, +0 }, + { 0x032D493,0x111EB11, 0x91,0x00, 0x8, +0 }, + { 0x032D453,0x112EB13, 0x91,0x0D, 0x9, +0 }, + { 0x3E5F720,0x0E5F521, 0x00,0x0C, 0xD, +0 }, + { 0x0207C21,0x10C6F22, 0x09,0x09, 0x7, +0 }, + { 0x133DD02,0x0166601, 0x83,0x80, 0xB, +0 }, + { 0x0298961,0x406D8A3, 0x33,0xA4, 0x6, +0 }, + { 0x005DA21,0x00B8F22, 0x17,0x80, 0x6, +0 }, + { 0x026EC08,0x016F804, 0x15,0x00, 0xA, +0 }, + { 0x026EC07,0x016F802, 0x15,0x00, 0xB, +0 }, + { 0x024682C,0x035DF01, 0xAB,0x00, 0x0, +0 }, + { 0x0356705,0x005DF01, 0x9D,0x00, 0x1, +0 }, + { 0x0A3FD07,0x078F902, 0xC0,0x00, 0xE, +0 }, + { 0x055FC14,0x005F311, 0x8D,0x00, 0xE, +0 }, + { 0x455F811,0x0E5F410, 0x86,0x00, 0xE, +0 }, + { 0x155F311,0x0E5F410, 0x9C,0x00, 0xF, +0 }, + { 0x0001E0E,0x3FE1800, 0x00,0x00, 0xE, +0 }, + { 0x05C5F0E,0x16C870E, 0x00,0x02, 0x0, +0 }, + { 0x0F0F00E,0x0841300, 0x00,0x00, 0xE, +0 }, + { 0x0F0F000,0x0F05F0C, 0x2E,0x00, 0xE, +0 }, + { 0x061F217,0x0B4F112, 0x4F,0x0A, 0x8, +0 }, + { 0x001EFEE,0x0069FE0, 0x01,0x04, 0x6, +0 }, + { 0x001FF26,0x71612E4, 0x00,0x00, 0xE, +0 }, + { 0x0F10001,0x0F10001, 0x3F,0x3F, 0xF, +0 }, + { 0x059F200,0x000F701, 0x00,0x00, 0xE, +0 }, + { 0x0F0F301,0x6C9F601, 0x00,0x00, 0xE, +0 }, + { 0x029A100,0x0696521, 0x02,0x08, 0xE, +0 }, + { 0x29BF300,0x008F311, 0x0C,0x00, 0xE, +0 }, + { 0x068FAC0,0x377F701, 0x02,0x00, 0x2, +0 }, + { 0x0C4FA01,0x33FF600, 0x03,0x00, 0x0, +0 }, + { 0x0FFF832,0x07FF511, 0x44,0x00, 0xE, +0 }, + { 0x27AFB12,0x047F611, 0x40,0x00, 0x6, +0 }, + { 0x144F5C6,0x018F6C1, 0x5C,0x83, 0xE, +0 }, + { 0x0D0CCC0,0x028EAC1, 0x10,0x00, 0x0, +0 }, + { 0x2B7F811,0x006F311, 0x46,0x00, 0x8, +0 }, + { 0x2BAE610,0x005EA10, 0x04,0x00, 0x0, +0 }, + { 0x0F70700,0x0F70710, 0xFF,0xFF, 0x0, +0 }, + { 0x218F401,0x008F800, 0x00,0x00, 0xC, +0 }, + { 0x0F0F009,0x0F7B700, 0x0E,0x00, 0xE, +0 }, + { 0x0FEF812,0x07ED511, 0x47,0x00, 0xE, +0 }, + { 0x005F010,0x004D011, 0x25,0x80, 0xE, +0 }, + { 0x00F9F30,0x0FAE83A, 0x00,0x00, 0xE, +0 }, + { 0x0976800,0x3987802, 0x00,0x00, 0x0, +0 }, + { 0x0FBF116,0x069F911, 0x08,0x02, 0x0, +0 }, + { 0x06CF800,0x04AE80E, 0x00,0x40, 0x0, +0 }, + { 0x0F2FA25,0x09AF612, 0x1B,0x00, 0x0, +0 }, + { 0x2F5F5C5,0x005C301, 0x08,0x06, 0x1, +0 }, + { 0x257F900,0x046FB00, 0x00,0x00, 0x0, +12 }, + { 0x0FEF512,0x0FFF612, 0x11,0xA2, 0x6, +0 }, + { 0x0FFF901,0x0FFF811, 0x0F,0x00, 0x6, +0 }, + { 0x0F0F01E,0x0B6F70E, 0x00,0x00, 0xE, +0 }, + { 0x204FF82,0x015FF10, 0x00,0x06, 0xE, +0 }, + { 0x007FF00,0x008FF01, 0x02,0x00, 0xF, +0 }, + { 0x04CA800,0x13FD600, 0x0B,0x00, 0x0, +0 }, + { 0x25E980C,0x306FB0F, 0x00,0x00, 0xF, +12 }, + { 0x25E780C,0x32B8A0A, 0x00,0x80, 0xF, +12 }, + { 0x201C700,0x233F90B, 0x45,0x00, 0xE, +12 }, + { 0x04FF82E,0x3EFF521, 0x07,0x0B, 0xE, +0 }, + { 0x065F981,0x030F241, 0x00,0x00, 0xE, +0 }, + { 0x000FE46,0x055F585, 0x00,0x00, 0xE, +0 }, + { 0x0009429,0x344F904, 0x10,0x04, 0xE, +0 }, + { 0x282B2A4,0x1D49703, 0x00,0x80, 0xE, +0 }, + { 0x000F68E,0x3029F5E, 0x00,0x00, 0xE, +0 }, + { 0x152FE09,0x008F002, 0xC0,0x00, 0xE, +0 }, + { 0x055F201,0x000F441, 0x00,0x00, 0xE, +0 }, + { 0x000F301,0x0A4F48F, 0x00,0x00, 0xE, +0 }, + { 0x100FF80,0x1F7F500, 0x00,0x00, 0xC, +0 }, + { 0x05EFD2E,0x3EFF527, 0x07,0x0C, 0xE, +0 }, + { 0x256FB00,0x026FA00, 0x00,0x00, 0x4, +12 }, + { 0x256FB00,0x017F700, 0x80,0x00, 0x0, +12 }, + { 0x1779A01,0x084F700, 0x00,0x00, 0x8, +0 }, + { 0x367FD01,0x098F601, 0x00,0x00, 0x8, +12 }, + { 0x001FF0E,0x377790E, 0x00,0x02, 0xE, +0 }, + { 0x2079F20,0x22B950E, 0x1C,0x00, 0x0, +0 }, + { 0x2079F20,0x23B940E, 0x1E,0x00, 0x0, +0 }, + { 0x506F680,0x016F610, 0x00,0x00, 0xC, +0 }, + { 0x50F6F00,0x50F6F00, 0x00,0x00, 0xD, +0 }, + { 0x50F4F00,0x50F4F00, 0x00,0x00, 0xD, +0 }, + { 0x0FFEE03,0x0FFE808, 0x40,0x00, 0xC, +0 }, + { 0x060F2C5,0x07AF4D4, 0x4F,0x80, 0x8, +12 }, + { 0x160F285,0x0B7F294, 0x4F,0x80, 0x8, +12 }, + { 0x04F760F,0x2187700, 0x40,0x08, 0xE, +0 }, + { 0x332F905,0x0A6D604, 0x05,0x40, 0xE, +0 }, + { 0x332F805,0x0A67404, 0x05,0x40, 0xF, +0 }, + { 0x0F0F126,0x0F5F527, 0x44,0x40, 0x6, +0 }, + { 0x3948F03,0x06FFA15, 0x00,0x00, 0x0, +0 }, + { 0x0F0F007,0x0DC5C00, 0x00,0x00, 0xE, +0 }, + { 0x00FFF7E,0x00F3F6E, 0x00,0x00, 0xE, +0 }, + { 0x0B3FA00,0x005D000, 0x00,0x00, 0xC, +0 }, + { 0x0FFF832,0x07FF511, 0x84,0x00, 0xE, +0 }, + { 0x0089FD4,0x0089FD4, 0xC0,0xC0, 0x4, +0 }, + { 0x2F4F50E,0x424120CA, 0x00,0x51, 0x3, +0 }, + { 0x283E0C4,0x14588C0, 0x81,0x00, 0xE, +0 }, + { 0x0B0900E,0x0BF990E, 0x03,0x03, 0xA, +0 }, + { 0x0DFDCC2,0x026C9C0, 0x17,0x00, 0x0, +0 }, + { 0x0D0ACC0,0x028EAC1, 0x18,0x00, 0x0, +0 }, + { 0x0A7CDC2,0x028EAC1, 0x2B,0x02, 0x0, +0 }, + { 0x0FE6227,0x3D9950A, 0x00,0x07, 0x8, +0 }, + { 0x1199523,0x0198421, 0x48,0x00, 0x8, +0 }, + { 0x055F231,0x076F221, 0x49,0x00, 0x8, +0 }, + { 0x038F101,0x028F121, 0x57,0x00, 0x0, +0 }, + { 0x038F101,0x028F121, 0x93,0x00, 0x0, +0 }, + { 0x001A221,0x0D5F136, 0x80,0x0E, 0x8, +0 }, + { 0x0A8C201,0x058C201, 0x92,0x00, 0xA, +0 }, + { 0x054F60C,0x0B5F381, 0x5C,0x00, 0x0, +0 }, + { 0x032F607,0x011F511, 0x97,0x80, 0x2, +0 }, + { 0x0E6F318,0x0F6F281, 0x62,0x00, 0x0, +0 }, + { 0x0A6F615,0x0E6F601, 0x91,0x00, 0x4, +0 }, + { 0x082D345,0x0E3A381, 0x59,0x80, 0xC, +0 }, + { 0x122F603,0x0F3F321, 0x87,0x80, 0x6, +0 }, + { 0x09AA101,0x0DFF221, 0x89,0x40, 0x8, +0 }, + { 0x15572A1,0x0187121, 0x86,0x0D, 0x0, +0 }, + { 0x0F00010,0x0F00010, 0x3F,0x3F, 0x0, +0 }, + { 0x0F1F02E,0x3487407, 0x00,0x07, 0x8, +0 }, + { 0x0FE5229,0x3D9850E, 0x00,0x07, 0x6, +0 }, + { 0x0FDF800,0x0C7F601, 0x0B,0x00, 0x8, +0 }, + { 0x000FF26,0x0A7F802, 0x00,0x02, 0xE, +0 }, + { 0x01FFA06,0x0F5F511, 0x0A,0x00, 0xF, +0 }, + { 0x0F1F52E,0x3F99906, 0x05,0x02, 0x0, +0 }, + { 0x0F89227,0x3D8750A, 0x00,0x03, 0x8, +0 }, + { 0x2009F2C,0x3A4C50E, 0x00,0x09, 0xE, +0 }, + { 0x0009429,0x344F904, 0x10,0x0C, 0xE, +0 }, + { 0x0F1F52E,0x3F78706, 0x09,0x02, 0x0, +0 }, + { 0x2F1F535,0x028F703, 0x19,0x02, 0x0, +0 }, + { 0x0FAFA25,0x0F99803, 0xCD,0x00, 0x0, +0 }, + { 0x1FAF825,0x0F7A803, 0x1B,0x00, 0x0, +0 }, + { 0x1FAF825,0x0F69603, 0x21,0x00, 0xE, +0 }, + { 0x2F5F504,0x236F603, 0x16,0x03, 0xA, +0 }, + { 0x091F015,0x0E8A617, 0x1E,0x04, 0xE, +0 }, + { 0x001FF0E,0x077780E, 0x06,0x04, 0xE, +0 }, + { 0x0F7F020,0x33B8809, 0x00,0x00, 0xC, +0 }, + { 0x0F7F420,0x33B560A, 0x03,0x00, 0x0, +0 }, + { 0x05BF714,0x089F712, 0x4B,0x00, 0x0, +0 }, + { 0x0F2FA27,0x09AF612, 0x22,0x00, 0x0, +0 }, + { 0x1F75020,0x03B7708, 0x09,0x05, 0x0, +0 }, + { 0x1077F26,0x06B7703, 0x29,0x05, 0x0, +0 }, + { 0x0F0F126,0x0FCF727, 0x44,0x40, 0x6, +0 }, + { 0x0F3F821,0x0ADC620, 0x1C,0x00, 0xC, +0 }, + { 0x0FFFF01,0x0FFFF01, 0x3F,0x3F, 0x0, +0 }, + { 0x4FFEE03,0x0FFE804, 0x80,0x00, 0xC, +0 }, + { 0x122F603,0x0F8F3A1, 0x87,0x80, 0x6, +0 }, + { 0x007A810,0x005FA00, 0x86,0x03, 0x6, +0 }, + { 0x053F131,0x227F232, 0x48,0x00, 0x6, +0 }, + { 0x01A9161,0x01AC1E6, 0x40,0x03, 0x8, +0 }, + { 0x071FB11,0x0B9F301, 0x00,0x00, 0x0, +0 }, + { 0x1B57231,0x098D523, 0x0B,0x00, 0x8, +0 }, + { 0x024D501,0x0228511, 0x0F,0x00, 0xA, +0 }, + { 0x025F911,0x034F131, 0x05,0x00, 0xA, +0 }, + { 0x01576A1,0x0378261, 0x94,0x00, 0xC, +0 }, + { 0x1362261,0x0084F22, 0x10,0x40, 0x8, +0 }, + { 0x2363360,0x0084F22, 0x15,0x40, 0xC, +0 }, + { 0x007F804,0x0748201, 0x0E,0x05, 0x6, +0 }, + { 0x0E5F131,0x174F131, 0x89,0x00, 0xC, +0 }, + { 0x0E3F131,0x073F172, 0x8A,0x00, 0xA, +0 }, + { 0x0FFF101,0x0FF5091, 0x0D,0x80, 0x6, +0 }, + { 0x1473161,0x007AF61, 0x0F,0x00, 0xA, +0 }, + { 0x0D3B303,0x024F204, 0x40,0x80, 0x4, +0 }, + { 0x1037531,0x0445462, 0x1A,0x40, 0xE, +0 }, + { 0x021A1A1,0x116C261, 0x92,0x40, 0x6, +0 }, + { 0x0F0F240,0x0F4F440, 0x00,0x00, 0x4, +0 }, + { 0x003F1C0,0x001107E, 0x4F,0x0C, 0x2, +0 }, + { 0x0459BC0,0x015F9C1, 0x05,0x00, 0xE, +0 }, + { 0x0064F50,0x003FF50, 0x10,0x00, 0x0, +0 }, + { 0x2F0F005,0x1B4F600, 0x08,0x00, 0xC, +0 }, + { 0x0F2F931,0x042F210, 0x40,0x00, 0x4, +0 }, + { 0x00FFF7E,0x00F2F6E, 0x00,0x00, 0xE, +0 }, + { 0x2F95401,0x2FB5401, 0x19,0x00, 0x8, +0 }, + { 0x0665F53,0x0077F00, 0x05,0x00, 0x6, +0 }, + { 0x003F1C0,0x006707E, 0x4F,0x03, 0x2, +0 }, + { 0x1111EF0,0x11411E2, 0x00,0xC0, 0x8, +0 }, + { 0x0F0A006,0x075C584, 0x00,0x00, 0xE, +0 }, + { 0x1F5F213,0x0F5F111, 0xC6,0x05, 0x0, +0 }, + { 0x153F101,0x274F111, 0x49,0x02, 0x6, +0 }, + { 0x0E4F4D0,0x006A29E, 0x80,0x00, 0x8, +0 }, + { 0x0871321,0x0084221, 0xCD,0x80, 0x8, +0 }, + { 0x065B400,0x075B400, 0x00,0x00, 0x7, +0 }, + { 0x02AF800,0x145F600, 0x03,0x00, 0x4, +0 }, + { 0x0FFF830,0x07FF511, 0x44,0x00, 0x8, +0 }, + { 0x0F9F900,0x023F110, 0x08,0x00, 0xA, +0 }, + { 0x0F9F900,0x026F180, 0x04,0x00, 0x8, +0 }, + { 0x1FDF800,0x059F800, 0xC4,0x00, 0xA, +0 }, + { 0x06FFA00,0x08FF600, 0x0B,0x00, 0x0, +0 }, + { 0x0F9F900,0x023F191, 0x04,0x00, 0x8, +0 }, + { 0x097C802,0x097C802, 0x00,0x00, 0x1, +0 }, + { 0x0BFFA01,0x0BFDA02, 0x00,0x00, 0x8, +0 }, + { 0x2F0FB01,0x096F701, 0x10,0x00, 0xE, +0 }, + { 0x002FF04,0x007FF00, 0x00,0x00, 0xE, +0 }, + { 0x0F0F006,0x0B7F600, 0x00,0x00, 0xC, +0 }, + { 0x0F0F006,0x034C4C4, 0x00,0x03, 0xE, +0 }, + { 0x0F0F019,0x0F7B720, 0x0E,0x0A, 0xE, +0 }, + { 0x0F0F006,0x0B4F600, 0x00,0x00, 0xE, +0 }, + { 0x0F0F006,0x0B6F800, 0x00,0x00, 0xE, +0 }, + { 0x0F2F931,0x008F210, 0x40,0x00, 0x4, +0 }, + { 0x0BFFA01,0x0BFDA09, 0x00,0x08, 0x8, +0 }, + { 0x210BA2E,0x2F4B40E, 0x0E,0x00, 0xE, +0 }, + { 0x210FA2E,0x2F4F40E, 0x0E,0x00, 0xE, +0 }, + { 0x2A2B2A4,0x1D49703, 0x02,0x80, 0xE, +0 }, + { 0x200FF04,0x206FFC3, 0x00,0x00, 0x8, +0 }, + { 0x200FF04,0x2F5F6C3, 0x00,0x00, 0x8, +0 }, + { 0x0E1C000,0x153951E, 0x80,0x80, 0x6, +0 }, + { 0x200FF03,0x3F6F6C4, 0x00,0x00, 0x8, +0 }, + { 0x202FF4E,0x3F7F701, 0x00,0x00, 0x8, +0 }, + { 0x202FF4E,0x3F6F601, 0x00,0x00, 0x8, +0 }, + { 0x2588A51,0x018A452, 0x00,0x00, 0xC, +0 }, + { 0x0FFFB13,0x0FFE808, 0x40,0x00, 0x8, +0 }, + { 0x0FFEE05,0x0FFE808, 0x55,0x00, 0xE, +0 }, + { 0x0FF0006,0x0FDF715, 0x3F,0x0D, 0x1, +0 }, + { 0x0F6F80E,0x0F6F80E, 0x00,0x00, 0x0, +0 }, + { 0x060F207,0x072F212, 0x4F,0x09, 0x8, +0 }, + { 0x061F217,0x074F212, 0x4F,0x08, 0x8, +0 }, + { 0x022FB18,0x012F425, 0x88,0x80, 0x8, +0 }, + { 0x0F0FF04,0x0B5F4C1, 0x00,0x00, 0xE, +0 }, + { 0x02FC811,0x0F5F531, 0x2D,0x00, 0xC, +0 }, + { 0x03D6709,0x3FC692C, 0x00,0x00, 0xE, +0 }, + { 0x053D144,0x05642B2, 0x80,0x15, 0xE, +0 }, + { 0x253B1C4,0x083B1D2, 0x8F,0x84, 0x2, +0 }, + { 0x175F5C2,0x074F2D1, 0x21,0x83, 0xE, +0 }, + { 0x1F6FB34,0x04394B1, 0x83,0x00, 0xC, +0 }, + { 0x0BDF211,0x09BA004, 0x46,0x40, 0x8, +0 }, + { 0x144F221,0x3457122, 0x8A,0x40, 0x0, +0 }, + { 0x144F221,0x1447122, 0x8A,0x40, 0x0, +0 }, + { 0x053F101,0x153F108, 0x40,0x40, 0x0, +0 }, + { 0x102FF00,0x3FFF200, 0x08,0x00, 0x0, +0 }, + { 0x144F221,0x345A122, 0x8A,0x40, 0x0, +0 }, + { 0x028F131,0x018F031, 0x0F,0x00, 0xA, +0 }, + { 0x307D7E1,0x107B6E0, 0x8D,0x00, 0x1, +0 }, + { 0x03DD500,0x02CD500, 0x11,0x00, 0xA, +0 }, + { 0x1199563,0x219C420, 0x46,0x00, 0x8, +0 }, + { 0x044D08C,0x2F4D181, 0xA1,0x80, 0x8, +0 }, + { 0x0022171,0x1035231, 0x93,0x80, 0x0, +0 }, + { 0x1611161,0x01311A2, 0x91,0x80, 0x8, +0 }, + { 0x25666E1,0x02665A1, 0x4C,0x00, 0x0, +0 }, + { 0x038FB00,0x0DAF400, 0x00,0x00, 0x4, +0 }, + { 0x2BFFB15,0x31FF817, 0x0A,0x00, 0x0, +0 }, + { 0x0BFFBC6,0x02FE8C9, 0x00,0x00, 0xE, +0 }, + { 0x2F0F006,0x2B7F800, 0x00,0x00, 0xE, +0 }, + { 0x097C802,0x040E000, 0x00,0x00, 0x1, +0 }, + { 0x00FFF2E,0x04AF602, 0x0A,0x1B, 0xE, +0 }, + { 0x3A5F0EE,0x36786CE, 0x00,0x00, 0xE, +0 }, + { 0x0B0FCD6,0x008BDD6, 0x00,0x05, 0xA, +0 }, + { 0x0F0F007,0x0DC5C00, 0x08,0x00, 0xE, +0 }, + { 0x0E7F301,0x078F211, 0x58,0x00, 0xA, +0 }, + { 0x0EFF230,0x078F521, 0x1E,0x00, 0xE, +0 }, + { 0x019D530,0x01B6171, 0x88,0x80, 0xC, +0 }, + { 0x001F201,0x0B7F211, 0x0D,0x0D, 0xA, +0 }, + { 0x03DD500,0x02CD500, 0x14,0x00, 0xA, +0 }, + { 0x010E032,0x0337D16, 0x87,0x84, 0x8, +0 }, + { 0x0F8F161,0x008F062, 0x80,0x80, 0x8, +0 }, + { 0x0745391,0x0755451, 0x00,0x00, 0xA, +0 }, + { 0x08E6121,0x09E7231, 0x15,0x00, 0xE, +0 }, + { 0x0BC7321,0x0BC8121, 0x19,0x00, 0xE, +0 }, + { 0x23C7320,0x0BC8121, 0x19,0x00, 0xE, +0 }, + { 0x209A060,0x20FF014, 0x02,0x80, 0x1, +0 }, + { 0x064F207,0x075F612, 0x73,0x00, 0x8, +0 }, + { 0x054D221,0x075B231, 0x4D,0x80, 0x8, +0 }, + { 0x053D221,0x073B231, 0x56,0x80, 0x8, +0 }, + { 0x053D221,0x073B231, 0x55,0x80, 0x8, +0 }, + { 0x201AF70,0x0084F32, 0x19,0x40, 0xC, +0 }, + { 0x201AF70,0x0083F32, 0x19,0x40, 0xC, +0 }, + { 0x0302221,0x0064F32, 0x99,0x00, 0xE, +0 }, + { 0x0006F71,0x0064F32, 0x99,0x00, 0xE, +0 }, + { 0x0006F71,0x0074F32, 0x99,0x80, 0xE, +0 }, + { 0x0006F71,0x0054F32, 0x9E,0x80, 0xE, +0 }, + { 0x0006F71,0x0054F31, 0x9E,0x80, 0xE, +0 }, + { 0x0006F71,0x0054F32, 0x9C,0x80, 0xE, +0 }, + { 0x006F231,0x0084221, 0xCF,0x80, 0x6, +0 }, + { 0x0811321,0x0074221, 0xCD,0x80, 0x8, +0 }, + { 0x0957406,0x074A401, 0x5B,0x00, 0x0, +0 }, + { 0x021A1A1,0x116C261, 0x92,0x00, 0x6, +0 }, + { 0x0EFF230,0x078F522, 0x1E,0x00, 0xE, +0 }, + { 0x01FF003,0x01FF001, 0x5B,0x80, 0xA, +0 }, + { 0x00FFF24,0x00FFF21, 0x80,0x80, 0x1, +0 }, + { 0x00F4021,0x10F1020, 0x00,0x00, 0xE, +0 }, + { 0x045F221,0x076F221, 0x8F,0x06, 0x8, +0 }, + { 0x053B121,0x074C231, 0x4F,0x00, 0x6, +0 }, + { 0x011F111,0x0B3C101, 0x4A,0x80, 0x6, +0 }, + { 0x058F381,0x058F201, 0x63,0x80, 0x0, +0 }, + { 0x001F701,0x0B7F407, 0x0D,0x06, 0xA, +0 }, + { 0x060F206,0x072F211, 0x4F,0x0C, 0x8, +0 }, + { 0x0E3F318,0x093F281, 0x62,0x00, 0x0, +0 }, + { 0x326CE15,0x025F901, 0x57,0x00, 0xC, +0 }, + { 0x1558403,0x005D381, 0x49,0x80, 0x4, +0 }, + { 0x0F0FB3E,0x09BA071, 0x29,0x40, 0x0, +0 }, + { 0x01FF003,0x014F001, 0x5B,0x88, 0xA, +0 }, + { 0x14941A1,0x009CF21, 0x15,0x00, 0x0, +0 }, + { 0x074A302,0x075C441, 0x9A,0x80, 0xA, +0 }, + { 0x01FF260,0x07CF521, 0x11,0x00, 0xA, +0 }, + { 0x122F603,0x0F4F321, 0x87,0x80, 0x6, +0 }, + { 0x0442009,0x0F4D184, 0xA1,0x80, 0x8, +0 }, + { 0x066C101,0x066A201, 0x9A,0x40, 0xA, +0 }, + { 0x0236321,0x0266421, 0x97,0x00, 0x0, +0 }, + { 0x111C031,0x1157221, 0x20,0x06, 0xC, +0 }, + { 0x1107421,0x0165223, 0x0C,0x08, 0x2, +0 }, + { 0x1DBB851,0x1567591, 0x17,0x00, 0xC, +0 }, + { 0x075C502,0x0F3C201, 0x29,0x83, 0x0, +0 }, + { 0x0EFE800,0x0FFA401, 0x0D,0x00, 0x6, +0 }, + { 0x01171B1,0x1177261, 0x8B,0x40, 0x6, +0 }, + { 0x111F0F1,0x1131121, 0x95,0x00, 0x0, +0 }, + { 0x111C031,0x1159221, 0x20,0x06, 0xC, +0 }, + { 0x111C0B1,0x1159221, 0x20,0x08, 0xC, +0 }, + { 0x00B4131,0x03B9261, 0x1C,0x80, 0xC, +0 }, + { 0x01F4131,0x03B9261, 0x1C,0x80, 0xE, +0 }, + { 0x0646300,0x0757211, 0x1C,0x00, 0xE, +0 }, + { 0x0014131,0x03B9261, 0x1C,0x80, 0xE, +0 }, + { 0x05A5321,0x01AAA21, 0x9C,0x80, 0xE, +0 }, + { 0x003F200,0x0FFF220, 0x80,0x00, 0xE, +0 }, + { 0x0001F0E,0x3F01FC0, 0x00,0x00, 0xE, +0 }, + { 0x179A1A1,0x1495121, 0x1C,0x00, 0x0, +0 }, + { 0x0177EB1,0x00E7B22, 0xC5,0x05, 0x2, +0 }, + { 0x019D531,0x01B6132, 0xD1,0x80, 0xC, +0 }, + { 0x01B5132,0x03BA261, 0x9A,0x82, 0xC, +0 }, + { 0x1047021,0x06D6361, 0xC6,0x00, 0xE, +0 }, + { 0x08F6EE0,0x02A6561, 0xEC,0x00, 0xE, +0 }, + { 0x0297122,0x0296431, 0x08,0x04, 0xD, +0 }, + { 0x20FF2D0,0x08562C1, 0xEB,0x06, 0x0, +0 }, + { 0x0154221,0x0065021, 0xE3,0x00, 0x8, +0 }, + { 0x144F221,0x0439422, 0x8A,0x40, 0x0, +0 }, + { 0x05312C4,0x07212F1, 0x17,0x00, 0xA, +0 }, + { 0x0536244,0x0046041, 0x56,0x00, 0xC, +0 }, + { 0x0E6E800,0x0F6A300, 0x0D,0x00, 0x6, +0 }, + { 0x141FA11,0x2F5F411, 0x06,0x00, 0x4, +0 }, + { 0x0268721,0x1188421, 0x07,0x00, 0x6, +0 }, + { 0x055F502,0x053F601, 0x99,0x80, 0x0, +0 }, + { 0x060F207,0x072F212, 0x4F,0x00, 0x8, +0 }, + { 0x0105AEC,0x1F454EE, 0x00,0x00, 0xE, +0 }, + { 0x286F2A5,0x228670E, 0x00,0x00, 0xE, +0 }, + { 0x007FF01,0x007FF21, 0x00,0x00, 0x7, +0 }, + { 0x00CFF01,0x00BFF21, 0x00,0x00, 0x7, +0 }, + { 0x211BA12,0x2F5B400, 0x0B,0x00, 0xE, +0 }, + { 0x021FF13,0x003FF10, 0x51,0x40, 0xA, +0 }, + { 0x002F002,0x004D001, 0xC0,0x00, 0x4, +0 }, + { 0x050F101,0x07CD201, 0x4F,0x04, 0x6, +0 }, + { 0x2129A14,0x004FA01, 0x97,0x80, 0xE, +0 }, + { 0x0038165,0x007F171, 0xD2,0x00, 0x2, +0 }, + { 0x0AE7121,0x01ED320, 0x1C,0x00, 0xE, +0 }, + { 0x053F101,0x083F212, 0xCF,0x00, 0x2, +0 }, + { 0x154FF0A,0x0F5F002, 0x04,0x00, 0x0, +0 }, + { 0x035F813,0x004FF11, 0x12,0x00, 0x8, +0 }, + { 0x100FF22,0x10BF020, 0x92,0x00, 0x4, +0 }, + { 0x00FFF24,0x00FFF21, 0x00,0x40, 0x1, +0 }, + { 0x0F0FB3E,0x09BA071, 0x29,0x00, 0x0, +0 }, + { 0x275F602,0x066F521, 0x9B,0x00, 0x4, +0 }, + { 0x315EF11,0x0B5F481, 0x53,0x00, 0x8, +0 }, + { 0x10BF224,0x00B5231, 0x50,0x00, 0xE, +0 }, + { 0x000EA36,0x003D01A, 0x8B,0x00, 0x8, +0 }, + { 0x1C3C223,0x103D000, 0x14,0x00, 0xC, +0 }, + { 0x001F211,0x0B1F215, 0x0D,0x0D, 0xA, +0 }, + { 0x0AFF832,0x07FF310, 0x45,0x00, 0xE, +0 }, + { 0x153F101,0x274F111, 0x49,0x00, 0x6, +0 }, + { 0x0F7F000,0x00687A1, 0x30,0x00, 0xF, +0 }, + { 0x0009F71,0x1069F62, 0x45,0x00, 0x2, +0 }, + { 0x0009F71,0x1069062, 0x51,0x00, 0x0, +0 }, + { 0x275F602,0x066F521, 0x1B,0x00, 0x4, +0 }, + { 0x0F7F001,0x00687A1, 0x00,0x00, 0x1, +0 }, + { 0x141B403,0x03FF311, 0x5E,0x00, 0xA, +0 }, + { 0x141B203,0x097F211, 0x5E,0x00, 0xA, +0 }, + { 0x101F901,0x0F5F001, 0x34,0x00, 0x4, +0 }, + { 0x0EFF201,0x078F501, 0x1D,0x00, 0xA, +0 }, + { 0x1EFF201,0x078F501, 0x1D,0x00, 0x6, +0 }, + { 0x01774E1,0x01765E2, 0x83,0x00, 0x7, +0 }, + { 0x154F103,0x054F10A, 0x00,0x00, 0x0, +0 }, + { 0x001EF81,0x0FB9201, 0x8E,0x00, 0x4, +0 }, + { 0x000EA36,0x024DF1A, 0x8B,0x00, 0x8, +0 }, + { 0x061F217,0x076F212, 0x4F,0x00, 0x8, +0 }, + { 0x2298432,0x0448421, 0x1A,0x00, 0x6, +0 }, + { 0x0176EB1,0x00E8B22, 0xC5,0x05, 0x2, +0 }, + { 0x01572A1,0x02784A1, 0x1C,0x00, 0xE, +0 }, + { 0x0427887,0x0548594, 0x4D,0x00, 0xA, +0 }, + { 0x011F111,0x0B3F101, 0x4A,0x85, 0x6, +0 }, + { 0x0115172,0x11552A2, 0x89,0x00, 0xA, +0 }, + { 0x2F3F021,0x004F021, 0x4F,0x00, 0x6, +0 }, + { 0x095AB0E,0x0C6F702, 0xC0,0x00, 0xE, +0 }, + { 0x00351B2,0x01352A2, 0x1C,0x05, 0xE, +0 }, + { 0x01152B0,0x0FE31B1, 0xC5,0x40, 0x0, +0 }, + { 0x0B69401,0x0268300, 0x00,0x00, 0x0, +0 }, + { 0x075F502,0x0F3F201, 0x29,0x83, 0x0, +0 }, + { 0x243A321,0x022C411, 0x11,0x00, 0xC, +0 }, + { 0x01FF201,0x088F501, 0x11,0x00, 0xA, +0 }, + { 0x07D8207,0x07D8214, 0x8F,0x80, 0xC, +0 }, + { 0x00BF224,0x00B5231, 0x4F,0x00, 0xE, +0 }, + { 0x025DC03,0x009F031, 0x90,0x00, 0x8, +0 }, + { 0x02F2501,0x06C6521, 0x15,0x80, 0xA, +0 }, + { 0x0176E30,0x12F8B32, 0x4B,0x05, 0x4, +0 }, + { 0x08F7461,0x02A6561, 0x27,0x00, 0x2, +0 }, + { 0x0EBFA10,0x0DAFA0E, 0x00,0x00, 0x0, +0 }, + { 0x0F7F0F5,0x0068771, 0x2E,0x00, 0xB, +0 }, + { 0x0537101,0x07C5212, 0x4F,0x00, 0xA, +0 }, + { 0x3DFFF20,0x20FFF21, 0x00,0x00, 0x0, +0 }, + { 0x000FF24,0x00BF020, 0x97,0x00, 0x4, +0 }, + { 0x0176EB1,0x00E8BA2, 0xC5,0x05, 0x2, +0 }, + { 0x019D530,0x01B6171, 0xCD,0x40, 0xC, +0 }, + { 0x203B122,0x005F172, 0x4F,0x00, 0x2, +0 }, + { 0x0F16000,0x0F87001, 0x1D,0x00, 0xE, +0 }, + { 0x1009F71,0x1069F22, 0x45,0x00, 0x2, +0 }, + { 0x01D5321,0x03B5261, 0x1C,0x80, 0xC, +0 }, + { 0x01F41B1,0x03B9261, 0x1C,0x80, 0xE, +0 }, + { 0x05A5321,0x01AAA21, 0x9F,0x80, 0xC, +0 }, + { 0x0078061,0x0077062, 0x80,0x00, 0x7, +0 }, + { 0x2D3B121,0x0149121, 0x4F,0x80, 0x6, +0 }, + { 0x1F27021,0x0F68021, 0x14,0x00, 0xE, +0 }, + { 0x2129A16,0x0039A12, 0x97,0x00, 0x2, +0 }, + { 0x01FF003,0x019F000, 0x1F,0x05, 0xA, +0 }, + { 0x204D983,0x004D081, 0x17,0x00, 0xE, +0 }, + { 0x025DA05,0x015F901, 0x8E,0x00, 0xA, +0 }, + { 0x112AA83,0x1119B91, 0x1C,0x00, 0xE, +0 }, + { 0x001FF64,0x0F3F53E, 0xDB,0xC0, 0x4, +0 }, + { 0x0AC9051,0x1F4F071, 0x1A,0x00, 0xF, +0 }, + { 0x22F5570,0x31E87E0, 0x16,0x80, 0xC, +0 }, + { 0x08F6EA0,0x02A65E1, 0xEC,0x00, 0xE, +0 }, + { 0x0EFE800,0x0FFA500, 0x0D,0x00, 0x6, +0 }, + { 0x102FD16,0x0039F12, 0x96,0x80, 0xE, +0 }, + { 0x035F803,0x004FF01, 0x12,0x00, 0x8, +0 }, + { 0x006FA15,0x025F501, 0xD3,0x00, 0xA, +0 }, + { 0x2129A16,0x0019A12, 0x97,0x00, 0x2, +0 }, + { 0x0F0E029,0x031FF1E, 0x1A,0x00, 0x6, +0 }, + { 0x0056581,0x0743251, 0x83,0x00, 0xA, +0 }, + { 0x2129FD6,0x0F290D2, 0x17,0x00, 0x2, +0 }, + { 0x0F0F000,0x0048C2C, 0x2E,0x00, 0xE, +0 }, + { 0x0111E00,0x0A11220, 0x00,0x00, 0x6, +0 }, + { 0x054F10F,0x054F60F, 0x00,0x00, 0xE, +0 }, + { 0x069F000,0x0FFF633, 0x00,0x00, 0xE, +0 }, + { 0x0F00000,0x0F00000, 0x3F,0x3F, 0x0, +0 }, + { 0x04CA800,0x045D600, 0x0B,0x00, 0x0, +0 }, + { 0x04CA800,0x04FD600, 0x0B,0x00, 0x0, +0 }, + { 0x0F0F31E,0x0F6F610, 0x00,0x00, 0xE, +0 }, + { 0x00FFF2E,0x04CF600, 0x00,0x18, 0xE, +0 }, + { 0x0BFFA01,0x0B6D602, 0x00,0x00, 0x8, +0 }, + { 0x3F6F01E,0x307F01E, 0x00,0x00, 0xE, +0 }, + { 0x30AFF2E,0x306FF1E, 0x00,0x00, 0xE, +0 }, + { 0x0F0F31E,0x0F4F410, 0x00,0x00, 0xE, +0 }, + { 0x3F6F61E,0x302F21E, 0x00,0x0C, 0xE, +0 }, + { 0x1FBFA1E,0x102F21E, 0x00,0x04, 0xE, +0 }, + { 0x0BFFA11,0x0BFDA02, 0x34,0x00, 0x8, +0 }, + { 0x001FFEF,0x0F3F53E, 0xCD,0xC0, 0xE, +0 }, + { 0x16FAA12,0x006FF06, 0x14,0x00, 0x0, +0 }, + { 0x16FAA12,0x008FF06, 0x14,0x00, 0x0, +0 }, + { 0x0FFFB13,0x0FFE804, 0x40,0x00, 0x8, +0 }, + { 0x26EF800,0x03FF600, 0x08,0x02, 0x0, +0 }, + { 0x26EF800,0x034F400, 0x08,0x02, 0x0, +0 }, + { 0x16FAA12,0x006FF06, 0x00,0x00, 0x0, +0 }, + { 0x0F0E029,0x03FF21E, 0x1A,0x00, 0x6, +0 }, + { 0x0F0E029,0x0FFF13E, 0x1A,0x00, 0x6, +0 }, + { 0x050B233,0x1F5B131, 0x5A,0x00, 0x0, +0 }, + { 0x153F231,0x0F5F111, 0x49,0x03, 0x6, +0 }, + { 0x183D131,0x0F5C132, 0x95,0x03, 0xC, +0 }, + { 0x163F334,0x1F59211, 0x9B,0x00, 0x0, +0 }, + { 0x2B7F827,0x0F9F191, 0x28,0x00, 0x0, +0 }, + { 0x1EEF31A,0x0F5F111, 0x2D,0x00, 0x0, +0 }, + { 0x158F235,0x1F68132, 0x95,0x02, 0xE, +0 }, + { 0x040C931,0x1B9C235, 0x85,0x00, 0x0, +0 }, + { 0x064C709,0x035B201, 0x15,0x05, 0x9, +0 }, + { 0x144F406,0x034F201, 0x03,0x1B, 0x1, +0 }, + { 0x124A904,0x074F501, 0x06,0x01, 0xB, +0 }, + { 0x033F6D4,0x0E361F1, 0x00,0x00, 0x1, +0 }, + { 0x0E8F7D4,0x064A4D1, 0x00,0x00, 0x5, +0 }, + { 0x0F7F736,0x0F5B531, 0x16,0x07, 0x0, +0 }, + { 0x043A203,0x074F300, 0x1B,0x00, 0xA, +0 }, + { 0x135F8C3,0x194C311, 0x8E,0x00, 0x0, +0 }, + { 0x11BF4E2,0x10DF4E0, 0x07,0x00, 0x7, +0 }, + { 0x02CF6F2,0x10BF5F0, 0x00,0x00, 0x5, +0 }, + { 0x015B6F1,0x007BFF0, 0x06,0x00, 0xB, +0 }, + { 0x1167922,0x1086DE0, 0x03,0x00, 0x9, +0 }, + { 0x0066331,0x1175172, 0x27,0x00, 0x0, +0 }, + { 0x11653B4,0x1175171, 0x1D,0x00, 0xE, +0 }, + { 0x0159725,0x1085332, 0x29,0x00, 0x0, +0 }, + { 0x0156724,0x1065331, 0x9E,0x00, 0xE, +0 }, + { 0x1B4A313,0x0F8D231, 0x27,0x00, 0x4, +0 }, + { 0x032F317,0x1C7E211, 0xA3,0x00, 0x0, +0 }, + { 0x1C1D233,0x09CF131, 0x24,0x00, 0xE, +0 }, + { 0x044F831,0x1C9F232, 0x05,0x02, 0x0, +0 }, + { 0x07B9C21,0x0FB9502, 0x09,0x03, 0x6, +0 }, + { 0x1988121,0x059A121, 0x84,0x04, 0x6, +0 }, + { 0x04378B1,0x3FC9122, 0x0C,0x03, 0x0, +0 }, + { 0x08C8200,0x0ECB408, 0x0A,0x02, 0x8, +0 }, + { 0x046AB21,0x0F79321, 0x13,0x00, 0x0, +0 }, + { 0x032F901,0x058C122, 0x0A,0x04, 0x0, +0 }, + { 0x077FA21,0x06AC322, 0x07,0x02, 0xA, +0 }, + { 0x0577121,0x0876221, 0x17,0x00, 0xA, +0 }, + { 0x178FA25,0x097F312, 0x01,0x00, 0x6, +0 }, + { 0x088FA21,0x097B313, 0x03,0x00, 0xC, +0 }, + { 0x17FF521,0x0CCF323, 0x09,0x04, 0x8, +0 }, + { 0x09BA301,0x0AA9301, 0x10,0x00, 0x8, +0 }, + { 0x129F6E2,0x10878E1, 0x19,0x00, 0xC, +0 }, + { 0x129F6E2,0x10878E1, 0x1C,0x00, 0xC, +0 }, + { 0x1166961,0x1275461, 0x19,0x00, 0xA, +0 }, + { 0x1318271,0x0566132, 0x18,0x00, 0xC, +0 }, + { 0x10670E2,0x11675E1, 0x23,0x00, 0xC, +0 }, + { 0x0E68802,0x1F6F561, 0x00,0x00, 0x9, +0 }, + { 0x1D5F612,0x0E3F311, 0x20,0x80, 0xE, +0 }, + { 0x1F4F461,0x0F5B500, 0x0E,0x00, 0x0, +0 }, + { 0x1049C61,0x0167121, 0x1E,0x80, 0xE, +0 }, + { 0x2D6C0A2,0x1553021, 0x2A,0x00, 0xE, +0 }, + { 0x1357261,0x1366261, 0x21,0x00, 0xE, +0 }, + { 0x1237221,0x0075121, 0x1A,0x02, 0xE, +0 }, + { 0x03197E1,0x0396261, 0x16,0x00, 0x8, +0 }, + { 0x0457922,0x0276621, 0xC3,0x00, 0x0, +0 }, + { 0x1556321,0x0467321, 0xDE,0x00, 0x0, +0 }, + { 0x0F78642,0x1767450, 0x05,0x00, 0xB, +0 }, + { 0x0026131,0x0389261, 0x1C,0x81, 0xE, +0 }, + { 0x0235271,0x0197161, 0x1E,0x02, 0xE, +0 }, + { 0x0167621,0x0098121, 0x1A,0x01, 0xE, +0 }, + { 0x22C8925,0x24B8320, 0x28,0x00, 0x6, +0 }, + { 0x0167921,0x05971A2, 0x1F,0x05, 0x8, +0 }, + { 0x0168721,0x0398221, 0x19,0x03, 0xE, +0 }, + { 0x0357521,0x0178422, 0x17,0x82, 0xE, +0 }, + { 0x0586221,0x0167221, 0x22,0x02, 0xE, +0 }, + { 0x10759B1,0x00A7BA1, 0x1B,0x00, 0x0, +0 }, + { 0x0049F21,0x10C8521, 0x16,0x00, 0xA, +0 }, + { 0x020A821,0x10A7B23, 0x0F,0x00, 0xC, +0 }, + { 0x0048821,0x1187926, 0x0F,0x00, 0x8, +0 }, + { 0x0058F31,0x0087332, 0x18,0x01, 0x0, +0 }, + { 0x1378CA1,0x00A7724, 0x0A,0x04, 0x0, +0 }, + { 0x067A831,0x0195175, 0x04,0x00, 0xA, +0 }, + { 0x12677A2,0x0097421, 0x1F,0x01, 0x0, +0 }, + { 0x194B8E1,0x0286321, 0x07,0x01, 0x0, +0 }, + { 0x05987A1,0x00A65E1, 0x93,0x00, 0x0, +0 }, + { 0x0389F22,0x0296761, 0x10,0x00, 0x0, +0 }, + { 0x19A88E2,0x0096721, 0x0D,0x00, 0x0, +0 }, + { 0x09498A2,0x0286A21, 0x10,0x01, 0xE, +0 }, + { 0x02686F1,0x02755F1, 0x1C,0x00, 0xE, +0 }, + { 0x0099FE1,0x0086FE1, 0x3F,0x00, 0x1, +0 }, + { 0x019F7E2,0x0077A21, 0x3B,0x00, 0x0, +0 }, + { 0x00C9222,0x00DA261, 0x1E,0x06, 0xE, +0 }, + { 0x122F421,0x05FA321, 0x15,0x00, 0xE, +0 }, + { 0x16647F2,0x02742F1, 0x20,0x00, 0x2, +0 }, + { 0x0288861,0x049B261, 0x19,0x05, 0xE, +0 }, + { 0x01B8221,0x179B223, 0x16,0x00, 0x0, +0 }, + { 0x093CA21,0x01A7A22, 0x00,0x00, 0x0, +0 }, + { 0x1C99223,0x1288222, 0x00,0x00, 0x9, +0 }, + { 0x07BF321,0x05FC322, 0x1D,0x02, 0xE, +0 }, + { 0x12581E1,0x195C4A6, 0x00,0x86, 0x1, +0 }, + { 0x0013121,0x0154421, 0x27,0x00, 0xE, +0 }, + { 0x2358360,0x006D161, 0x14,0x00, 0xC, +0 }, + { 0x101D3E1,0x0378262, 0x5C,0x00, 0x0, +0 }, + { 0x2863428,0x0354121, 0x38,0x00, 0x0, +0 }, + { 0x1F35224,0x1F53223, 0x12,0x02, 0x4, +0 }, + { 0x0A66261,0x02661A1, 0x1D,0x00, 0xA, +0 }, + { 0x1D52222,0x1053F21, 0x0F,0x84, 0xA, +0 }, + { 0x024F9E3,0x0F6D131, 0x1F,0x01, 0x0, +0 }, + { 0x1554163,0x10541A2, 0x00,0x00, 0x7, +0 }, + { 0x165A7C7,0x0E4F3C1, 0x25,0x05, 0x0, +0 }, + { 0x1B7F7E3,0x1F59261, 0x19,0x00, 0x0, +0 }, + { 0x044A866,0x1E4F241, 0x9B,0x04, 0xE, +0 }, + { 0x0752261,0x0254561, 0x20,0x00, 0xC, +0 }, + { 0x084F6E1,0x036A3E1, 0x21,0x01, 0xE, +0 }, + { 0x16473E2,0x10598E1, 0x14,0x01, 0xA, +0 }, + { 0x0347221,0x1F6A324, 0x0B,0x02, 0x8, +0 }, + { 0x053F421,0x0F8F604, 0x16,0x00, 0xC, +0 }, + { 0x002DA21,0x0F5F335, 0x18,0x00, 0xC, +0 }, + { 0x063FA25,0x1E59402, 0x0F,0x00, 0x8, +0 }, + { 0x096F932,0x0448411, 0x07,0x00, 0x0, +0 }, + { 0x2189720,0x1188325, 0x0E,0x03, 0x8, +0 }, + { 0x029F661,0x1087862, 0x18,0x01, 0x0, +0 }, + { 0x01976E6,0x1088E61, 0x21,0x03, 0xA, +0 }, + { 0x0D4F027,0x046F205, 0x23,0x09, 0x0, +0 }, + { 0x131F91C,0x1E89615, 0x0C,0x00, 0xE, +0 }, + { 0x2167502,0x1F6F601, 0x00,0x00, 0x7, +0 }, + { 0x093F502,0x045C600, 0x1D,0x00, 0x0, +0 }, + { 0x032F511,0x0B4F410, 0x15,0x00, 0x4, +0 }, + { 0x099FA22,0x025D501, 0x06,0x00, 0x8, +0 }, + { 0x200FF2E,0x02D210E, 0x00,0x0E, 0xE, +0 }, + { 0x1E45630,0x2875517, 0x0B,0x00, 0x0, +0 }, + { 0x003FF24,0x1879805, 0x00,0x08, 0xC, +0 }, + { 0x200F00E,0x304170A, 0x00,0x04, 0xE, +0 }, + { 0x0F7F620,0x2F9770E, 0x08,0x05, 0x0, +0 }, + { 0x008F120,0x008F42E, 0x14,0x02, 0x0, +0 }, + { 0x100F220,0x1053623, 0x04,0x00, 0x2, +0 }, + { 0x002FF2E,0x355322A, 0x00,0x05, 0xE, +0 }, + { 0x00F9F3E,0x0FA8730, 0x00,0x00, 0xE, +0 }, + { 0x0977801,0x3988802, 0x00,0x00, 0x8, +0 }, + { 0x0FBF116,0x069F911, 0x08,0x00, 0x0, +0 }, + { 0x06CF800,0x04AE80E, 0x00,0x80, 0x0, +0 }, + { 0x0F3F900,0x08AF701, 0x00,0x00, 0x4, +0 }, + { 0x0FDFA01,0x047F601, 0x07,0x00, 0x4, +0 }, + { 0x000FF24,0x0A9F702, 0x00,0x00, 0xE, +0 }, + { 0x01FFA06,0x0F5F511, 0x0A,0x00, 0xD, +0 }, + { 0x0FEF22C,0x3D8B802, 0x00,0x06, 0x6, +0 }, + { 0x0F6822E,0x3F87404, 0x00,0x10, 0x4, +0 }, + { 0x2009F2C,0x3D4C50E, 0x00,0x05, 0xE, +0 }, + { 0x0F1F52E,0x3F78706, 0x09,0x03, 0x0, +0 }, + { 0x1A1F737,0x028F603, 0x14,0x00, 0x8, +0 }, + { 0x0FAFA25,0x0F99903, 0xC4,0x00, 0x0, +0 }, + { 0x1FAFB21,0x0F7A802, 0x03,0x00, 0x0, +0 }, + { 0x2FAF924,0x0F6A603, 0x18,0x00, 0xE, +0 }, + { 0x2F5F505,0x236F603, 0x14,0x00, 0x6, +0 }, + { 0x107AF20,0x22BA50E, 0x15,0x00, 0x4, +0 }, + { 0x107BF20,0x23B930E, 0x18,0x00, 0x0, +0 }, + { 0x0F7F020,0x33B8908, 0x00,0x01, 0xA, +0 }, + { 0x0FAF320,0x22B5308, 0x00,0x0A, 0x8, +0 }, + { 0x19AF815,0x089F613, 0x21,0x00, 0x8, +0 }, + { 0x0075F20,0x14B8708, 0x01,0x00, 0x0, +0 }, + { 0x1F75725,0x1677803, 0x12,0x00, 0x0, +0 }, + { 0x0F0F122,0x0FCF827, 0x2F,0x02, 0x6, +0 }, + { 0x0E5AD37,0x1A58211, 0x40,0x00, 0x0, +0 }, + { 0x053F335,0x1F5F111, 0xDA,0x03, 0x0, +0 }, + { 0x163F435,0x1F5F211, 0xCF,0x03, 0x0, +0 }, + { 0x163F374,0x1F5F251, 0xD3,0x03, 0x0, +0 }, + { 0x0F7F201,0x2C9F887, 0x06,0x15, 0x5, +0 }, + { 0x08EF63C,0x0F5F131, 0x1B,0x09, 0x0, +0 }, + { 0x20AFAB2,0x1F7C231, 0x15,0x05, 0xC, +0 }, + { 0x020F831,0x1DCF236, 0x0F,0x04, 0x0, +0 }, + { 0x234F825,0x085F401, 0xA2,0x07, 0x6, +0 }, + { 0x226F6C2,0x075A501, 0x05,0x05, 0x9, +0 }, + { 0x131F6F5,0x0E3F1F1, 0x2A,0x02, 0x0, +0 }, + { 0x0F8F8F8,0x064E4D1, 0x1A,0x07, 0xC, +0 }, + { 0x0F7F73C,0x0F5F531, 0x0C,0x06, 0x9, +0 }, + { 0x0F0B022,0x0F4C425, 0x21,0x08, 0x0, +0 }, + { 0x136F8C5,0x194C311, 0x09,0x06, 0x0, +0 }, + { 0x11BF4E2,0x11DD4E0, 0x08,0x04, 0x1, +0 }, + { 0x04CF7F2,0x00BF5F0, 0x02,0x04, 0x1, +0 }, + { 0x13DF4E0,0x13BF5E0, 0x03,0x00, 0x7, +0 }, + { 0x1166722,0x1086DE0, 0x09,0x05, 0xB, +0 }, + { 0x0066331,0x1175172, 0x27,0x04, 0x0, +0 }, + { 0x11653B4,0x1175171, 0x1B,0x06, 0xE, +0 }, + { 0x1057824,0x1085333, 0x1E,0x09, 0x0, +0 }, + { 0x11653B3,0x1175172, 0x1F,0x05, 0x0, +0 }, + { 0x127F833,0x0F8F231, 0x23,0x04, 0xE, +0 }, + { 0x132F418,0x1A7E211, 0x26,0x03, 0x0, +0 }, + { 0x0C1A233,0x09CB131, 0x9D,0x85, 0x8, +0 }, + { 0x1F4F335,0x1C9F232, 0x16,0x07, 0xA, +0 }, + { 0x07B9C21,0x0FB9402, 0x12,0x03, 0xA, +0 }, + { 0x24C8120,0x17AF126, 0x06,0x0C, 0x0, +0 }, + { 0x28B7120,0x378F120, 0x11,0x06, 0x0, +0 }, + { 0x38C7205,0x19CE203, 0x13,0x0A, 0x4, +0 }, + { 0x0B6AF31,0x0F78331, 0x00,0x00, 0x0, +0 }, + { 0x068F321,0x0FCC121, 0x17,0x06, 0x8, +0 }, + { 0x077FB21,0x06AC322, 0x00,0x03, 0x8, +0 }, + { 0x047A131,0x0878231, 0x97,0x84, 0xA, +0 }, + { 0x0A8FA25,0x197F312, 0x0D,0x00, 0x8, +0 }, + { 0x06CFA21,0x0FCF334, 0x05,0x07, 0xC, +0 }, + { 0x17FF521,0x0CCF322, 0x17,0x03, 0xE, +0 }, + { 0x09BA301,0x0AA9301, 0x13,0x04, 0xA, +0 }, + { 0x129F6E2,0x10878E1, 0x19,0x05, 0xC, +0 }, + { 0x129F6E2,0x10878E1, 0x1C,0x03, 0xC, +0 }, + { 0x0099861,0x1087E61, 0x20,0x03, 0xC, +0 }, + { 0x1017171,0x05651F1, 0x1E,0x06, 0xE, +0 }, + { 0x10670E2,0x11675E1, 0x23,0x04, 0xC, +0 }, + { 0x0E69802,0x0F6F521, 0x05,0x07, 0x9, +0 }, + { 0x075F602,0x0C5F401, 0x2A,0x82, 0xE, +0 }, + { 0x1BABF61,0x0468501, 0x40,0x00, 0x0, +0 }, + { 0x195CCE1,0x12850E1, 0x00,0x00, 0x0, +0 }, + { 0x2D6C0E2,0x15530E1, 0x27,0x09, 0xE, +0 }, + { 0x1556261,0x1566261, 0x26,0x03, 0xE, +0 }, + { 0x16372A1,0x00751A1, 0x18,0x07, 0xE, +0 }, + { 0x145B822,0x0278621, 0xD2,0x02, 0x0, +0 }, + { 0x1556321,0x0467321, 0xDE,0x05, 0x0, +0 }, + { 0x0F78642,0x1767450, 0x0A,0x00, 0xD, +0 }, + { 0x0026131,0x0388261, 0x1F,0x87, 0xE, +0 }, + { 0x0135571,0x0197061, 0x20,0x0B, 0xE, +0 }, + { 0x0166621,0x0097121, 0x1C,0x06, 0xE, +0 }, + { 0x21C7824,0x14B9321, 0x19,0x84, 0x0, +0 }, + { 0x0167921,0x05971A1, 0x21,0x03, 0xC, +0 }, + { 0x0358221,0x0388221, 0x1B,0x07, 0xE, +0 }, + { 0x0357221,0x0378222, 0x1A,0x87, 0xE, +0 }, + { 0x0586221,0x0167221, 0x23,0x06, 0xE, +0 }, + { 0x10759F1,0x00A7B61, 0x1B,0x06, 0x0, +0 }, + { 0x0049F21,0x10C8521, 0x16,0x07, 0xA, +0 }, + { 0x010B821,0x1DC72A6, 0x04,0x04, 0x8, +0 }, + { 0x0096831,0x1086334, 0x0B,0x09, 0x6, +0 }, + { 0x1058F31,0x00B5333, 0x14,0x16, 0x0, +0 }, + { 0x1079FA1,0x00A7724, 0x1D,0x08, 0xA, +0 }, + { 0x009D531,0x01D6175, 0x1B,0x4C, 0xA, +0 }, + { 0x0076172,0x01B6223, 0x26,0x10, 0xE, +0 }, + { 0x194A8E1,0x0086221, 0x0F,0x04, 0x0, +0 }, + { 0x00986F1,0x00B75E1, 0x9C,0x0B, 0x0, +0 }, + { 0x008DF22,0x0297761, 0x2C,0x03, 0x0, +0 }, + { 0x27A88E2,0x0097721, 0x2C,0x00, 0x0, +0 }, + { 0x05488E2,0x0087721, 0x17,0x0B, 0xE, +0 }, + { 0x02686F1,0x02755F1, 0x1F,0x04, 0xE, +0 }, + { 0x0099FE1,0x0086FE1, 0x3F,0x05, 0x1, +0 }, + { 0x004A822,0x0096A21, 0xE6,0x05, 0x0, +0 }, + { 0x00C9222,0x00DA261, 0x1B,0x0A, 0xE, +0 }, + { 0x122F461,0x05FA361, 0x15,0x04, 0xE, +0 }, + { 0x10ABB21,0x0096FA1, 0xD2,0x03, 0xC, +0 }, + { 0x0387761,0x0499261, 0x17,0x09, 0x8, +0 }, + { 0x21D7120,0x178F124, 0x08,0x05, 0x0, +0 }, + { 0x193CA21,0x01A7A21, 0x00,0x03, 0x0, +0 }, + { 0x1C99223,0x1089122, 0x06,0x08, 0xB, +0 }, + { 0x01BF321,0x05FE122, 0x1D,0x04, 0xE, +0 }, + { 0x15562E1,0x125FAC8, 0x01,0x0B, 0x5, +0 }, + { 0x0012161,0x01534E1, 0x26,0x02, 0xE, +0 }, + { 0x0358361,0x106D161, 0x19,0x02, 0xC, +0 }, + { 0x101D3E1,0x0378262, 0xDC,0x82, 0x0, +0 }, + { 0x166446A,0x0365161, 0x33,0x04, 0x0, +0 }, + { 0x0F38262,0x1F53261, 0x0B,0x06, 0x4, +0 }, + { 0x1766261,0x02661A1, 0x9A,0x04, 0xC, +0 }, + { 0x1D52222,0x1053F21, 0x13,0x06, 0xA, +0 }, + { 0x0F4F2E1,0x0F69121, 0x9C,0x05, 0xE, +0 }, + { 0x1554163,0x10541A2, 0x0A,0x06, 0xB, +0 }, + { 0x005F604,0x0E5F301, 0x18,0x0E, 0x0, +0 }, + { 0x196F9E3,0x1F5C261, 0x10,0x00, 0x8, +0 }, + { 0x1C6A144,0x1E5B241, 0xD2,0x06, 0xE, +0 }, + { 0x1772261,0x0264561, 0x94,0x05, 0xE, +0 }, + { 0x184F5E1,0x036A2E1, 0x19,0x07, 0xE, +0 }, + { 0x16473E2,0x10598E1, 0x14,0x07, 0xA, +0 }, + { 0x0348321,0x1F6C324, 0x0B,0x09, 0x8, +0 }, + { 0x19AFB25,0x1F7F432, 0x00,0x03, 0x0, +0 }, + { 0x002DA21,0x0F5F335, 0x1B,0x04, 0xC, +0 }, + { 0x034F763,0x1E5F301, 0x4E,0x05, 0x0, +0 }, + { 0x296F931,0x0F6F531, 0x0F,0x04, 0xA, +0 }, + { 0x1176731,0x01A7325, 0x17,0x0A, 0xE, +0 }, + { 0x129F6E1,0x20868E2, 0x15,0x07, 0x0, +0 }, + { 0x019A6E6,0x1088E61, 0x23,0x05, 0x0, +0 }, + { 0x0D4F027,0x046F205, 0x23,0x0C, 0x0, +0 }, + { 0x1167504,0x1F6C601, 0x07,0x00, 0x5, +0 }, + { 0x033F731,0x085F510, 0x19,0x00, 0x0, +0 }, + { 0x089FA22,0x025F501, 0x0F,0x05, 0xE, +0 }, + { 0x200FF2E,0x02D210E, 0x00,0x18, 0xE, +0 }, + { 0x0F45630,0x2875517, 0x00,0x00, 0x8, +0 }, + { 0x003FF20,0x3967604, 0x00,0x06, 0xE, +0 }, + { 0x200F00E,0x304170A, 0x00,0x13, 0xE, +0 }, + { 0x007F020,0x2F9920E, 0x0C,0x08, 0x0, +0 }, + { 0x008F120,0x008F42E, 0x14,0x08, 0x0, +0 }, + { 0x100F220,0x0052423, 0x09,0x05, 0xE, +0 }, + { 0x002FF2E,0x325332E, 0x00,0x0A, 0xE, +0 }, + { 0x0DF8120,0x0DFF310, 0x00,0x03, 0xE, +0 }, + { 0x1FCF720,0x04AF80A, 0x00,0x00, 0x6, +0 }, + { 0x053F600,0x07AF710, 0x0C,0x00, 0x0, +0 }, + { 0x0FEF227,0x3D8980A, 0x00,0x0C, 0x8, +0 }, + { 0x0F8F128,0x3667606, 0x00,0x0A, 0xC, +0 }, + { 0x050F335,0x1F5F111, 0x69,0x02, 0x0, +0 }, + { 0x2B49230,0x208A421, 0x0F,0x00, 0xC, +0 }, + { 0x0A7FB2C,0x0C9F281, 0x16,0x08, 0x0, +0 }, + { 0x08EA43A,0x085A131, 0x35,0x07, 0xC, +0 }, + { 0x0F7F838,0x0F5F537, 0x13,0x06, 0x8, +0 }, + { 0x061C21A,0x072C212, 0x18,0x03, 0x6, +0 }, + { 0x136F8C2,0x194C311, 0x03,0x03, 0x0, +0 }, + { 0x34FFAE1,0x11AD4E0, 0x07,0x07, 0x1, +0 }, + { 0x13DF9E3,0x03BF5E0, 0x00,0x00, 0x0, +0 }, + { 0x1F62334,0x1173131, 0x1E,0x06, 0xE, +0 }, + { 0x1F2F235,0x1A7E211, 0x02,0x03, 0x0, +0 }, + { 0x084FA37,0x1C9F232, 0x09,0x00, 0x0, +0 }, + { 0x3CEFA21,0x0FBF403, 0x03,0x00, 0x0, +0 }, + { 0x2989120,0x159B125, 0x06,0x06, 0x0, +0 }, + { 0x073F9A1,0x3FCA120, 0x0D,0x04, 0xA, +0 }, + { 0x036F821,0x0F7C123, 0x11,0x00, 0x8, +0 }, + { 0x017F821,0x0FAF223, 0x9E,0x00, 0xE, +0 }, + { 0x146F821,0x006C322, 0x0C,0x07, 0x6, +0 }, + { 0x047F531,0x087F233, 0x96,0x80, 0xA, +0 }, + { 0x0B8FA21,0x077F412, 0x04,0x07, 0x0, +0 }, + { 0x08CF921,0x0FCF334, 0x05,0x00, 0x0, +0 }, + { 0x066F801,0x1F6F521, 0x08,0x06, 0x8, +0 }, + { 0x09BF501,0x0AAF302, 0x19,0x04, 0xC, +0 }, + { 0x124F661,0x2065860, 0x17,0x0B, 0xE, +0 }, + { 0x006F701,0x3F6F720, 0x19,0x08, 0xE, +0 }, + { 0x1F4F461,0x0F5B500, 0x14,0x00, 0x0, +0 }, + { 0x104F6E1,0x12670E1, 0x23,0x05, 0xE, +0 }, + { 0x113F221,0x0055121, 0x20,0x09, 0xE, +0 }, + { 0x0026131,0x0388261, 0x1F,0x83, 0xE, +0 }, + { 0x0135571,0x0197061, 0x20,0x06, 0xE, +0 }, + { 0x0157121,0x0177122, 0x1C,0x00, 0xE, +0 }, + { 0x0257521,0x01771A1, 0x21,0x00, 0xC, +0 }, + { 0x0358221,0x0388221, 0x19,0x03, 0xE, +0 }, + { 0x0357221,0x0378222, 0x1A,0x82, 0xE, +0 }, + { 0x1058F31,0x0085333, 0x14,0x0A, 0x0, +0 }, + { 0x009D531,0x01B6175, 0x1B,0x84, 0xA, +0 }, + { 0x0076172,0x0186223, 0x26,0x0A, 0xE, +0 }, + { 0x00986F1,0x00A75E1, 0x9C,0x05, 0x0, +0 }, + { 0x02384F1,0x01655F2, 0x1D,0x00, 0xE, +0 }, + { 0x2D86901,0x0B65701, 0x1B,0x00, 0xC, +0 }, + { 0x0C4FF22,0x0077921, 0x00,0x0D, 0x0, +0 }, + { 0x05FB9A2,0x0FB9121, 0x0B,0x0F, 0xE, +0 }, + { 0x072FA62,0x198F541, 0x09,0x00, 0xC, +0 }, + { 0x21D8120,0x179F125, 0x08,0x05, 0x0, +0 }, + { 0x1C99223,0x1089122, 0x0C,0x0E, 0xD, +0 }, + { 0x01BF321,0x05FE121, 0x1D,0x0A, 0xE, +0 }, + { 0x001F1A1,0x0153421, 0x27,0x07, 0xE, +0 }, + { 0x2A2F120,0x315F321, 0x14,0x12, 0x0, +0 }, + { 0x034D2E8,0x1343261, 0xDD,0x8B, 0x0, +0 }, + { 0x053F265,0x1F33263, 0x0E,0x11, 0x0, +0 }, + { 0x0837222,0x1055221, 0x19,0x05, 0xC, +0 }, + { 0x074F161,0x07441A1, 0x22,0x06, 0xE, +0 }, + { 0x00553A1,0x0F43221, 0x25,0x00, 0xE, +0 }, + { 0x1554163,0x10541A2, 0x0A,0x03, 0xB, +0 }, + { 0x091F010,0x0E7A51E, 0x0C,0x00, 0x0, +0 }, + { 0x2B29130,0x204A121, 0x10,0x00, 0xC, +0 }, + { 0x0D6F662,0x2E5B241, 0x22,0x00, 0xE, +0 }, + { 0x104F021,0x0043221, 0x2B,0x06, 0xE, +0 }, + { 0x06473E4,0x10548E1, 0x25,0x08, 0x0, +0 }, + { 0x156FA23,0x0FBF622, 0x00,0x00, 0x0, +0 }, + { 0x28CFA21,0x1F7F331, 0x13,0x04, 0xC, +0 }, + { 0x0559131,0x3788133, 0x0D,0x02, 0xA, +0 }, + { 0x0411160,0x14431E6, 0x05,0x00, 0x8, +0 }, + { 0x0722121,0x2646129, 0x0D,0x0D, 0x4, +0 }, + { 0x3922220,0x0A44125, 0x84,0x82, 0x8, +0 }, + { 0x1023220,0x3343120, 0x03,0x00, 0xC, +0 }, + { 0x0B5F100,0x0C2D400, 0x0B,0x07, 0xA, +0 }, + { 0x300FF36,0x2F4F41E, 0x09,0x00, 0xE, +0 }, + { 0x0211131,0x0937122, 0x0A,0x02, 0xA, +0 }, + { 0x1728281,0x0743182, 0x0E,0x05, 0xC, +0 }, + { 0x0331221,0x1243122, 0x00,0x00, 0x8, +0 }, + { 0x0F9F700,0x0CA8601, 0x08,0x00, 0x0, +0 }, + { 0x1F3F030,0x1F4F130, 0x54,0x00, 0xA, +12 }, + { 0x0F3F030,0x1F4F130, 0x52,0x00, 0xA, +12 }, + { 0x1F3E130,0x0F4F130, 0x4E,0x00, 0x8, +12 }, + { 0x015E811,0x014F712, 0x00,0x00, 0x1, +12 }, + { 0x153F110,0x0F4D110, 0x4F,0x00, 0x6, +12 }, + { 0x053F111,0x0F4D111, 0x4F,0x00, 0x6, +12 }, + { 0x051F121,0x0E5D231, 0x66,0x00, 0x6, +0 }, + { 0x0E6F130,0x0E5F1B0, 0x51,0x40, 0x6, +12 }, + { 0x079F212,0x099F110, 0x43,0x40, 0x9, +12 }, + { 0x201F230,0x1F4C130, 0x87,0x00, 0x6, +12 }, + { 0x162A190,0x1A79110, 0x8E,0x00, 0xC, +12 }, + { 0x164F228,0x0E4F231, 0x4F,0x00, 0x8, +0 }, + { 0x0119113,0x0347D14, 0x0E,0x00, 0x9, +0 }, + { 0x041F6B2,0x092D290, 0x0F,0x00, 0x0, +12 }, + { 0x0F3F1F0,0x0F4F1F2, 0x02,0x00, 0x1, +12 }, + { 0x0157980,0x275F883, 0x00,0x00, 0x1, +12 }, + { 0x093F614,0x053F610, 0x1F,0x00, 0x8, +12 }, + { 0x113B681,0x013FF02, 0x99,0x00, 0xA, +0 }, + { 0x0119130,0x0535211, 0x47,0x80, 0x8, +12 }, + { 0x016B1A0,0x117D161, 0x88,0x80, 0x7, +12 }, + { 0x105F130,0x036F494, 0x00,0x00, 0x7, +0 }, + { 0x017F2E2,0x107FF60, 0x9E,0x80, 0x0, +0 }, + { 0x117F2E0,0x007FFA0, 0x9E,0x80, 0x0, +12 }, + { 0x0043030,0x1145431, 0x92,0x80, 0x9, +12 }, + { 0x0178000,0x1176081, 0x49,0x80, 0x6, +12 }, + { 0x015A220,0x1264131, 0x48,0x00, 0xA, +12 }, + { 0x0158220,0x1264631, 0x4A,0x00, 0xA, +12 }, + { 0x03460B0,0x01642B2, 0x0C,0x80, 0x8, +12 }, + { 0x105F020,0x2055231, 0x92,0x00, 0x8, +12 }, + { 0x105F020,0x2055231, 0x92,0x00, 0x0, +12 }, + { 0x0F5F120,0x0F6F120, 0x8D,0x00, 0x0, +12 }, + { 0x1E4E130,0x0E3F230, 0x0D,0x00, 0xA, +12 }, + { 0x21FF100,0x088F400, 0x21,0x00, 0xA, +12 }, + { 0x132EA10,0x2E7D210, 0x87,0x00, 0x2, +12 }, + { 0x0F4E030,0x0F5F230, 0x92,0x80, 0x0, +12 }, + { 0x0FFF100,0x1FFF051, 0x10,0x00, 0xA, +12 }, + { 0x0FFF110,0x1FFF051, 0x0D,0x00, 0xC, +12 }, + { 0x297A110,0x0E7E111, 0x43,0x00, 0x0, +12 }, + { 0x020C420,0x0F6C3B0, 0x0E,0x00, 0x0, +12 }, + { 0x0FFF030,0x0F8F131, 0x96,0x00, 0xA, +12 }, + { 0x014E020,0x0D6E130, 0x8F,0x80, 0x8, +12 }, + { 0x14551E1,0x14691A0, 0x4D,0x00, 0x0, +0 }, + { 0x14551A1,0x14681A0, 0x4D,0x00, 0x0, +12 }, + { 0x2E7F030,0x047F131, 0x00,0x00, 0x0, +0 }, + { 0x0E5F030,0x0F5F131, 0x90,0x80, 0x8, +12 }, + { 0x1F5F430,0x0F6F330, 0x0A,0x00, 0xA, +12 }, + { 0x1468330,0x017D231, 0x15,0x00, 0xA, +12 }, + { 0x1455060,0x14661A1, 0x17,0x00, 0x6, +12 }, + { 0x04460F0,0x0154171, 0x8F,0x00, 0x2, +12 }, + { 0x214D0B0,0x1176261, 0x0F,0x80, 0x6, +0 }, + { 0x211B1F0,0x115A020, 0x8A,0x80, 0x6, +12 }, + { 0x201C3F0,0x0058361, 0x89,0x40, 0x6, +0 }, + { 0x201B370,0x1059360, 0x89,0x40, 0x6, +12 }, + { 0x2F9F830,0x0E67620, 0x97,0x00, 0xE, +12 }, + { 0x035F131,0x0B3F320, 0x24,0x00, 0x0, +12 }, + { 0x0C8AA00,0x0B3D210, 0x04,0x00, 0xA, +12 }, + { 0x104C060,0x10455B1, 0x51,0x80, 0x4, +12 }, + { 0x10490A0,0x1045531, 0x52,0x80, 0x6, +12 }, + { 0x1059020,0x10535A1, 0x51,0x80, 0x4, +12 }, + { 0x10590A0,0x1053521, 0x52,0x80, 0x6, +12 }, + { 0x20569A1,0x20266F1, 0x93,0x00, 0xA, +0 }, + { 0x0031121,0x1043120, 0x4D,0x80, 0x0, +12 }, + { 0x2331100,0x1363100, 0x82,0x80, 0x8, +12 }, + { 0x0549060,0x0047060, 0x56,0x40, 0x0, +12 }, + { 0x0549020,0x0047060, 0x92,0xC0, 0x0, +12 }, + { 0x0B7B1A0,0x08572A0, 0x99,0x80, 0x0, +12 }, + { 0x05460B0,0x07430B0, 0x5A,0x80, 0x0, +12 }, + { 0x0433010,0x0146410, 0x90,0x00, 0x2, -12 }, + { 0x0425090,0x0455411, 0x8F,0x00, 0x2, +0 }, + { 0x1158020,0x0365130, 0x8E,0x00, 0xA, +12 }, + { 0x01F71B0,0x03B7220, 0x1A,0x80, 0xE, +12 }, + { 0x0468020,0x1569220, 0x16,0x00, 0xC, +12 }, + { 0x1E68080,0x1F65190, 0x8D,0x00, 0xC, +12 }, + { 0x0B87020,0x0966120, 0x22,0x80, 0xE, +12 }, + { 0x0B87020,0x0966120, 0x23,0x80, 0xE, +12 }, + { 0x1156020,0x0365130, 0x8E,0x00, 0xA, +12 }, + { 0x1177030,0x1366130, 0x92,0x00, 0xE, +12 }, + { 0x2A69120,0x1978120, 0x4D,0x00, 0xC, +12 }, + { 0x2A69120,0x1979120, 0x8C,0x00, 0xC, +12 }, + { 0x2A68130,0x1976130, 0x50,0x00, 0xC, +12 }, + { 0x2A68130,0x1976130, 0x4A,0x00, 0xA, +12 }, + { 0x00560A0,0x11652B1, 0x96,0x00, 0x6, +12 }, + { 0x10670A0,0x11662B0, 0x89,0x00, 0x6, +12 }, + { 0x00B98A0,0x10B73B0, 0x4A,0x00, 0xA, +12 }, + { 0x10B90A0,0x11B63B0, 0x85,0x00, 0xA, +12 }, + { 0x0167070,0x0085CA2, 0x90,0x80, 0x6, +12 }, + { 0x007C820,0x1077331, 0x4F,0x00, 0xA, +12 }, + { 0x0199030,0x01B6131, 0x91,0x80, 0xA, +12 }, + { 0x017A530,0x01763B0, 0x8D,0x80, 0x8, +12 }, + { 0x08F6EF0,0x02A3570, 0x80,0x00, 0xE, +12 }, + { 0x08850A0,0x02A5560, 0x93,0x80, 0x8, +12 }, + { 0x0176520,0x02774A0, 0x0A,0x00, 0xB, +12 }, + { 0x12724B0,0x01745B0, 0x84,0x00, 0x9, +12 }, + { 0x00457E1,0x0375760, 0xAD,0x00, 0xE, +12 }, + { 0x33457F1,0x05D67E1, 0x28,0x00, 0xE, +0 }, + { 0x00F31D0,0x0053270, 0xC7,0x00, 0xB, +12 }, + { 0x00551B0,0x0294230, 0xC7,0x00, 0xB, +12 }, + { 0x15B5122,0x1256030, 0x52,0x00, 0x0, +12 }, + { 0x15B9122,0x125F030, 0x4D,0x00, 0x0, +12 }, + { 0x19BC120,0x165C031, 0x43,0x00, 0x8, +12 }, + { 0x1ABB160,0x005F131, 0x41,0x00, 0x8, +12 }, + { 0x33357F0,0x00767E0, 0x28,0x00, 0xE, +12 }, + { 0x30457E0,0x04D67E0, 0x23,0x00, 0xE, +12 }, + { 0x304F7E0,0x04D87E0, 0x23,0x00, 0xE, +12 }, + { 0x10B78A1,0x12BF130, 0x42,0x00, 0x8, +12 }, + { 0x0558060,0x014F2E0, 0x21,0x00, 0x8, +12 }, + { 0x0559020,0x014A2A0, 0x21,0x00, 0x8, +12 }, + { 0x195C120,0x16370B0, 0x43,0x80, 0xA, +12 }, + { 0x19591A0,0x1636131, 0x49,0x00, 0xA, +7 }, + { 0x1075124,0x229FDA0, 0x40,0x00, 0x9, +0 }, + { 0x0053280,0x0053360, 0xC0,0x00, 0x9, +12 }, + { 0x0053240,0x00533E0, 0x40,0x00, 0x9, +12 }, + { 0x2A5A1A0,0x196A1A0, 0x8F,0x00, 0xC, +12 }, + { 0x005F0E0,0x0548160, 0x44,0x00, 0x1, +12 }, + { 0x105F0E0,0x0547160, 0x44,0x80, 0x1, +12 }, + { 0x033A180,0x05452E0, 0x8A,0x00, 0x7, +12 }, + { 0x1528081,0x1532340, 0x9D,0x80, 0xE, +12 }, + { 0x14551E1,0x14691A0, 0x4D,0x00, 0x0, +12 }, + { 0x15211E1,0x17380E0, 0x8C,0x80, 0x8, +12 }, + { 0x0477220,0x019F883, 0x40,0x00, 0xB, +12 }, + { 0x1028500,0x11245C1, 0xD2,0x00, 0xA, +0 }, + { 0x0034522,0x23535E3, 0xD2,0x00, 0xA, +7 }, + { 0x074F604,0x024A302, 0xC0,0x00, 0x0, -12 }, + { 0x0D2C090,0x0D2D130, 0x8E,0x00, 0x0, +12 }, + { 0x0D2D090,0x0D2F130, 0x8E,0x00, 0x0, +12 }, + { 0x0F390D0,0x0F3C2C0, 0x12,0x00, 0x0, +12 }, + { 0x0F390D0,0x0F2C2C0, 0x12,0x80, 0x0, +12 }, + { 0x15213E0,0x21333F1, 0x1A,0x80, 0x0, +0 }, + { 0x0BA45E0,0x19132F0, 0x1A,0x00, 0x0, +12 }, + { 0x1025810,0x0724202, 0x18,0x00, 0xA, +12 }, + { 0x0B36320,0x0B36324, 0x08,0x00, 0x2, +12 }, + { 0x0127730,0x1F4F310, 0x0D,0x00, 0x4, +12 }, + { 0x033F900,0x273F400, 0x80,0x80, 0x0, +12 }, + { 0x2ACF907,0x229F90F, 0x1A,0x00, 0x0, +12 }, + { 0x153F220,0x0E49122, 0x21,0x00, 0x8, +12 }, + { 0x339F103,0x074D615, 0x4F,0x00, 0x6, +0 }, + { 0x1158930,0x2076B21, 0x42,0x00, 0xA, +12 }, + { 0x003A130,0x0265221, 0x1F,0x00, 0xE, +12 }, + { 0x0134030,0x1166130, 0x13,0x80, 0x8, +12 }, + { 0x032A113,0x172B212, 0x00,0x80, 0x1, +5 }, + { 0x001E795,0x0679616, 0x81,0x00, 0x4, +12 }, + { 0x104F003,0x0058220, 0x49,0x00, 0x6, +12 }, + { 0x0D1F813,0x078F512, 0x44,0x00, 0x6, +12 }, + { 0x0ECA710,0x0F5D510, 0x0B,0x00, 0x0, +0 }, + { 0x0C8A820,0x0B7D601, 0x0B,0x00, 0x0, +0 }, + { 0x0C4F800,0x0B7D300, 0x0B,0x00, 0x0, +12 }, + { 0x031410C,0x31D2110, 0x8F,0x80, 0xE, +0 }, + { 0x1B33432,0x3F75431, 0x21,0x00, 0xE, +12 }, + { 0x00437D1,0x0343750, 0xAD,0x00, 0xE, +12 }, + { 0x2013E02,0x2F31408, 0x00,0x00, 0xE, +0 }, + { 0x003EBF5,0x06845F6, 0xD4,0x00, 0x7, +0 }, + { 0x171DAF0,0x117B0CA, 0x00,0xC0, 0x8, +0 }, + { 0x1111EF0,0x11121E2, 0x00,0xC0, 0x8, -24 }, + { 0x20053EF,0x30210EF, 0x86,0xC0, 0xE, +12 }, + { 0x2F0F00C,0x0E6F604, 0x00,0x00, 0xE, +0 }, + { 0x047FA00,0x006F900, 0x00,0x00, 0x6, +12 }, + { 0x067FD02,0x078F703, 0x80,0x00, 0x6, +12 }, + { 0x214F70F,0x247F900, 0x05,0x00, 0xE, +12 }, + { 0x3FB88E1,0x2A8A6FF, 0x00,0x00, 0xF, +12 }, + { 0x0FFAA06,0x0FAF700, 0x00,0x00, 0xE, +12 }, + { 0x06CF502,0x138F703, 0x00,0x00, 0x7, +0 }, + { 0x078F502,0x137F700, 0x00,0x00, 0x7, +0 }, + { 0x037F502,0x137F702, 0x00,0x00, 0x3, +12 }, + { 0x0E6C204,0x343E800, 0x10,0x00, 0xE, +12 }, + { 0x212FD03,0x205FD02, 0x80,0x80, 0xA, +12 }, + { 0x085E400,0x234D7C0, 0x80,0x80, 0xE, +12 }, + { 0x0E6E204,0x144B801, 0x90,0x00, 0xE, +12 }, + { 0x2777602,0x3679801, 0x87,0x00, 0xF, +12 }, + { 0x270F604,0x3A3C607, 0x81,0x00, 0xE, +12 }, + { 0x067FD00,0x098F601, 0x00,0x00, 0x6, +12 }, + { 0x0F0F081,0x004F49F, 0x00,0xC3, 0xA, +0 }, + { 0x056FB03,0x017F700, 0x81,0x00, 0x0, +12 }, + { 0x2D65A00,0x0FFFFBF, 0x0E,0xC0, 0xA, +12 }, + { 0x1C7F900,0x0FFFF80, 0x07,0xC0, 0xA, +12 }, + { 0x1D1F813,0x078F512, 0x44,0x00, 0x6, +12 }, + { 0x1DC5E01,0x0FFFFBF, 0x0B,0xC0, 0xA, +12 }, + { 0x113F020,0x027E322, 0x8C,0x80, 0xA, +12 }, + { 0x125A020,0x136B220, 0x86,0x00, 0x6, +12 }, + { 0x015C520,0x0A6D221, 0x28,0x00, 0xC, +12 }, + { 0x1006010,0x0F68110, 0x1A,0x00, 0x8, +12 }, + { 0x2E7F030,0x047F131, 0x12,0x00, 0x0, +0 }, + { 0x1E7F510,0x2E7F610, 0x0D,0x00, 0xD, +12 }, + { 0x0465020,0x1569220, 0x96,0x80, 0xC, +12 }, + { 0x075FC01,0x037F800, 0x00,0x00, 0x0, +12 }, + { 0x175F701,0x336FC00, 0xC0,0x00, 0xC, +54 }, + { 0x2709404,0x3A3C607, 0x81,0x00, 0xE, +12 }, + { 0x0B5F901,0x050D4BF, 0x07,0xC0, 0xB, +12 }, + { 0x0FFF110,0x1FFF051, 0x06,0x00, 0x2, +12 }, + { 0x0069421,0x0A6C3A2, 0x0E,0x00, 0x2, +0 }, + { 0x000F081,0x004F41F, 0x00,0xC3, 0xA, +0 }, + { 0x03BF271,0x00BF3A1, 0x0E,0x00, 0x6, +0 }, + { 0x054F60C,0x0B5F341, 0x5C,0x00, 0x0, +0 }, + { 0x0E6F318,0x0F6F241, 0x62,0x00, 0x0, +0 }, + { 0x082D385,0x0E3A341, 0x59,0x80, 0xC, +0 }, + { 0x1557403,0x005B341, 0x49,0x80, 0x4, +0 }, + { 0x014F6B1,0x007F131, 0x92,0x00, 0x2, +0 }, + { 0x058C7B2,0x008C730, 0x14,0x00, 0x2, +0 }, + { 0x018AAB0,0x0088A71, 0x44,0x00, 0x4, +0 }, + { 0x1239723,0x0145571, 0x93,0x00, 0x4, +0 }, + { 0x10497A1,0x0045571, 0x13,0x80, 0x0, +0 }, + { 0x12A9824,0x01A4671, 0x48,0x00, 0xC, +0 }, + { 0x10691A1,0x0076121, 0x13,0x00, 0xA, +0 }, + { 0x0067121,0x0076161, 0x13,0x89, 0x6, +0 }, + { 0x194F302,0x0C8F381, 0x9C,0x80, 0xC, +0 }, + { 0x04F2009,0x0F8D144, 0xA1,0x80, 0x8, +0 }, + { 0x0069421,0x0A6C362, 0x1E,0x00, 0x2, +0 }, + { 0x11CD1B1,0x00C6131, 0x49,0x00, 0x8, +0 }, + { 0x1037F61,0x1073F21, 0x98,0x00, 0x0, +0 }, + { 0x012C161,0x0054FA1, 0x93,0x00, 0xA, +0 }, + { 0x022C121,0x0054FA1, 0x18,0x00, 0xC, +0 }, + { 0x015F431,0x0058AB2, 0x5B,0x83, 0x0, +0 }, + { 0x0397461,0x06771A1, 0x90,0x00, 0x0, +0 }, + { 0x00554B1,0x0057AB2, 0x57,0x00, 0xC, +0 }, + { 0x0635450,0x045A581, 0x00,0x00, 0x8, +0 }, + { 0x0157621,0x03782A1, 0x94,0x00, 0xC, +0 }, + { 0x01F75A1,0x00F7422, 0x8A,0x06, 0x8, +0 }, + { 0x1557261,0x0187121, 0x86,0x0D, 0x0, +0 }, + { 0x1029331,0x00B72A1, 0x8F,0x00, 0x8, +0 }, + { 0x1039331,0x00982A1, 0x91,0x00, 0xA, +0 }, + { 0x10F9331,0x00F72A1, 0x8E,0x00, 0xA, +0 }, + { 0x01F7561,0x00A7521, 0x9C,0x00, 0x2, +0 }, + { 0x05666E1,0x0266561, 0x4C,0x00, 0x0, +0 }, + { 0x04676A2,0x0365561, 0xCB,0x00, 0x0, +0 }, + { 0x00757A2,0x0075661, 0x99,0x00, 0xB, +0 }, + { 0x00777A2,0x0077661, 0x93,0x00, 0xB, +0 }, + { 0x0126621,0x00A9661, 0x45,0x00, 0x0, +0 }, + { 0x005DF62,0x0076FA1, 0x9E,0x40, 0x2, +0 }, + { 0x001EF20,0x2068FA0, 0x1A,0x00, 0x0, +0 }, + { 0x09453B7,0x005A061, 0xA5,0x00, 0x2, +0 }, + { 0x011A8A1,0x0032571, 0x1F,0x80, 0xA, +0 }, + { 0x03491A1,0x01655A1, 0x17,0x00, 0xC, +0 }, + { 0x00154B1,0x0036AB2, 0x5D,0x00, 0x0, +0 }, + { 0x0432121,0x0354262, 0x97,0x00, 0x8, +0 }, + { 0x177A161,0x1473121, 0x1C,0x00, 0x0, +0 }, + { 0x0F6F83A,0x0028691, 0xCE,0x00, 0x2, +0 }, + { 0x081B122,0x026F2A1, 0x92,0x83, 0xC, +0 }, + { 0x151F181,0x0F5F282, 0x4D,0x00, 0x0, +0 }, + { 0x15111A1,0x0131163, 0x94,0x80, 0x6, +0 }, + { 0x032D453,0x111EB51, 0x91,0x00, 0x8, +0 }, + { 0x303FF40,0x014FF10, 0x00,0x0D, 0xC, +0 }, + { 0x306F640,0x3176711, 0x00,0x00, 0xE, +0 }, + { 0x205F540,0x3164611, 0x00,0x09, 0xE, +0 }, + { 0x048F881,0x0057582, 0x45,0x08, 0x0, +0 }, + { 0x132FA13,0x1F9F211, 0x80,0x0A, 0x8, +0 }, + { 0x0F2F409,0x0E2F211, 0x1B,0x80, 0x2, +0 }, + { 0x0F3D403,0x0F3A340, 0x94,0x40, 0x6, +0 }, + { 0x1058761,0x0058730, 0x80,0x03, 0x7, +0 }, + { 0x174A423,0x0F8F271, 0x9D,0x80, 0xC, +0 }, + { 0x0007FF1,0x1167F21, 0x8D,0x00, 0x0, +0 }, + { 0x0759511,0x1F5C501, 0x0D,0x80, 0x0, +0 }, + { 0x073F222,0x0F3F331, 0x97,0x80, 0x2, +0 }, + { 0x105F510,0x0C3F411, 0x41,0x00, 0x6, +0 }, + { 0x01096C1,0x1166221, 0x8B,0x00, 0x6, +0 }, + { 0x01096C1,0x1153221, 0x8E,0x00, 0x6, +0 }, + { 0x012C4A1,0x0065F61, 0x97,0x00, 0xE, +0 }, + { 0x010E4B1,0x0056A62, 0xCD,0x83, 0x0, +0 }, + { 0x0F57591,0x144A440, 0x0D,0x00, 0xE, +0 }, + { 0x0256421,0x0088F21, 0x92,0x01, 0xC, +0 }, + { 0x0167421,0x0078F21, 0x93,0x00, 0xC, +0 }, + { 0x0176421,0x0378261, 0x94,0x00, 0xC, +0 }, + { 0x0195361,0x0077F21, 0x94,0x04, 0xA, +0 }, + { 0x0187461,0x0088422, 0x8F,0x00, 0xA, +0 }, + { 0x016A571,0x00A8F21, 0x4A,0x00, 0x8, +0 }, + { 0x00A8871,0x1198131, 0x4A,0x00, 0x0, +0 }, + { 0x0219632,0x0187261, 0x4A,0x00, 0x4, +0 }, + { 0x04A85E2,0x01A85E1, 0x59,0x00, 0x0, +0 }, + { 0x02887E1,0x01975E1, 0x48,0x00, 0x0, +0 }, + { 0x0451261,0x1045F21, 0x8E,0x84, 0x8, +0 }, + { 0x106A510,0x004FA00, 0x86,0x03, 0x6, +0 }, + { 0x202A50E,0x017A700, 0x09,0x00, 0xE, +0 }, + { 0x0F6B710,0x005F011, 0x40,0x00, 0x6, +0 }, + { 0x00BF506,0x008F602, 0x07,0x00, 0xA, +0 }, + { 0x001FF0E,0x008FF0E, 0x00,0x00, 0xE, +0 }, + { 0x209F300,0x005F600, 0x06,0x00, 0x4, +0 }, + { 0x006F60C,0x247FB12, 0x00,0x00, 0xE, +0 }, + { 0x004F60C,0x244CB12, 0x00,0x05, 0xE, +0 }, + { 0x001F60C,0x242CB12, 0x00,0x00, 0xA, +0 }, + { 0x000F00E,0x3049F40, 0x00,0x00, 0xE, +0 }, + { 0x030F50E,0x0039F50, 0x00,0x04, 0xE, +0 }, + { 0x204940E,0x0F78700, 0x02,0x0A, 0xA, +0 }, + { 0x000F64E,0x2039F1E, 0x00,0x00, 0xE, +0 }, + { 0x000F60E,0x3029F50, 0x00,0x00, 0xE, +0 }, + { 0x100FF00,0x014FF10, 0x00,0x00, 0xC, +0 }, + { 0x04F760E,0x2187700, 0x40,0x03, 0xE, +0 }, + { 0x1F4FC02,0x0F4F712, 0x00,0x05, 0x6, +0 }, + { 0x053F101,0x074D211, 0x4F,0x00, 0x6, +0 }, + { 0x00381A5,0x005F1B2, 0xD2,0x80, 0x2, +0 }, + { 0x0F0FB3E,0x09BA0B1, 0x29,0x00, 0x0, +0 }, + { 0x315EF11,0x0B5F441, 0x53,0x00, 0x8, +0 }, + { 0x0F7F000,0x0068761, 0x30,0x00, 0xF, +0 }, + { 0x0100133,0x0337D14, 0x87,0x80, 0x8, +0 }, + { 0x1FFF000,0x1FFF001, 0x0A,0x00, 0xE, +0 }, + { 0x0AE71E1,0x09E81E1, 0x16,0x00, 0xA, +0 }, + { 0x2831621,0x0C31320, 0xDA,0x00, 0x8, +0 }, + { 0x0022A95,0x0F34212, 0x97,0x80, 0x0, +0 }, + { 0x001EF4F,0x0F19801, 0x81,0x00, 0x4, +0 }, + { 0x019D530,0x01B61B1, 0x88,0x80, 0xC, +0 }, + { 0x0176E71,0x00E8B22, 0xC5,0x05, 0x2, +0 }, + { 0x0157261,0x0278461, 0x1C,0x00, 0xE, +0 }, + { 0x0427847,0x0548554, 0x4D,0x00, 0xA, +0 }, + { 0x011F111,0x0B3F101, 0x4A,0x88, 0x6, +0 }, + { 0x0117171,0x11562A1, 0x8B,0x00, 0x6, +0 }, + { 0x0035172,0x0135262, 0x1C,0x05, 0xE, +0 }, + { 0x0035131,0x06754A1, 0x1C,0x80, 0xE, +0 }, + { 0x0115270,0x0FE3171, 0xC5,0x40, 0x0, +0 }, + { 0x021FF13,0x003FF11, 0x96,0x80, 0xA, +0 }, + { 0x01797F1,0x018F121, 0x01,0x0D, 0x8, +0 }, + { 0x0F7F0F5,0x00687B1, 0x2E,0x00, 0xB, +0 }, + { 0x01B5132,0x03BA2A1, 0x9A,0x82, 0xC, +0 }, + { 0x0176E71,0x00E8B62, 0xC5,0x05, 0x2, +0 }, + { 0x019D530,0x01B61B1, 0xCD,0x40, 0xC, +0 }, + { 0x00B4131,0x03B92A1, 0x1C,0x80, 0xC, +0 }, + { 0x01D5321,0x03B52A1, 0x1C,0x80, 0xC, +0 }, + { 0x01F4171,0x03B92A1, 0x1C,0x80, 0xE, +0 }, + { 0x0177421,0x0176562, 0x83,0x00, 0x7, +0 }, + { 0x0AE7121,0x09E8121, 0x16,0x00, 0xE, +0 }, + { 0x212AA53,0x021AC51, 0x97,0x80, 0xE, +0 }, + { 0x112AA43,0x1119B51, 0x1C,0x00, 0xE, +0 }, + { 0x001FFA4,0x0F3F53E, 0xDB,0xC0, 0x4, +0 }, + { 0x0AC9011,0x1F4F071, 0x1A,0x00, 0xF, +0 }, + { 0x22F55B0,0x31E87E0, 0x16,0x80, 0xC, +0 }, + { 0x08F6EE0,0x02A65A1, 0xEC,0x00, 0xE, +0 }, + { 0x2A2B264,0x1D49703, 0x02,0x80, 0xE, +0 }, + { 0x0F3F8E2,0x0F3F770, 0x86,0x40, 0x4, +0 }, + { 0x0F0E026,0x031FF1E, 0x03,0x00, 0x8, +0 }, + { 0x0056541,0x0743291, 0x83,0x00, 0xA, +0 }, + { 0x061F217,0x0B2F112, 0x4F,0x08, 0x8, +0 }, + { 0x011F111,0x061D001, 0x4A,0x40, 0x6, +0 }, + { 0x282B264,0x1DA9803, 0x00,0x00, 0xE, +0 }, + { 0x282B264,0x1D49703, 0x00,0x80, 0xE, +0 }, + { 0x06F9A02,0x007A006, 0x00,0x00, 0x0, +0 }, + { 0x0B2F131,0x0AFF111, 0x8F,0x83, 0x8, +0 }, + { 0x0B2F131,0x0D5C131, 0x19,0x01, 0x9, +0 }, + { 0x0D2F111,0x0E6F211, 0x4C,0x83, 0xA, +0 }, + { 0x0D5C111,0x0E6C231, 0x15,0x00, 0xB, +0 }, + { 0x0D4F315,0x0E4B115, 0x5F,0x61, 0xE, +0 }, + { 0x0E4B111,0x0B5B111, 0x5C,0x00, 0xE, +0 }, + { 0x0D4F111,0x0E4C302, 0x89,0x5F, 0xD, +12 }, + { 0x035C100,0x0D5C111, 0x9B,0x00, 0xC, +0 }, + { 0x050F210,0x0F0E131, 0x60,0x5D, 0x4, +12 }, + { 0x040B230,0x5E9F111, 0xA2,0x80, 0x4, +0 }, + { 0x0E3F217,0x0E2C211, 0x54,0x06, 0xA, +0 }, + { 0x0C3F219,0x0D2F291, 0x2B,0x07, 0xB, +0 }, + { 0x004A61A,0x004F600, 0x27,0x0A, 0x3, +0 }, + { 0x0790824,0x0E6E384, 0x9A,0x5B, 0xA, +12 }, + { 0x0E6F314,0x0E6F280, 0x62,0x00, 0xB, +0 }, + { 0x055F71C,0x0D88520, 0xA3,0x0D, 0x6, +0 }, + { 0x055F718,0x0D8E521, 0x23,0x00, 0x7, +0 }, + { 0x0F7E701,0x1557403, 0x84,0x49, 0xD, +0 }, + { 0x005B301,0x0F77601, 0x80,0x80, 0xD, +0 }, + { 0x02AA2A0,0x02AA522, 0x85,0x9E, 0x7, +0 }, + { 0x02AA5A2,0x02AA128, 0x83,0x95, 0x7, +0 }, + { 0x038C620,0x057F621, 0x81,0x80, 0x7, +0 }, + { 0x00AAFE1,0x00AAF62, 0x91,0x83, 0x9, +0 }, + { 0x002B025,0x0057030, 0x5F,0x40, 0xC, +0 }, + { 0x002C031,0x0056031, 0x46,0x80, 0xD, +0 }, + { 0x015C821,0x0056F31, 0x93,0x00, 0xC, +0 }, + { 0x005CF31,0x0057F32, 0x16,0x87, 0xD, +0 }, + { 0x4F2B913,0x0119102, 0x0D,0x1A, 0xA, +0 }, + { 0x14A9221,0x02A9122, 0x99,0x00, 0xA, +0 }, + { 0x242F823,0x2FA9122, 0x96,0x1A, 0x0, +0 }, + { 0x0BA9221,0x04A9122, 0x99,0x00, 0x0, +0 }, + { 0x0487131,0x0487131, 0x19,0x00, 0xD, +0 }, + { 0x0DAF904,0x0DFF701, 0x0B,0x80, 0x9, +0 }, + { 0x09AA101,0x0DFF221, 0x89,0x40, 0x6, +0 }, + { 0x0DAF904,0x0DFF701, 0x0B,0x80, 0x7, +0 }, + { 0x0C8F621,0x0C8F101, 0x1C,0x1F, 0xA, +0 }, + { 0x0C8F101,0x0C8F201, 0xD8,0x00, 0xA, +0 }, + { 0x1038D12,0x0866503, 0x95,0x8B, 0x9, +0 }, + { 0x113DD31,0x0265621, 0x17,0x00, 0x8, +0 }, + { 0x012C121,0x0054F61, 0x1A,0x00, 0xC, +0 }, + { 0x012C1A1,0x0054F21, 0x93,0x00, 0xD, +0 }, + { 0x022C122,0x0054F22, 0x0B,0x1C, 0xD, +0 }, + { 0x0F5A006,0x035A3E4, 0x03,0x23, 0xE, +0 }, + { 0x0077FA1,0x0077F61, 0x51,0x00, 0xF, +0 }, + { 0x0578402,0x074A7E4, 0x05,0x16, 0xE, +0 }, + { 0x03974A1,0x0677161, 0x90,0x00, 0xF, +0 }, + { 0x054990A,0x0639707, 0x65,0x60, 0x8, +0 }, + { 0x1045FA1,0x0066F61, 0x59,0x00, 0x8, +0 }, + { 0x0178421,0x008AF61, 0x15,0x0B, 0xD, +0 }, + { 0x0178521,0x0097F21, 0x94,0x05, 0xC, +0 }, + { 0x0178421,0x008AF61, 0x15,0x0D, 0xD, +0 }, + { 0x1277131,0x0499161, 0x15,0x83, 0xC, +0 }, + { 0x0277DB1,0x0297A21, 0x10,0x08, 0xD, +0 }, + { 0x00A6321,0x00B7F21, 0x9F,0x00, 0xE, +0 }, + { 0x00A65A1,0x00B7F61, 0xA2,0x00, 0xF, +0 }, + { 0x02AA961,0x036A823, 0xA3,0x52, 0x8, +0 }, + { 0x016AAA1,0x00A8F21, 0x94,0x80, 0x8, +0 }, + { 0x011DA25,0x068A6E3, 0x00,0x2B, 0xC, +0 }, + { 0x05F85E1,0x01A65E1, 0x1F,0x00, 0xD, +0 }, + { 0x05F88E1,0x01A65E1, 0x46,0x00, 0xD, +0 }, + { 0x011DA25,0x068A623, 0x00,0x1E, 0xC, +0 }, + { 0x0588821,0x01A6521, 0x8C,0x00, 0xD, +0 }, + { 0x001DF26,0x03876E4, 0x00,0x2B, 0xC, +0 }, + { 0x0369522,0x00776E1, 0xD8,0x00, 0xD, +0 }, + { 0x087C4A3,0x076C626, 0x00,0x57, 0xE, +0 }, + { 0x0558622,0x0186421, 0x46,0x80, 0xF, +0 }, + { 0x04AA321,0x00A8621, 0x48,0x00, 0x8, +0 }, + { 0x0126621,0x00A9621, 0x45,0x00, 0x9, +0 }, + { 0x109F121,0x109F121, 0x1D,0x80, 0xB, +0 }, + { 0x0332121,0x0454222, 0x97,0x03, 0x8, +0 }, + { 0x0D421A1,0x0D54221, 0x99,0x03, 0x9, +0 }, + { 0x0336121,0x0354261, 0x8D,0x03, 0xA, +0 }, + { 0x177A1A1,0x1473121, 0x1C,0x00, 0xB, +0 }, + { 0x0331121,0x0354261, 0x89,0x03, 0xA, +0 }, + { 0x0E42121,0x0D54261, 0x8C,0x03, 0xB, +0 }, + { 0x1471121,0x007CF21, 0x15,0x00, 0x0, +0 }, + { 0x0E41121,0x0D55261, 0x8C,0x00, 0x1, +0 }, + { 0x58AFE0F,0x006FB04, 0x83,0x85, 0xC, +0 }, + { 0x003A821,0x004A722, 0x99,0x00, 0xD, +0 }, + { 0x0937501,0x0B4C502, 0x61,0x80, 0x8, +0 }, + { 0x0957406,0x072A501, 0x5B,0x00, 0x9, +0 }, + { 0x056B222,0x056F261, 0x92,0x8A, 0xC, +0 }, + { 0x2343121,0x00532A1, 0x9D,0x80, 0xD, +0 }, + { 0x088A324,0x087A322, 0x40,0x5B, 0xE, +0 }, + { 0x151F101,0x0F5F241, 0x13,0x00, 0xF, +0 }, + { 0x04211A1,0x0731161, 0x10,0x92, 0xA, +0 }, + { 0x0211161,0x0031DA1, 0x98,0x80, 0xB, +0 }, + { 0x0167D62,0x01672A2, 0x57,0x80, 0x4, +0 }, + { 0x0069F61,0x0049FA1, 0x5B,0x00, 0x5, +0 }, + { 0x024A238,0x024F231, 0x9F,0x9C, 0x6, +0 }, + { 0x014F123,0x0238161, 0x9F,0x00, 0x6, +0 }, + { 0x053C601,0x0D5F583, 0x71,0x40, 0x7, +0 }, + { 0x4FCFA15,0x0ECFA12, 0x11,0x80, 0xA, +0 }, + { 0x0FCFA18,0x0E5F812, 0x9D,0x00, 0xB, +0 }, + { 0x007A801,0x083F600, 0x5C,0x03, 0x7, +0 }, + { 0x458F811,0x0E5F310, 0x8F,0x00, 0xE, +0 }, + { 0x154F610,0x0E4F410, 0x92,0x00, 0xF, +0 }, + { 0x0001F0F,0x3F01FC0, 0x00,0x00, 0xE, +0 }, + { 0x0001F0F,0x3F11FC0, 0x3F,0x3F, 0xF, +0 }, + { 0x024F806,0x7845603, 0x80,0x88, 0xE, +0 }, + { 0x024D803,0x7846604, 0x1E,0x08, 0xF, +0 }, + { 0x001FF06,0x3043414, 0x00,0x00, 0xE, +0 }, + { 0x001FF26,0x1841204, 0x00,0x00, 0xE, +0 }, + { 0x0F86848,0x0F10001, 0x00,0x3F, 0x5, +0 }, + { 0x0F86747,0x0F8464C, 0x00,0x00, 0x5, +0 }, + { 0x261B235,0x015F414, 0x1C,0x08, 0xA, +1 }, + { 0x715FE11,0x019F487, 0x20,0xC0, 0xB, +0 }, + { 0x1112EF0,0x11621E2, 0x00,0xC0, 0x8, -36 }, + { 0x7112EF0,0x11621E2, 0x00,0xC0, 0x9, +0 }, + { 0x007FC01,0x638F802, 0x03,0x03, 0xF, +0 }, + { 0x007FC00,0x638F801, 0x03,0x03, 0xF, +0 }, + { 0x00CFD01,0x03CD600, 0x07,0x00, 0x0, +0 }, + { 0x00CF600,0x006F600, 0x00,0x00, 0x1, +0 }, + { 0x008F60C,0x247FB12, 0x00,0x00, 0xB, +0 }, + { 0x008F60C,0x2477B12, 0x00,0x00, 0xA, +0 }, + { 0x008F60C,0x2477B12, 0x00,0x00, 0xB, +0 }, + { 0x002F60C,0x243CB12, 0x00,0x15, 0xB, +0 }, + { 0x3E4E40F,0x1E5F508, 0x00,0x0A, 0x6, +0 }, + { 0x366F50F,0x1A5F508, 0x00,0x19, 0x7, +0 }, + { 0x3E4E40F,0x1E5F507, 0x00,0x11, 0x6, +0 }, + { 0x365F50F,0x1A5F506, 0x00,0x1E, 0x7, +0 }, + { 0x0C49406,0x2F5F604, 0x00,0x00, 0x0, +0 }, + { 0x004F902,0x0F79705, 0x00,0x03, 0x0, +0 }, + { 0x156F28F,0x100F446, 0x03,0x00, 0xE, +0 }, + { 0x000F38F,0x0A5F442, 0x00,0x06, 0xE, +0 }, + { 0x237F811,0x005F310, 0x45,0x00, 0x8, +0 }, + { 0x037F811,0x005F310, 0x05,0x08, 0x9, +0 }, + { 0x155F381,0x000F441, 0x00,0x00, 0xE, +0 }, + { 0x000F341,0x0A4F48F, 0x00,0x00, 0xE, +0 }, + { 0x503FF80,0x014FF10, 0x00,0x00, 0xC, +0 }, + { 0x503FF80,0x014FF10, 0x00,0x0D, 0xD, +0 }, + { 0x3E5E40F,0x1E7F508, 0x00,0x0A, 0x6, +0 }, + { 0x366F50F,0x1A8F608, 0x00,0x19, 0x7, +0 }, + { 0x00CF506,0x008F502, 0xC8,0x0B, 0x6, +0 }, + { 0x00CF506,0x007F501, 0xC5,0x03, 0x7, +0 }, + { 0x0BFFA01,0x096C802, 0x8F,0x80, 0x6, +0 }, + { 0x0BFFA01,0x096C802, 0xCF,0x0B, 0x7, +0 }, + { 0x087FA01,0x0B7FA01, 0x4F,0x08, 0x7, +0 }, + { 0x08DFA01,0x0B5F802, 0x55,0x00, 0x6, +0 }, + { 0x08DFA01,0x0B5F802, 0x55,0x12, 0x7, +0 }, + { 0x08DFA01,0x0B6F802, 0x59,0x00, 0x6, +0 }, + { 0x08DFA01,0x0B6F802, 0x59,0x12, 0x7, +0 }, + { 0x00AFA01,0x006F900, 0x00,0x00, 0xE, +0 }, + { 0x00AFA01,0x006F900, 0x00,0x0D, 0xF, +0 }, + { 0x089F900,0x06CF600, 0x80,0x08, 0xF, +0 }, + { 0x388F803,0x0B6F60C, 0x8D,0x00, 0xE, +0 }, + { 0x088F803,0x0B8F80C, 0x88,0x12, 0xF, +0 }, + { 0x388F803,0x0B6F60C, 0x88,0x03, 0xE, +0 }, + { 0x388F803,0x0B8F80C, 0x88,0x0F, 0xF, +0 }, + { 0x04F760F,0x2187700, 0x00,0x12, 0xF, +0 }, + { 0x249C80F,0x2699B02, 0x40,0x80, 0xE, +0 }, + { 0x249C80F,0x2699B0F, 0xC0,0x19, 0xF, +0 }, + { 0x305AD57,0x0058D87, 0xDC,0x00, 0xE, +0 }, + { 0x305AD47,0x0058D87, 0xDC,0x12, 0xF, +0 }, + { 0x304A857,0x0048887, 0xDC,0x00, 0xE, +0 }, + { 0x304A857,0x0058887, 0xDC,0x08, 0xF, +0 }, + { 0x3F40006,0x0F5F715, 0x3F,0x00, 0x0, +0 }, + { 0x3F40006,0x0F5F715, 0x3F,0x08, 0x1, +0 }, + { 0x3F40006,0x0F5F712, 0x3F,0x08, 0x1, +0 }, + { 0x7476701,0x0476703, 0xCD,0x40, 0x8, +0 }, + { 0x0476701,0x0556501, 0xC0,0x00, 0x9, +0 }, + { 0x0A76701,0x0356503, 0x17,0x1E, 0xA, +0 }, + { 0x0777701,0x0057501, 0x9D,0x00, 0xB, +0 }, + { 0x3F0E00A,0x005FF1F, 0x40,0x40, 0x8, +0 }, + { 0x3F0E00A,0x005FF1F, 0x40,0x48, 0x9, +0 }, + { 0x3F0E00A,0x002FF1F, 0x7C,0x40, 0x8, +0 }, + { 0x3E0F50A,0x003FF1F, 0x7C,0x40, 0x9, +0 }, + { 0x04F7F0F,0x21E7E00, 0x40,0x88, 0xE, +0 }, + { 0x04F7F0F,0x21E7E00, 0x40,0x14, 0xF, +0 }, + { 0x6E5E403,0x7E7F507, 0x0D,0x11, 0xB, +0 }, + { 0x366F500,0x4A8F604, 0x1B,0x15, 0xA, +0 }, + { 0x3F40003,0x0F5F715, 0x3F,0x00, 0x8, +0 }, + { 0x3F40003,0x0F5F715, 0x3F,0x08, 0x9, +0 }, + { 0x08DFA01,0x0B5F802, 0x4F,0x00, 0x6, +0 }, + { 0x08DFA01,0x0B5F802, 0x4F,0x12, 0x7, +0 }, + { 0x084FA01,0x0B4F800, 0x4F,0x00, 0x6, +0 }, + { 0x084FA01,0x0B4F800, 0x4F,0x00, 0x7, +0 }, + { 0x0F3F040,0x0038761, 0x30,0x00, 0xF, +0 }, + { 0x033E813,0x0F3F011, 0x12,0x00, 0x8, +0 }, + { 0x133F721,0x2F4F320, 0x48,0x00, 0x4, +0 }, + { 0x1F4F201,0x0F5F009, 0x00,0x00, 0x6, +0 }, + { 0x1114070,0x0034061, 0x84,0x00, 0x0, +0 }, + { 0x0D3B305,0x024F246, 0x40,0x80, 0x2, +0 }, + { 0x106F90E,0x0F4F001, 0x2F,0x00, 0xB, +0 }, + { 0x0126E71,0x0045061, 0x0D,0x00, 0x0, +0 }, + { 0x2A31321,0x0F31220, 0x1A,0x00, 0x8, +0 }, + { 0x025DC03,0x009F031, 0xA2,0x00, 0x8, +0 }, + { 0x025DC03,0x009F021, 0x17,0x00, 0x8, +0 }, + { 0x025DF23,0x0F9F021, 0x20,0x00, 0xE, +0 }, + { 0x1025161,0x0024173, 0x52,0x00, 0xA, +0 }, + { 0x0195132,0x0396061, 0x5A,0x85, 0xC, +0 }, + { 0x025DC03,0x009F031, 0x9A,0x00, 0x8, +0 }, + { 0x025DC03,0x009F031, 0x98,0x00, 0x8, +0 }, + { 0x1126EB1,0x0045021, 0x47,0x02, 0x0, +0 }, + { 0x025DC03,0x009F031, 0x97,0x00, 0x8, +0 }, + { 0x025DC03,0x009F031, 0x96,0x00, 0x8, +0 }, + { 0x025DC03,0x009F031, 0x94,0x00, 0x8, +0 }, + { 0x025DB02,0x006F030, 0x10,0x00, 0x8, +0 }, + { 0x1145152,0x0147242, 0x88,0x00, 0xA, +0 }, + { 0x0115172,0x01572A2, 0x89,0x00, 0xA, +0 }, + { 0x0F8AF00,0x0F6F401, 0xC0,0x00, 0xE, +0 }, + { 0x0009FB1,0x1069FA2, 0x45,0x0D, 0x2, +0 }, + { 0x0009FB1,0x1069FA2, 0x45,0x08, 0x2, +0 }, + { 0x1016F00,0x0F57001, 0x19,0x00, 0xE, +0 }, + { 0x229FFF2,0x0F480E1, 0x1A,0x00, 0x6, +0 }, + { 0x025DC03,0x009F032, 0x12,0x00, 0xA, +0 }, + { 0x025DC03,0x009F032, 0x10,0x00, 0xA, +0 }, + { 0x025DC03,0x009F032, 0x0E,0x00, 0xA, +0 }, + { 0x025DC03,0x009F032, 0x0C,0x00, 0xA, +0 }, + { 0x025DC03,0x009F032, 0x0A,0x00, 0xA, +0 }, + { 0x025DC03,0x009F031, 0x92,0x00, 0x8, +0 }, + { 0x1062F01,0x0076521, 0x07,0x00, 0x0, +0 }, + { 0x00470F5,0x0F38071, 0x1C,0x00, 0xB, +0 }, + { 0x0F77061,0x0256061, 0x21,0x00, 0x2, +0 }, + { 0x0C76012,0x00550F1, 0x28,0x00, 0x2, +0 }, + { 0x0049F21,0x0049F62, 0x00,0x00, 0x1, +0 }, + { 0x2119A16,0x0029012, 0x14,0x00, 0x2, +0 }, + { 0x033F813,0x003FF11, 0x0E,0x00, 0x8, +0 }, + { 0x0057F72,0x0F56071, 0x1D,0x00, 0x2, +0 }, + { 0x203B162,0x005F172, 0x4A,0x00, 0x2, +0 }, + { 0x2027062,0x0029062, 0x4A,0x00, 0x2, +0 }, + { 0x0FF0F20,0x0F1F021, 0xFF,0x00, 0x0, +0 }, + { 0x0F28021,0x0037021, 0x8F,0x00, 0x0, +0 }, + { 0x2129A16,0x0039012, 0x97,0x00, 0x2, +0 }, + { 0x212AA93,0x021AC91, 0x97,0x80, 0xE, +0 }, + { 0x024DA05,0x013F901, 0x8B,0x00, 0xA, +0 }, + { 0x203B162,0x0046172, 0xCF,0x00, 0x2, +0 }, + { 0x006FA04,0x095F201, 0xD3,0x00, 0xA, +0 }, + { 0x0847162,0x0246061, 0x21,0x00, 0x8, +0 }, + { 0x0FFF000,0x02FF607, 0x00,0x00, 0x0, +0 }, + { 0x3F27026,0x0568705, 0x00,0x00, 0xE, +0 }, + { 0x005FC11,0x1F5DF12, 0x00,0x00, 0x1, +0 }, + { 0x104F021,0x0D6F401, 0xCF,0x00, 0xA, +0 }, + { 0x104F021,0x0D6F401, 0xC7,0x00, 0x0, +0 }, + { 0x004F021,0x0D6F401, 0x1B,0x00, 0xA, +0 }, + { 0x104F061,0x1D6F441, 0xCE,0x00, 0x4, +0 }, + { 0x065F301,0x07DF111, 0x12,0x00, 0x8, +0 }, + { 0x254F5A8,0x0B7F321, 0xE8,0x00, 0x0, +0 }, + { 0x14FF101,0x3D6F311, 0xC6,0x06, 0xA, +0 }, + { 0x0ADF303,0x15E8301, 0x58,0x00, 0xE, +0 }, + { 0x01F4C28,0x045F601, 0xD4,0x00, 0xE, +0 }, + { 0x223F208,0x073F414, 0x92,0x80, 0x0, -12 }, + { 0x22F6216,0x06AF401, 0x64,0x41, 0x0, +0 }, + { 0x036F506,0x025FDA1, 0x10,0x80, 0x3, +0 }, + { 0x0176D0A,0x005F001, 0xD5,0x00, 0x4, +0 }, + { 0x265F812,0x0D7F601, 0xC8,0x00, 0xC, +0 }, + { 0x092FF43,0x003F015, 0x00,0x00, 0xE, -12 }, + { 0x0388B03,0x2398300, 0xC0,0x80, 0x0, +0 }, + { 0x00FF060,0x00FF062, 0xC0,0x06, 0xD, +0 }, + { 0x29FFF24,0x10FF021, 0x00,0x00, 0xF, +0 }, + { 0x11FFF30,0x14C5E32, 0x00,0x00, 0x7, +0 }, + { 0x10BF024,0x20B5030, 0x49,0x00, 0xF, +0 }, + { 0x00BF024,0x10B5031, 0xCC,0x0A, 0xA, +0 }, + { 0x12F6F24,0x20D4030, 0xCA,0x0A, 0x0, +0 }, + { 0x00BF022,0x10B5071, 0xCD,0x03, 0x0, +0 }, + { 0x105F003,0x1C8F211, 0xCE,0x00, 0x0, +0 }, + { 0x125FF03,0x1C8F211, 0x49,0x00, 0x0, +0 }, + { 0x145F503,0x03AF621, 0xD3,0x00, 0xE, +0 }, + { 0x1269E03,0x0BBF221, 0x90,0x80, 0xE, +0 }, + { 0x047FF01,0x2BCF400, 0xC0,0x00, 0xE, +0 }, + { 0x04F6F20,0x31FFF20, 0xE0,0x01, 0x0, +0 }, + { 0x32F5F30,0x31FFE30, 0xE0,0x01, 0x0, +0 }, + { 0x3598600,0x02A7284, 0x42,0x80, 0xC, +0 }, + { 0x054FE10,0x00FF030, 0x00,0x00, 0x6, +12 }, + { 0x0397530,0x088F220, 0xC2,0x40, 0x8, +12 }, + { 0x125FF10,0x006F030, 0x0A,0x00, 0xC, +12 }, + { 0x039F330,0x00CF0A0, 0x0F,0x00, 0x8, +12 }, + { 0x07FF420,0x00FF021, 0x18,0x00, 0xE, +0 }, + { 0x106F010,0x006F030, 0x00,0x00, 0x6, +12 }, + { 0x05FF620,0x00FF021, 0x16,0x00, 0xE, +0 }, + { 0x006F010,0x006F030, 0x08,0x00, 0x4, +0 }, + { 0x092FF43,0x003F015, 0x00,0x00, 0xE, +0 }, + { 0x106F031,0x10650B1, 0xC5,0x00, 0x0, +0 }, + { 0x11FF431,0x13653A1, 0x40,0x00, 0x0, +0 }, + { 0x01FF431,0x13663A1, 0xC0,0x00, 0x0, +0 }, + { 0x043F271,0x1285161, 0x1D,0x00, 0xE, +0 }, + { 0x279A702,0x284F410, 0xD2,0x00, 0x0, +0 }, + { 0x194F622,0x09BF231, 0x1B,0x80, 0xA, +0 }, + { 0x126F801,0x105F000, 0x40,0x00, 0x0, +0 }, + { 0x043F231,0x1285121, 0x1D,0x00, 0xE, +0 }, + { 0x1011031,0x2042030, 0x56,0x00, 0xE, +0 }, + { 0x136F131,0x0286121, 0x1B,0x00, 0xE, +0 }, + { 0x034F131,0x0285121, 0x1C,0x00, 0xE, +0 }, + { 0x015F431,0x0056072, 0x5B,0x83, 0x0, +0 }, + { 0x172FCE1,0x01762B1, 0x46,0x00, 0x0, +0 }, + { 0x0053071,0x0055072, 0x57,0x00, 0xC, +0 }, + { 0x062F600,0x01BF301, 0x00,0x08, 0x6, +0 }, + { 0x06553B1,0x00FF021, 0x14,0x00, 0xA, +0 }, + { 0x0254231,0x00FF0A1, 0x56,0x01, 0xE, +0 }, + { 0x1255221,0x02993A1, 0x55,0x01, 0xE, +0 }, + { 0x07554B1,0x0089021, 0x20,0x00, 0xE, +0 }, + { 0x0375421,0x008F021, 0x1B,0x00, 0xE, +0 }, + { 0x1396521,0x09EF221, 0x16,0x00, 0xE, +0 }, + { 0x0375621,0x00AF021, 0x1E,0x00, 0xE, +0 }, + { 0x0046021,0x1095031, 0x4E,0x00, 0x6, +0 }, + { 0x0046021,0x1095031, 0x8E,0x00, 0xA, +0 }, + { 0x0055021,0x1095021, 0x8E,0x00, 0xA, +0 }, + { 0x0055031,0x1095021, 0x8E,0x00, 0xA, +0 }, + { 0x0038031,0x136F132, 0x17,0x00, 0x0, +0 }, + { 0x2066020,0x10A7022, 0x19,0x00, 0x0, +0 }, + { 0x1065020,0x00A6022, 0x1E,0x00, 0x0, +0 }, + { 0x0258C32,0x0176221, 0x4C,0x00, 0xC, +0 }, + { 0x00430B1,0x00A5021, 0x57,0x00, 0xC, +0 }, + { 0x04451B1,0x00A5021, 0x55,0x00, 0xC, +0 }, + { 0x20F4032,0x0095021, 0xDF,0x00, 0x0, +0 }, + { 0x39C4611,0x05A6321, 0x20,0x00, 0xE, +0 }, + { 0x39D7531,0x0095021, 0x17,0x00, 0xE, +0 }, + { 0x35AF802,0x02A42B1, 0x00,0x00, 0xE, +0 }, + { 0x20FF022,0x00FF021, 0x5D,0x00, 0xE, +0 }, + { 0x0535231,0x147F221, 0x0F,0x00, 0xC, +0 }, + { 0x39D65B1,0x0095021, 0x17,0x00, 0xE, +0 }, + { 0x05AF802,0x22A42B0, 0x00,0x00, 0xE, +0 }, + { 0x057F421,0x228F232, 0xC0,0x00, 0x0, +0 }, + { 0x29D6561,0x2095021, 0xC6,0x00, 0x0, -12 }, + { 0x358F423,0x3486422, 0xC0,0x10, 0xB, -24 }, + { 0x0EDF331,0x07DF131, 0xCB,0x00, 0x8, +0 }, + { 0x395FF09,0x02552E1, 0xC0,0x00, 0x0, +0 }, + { 0x0052031,0x0063031, 0x58,0x40, 0x0, +0 }, + { 0x0735421,0x008F021, 0x0E,0x07, 0xA, +0 }, + { 0x0033071,0x0044072, 0x5D,0x00, 0x0, +0 }, + { 0x2023034,0x003F021, 0x27,0x09, 0xE, +0 }, + { 0x3042001,0x2042030, 0x63,0x00, 0x0, +0 }, + { 0x0585201,0x0364161, 0x99,0x00, 0x6, +0 }, + { 0x0261131,0x0071031, 0x1B,0x00, 0xC, +0 }, + { 0x0B4F251,0x075F101, 0xD0,0x00, 0x0, +0 }, + { 0x0572132,0x01942A3, 0x06,0x00, 0x9, -12 }, + { 0x3859F45,0x043F311, 0x15,0x00, 0xE, +0 }, + { 0x115F403,0x0C8F221, 0xD7,0x00, 0xA, +0 }, + { 0x295F300,0x2B9F260, 0x11,0x00, 0x0, +0 }, + { 0x0050021,0x2041020, 0xCF,0x00, 0x0, +0 }, + { 0x2A3F400,0x2B9F260, 0x1B,0x00, 0x0, +0 }, + { 0x0644312,0x2028030, 0x22,0x00, 0xE, +0 }, + { 0x098F201,0x1D5F307, 0x40,0x09, 0x0, +0 }, + { 0x083FF00,0x166F502, 0x00,0x00, 0xE, -12 }, + { 0x275FF12,0x2E8F310, 0x80,0x00, 0xE, +0 }, + { 0x163F402,0x164F502, 0x0F,0x00, 0x0, -12 }, + { 0x064FB05,0x2579600, 0xC9,0x00, 0x0, +0 }, + { 0x1B2FF13,0x30F5030, 0x0C,0x0A, 0xE, +0 }, + { 0x21DF230,0x10C4021, 0x0E,0x00, 0xA, +0 }, + { 0x3023030,0x2064030, 0xC0,0x00, 0x0, +0 }, + { 0x375FF25,0x033FE03, 0xC0,0x00, 0x0, -7 }, + { 0x37DFE25,0x0079003, 0xC0,0x00, 0x0, -7 }, + { 0x0034007,0x0056001, 0xDC,0x00, 0x0, +0 }, + { 0x2B3F811,0x003F010, 0xC1,0x03, 0x4, -7 }, + { 0x00CF000,0x006F000, 0x00,0x00, 0x4, +2 }, + { 0x32C8F01,0x006F000, 0x00,0x00, 0xE, +0 }, + { 0x2A2FF40,0x30E104E, 0x00,0x00, 0xE, +0 }, + { 0x092FF11,0x306301E, 0xC0,0x00, 0xE, +0 }, + { 0x003402E,0x003105E, 0x00,0x00, 0xE, +0 }, + { 0x2A3375B,0x237461A, 0x95,0x40, 0x0, +0 }, + { 0x344FF6B,0x02AF1EA, 0xC0,0x01, 0xC, -12 }, + { 0x10EF07E,0x00E3030, 0x00,0x0A, 0xE, +0 }, + { 0x003F02E,0x00310FE, 0x00,0x00, 0xE, +0 }, + { 0x023FCC0,0x006F04E, 0x00,0x00, 0xE, +0 }, + { 0x0A3FB00,0x007F000, 0xC0,0x00, 0xA, +0 }, + { 0x0C2FD05,0x3D9F910, 0xC0,0x00, 0x0, +0 }, + { 0x03A8F2E,0x067A800, 0x00,0x00, 0xE, +0 }, + { 0x22C8305,0x0589903, 0x00,0x00, 0xE, +0 }, + { 0x25C8400,0x08AF800, 0x00,0x00, 0xE, +0 }, + { 0x00CFF00,0x006FF00, 0x00,0x00, 0x4, +0 }, + { 0x004F041,0x308F009, 0xC0,0x00, 0xE, +0 }, + { 0x006F001,0x339880D, 0x40,0x00, 0xC, +0 }, + { 0x12FF201,0x356F54E, 0xC0,0x00, 0xE, +0 }, + { 0x12FF241,0x356F54E, 0xC0,0x00, 0xE, +0 }, + { 0x155AF00,0x364FF4B, 0x00,0x00, 0xE, +0 }, + { 0x1496401,0x356F54A, 0xC0,0x00, 0xE, +0 }, + { 0x2678900,0x357874E, 0x00,0x00, 0xE, +0 }, + { 0x02FF241,0x356F54E, 0xC0,0x00, 0x0, +0 }, + { 0x05FF210,0x27FC40E, 0x00,0x00, 0x6, +0 }, + { 0x00CF003,0x03AF802, 0xC0,0x00, 0x0, +0 }, + { 0x00BF003,0x037F702, 0xC0,0x00, 0x0, +0 }, + { 0x00CF003,0x01AFD02, 0xC0,0x00, 0xE, +0 }, + { 0x00BF002,0x037F702, 0xC0,0x00, 0x0, +0 }, + { 0x325FF25,0x0078003, 0xC0,0x00, 0x0, +0 }, + { 0x0089011,0x357894E, 0xC0,0x00, 0xE, +0 }, + { 0x11BF100,0x3468B5E, 0x00,0x00, 0xE, +0 }, + { 0x205508C,0x05C855D, 0x80,0x0A, 0xA, +0 }, + { 0x205504C,0x05C858D, 0x40,0x0A, 0x0, +0 }, + { 0x206F04B,0x346F610, 0x00,0x00, 0xE, +0 }, + { 0x392F700,0x2AF475E, 0x00,0x00, 0xE, +0 }, + { 0x30FF01D,0x0F0F715, 0x00,0x00, 0x1, +0 }, + { 0x0EB3402,0x0075004, 0x87,0x00, 0x0, +0 }, + { 0x0EF3301,0x0075002, 0xCB,0x00, 0x0, +0 }, + { 0x2B2FF04,0x2188719, 0x80,0x04, 0x0, +0 }, + { 0x27FFF06,0x204F009, 0x80,0x0A, 0x0, +0 }, + { 0x053F300,0x247694E, 0x43,0x00, 0xE, +0 }, + { 0x224F10E,0x335FF4E, 0x40,0x02, 0x0, +0 }, + { 0x274F911,0x108F010, 0x41,0x00, 0x2, +0 }, + { 0x288F911,0x004F010, 0xC1,0x03, 0x4, +0 }, + { 0x15DFD25,0x0079003, 0xC0,0x00, 0x0, +0 }, + { 0x015FF0E,0x0BFF800, 0x00,0x00, 0xE, +0 }, + { 0x008A000,0x1679810, 0x00,0x00, 0xE, +0 }, + { 0x104F041,0x308F009, 0xC0,0x00, 0xE, +0 }, + { 0x040F520,0x0F7F010, 0x0D,0x89, 0xA, +0 }, + { 0x060F101,0x07BD211, 0x4D,0x00, 0x8, +0 }, + { 0x013F202,0x043F502, 0x22,0x00, 0xE, +0 }, + { 0x0F0FB3E,0x09BA0B1, 0x29,0x40, 0x0, +0 }, + { 0x00381A5,0x005F1B1, 0xD2,0x40, 0x2, +0 }, + { 0x0F466E1,0x086B0E1, 0x13,0x00, 0xC, +0 }, + { 0x0014171,0x03B92A1, 0x1C,0x00, 0xE, +0 }, + { 0x0064131,0x03792A1, 0x1A,0x80, 0xC, +0 }, + { 0x175A563,0x045A421, 0x0F,0x8D, 0x0, +0 }, + { 0x002A474,0x04245D7, 0x47,0x40, 0x6, +0 }, + { 0x05331C5,0x07242D9, 0x8F,0x00, 0x6, +0 }, + { 0x1F07151,0x1856092, 0x91,0x80, 0xA, +0 }, + { 0x3D3B1E1,0x1741221, 0x4F,0x00, 0x6, +0 }, + { 0x00FF071,0x15F63B2, 0x8D,0x80, 0xA, +0 }, + { 0x175F502,0x0358501, 0x1A,0x88, 0x0, +0 }, + { 0x053F101,0x053F108, 0x40,0x40, 0x0, +0 }, + { 0x040F520,0x0F7F010, 0x0D,0x90, 0xA, +0 }, + { 0x0A4F3F0,0x1F5F460, 0x00,0x07, 0x8, +0 }, + { 0x0051F21,0x00A7121, 0x98,0x00, 0x2, +0 }, + { 0x03FFA10,0x064F210, 0x86,0x0C, 0xE, +0 }, + { 0x0013171,0x03BF2A1, 0x1C,0x00, 0xE, +0 }, + { 0x0754231,0x0F590A1, 0x98,0x80, 0xC, +0 }, + { 0x0044131,0x034F2A1, 0x1A,0x80, 0xC, +0 }, + { 0x0289130,0x048C131, 0x58,0x0E, 0xE, +0 }, + { 0x0F463E0,0x08670E1, 0x1E,0x00, 0xC, +0 }, + { 0x2034122,0x10561F2, 0x4F,0x80, 0x2, +0 }, + { 0x0175331,0x03B92A1, 0x18,0x80, 0xC, +0 }, + { 0x00B5131,0x03BA2A1, 0x1C,0x40, 0xE, +0 }, + { 0x03A4331,0x00AAA21, 0x1C,0x00, 0xC, +0 }, + { 0x1FAF000,0x1FAF211, 0x02,0x85, 0x6, +0 }, + { 0x1A57121,0x0958121, 0x17,0x00, 0xE, +0 }, + { 0x0AE7161,0x02E8160, 0x1C,0x00, 0xE, +0 }, + { 0x054F606,0x0B3F241, 0x73,0x0E, 0x0, +0 }, + { 0x055F718,0x0D5E521, 0x23,0x0E, 0x0, +0 }, + { 0x0A21B14,0x0A4A0F0, 0x7F,0x7F, 0x2, +0 }, + { 0x05285E1,0x05662E1, 0x18,0x00, 0x0, +0 }, + { 0x3F0FB02,0x006F3C2, 0x00,0x0D, 0x0, +0 }, + { 0x2448711,0x0B68041, 0x00,0x84, 0x0, +0 }, + { 0x00FBF0C,0x004F001, 0x07,0x0A, 0x0, +0 }, + { 0x0F9F913,0x0047310, 0x86,0x06, 0x0, +0 }, + { 0x03FFA10,0x064F210, 0x86,0x06, 0xE, +0 }, + { 0x1F0F001,0x136F7E4, 0x00,0x0A, 0x0, +0 }, + { 0x277F810,0x006F311, 0x44,0x07, 0x8, +0 }, + { 0x200A01E,0x0FFF810, 0x00,0x0E, 0xE, +0 }, + { 0x018BF20,0x066F800, 0x00,0x11, 0xE, +0 }, + { 0x0FFF902,0x0FFF811, 0x19,0x06, 0x0, +0 }, + { 0x215CF3E,0x0F9D92E, 0x00,0x11, 0xE, +0 }, + { 0x2A0B26E,0x2D4960E, 0x00,0x00, 0xE, +0 }, + { 0x2E0136E,0x1D4A502, 0x00,0x00, 0x0, +0 }, + { 0x025F522,0x005EF24, 0x95,0x9A, 0xE, +0 }, + { 0x004EF26,0x0065F24, 0xA1,0x07, 0xE, +0 }, + { 0x1047B20,0x072F521, 0x4B,0x00, 0xE, +0 }, + { 0x019992F,0x0BFFAA2, 0x00,0x22, 0xE, +0 }, + { 0x015FAA1,0x00B7F21, 0x55,0x08, 0xE, +0 }, + { 0x0137221,0x0B26425, 0x94,0x3E, 0xC, +0 }, + { 0x0739321,0x0099DA1, 0x38,0x04, 0xC, +0 }, + { 0x0298421,0x0CFF828, 0x9C,0xB2, 0xE, +0 }, + { 0x0187521,0x00A9F21, 0x22,0x07, 0xE, +0 }, + { 0x0F3F211,0x034F2E1, 0x0F,0x00, 0xA, +0 }, + { 0x1039761,0x004C770, 0x41,0x00, 0x3, +0 }, + { 0x00221C1,0x014B421, 0x1A,0x00, 0xE, +0 }, + { 0x001F2F1,0x02562E1, 0xCE,0x40, 0x6, +0 }, + { 0x212F1C2,0x054F743, 0x25,0x03, 0xE, +0 }, + { 0x2017230,0x2269420, 0x1C,0x00, 0xE, +0 }, + { 0x021A161,0x116C2A1, 0x92,0x40, 0x6, +0 }, + { 0x046A502,0x044F901, 0x64,0x80, 0x0, +0 }, + { 0x175F403,0x0F4F301, 0x31,0x83, 0xE, +0 }, + { 0x0858300,0x0C872A0, 0x2A,0x80, 0x6, +0 }, + { 0x0437721,0x006A5E1, 0x25,0x80, 0x8, +0 }, + { 0x0177423,0x017C563, 0x83,0x8D, 0x7, +0 }, + { 0x0187132,0x038B2A1, 0x9A,0x82, 0xC, +0 }, + { 0x0065231,0x037F2A1, 0x1B,0x80, 0xE, +0 }, + { 0x060F207,0x072F212, 0x13,0x00, 0x8, +0 }, + { 0x036BA02,0x015F901, 0x0A,0x00, 0x4, +0 }, + { 0x024F621,0x014C421, 0x13,0x80, 0x0, +0 }, + { 0x025F521,0x015C521, 0x17,0x80, 0x0, +0 }, + { 0x02C6621,0x014A521, 0x17,0x80, 0x0, +0 }, + { 0x064E400,0x074A400, 0x00,0x00, 0x7, +0 }, + { 0x2F0F009,0x047F920, 0x0D,0x00, 0xE, +0 }, + { 0x0F6E901,0x006D600, 0x15,0x00, 0xE, +0 }, + { 0x0F0F280,0x0F4F480, 0x00,0x00, 0x4, +0 }, + { 0x003F1C0,0x00110BE, 0x4F,0x0C, 0x2, +0 }, + { 0x202FF8E,0x3F6F601, 0x00,0x00, 0x8, +0 }, + { 0x202FF8E,0x3F7F701, 0x00,0x00, 0x8, +0 }, + { 0x053F101,0x074F131, 0x4B,0x00, 0x4, +0 }, + { 0x053F201,0x064F311, 0x49,0x00, 0x6, +0 }, + { 0x053F201,0x064F331, 0x50,0x00, 0x4, +0 }, + { 0x078C423,0x048C231, 0x99,0x00, 0x8, +0 }, + { 0x098C423,0x058C231, 0x97,0x00, 0x6, +0 }, + { 0x088C423,0x048C231, 0x5E,0x00, 0x0, +0 }, + { 0x05AC421,0x03AC231, 0x4E,0x00, 0x6, +0 }, + { 0x056B301,0x056B301, 0x8D,0x00, 0x8, +0 }, + { 0x019D0A3,0x017F021, 0x5C,0x80, 0xC, +0 }, + { 0x018D0A3,0x018F021, 0x64,0x80, 0x0, +0 }, + { 0x018F6B3,0x008F131, 0x61,0x00, 0x2, +0 }, + { 0x09EAAB3,0x03E80A1, 0x08,0x00, 0x6, +0 }, + { 0x1239723,0x0144571, 0x93,0x00, 0x4, +0 }, + { 0x12497A1,0x0145571, 0x0D,0x80, 0x2, +0 }, + { 0x1249761,0x0144571, 0x8F,0x00, 0xA, +0 }, + { 0x000A121,0x0F6F236, 0x80,0x00, 0x8, +0 }, + { 0x085F211,0x0B7F212, 0x87,0x80, 0x4, +0 }, + { 0x054F607,0x0B6F242, 0x73,0x00, 0x0, +0 }, + { 0x054F60E,0x0B6F242, 0x73,0x00, 0x0, +0 }, + { 0x1E26301,0x01E8821, 0x46,0x00, 0x6, +0 }, + { 0x24D7520,0x01D8921, 0x8B,0x80, 0xA, +0 }, + { 0x01C6421,0x03CD621, 0xC4,0x00, 0xA, +0 }, + { 0x03C6421,0x01CA621, 0x4A,0x00, 0x8, +0 }, + { 0x008F321,0x228F322, 0x92,0x80, 0xA, +0 }, + { 0x028F331,0x038B1B1, 0x92,0x00, 0xA, +0 }, + { 0x002DB77,0x0125831, 0xE0,0x00, 0x8, +0 }, + { 0x00211B1,0x0034231, 0x93,0x80, 0x0, +0 }, + { 0x0023AB1,0x0134232, 0xAF,0x80, 0x0, +0 }, + { 0x2556823,0x1055461, 0xD2,0x00, 0xA, +0 }, + { 0x05312C4,0x07212F1, 0x10,0x00, 0x2, +0 }, + { 0x1D6FB34,0x0269471, 0x83,0x00, 0xC, +0 }, + { 0x061F217,0x074F212, 0x4F,0x00, 0x8, +0 }, + { 0x0096821,0x01B5731, 0x11,0x80, 0xA, +0 }, + { 0x02FA433,0x0117575, 0x14,0x00, 0x0, +0 }, + { 0x078F71A,0x0024691, 0xC6,0x00, 0x2, +0 }, + { 0x0287C31,0x01AAB23, 0x91,0x00, 0xA, +0 }, + { 0x0124D01,0x013F501, 0x02,0x00, 0x7, +0 }, + { 0x118D671,0x018F571, 0x1E,0x00, 0xC, +0 }, + { 0x0287271,0x0186361, 0x95,0x00, 0xC, +0 }, + { 0x054F589,0x023F582, 0x5E,0x07, 0x2, +0 }, + { 0x20FFF22,0x00FFF21, 0x5A,0x80, 0x0, +0 }, + { 0x125F121,0x0087262, 0x56,0x00, 0xE, +0 }, + { 0x121F131,0x0166F21, 0x40,0x00, 0x2, +0 }, + { 0x1388231,0x0086821, 0x4B,0x00, 0x0, +0 }, + { 0x175F502,0x0F8F501, 0x58,0x80, 0x0, +0 }, + { 0x11561B1,0x00562A1, 0x16,0x00, 0x8, +0 }, + { 0x01351A1,0x0175221, 0x1E,0x80, 0xE, +0 }, + { 0x1145131,0x00552A1, 0x92,0x00, 0xA, +0 }, + { 0x12CF131,0x01C61B1, 0x8F,0x00, 0x8, +0 }, + { 0x1228131,0x0167223, 0x4D,0x80, 0x2, +0 }, + { 0x171D201,0x238F301, 0x55,0x00, 0x2, +0 }, + { 0x114F413,0x013F201, 0x49,0x80, 0x6, +0 }, + { 0x154F203,0x044F301, 0x4C,0x40, 0x4, +0 }, + { 0x119F523,0x019F421, 0x51,0x00, 0xC, +0 }, + { 0x1547003,0x004B301, 0x51,0x80, 0xC, +0 }, + { 0x05FF561,0x02AF562, 0x21,0x00, 0x2, +0 }, + { 0x018F221,0x018F521, 0x0F,0x80, 0x6, +0 }, + { 0x038F2A1,0x018F321, 0x93,0x00, 0xA, +0 }, + { 0x13FF631,0x01FF321, 0x89,0x40, 0xA, +0 }, + { 0x13FF431,0x01FF221, 0x88,0x40, 0xA, +0 }, + { 0x04F6421,0x028F231, 0x91,0x00, 0xA, +0 }, + { 0x05FF561,0x05A6661, 0x1E,0x00, 0x2, +0 }, + { 0x05FF561,0x02A7561, 0x1E,0x07, 0x2, +0 }, + { 0x03FF561,0x01A7562, 0x28,0x04, 0x2, +0 }, + { 0x01F7561,0x02A7561, 0x21,0x00, 0x2, +0 }, + { 0x05F8571,0x01A6661, 0x51,0x00, 0xC, +0 }, + { 0x13F93B1,0x01F6221, 0x45,0x80, 0x8, +0 }, + { 0x13FA3B1,0x00F8221, 0x89,0x80, 0x8, +0 }, + { 0x13F86B1,0x00F7221, 0x8F,0x80, 0xC, +0 }, + { 0x137C6B1,0x0067221, 0x87,0x80, 0xC, +0 }, + { 0x0217B32,0x0176221, 0x95,0x00, 0x0, +0 }, + { 0x0219B32,0x0176221, 0x97,0x00, 0x0, +0 }, + { 0x0115231,0x11E3132, 0xC5,0x00, 0x8, +0 }, + { 0x1177E31,0x10C8B21, 0x43,0x00, 0x2, +0 }, + { 0x019D520,0x11B6121, 0x93,0x00, 0xC, +0 }, + { 0x0069161,0x0076161, 0x12,0x00, 0xA, +0 }, + { 0x00D5131,0x01F7221, 0x1C,0x80, 0xE, +0 }, + { 0x13DC231,0x00F7761, 0x8A,0x80, 0xA, +0 }, + { 0x02DF431,0x00F7321, 0x8B,0x80, 0x6, +0 }, + { 0x02DA831,0x00F8321, 0x8B,0x80, 0x6, +0 }, + { 0x06A6121,0x00A7F21, 0x26,0x00, 0x2, +0 }, + { 0x01C8D21,0x00FA521, 0x90,0x00, 0xA, +0 }, + { 0x01F75A1,0x00F7422, 0x10,0x00, 0x8, +0 }, + { 0x11F75A0,0x01F7521, 0x15,0x00, 0xC, +0 }, + { 0x033F5C5,0x025FDE1, 0x53,0x80, 0xA, +0 }, + { 0x013F5C5,0x005FDE1, 0x59,0x80, 0xA, +0 }, + { 0x0248305,0x014A301, 0x66,0x00, 0x2, +0 }, + { 0x031A585,0x011F511, 0xD3,0x80, 0x2, +0 }, + { 0x033F284,0x022F211, 0xC7,0x80, 0xA, +0 }, + { 0x122F210,0x012FC11, 0xC9,0x00, 0x6, +0 }, + { 0x206FB03,0x006D901, 0xD2,0x00, 0x4, +0 }, + { 0x024D443,0x004E741, 0x51,0x40, 0x8, +0 }, + { 0x05FF561,0x01A6661, 0x1E,0x00, 0x2, +0 }, + { 0x0275722,0x0275661, 0x59,0x40, 0xB, +0 }, + { 0x0175622,0x0176361, 0xA7,0x40, 0x5, +0 }, + { 0x205A8F1,0x00563B1, 0x9B,0x00, 0xA, +0 }, + { 0x05F8571,0x00A6B61, 0x4B,0x00, 0xC, +0 }, + { 0x105F510,0x0C3F211, 0x47,0x00, 0x2, +0 }, + { 0x247F811,0x054F311, 0x47,0x00, 0x4, +0 }, + { 0x21AF400,0x008F800, 0x00,0x00, 0xC, +0 }, + { 0x01AF400,0x038F800, 0x00,0x00, 0xA, +0 }, + { 0x079F400,0x017F600, 0x03,0x00, 0xA, +0 }, + { 0x007A810,0x115DA00, 0x06,0x00, 0x6, +0 }, + { 0x009A810,0x107DF10, 0x07,0x00, 0xE, +0 }, + { 0x334F407,0x2D4F415, 0x00,0x00, 0xE, +0 }, + { 0x0F4000A,0x0F6F717, 0x3F,0x00, 0x1, +0 }, + { 0x0F2E00E,0x033FF1E, 0x5E,0x40, 0x8, +0 }, + { 0x0645451,0x045A581, 0x00,0x00, 0xA, +0 }, + { 0x261B235,0x0B2F112, 0x5C,0x08, 0xA, +0 }, + { 0x38CF800,0x06BF600, 0x80,0x00, 0xF, +0 }, + { 0x060F207,0x072F212, 0x54,0x80, 0x4, +0 }, + { 0x0557542,0x0257541, 0x96,0x87, 0x8, +0 }, + { 0x268F911,0x005F211, 0x46,0x00, 0x8, +0 }, + { 0x14BFA01,0x03BFA08, 0x08,0x00, 0xD, +0 }, + { 0x007FF21,0x107F900, 0x80,0x00, 0xE, +0 }, + { 0x20DFF20,0x027FF02, 0x00,0x00, 0xE, +0 }, + { 0x0C8F60C,0x257FF12, 0xC2,0x00, 0xC, +0 }, + { 0x000F60E,0x3059F10, 0x00,0x00, 0xE, +0 }, + { 0x000F60E,0x3039F10, 0x00,0x00, 0xE, +0 }, + { 0x0C5F59E,0x2F7F70E, 0x00,0x00, 0xF, +0 }, + { 0x2B7F811,0x003F310, 0x45,0x00, 0x8, +0 }, + { 0x0BFFA01,0x097C803, 0x00,0x00, 0x7, +0 }, + { 0x08DFA01,0x0BAFA03, 0x4F,0x00, 0x7, +0 }, + { 0x38FF801,0x06FF600, 0x47,0x00, 0xF, +0 }, + { 0x38CF800,0x06EF600, 0x80,0x00, 0xF, +0 }, + { 0x38CF803,0x0B5F80C, 0x80,0x00, 0xF, +0 }, + { 0x38CF803,0x0B5F80C, 0x83,0x00, 0xF, +0 }, + { 0x0DFF611,0x0DEF710, 0x4F,0x40, 0xC, +0 }, + { 0x053F101,0x0F3F211, 0x4F,0x80, 0x4, +0 }, + { 0x1C5C202,0x104D000, 0x11,0x00, 0xC, +0 }, + { 0x2129A16,0x0039012, 0x97,0x04, 0x2, +0 }, + { 0x0F3F507,0x0F2F501, 0x19,0x00, 0xA, +0 }, + { 0x2F3F507,0x0F2F501, 0x19,0x00, 0xA, +0 }, + { 0x0229F16,0x032B0D2, 0x16,0x00, 0x8, +0 }, + { 0x025DA05,0x015F001, 0x4E,0x00, 0xA, +0 }, + { 0x025C811,0x0F2F511, 0x29,0x00, 0xC, +0 }, + { 0x012FF54,0x0F2F051, 0x16,0x00, 0x0, +0 }, + { 0x212FF54,0x0F2F051, 0x16,0x00, 0x0, +0 }, + { 0x106DF24,0x005FF21, 0x15,0x00, 0x1, +0 }, + { 0x104F223,0x0045231, 0x50,0x80, 0xE, +0 }, + { 0x00BF223,0x00B5230, 0x4F,0x82, 0xE, +0 }, + { 0x2036162,0x0058172, 0x4A,0x00, 0x2, +0 }, + { 0x01CF201,0x087F501, 0x10,0x00, 0xA, +0 }, + { 0x014F201,0x084F501, 0x10,0x00, 0xA, +0 }, + { 0x103AF00,0x3FFF021, 0x06,0x00, 0x6, +0 }, + { 0x025DA05,0x06A5334, 0x8E,0x00, 0xA, +0 }, + { 0x035F813,0x004FF11, 0x12,0x03, 0x8, +0 }, + { 0x0114172,0x01562A2, 0x89,0x40, 0xA, +0 }, + { 0x0F9F121,0x0F6F721, 0x1C,0x00, 0xE, +0 }, + { 0x075F502,0x0F3F201, 0x29,0x00, 0x0, +0 }, + { 0x005FF00,0x0F3F020, 0x18,0x00, 0x0, +0 }, + { 0x0114172,0x01562A1, 0x89,0x40, 0xA, +0 }, + { 0x2A32321,0x1F34221, 0x1A,0x00, 0x8, +0 }, + { 0x010A130,0x0337D10, 0x07,0x00, 0x0, +0 }, + { 0x01D5320,0x03B6261, 0x18,0x00, 0xA, +0 }, + { 0x01572A1,0x02784A1, 0x17,0x00, 0xE, +0 }, + { 0x05A5321,0x01A8A21, 0x9F,0x00, 0xC, +0 }, + { 0x0009F71,0x0069060, 0x51,0x00, 0x0, +0 }, + { 0x0009F71,0x0069062, 0x51,0x00, 0x0, +0 }, + { 0x0077061,0x0077062, 0x80,0x80, 0x7, +0 }, + { 0x0077061,0x0077041, 0x80,0x80, 0x7, +0 }, + { 0x0F7F000,0x00687A2, 0x30,0x00, 0xF, +0 }, + { 0x2129A16,0x1039012, 0x97,0x04, 0x2, +0 }, + { 0x0037165,0x0076171, 0xD2,0x00, 0x2, +0 }, + { 0x0011E00,0x0A11220, 0x40,0x40, 0x6, +0 }, + { 0x0059221,0x1059421, 0x1C,0x00, 0xE, +0 }, + { 0x044FF25,0x033F324, 0x15,0x01, 0xC, +0 }, + { 0x0132F20,0x0132321, 0x0D,0x00, 0x1, +0 }, + { 0x0012E01,0x0216221, 0x40,0x40, 0x6, +0 }, + { 0x3134362,0x0038261, 0x2E,0x00, 0x2, +0 }, + { 0x2035FE6,0x00350E1, 0x0F,0x00, 0x3, +0 }, + { 0x3034F61,0x0035061, 0x0D,0x00, 0x9, +0 }, + { 0x1034F61,0x0035061, 0x00,0x00, 0x9, +0 }, + { 0x3033F60,0x0033061, 0x0D,0x00, 0x7, +0 }, + { 0x112FF53,0x0F1F071, 0x13,0x00, 0x0, +0 }, + { 0x112FFD1,0x0F1F0F1, 0x12,0x00, 0x0, +0 }, + { 0x0E11126,0x0E11120, 0xA5,0x00, 0x0, +0 }, + { 0x30244A1,0x04245E1, 0x51,0x00, 0x2, +0 }, + { 0x0E1A126,0x0E1A120, 0xA5,0x0E, 0x0, +0 }, + { 0x054F101,0x004F008, 0x40,0x00, 0x0, +0 }, + { 0x011A131,0x0437D16, 0x47,0x40, 0x8, +0 }, + { 0x211A131,0x0437D11, 0x14,0x00, 0x0, +0 }, + { 0x091AB0E,0x0C3F702, 0xC0,0x00, 0xE, +0 }, + { 0x02FC811,0x0F5F431, 0x2D,0x00, 0xC, +0 }, + { 0x1176E31,0x20CAB22, 0x43,0x08, 0x2, +0 }, + { 0x1176E31,0x20CAB22, 0x4F,0x08, 0x2, +0 }, + { 0x002FF64,0x0F3F522, 0xDB,0x02, 0x4, +0 }, + { 0x001FF63,0x0F3F534, 0xDB,0x00, 0x2, +0 }, + { 0x0FFFB13,0x0FFE802, 0x40,0x00, 0x8, +0 }, + { 0x108FF00,0x006F000, 0x00,0x00, 0x0, +0 }, + { 0x0F1100E,0x0F61800, 0x00,0x00, 0xE, +0 }, + { 0x1F18F2A,0x1F63816, 0x00,0x00, 0x8, +0 }, + { 0x0F0102E,0x2821020, 0x00,0x00, 0xE, +0 }, + { 0x201EFEE,0x0069FEE, 0x10,0x04, 0x6, +0 }, + { 0x201EFEE,0x0069FEE, 0x01,0x04, 0x6, +0 }, + { 0x001F02E,0x0064820, 0x00,0x00, 0xE, +0 }, + { 0x3EFF71C,0x08FFD0E, 0x00,0x00, 0xF, +0 }, + { 0x202FF0E,0x103FF1E, 0x00,0x80, 0xE, +0 }, + { 0x202BF8E,0x2049F0E, 0x00,0x00, 0xE, +0 }, + { 0x003FF64,0x0F6F73E, 0xDB,0x00, 0x4, +0 }, + { 0x100F300,0x054F600, 0x00,0x00, 0xC, +0 }, + { 0x2F3F40C,0x3D66E0E, 0x00,0x00, 0xE, +0 }, + { 0x07B9C21,0x0FB9502, 0x0A,0x00, 0x8, +0 }, + { 0x0778121,0x0879221, 0x17,0x00, 0xA, +0 }, + { 0x1237221,0x0075121, 0x1A,0x06, 0xE, +0 }, + { 0x0295231,0x0197121, 0x1E,0x04, 0xE, +0 }, + { 0x0187621,0x0098121, 0x1A,0x05, 0xE, +0 }, + { 0x0167921,0x05971A1, 0x1F,0x00, 0x8, +0 }, + { 0x0257521,0x0178421, 0x1A,0x81, 0xE, +0 }, + { 0x0586221,0x0167221, 0x22,0x01, 0xE, +0 }, + { 0x10759B1,0x00A7BA1, 0x1B,0x05, 0x0, +0 }, + { 0x020A821,0x10A7B23, 0x0F,0x05, 0xC, +0 }, + { 0x1378CA1,0x00A7724, 0x0A,0x07, 0x0, +0 }, + { 0x06BFF31,0x0195175, 0x04,0x03, 0xA, +0 }, + { 0x0599BA1,0x00A75E1, 0x8C,0x00, 0x0, +0 }, + { 0x0389F22,0x0296761, 0x1D,0x01, 0x0, +0 }, + { 0x00C9222,0x00DA261, 0x1D,0x03, 0xE, +0 }, + { 0x1C99223,0x1288222, 0x00,0x00, 0x9, -12 }, + { 0x2863428,0x0354121, 0x39,0x07, 0x0, +0 }, + { 0x1F35224,0x1F53223, 0x12,0x09, 0x4, +0 }, + { 0x1D52222,0x1053F21, 0x10,0x88, 0xA, +0 }, + { 0x1554163,0x10541A2, 0x00,0x00, 0x7, -12 }, + { 0x0F7F620,0x2F9770E, 0x08,0x05, 0x0, -24 }, + { 0x100F220,0x1053623, 0x04,0x00, 0x2, -36 }, + { 0x0EFA120,0x0DFF310, 0x00,0x00, 0xE, +0 }, + { 0x000FF24,0x0A9F802, 0x00,0x03, 0xE, +0 }, + { 0x0FEF22C,0x3D8B802, 0x00,0x01, 0x6, +0 }, + { 0x0FE822C,0x3D98802, 0x00,0x07, 0x6, +0 }, + { 0x0F6822E,0x3F87404, 0x00,0x09, 0x4, +0 }, + { 0x100FF2E,0x334D609, 0x00,0x01, 0xC, +0 }, + { 0x389F837,0x0F8F703, 0x0C,0x04, 0x0, +0 }, + { 0x0FAFA25,0x0F9AA03, 0x14,0x00, 0x0, +0 }, + { 0x0F7F241,0x0F7F281, 0x12,0x00, 0x6, +0 }, + { 0x10BD0E0,0x109E0A4, 0x80,0x8E, 0x1, +0 }, + { 0x0F4F60C,0x0F5F341, 0x5C,0x00, 0x0, +0 }, + { 0x1557261,0x0187121, 0x86,0x83, 0x0, +0 }, + { 0x09612F3,0x10430B1, 0x45,0x86, 0x1, +0 }, + { 0x204F061,0x2055020, 0x9D,0x83, 0xC, +0 }, + { 0x236F312,0x2D7B300, 0x2A,0x00, 0x0, +0 }, + { 0x143F701,0x1E4F3A2, 0x00,0x00, 0x8, +0 }, + { 0x35B8721,0x00A6021, 0x99,0x00, 0xE, +0 }, + { 0x0F3D385,0x0F3A341, 0x59,0x80, 0xC, +0 }, + { 0x125FF10,0x015F711, 0x56,0x00, 0xE, +0 }, + { 0x04AFA02,0x074F490, 0x16,0x01, 0xE, +0 }, + { 0x045F668,0x0289E87, 0x00,0x01, 0x6, +0 }, + { 0x164F923,0x177F607, 0x95,0x00, 0xE, +0 }, + { 0x0E7F21C,0x0B8F201, 0x6F,0x80, 0xC, +12 }, + { 0x0E2CE02,0x4E2F402, 0x25,0x00, 0x0, +0 }, + { 0x0E2F507,0x0E2F341, 0xA1,0x00, 0x0, +0 }, + { 0x2E5F5D9,0x0E5F251, 0x22,0x00, 0x8, +0 }, + { 0x0E1F111,0x0E1F251, 0x10,0x08, 0x9, +0 }, + { 0x4B1F0C9,0x0B2F251, 0x98,0x01, 0x8, +0 }, + { 0x082F311,0x0E3F311, 0x44,0x80, 0x9, +0 }, + { 0x0828523,0x0728212, 0xB3,0xA7, 0xE, +0 }, + { 0x0728201,0x0328411, 0x27,0x00, 0xE, +0 }, + { 0x4E5F111,0x4E5F312, 0xA1,0x40, 0x4, -12 }, + { 0x0E5F111,0x0E6F111, 0x89,0x00, 0x5, +0 }, + { 0x5047130,0x01474A0, 0x99,0x01, 0xE, +12 }, + { 0x1147561,0x0147522, 0x88,0x00, 0xF, +0 }, + { 0x5047130,0x01474A0, 0x99,0x01, 0xE, +0 }, + { 0x0141161,0x0165561, 0x17,0x00, 0xC, +12 }, + { 0x7217230,0x604BF31, 0x1B,0x03, 0xC, +0 }, + { 0x0357A31,0x03A7A31, 0x1D,0x09, 0xD, +0 }, + { 0x06599E1,0x0154825, 0x80,0x85, 0x8, +0 }, + { 0x015AA62,0x0058F21, 0x94,0x80, 0x9, +0 }, + { 0x025C9A4,0x0056F21, 0xA2,0x80, 0xC, +0 }, + { 0x015CAA2,0x0056F21, 0xAA,0x00, 0xD, +0 }, + { 0x07E0824,0x0E4E383, 0x80,0x40, 0xA, +24 }, + { 0x0E6F314,0x0E6F281, 0x63,0x00, 0xB, +0 }, + { 0x205FC00,0x017FA00, 0x40,0x00, 0xE, +0 }, + { 0x007FC00,0x638F801, 0x00,0x80, 0xF, +0 }, + { 0x0038165,0x005F172, 0xD2,0x80, 0x2, +0 }, + { 0x0038165,0x005F171, 0xD2,0x40, 0x2, +0 }, + { 0x002A4B4,0x04245D7, 0x47,0x40, 0x6, +0 }, + { 0x0022A55,0x0F34212, 0x97,0x80, 0x0, +0 }, + { 0x001EF8F,0x0F19801, 0x81,0x00, 0x4, +0 }, + { 0x01171B1,0x1154261, 0x8B,0x40, 0x6, +0 }, + { 0x053090E,0x094F702, 0x80,0x00, 0xE, +0 }, + { 0x08F74A1,0x02A65A1, 0x27,0x80, 0x2, +0 }, + { 0x0667190,0x08B5250, 0x92,0x00, 0xE, +0 }, + { 0x0247332,0x0577521, 0x16,0x80, 0xE, +0 }, + { 0x28FA520,0x03D3621, 0x8E,0x00, 0x6, +0 }, + { 0x08C4321,0x02F8521, 0x19,0x80, 0xC, +0 }, + { 0x0AE71A1,0x02E81A0, 0x1C,0x00, 0xE, +0 }, + { 0x054F606,0x0B3F281, 0x73,0x03, 0x0, +0 }, + { 0x0177421,0x01765A2, 0x83,0x8D, 0x7, +0 }, + { 0x0F3F8E2,0x0F3F7B0, 0x86,0x40, 0x4, +0 }, + { 0x0031801,0x090F6B4, 0x80,0xC1, 0xE, +0 }, + { 0x04CA800,0x04FD600, 0x0B,0x03, 0x0, +0 }, + { 0x282B2A4,0x1DA9803, 0x00,0x93, 0xE, +0 }, + { 0x0A0B2A4,0x1D69603, 0x02,0x80, 0xE, +0 }, + { 0x104F0A1,0x1D6F481, 0xCE,0x00, 0x4, +0 }, + { 0x254F568,0x0B7F321, 0xE8,0x00, 0x0, +0 }, + { 0x036F506,0x025FD61, 0x10,0x80, 0x3, +0 }, + { 0x092FF83,0x003F015, 0x00,0x00, 0xE, -12 }, + { 0x00FF0A0,0x00FF0A2, 0xC0,0x06, 0xD, +0 }, + { 0x00BF022,0x10B50B1, 0xCD,0x03, 0x0, +0 }, + { 0x3598600,0x02A7244, 0x42,0x80, 0xC, +0 }, + { 0x039F330,0x00CF060, 0x0F,0x00, 0x8, +12 }, + { 0x1378D31,0x0163871, 0x85,0x00, 0xA, +0 }, + { 0x106F031,0x1065071, 0xC5,0x00, 0x0, +0 }, + { 0x11FF431,0x1365361, 0x40,0x00, 0x0, +0 }, + { 0x01FF431,0x1366361, 0xC0,0x00, 0x0, +0 }, + { 0x043F2B1,0x12851A1, 0x1D,0x00, 0xE, +0 }, + { 0x015F431,0x00560B2, 0x5B,0x83, 0x0, +0 }, + { 0x172FCE1,0x0176271, 0x46,0x00, 0x0, +0 }, + { 0x00530B1,0x00550B2, 0x57,0x00, 0xC, +0 }, + { 0x0655371,0x00FF021, 0x14,0x00, 0xA, +0 }, + { 0x0254231,0x00FF061, 0x56,0x01, 0xE, +0 }, + { 0x1255221,0x0299361, 0x55,0x01, 0xE, +0 }, + { 0x0755471,0x0089021, 0x20,0x00, 0xE, +0 }, + { 0x0043071,0x00A5021, 0x57,0x00, 0xC, +0 }, + { 0x0445171,0x00A5021, 0x55,0x00, 0xC, +0 }, + { 0x35AF802,0x02A4271, 0x00,0x00, 0xE, +0 }, + { 0x08F4EE0,0x02A55A1, 0xEC,0x00, 0xE, +0 }, + { 0x39D6571,0x0095021, 0x17,0x00, 0xE, +0 }, + { 0x05AF802,0x22A4270, 0x00,0x00, 0xE, +0 }, + { 0x29D65A1,0x2095021, 0xC6,0x00, 0x0, -12 }, + { 0x00330B1,0x00440B2, 0x5D,0x00, 0x0, +0 }, + { 0x0585201,0x03641A1, 0x99,0x00, 0x6, +0 }, + { 0x0B4F291,0x075F101, 0xD0,0x00, 0x0, +0 }, + { 0x0572132,0x0194263, 0x06,0x00, 0x9, -12 }, + { 0x3859F85,0x043F311, 0x15,0x00, 0xE, +0 }, + { 0x295F300,0x2B9F2A0, 0x11,0x00, 0x0, +0 }, + { 0x2A3F400,0x2B9F2A0, 0x1B,0x00, 0x0, +0 }, + { 0x2A2FF80,0x30E108E, 0x00,0x00, 0xE, +0 }, + { 0x003402E,0x003109E, 0x00,0x00, 0xE, +0 }, + { 0x2A3379B,0x237461A, 0x95,0x40, 0x0, +0 }, + { 0x344FFAB,0x02AF1EA, 0xC0,0x01, 0xC, -12 }, + { 0x10EF0BE,0x00E3030, 0x00,0x0A, 0xE, +0 }, + { 0x023FCC0,0x006F08E, 0x00,0x00, 0xE, +0 }, + { 0x004F081,0x308F009, 0xC0,0x00, 0xE, +0 }, + { 0x12FF201,0x356F58E, 0xC0,0x00, 0xE, +0 }, + { 0x12FF281,0x356F58E, 0xC0,0x00, 0xE, +0 }, + { 0x155AF00,0x364FF8B, 0x00,0x00, 0xE, +0 }, + { 0x1496401,0x356F58A, 0xC0,0x00, 0xE, +0 }, + { 0x2678900,0x357878E, 0x00,0x00, 0xE, +0 }, + { 0x02FF281,0x356F58E, 0xC0,0x00, 0x0, +0 }, + { 0x0089011,0x357898E, 0xC0,0x00, 0xE, +0 }, + { 0x11BF100,0x3468B9E, 0x00,0x00, 0xE, +0 }, + { 0x205504C,0x05C859D, 0x80,0x0A, 0xA, +0 }, + { 0x205508C,0x05C854D, 0x40,0x0A, 0x0, +0 }, + { 0x206F08B,0x346F610, 0x00,0x00, 0xE, +0 }, + { 0x392F700,0x2AF479E, 0x00,0x00, 0xE, +0 }, + { 0x053F300,0x247698E, 0x43,0x00, 0xE, +0 }, + { 0x224F10E,0x335FF8E, 0x40,0x02, 0x0, +0 }, + { 0x104F081,0x308F009, 0xC0,0x00, 0xE, +0 }, + { 0x215BFD1,0x20473C1, 0x9C,0x00, 0x4, +0 }, + { 0x177F810,0x008F711, 0x91,0x00, 0x6, +0 }, + { 0x277F810,0x108F311, 0xF9,0xC0, 0x6, +0 }, + { 0x25DFB14,0x058F611, 0x80,0x00, 0x8, +0 }, + { 0x12AF900,0x22BFA01, 0x02,0x00, 0x5, +0 }, + { 0x28268D1,0x10563D0, 0x42,0x00, 0xA, +0 }, + { 0x317B142,0x317B101, 0x93,0x00, 0x3, +0 }, + { 0x317B242,0x317B201, 0x93,0x00, 0x3, +0 }, + { 0x2BAE610,0x005EA10, 0x3F,0x3F, 0x0, +0 }, + { 0x053B101,0x074C211, 0x4F,0x00, 0x6, +0 }, + { 0x011F111,0x0B3F101, 0x4A,0x80, 0x6, +0 }, + { 0x1FAF000,0x1FAF211, 0x02,0x80, 0x6, +0 }, + { 0x032F607,0x012F511, 0x97,0x80, 0x2, +0 }, + { 0x0E3F318,0x093F241, 0x62,0x00, 0x0, +0 }, + { 0x025DA05,0x015F901, 0x4E,0x00, 0xA, +0 }, + { 0x1558403,0x005D341, 0x49,0x80, 0x4, +0 }, + { 0x01FF003,0x012F001, 0x5B,0x92, 0xA, +0 }, + { 0x01FF2A0,0x07CF521, 0x11,0x00, 0xA, +0 }, + { 0x0442009,0x0F4D144, 0xA1,0x80, 0x8, +0 }, + { 0x08AE220,0x0A8E420, 0x11,0x00, 0xA, +0 }, + { 0x1DBB891,0x1567551, 0x17,0x00, 0xC, +0 }, + { 0x0117171,0x11772A1, 0x8B,0x40, 0x6, +0 }, + { 0x111F0F1,0x1151121, 0x95,0x00, 0x0, +0 }, + { 0x111C071,0x1159221, 0x20,0x08, 0xC, +0 }, + { 0x0C57461,0x165B220, 0x0F,0x08, 0xA, +0 }, + { 0x08153E1,0x0B962E1, 0x9F,0x05, 0xE, +0 }, + { 0x0AE71E1,0x09E81E1, 0x19,0x07, 0xA, +0 }, + { 0x0AE73E1,0x09881E2, 0x49,0x08, 0xC, +0 }, + { 0x0177E71,0x00E7B22, 0xC5,0x05, 0x2, +0 }, + { 0x08F7461,0x02A6561, 0x27,0x80, 0x2, +0 }, + { 0x0D761E1,0x0F793E1, 0x85,0x80, 0xB, +0 }, + { 0x1F6FB34,0x0439471, 0x83,0x00, 0xC, +0 }, + { 0x011A131,0x0437D16, 0x87,0x80, 0x8, +0 }, + { 0x1111EF0,0x11111E2, 0x00,0xC0, 0x8, +0 }, + { 0x053F101,0x1F5F718, 0x4F,0x00, 0x6, +0 }, + { 0x20CA808,0x13FD903, 0x09,0x00, 0x0, +0 }, + { 0x0A1B2E0,0x1D6950E, 0x84,0x00, 0xE, +0 }, + { 0x286F265,0x228670E, 0x00,0x00, 0xE, +0 }, + { 0x00CFD01,0x034D600, 0x07,0x00, 0x0, +0 }, + { 0x00CF600,0x004F600, 0x00,0x00, 0x1, +0 }, + { 0x0FEF512,0x0FFF652, 0x11,0xA2, 0x6, +0 }, + { 0x0FFF941,0x0FFF851, 0x0F,0x00, 0x6, +0 }, + { 0x205FC80,0x017FA00, 0x00,0x00, 0xE, +0 }, + { 0x034A501,0x602FF01, 0x00,0x00, 0x7, +0 }, + { 0x007FB00,0x004A401, 0x09,0x00, 0x7, +0 }, + { 0x004F902,0x0F69705, 0x00,0x03, 0x0, +0 }, + { 0x156F284,0x100F442, 0x03,0x00, 0xE, +0 }, + { 0x000F34F,0x0A5F48F, 0x00,0x06, 0xE, +0 }, + { 0x0B6FA01,0x096C802, 0x8A,0x40, 0xE, +0 }, + { 0x00CF505,0x007F501, 0xEC,0x00, 0xF, +0 }, + { 0x0BFFA01,0x095C802, 0x8F,0x80, 0x6, +0 }, + { 0x00CF505,0x006F501, 0xEC,0x00, 0x7, +0 }, + { 0x08DFA01,0x0BAFA03, 0x4F,0x00, 0x6, +0 }, + { 0x08DFA01,0x0B5F803, 0x4F,0x00, 0x6, +0 }, + { 0x006FA01,0x006FA00, 0x00,0x00, 0xE, +0 }, + { 0x38CF800,0x06EF600, 0x80,0x00, 0xE, +0 }, + { 0x38CF803,0x0B5F80C, 0x80,0x00, 0xE, +0 }, + { 0x38CF803,0x0B5F80C, 0x83,0x00, 0xE, +0 }, + { 0x049C80F,0x2699B03, 0x40,0x00, 0xE, +0 }, + { 0x305AD57,0x2058D47, 0xDC,0x00, 0xE, +0 }, + { 0x304A857,0x2048847, 0xDC,0x00, 0xE, +0 }, + { 0x506FF80,0x016FF10, 0x00,0x00, 0xC, +0 }, + { 0x7476601,0x0476603, 0xCD,0x40, 0x8, +0 }, + { 0x0476601,0x0576601, 0xC0,0x00, 0x9, +0 }, + { 0x0E56701,0x0356503, 0x11,0x24, 0xA, +0 }, + { 0x0757900,0x0057601, 0x9A,0x00, 0xB, +0 }, + { 0x0E6F622,0x0E5F923, 0x1E,0x03, 0x0, +0 }, + { 0x0E6F924,0x0E4F623, 0x28,0x00, 0x1, +0 }, + { 0x0E6F522,0x0E5F623, 0x1E,0x03, 0x0, +0 }, + { 0x0E6F524,0x0E4F423, 0x28,0x00, 0x1, +0 }, + { 0x0E5F108,0x0E5C302, 0x66,0x86, 0x8, +0 }, + { 0x052F605,0x0D5F582, 0x69,0x47, 0x9, +0 }, + { 0x131FF13,0x003FF11, 0x43,0x00, 0x6, +0 }, + { 0x074A302,0x075C401, 0x9A,0x80, 0xA, +0 }, + { 0x103E702,0x005E604, 0x86,0x40, 0xB, +0 }, + { 0x0145321,0x025D221, 0x8B,0x21, 0x8, +0 }, + { 0x104C3A1,0x0158221, 0x9F,0x0F, 0x8, +0 }, + { 0x075F502,0x0F3F201, 0x20,0x83, 0xC, +0 }, + { 0x7D2FE85,0x074F342, 0x8F,0x80, 0x6, -12 }, + { 0x0119131,0x11572A1, 0x8A,0x00, 0x6, +0 }, + { 0x0013121,0x10545A1, 0x4D,0x82, 0x6, +0 }, + { 0x0075131,0x0399261, 0x1D,0x80, 0xE, +0 }, + { 0x00741B1,0x0398221, 0x1C,0x87, 0xF, +0 }, + { 0x21A73A0,0x03A8523, 0x95,0x00, 0xE, +0 }, + { 0x05A5321,0x01A6C21, 0x9F,0x80, 0xC, +0 }, + { 0x0565321,0x0277C21, 0x18,0x00, 0xD, +0 }, + { 0x0299960,0x036F823, 0xA3,0x5D, 0xA, +12 }, + { 0x015FAA0,0x00B8F22, 0x90,0x08, 0xA, +0 }, + { 0x22871A0,0x01A8124, 0x23,0x00, 0xA, +0 }, + { 0x2287320,0x01A8424, 0x97,0x98, 0xB, +0 }, + { 0x0068B20,0x0008F21, 0x2F,0x20, 0xE, +12 }, + { 0x007CF20,0x0097F22, 0x5B,0x00, 0xE, +0 }, + { 0x0277784,0x01655A1, 0x9B,0x85, 0xC, +0 }, + { 0x01566A2,0x00566A1, 0x9B,0x06, 0xD, +0 }, + { 0x137FB00,0x05CE711, 0x05,0x00, 0x8, +0 }, + { 0x04CA900,0x04FD600, 0x0B,0x00, 0x0, +0 }, + { 0x023F302,0x067F700, 0x08,0x00, 0xE, +0 }, + { 0x017FB01,0x008FD02, 0x40,0x00, 0x9, +0 }, + { 0x0F4F306,0x0E4E203, 0xA4,0x6D, 0x6, +0 }, + { 0x0D4E101,0x0E5E111, 0x53,0x02, 0x6, +0 }, + { 0x053F241,0x0F3F213, 0x9D,0x00, 0x6, +0 }, + { 0x050F101,0x076D201, 0x4F,0x04, 0x6, +0 }, + { 0x053F101,0x0849212, 0xC3,0x09, 0x8, +0 }, + { 0x074F202,0x077F401, 0x92,0x83, 0x8, +0 }, + { 0x013F202,0x044F502, 0x22,0x00, 0xE, +0 }, + { 0x475F113,0x256F201, 0x96,0x81, 0x6, +0 }, + { 0x0100133,0x033AD14, 0x87,0x80, 0x8, +0 }, + { 0x0E5F14C,0x0E5C301, 0x69,0x06, 0x8, +0 }, + { 0x0E2660F,0x0E4C191, 0x9D,0x06, 0xE, +0 }, + { 0x033F584,0x015FDA0, 0x59,0x80, 0x2, +0 }, + { 0x0B5F615,0x0E6F311, 0x97,0x01, 0x4, +0 }, + { 0x0F8FF06,0x055F8C4, 0x01,0x00, 0xE, +0 }, + { 0x063F207,0x074F212, 0x4F,0x00, 0x8, +0 }, + { 0x341F5A3,0x203F811, 0x11,0x00, 0x0, +0 }, + { 0x01AF003,0x01DF001, 0x5B,0x80, 0xA, +0 }, + { 0x22A9132,0x12A91B1, 0xCD,0x80, 0x9, +0 }, + { 0x0038165,0x005F171, 0xD2,0x80, 0x2, +0 }, + { 0x00AFF24,0x00DFF21, 0x80,0x80, 0x1, +0 }, + { 0x01CF003,0x01EA001, 0x54,0x84, 0xC, +0 }, + { 0x0186223,0x02A6221, 0x19,0x84, 0xE, +0 }, + { 0x0087224,0x00B4231, 0x4F,0x00, 0xE, +0 }, + { 0x0186222,0x02A6221, 0x19,0x84, 0xE, +0 }, + { 0x0C3C201,0x056F501, 0x0A,0x00, 0x6, +0 }, + { 0x034F401,0x039F201, 0x13,0x80, 0x8, +0 }, + { 0x07FC611,0x0DFF511, 0x4D,0x00, 0x6, +0 }, + { 0x4C5A421,0x004F821, 0x20,0x00, 0x2, +0 }, + { 0x0E78301,0x078F201, 0x56,0x00, 0xA, +0 }, + { 0x0AFF301,0x078F501, 0x11,0x00, 0x8, +0 }, + { 0x114FF20,0x0D4F561, 0xCB,0x00, 0xC, +0 }, + { 0x1937510,0x182F501, 0x00,0x00, 0x0, +0 }, + { 0x01379C0,0x07472D2, 0x4F,0x00, 0x6, +12 }, + { 0x2355612,0x12D9531, 0x9C,0x00, 0xA, +0 }, + { 0x21351A0,0x2275360, 0x9B,0x01, 0xE, +0 }, + { 0x163F2A1,0x0368331, 0x48,0x00, 0x6, +0 }, + { 0x171A501,0x2539600, 0x0D,0x02, 0x7, +0 }, + { 0x051F431,0x074B711, 0x57,0x00, 0xC, +0 }, + { 0x005F624,0x095C702, 0xDB,0x23, 0x8, +0 }, + { 0x095F422,0x0D5F401, 0x22,0x00, 0x8, +0 }, + { 0x016F521,0x03493A1, 0x8C,0x00, 0x0, +0 }, + { 0x01FB431,0x01FA2A1, 0x1A,0x80, 0xE, +0 }, + { 0x04654A1,0x0078FA1, 0x1C,0x07, 0xE, +0 }, + { 0x0466421,0x0078FE1, 0x14,0x01, 0xF, +0 }, + { 0x0796520,0x0268AA1, 0x8C,0x03, 0x8, +12 }, + { 0x2179280,0x03686A0, 0xCF,0x00, 0x9, +0 }, + { 0x03A5321,0x00B6521, 0x9C,0x01, 0xA, +0 }, + { 0x01C7321,0x02C7C21, 0xC0,0x97, 0xB, +0 }, + { 0x06581E1,0x07C52F2, 0x51,0x00, 0xC, +0 }, + { 0x22E71E0,0x01E80E4, 0x23,0x00, 0xA, +0 }, + { 0x019D530,0x01B6171, 0xC8,0x80, 0xC, +0 }, + { 0x01582A3,0x007E562, 0x21,0x9E, 0xE, +0 }, + { 0x005D224,0x0076F21, 0x9F,0x02, 0xF, +0 }, + { 0x48674A1,0x02765A1, 0x1F,0x00, 0x0, +0 }, + { 0x0277584,0x01655A1, 0xA0,0x81, 0xC, +0 }, + { 0x01566A2,0x00566A1, 0x8A,0x00, 0xD, +0 }, + { 0x016D322,0x07DE82F, 0x9B,0x2E, 0xE, -12 }, + { 0x006C524,0x02764B2, 0x62,0x04, 0xE, +0 }, + { 0x0557221,0x096F481, 0x0B,0x08, 0x6, +0 }, + { 0x0A6CF22,0x09C8410, 0xD5,0x0D, 0x7, +0 }, + { 0x001F501,0x0F1F101, 0x37,0x20, 0x0, +0 }, + { 0x0E3F201,0x0E7F501, 0x11,0x00, 0x0, +0 }, + { 0x03CF201,0x0E2F111, 0x3F,0x14, 0x0, +0 }, + { 0x0E6F541,0x0E7F312, 0x13,0x01, 0x0, +0 }, + { 0x01582A3,0x00AF562, 0x21,0xA3, 0xE, +0 }, + { 0x005F224,0x00A6F21, 0xA2,0x09, 0xF, +0 }, + { 0x0F0F006,0x2B6F800, 0x00,0x00, 0xE, +0 }, + { 0x04CA900,0x03FF600, 0x07,0x00, 0xA, +0 }, + { 0x008B902,0x01DFC03, 0x00,0x00, 0xB, +0 }, + { 0x60AF905,0x41CFC0A, 0x00,0x00, 0xD, +0 }, + { 0x033F400,0x4FFF700, 0x04,0x00, 0xE, +0 }, + { 0x40AFF02,0x01CFF00, 0xC0,0x01, 0x4, +0 }, + { 0x003F902,0x247FB00, 0x00,0x00, 0xE, +0 }, + { 0x403FB02,0x447FB01, 0x00,0x00, 0xE, +0 }, + { 0x609F505,0x709F30F, 0x00,0x00, 0x6, +0 }, + { 0x201C687,0x023BC15, 0xC0,0x40, 0xE, +0 }, + { 0x435DE00,0x438F801, 0xC0,0x00, 0xA, +0 }, + { 0x30AF400,0x278F700, 0x47,0x04, 0xE, +0 }, + { 0x30AF400,0x278F700, 0x4B,0x02, 0xE, +0 }, + { 0x509F601,0x429F701, 0x00,0x00, 0x7, +0 }, + { 0x407FF00,0x769A901, 0x00,0x40, 0x9, +0 }, + { 0x408FA01,0x769DB02, 0x00,0x40, 0x7, +0 }, + { 0x112AA03,0x1F59011, 0x1C,0x00, 0xE, +0 }, + { 0x073F668,0x063F5A1, 0x1B,0x0D, 0x0, +0 }, + { 0x054F1A1,0x0F4F060, 0x54,0x00, 0x2, +0 }, + { 0x0038164,0x005D171, 0xD2,0x80, 0x2, +0 }, + { 0x0F1FB3E,0x093A071, 0x29,0x00, 0x0, +0 }, + { 0x022FE30,0x007FB20, 0x07,0x00, 0x0, +0 }, + { 0x0527101,0x0735012, 0x8F,0x00, 0xA, +0 }, + { 0x1249F16,0x035B012, 0x11,0x00, 0x8, +0 }, + { 0x1119183,0x0F1B142, 0xD7,0x00, 0x0, +0 }, + { 0x006FA04,0x005FF01, 0xD3,0x00, 0xA, +0 }, + { 0x044F406,0x034F201, 0x03,0x1B, 0x1, +0 }, + { 0x088FA21,0x097B313, 0x06,0x00, 0xC, +0 }, + { 0x031F91C,0x0E89615, 0x0C,0x00, 0xE, +0 }, + { 0x0F7F521,0x0F7F521, 0x99,0x80, 0xE, +0 }, + { 0x038B2F1,0x0488122, 0x19,0x40, 0xC, +0 }, + { 0x016D221,0x0F8C201, 0x1D,0x00, 0xA, +0 }, + { 0x082D301,0x0B8D301, 0x4E,0x06, 0xA, +0 }, + { 0x0036101,0x0F86101, 0x14,0x0D, 0xC, +0 }, + { 0x017F321,0x0E8F222, 0x17,0x08, 0xC, +0 }, + { 0x0CEB161,0x1BAD061, 0x13,0x40, 0xA, +0 }, + { 0x075C130,0x0659131, 0x10,0x42, 0xA, +0 }, + { 0x0977801,0x0988802, 0x00,0x00, 0x8, +0 }, + { 0x0FEF22C,0x0D8B802, 0x00,0x1A, 0x6, +0 }, + { 0x0F6822E,0x0F87404, 0x00,0x27, 0x4, +0 }, + { 0x0009F2C,0x0D4C50E, 0x00,0x05, 0xE, +0 }, + { 0x0009429,0x044F904, 0x10,0x04, 0xE, +0 }, + { 0x0F1F52E,0x0F78706, 0x09,0x03, 0x0, +0 }, + { 0x0A1F737,0x028F603, 0x14,0x00, 0x8, +0 }, + { 0x000FF80,0x0F7F500, 0x00,0x00, 0xC, +0 }, + { 0x0FAFB21,0x0F7A802, 0x03,0x00, 0x0, +0 }, + { 0x0FAF924,0x0F6A603, 0x18,0x00, 0xE, +0 }, + { 0x0F5F505,0x036F603, 0x14,0x00, 0x6, +0 }, + { 0x001FF0E,0x077790E, 0x00,0x02, 0xE, +0 }, + { 0x007AF20,0x02BA50E, 0x15,0x00, 0x4, +0 }, + { 0x007BF20,0x03B930E, 0x18,0x00, 0x0, +0 }, + { 0x0F7F020,0x03B8908, 0x00,0x01, 0xA, +0 }, + { 0x0FAF320,0x02B5308, 0x00,0x0A, 0x8, +0 }, + { 0x09AF815,0x089F613, 0x21,0x10, 0x8, +0 }, + { 0x0075F20,0x04B8708, 0x01,0x00, 0x0, +0 }, + { 0x0F75725,0x0677803, 0x12,0x00, 0x0, +0 }, + { 0x0F0F126,0x0F5F527, 0x97,0xA1, 0x4, +0 }, + { 0x054F123,0x173F231, 0x66,0x00, 0x6, +0 }, + { 0x010A132,0x0337D16, 0x87,0x80, 0x8, +0 }, + { 0x143F523,0x204F811, 0x0E,0x00, 0x0, +0 }, + { 0x0100133,0x0027D14, 0x87,0x80, 0x8, +0 }, + { 0x001AF64,0x062A33F, 0xDB,0xC0, 0x4, +0 }, + { 0x0118171,0x1156261, 0x8B,0x40, 0x6, +0 }, + { 0x0127171,0x11652E1, 0x8B,0x40, 0x6, +0 }, + { 0x143F523,0x208F831, 0x0E,0x00, 0x0, +0 }, + { 0x0E7F301,0x078F201, 0x58,0x00, 0xA, +0 }, + { 0x054C701,0x096A201, 0x4D,0x00, 0x4, +0 }, + { 0x154C701,0x096A201, 0x4D,0x00, 0x4, +0 }, + { 0x0C28621,0x0BDF221, 0x16,0x00, 0x2, +0 }, + { 0x08DF520,0x08CF311, 0x49,0x00, 0xA, +12 }, + { 0x09EF520,0x05BF411, 0x90,0x00, 0xC, +12 }, + { 0x5144261,0x3344261, 0x87,0x82, 0x1, +0 }, + { 0x02371A1,0x1286371, 0x4F,0x02, 0x6, +0 }, + { 0x11152F0,0x12E32F1, 0xC5,0x80, 0x0, +0 }, + { 0x01171F1,0x11542E1, 0x8B,0x40, 0x6, +0 }, + { 0x01FF201,0x088F701, 0x17,0x00, 0xA, +0 }, + { 0x054C701,0x096A201, 0x8D,0x00, 0x4, -24 }, + { 0x0117171,0x11542E1, 0x8B,0x40, 0x6, +0 }, + { 0x053F121,0x1743232, 0x4F,0x00, 0x6, +0 }, + { 0x0117171,0x1154261, 0x8B,0x40, 0x6, +0 }, + { 0x01271B1,0x1166261, 0x8B,0x40, 0x6, +0 }, + { 0x011A1B1,0x1159261, 0x8B,0x40, 0x6, +0 }, + { 0x5176261,0x3176261, 0x80,0x82, 0x5, +0 }, + { 0x5155261,0x3166362, 0x80,0x83, 0x5, +0 }, + { 0x0065131,0x03B9261, 0x1C,0x80, 0xE, +0 }, + { 0x01F61B1,0x03B9261, 0x1C,0x80, 0xE, +0 }, + { 0x0276561,0x2275570, 0x83,0x03, 0xB, +0 }, + { 0x0537101,0x07C6212, 0x4E,0x00, 0xA, +0 }, + { 0x0658181,0x07C52B2, 0x93,0x00, 0xA, +0 }, + { 0x02661B0,0x0375271, 0x96,0x00, 0xE, +12 }, + { 0x0A6FF64,0x01424B1, 0x8A,0x00, 0xE, +0 }, + { 0x0A4F724,0x0132431, 0x5B,0x00, 0xE, +0 }, + { 0x0384161,0x028E1A1, 0x97,0x00, 0x6, +0 }, + { 0x01797F1,0x048F321, 0x06,0x0D, 0x8, +0 }, + { 0x054F406,0x053F281, 0x73,0x03, 0x0, +0 }, + { 0x1E31111,0x0D42101, 0x09,0x05, 0x6, +0 }, + { 0x30217B1,0x0057321, 0x29,0x03, 0x6, +0 }, + { 0x08311E6,0x0541120, 0x11,0x00, 0x0, +0 }, + { 0x00361B1,0x0175461, 0x1F,0x01, 0xE, +0 }, + { 0x0F00000,0x0A21B14, 0x02,0x80, 0xE, +0 }, + { 0x03FB300,0x0F0AB08, 0x80,0x00, 0xA, +0 }, + { 0x1B29510,0x0069510, 0x11,0x00, 0x8, +0 }, + { 0x0F0F000,0x0B69800, 0x00,0x08, 0xE, +0 }, + { 0x0F0F009,0x0F7B720, 0x0E,0x0A, 0xE, +0 }, + { 0x21AF400,0x008F800, 0x00,0x08, 0xC, +0 }, + { 0x054C701,0x096A201, 0x8D,0x00, 0x4, +0 }, + { 0x202FF4F,0x3F6F601, 0x00,0x0F, 0x8, +0 }, + { 0x300EF9E,0x0D8A705, 0x80,0x00, 0xC, +0 }, + { 0x0F0F006,0x035C4C4, 0x00,0x03, 0xE, +0 }, + { 0x210BA2F,0x2F4B40F, 0x0E,0x00, 0xE, +0 }, + { 0x053F101,0x0B5F700, 0x7F,0x00, 0x6, +0 }, + { 0x013FA43,0x096F342, 0xD6,0x80, 0xA, +0 }, + { 0x030F930,0x0FEF600, 0x01,0x00, 0xE, +0 }, + { 0x0FF0006,0x0FDF715, 0x3F,0x0D, 0x0, +0 }, + { 0x0F0F006,0x0B4F600, 0x00,0x20, 0xE, +0 }, + { 0x1DEB421,0x0EEF231, 0x45,0x00, 0x6, +0 }, + { 0x0135821,0x0031531, 0x2B,0x00, 0x8, +0 }, + { 0x0ADF321,0x05DF321, 0x08,0x00, 0x8, +0 }, + { 0x0EFD245,0x0EFA301, 0x4F,0x00, 0xA, +0 }, + { 0x0E7F217,0x0E7C211, 0x54,0x06, 0xA, +0 }, + { 0x0C7F219,0x0D7F291, 0x2B,0x07, 0xB, +0 }, + { 0x1084331,0x0084232, 0x93,0x00, 0xC, +0 }, + { 0x0084522,0x01844F1, 0x65,0x00, 0xD, +0 }, + { 0x0E8F318,0x0F8F281, 0x62,0x00, 0x0, +0 }, + { 0x0DFD441,0x0DFC280, 0x8A,0x0C, 0x4, +0 }, + { 0x0DFD345,0x0FFA381, 0x93,0x00, 0x5, +0 }, + { 0x02CA760,0x00DAFE1, 0xC6,0x80, 0x4, +0 }, + { 0x0EEF121,0x17FD131, 0x00,0x00, 0x4, +0 }, + { 0x02FA7A3,0x00FAFE1, 0x56,0x83, 0x8, +0 }, + { 0x00FAF61,0x00FAFA2, 0x91,0x83, 0x9, +0 }, + { 0x275A421,0x1456161, 0x13,0x00, 0x4, +0 }, + { 0x4FAB913,0x0DA9102, 0x0D,0x1A, 0xA, +0 }, + { 0x04FF923,0x2FF9122, 0xA1,0x16, 0xE, +0 }, + { 0x0BF9120,0x04F9122, 0x99,0x00, 0xE, +0 }, + { 0x0432121,0x0355222, 0x97,0x00, 0x8, +0 }, + { 0x0AD9101,0x0CD9301, 0x53,0x00, 0x2, +0 }, + { 0x0EAF111,0x0EAF312, 0xA8,0x57, 0x4, +0 }, + { 0x0EAE111,0x0EAE111, 0x97,0x00, 0x4, +0 }, + { 0x0ECF131,0x07DF131, 0x8D,0x00, 0xA, +0 }, + { 0x02A5131,0x04A7132, 0x5B,0x00, 0xC, +0 }, + { 0x04A7131,0x04A7131, 0x19,0x00, 0xD, +0 }, + { 0x0AE9101,0x0CE9302, 0x93,0x00, 0x6, +0 }, + { 0x02FF120,0x3CFF220, 0x8C,0x00, 0x6, +0 }, + { 0x04FF220,0x35FF222, 0x94,0x00, 0x8, +0 }, + { 0x2036130,0x21754A0, 0x95,0x00, 0xA, +0 }, + { 0x3107560,0x2176520, 0x89,0x00, 0xB, +0 }, + { 0x513DD31,0x0385621, 0x95,0x00, 0xC, +0 }, + { 0x1038D13,0x0786025, 0x95,0x89, 0xD, +0 }, + { 0x121F131,0x0166FE1, 0x40,0x00, 0x2, +0 }, + { 0x1038D14,0x0266620, 0x95,0x89, 0x9, +0 }, + { 0x1FFF510,0x0FFF211, 0x41,0x00, 0x2, +0 }, + { 0x1176561,0x0176521, 0x96,0x00, 0xF, +0 }, + { 0x2097861,0x1095821, 0x16,0x00, 0x8, +0 }, + { 0x121F131,0x0177C61, 0x40,0x00, 0x2, +0 }, + { 0x6EF1F15,0x6E21115, 0xC0,0x40, 0xE, +0 }, + { 0x0E21111,0x0E31111, 0x40,0x00, 0xE, +0 }, + { 0x2686500,0x616C500, 0x00,0x00, 0xB, +0 }, + { 0x6DAC600,0x30E7400, 0x00,0x00, 0xB, +0 }, + { 0x01C8521,0x00C8F21, 0x92,0x01, 0xC, +0 }, + { 0x01C8421,0x00CAF61, 0x15,0x0B, 0xD, +0 }, + { 0x01B8521,0x00B7F21, 0x94,0x05, 0xC, +0 }, + { 0x01B8421,0x00BAF61, 0x15,0x0D, 0xD, +0 }, + { 0x0158621,0x0378221, 0x94,0x00, 0xC, +0 }, + { 0x0178521,0x0098F61, 0x92,0x00, 0xC, +0 }, + { 0x00A7321,0x00B8F21, 0x9F,0x00, 0xE, +0 }, + { 0x00A65A1,0x00B9F61, 0x9B,0x00, 0xF, +0 }, + { 0x02E7221,0x00E8F21, 0x16,0x00, 0xC, +0 }, + { 0x0EE7521,0x03E8A21, 0x1D,0x00, 0xD, +0 }, + { 0x0AC54A1,0x01CA661, 0x50,0x00, 0x8, +0 }, + { 0x2089331,0x00A72A1, 0x96,0x00, 0x8, +0 }, + { 0x0088521,0x12A8431, 0x96,0x00, 0x9, +0 }, + { 0x10A9331,0x00D72A1, 0x8E,0x00, 0x8, +0 }, + { 0x00AC524,0x12D6431, 0xA1,0x00, 0x9, +0 }, + { 0x10F9331,0x00F7271, 0x8D,0x00, 0xA, +0 }, + { 0x006A524,0x11664B1, 0x9D,0x00, 0xB, +0 }, + { 0x51E7E71,0x10F8B21, 0x4D,0x00, 0x6, +0 }, + { 0x1197531,0x0196172, 0x8E,0x00, 0xA, +0 }, + { 0x0269B32,0x0187321, 0x90,0x00, 0x4, +0 }, + { 0x02F7721,0x02F7A73, 0x21,0x55, 0x2, +0 }, + { 0x01F7A21,0x01F7A22, 0x93,0x00, 0x2, +0 }, + { 0x01DAFA1,0x00D7521, 0x9C,0x00, 0x2, +0 }, + { 0x011DA65,0x068A663, 0x00,0x1E, 0xC, +0 }, + { 0x0588861,0x01A6561, 0x8C,0x00, 0xD, +0 }, + { 0x1282121,0x0184161, 0x12,0x00, 0x0, +0 }, + { 0x00FFF21,0x60FFF21, 0x09,0x80, 0x5, +0 }, + { 0x3FAF100,0x3FAF111, 0x8E,0x00, 0x0, +0 }, + { 0x2C686A1,0x0569321, 0x46,0x80, 0xA, +0 }, + { 0x01B7D61,0x01B72B1, 0x40,0x23, 0xE, +0 }, + { 0x00BDFA2,0x00B7F61, 0x5D,0x80, 0xF, +0 }, + { 0x009FF20,0x40A8F61, 0x36,0x00, 0x8, +0 }, + { 0x00FFF21,0x40D8F61, 0x27,0x00, 0x9, +0 }, + { 0x0FCF521,0x0FDF523, 0x0F,0x00, 0xA, +0 }, + { 0x0FDF926,0x6FCF921, 0x16,0x00, 0xB, +0 }, + { 0x011A861,0x0032531, 0x1F,0x80, 0xA, +0 }, + { 0x031A101,0x0032571, 0xA1,0x00, 0xB, +0 }, + { 0x0141161,0x0175561, 0x17,0x00, 0xC, +0 }, + { 0x446C361,0x026C361, 0x14,0x00, 0xD, +0 }, + { 0x63311E1,0x0353261, 0x89,0x03, 0xA, +0 }, + { 0x6E42161,0x6D53261, 0x8C,0x03, 0xB, +0 }, + { 0x0336121,0x0355261, 0x8D,0x03, 0xA, +0 }, + { 0x177A1A1,0x1471121, 0x1C,0x00, 0xB, +0 }, + { 0x03311E1,0x0353261, 0x89,0x03, 0xA, +0 }, + { 0x0E42161,0x0D53261, 0x8C,0x03, 0xB, +0 }, + { 0x003A801,0x005A742, 0x99,0x00, 0xD, +0 }, + { 0x2332121,0x0143260, 0x8C,0x97, 0x6, +0 }, + { 0x1041161,0x0143121, 0x0E,0x00, 0x7, +0 }, + { 0x056B222,0x054F261, 0x92,0x00, 0xC, +0 }, + { 0x04311A1,0x0741161, 0x0E,0x92, 0xA, +0 }, + { 0x0841161,0x0041DA1, 0x8E,0x80, 0xB, +0 }, + { 0x0346161,0x0055D21, 0x4C,0x80, 0x6, +0 }, + { 0x0CFF411,0x1EFF411, 0x05,0x00, 0x4, +0 }, + { 0x035D493,0x114EB11, 0x11,0x00, 0x8, +0 }, + { 0x035D453,0x116EB13, 0x11,0x0D, 0x9, +0 }, + { 0x1E31117,0x2E31114, 0x10,0x6E, 0xC, +0 }, + { 0x0E31111,0x0E31111, 0x80,0x00, 0xC, +0 }, + { 0x017A821,0x0042571, 0x23,0x00, 0xA, +0 }, + { 0x45FF811,0x0EFF310, 0x4F,0x00, 0xE, +0 }, + { 0x15FF630,0x0EFF410, 0x12,0x00, 0xF, +0 }, + { 0x00F4F2F,0x30F3F20, 0x00,0x00, 0xC, +0 }, + { 0x03FF923,0x2FF9222, 0x23,0x0A, 0xE, +0 }, + { 0x0BF9122,0x04FA123, 0x18,0x00, 0xE, +0 }, + { 0x000F80F,0x3F93410, 0x00,0x05, 0xE, +0 }, + { 0x034A121,0x0166521, 0x17,0x00, 0xC, +0 }, + { 0x0FA6848,0x04AAA01, 0x00,0x3F, 0x5, +0 }, + { 0x0FA6747,0x0FA464C, 0x00,0x00, 0x5, +0 }, + { 0x2037F21,0x1065F61, 0x18,0x00, 0x0, +0 }, + { 0x10C2EF0,0x10C21E2, 0x00,0x00, 0x4, -36 }, + { 0x70C2EF0,0x10C21E2, 0x00,0x00, 0x5, +0 }, + { 0x039A321,0x03C7461, 0x8D,0x03, 0xA, +0 }, + { 0x179A3A1,0x14C2321, 0x1C,0x00, 0xB, +0 }, + { 0x01A7521,0x00F8F21, 0x97,0x00, 0xC, +0 }, + { 0x0FFF920,0x0FFF620, 0xC0,0x00, 0x8, +0 }, + { 0x277F810,0x0AFF611, 0x44,0x00, 0x8, +0 }, + { 0x01FF933,0x0FFF810, 0x80,0x00, 0x4, +0 }, + { 0x2FFF500,0x0FFF700, 0x00,0x00, 0xC, +0 }, + { 0x0DFF712,0x0DFF811, 0x08,0x00, 0x2, +0 }, + { 0x0FFF210,0x0FFF510, 0x00,0x00, 0xC, +0 }, + { 0x1DFE920,0x0CEF400, 0x00,0x00, 0x4, +0 }, + { 0x2DFF50E,0x0AFF712, 0x00,0x00, 0xE, +0 }, + { 0x03FF800,0x1FFF410, 0x03,0x00, 0x4, +0 }, + { 0x2FFF012,0x3BF8608, 0x11,0x80, 0xE, +0 }, + { 0x0FFF20E,0x2DF9502, 0x00,0x00, 0xC, +0 }, + { 0x2DDF014,0x0FF93F0, 0x00,0x00, 0xE, +0 }, + { 0x3EFE40E,0x1EFF507, 0x0A,0x40, 0x6, +0 }, + { 0x0EFB402,0x0FF9705, 0x03,0x0A, 0xE, +0 }, + { 0x01FF66E,0x3FF945E, 0x08,0x00, 0xE, +0 }, + { 0x200F6CE,0x3FFF21A, 0x04,0x00, 0xC, +0 }, + { 0x3FFF040,0x0FEF510, 0x00,0x00, 0xC, +0 }, + { 0x38CF803,0x0BCF60C, 0x80,0x08, 0xF, +0 }, + { 0x38FF803,0x0BFF60C, 0x85,0x00, 0xF, +0 }, + { 0x04F760E,0x2CF7800, 0x40,0x08, 0xE, +0 }, + { 0x04FC80E,0x26F9903, 0x40,0x00, 0xE, +0 }, + { 0x1DF75CE,0x2EF38E1, 0x00,0x00, 0xE, +0 }, + { 0x03FF162,0x0FF4B20, 0x00,0x00, 0x8, +0 }, + { 0x0F40006,0x0FBF715, 0x3F,0x00, 0x1, +0 }, + { 0x0FF47E1,0x0FF47EA, 0x00,0x00, 0x0, +0 }, + { 0x3FFE00A,0x0FFF51E, 0x40,0x0E, 0x8, +0 }, + { 0x3FFE00A,0x0FFF21E, 0x7C,0x52, 0x8, +0 }, + { 0x04E7A0E,0x21E7B00, 0x81,0x00, 0xE, +0 }, + { 0x35FF925,0x0FFD524, 0x05,0x40, 0xE, +0 }, + { 0x08FFA01,0x0FFF802, 0x4F,0x00, 0x7, +0 }, + { 0x0FFFC00,0x0FFF520, 0x00,0x00, 0x4, +0 }, + { 0x60FF331,0x70FB135, 0x94,0xD5, 0xF, +0 }, + { 0x302B133,0x305B131, 0x63,0x00, 0xE, +0 }, + { 0x04F270C,0x0F8D104, 0x98,0x90, 0x8, +0 }, + { 0x0F8F502,0x0F8F402, 0x96,0x00, 0x9, +0 }, + { 0x759F201,0x600F701, 0x40,0x00, 0x0, +0 }, + { 0x6F0F301,0x7C9F601, 0x00,0x00, 0x0, +0 }, + { 0x60FFF15,0x66FB115, 0xC0,0x40, 0xE, +0 }, + { 0x68FB111,0x6EFB111, 0x40,0x00, 0xE, +0 }, + { 0x44FF920,0x2FF9122, 0x80,0x09, 0xE, +0 }, + { 0x7BF9121,0x64F9122, 0x99,0x00, 0xE, +0 }, + { 0x00AAFE1,0x00AAF62, 0x11,0x00, 0x9, +0 }, + { 0x022FA02,0x0F3F501, 0x4C,0x97, 0x8, +0 }, + { 0x1F3C504,0x0F7C511, 0x9D,0x00, 0x8, +0 }, + { 0x0AFC711,0x0F8F501, 0x8D,0x04, 0x8, +0 }, + { 0x098C301,0x0F8C302, 0x18,0x06, 0x9, +0 }, + { 0x40FF923,0x20F9122, 0x90,0x1B, 0xE, +0 }, + { 0x00F9121,0x00F9122, 0x9F,0x00, 0xE, +0 }, + { 0x60FFF15,0x61FB015, 0xC0,0x40, 0xE, +0 }, + { 0x65FB111,0x63FB011, 0x40,0x00, 0xE, +0 }, + { 0x60FFF35,0x60FB135, 0xC0,0x40, 0xE, +0 }, + { 0x6BFB131,0x60FB131, 0x40,0x00, 0xE, +0 }, + { 0x0C8F121,0x0C8F501, 0x13,0x29, 0x6, +0 }, + { 0x0C8F501,0x0C8F401, 0x14,0x00, 0x6, +0 }, + { 0x09AF381,0x0DFF521, 0x89,0x40, 0x6, +0 }, + { 0x0C8F121,0x0C8F701, 0x0F,0x25, 0xA, +0 }, + { 0x0C8F601,0x0C8F601, 0x12,0x00, 0xA, +0 }, + { 0x105F510,0x0C5F411, 0x41,0x00, 0x2, +0 }, + { 0x005F511,0x0C5F212, 0x01,0x1E, 0x3, +0 }, + { 0x012C1A1,0x0076F21, 0x93,0x00, 0xD, +0 }, + { 0x011DA65,0x068A663, 0x00,0x1B, 0xC, +0 }, + { 0x0588861,0x01A6561, 0x0A,0x00, 0xD, +0 }, + { 0x00FFF21,0x409CF61, 0x1D,0x05, 0xA, +0 }, + { 0x70FFF20,0x30FFF61, 0x1A,0x14, 0x0, +0 }, + { 0x00FFF61,0x609CF61, 0x1A,0x07, 0x0, +0 }, + { 0x10D5317,0x00E3608, 0x1A,0x0D, 0x2, +0 }, + { 0x03D41A1,0x01E6161, 0x9D,0x00, 0x3, +0 }, + { 0x0FC8561,0x4FD8463, 0x15,0x07, 0xA, +0 }, + { 0x0FD8966,0x6FC7761, 0x1F,0x00, 0xB, +0 }, + { 0x10A5317,0x0033608, 0x1A,0x0D, 0x2, +0 }, + { 0x0041121,0x3355261, 0x8C,0x00, 0x1, +0 }, + { 0x0C6F521,0x096F461, 0x92,0x8A, 0xC, +0 }, + { 0x266F521,0x496F5A1, 0x90,0x80, 0xD, +0 }, + { 0x035D493,0x114EB11, 0x91,0x00, 0x8, +0 }, + { 0x035D453,0x116EB13, 0x91,0x0D, 0x9, +0 }, + { 0x56FF500,0x40FF300, 0x08,0x00, 0x1, +0 }, + { 0x65FF604,0x38FF580, 0x00,0x40, 0x0, +0 }, + { 0x66FF100,0x40FF300, 0x09,0x00, 0x0, +0 }, + { 0x65FF601,0x73FF580, 0x1C,0x00, 0x0, +0 }, + { 0x00F112F,0x30F1120, 0x00,0x00, 0xE, +0 }, + { 0x00F1129,0x30F1120, 0x38,0x35, 0xF, +0 }, + { 0x024F806,0x7845603, 0x00,0x04, 0xE, +0 }, + { 0x624D803,0x784F604, 0x0B,0x00, 0xF, +0 }, + { 0x624F802,0x7845604, 0x00,0x04, 0xA, +0 }, + { 0x624D800,0x784F603, 0x0B,0x00, 0xB, +0 }, + { 0x46FF220,0x07FF400, 0x14,0x00, 0xF, +1 }, + { 0x01FF501,0x51FF487, 0x00,0xC0, 0xF, +0 }, + { 0x059F200,0x700F701, 0x00,0x00, 0xE, +0 }, + { 0x0F0F301,0x6C9F401, 0x00,0x00, 0xE, +0 }, + { 0x0F7F810,0x006F211, 0x40,0x00, 0x8, +0 }, + { 0x002F010,0x006FE00, 0x00,0x00, 0xC, +0 }, + { 0x207F70E,0x008FF12, 0x00,0x00, 0xE, +0 }, + { 0x092FF83,0x003F015, 0x00,0x00, 0xE, +0 }, + { 0x0F4C306,0x0E4C203, 0xB5,0x76, 0x4, +0 }, + { 0x0D4C101,0x0E5B111, 0x53,0x02, 0x4, +0 }, + { 0x0F3C301,0x0F3C307, 0xA1,0x70, 0xC, +12 }, + { 0x034B000,0x0F5A111, 0xCC,0x00, 0xC, +0 }, + { 0x034FB31,0x0F7C131, 0x93,0x00, 0xC, +0 }, + { 0x0DFB811,0x0F7F121, 0x97,0x8B, 0xD, +0 }, + { 0x0E4A115,0x0E4A115, 0x6A,0x67, 0xE, +0 }, + { 0x0E4A111,0x0E5A111, 0x55,0x03, 0xE, +0 }, + { 0x0E7C21A,0x0E7C201, 0x33,0x85, 0x0, +0 }, + { 0x0F4B111,0x0E4B111, 0x1D,0x83, 0x1, +0 }, + { 0x0E7C21C,0x0E6C201, 0xBD,0x8B, 0xE, +0 }, + { 0x0E4B111,0x0E5B111, 0x52,0x85, 0xF, +0 }, + { 0x050F210,0x0F0E12A, 0xA1,0x64, 0xE, +12 }, + { 0x020BD20,0x0E7C112, 0x19,0x03, 0xE, +0 }, + { 0x12A91B1,0x00AF021, 0x80,0xA1, 0x7, +12 }, + { 0x038D620,0x0B7F8A6, 0x03,0x05, 0x7, +0 }, + { 0x017F820,0x0057F31, 0x94,0x08, 0xC, +12 }, + { 0x029F623,0x00A8F22, 0x1E,0x0B, 0xD, +0 }, + { 0x00AB028,0x00AB0A1, 0x5A,0x21, 0x1, +0 }, + { 0x00A8024,0x00AB021, 0xC0,0x09, 0x1, +0 }, + { 0x00AF0A2,0x00AF024, 0x06,0xA1, 0x5, +0 }, + { 0x00AF0A4,0x00AF021, 0x0A,0x06, 0x5, +0 }, + { 0x00FFF27,0x00FFF21, 0x29,0x07, 0x0, +0 }, + { 0x00FFF21,0x00FFF22, 0x18,0x06, 0x1, +0 }, + { 0x00AFF61,0x00AFF22, 0x0E,0xA1, 0x7, +0 }, + { 0x00AFF64,0x00AFF21, 0x0A,0x0B, 0x7, +0 }, + { 0x00FFF20,0x00FFFA1, 0x22,0x88, 0xC, +12 }, + { 0x00FFF22,0x00FFFA1, 0x56,0x84, 0xD, +0 }, + { 0x0F6EA09,0x0F4F518, 0x0F,0x8C, 0x0, +0 }, + { 0x00FEFA2,0x00B8F21, 0x3E,0x07, 0x1, +0 }, + { 0x0186223,0x02A6221, 0x1C,0x87, 0xE, +0 }, + { 0x1186223,0x02A62A2, 0x19,0x82, 0xF, +0 }, + { 0x001F201,0x0F1F101, 0x21,0x1D, 0xA, +0 }, + { 0x0E3F301,0x0E6F211, 0x4B,0x00, 0xA, +0 }, + { 0x030FE10,0x0F0E13A, 0x9F,0x65, 0xE, +12 }, + { 0x020BD20,0x0E7C112, 0x8D,0x07, 0xE, +0 }, + { 0x025F5E2,0x005EF24, 0x1E,0x9F, 0xE, -12 }, + { 0x004EF26,0x006CF24, 0x9E,0x06, 0xE, +0 }, + { 0x043D227,0x0E4E215, 0x9A,0x03, 0x8, -12 }, + { 0x023A7B7,0x0E4C215, 0x19,0x08, 0x9, +0 }, + { 0x043D223,0x0E4E212, 0x98,0x03, 0x8, +0 }, + { 0x023A7B3,0x0E4C212, 0x19,0x08, 0x9, +0 }, + { 0x0E6CE22,0x0E6F421, 0x25,0x03, 0x0, +0 }, + { 0x0E6F727,0x0E5F521, 0x32,0x09, 0x1, +0 }, + { 0x006F504,0x041F001, 0x3F,0x05, 0x0, +0 }, + { 0x035D208,0x005F120, 0x00,0x06, 0x0, +0 }, + { 0x034D201,0x003F120, 0x00,0x06, 0x0, +0 }, + { 0x0276621,0x0486621, 0x1C,0x00, 0xE, +0 }, + { 0x00A6621,0x0486621, 0x94,0x00, 0xF, +0 }, + { 0x0E44100,0x0046620, 0x91,0x08, 0xC, +12 }, + { 0x0E65120,0x0066620, 0x8E,0x08, 0xD, +0 }, + { 0x0257521,0x00AAF21, 0x1A,0x08, 0xE, +0 }, + { 0x0257521,0x00AAF21, 0x1A,0x0C, 0xF, +0 }, + { 0x015A221,0x00AAF21, 0x12,0x02, 0xC, +0 }, + { 0x055F2A1,0x00AAF21, 0x28,0x05, 0xD, +0 }, + { 0x0CFF416,0x0E6F205, 0x23,0x69, 0xC, +12 }, + { 0x0D5F200,0x0ECE301, 0x15,0x00, 0xC, +0 }, + { 0x058F620,0x05AF520, 0x98,0x19, 0xE, +12 }, + { 0x009FF21,0x00CFF20, 0x24,0x00, 0xE, +0 }, + { 0x006F801,0x0D5D500, 0x17,0x17, 0x8, +12 }, + { 0x4E6F511,0x0E8F500, 0x14,0x00, 0x8, +0 }, + { 0x045FB01,0x050FF12, 0x10,0x0C, 0x0, +12 }, + { 0x034FF00,0x027F300, 0x16,0x00, 0x0, +0 }, + { 0x0EAF50C,0x0E6F21F, 0x21,0x21, 0xE, +0 }, + { 0x0F6F401,0x0E7F113, 0x15,0x03, 0xE, +0 }, + { 0x0E6F407,0x0F6A114, 0x9B,0x1D, 0xE, +0 }, + { 0x00FFF21,0x0E6F112, 0x12,0x04, 0xE, +0 }, + { 0x062F227,0x062F231, 0x26,0x18, 0xC, +0 }, + { 0x066F521,0x0E4F116, 0x0E,0x03, 0xC, +0 }, + { 0x015A221,0x0DAC401, 0x13,0x14, 0xC, +0 }, + { 0x055F221,0x0DAA401, 0x2A,0x00, 0xC, +0 }, + { 0x09CF901,0x0F98701, 0x00,0x03, 0x6, +0 }, + { 0x0ACF904,0x0F98701, 0x00,0x00, 0x7, +0 }, + { 0x025F261,0x015F2A5, 0x22,0x5E, 0xE, +0 }, + { 0x015F223,0x0C6E111, 0x5B,0x02, 0xE, +0 }, + { 0x006FF22,0x00B9F22, 0x1C,0x08, 0xE, +0 }, + { 0x005FA21,0x00B9F21, 0x19,0x07, 0xF, +0 }, + { 0x0F6D133,0x0F7F221, 0x9A,0x03, 0xC, +0 }, + { 0x0E4F22F,0x0F7F224, 0x28,0x8A, 0xD, +0 }, + { 0x03FF43A,0x04FF231, 0x64,0x5A, 0xE, +0 }, + { 0x024F211,0x085F311, 0x25,0x08, 0xE, +0 }, + { 0x026F211,0x04FF43A, 0x23,0x5F, 0xE, +0 }, + { 0x04FF231,0x0D6F211, 0x63,0x07, 0xE, +0 }, + { 0x03AA021,0x097A123, 0x23,0x21, 0xE, +12 }, + { 0x0F2A310,0x0F5A020, 0x12,0x05, 0xE, +0 }, + { 0x030F70C,0x0A8F101, 0x23,0x26, 0xA, +0 }, + { 0x0C6F201,0x043F212, 0x13,0x00, 0xA, +0 }, + { 0x054D41F,0x0F5C411, 0x65,0x42, 0xC, +0 }, + { 0x0F4B113,0x0E5A111, 0x50,0x05, 0xD, +0 }, + { 0x0AFF505,0x03DFD2C, 0x3F,0x13, 0xA, +0 }, + { 0x0B0F607,0x074F411, 0x0F,0x08, 0xA, +0 }, + { 0x022E832,0x0F5B210, 0x08,0x12, 0x2, +12 }, + { 0x021F730,0x0F5B214, 0x08,0x0D, 0x3, +0 }, + { 0x025F5E2,0x005EF24, 0x20,0x9F, 0xE, -12 }, + { 0x004EF26,0x0065F24, 0x9E,0x06, 0xE, +0 }, + { 0x004EFE2,0x005EF24, 0x24,0x21, 0xE, -12 }, + { 0x004EF26,0x0065F24, 0x9F,0x07, 0xE, +0 }, + { 0x002EFE2,0x003EF24, 0xAA,0xA1, 0xE, -12 }, + { 0x003EF26,0x0065F24, 0xA4,0x03, 0xE, +0 }, + { 0x016D122,0x0055572, 0x9A,0x06, 0xE, -12 }, + { 0x0F6C102,0x2055571, 0xD9,0x0D, 0xF, +0 }, + { 0x012F322,0x0054F22, 0x1D,0x04, 0xE, +0 }, + { 0x013F321,0x0054F22, 0x91,0x80, 0xF, +0 }, + { 0x015F322,0x0065F22, 0x1D,0x05, 0xE, +0 }, + { 0x015F321,0x0075F23, 0x91,0x80, 0xF, +0 }, + { 0x295F520,0x353F411, 0x90,0x00, 0xC, +12 }, + { 0x295F520,0x353F411, 0x90,0x09, 0xC, +12 }, + { 0x0FAF52F,0x0FAF423, 0xB2,0x64, 0xE, +0 }, + { 0x0FAE323,0x0FAF321, 0x66,0x03, 0xE, +0 }, + { 0x036D122,0x0055572, 0x9A,0x00, 0xE, -12 }, + { 0x4F6C102,0x2055574, 0xD9,0x07, 0xF, +0 }, + { 0x0D6F328,0x0F9F423, 0xA2,0x5F, 0xC, +0 }, + { 0x0F8E223,0x0E8F301, 0xA6,0x03, 0xC, +0 }, + { 0x01AD1A1,0x00A9F22, 0x2C,0x8F, 0xE, +0 }, + { 0x00A9F22,0x00A9F22, 0x0F,0x08, 0xE, +0 }, + { 0x020FE70,0x0E9C212, 0x13,0x80, 0xA, +12 }, + { 0x07FBC20,0x0E9C212, 0x11,0x05, 0xB, +0 }, + { 0x020FE10,0x0E7C212, 0x12,0x00, 0xC, +12 }, + { 0x053BD00,0x0E7C212, 0x15,0x07, 0xD, +0 }, + { 0x0E54151,0x0E8F652, 0xA9,0x63, 0xE, +0 }, + { 0x0E8D151,0x0E6C251, 0x9B,0x80, 0xE, +0 }, + { 0x0C8F621,0x0F8F821, 0x1D,0x23, 0xE, +12 }, + { 0x0F8F420,0x0F8F320, 0x20,0x00, 0xE, +0 }, + { 0x058F520,0x059F520, 0x9B,0x19, 0xE, +12 }, + { 0x089F320,0x00CFF20, 0x19,0x07, 0xE, +0 }, + { 0x061F800,0x0EAF582, 0x2B,0x15, 0xC, +12 }, + { 0x0FFF420,0x097F400, 0x1B,0x00, 0xC, +0 }, + { 0x0E54711,0x0E68511, 0x29,0x1E, 0xE, +0 }, + { 0x0E8F512,0x0E6C251, 0x5E,0x40, 0xE, +0 }, + { 0x010F101,0x0C2F101, 0x35,0x17, 0xA, +0 }, + { 0x0C4F307,0x0E3F212, 0x12,0x00, 0xA, +0 }, + { 0x0DFF63C,0x0DFF521, 0xA7,0x18, 0xE, +12 }, + { 0x0D7F220,0x0E8F320, 0x1A,0x00, 0xE, +0 }, + { 0x0A0F400,0x0A7F101, 0x05,0x26, 0xA, +12 }, + { 0x0C5F201,0x043F212, 0x12,0x00, 0xA, +0 }, + { 0x019AA2F,0x0CFF9A2, 0x00,0x1F, 0xE, +0 }, + { 0x015FAA1,0x00B7F21, 0x9F,0x06, 0xE, +0 }, + { 0x01171B1,0x1E54141, 0x8B,0x40, 0x6, +0 }, + { 0x0AE7101,0x0EE8101, 0x1E,0x00, 0xE, +0 }, + { 0x0AE7101,0x0EE8101, 0x20,0x00, 0xE, +0 }, + { 0x016D322,0x02764B2, 0x9A,0x04, 0xE, -12 }, + { 0x006C524,0x02764B2, 0x61,0x09, 0xF, +0 }, + { 0x0066231,0x0E7A241, 0x1E,0x80, 0xE, +0 }, + { 0x0AE7101,0x0EE8101, 0x1C,0x00, 0xE, +0 }, + { 0x2129A13,0x0119B91, 0x97,0x80, 0xE, +0 }, + { 0x0056F22,0x0094F31, 0x56,0x0A, 0x8, +0 }, + { 0x0056F22,0x0094FB1, 0x59,0x0C, 0x9, +0 }, + { 0x1298920,0x1268532, 0x1F,0x5F, 0x0, +12 }, + { 0x0159AA0,0x01A8D22, 0x4C,0x03, 0x0, +0 }, + { 0x007CF20,0x0E97102, 0x5B,0x00, 0xE, +0 }, + { 0x0014131,0x03B9261, 0x99,0x80, 0xE, +0 }, + { 0x0475421,0x0097F21, 0x1D,0x07, 0xE, +0 }, + { 0x0476421,0x0087F61, 0x19,0x0B, 0xF, +0 }, + { 0x0176421,0x0098F21, 0x98,0x07, 0xE, +0 }, + { 0x0176421,0x0087F61, 0x17,0x0F, 0xF, +0 }, + { 0x0296321,0x00A7F21, 0x22,0x03, 0xE, +0 }, + { 0x0186521,0x00A7F61, 0x1B,0x0D, 0xF, +0 }, + { 0x0156220,0x0E67141, 0x9A,0x00, 0xE, +12 }, + { 0x02651B1,0x0E65151, 0xDB,0x87, 0xF, +0 }, + { 0x02365A3,0x0059F21, 0x1C,0x1C, 0xE, +0 }, + { 0x003DFA1,0x00BDF21, 0x1A,0x07, 0xE, +0 }, + { 0x0014131,0x03B9261, 0x20,0x80, 0xE, +0 }, + { 0x04AF823,0x0C5D283, 0xB5,0x52, 0x8, +12 }, + { 0x0E6F414,0x0D5F280, 0x99,0x00, 0x9, +0 }, + { 0x0FAF40C,0x0F4C212, 0x37,0x2B, 0x0, +0 }, + { 0x053F685,0x0E4F191, 0x64,0x00, 0x0, +0 }, + { 0x006F600,0x0E9F51F, 0x35,0x25, 0x0, +12 }, + { 0x000F023,0x0E5F280, 0x5E,0x00, 0x0, +0 }, + { 0x0F5F50C,0x0F5F2A1, 0xA9,0x05, 0xE, +0 }, + { 0x0F6F307,0x0F6F281, 0x31,0x04, 0xF, +0 }, + { 0x0E5F14F,0x0E5C301, 0x69,0x06, 0x8, +0 }, + { 0x052F605,0x0D5F281, 0x2D,0x03, 0x9, +0 }, + { 0x0E6F482,0x03AFE00, 0x0F,0x26, 0x1, +12 }, + { 0x0F6F380,0x0F5F787, 0x03,0x10, 0x1, +0 }, + { 0x0F5FD2C,0x0F5F427, 0x8E,0x20, 0x0, +0 }, + { 0x0F4F827,0x0F5F421, 0x20,0x00, 0x0, +0 }, + { 0x097CB05,0x0D5E801, 0x9F,0x00, 0xA, +0 }, + { 0x035F705,0x0E6E401, 0x28,0x05, 0xB, +0 }, + { 0x0095FE1,0x0076FE1, 0x58,0x03, 0x0, +0 }, + { 0x054890A,0x063A726, 0x6C,0x63, 0xA, +0 }, + { 0x0094F21,0x0083F61, 0xCE,0x02, 0xA, +0 }, + { 0x00F7F04,0x0CFF5EA, 0x30,0xA9, 0x8, +0 }, + { 0x00F5F21,0x00AAF61, 0x1C,0x06, 0x8, +0 }, + { 0x0549963,0x06AA768, 0x98,0xA9, 0xE, +0 }, + { 0x0095F61,0x0097F61, 0xD1,0x03, 0xE, +0 }, + { 0x0549963,0x06AA768, 0xD4,0x5E, 0xE, +0 }, + { 0x0095F61,0x0097F61, 0xC9,0x06, 0xE, +0 }, + { 0x0B643A1,0x0B6F6A3, 0x2A,0xB0, 0xE, +0 }, + { 0x0067FA1,0x0066F61, 0x2C,0x02, 0xE, +0 }, + { 0x053F101,0x0B5F700, 0x73,0x00, 0x6, +0 }, + { 0x021A121,0x116C221, 0x92,0x40, 0x6, +0 }, + { 0x024A80F,0x005DF02, 0xB8,0x03, 0x0, -12 }, + { 0x035A70A,0x005DF02, 0xA2,0x03, 0x1, +0 }, + { 0x01379C0,0x07372D2, 0x4F,0x00, 0x6, -12 }, + { 0x013FA43,0x095F342, 0xD6,0x80, 0xA, -24 }, + { 0x020D933,0x0E4B211, 0x08,0x08, 0x6, +0 }, + { 0x02278B0,0x0E4B214, 0x06,0x0D, 0x7, +0 }, + { 0x10475A0,0x0057221, 0x12,0x40, 0x6, +0 }, + { 0x0F1F007,0x0349800, 0x00,0x00, 0xE, +0 }, + { 0x1137521,0x0B47182, 0x92,0x40, 0xA, +0 }, + { 0x6B5F100,0x6B8F100, 0xD5,0x51, 0xB, +0 }, + { 0x0F0F601,0x0E2F01C, 0x3F,0x1C, 0x8, +0 }, + { 0x003F103,0x093F0A0, 0x00,0x00, 0x8, +0 }, + { 0x0F00010,0x0F00000, 0x3F,0x3F, 0x0, +0 }, + { 0x025C5A2,0x005EF24, 0x20,0x9F, 0xE, -12 }, + { 0x004EF26,0x0068F24, 0x9C,0x02, 0xE, +0 }, + { 0x0064131,0x03892A1, 0x1C,0x80, 0xE, +0 }, + { 0x0064131,0x02882A1, 0x1B,0x80, 0xF, +0 }, + { 0x0156220,0x0267321, 0x98,0x00, 0xE, +12 }, + { 0x02651B1,0x0265171, 0xD1,0x00, 0xF, +0 }, + { 0x0766321,0x0167CA1, 0x93,0x00, 0xC, +0 }, + { 0x1168321,0x0269CA1, 0x4D,0x00, 0xD, +0 }, + { 0x163F401,0x174F111, 0x12,0x00, 0xA, +0 }, + { 0x201F130,0x083F001, 0x44,0x83, 0xA, +0 }, + { 0x0117171,0x11542A1, 0x8B,0x40, 0x6, +0 }, + { 0x0667150,0x08B5290, 0x92,0x00, 0xE, +0 }, + { 0x054F606,0x0B3F241, 0x73,0x03, 0x0, +0 }, + { 0x0177421,0x0176562, 0x83,0x8D, 0x7, +0 }, + { 0x0031801,0x090F674, 0x80,0xC1, 0xE, +0 }, + { 0x282B264,0x1DA9803, 0x00,0x93, 0xE, +0 }, + { 0x0A0B264,0x1D69603, 0x02,0x80, 0xE, +0 }, + { 0x053F101,0x074F111, 0x4B,0x00, 0x6, +0 }, + { 0x0117F27,0x0441122, 0x0E,0x00, 0xE, +0 }, + { 0x0111122,0x0121123, 0x15,0x00, 0x4, +0 }, + { 0x053F101,0x074F111, 0x59,0x00, 0x6, +0 }, + { 0x0FFF691,0x0F4F511, 0x00,0x00, 0x8, +0 }, + { 0x3087631,0x00F6531, 0x08,0x00, 0x2, +0 }, + { 0x019D083,0x017F002, 0x5D,0x80, 0xA, +0 }, + { 0x019D083,0x017F002, 0x58,0x80, 0xA, +0 }, + { 0x013F6A6,0x005F1B1, 0xE5,0x40, 0x2, +0 }, + { 0x1239722,0x013457A, 0x44,0x00, 0x4, +0 }, + { 0x1239721,0x0134572, 0x8A,0x80, 0x2, +0 }, + { 0x0FFF4F1,0x06FF2F1, 0x02,0x00, 0x0, +0 }, + { 0x00F3FF1,0x06FF2F1, 0x02,0x00, 0x0, +0 }, + { 0x1E26301,0x01EB821, 0x16,0x00, 0x8, +0 }, + { 0x1226341,0x01E8821, 0x8F,0x00, 0x8, +0 }, + { 0x0024471,0x01E8831, 0x9D,0x00, 0xE, +0 }, + { 0x002A434,0x0427575, 0x54,0x40, 0x8, +0 }, + { 0x256F605,0x2047404, 0xC0,0x00, 0xE, +0 }, + { 0x0FFF09E,0x00F3F00, 0x07,0x00, 0xE, +0 }, + { 0x1217131,0x0066222, 0x40,0x40, 0x2, +0 }, + { 0x131F231,0x0066F21, 0x47,0x00, 0x0, +0 }, + { 0x0035131,0x06764A1, 0x1C,0x80, 0xE, +0 }, + { 0x0115270,0x0FE4171, 0xC5,0x40, 0x0, +0 }, + { 0x1218131,0x0167423, 0x4D,0x40, 0x2, +0 }, + { 0x151D203,0x278F301, 0x1D,0x00, 0xA, +0 }, + { 0x0F0F09E,0x063F300, 0x07,0x00, 0xE, +0 }, + { 0x0F7B096,0x00FFFE0, 0x00,0x00, 0x0, +0 }, + { 0x3199B85,0x0297424, 0x49,0x00, 0x6, +0 }, + { 0x0FFA691,0x0F45511, 0x00,0x00, 0x8, +0 }, + { 0x1226341,0x000A821, 0x8F,0x00, 0x8, +0 }, + { 0x1239721,0x0136572, 0x8A,0x80, 0x2, +0 }, + { 0x061F217,0x074F212, 0x6C,0x00, 0x8, +0 }, + { 0x1239721,0x0138572, 0x8A,0x80, 0x2, +0 }, + { 0x00D5131,0x01F7221, 0x1C,0x80, 0xC, +0 }, + { 0x08C6320,0x02F9520, 0x19,0x80, 0xC, +0 }, + { 0x1F5E510,0x162E231, 0x46,0x00, 0x0, +0 }, + { 0x24FF60E,0x318F700, 0x40,0x00, 0xE, +0 }, + { 0x0C8F60C,0x257FF12, 0xC0,0x00, 0xA, +0 }, + { 0x354B506,0x095D507, 0x00,0xC0, 0x0, +0 }, + { 0x0F0E02A,0x031FF1E, 0x52,0x54, 0x8, +0 }, + { 0x0745451,0x0756591, 0x00,0x00, 0xA, +0 }, + { 0x002A414,0x0427555, 0x54,0x40, 0x8, +0 }, + { 0x0115F31,0x11E3132, 0xC5,0x00, 0x8, +0 }, + { 0x1217131,0x0069222, 0x40,0x40, 0x2, +0 }, + { 0x053F101,0x0B5F704, 0x4F,0x00, 0x7, +0 }, + { 0x04FF60E,0x218F700, 0x40,0x00, 0xE, +0 }, + { 0x0297721,0x00B9721, 0x89,0x80, 0x6, +0 }, + { 0x12DC331,0x00F7861, 0x8A,0x00, 0xA, +0 }, + { 0x07A6161,0x00AC121, 0x99,0x80, 0x4, +0 }, + { 0x07A6161,0x00AC121, 0x9A,0x80, 0x4, +0 }, + { 0x04CA900,0x04FF600, 0x07,0x00, 0xA, +0 }, + { 0x075F80F,0x2B78A03, 0x80,0x00, 0xE, +0 }, + { 0x059A490,0x4C86590, 0x0B,0x00, 0xE, +0 }, + { 0x055A210,0x4766600, 0x0A,0x00, 0xE, +0 }, + { 0x059FA00,0x09AF500, 0x05,0x00, 0x6, +0 }, + { 0x02661B1,0x0266171, 0xD3,0x80, 0xD, +12 }, + { 0x035C100,0x0D5C111, 0x9B,0x00, 0xC, +12 }, + { 0x040B230,0x5E9F111, 0xA2,0x80, 0x4, +12 }, + { 0x0E6F314,0x0E6F280, 0x62,0x00, 0xB, +12 }, + { 0x715FE11,0x019F487, 0x20,0xC0, 0xB, +1 }, + { 0x7112EF0,0x11621E2, 0x00,0xC0, 0x9, -36 }, + { 0x00CF600,0x00CF600, 0x00,0x00, 0x1, +0 }, + { 0x001FF26,0x3751304, 0x00,0x00, 0xE, +0 }, + { 0x0E5F108,0x0E5C302, 0x66,0x86, 0x8, +5 }, + { 0x052F605,0x0D5F582, 0x69,0x47, 0x9, +5 }, + { 0x6E5E403,0x7E7F507, 0x0D,0x11, 0xB, +12 }, + { 0x366F500,0x4A8F604, 0x1B,0x15, 0xA, +12 }, + { 0x053F101,0x065D131, 0x4E,0x0C, 0x6, +0 }, + { 0x014F201,0x097F201, 0x22,0x08, 0xE, +0 }, + { 0x050F101,0x07CD301, 0x4F,0x10, 0x6, +0 }, + { 0x001F141,0x188D251, 0x4E,0x0A, 0x4, +0 }, + { 0x134A401,0x0A6C301, 0x0A,0x09, 0x9, +0 }, + { 0x103A361,0x022C411, 0x28,0x05, 0xC, +0 }, + { 0x010C733,0x033D311, 0x84,0x8A, 0x8, +0 }, + { 0x0188232,0x0076061, 0x1C,0x91, 0xC, +0 }, + { 0x0100132,0x0337212, 0x80,0x8F, 0x8, +0 }, + { 0x055F587,0x054F022, 0x91,0x13, 0x6, +0 }, + { 0x013F218,0x0E3C1E1, 0x4D,0x15, 0x8, +0 }, + { 0x043FA07,0x045F341, 0x51,0x11, 0x6, +0 }, + { 0x025DA05,0x015F901, 0x4E,0x0C, 0xA, +0 }, + { 0x0F0FE04,0x0B5F6C2, 0x00,0x0C, 0xE, +0 }, + { 0x032B6B3,0x031D190, 0x4A,0x0E, 0xE, +0 }, + { 0x011F111,0x0B3F101, 0x8A,0x4F, 0x6, +0 }, + { 0x10FFF22,0x00FFF21, 0x8D,0x87, 0x1, +0 }, + { 0x0B5F708,0x0CFD001, 0x07,0x11, 0x1, +0 }, + { 0x107E465,0x078F241, 0xD7,0x8C, 0x0, +0 }, + { 0x11F4001,0x11F8002, 0x42,0x0B, 0xB, +0 }, + { 0x0038121,0x00C6171, 0x12,0x92, 0x8, +0 }, + { 0x00BD224,0x00B5231, 0x4F,0x16, 0xE, +0 }, + { 0x243A321,0x022C411, 0x11,0x00, 0xD, +0 }, + { 0x143F401,0x074F111, 0x49,0x11, 0x4, +0 }, + { 0x133FF01,0x077F111, 0x80,0x17, 0xA, +0 }, + { 0x249A320,0x039C411, 0x0A,0x0C, 0xD, +0 }, + { 0x1E7C271,0x018F131, 0x09,0x13, 0x6, +0 }, + { 0x01FF201,0x047F701, 0x16,0x0D, 0xA, +0 }, + { 0x10BB021,0x057E221, 0x08,0x14, 0x4, +0 }, + { 0x010F631,0x016E233, 0x02,0x1A, 0x8, +0 }, + { 0x267AA01,0x013C603, 0x17,0x80, 0x8, +0 }, + { 0x1539321,0x08AC311, 0x1B,0x0B, 0x0, +0 }, + { 0x07BE001,0x098E212, 0x4E,0x12, 0x6, +0 }, + { 0x126F531,0x0C8E111, 0x49,0x0F, 0x4, +0 }, + { 0x023F221,0x0D6B212, 0x1C,0x0D, 0x4, +0 }, + { 0x0B8F413,0x0DBF111, 0x13,0x18, 0x2, +0 }, + { 0x147D621,0x00BF431, 0x88,0x8E, 0x8, +0 }, + { 0x175A501,0x0A48251, 0x86,0x16, 0x8, +0 }, + { 0x019D531,0x08B8352, 0x89,0x91, 0xA, +0 }, + { 0x0035171,0x0175421, 0x1C,0x0D, 0xE, +0 }, + { 0x0155471,0x0495321, 0x1C,0x11, 0xE, +0 }, + { 0x0035171,0x0175461, 0x56,0x10, 0xE, +0 }, + { 0x0035171,0x0175421, 0x1C,0x0C, 0xE, +0 }, + { 0x10351F1,0x01754A1, 0x16,0x0D, 0xC, +0 }, + { 0x0038171,0x017B601, 0x0E,0x0E, 0x8, +0 }, + { 0x075F502,0x0F3F201, 0x2A,0x8B, 0x0, +0 }, + { 0x117ED40,0x069C541, 0x80,0x89, 0x2, +0 }, + { 0x1DAD0A1,0x1D69012, 0x17,0x0D, 0xC, +0 }, + { 0x1DAD0A1,0x0D69012, 0x11,0x16, 0xA, +0 }, + { 0x1DAD061,0x1D69012, 0x11,0x11, 0xA, +0 }, + { 0x2DAD021,0x1D69091, 0x11,0x11, 0xA, +0 }, + { 0x207F0A0,0x03C7222, 0x17,0x16, 0xC, +0 }, + { 0x307F020,0x00C7022, 0x1A,0x16, 0x8, +0 }, + { 0x3078020,0x00C7022, 0x32,0x12, 0xE, +0 }, + { 0x20B73A1,0x246A500, 0x13,0x09, 0x8, +0 }, + { 0x00753B1,0x067D061, 0x19,0x13, 0xC, +0 }, + { 0x0064131,0x036A061, 0x1F,0x0F, 0xC, +0 }, + { 0x0586361,0x018A021, 0x19,0x12, 0xC, +0 }, + { 0x05A6321,0x01A7A21, 0x9F,0x80, 0xC, +0 }, + { 0x0577261,0x017A021, 0x19,0x12, 0xC, +0 }, + { 0x0777261,0x017A021, 0x15,0x14, 0xC, +0 }, + { 0x0577361,0x017A021, 0x19,0x13, 0xE, +0 }, + { 0x00A6331,0x00B63A1, 0x16,0x12, 0xC, +0 }, + { 0x00E7321,0x00E6361, 0x0E,0x15, 0x8, +0 }, + { 0x00A6331,0x00B6321, 0x13,0x14, 0xA, +0 }, + { 0x0178E71,0x00E8B22, 0xC3,0x13, 0x2, +0 }, + { 0x01B5132,0x0389261, 0x9A,0x89, 0xC, +0 }, + { 0x06FF4A1,0x01D53A1, 0x27,0x8A, 0xA, +0 }, + { 0x24369C1,0x00DBD21, 0x1C,0x0C, 0x0, +0 }, + { 0x0537901,0x20DBD21, 0x0D,0x0D, 0x6, +0 }, + { 0x0F0F530,0x09BF034, 0x35,0x13, 0x2, +0 }, + { 0x0F0F530,0x09BF032, 0x35,0x10, 0x2, +0 }, + { 0x047F021,0x078F012, 0x1B,0x16, 0xE, +0 }, + { 0x0FFF001,0x088F202, 0x0B,0x17, 0x8, +0 }, + { 0x25368C1,0x10DBD61, 0x14,0x0C, 0x0, +0 }, + { 0x014F6A1,0x0EAF102, 0x09,0x19, 0x2, +0 }, + { 0x02811A1,0x0187121, 0x20,0x11, 0xC, +0 }, + { 0x047F121,0x078D012, 0x11,0x19, 0xA, +0 }, + { 0x232B583,0x035D221, 0x52,0x14, 0x0, +0 }, + { 0x1137323,0x0229331, 0x1C,0x14, 0xC, +0 }, + { 0x0BF1182,0x38C9301, 0x10,0x16, 0xF, +0 }, + { 0x1E884A1,0x0487061, 0x19,0x95, 0x4, +0 }, + { 0x357B260,0x13C9022, 0x0E,0x91, 0x8, +0 }, + { 0x3679400,0x056B191, 0x4E,0x0E, 0xC, +0 }, + { 0x23AACA3,0x0BAC301, 0x18,0x12, 0x8, +0 }, + { 0x068C2A1,0x04872A1, 0x20,0x0D, 0xE, +0 }, + { 0x23A9CA3,0x04A9241, 0x17,0x95, 0x8, +0 }, + { 0x1A3C282,0x1F6E201, 0x18,0x11, 0x8, +0 }, + { 0x3C3A621,0x144E311, 0x14,0x0C, 0xA, +0 }, + { 0x243F702,0x027DC01, 0x18,0x00, 0x8, +0 }, + { 0x001A021,0x0612102, 0x02,0x18, 0xE, +0 }, + { 0x24C5803,0x11FF315, 0x00,0x00, 0x1, +0 }, + { 0x049C441,0x026F741, 0x08,0x03, 0xE, +0 }, + { 0x038FA00,0x07BF701, 0x06,0x00, 0x0, +0 }, + { 0x20A60E0,0x228F00E, 0x1D,0x00, 0xC, +0 }, + { 0x10A60E0,0x228F00E, 0x1D,0x00, 0x2, +0 }, + { 0x227F0F0,0x017F6C1, 0x0B,0x00, 0xE, +0 }, + { 0x05CA800,0x07FD600, 0x0F,0x00, 0x8, +0 }, + { 0x218F201,0x06BE601, 0x09,0x04, 0xC, +0 }, + { 0x246A321,0x026C511, 0x06,0x04, 0xE, +0 }, + { 0x248A721,0x006C801, 0x0A,0x04, 0x0, +0 }, + { 0x047A34F,0x138B703, 0x03,0x00, 0x2, +0 }, + { 0x248A721,0x015C801, 0x0A,0x04, 0x0, +0 }, + { 0x017A30E,0x119B602, 0x03,0x00, 0x8, +0 }, + { 0x248A721,0x015C801, 0x0A,0x07, 0x0, +0 }, + { 0x286F30D,0x148E404, 0x07,0x03, 0xA, +0 }, + { 0x248A721,0x025C801, 0x0C,0x07, 0x8, +0 }, + { 0x473A128,0x264A329, 0x07,0x00, 0x2, +0 }, + { 0x344F427,0x254F526, 0x09,0x00, 0xC, +0 }, + { 0x15B8308,0x32AC60A, 0x11,0x00, 0xA, +0 }, + { 0x473A048,0x264A329, 0x0D,0x00, 0x4, +0 }, + { 0x052F221,0x073D231, 0x4F,0x00, 0x6, +0 }, + { 0x050F201,0x076D201, 0x4B,0x03, 0x6, +0 }, + { 0x0F9F131,0x0F9F331, 0x8E,0x80, 0xA, +0 }, + { 0x0F9F131,0x0F9F332, 0x8E,0x81, 0xA, +0 }, + { 0x061F216,0x074F211, 0x4F,0x0A, 0x8, +0 }, + { 0x0617216,0x0B2F311, 0x4F,0x08, 0x8, +0 }, + { 0x212AA93,0x021AC91, 0x97,0x00, 0xE, +0 }, + { 0x016DA85,0x005F981, 0x4D,0x80, 0xA, +0 }, + { 0x065FE05,0x085F8C4, 0x05,0x00, 0xE, +0 }, + { 0x096F527,0x057F521, 0x1F,0x03, 0x8, +0 }, + { 0x053F103,0x074F217, 0x4F,0x0B, 0x6, +0 }, + { 0x00FFF64,0x00FFF21, 0x86,0x80, 0x1, +0 }, + { 0x20BD8F0,0x10BB3F2, 0x93,0x07, 0xA, +0 }, + { 0x4069FB2,0x10F95B0, 0x43,0x00, 0x9, +0 }, + { 0x0FFF001,0x00F9033, 0x4F,0x05, 0x6, +0 }, + { 0x10BF224,0x00B5231, 0x4F,0x10, 0xE, +0 }, + { 0x0035121,0x06742A2, 0x15,0x80, 0xA, +0 }, + { 0x0AFF5E1,0x10FF4E1, 0xD0,0x00, 0xC, +0 }, + { 0x001FF11,0x003FF11, 0x8D,0x80, 0x0, +0 }, + { 0x031F121,0x044F406, 0x40,0x85, 0x0, +0 }, + { 0x0BF73C8,0x09FF4C4, 0x12,0x03, 0x8, +0 }, + { 0x0B69402,0x0268301, 0x00,0x00, 0x1, +0 }, + { 0x0EEC101,0x0DEF302, 0x62,0x00, 0xA, +0 }, + { 0x0EFF231,0x078F522, 0x1E,0x00, 0xE, +0 }, + { 0x1B57431,0x0B8D423, 0x0B,0x00, 0x8, +0 }, + { 0x2035130,0x24753A0, 0x1C,0x00, 0xE, +0 }, + { 0x3115230,0x1254131, 0xD0,0x80, 0x0, +0 }, + { 0x11152B1,0x1FE41B2, 0xC5,0x80, 0x0, +0 }, + { 0x07572C1,0x1FE61C1, 0xCA,0x80, 0x6, +0 }, + { 0x0A7F131,0x0C6F731, 0x50,0x80, 0xE, +0 }, + { 0x171F502,0x083F211, 0x60,0x40, 0xE, +0 }, + { 0x2005130,0x2655420, 0x1C,0x00, 0xE, +0 }, + { 0x01151B1,0x1154261, 0x8B,0x40, 0x6, +0 }, + { 0x1817021,0x12C7322, 0x16,0x07, 0xC, +0 }, + { 0x0537141,0x07C62C2, 0x4F,0x40, 0xA, +0 }, + { 0x173F141,0x074F241, 0x4F,0x10, 0x6, +0 }, + { 0x10691C1,0x20562C1, 0x0F,0x00, 0xC, +0 }, + { 0x00B4131,0x03B9261, 0x1C,0x80, 0xE, +0 }, + { 0x0655201,0x0767301, 0x1D,0x00, 0xE, +0 }, + { 0x0AE71E1,0x09E81E2, 0x15,0x0A, 0xC, +0 }, + { 0x029BB21,0x00A9021, 0x8E,0x80, 0x8, +0 }, + { 0x0AE71E1,0x09E81E1, 0x16,0x0A, 0xA, +0 }, + { 0x2AE71E0,0x19E80E2, 0x23,0x00, 0xA, +0 }, + { 0x0687121,0x05E5232, 0x4E,0x00, 0xA, +0 }, + { 0x05B7111,0x07B5212, 0x56,0x00, 0xE, +0 }, + { 0x009F021,0x00A9024, 0x94,0x05, 0xA, +0 }, + { 0x0176EB1,0x00E8BA1, 0xC5,0x80, 0x2, +0 }, + { 0x02495A1,0x02A60A1, 0x1D,0x85, 0x2, +0 }, + { 0x0195132,0x0396061, 0x9A,0x8B, 0xC, +0 }, + { 0x030F5A2,0x03A61A1, 0x12,0x8B, 0x2, +0 }, + { 0x00457E2,0x0775761, 0x6D,0x00, 0xE, +0 }, + { 0x0C70CF1,0x0A560F1, 0x9A,0x80, 0xD, +0 }, + { 0x0537102,0x07C5211, 0x4F,0x05, 0xA, +0 }, + { 0x007F804,0x0748201, 0x08,0x05, 0x8, +0 }, + { 0x04FF660,0x00F7660, 0x03,0x04, 0x2, +0 }, + { 0x33457F1,0x00D67E1, 0x28,0x04, 0xE, +0 }, + { 0x0F55551,0x0F55501, 0x80,0x00, 0x8, +0 }, + { 0x0339661,0x02B5521, 0x00,0x02, 0x6, +0 }, + { 0x0F2F251,0x2F2F241, 0x0D,0x00, 0xA, +0 }, + { 0x091A311,0x094C503, 0x80,0x80, 0x6, +0 }, + { 0x145F171,0x044F423, 0x00,0x00, 0x5, +0 }, + { 0x251B1E0,0x275E0F0, 0x16,0x03, 0x0, +0 }, + { 0x102FF51,0x002FF01, 0x03,0x08, 0x4, +0 }, + { 0x11122F1,0x02E31F1, 0x46,0x80, 0xC, +0 }, + { 0x0FFF101,0x0FF5011, 0x0D,0x80, 0x6, +0 }, + { 0x0FF1000,0x0FF5011, 0x12,0x80, 0xA, +0 }, + { 0x002A4B4,0x04245F5, 0x87,0x80, 0x6, +0 }, + { 0x01111F1,0x01111F1, 0x41,0x41, 0x2, +0 }, + { 0x002A4B4,0x04245F7, 0x87,0x80, 0x6, +0 }, + { 0x1007861,0x247A260, 0x54,0x03, 0x6, +0 }, + { 0x0417F21,0x0213521, 0x56,0x00, 0xE, +0 }, + { 0x301F171,0x001F131, 0x00,0x40, 0x4, +0 }, + { 0x053F101,0x074F219, 0x4F,0x00, 0x6, +0 }, + { 0x01FF201,0x088F508, 0x11,0x00, 0x8, +0 }, + { 0x1176E31,0x20C8B22, 0x43,0x05, 0x2, +0 }, + { 0x1037531,0x0445462, 0x1C,0x00, 0xE, +0 }, + { 0x0427880,0x0548595, 0x4D,0x00, 0xE, +0 }, + { 0x072F107,0x004FC08, 0x48,0x80, 0x0, +0 }, + { 0x0FFF835,0x075F511, 0x44,0x00, 0xE, +0 }, + { 0x1068F02,0x005FF00, 0xC0,0x00, 0xA, +0 }, + { 0x0ECA710,0x0F5D510, 0x0B,0x08, 0x0, +0 }, + { 0x10B5F01,0x10B5F01, 0x80,0x80, 0x4, +0 }, + { 0x2056651,0x0066642, 0x00,0x05, 0x0, +0 }, + { 0x000200E,0x001210E, 0x00,0x00, 0xE, +0 }, + { 0x08785F4,0x09974F3, 0x50,0x80, 0xC, +0 }, + { 0x050F102,0x076D201, 0x50,0x0E, 0x6, +0 }, + { 0x050F101,0x076D201, 0x4B,0x0E, 0x6, +0 }, + { 0x050F113,0x076D201, 0x50,0x0E, 0x6, +0 }, + { 0x011FF32,0x013FF01, 0x92,0x8B, 0xA, +0 }, + { 0x010FF34,0x004FF03, 0x92,0x0B, 0xA, +0 }, + { 0x000F153,0x086D251, 0x4E,0x11, 0x6, +0 }, + { 0x0E5F828,0x0FFC021, 0xCF,0x0B, 0x0, +0 }, + { 0x0E5F8E2,0x00EC0E1, 0xCA,0x0B, 0x8, +0 }, + { 0x0FFF92C,0x0FFC0A1, 0xD4,0x0B, 0x0, +0 }, + { 0x0E5F82B,0x0FFC021, 0xCA,0x0B, 0x0, +0 }, + { 0x091F029,0x086E021, 0xCD,0x0B, 0x2, +0 }, + { 0x001F024,0x086E021, 0xD0,0x0B, 0x2, +0 }, + { 0x001F023,0x086E021, 0xC8,0x0B, 0x2, +0 }, + { 0x001B064,0x086F061, 0xC9,0x0B, 0x2, +0 }, + { 0x010A133,0x0237215, 0x85,0x8B, 0x8, +0 }, + { 0x010A131,0x0337315, 0x85,0x8B, 0x8, +0 }, + { 0x030A131,0x074C216, 0x81,0x8B, 0x8, +0 }, + { 0x07BF003,0x07BF402, 0x8A,0x8B, 0x8, +0 }, + { 0x07BF003,0x07BF401, 0x8A,0x80, 0x8, +0 }, + { 0x07BF223,0x07BF401, 0x8A,0x80, 0x8, +0 }, + { 0x0100132,0x0337212, 0x80,0x8B, 0x8, +0 }, + { 0x0100132,0x0337314, 0x80,0x8B, 0x8, +0 }, + { 0x08E7331,0x09E8021, 0x16,0x0B, 0xE, +0 }, + { 0x07E7330,0x09E8021, 0x16,0x0B, 0xE, +0 }, + { 0x0733331,0x097A021, 0x94,0x00, 0xE, +0 }, + { 0x073D331,0x097A021, 0x94,0x0C, 0xE, +0 }, + { 0x053F131,0x027F232, 0x45,0x0B, 0x6, +0 }, + { 0x001F213,0x0B6F215, 0x0C,0x18, 0x8, +0 }, + { 0x001F211,0x0B6F211, 0x0C,0x0B, 0x8, +0 }, + { 0x004FE11,0x0BDF211, 0x0A,0x0B, 0x8, +0 }, + { 0x011CA53,0x0F171E1, 0x4D,0x13, 0x2, +0 }, + { 0x011BA12,0x03124F1, 0x40,0x0C, 0x2, +0 }, + { 0x08E7261,0x01A50E1, 0xA7,0x8B, 0x2, +0 }, + { 0x0133218,0x0E351E1, 0x4D,0x0C, 0x8, +0 }, + { 0x0411217,0x0311331, 0xC0,0x8B, 0x6, +0 }, + { 0x055F503,0x033F321, 0x8F,0x8B, 0x0, +0 }, + { 0x011FA13,0x0F1F1E1, 0x4D,0x0C, 0x8, +0 }, + { 0x0154011,0x0F8A1F1, 0x43,0x0C, 0x8, +0 }, + { 0x0978211,0x0F2F0E4, 0x03,0x4C, 0x8, +0 }, + { 0x053D105,0x0715114, 0x40,0x0B, 0x6, +0 }, + { 0x01727F1,0x0185120, 0x01,0x0B, 0xC, +0 }, + { 0x01132F1,0x013F1E1, 0x18,0x0B, 0x0, +0 }, + { 0x053F173,0x006F171, 0x48,0x17, 0x8, +0 }, + { 0x0117171,0x0157261, 0x8D,0x4B, 0x6, +0 }, + { 0x061F2D7,0x0B2F1D2, 0x4F,0x0B, 0x8, +0 }, + { 0x0FFF001,0x0F8F001, 0x11,0x0B, 0xA, +0 }, + { 0x0114131,0x0132261, 0x8B,0x0B, 0x6, +0 }, + { 0x021FF31,0x0154461, 0x8B,0x0B, 0xA, +0 }, + { 0x0114131,0x0153261, 0x8B,0x0B, 0x2, +0 }, + { 0x013FD71,0x0D6E721, 0x1C,0x0B, 0xE, +0 }, + { 0x0035171,0x0675421, 0x1C,0x0B, 0xE, +0 }, + { 0x0035171,0x0175421, 0x1C,0x0B, 0xE, +0 }, + { 0x0155471,0x0495321, 0x1C,0x0B, 0xE, +0 }, + { 0x0035171,0x0175461, 0x56,0x0B, 0xE, +0 }, + { 0x075F502,0x0F3F201, 0x29,0x8B, 0x0, +0 }, + { 0x075F002,0x033F401, 0x29,0x8B, 0x0, +0 }, + { 0x053F101,0x074F111, 0x49,0x0B, 0x6, +0 }, + { 0x053F101,0x074F111, 0x89,0x0B, 0x6, +0 }, + { 0x053F102,0x074F111, 0x89,0x0B, 0x6, +0 }, + { 0x053F102,0x074F111, 0x80,0x0B, 0x6, +0 }, + { 0x053F101,0x053F108, 0x40,0x4B, 0x0, +0 }, + { 0x02CD321,0x02CC321, 0x15,0x8B, 0xA, +0 }, + { 0x0F2D401,0x08AC421, 0x18,0x8B, 0xA, +0 }, + { 0x07BF001,0x0C8F411, 0x4E,0x0B, 0x4, +0 }, + { 0x0ABF001,0x0ABF311, 0x44,0x0B, 0x4, +0 }, + { 0x0C8F453,0x0BBF111, 0x0E,0x0B, 0x4, +0 }, + { 0x0C8F253,0x0C5F211, 0x0B,0x0B, 0x4, +0 }, + { 0x04CB421,0x0AC9421, 0x15,0x0B, 0xA, +0 }, + { 0x01C9421,0x0AC6421, 0x15,0x0B, 0xA, +0 }, + { 0x08F7721,0x02A60A1, 0x16,0x8B, 0x6, +0 }, + { 0x0BF7721,0x02A60A1, 0x19,0x8B, 0x6, +0 }, + { 0x0AFD6A1,0x02A60E2, 0x13,0x8B, 0x2, +0 }, + { 0x02495A2,0x02A60E2, 0x1D,0x8B, 0x2, +0 }, + { 0x130F4A4,0x02A60E1, 0x12,0x8B, 0xA, +0 }, + { 0x00E6321,0x00E6321, 0x16,0x0B, 0xC, +0 }, + { 0x00A6331,0x00B6321, 0x16,0x0B, 0xC, +0 }, + { 0x00A6321,0x00B6321, 0x1B,0x0B, 0xC, +0 }, + { 0x00A6320,0x00B6321, 0x1B,0x0B, 0xC, +0 }, + { 0x0188232,0x0076061, 0x1C,0x8B, 0xC, +0 }, + { 0x0145132,0x03662E1, 0x18,0x8B, 0xC, +0 }, + { 0x0178731,0x00E8B22, 0xC3,0x0B, 0x2, +0 }, + { 0x0178E71,0x00E8B22, 0xC3,0x0F, 0x2, +0 }, + { 0x0176E70,0x00E6B22, 0x8D,0x0B, 0x2, +0 }, + { 0x006F224,0x0065231, 0x4F,0x0B, 0xE, +0 }, + { 0x0076431,0x067D061, 0x1B,0x0B, 0xE, +0 }, + { 0x0066131,0x036D261, 0x1B,0x0B, 0xC, +0 }, + { 0x0063131,0x0365061, 0x1F,0x0B, 0xC, +0 }, + { 0x0064131,0x036A061, 0x1F,0x0B, 0xC, +0 }, + { 0x0565321,0x016A021, 0x9A,0x8B, 0xE, +0 }, + { 0x0585361,0x018A021, 0x19,0x0B, 0xC, +0 }, + { 0x0577361,0x017A021, 0x19,0x0B, 0xC, +0 }, + { 0x0A67121,0x096A121, 0x1B,0x0B, 0xE, +0 }, + { 0x044F585,0x045F0A1, 0x91,0x0B, 0x6, +0 }, + { 0x033F507,0x025F061, 0x51,0x0B, 0x6, +0 }, + { 0x021FF13,0x003FF11, 0x8C,0x80, 0xE, +0 }, + { 0x00DF338,0x033F5B1, 0x8C,0x40, 0xE, +0 }, + { 0x055F587,0x054F022, 0x91,0x0B, 0x6, +0 }, + { 0x032B6B3,0x031D190, 0x4A,0x0B, 0xE, +0 }, + { 0x0F0FEC4,0x0B5F6C2, 0x0E,0x12, 0x0, +0 }, + { 0x015DA05,0x013F001, 0x4E,0x80, 0xA, +0 }, + { 0x09AF231,0x027F032, 0x44,0x0B, 0x6, +0 }, + { 0x002A4B0,0x04240D7, 0xC4,0x8B, 0x0, +0 }, + { 0x0F0F0CA,0x06259CC, 0x84,0x0B, 0xC, +0 }, + { 0x0F0F530,0x09BF035, 0x35,0x0B, 0x2, +0 }, + { 0x002A4B4,0x04240D7, 0x87,0x8B, 0x6, +0 }, + { 0x0530907,0x094F605, 0x40,0x0B, 0xE, +0 }, + { 0x025DA09,0x015F101, 0x4E,0x0B, 0xA, +0 }, + { 0x0A0F406,0x046F600, 0x00,0x0B, 0xE, +0 }, + { 0x0F0F007,0x0DC5C00, 0x00,0x0B, 0xE, +0 }, + { 0x0FFF832,0x07FF511, 0x44,0x0B, 0xE, +0 }, + { 0x0FFF832,0x07FF511, 0x44,0x0E, 0xE, +0 }, + { 0x0FFF832,0x07FF511, 0x44,0x10, 0xE, +0 }, + { 0x0530900,0x094F702, 0x40,0x00, 0xE, +0 }, + { 0x0A8F211,0x0A8A001, 0x86,0x8B, 0x8, +0 }, + { 0x070F200,0x072F213, 0x50,0x0B, 0xE, +0 }, + { 0x01111F0,0x01111E0, 0x00,0xCB, 0xE, +0 }, + { 0x060F207,0x072F212, 0x4F,0x0B, 0x8, +0 }, + { 0x04FA800,0x04FD600, 0x0B,0x00, 0x0, +0 }, + { 0x0BFF80C,0x04FD600, 0x00,0x00, 0x1, +0 }, + { 0x0BFF704,0x04FD600, 0x00,0x00, 0x1, +0 }, + { 0x0BFF501,0x04FD600, 0x00,0x00, 0x1, +0 }, + { 0x0BFF701,0x00F10DE, 0x00,0x00, 0x1, +0 }, + { 0x0045617,0x004F601, 0x21,0x00, 0x2, +12 }, + { 0x0790825,0x0E6E385, 0x9A,0x5B, 0xA, +0 }, + { 0x0E6F315,0x0E6F281, 0x62,0x00, 0xB, +0 }, + { 0x055F71C,0x0D88520, 0xA3,0x0D, 0x6, +12 }, + { 0x002B025,0x0057030, 0x5F,0x40, 0xC, +12 }, + { 0x042B401,0x0C8F201, 0x12,0x00, 0xA, +0 }, + { 0x261B235,0x015F414, 0x1C,0x08, 0xA, +0 }, + { 0x1112EF0,0x11621E2, 0x00,0xC0, 0x8, +0 }, + { 0x00AF601,0x036D600, 0x07,0x00, 0x0, +0 }, + { 0x006F600,0x00CF600, 0x00,0x00, 0x1, +0 }, + { 0x204FF82,0x055FF10, 0x00,0x06, 0xE, +0 }, + { 0x00CFD01,0x036D600, 0x07,0x00, 0x0, +0 }, + { 0x3E2E20F,0x1E3F308, 0x00,0x0A, 0x6, +0 }, + { 0x366F30F,0x1A5F508, 0x00,0x19, 0x7, +0 }, + { 0x3E2E20F,0x1E4F408, 0x00,0x0A, 0x6, +0 }, + { 0x153F101,0x074F111, 0x49,0x04, 0x6, +0 }, + { 0x153F101,0x074F111, 0x89,0x07, 0x6, +0 }, + { 0x160F101,0x07BD211, 0x4D,0x01, 0x8, +0 }, + { 0x153F181,0x074F111, 0x49,0x00, 0x6, +0 }, + { 0x150F101,0x07CD201, 0x4F,0x05, 0x6, +0 }, + { 0x118F603,0x0F9F212, 0x1C,0x04, 0xF, +0 }, + { 0x1F9F131,0x0F9F331, 0x0E,0x04, 0xA, +0 }, + { 0x153F101,0x074F111, 0x49,0x01, 0x6, +0 }, + { 0x1100133,0x0037D14, 0x07,0x00, 0x8, +0 }, + { 0x1F0F517,0x0F3F201, 0x53,0x09, 0x8, +0 }, + { 0x1FFF5A3,0x0FFF5A2, 0x47,0x00, 0x0, +0 }, + { 0x154F606,0x0B3F281, 0x73,0x03, 0x0, +0 }, + { 0x105F012,0x003F011, 0x15,0x80, 0xA, +0 }, + { 0x108F006,0x008F001, 0x0E,0x00, 0xE, +0 }, + { 0x101FF64,0x062F32E, 0x1B,0x00, 0x4, +0 }, + { 0x4049404,0x0059500, 0x00,0x00, 0x0, +0 }, + { 0x1118371,0x0828F73, 0x03,0x80, 0x9, +0 }, + { 0x111C371,0x082CF73, 0x03,0x80, 0x9, +0 }, + { 0x10381F0,0x005F171, 0xD9,0x85, 0xE, +0 }, + { 0x10F75F2,0x00FFFF0, 0x81,0x0E, 0x3, +0 }, + { 0x1037532,0x0F8B062, 0x1C,0x04, 0xE, +0 }, + { 0x10BF224,0x00B5231, 0x4F,0x08, 0xE, +0 }, + { 0x1F09091,0x0FC4082, 0x88,0x80, 0x8, +0 }, + { 0x10BF261,0x00B5270, 0x68,0x10, 0xA, +0 }, + { 0x131F121,0x045C302, 0x0F,0x03, 0x0, +0 }, + { 0x112F101,0x082F101, 0x10,0x04, 0xA, +0 }, + { 0x1518503,0x071D211, 0x5E,0x07, 0xE, +0 }, + { 0x113F201,0x0F88401, 0x11,0x00, 0xA, +0 }, + { 0x121FF13,0x003FF11, 0x16,0x00, 0xA, +0 }, + { 0x14AF8F0,0x047F022, 0x00,0x0A, 0x8, +0 }, + { 0x11797F0,0x018F161, 0x01,0x0A, 0x8, +0 }, + { 0x11797F1,0x018F126, 0x01,0x08, 0x8, +0 }, + { 0x1EFF201,0x078F101, 0x1D,0x0A, 0xA, +0 }, + { 0x10FF7E1,0x00BF9B1, 0x9A,0x09, 0x8, +0 }, + { 0x1618221,0x0619522, 0x12,0x05, 0x8, +0 }, + { 0x18AE221,0x0A8E421, 0x11,0x00, 0xA, +0 }, + { 0x150F101,0x025F301, 0x4F,0x05, 0x6, +0 }, + { 0x1937511,0x082F501, 0x4F,0x05, 0x0, +0 }, + { 0x119D531,0x01B6171, 0x88,0x80, 0xC, +0 }, + { 0x125F871,0x085F171, 0x40,0x08, 0x8, +0 }, + { 0x1035131,0x0065461, 0x1C,0x04, 0xE, +0 }, + { 0x1035131,0x0065461, 0x16,0x04, 0xE, +0 }, + { 0x11152B0,0x00531B1, 0xC5,0x82, 0x0, +0 }, + { 0x1B69401,0x0268300, 0x00,0x14, 0x1, +0 }, + { 0x11171B1,0x0154261, 0x82,0x04, 0x6, +0 }, + { 0x171E4B1,0x0E5E461, 0x8B,0x40, 0x6, +0 }, + { 0x1829531,0x0B1F130, 0x9C,0x88, 0xC, +0 }, + { 0x1847824,0x004B000, 0x9A,0x00, 0x0, +0 }, + { 0x111A1B1,0x0157261, 0x81,0x04, 0x6, +0 }, + { 0x11161B1,0x0153261, 0x81,0x04, 0x6, +0 }, + { 0x1339111,0x0345122, 0x8A,0x80, 0x6, +0 }, + { 0x11171B1,0x0154261, 0x85,0x04, 0x6, +0 }, + { 0x015E5D1,0x0057B72, 0x5B,0x82, 0x0, +0 }, + { 0x04964F2,0x0069261, 0x90,0x06, 0x0, +0 }, + { 0x1537101,0x00CB222, 0x4F,0x08, 0xA, +0 }, + { 0x1526641,0x0768501, 0x00,0x00, 0x0, +0 }, + { 0x0177E61,0x0098E21, 0x92,0x00, 0xE, +0 }, + { 0x0176E60,0x0096E21, 0x92,0x10, 0xE, +0 }, + { 0x165C201,0x006F321, 0x1D,0x0C, 0xE, +0 }, + { 0x0177E61,0x0098E21, 0x8F,0x04, 0xE, +0 }, + { 0x15A5321,0x01AAA21, 0x9F,0x82, 0xC, +0 }, + { 0x1AE71E1,0x00E81E2, 0x15,0x08, 0xE, +0 }, + { 0x1AE7081,0x09EB023, 0x12,0x09, 0xA, +0 }, + { 0x1AE7081,0x09EB023, 0x18,0x09, 0xA, +0 }, + { 0x1FB7012,0x0FF5014, 0x92,0x04, 0xE, +0 }, + { 0x1FB7012,0x0FF5013, 0x92,0x06, 0xE, +0 }, + { 0x1FB7011,0x0FF5013, 0x92,0x02, 0xE, +0 }, + { 0x1FB7010,0x0FF5011, 0x92,0x0A, 0xE, +0 }, + { 0x1FB7010,0x0FF5013, 0x92,0x06, 0xE, +0 }, + { 0x1FB7011,0x0FF5011, 0x92,0x02, 0xE, +0 }, + { 0x119D530,0x01B6171, 0xC8,0x82, 0xC, +0 }, + { 0x11B5132,0x00BA261, 0x1A,0x0A, 0xC, +0 }, + { 0x1297461,0x0097362, 0x12,0x80, 0xB, +0 }, + { 0x05FF732,0x01F65B1, 0x43,0x80, 0x8, +0 }, + { 0x05F87B1,0x01F67B0, 0x83,0x83, 0x8, +0 }, + { 0x05F8732,0x01F65B1, 0x83,0x80, 0x8, +0 }, + { 0x15F87A2,0x01F65B1, 0x03,0x00, 0x6, +0 }, + { 0x0177E62,0x0098E21, 0x92,0x0C, 0xE, +0 }, + { 0x1C70CB3,0x0A560B2, 0x9A,0x80, 0xD, +0 }, + { 0x15F6721,0x0FF5501, 0x83,0x86, 0x7, +0 }, + { 0x11797F1,0x0E8F121, 0x00,0x04, 0x8, +0 }, + { 0x31797F1,0x0E8F121, 0x00,0x06, 0x8, +0 }, + { 0x15F8781,0x01B6580, 0x83,0x80, 0x6, +0 }, + { 0x1F69401,0x009F426, 0x80,0x04, 0xA, +0 }, + { 0x1F69442,0x008F423, 0x80,0x04, 0xA, +0 }, + { 0x10875E6,0x00963E3, 0x66,0x00, 0xF, +0 }, + { 0x1177426,0x017F5A0, 0x8E,0x83, 0xD, +0 }, + { 0x116F1A1,0x008F421, 0x88,0x02, 0xC, +0 }, + { 0x143C373,0x0432370, 0x0C,0x00, 0x5, +0 }, + { 0x04914F2,0x0665261, 0x90,0x08, 0x0, +0 }, + { 0x11797B1,0x018F161, 0x06,0x08, 0x8, +0 }, + { 0x1176E81,0x0048B22, 0xC5,0x08, 0x8, +0 }, + { 0x100586E,0x0012632, 0x18,0x80, 0x6, +0 }, + { 0x104C113,0x0075161, 0xD3,0x0A, 0xE, +0 }, + { 0x107F021,0x0089022, 0x8E,0x40, 0x0, +0 }, + { 0x111D570,0x0112671, 0xC8,0x82, 0xA, +0 }, + { 0x1427887,0x00485B6, 0x4D,0x02, 0xA, +0 }, + { 0x11171B1,0x0154261, 0x8B,0x00, 0x6, +0 }, + { 0x654F699,0x003F2A1, 0x33,0x08, 0x0, +0 }, + { 0x1537101,0x0047132, 0x49,0x0A, 0x6, +0 }, + { 0x102A4B4,0x00245F6, 0x07,0x00, 0x6, +0 }, + { 0x10214B3,0x00285F5, 0x07,0x00, 0x6, +0 }, + { 0x015E5D1,0x0027B72, 0x9B,0x83, 0x0, +0 }, + { 0x1339660,0x02B5520, 0x00,0x03, 0x6, +0 }, + { 0x153F101,0x053F108, 0x00,0x00, 0x0, +0 }, + { 0x11FF721,0x03FF523, 0x0A,0x00, 0x4, +0 }, + { 0x153F101,0x088F108, 0x00,0x00, 0x0, +0 }, + { 0x110F201,0x004F508, 0x11,0x00, 0x8, +0 }, + { 0x105F011,0x003F010, 0x15,0x40, 0xA, +0 }, + { 0x1176E30,0x00C8B21, 0x61,0x0C, 0x2, +0 }, + { 0x1035131,0x0075462, 0x1C,0x05, 0xE, +0 }, + { 0x2FB7010,0x0FF5013, 0x52,0x06, 0xE, +0 }, + { 0x106FF09,0x004FF84, 0x4D,0x00, 0xC, +0 }, + { 0x106FF09,0x007FF84, 0x0D,0x00, 0xC, +0 }, + { 0x1847825,0x004B001, 0x9A,0x06, 0x0, +0 }, + { 0x340FF55,0x007FF12, 0x80,0x00, 0x0, +0 }, + { 0x340FF90,0x003FF10, 0x80,0x11, 0x0, +0 }, + { 0x040FF10,0x003FF10, 0x80,0x8C, 0xE, +0 }, + { 0x640FF10,0x003FF10, 0x37,0x0E, 0x0, +0 }, + { 0x000FF4E,0x0FD1F40, 0x00,0x00, 0xA, +0 }, + { 0x1945315,0x0757800, 0x00,0x00, 0x0, +0 }, + { 0x3063F72,0x0075F20, 0x85,0x0A, 0x6, +0 }, + { 0x000FF4E,0x0021E60, 0x00,0x00, 0xA, +0 }, + { 0x1F0F000,0x0FF5F09, 0x2E,0x00, 0xE, +0 }, + { 0x111FE3E,0x019F123, 0x00,0xC0, 0x8, +0 }, + { 0x111FEB0,0x019F1A0, 0x00,0xC0, 0x8, +0 }, + { 0x000FF4E,0x0022C60, 0x00,0x00, 0xA, +0 }, + { 0x000FF0D,0x006F020, 0x00,0x00, 0xA, +0 }, + { 0x0000000,0x0000000, 0x00,0x00, 0x0, +0 }, + { 0x0E8E800,0x0F8A500, 0x0D,0x00, 0x6, +0 }, + { 0x038EC12,0x009FA00, 0x06,0x06, 0xE, +0 }, + { 0x2F5F02F,0x207FA0F, 0x00,0x00, 0xE, +0 }, + { 0x077F005,0x0EDFA00, 0x00,0x00, 0xE, +0 }, + { 0x0F0F006,0x0F7F700, 0x00,0x00, 0xE, +0 }, + { 0x0F6F600,0x097F700, 0x00,0x03, 0x1, +0 }, + { 0x100F046,0x067FE02, 0x00,0x00, 0xE, +0 }, + { 0x0F6F600,0x0C7F700, 0x00,0x03, 0x1, +0 }, + { 0x0F0F063,0x2099902, 0x00,0x03, 0xE, +0 }, + { 0x0F6F600,0x0C6F600, 0x00,0x03, 0x1, +0 }, + { 0x1F0F043,0x204FD02, 0x00,0x03, 0xE, +0 }, + { 0x0F6F500,0x0C5F500, 0x00,0x03, 0x1, +0 }, + { 0x000F00F,0x2F4F4A0, 0x00,0x00, 0xE, +0 }, + { 0x342F809,0x3E4F407, 0x06,0x40, 0xE, +0 }, + { 0x320F413,0x254F800, 0x4B,0x00, 0xE, +0 }, + { 0x04F960E,0x218B700, 0x40,0x08, 0xE, +0 }, + { 0x276F502,0x0D6F809, 0x1B,0x05, 0x4, +0 }, + { 0x10070E1,0x0F4A4E0, 0x00,0x09, 0xE, +0 }, + { 0x342F809,0x3E4F404, 0x06,0x44, 0xE, +0 }, + { 0x1F8F830,0x0B6F511, 0x21,0x08, 0x0, +0 }, + { 0x1F8F830,0x0A6F511, 0x1E,0x08, 0x0, +0 }, + { 0x248EB00,0x078F700, 0x95,0x0D, 0x0, +0 }, + { 0x259FB00,0x038E700, 0x94,0x0D, 0x0, +0 }, + { 0x256FB00,0x0C7F600, 0x98,0x0D, 0x0, +0 }, + { 0x1F8F832,0x0F5F531, 0x85,0x08, 0xC, +0 }, + { 0x1BAE812,0x099F511, 0x80,0x08, 0xC, +0 }, + { 0x387FD00,0x0F6E622, 0x00,0x08, 0x0, +0 }, + { 0x387FD00,0x0F6F522, 0x00,0x08, 0x0, +0 }, + { 0x0FEF025,0x2586C03, 0x00,0x93, 0xE, +0 }, + { 0x04F760E,0x2187704, 0x40,0x08, 0xE, +0 }, + { 0x3F77723,0x2F68623, 0x04,0x0A, 0xC, +0 }, + { 0x3F76623,0x2F68623, 0x04,0x0A, 0xC, +0 }, + { 0x306FF80,0x0176F11, 0x00,0x0B, 0xE, +0 }, + { 0x306FF80,0x0166F11, 0x00,0x0B, 0xE, +0 }, + { 0x1D1F813,0x0F5F532, 0x61,0x0C, 0x6, +0 }, + { 0x1D1F813,0x0F6F632, 0x6C,0x08, 0x6, +0 }, + { 0x045FC41,0x0C56943, 0x45,0x00, 0x0, +0 }, + { 0x045FC41,0x0056942, 0x45,0x00, 0x0, +0 }, + { 0x060F205,0x07AF414, 0x51,0x80, 0xA, +0 }, + { 0x060F285,0x0B8F294, 0x51,0x80, 0xA, +0 }, + { 0x1F5F213,0x0F5F111, 0xC6,0x00, 0x0, +0 }, + { 0x013F201,0x043F501, 0x22,0x00, 0xE, +0 }, + { 0x0F9F131,0x0F9F332, 0x8E,0x80, 0xA, +0 }, + { 0x060F207,0x072F212, 0x4F,0x0A, 0x8, +0 }, + { 0x015DA85,0x013F981, 0x4E,0x80, 0xA, +0 }, + { 0x0F0FF06,0x0B5F8C4, 0x00,0x00, 0xE, +0 }, + { 0x060F217,0x072F202, 0x4F,0x10, 0x8, +0 }, + { 0x053F103,0x074F217, 0x0F,0x0B, 0x0, +0 }, + { 0x00FFF24,0x00FFF22, 0x80,0x40, 0x1, +0 }, + { 0x0FFF001,0x00F9031, 0x4F,0x00, 0x6, +0 }, + { 0x1069FB2,0x10FB4B0, 0xC0,0x80, 0x9, +0 }, + { 0x0FFF001,0x00F9033, 0x4F,0x08, 0x6, +0 }, + { 0x00BF224,0x00B9231, 0x4F,0x10, 0xE, +0 }, + { 0x0035121,0x0677262, 0x15,0x80, 0xA, +0 }, + { 0x1AFF5E0,0x10FF4E1, 0xCE,0x00, 0xC, +0 }, + { 0x021FF13,0x003FF11, 0x93,0x80, 0xA, +0 }, + { 0x101FF11,0x003FF11, 0x8B,0x80, 0x0, +0 }, + { 0x171F503,0x083F211, 0x5E,0x00, 0xE, +0 }, + { 0x031F121,0x044F406, 0x40,0x80, 0x0, +0 }, + { 0x01A9161,0x01AC1E5, 0x40,0x03, 0x8, +0 }, + { 0x0AE71E1,0x07EF0E7, 0x16,0x40, 0xA, +0 }, + { 0x0EEC101,0x0DEF302, 0x23,0x00, 0xA, +0 }, + { 0x071FB51,0x0B9F301, 0x00,0x00, 0x0, +0 }, + { 0x0EFF230,0x078F520, 0x1E,0x00, 0xE, +0 }, + { 0x1889501,0x003FF12, 0x40,0x00, 0x6, +0 }, + { 0x1F7F501,0x2F7F501, 0x10,0x00, 0x0, +0 }, + { 0x029D521,0x006B332, 0x4F,0x00, 0xA, +0 }, + { 0x2035170,0x267B420, 0x1C,0x00, 0xE, +0 }, + { 0x21152F0,0x1FE91F1, 0xD0,0x40, 0x0, +0 }, + { 0x11152B0,0x1FE71B1, 0xC5,0x80, 0x0, +0 }, + { 0x01152B1,0x1CF80B1, 0xC5,0x84, 0x8, +0 }, + { 0x01171B1,0x1156261, 0x8B,0x40, 0x6, +0 }, + { 0x0F9F131,0x0D5F531, 0x9C,0x80, 0xE, +0 }, + { 0x123B391,0x106F761, 0x4F,0x40, 0x6, +0 }, + { 0x005F010,0x004D010, 0x25,0x80, 0xE, +0 }, + { 0x2005130,0x2656420, 0x1C,0x00, 0xE, +0 }, + { 0x1037531,0x1445462, 0x1C,0x02, 0xE, +0 }, + { 0x081B021,0x12CD323, 0x16,0x00, 0xC, +0 }, + { 0x10872E1,0x02BFAE2, 0xC0,0x89, 0x0, +0 }, + { 0x1C2F071,0x0F2F2C1, 0x46,0x00, 0x4, +0 }, + { 0x173F141,0x174F242, 0x4F,0x03, 0x6, +0 }, + { 0x0059100,0x3068200, 0x0F,0x00, 0x0, +0 }, + { 0x00B4131,0x03BC262, 0x1C,0x80, 0xE, +0 }, + { 0x01F41B1,0x03BB261, 0x1C,0x80, 0xE, +0 }, + { 0x0655200,0x076A321, 0x1D,0x00, 0xE, +0 }, + { 0x08C4321,0x12FA522, 0x19,0x80, 0xC, +0 }, + { 0x05A5321,0x11ABA21, 0x9F,0x80, 0xC, +0 }, + { 0x1AE91E1,0x09EA1E1, 0x55,0x0A, 0xE, +0 }, + { 0x029BB21,0x00AB061, 0x8E,0x80, 0x8, +0 }, + { 0x0AE71E1,0x19EA1E1, 0x16,0x06, 0xA, +0 }, + { 0x2AE71E0,0x19EA1E2, 0x23,0x00, 0xA, +0 }, + { 0x0537101,0x07C9212, 0x4F,0x00, 0xA, +0 }, + { 0x0687120,0x05E9232, 0x4E,0x00, 0xA, +0 }, + { 0x05B7110,0x07B9250, 0x4F,0x00, 0xE, +0 }, + { 0x009F021,0x10AC024, 0x96,0x00, 0xA, +0 }, + { 0x0176EB1,0x10EDBA2, 0xC5,0x00, 0x2, +0 }, + { 0x019D531,0x00A9173, 0x4D,0x00, 0x8, +0 }, + { 0x01B5132,0x03BB261, 0x9A,0x02, 0xC, +0 }, + { 0x0160020,0x015B022, 0x5B,0x00, 0xA, +0 }, + { 0x0177421,0x117A5A1, 0x83,0x40, 0x7, +0 }, + { 0x18F7EE2,0x02A8661, 0xDB,0x00, 0xE, +0 }, + { 0x0160020,0x01560E1, 0x5B,0x40, 0xA, +0 }, + { 0x1063F54,0x0077E01, 0x85,0x00, 0x6, +0 }, + { 0x08F6EE0,0x02AA661, 0xEC,0x00, 0xE, +0 }, + { 0x0C70CF4,0x0A580F3, 0x9A,0x40, 0xD, +0 }, + { 0x0537102,0x07C7211, 0x4F,0x00, 0xA, +0 }, + { 0x007F803,0x074B201, 0x08,0x00, 0x8, +0 }, + { 0x14FF661,0x00FA661, 0x0B,0x00, 0x2, +0 }, + { 0x0086882,0x008C7F1, 0x90,0x00, 0x4, +0 }, + { 0x0F55551,0x1E65602, 0x80,0x00, 0x8, +0 }, + { 0x0339661,0x02B6522, 0x00,0x00, 0x6, +0 }, + { 0x303F660,0x016F621, 0x07,0x00, 0x4, +0 }, + { 0x0E1B311,0x0E4A101, 0x85,0x00, 0xA, +0 }, + { 0x1E9F251,0x0B6F272, 0x41,0x00, 0xA, +0 }, + { 0x002A4B3,0x04285F5, 0x87,0x00, 0x6, +0 }, + { 0x19041F1,0x005B2B1, 0xC0,0x00, 0x8, +0 }, + { 0x102FF52,0x104FF01, 0x03,0x01, 0x4, +0 }, + { 0x0AFF5E1,0x20FF4E0, 0xD0,0x00, 0xC, +0 }, + { 0x21133F4,0x32E53F1, 0x02,0x00, 0x3, +0 }, + { 0x0D3B305,0x125F243, 0x40,0x00, 0x2, +0 }, + { 0x3CF7232,0x1EE5111, 0x4D,0x00, 0x2, +0 }, + { 0x0FF1001,0x0FF5011, 0x12,0x00, 0xA, +0 }, + { 0x00FFF7E,0x10F6F61, 0x1A,0x00, 0xE, +0 }, + { 0x01131F1,0x11222F1, 0x41,0x40, 0x2, +0 }, + { 0x203E5B6,0x14245F1, 0x4B,0x00, 0x6, +0 }, + { 0x1005872,0x0022620, 0x18,0x40, 0x6, +0 }, + { 0x202F950,0x001FFC5, 0x90,0x00, 0x4, +0 }, + { 0x00F4D20,0x105FF00, 0x03,0x00, 0x2, +0 }, + { 0x0427F35,0x02135A2, 0xD7,0x00, 0xE, +0 }, + { 0x303F17C,0x001F130, 0x40,0x00, 0x6, +0 }, + { 0x053F101,0x053F128, 0x40,0x80, 0x0, +0 }, + { 0x011A131,0x0438D13, 0x87,0x80, 0x8, +0 }, + { 0x053F101,0x074F237, 0x4F,0x00, 0x6, +0 }, + { 0x01FF201,0x188F521, 0x0B,0x00, 0x0, +0 }, + { 0x055F502,0x053F621, 0x99,0x00, 0x0, +0 }, + { 0x1176E31,0x10CABA1, 0x43,0x00, 0x2, +0 }, + { 0x2035530,0x1448461, 0x19,0x00, 0xA, +0 }, + { 0x0427881,0x0558593, 0x4B,0x00, 0xE, +0 }, + { 0x272F107,0x104FC18, 0x46,0x00, 0x0, +0 }, + { 0x0E6F80E,0x0F6F80F, 0x00,0x00, 0x0, +0 }, + { 0x1078F03,0x1059F02, 0xC0,0x00, 0xA, +0 }, + { 0x097C802,0x097F802, 0x00,0x00, 0x1, +0 }, + { 0x007FF01,0x107FF00, 0x00,0x00, 0x5, +0 }, + { 0x196C801,0x086F800, 0x00,0x00, 0xA, +0 }, + { 0x0B3F109,0x0B4F600, 0x00,0x00, 0xE, +0 }, + { 0x00B5F01,0x30F5F00, 0x80,0x00, 0x6, +0 }, + { 0x2056651,0x2066642, 0x00,0x00, 0x2, +0 }, + { 0x3665F54,0x0077F40, 0x0A,0x00, 0x4, +0 }, + { 0x005F1C0,0x02394FB, 0x51,0x00, 0x2, +0 }, + { 0x10FFFFC,0x30FFFF0, 0xC0,0x00, 0x0, +0 }, + { 0x00FFF7E,0x00F5F6E, 0x00,0x00, 0xE, +0 }, + { 0x0F0A00A,0x075C586, 0x00,0x00, 0xE, +0 }, + { 0x050F101,0x0D6D101, 0x4E,0x06, 0xA, +0 }, + { 0x054F231,0x0C6F201, 0x48,0x00, 0x8, +0 }, + { 0x023F503,0x0E7D101, 0x47,0x06, 0xA, +0 }, + { 0x000F113,0x0F6D194, 0x54,0x00, 0x4, +0 }, + { 0x15BF80C,0x0CCD201, 0x71,0x03, 0x0, +0 }, + { 0x0DAF101,0x0E9F301, 0x93,0x00, 0x0, +0 }, + { 0x128FB23,0x0E8D301, 0x87,0x40, 0x6, +0 }, + { 0x0A5C201,0x0D7C201, 0x92,0x00, 0xA, +0 }, + { 0x040FF36,0x0F4F311, 0xC0,0x80, 0x4, +0 }, + { 0x055F587,0x0C4F411, 0x91,0x00, 0x2, +0 }, + { 0x0045616,0x034F601, 0x21,0x00, 0x2, +0 }, + { 0x0E6F318,0x0F6F281, 0xD0,0x00, 0x0, +0 }, + { 0x0FFF718,0x0D8B501, 0x21,0x00, 0x0, +0 }, + { 0x0FFF816,0x0F6F601, 0x98,0x00, 0x0, +0 }, + { 0x032FD13,0x042FD00, 0x86,0x03, 0x8, +0 }, + { 0x1058401,0x0C5F481, 0x49,0x82, 0x0, +0 }, + { 0x2E5F062,0x00EC060, 0x5D,0x00, 0x0, +0 }, + { 0x0FFF062,0x00FCF60, 0xCF,0x00, 0x0, +0 }, + { 0x00FAA30,0x10FFF71, 0x57,0x00, 0x0, +0 }, + { 0x14BF02C,0x01B5071, 0x55,0x08, 0x0, +0 }, + { 0x1059721,0x0054F31, 0x13,0x80, 0x0, +0 }, + { 0x1058721,0x0054F32, 0x8F,0x00, 0x6, +0 }, + { 0x006F223,0x00642B1, 0x53,0x0B, 0xE, +0 }, + { 0x1058721,0x0054F31, 0x50,0x00, 0x0, +0 }, + { 0x2F4F502,0x0F8F301, 0x64,0x00, 0xA, +0 }, + { 0x1FAF303,0x0F7C301, 0x57,0x00, 0xE, +0 }, + { 0x0F0F003,0x0F8F301, 0xD5,0x00, 0x2, +0 }, + { 0x120F723,0x0F7F401, 0x86,0x40, 0x8, +0 }, + { 0x043F903,0x0FAF421, 0xC0,0x00, 0x6, +0 }, + { 0x0FFF101,0x3FFF054, 0x43,0x40, 0x8, +0 }, + { 0x353F100,0x396F110, 0x49,0x00, 0xC, +0 }, + { 0x15FF510,0x1FFF134, 0x40,0x00, 0x0, +0 }, + { 0x0F2D401,0x08AC321, 0x18,0x80, 0xA, +0 }, + { 0x02FF131,0x086F131, 0x8F,0x00, 0xA, +0 }, + { 0x02FF131,0x086F131, 0x8C,0x00, 0xA, +0 }, + { 0x04CB421,0x0FC8201, 0x15,0x00, 0xA, +0 }, + { 0x016F701,0x088F321, 0x8E,0x00, 0xC, +0 }, + { 0x016FD01,0x088F321, 0x0D,0x00, 0xC, +0 }, + { 0x016F501,0x088F321, 0x8C,0x00, 0xA, +0 }, + { 0x004F311,0x06DF231, 0x06,0x00, 0x8, +0 }, + { 0x1035171,0x0155221, 0x1C,0x00, 0xE, +0 }, + { 0x113FF31,0x0366661, 0x16,0x00, 0x8, +0 }, + { 0x0035171,0x0175461, 0x56,0x00, 0xE, +0 }, + { 0x0035171,0x0476421, 0x1D,0x00, 0xE, +0 }, + { 0x121F131,0x0166FE1, 0x46,0x00, 0x2, +0 }, + { 0x0FFF611,0x0F37211, 0x05,0x00, 0x0, +0 }, + { 0x075F002,0x053F701, 0x1D,0x00, 0x0, +0 }, + { 0x1057510,0x0F3F311, 0x41,0x00, 0x0, +0 }, + { 0x1035131,0x0153061, 0x1C,0x00, 0xE, +0 }, + { 0x014C121,0x0054161, 0x93,0x00, 0xA, +0 }, + { 0x0223101,0x0159041, 0x18,0x00, 0xC, +0 }, + { 0x01FF421,0x0073F72, 0xDB,0x07, 0x0, +0 }, + { 0x0697961,0x0677121, 0x96,0x00, 0x0, +0 }, + { 0x069E961,0x0677121, 0x96,0x00, 0x0, +0 }, + { 0x0665410,0x045A581, 0x04,0x00, 0x8, +0 }, + { 0x0076431,0x067D021, 0x1E,0x00, 0xE, +0 }, + { 0x0177521,0x0078F21, 0x94,0x80, 0xC, +0 }, + { 0x0586321,0x018A021, 0x19,0x00, 0xC, +0 }, + { 0x00F7321,0x00F9321, 0x16,0x00, 0xC, +0 }, + { 0x0565321,0x016A021, 0x9A,0x80, 0xE, +0 }, + { 0x0076431,0x067D021, 0x18,0x00, 0xE, +0 }, + { 0x03E4131,0x09EF022, 0x16,0x00, 0xE, +0 }, + { 0x0566121,0x016A021, 0x99,0x80, 0xE, +0 }, + { 0x100FF31,0x0087F61, 0x94,0x00, 0x8, +0 }, + { 0x1009831,0x0096F61, 0x8E,0x00, 0x8, +0 }, + { 0x1055E31,0x0087F61, 0x8D,0x00, 0xA, +0 }, + { 0x0178731,0x00E8BA2, 0xC1,0x00, 0xC, +0 }, + { 0x0178731,0x10E8BA2, 0xC3,0x00, 0xC, +0 }, + { 0x017FE71,0x00A6B22, 0x0D,0x00, 0x8, +0 }, + { 0x0219F32,0x0F770B1, 0x48,0x00, 0x4, +0 }, + { 0x03794A1,0x00A6521, 0x1F,0x00, 0x0, +0 }, + { 0x05F7621,0x02A60A1, 0x19,0x80, 0x6, +0 }, + { 0x0195131,0x0396021, 0x9A,0x80, 0xC, +0 }, + { 0x05084B2,0x0186721, 0x8D,0x00, 0x0, +0 }, + { 0x00457E2,0x0876861, 0x52,0x00, 0x8, +0 }, + { 0x1032171,0x0175461, 0x96,0x00, 0x4, +0 }, + { 0x0031171,0x0175461, 0xD6,0x00, 0x4, +0 }, + { 0x00FF032,0x0077621, 0xF4,0x00, 0x0, +0 }, + { 0x203F422,0x00CF061, 0xA1,0x00, 0x0, +0 }, + { 0x10FFC21,0x10FF9A1, 0x0E,0x00, 0x0, +0 }, + { 0x0558721,0x0186421, 0x42,0x80, 0x0, +0 }, + { 0x0126621,0x0099021, 0x45,0x00, 0x6, +0 }, + { 0x121A221,0x02A91A2, 0x8E,0x00, 0xA, +0 }, + { 0x069E962,0x0677121, 0xAA,0x00, 0x0, +0 }, + { 0x0104100,0x206F760, 0xC4,0x00, 0x0, +0 }, + { 0x030F201,0x009F461, 0x8F,0x80, 0xA, +0 }, + { 0x0F45217,0x005A0A1, 0xA7,0x00, 0xE, +0 }, + { 0x011E861,0x00327B1, 0x1F,0x80, 0xA, +0 }, + { 0x02C6161,0x018F521, 0x16,0x00, 0xC, +0 }, + { 0x001EF71,0x0036172, 0x60,0x00, 0x0, +0 }, + { 0x0935136,0x0714331, 0xC4,0x80, 0x6, +0 }, + { 0x175A1C1,0x1752101, 0x51,0x00, 0x0, +0 }, + { 0x010F4A1,0x0033F32, 0xDB,0x07, 0x0, +0 }, + { 0x1181121,0x007CFA1, 0x15,0x00, 0x0, +0 }, + { 0x0F1B061,0x0F2F1B1, 0x1F,0x00, 0xA, +0 }, + { 0x1051201,0x0144121, 0x15,0x00, 0x0, +0 }, + { 0x0156215,0x004AD81, 0x58,0x00, 0x2, +0 }, + { 0x056F523,0x025F3A1, 0x48,0x80, 0x0, +0 }, + { 0x151F261,0x0A5F242, 0x4D,0x00, 0x0, +0 }, + { 0x1511261,0x0131123, 0x09,0x80, 0xC, +0 }, + { 0x0F11141,0x0031DA1, 0x8E,0x06, 0xA, +0 }, + { 0x15AB061,0x01AB0A3, 0x94,0x80, 0x0, +0 }, + { 0x083F101,0x085F108, 0x40,0x40, 0x0, +0 }, + { 0x1119311,0x0C5A213, 0x0C,0x09, 0x0, +0 }, + { 0x1429811,0x0D7F311, 0x06,0x00, 0x4, +0 }, + { 0x0328513,0x112E591, 0x91,0x00, 0x8, +0 }, + { 0x2569D04,0x005F201, 0x8F,0x80, 0xE, +0 }, + { 0x1206721,0x10C6F22, 0x41,0x00, 0x6, +0 }, + { 0x0206721,0x10C6F22, 0x41,0x00, 0x6, +0 }, + { 0x1178731,0x00E8B22, 0x48,0x80, 0xC, +0 }, + { 0x0E5F105,0x0E5C302, 0xD8,0x80, 0x6, +0 }, + { 0x026EC07,0x087F702, 0x0A,0x00, 0xE, +0 }, + { 0x0155805,0x005EF01, 0x9D,0x00, 0xE, +0 }, + { 0x018FA17,0x054F812, 0x18,0x00, 0x8, +0 }, + { 0x0F3E900,0x005FF00, 0x11,0x00, 0x0, +0 }, + { 0x147F811,0x003F310, 0x01,0x80, 0x4, +0 }, + { 0x0696940,0x0657300, 0x96,0x00, 0x4, +0 }, + { 0x0F0F00C,0x0DF270C, 0x00,0x00, 0xE, +0 }, + { 0x024F806,0x2D65602, 0x80,0x8D, 0xE, +0 }, + { 0x07DF011,0x0865611, 0x0A,0x89, 0xE, +0 }, + { 0x0FFF00B,0x2FF120C, 0x00,0x00, 0xE, +0 }, + { 0x05BE51C,0x0FA5D0C, 0x1E,0x00, 0xE, +0 }, + { 0x0FFD02C,0x0FFF020, 0x40,0x00, 0xE, +0 }, + { 0x200F600,0x2FF4FD0, 0x00,0x00, 0xE, +0 }, + { 0x001FF6C,0x016126C, 0x00,0x40, 0xE, +0 }, + { 0x0FFF30C,0x1DFF60C, 0x00,0x00, 0xE, +0 }, + { 0x0F00020,0x0F00000, 0x3F,0x3F, 0xD, +0 }, + { 0x0C8AA00,0x0B7D200, 0x00,0x00, 0x0, +0 }, + { 0x22BFB03,0x00BF507, 0x00,0x00, 0xF, +0 }, + { 0x3FFFFF0,0x0F0FBE5, 0xC0,0x00, 0xE, +0 }, + { 0x00AFF21,0x119F800, 0x80,0x00, 0xE, +0 }, + { 0x098C601,0x098C601, 0x08,0x08, 0x5, +0 }, + { 0x098C601,0x098C601, 0x00,0x08, 0x5, +0 }, + { 0x342F80E,0x3E4F407, 0x00,0x40, 0xE, +0 }, + { 0x342F80F,0x3E4F407, 0x00,0x40, 0xE, +0 }, + { 0x342F804,0x3E4F407, 0x00,0x44, 0xE, +0 }, + { 0x342F80F,0x3E4F40D, 0x00,0x40, 0xE, +0 }, + { 0x342F80F,0x3E4F408, 0x00,0x40, 0xE, +0 }, + { 0x200F880,0x3049F90, 0x0D,0x00, 0xE, +0 }, + { 0x08DFA01,0x0B5F801, 0x4F,0x00, 0x7, +0 }, + { 0x30AF901,0x006FA00, 0x00,0x00, 0xF, +0 }, + { 0x0EFF702,0x397C802, 0x00,0x00, 0xB, +0 }, + { 0x0EFF702,0x397C802, 0x00,0x40, 0xB, +0 }, + { 0x276F502,0x2D6F609, 0x1B,0x05, 0x4, +0 }, + { 0x05BE51C,0x0FA7D07, 0x16,0x00, 0xE, +0 }, + { 0x0FEE51C,0x0067D07, 0x16,0x00, 0xE, +0 }, + { 0x0F40006,0x005F713, 0x3F,0x00, 0x1, +0 }, + { 0x0F40006,0x005F712, 0x3F,0x00, 0x1, +0 }, + { 0x3F0E02A,0x005FF1E, 0x40,0x40, 0x8, +0 }, + { 0x3F0E02A,0x002FF1E, 0x7C,0x40, 0x8, +0 }, + { 0x053F171,0x227F272, 0x48,0x00, 0xA, +0 }, + { 0x121F1B1,0x0166F61, 0x46,0x00, 0x2, +0 }, + { 0x03EC131,0x09EF022, 0x1B,0x00, 0xE, +0 }, + { 0x203F4A2,0x00CF0F1, 0xA1,0x00, 0x0, +0 }, + { 0x008C782,0x00857F1, 0x0D,0x00, 0x0, +0 }, + { 0x053F101,0x074F217, 0x4F,0x00, 0x6, +0 }, + { 0x0328513,0x112E591, 0x90,0x00, 0x8, +0 }, + { 0x2569D04,0x005F201, 0xCF,0x80, 0xE, +0 }, + { 0x0F2EB20,0x005FF10, 0x08,0x00, 0x0, +0 }, + { 0x0F0F029,0x1DF2703, 0x00,0x00, 0xE, +0 }, + { 0x0F6A90F,0x2F6F90F, 0x02,0xC0, 0x0, +0 }, + { 0x098F601,0x008CB00, 0x00,0x00, 0x5, +0 }, + { 0x306FF80,0x0176F11, 0x00,0x00, 0xE, +0 }, + { 0x306FF80,0x0166F11, 0x00,0x00, 0xE, +0 }, + { 0x0F00006,0x0FFF816, 0x3F,0x00, 0x1, +0 }, + { 0x0FF0006,0x0FFF815, 0x3F,0x00, 0x1, +0 }, + { 0x094F3C1,0x0C8E3C1, 0x8C,0x40, 0xC, +12 }, + { 0x1E4E130,0x0E3F230, 0x8D,0x00, 0xA, +12 }, + { 0x21FF120,0x088F420, 0x21,0x00, 0xA, +12 }, + { 0x100A010,0x0F6B110, 0x15,0x00, 0x8, +12 }, + { 0x054A1E0,0x0049160, 0x4B,0x40, 0x0, +12 }, + { 0x1059020,0x10575A1, 0x51,0x80, 0x4, +12 }, + { 0x10580A0,0x1056521, 0x52,0x80, 0x6, +12 }, + { 0x10569A0,0x10266E0, 0x93,0x00, 0xA, +12 }, + { 0x0033221,0x1042120, 0x4D,0x80, 0x0, +12 }, + { 0x054A160,0x0049160, 0x4D,0x80, 0x0, +12 }, + { 0x10BA8A1,0x128D330, 0x48,0x00, 0xA, +12 }, + { 0x0C8A820,0x0B7D601, 0x00,0x00, 0x0, +0 }, + { 0x117F7CE,0x04CF9C0, 0x21,0x00, 0xF, +12 }, + { 0x075FC01,0x037F800, 0x21,0x00, 0x1, +12 }, + { 0x25E980C,0x306FB0F, 0x80,0x80, 0xF, +12 }, + { 0x0F5F201,0x0F6F201, 0x8F,0x06, 0x8, +0 }, + { 0x0F5F201,0x0F6F201, 0x4B,0x00, 0x8, +0 }, + { 0x0F5F201,0x0F5F201, 0x49,0x00, 0x8, +0 }, + { 0x0F6F2C1,0x0F6F241, 0x12,0x00, 0x6, +0 }, + { 0x0F8F181,0x0F7F201, 0x57,0x00, 0x0, +0 }, + { 0x0F7F101,0x0F6F201, 0x93,0x00, 0x0, +0 }, + { 0x0F5F60C,0x0F5F381, 0x5C,0x00, 0x0, +0 }, + { 0x0F5F3D8,0x0F5F281, 0x62,0x00, 0x0, +0 }, + { 0x014F6B1,0x004F1F1, 0x92,0x00, 0x2, +0 }, + { 0x05FC772,0x004C730, 0x14,0x00, 0x2, +0 }, + { 0x016AA70,0x0048AB1, 0x44,0x00, 0x4, +0 }, + { 0x1259723,0x01355B1, 0x93,0x00, 0x4, +0 }, + { 0x1299824,0x01646B1, 0x48,0x00, 0xC, +0 }, + { 0x1069121,0x0066161, 0x13,0x00, 0xA, +0 }, + { 0x0067121,0x00661E1, 0x13,0x89, 0x6, +0 }, + { 0x197F302,0x0C6F341, 0x9C,0x80, 0xC, +0 }, + { 0x198F303,0x0E5F111, 0x54,0x00, 0xC, +0 }, + { 0x03EF123,0x0F7F221, 0x5F,0x00, 0x0, +0 }, + { 0x127F623,0x0F7F321, 0x87,0x80, 0x6, +0 }, + { 0x054F903,0x03FF621, 0x47,0x00, 0x0, +0 }, + { 0x1479163,0x0178421, 0x4A,0x05, 0x8, +0 }, + { 0x1189563,0x0179461, 0x4A,0x00, 0x8, +0 }, + { 0x0482029,0x0F7D1A4, 0xA1,0x80, 0x8, +0 }, + { 0x077F131,0x005F771, 0x13,0x00, 0xA, +0 }, + { 0x0E7F171,0x075F171, 0x8D,0x00, 0xA, +0 }, + { 0x0276131,0x0157172, 0x5B,0x00, 0xC, +0 }, + { 0x096A101,0x0D6F261, 0x8B,0x40, 0x8, +0 }, + { 0x016A261,0x0D6A121, 0x8A,0x08, 0x8, +0 }, + { 0x0E5F431,0x075F131, 0x8B,0x00, 0xA, +0 }, + { 0x057F271,0x007E122, 0x0F,0x00, 0x6, +0 }, + { 0x114DD31,0x0265621, 0x15,0x00, 0x8, +0 }, + { 0x113DD31,0x00666E1, 0x16,0x00, 0x8, +0 }, + { 0x116D171,0x0066131, 0x49,0x00, 0x8, +0 }, + { 0x11471A1,0x0057263, 0x4D,0x80, 0x2, +0 }, + { 0x124F1F1,0x0156FE1, 0x40,0x00, 0x2, +0 }, + { 0x176F502,0x0358501, 0x1A,0x80, 0x0, +0 }, + { 0x175F422,0x0F3F301, 0x1D,0x80, 0x0, +0 }, + { 0x126B121,0x00572A2, 0x9B,0x01, 0xE, +0 }, + { 0x1037FA1,0x1053F21, 0x98,0x00, 0x0, +0 }, + { 0x03441A1,0x0035161, 0x93,0x00, 0xA, +0 }, + { 0x025C121,0x0054F61, 0x18,0x00, 0xC, +0 }, + { 0x013F431,0x0038A72, 0x5B,0x83, 0x0, +0 }, + { 0x03974A1,0x0667161, 0x90,0x00, 0x0, +0 }, + { 0x08662E1,0x0057A72, 0x57,0x00, 0xC, +0 }, + { 0x0188561,0x0088F61, 0x92,0x01, 0xC, +0 }, + { 0x01775A1,0x0078F21, 0x94,0x05, 0xC, +0 }, + { 0x0157621,0x0368261, 0x94,0x00, 0xC, +0 }, + { 0x1189E31,0x1286221, 0x43,0x00, 0x2, +0 }, + { 0x0676121,0x0067F21, 0x9B,0x00, 0x2, +0 }, + { 0x0187561,0x00874A2, 0x8A,0x06, 0x8, +0 }, + { 0x15772A1,0x0177161, 0x86,0x83, 0x0, +0 }, + { 0x0375421,0x016A621, 0x4D,0x00, 0x8, +0 }, + { 0x1079331,0x0077261, 0x8F,0x00, 0x8, +0 }, + { 0x1079331,0x0077261, 0x8E,0x00, 0x8, +0 }, + { 0x1079331,0x0078261, 0x91,0x00, 0xA, +0 }, + { 0x118AA61,0x0088F21, 0x4B,0x00, 0x8, +0 }, + { 0x1167E31,0x1078B21, 0x90,0x00, 0x6, +0 }, + { 0x0289B32,0x0187221, 0x90,0x00, 0x4, +0 }, + { 0x05C85E1,0x01765E1, 0x1F,0x00, 0x0, +0 }, + { 0x05C88E1,0x01765E1, 0x46,0x00, 0x0, +0 }, + { 0x01F75A1,0x0077521, 0x9C,0x00, 0x2, +0 }, + { 0x2FCF122,0x006FF61, 0x51,0x00, 0x0, +0 }, + { 0x1FCF121,0x207FF21, 0x0E,0x00, 0x0, +0 }, + { 0x0588622,0x01664E1, 0x46,0x80, 0x0, +0 }, + { 0x17A9221,0x02A9122, 0x8B,0x00, 0x0, +0 }, + { 0x005DFA2,0x0056F61, 0x9E,0x40, 0x2, +0 }, + { 0x0A8F121,0x007F461, 0x8F,0x80, 0xA, +0 }, + { 0x0935337,0x005A0E1, 0xA5,0x00, 0x2, +0 }, + { 0x0759121,0x0155561, 0x17,0x00, 0xC, +0 }, + { 0x0025471,0x0036A72, 0x5D,0x00, 0x0, +0 }, + { 0x0432161,0x03542A2, 0x97,0x00, 0x8, +0 }, + { 0x173A161,0x1433161, 0x1C,0x00, 0x0, +0 }, + { 0x0341121,0x0244261, 0x89,0x03, 0xA, +0 }, + { 0x14711A1,0x007CF61, 0x15,0x00, 0x0, +0 }, + { 0x085B122,0x025F261, 0x92,0x83, 0xC, +0 }, + { 0x155F101,0x0F4F242, 0x4D,0x00, 0x0, +0 }, + { 0x1511161,0x01321A3, 0x94,0x80, 0x6, +0 }, + { 0x0311161,0x0035DA1, 0x8C,0x80, 0x6, +0 }, + { 0x1F1A131,0x0F4A233, 0x0C,0x80, 0x0, +0 }, + { 0x0277C21,0x1076F22, 0x49,0x00, 0x6, +0 }, + { 0x134DD31,0x0165621, 0x85,0x00, 0xA, +0 }, + { 0x207DA20,0x0078F21, 0x04,0x81, 0x6, +0 }, + { 0x0E5F105,0x0E3C303, 0x6A,0x80, 0x6, +0 }, + { 0x0A337D5,0x03756FA, 0x95,0x40, 0x0, +0 }, + { 0x261B2B5,0x0A5F4B4, 0x5C,0x08, 0xA, +0 }, + { 0x001F6EE,0x3A54FF0, 0x00,0x00, 0xE, +0 }, + { 0x0F0F300,0x2C6F600, 0x00,0x00, 0xE, +0 }, + { 0x060F213,0x072F201, 0x4F,0x10, 0x8, +0 }, + { 0x0FFF001,0x00F9031, 0x4F,0x04, 0x6, +0 }, + { 0x021FF13,0x0F6F311, 0x96,0x80, 0xA, +0 }, + { 0x001FF11,0x0F5F311, 0x8D,0x80, 0x0, +0 }, + { 0x171F503,0x0F6F211, 0x5E,0x00, 0xE, +0 }, + { 0x031F121,0x0F8F406, 0x40,0x85, 0x0, +0 }, + { 0x09F8331,0x078F422, 0x10,0x04, 0xA, +0 }, + { 0x024D501,0x0258531, 0x0F,0x00, 0xA, +0 }, + { 0x0A7F131,0x0C6F731, 0x5A,0x80, 0xE, +0 }, + { 0x08B7261,0x01950E1, 0xA7,0x81, 0x2, +0 }, + { 0x0089782,0x00897F1, 0x0D,0x00, 0x0, +0 }, + { 0x0E1A311,0x0E4A103, 0x80,0x80, 0x6, +0 }, + { 0x003FF41,0x0F4F123, 0x23,0x00, 0x8, +0 }, + { 0x007FF01,0x007FF01, 0x00,0x00, 0x7, +0 }, + { 0x096D801,0x096D801, 0x00,0x00, 0xA, +0 }, + { 0x277C005,0x0EDB900, 0x00,0x00, 0xC, +0 }, + { 0x204FD36,0x0F8F809, 0x00,0x00, 0xC, +0 }, + { 0x0530900,0x094F700, 0x40,0x00, 0x0, +0 }, + { 0x077F005,0x0EBFA00, 0x00,0x00, 0xE, +0 }, + { 0x077F005,0x0E58A00, 0x00,0x00, 0xE, +0 }, + { 0x073F005,0x0A3FA00, 0x00,0x00, 0xE, +0 }, + { 0x042F80E,0x3E4F407, 0x08,0x51, 0xE, +0 }, + { 0x37CFD23,0x0F58401, 0x15,0x08, 0x0, +0 }, + { 0x035F203,0x4F5F401, 0x5D,0x08, 0x1, +0 }, + { 0x0F5F303,0x4F5F301, 0x7D,0x08, 0x0, +0 }, + { 0x0F5F203,0x4F5F201, 0x55,0x08, 0x1, +0 }, + { 0x0F5B111,0x0D5F211, 0x1B,0x80, 0x1, +0 }, + { 0x005F276,0x006F27A, 0x25,0x29, 0xE, +12 }, + { 0x0F2F100,0x0F7F200, 0x24,0x00, 0xE, +0 }, + { 0x185DC85,0x055F401, 0x91,0x0E, 0x3, +0 }, + { 0x0F6E181,0x0F6E798, 0x00,0x2B, 0x1, +0 }, + { 0x0F4F194,0x0A7E98A, 0x00,0x15, 0x1, +0 }, + { 0x0B3D407,0x0B4C202, 0xA0,0x00, 0xA, -12 }, + { 0x082D307,0x0E3A302, 0x58,0x80, 0xB, +0 }, + { 0x156940A,0x132F411, 0xA7,0x05, 0x5, +0 }, + { 0x027A2A0,0x023A522, 0x85,0x9E, 0x7, +0 }, + { 0x02AA5A2,0x02AA168, 0x80,0x8F, 0x7, +0 }, + { 0x02AA623,0x00AAF61, 0x51,0x80, 0x8, -12 }, + { 0x00AAF61,0x00AAF22, 0x91,0x80, 0x9, +0 }, + { 0x1239723,0x01455B1, 0x93,0x00, 0x4, +12 }, + { 0x1069FB2,0x10B55B0, 0x09,0x22, 0x5, +0 }, + { 0x066752A,0x067702A, 0x26,0x2B, 0xE, +0 }, + { 0x013C321,0x00B7022, 0x22,0x00, 0xE, +0 }, + { 0x0F4F505,0x0F9F200, 0x29,0x1E, 0x6, +12 }, + { 0x0F1F101,0x0F7F100, 0x2F,0x00, 0x6, +0 }, + { 0x0F4F405,0x0F6F100, 0x20,0x19, 0x6, +12 }, + { 0x19F53C8,0x07FFAE4, 0x1C,0x03, 0x9, +0 }, + { 0x0049420,0x0A5C523, 0x2A,0x24, 0xE, +12 }, + { 0x0F9F200,0x0F8F101, 0x21,0x00, 0xE, +0 }, + { 0x02BF82A,0x02BF620, 0x24,0x2D, 0xE, +12 }, + { 0x02BF420,0x02BF420, 0x12,0x00, 0xE, +0 }, + { 0x0ABF82A,0x02BF620, 0x23,0x32, 0xE, +12 }, + { 0x0285130,0x0487130, 0x5B,0x00, 0x0, +12 }, + { 0x0487130,0x048A130, 0x1E,0x00, 0x1, +0 }, + { 0x0F7F52F,0x1C7F523, 0x14,0x33, 0x8, +12 }, + { 0x097F320,0x0F8F121, 0x20,0x00, 0x8, +0 }, + { 0x0F7F52F,0x1C7F523, 0x1B,0x30, 0x8, +12 }, + { 0x0E8F431,0x078F131, 0x15,0x00, 0xE, +0 }, + { 0x0E8F501,0x078F101, 0x15,0x00, 0xF, +0 }, + { 0x0CFF416,0x0E6F405, 0x23,0x69, 0xC, +12 }, + { 0x0D6F200,0x0E6E201, 0x14,0x00, 0xC, +0 }, + { 0x2036130,0x20434A0, 0x98,0x0B, 0xE, +0 }, + { 0x1156561,0x0073521, 0x92,0x01, 0xF, +0 }, + { 0x012D121,0x0054F61, 0x1A,0x00, 0xC, +0 }, + { 0x016C1A1,0x0044F21, 0x93,0x00, 0xD, +0 }, + { 0x0049100,0x2045240, 0x0F,0x00, 0x9, +0 }, + { 0x0157620,0x0368261, 0x94,0x00, 0xC, +12 }, + { 0x02661B1,0x0276171, 0xD3,0x80, 0xD, +0 }, + { 0x04A6121,0x00B7F21, 0x9F,0x00, 0xE, +0 }, + { 0x00A65A1,0x0067F61, 0xA2,0x00, 0xF, +0 }, + { 0x0277221,0x0067F21, 0x16,0x05, 0xC, +0 }, + { 0x0866131,0x0D6C261, 0x1A,0x00, 0xE, +0 }, + { 0x0678221,0x0179222, 0x17,0x00, 0xE, +0 }, + { 0x00AD961,0x006A861, 0x28,0x1E, 0xE, +0 }, + { 0x0069A21,0x00ACF24, 0x25,0x00, 0xE, +0 }, + { 0x02A9B32,0x0177221, 0x90,0x00, 0x4, +0 }, + { 0x01CB632,0x01B66E1, 0x92,0x82, 0x5, +0 }, + { 0x00FFF61,0x00FFF22, 0x1C,0x00, 0xE, +0 }, + { 0x00FFF21,0x009CF62, 0x1C,0x00, 0xF, +0 }, + { 0x0559622,0x0187421, 0x46,0x80, 0xF, +0 }, + { 0x09041F1,0x00322B1, 0xCB,0x07, 0xB, +0 }, + { 0x0022A55,0x0F34212, 0x97,0x86, 0x7, +0 }, + { 0x1D38201,0x04442E1, 0x40,0x0D, 0x1, +0 }, + { 0x2164460,0x00450E1, 0xAB,0x01, 0xB, +0 }, + { 0x0022A55,0x0F34212, 0x97,0x86, 0x1, +0 }, + { 0x1623524,0x1023171, 0x20,0x05, 0x1, +0 }, + { 0x011A131,0x0137D16, 0x87,0x08, 0x1, +0 }, + { 0x0F0A101,0x0437516, 0x0C,0x03, 0x1, +0 }, + { 0x053F201,0x052F317, 0x8F,0x09, 0x5, +0 }, + { 0x055C902,0x024A601, 0x1A,0x05, 0xD, +0 }, + { 0x0175E31,0x20C7B21, 0x18,0x08, 0x7, +0 }, + { 0x119FFA1,0x0089024, 0x0C,0x11, 0x7, +0 }, + { 0x004F007,0x004F081, 0x51,0x13, 0x7, +0 }, + { 0x026EC07,0x016F801, 0x15,0x00, 0xA, +0 }, + { 0x001FF17,0x0057A12, 0x1C,0x0B, 0xB, +0 }, + { 0x09FF831,0x004FF10, 0x8B,0x05, 0x7, +0 }, + { 0x001FF0E,0x20F2F01, 0x00,0x0D, 0xE, +0 }, + { 0x2077405,0x106F403, 0x80,0x0F, 0xF, +0 }, + { 0x003FF15,0x0934511, 0x09,0x1F, 0xF, +0 }, + { 0x000200E,0x0022F0E, 0x00,0x0F, 0xF, +0 }, + { 0x060F209,0x072F214, 0x4F,0x19, 0xB, +0 }, + { 0x1111EF0,0x11311E2, 0x00,0xC5, 0xF, +0 }, + { 0x000FFEE,0x30318EE, 0x00,0x00, 0xE, +0 }, + { 0x0F7B710,0x005F011, 0x42,0x00, 0x8, +0 }, + { 0x6EF8801,0x608B502, 0x0D,0x00, 0x0, +0 }, + { 0x0F1F10F,0x007840F, 0x00,0x08, 0xC, +12 }, + { 0x6EF8800,0x608F502, 0x13,0x00, 0x0, +8 }, + { 0x0F1D101,0x0078400, 0x00,0x00, 0xE, +1 }, + { 0x254F307,0x307F905, 0x04,0x0B, 0x6, -5 }, + { 0x254F307,0x207F905, 0x04,0x0B, 0x8, +0 }, + { 0x25CD808,0x32B8A06, 0x04,0x08, 0xC, +0 }, + { 0x2F2E327,0x3F5C525, 0x04,0x08, 0xA, -5 }, + { 0x2F2F326,0x2F5C525, 0x04,0x08, 0x8, +0 }, + { 0x292F108,0x354F201, 0x00,0x08, 0x8, +12 }, + { 0x283E108,0x334D700, 0x00,0x08, 0x8, +12 }, + { 0x283E109,0x334D500, 0x00,0x08, 0x8, +11 }, + { 0x2E1F119,0x3F3F11B, 0x04,0x08, 0x8, +0 }, + { 0x251F206,0x263C504, 0x04,0x09, 0xA, +0 }, + { 0x241F287,0x353B502, 0x05,0x09, 0xA, +1 }, + { 0x292F108,0x354F201, 0x00,0x03, 0x8, +12 }, + { 0x456FB02,0x017F700, 0x81,0x00, 0x0, +12 }, + { 0x556FA01,0x117F701, 0x00,0x0D, 0x6, +10 }, + { 0x556FB02,0x117F701, 0x81,0x0D, 0x6, +10 }, + { 0x106F680,0x016F610, 0x00,0x00, 0xC, +0 }, + { 0x20F6F00,0x20F6F00, 0x00,0x00, 0x0, +0 }, + { 0x106F680,0x016F610, 0x00,0x00, 0x6, +0 }, + { 0x20F4F00,0x20F4F00, 0x00,0x00, 0x6, +0 }, + { 0x1DC5D01,0x06FF79F, 0x0B,0x00, 0xA, +12 }, + { 0x1C7C900,0x05FF49F, 0x07,0x00, 0xA, +12 }, + { 0x3F0E00A,0x0F7F21F, 0x7C,0x40, 0x8, +0 }, + { 0x3E0F50A,0x0FAF31F, 0x7C,0x40, 0x9, +0 }, + { 0x1F5F213,0x0F5F111, 0xC6,0x0A, 0x0, +0 }, + { 0x019F603,0x0F4F212, 0x30,0x10, 0xF, +0 }, + { 0x1069FB2,0x10F94B0, 0xC0,0x86, 0x9, +0 }, + { 0x1069FB2,0x10F94B0, 0xC0,0x80, 0x9, +0 }, + { 0x1F69182,0x1F69180, 0xC0,0x86, 0x9, +0 }, + { 0x00BF224,0x00B5231, 0x4F,0x1B, 0xE, +0 }, + { 0x021FE13,0x094F231, 0x96,0x80, 0xA, +0 }, + { 0x153F201,0x174F511, 0x4D,0x00, 0x8, +0 }, + { 0x0199421,0x0099428, 0x01,0x09, 0x6, +0 }, + { 0x0199421,0x0099428, 0x01,0x13, 0x6, +0 }, + { 0x06FFA24,0x0F891C2, 0x8A,0x03, 0x8, +0 }, + { 0x0CFF121,0x048F621, 0x1A,0x00, 0xA, +0 }, + { 0x022B701,0x037C422, 0x1D,0x00, 0xE, +0 }, + { 0x0EFF231,0x068F122, 0x1E,0x00, 0xE, +0 }, + { 0x025F911,0x034F131, 0x05,0x09, 0xA, +0 }, + { 0x019D531,0x01B6172, 0x8A,0x00, 0xC, +0 }, + { 0x255A511,0x1B3F511, 0x96,0x80, 0xC, +0 }, + { 0x0058001,0x006F011, 0x9C,0x80, 0x0, +0 }, + { 0x243A5C0,0x123D400, 0x0D,0x00, 0x0, +0 }, + { 0x1E3C221,0x3166120, 0x58,0x00, 0x0, +0 }, + { 0x081B021,0x12CB322, 0x16,0x00, 0xC, +0 }, + { 0x0AE7121,0x09E8121, 0x1D,0x00, 0xE, +0 }, + { 0x00B4131,0x01B92F1, 0x1C,0x00, 0xA, +0 }, + { 0x0AE71E1,0x09E81E2, 0x15,0x03, 0xE, +0 }, + { 0x0537121,0x04C5232, 0x4F,0x00, 0xA, +0 }, + { 0x0687120,0x05E5231, 0x4E,0x00, 0xA, +0 }, + { 0x0219B32,0x0077221, 0xC0,0x00, 0x4, +0 }, + { 0x08F6EE1,0x02A6562, 0xEC,0x00, 0xE, +0 }, + { 0x0C70CF5,0x0A560F2, 0x9A,0x80, 0xD, +0 }, + { 0x203FF22,0x00FFF21, 0x59,0x08, 0x0, +0 }, + { 0x00E7121,0x00E8121, 0x1D,0x00, 0xE, +0 }, + { 0x0FFF100,0x0FF5011, 0x0D,0x80, 0x6, +0 }, + { 0x0EF1100,0x00FB031, 0x10,0x80, 0xA, +0 }, + { 0x173F3A4,0x0238161, 0x4C,0x10, 0x4, +0 }, + { 0x200F601,0x3061FDD, 0x00,0x00, 0xC, +0 }, + { 0x22BFB03,0x00BF50F, 0x00,0x00, 0xF, +0 }, + { 0x07CFA01,0x004F200, 0x00,0x00, 0x0, +0 }, + { 0x000F601,0x3029FDD, 0x0C,0x00, 0xC, +0 }, + { 0x002F60C,0x213CB12, 0x00,0x00, 0xA, +0 }, + { 0x342F80E,0x3E4F407, 0x06,0x44, 0xE, +0 }, + { 0x200F601,0x3029FDD, 0x00,0x00, 0xC, +0 }, + { 0x276F502,0x0D6F609, 0x1B,0x00, 0x4, +0 }, + { 0x000F600,0x24393DF, 0x09,0x00, 0xE, +0 }, + { 0x0BFFA02,0x097C804, 0x00,0x00, 0xB, +0 }, + { 0x306FF80,0x0164F11, 0x00,0x00, 0xE, +0 }, + { 0x332F985,0x0A5D684, 0x05,0x40, 0xE, +0 }, + { 0x0FFF00B,0x2FF220C, 0x00,0x00, 0xE, +0 }, + { 0x223A133,0x4F4F131, 0xD6,0x09, 0x6, +0 }, + { 0x023B131,0x0F4F131, 0xD3,0x0A, 0x6, +0 }, + { 0x433F133,0x0F4F131, 0xD6,0x09, 0x8, +0 }, + { 0x2F3F132,0x4F6F131, 0xD3,0x0A, 0x6, +0 }, + { 0x2A4A112,0x4B5F211, 0xD2,0x05, 0x4, +0 }, + { 0x4A49112,0x2B5D110, 0xCF,0x05, 0x4, +0 }, + { 0x073FA31,0x4F4D111, 0x8E,0x08, 0xA, +0 }, + { 0x473FA32,0x4F4D111, 0x8C,0x09, 0xA, +0 }, + { 0x2E7F21A,0x0B8F201, 0x6F,0x48, 0xC, +0 }, + { 0x0E5B111,0x0B8F211, 0x9C,0x80, 0x0, +0 }, + { 0x2C7F436,0x0D7F231, 0x9D,0x0A, 0xE, +0 }, + { 0x0C7F021,0x0F8F111, 0x1E,0x0F, 0x0, +0 }, + { 0x523F134,0x4F5D111, 0x51,0x0D, 0x6, +0 }, + { 0x203FC32,0x1F7D111, 0x4B,0x0D, 0x6, +0 }, + { 0x559F101,0x0F7F111, 0x44,0x08, 0x6, +0 }, + { 0x0F00000,0x4F7F111, 0x3F,0x0D, 0x9, +0 }, + { 0x087F607,0x0E4F231, 0x54,0x08, 0x9, +0 }, + { 0x587F617,0x0E4F231, 0x54,0x08, 0x9, +0 }, + { 0x0A5F33F,0x0F2C312, 0xA1,0x06, 0xC, -12 }, + { 0x0A5F43F,0x0F2F392, 0xD5,0x07, 0x0, -12 }, + { 0x462A417,0x0027A11, 0x9C,0x08, 0x9, +0 }, + { 0x062A416,0x0028811, 0x99,0x07, 0x9, +0 }, + { 0x0F6F2B2,0x0F6F281, 0xE8,0x05, 0xF, +0 }, + { 0x0F6F2A4,0x007F08F, 0x45,0x05, 0x1, +0 }, + { 0x0F6F618,0x0F7E500, 0x63,0x80, 0x6, +12 }, + { 0x5A6F40E,0x007D804, 0x5B,0x80, 0x0, +0 }, + { 0x2F6F71A,0x0F5F413, 0x1F,0x03, 0x4, -19 }, + { 0x0F00000,0x1F7F715, 0x3F,0x00, 0x1, +2 }, + { 0x082F307,0x0E3F302, 0x97,0x8A, 0x6, -12 }, + { 0x082D307,0x0E3F302, 0x97,0x8A, 0x6, -12 }, + { 0x4109131,0x3B5F322, 0x52,0x88, 0x8, +0 }, + { 0x118B1A4,0x11BD161, 0x88,0x80, 0x7, +0 }, + { 0x108B1A3,0x11BD161, 0x88,0x88, 0x5, +12 }, + { 0x0F8F032,0x0F8F001, 0x65,0x07, 0xE, -12 }, + { 0x0F8F024,0x008F009, 0x43,0x07, 0x1, -12 }, + { 0x018AA70,0x0088AB1, 0x44,0x10, 0x4, +0 }, + { 0x118AA71,0x0088AB2, 0x4B,0x10, 0x4, +0 }, + { 0x1043031,0x1145432, 0x92,0x80, 0xD, +0 }, + { 0x1045033,0x1145430, 0x92,0x80, 0xB, +0 }, + { 0x1178001,0x1176082, 0x5D,0x83, 0x4, +0 }, + { 0x4178000,0x1176081, 0x54,0x83, 0x6, +0 }, + { 0x025A721,0x1264132, 0x4D,0x08, 0x6, +0 }, + { 0x1258621,0x1264633, 0x4F,0x08, 0x6, +0 }, + { 0x4FAF022,0x01A6221, 0x96,0x08, 0xC, +0 }, + { 0x105FF2C,0x01A6222, 0x9D,0x12, 0x8, +0 }, + { 0x107F021,0x2055232, 0x92,0x07, 0x8, +0 }, + { 0x107F021,0x2055232, 0x92,0x07, 0x0, +0 }, + { 0x574A613,0x4B8F401, 0x9D,0x0D, 0x6, +0 }, + { 0x2249134,0x2B8D301, 0x61,0x05, 0xA, -12 }, + { 0x5E5F133,0x1E4F211, 0x99,0x07, 0x6, +0 }, + { 0x1E5F133,0x5E4F211, 0x9E,0x0B, 0x0, +0 }, + { 0x21FF021,0x088F211, 0xA5,0x80, 0xA, +12 }, + { 0x11FF023,0x088F211, 0x5E,0x80, 0xA, +0 }, + { 0x132ED11,0x3E7D211, 0x87,0x0A, 0x6, +0 }, + { 0x332ED12,0x1E7D211, 0x80,0x45, 0x2, +0 }, + { 0x0F4E431,0x0F5F331, 0x97,0x86, 0x8, +0 }, + { 0x3F0F701,0x1F8F900, 0x00,0x0D, 0xE, +0 }, + { 0x0F77111,0x3F7F011, 0x48,0x87, 0xA, +0 }, + { 0x0F78140,0x3F7F040, 0x86,0x00, 0xC, +14 }, + { 0x0F78140,0x3F7F040, 0x07,0x40, 0xC, +12 }, + { 0x0F78100,0x3F7F000, 0x86,0x03, 0xC, +14 }, + { 0x6F78AE8,0x649B1F4, 0x03,0x0A, 0xA, +0 }, + { 0x6F78AE8,0x649B1F4, 0x43,0x4B, 0xA, +0 }, + { 0x0609533,0x4E5C131, 0x63,0x05, 0x0, +0 }, + { 0x0608521,0x0E4A131, 0xD4,0x05, 0x4, +0 }, + { 0x0F9F030,0x0F8F131, 0x9D,0x05, 0xA, +12 }, + { 0x7F0F017,0x7F9B700, 0x00,0x0F, 0xA, +12 }, + { 0x026AA21,0x0D7F132, 0xCF,0x84, 0xA, +0 }, + { 0x5F9F40B,0x445F711, 0x4B,0x4D, 0x2, +0 }, + { 0x010D331,0x0B68112, 0x9A,0x40, 0x6, +0 }, + { 0x0404121,0x0B56113, 0x9B,0x4C, 0x8, +0 }, + { 0x2E69419,0x5B6B311, 0x5E,0x08, 0x0, +0 }, + { 0x077FA21,0x0F79321, 0x07,0x0D, 0x0, +0 }, + { 0x2E69515,0x1B6B211, 0x17,0x08, 0x0, +0 }, + { 0x077FA21,0x06AC332, 0x07,0x0D, 0x0, +0 }, + { 0x0F5F430,0x0F6F330, 0x0E,0x00, 0xA, +12 }, + { 0x139A331,0x0F8F133, 0x93,0x08, 0xC, +0 }, + { 0x139A331,0x0F8F133, 0x93,0x08, 0xA, +0 }, + { 0x2257020,0x4266161, 0x95,0x05, 0xA, +0 }, + { 0x1257021,0x0266141, 0x99,0x07, 0x8, +0 }, + { 0x2426070,0x2154130, 0x4F,0x00, 0xA, +0 }, + { 0x214D070,0x1175222, 0x0F,0x88, 0x2, +0 }, + { 0x524D071,0x5075222, 0x13,0x88, 0x0, +0 }, + { 0x521F571,0x4166022, 0x90,0x09, 0x6, +0 }, + { 0x52151F0,0x4156021, 0x97,0x0D, 0x4, +12 }, + { 0x223F8F2,0x4055421, 0x99,0x8A, 0xC, +0 }, + { 0x4A35211,0x0E4C411, 0x9C,0x08, 0x6, +0 }, + { 0x2C79613,0x4E45411, 0xD7,0x08, 0xA, +0 }, + { 0x023E133,0x0F2F131, 0xA2,0x09, 0xE, +0 }, + { 0x023F132,0x0F2F131, 0x24,0x0A, 0xE, +0 }, + { 0x4C3C404,0x4B4B519, 0x21,0x05, 0x0, -31 }, + { 0x17A9913,0x0B4F213, 0x0F,0x00, 0x0, -19 }, + { 0x223F832,0x4056421, 0x99,0x8A, 0xC, +0 }, + { 0x433CB32,0x5057561, 0x9B,0x8A, 0xA, +0 }, + { 0x1029033,0x4044561, 0x5B,0x85, 0x4, +0 }, + { 0x4109033,0x2044520, 0xA8,0x85, 0xA, +0 }, + { 0x2034170,0x0043671, 0x0B,0x20, 0xB, +0 }, + { 0x1024171,0x0043671, 0x0C,0x17, 0xB, +0 }, + { 0x005A061,0x0F55022, 0x69,0x06, 0x0, +0 }, + { 0x0008060,0x0F55021, 0x33,0x08, 0x0, +12 }, + { 0x239B420,0x0076121, 0x50,0x05, 0x6, +0 }, + { 0x139B462,0x00D7161, 0x91,0x14, 0x0, +0 }, + { 0x05470F1,0x07440B1, 0x69,0x80, 0x0, +0 }, + { 0x054A0F1,0x07430B1, 0x5E,0x80, 0x0, +0 }, + { 0x2436110,0x714D211, 0xCD,0x00, 0xA, +0 }, + { 0x5436192,0x745F312, 0xCB,0x00, 0xA, +0 }, + { 0x0147421,0x0077521, 0x94,0x04, 0xE, +0 }, + { 0x0178461,0x008AF28, 0x10,0xA6, 0xC, +0 }, + { 0x0235271,0x0198161, 0x1E,0x08, 0xE, +0 }, + { 0x0235361,0x0196161, 0x1D,0x03, 0xE, +0 }, + { 0x0155331,0x0378261, 0x94,0x00, 0xA, +0 }, + { 0x118543A,0x5177472, 0x1E,0x00, 0x4, -12 }, + { 0x0365121,0x0257221, 0x1E,0x08, 0x0, +0 }, + { 0x2844521,0x20592A0, 0x23,0x03, 0x0, +0 }, + { 0x0578321,0x117C021, 0x19,0x03, 0xC, +0 }, + { 0x2E77530,0x307F520, 0x10,0x08, 0x8, +0 }, + { 0x036F121,0x337F121, 0x95,0x08, 0xE, +0 }, + { 0x0368121,0x037F121, 0x95,0x08, 0xE, +0 }, + { 0x0A66121,0x0976121, 0x9B,0x08, 0xE, +0 }, + { 0x5237731,0x1F65012, 0x4B,0x00, 0xA, +0 }, + { 0x0137732,0x0F65011, 0xC7,0x0A, 0xA, +0 }, + { 0x1067021,0x1165231, 0x46,0x00, 0x6, +0 }, + { 0x00B9820,0x10B5330, 0x8E,0x00, 0xA, +12 }, + { 0x10B8020,0x11B6330, 0x87,0x00, 0x8, +12 }, + { 0x1235031,0x0077C24, 0xC0,0x08, 0x2, +0 }, + { 0x045D933,0x4076C35, 0xD0,0x26, 0x4, +0 }, + { 0x6077831,0x2076331, 0x1E,0x00, 0x6, +0 }, + { 0x0199031,0x01B6134, 0x95,0x80, 0xA, +0 }, + { 0x0177532,0x0174531, 0x93,0x03, 0xC, +0 }, + { 0x0277530,0x0174536, 0x14,0x9C, 0xE, +12 }, + { 0x08B8EF1,0x0285571, 0xC0,0x00, 0xE, +0 }, + { 0x08860A1,0x01A6561, 0x5C,0x00, 0x8, +0 }, + { 0x2176522,0x0277421, 0x5A,0x00, 0x6, +0 }, + { 0x1267532,0x0166531, 0x8D,0x05, 0x4, +0 }, + { 0x2F0F011,0x0987801, 0x03,0x17, 0xA, +0 }, + { 0x00457F2,0x0375761, 0xA8,0x00, 0xE, +0 }, + { 0x2545C73,0x0776821, 0x00,0x0D, 0xE, +0 }, + { 0x5543737,0x25D67A1, 0x28,0x00, 0x8, +0 }, + { 0x6243371,0x46D6331, 0x20,0x00, 0x6, +0 }, + { 0x00F31D1,0x0053271, 0xC7,0x00, 0xB, +0 }, + { 0x00581A2,0x0295231, 0x37,0x00, 0x6, +0 }, + { 0x20FFF22,0x60FFF21, 0x7F,0x12, 0x5, +0 }, + { 0x30FFF22,0x60FFF21, 0xBF,0x12, 0x5, +0 }, + { 0x39BC120,0x368C030, 0xBF,0x06, 0x0, +0 }, + { 0x3AB8120,0x308F130, 0x9E,0x06, 0x0, +0 }, + { 0x13357F1,0x00767E1, 0x21,0x00, 0xA, +0 }, + { 0x43357F2,0x00767E1, 0x28,0x00, 0x0, +0 }, + { 0x2444830,0x21D67A1, 0x22,0x00, 0x8, +0 }, + { 0x534B821,0x02D87A1, 0x1F,0x00, 0xA, +0 }, + { 0x32B7420,0x12BF134, 0x46,0x00, 0x8, +0 }, + { 0x5029072,0x0069061, 0x96,0x0C, 0x8, +0 }, + { 0x1019031,0x0069061, 0x1A,0x0C, 0x6, +0 }, + { 0x245C224,0x2550133, 0x81,0x80, 0x9, -36 }, + { 0x2459224,0x2556133, 0x81,0x80, 0x9, -36 }, + { 0x132ED10,0x3E7D010, 0x87,0x0D, 0x6, +12 }, + { 0x132ED30,0x3E7D010, 0x87,0x12, 0x6, +12 }, + { 0x073513A,0x013C121, 0xA4,0x0A, 0x2, +0 }, + { 0x273F325,0x0228231, 0x20,0x0A, 0x4, +0 }, + { 0x0031131,0x0054361, 0xD4,0x08, 0x4, +0 }, + { 0x20311B0,0x00543E1, 0xD9,0x08, 0x4, +0 }, + { 0x245A121,0x126A121, 0x98,0x05, 0xC, +0 }, + { 0x255A421,0x126A121, 0x98,0x05, 0xC, +0 }, + { 0x50470E1,0x1148161, 0x59,0x03, 0x2, +0 }, + { 0x10460E2,0x4148161, 0x5F,0x83, 0x6, +0 }, + { 0x0336186,0x05452E1, 0xA7,0x00, 0x6, +0 }, + { 0x13351A6,0x05452E1, 0xA7,0x00, 0x0, +0 }, + { 0x2529084,0x1534341, 0x9D,0x80, 0xC, +0 }, + { 0x2529082,0x0534341, 0x9D,0x80, 0xC, +0 }, + { 0x2345231,0x2135120, 0x98,0x00, 0x6, +0 }, + { 0x410F422,0x1233231, 0x20,0x00, 0xA, +0 }, + { 0x1522162,0x1633021, 0x99,0x80, 0x8, +0 }, + { 0x1522161,0x1633021, 0x99,0x80, 0x8, +0 }, + { 0x157B261,0x019F806, 0x04,0x40, 0x7, +0 }, + { 0x157B261,0x0145114, 0x04,0x40, 0x7, +0 }, + { 0x2322122,0x0133221, 0x8C,0x92, 0x6, +0 }, + { 0x4033121,0x0132122, 0x93,0x48, 0x4, +7 }, + { 0x074F624,0x0249303, 0xC0,0x0D, 0x0, +0 }, + { 0x3D2C092,0x1D2D131, 0x8E,0x09, 0x0, +0 }, + { 0x0D2D091,0x1D23132, 0x8E,0x09, 0x0, +0 }, + { 0x5F29054,0x0F2C241, 0x99,0x06, 0xE, +0 }, + { 0x1F19011,0x0F2C241, 0x1A,0x06, 0x6, +0 }, + { 0x05233E1,0x0131371, 0x1A,0x88, 0x7, +0 }, + { 0x5522363,0x0131331, 0x1A,0x8D, 0x7, +0 }, + { 0x0B67061,0x0928032, 0x9C,0x11, 0xA, +0 }, + { 0x0057F21,0x0038F62, 0x9C,0x11, 0xA, +0 }, + { 0x0625331,0x1648221, 0x94,0x06, 0xE, +0 }, + { 0x2645321,0x2445521, 0x15,0x0D, 0xA, +0 }, + { 0x0B37121,0x5F48221, 0x16,0x08, 0x2, +0 }, + { 0x2B37102,0x5F48221, 0x90,0x08, 0x6, +0 }, + { 0x1127533,0x4F4F211, 0x58,0x03, 0x6, +0 }, + { 0x3F0F014,0x6F7F611, 0x40,0x43, 0xA, +0 }, + { 0x033F201,0x373F402, 0xD1,0x8A, 0x0, +0 }, + { 0x6A7F907,0x229A904, 0x1A,0x00, 0xA, -12 }, + { 0x5E2F321,0x6E4F523, 0x1B,0x08, 0x8, +0 }, + { 0x455F71C,0x0D68501, 0xA3,0x08, 0x6, +0 }, + { 0x055F718,0x0D6E501, 0x23,0x08, 0x0, +0 }, + { 0x1397931,0x2099B22, 0x80,0x00, 0x6, +0 }, + { 0x2137931,0x1079B22, 0x42,0xC2, 0xA, +0 }, + { 0x302A130,0x0266221, 0x1E,0x00, 0xE, +0 }, + { 0x0136031,0x1169131, 0x12,0x80, 0x8, +0 }, + { 0x032A115,0x172B212, 0x00,0x80, 0x1, +5 }, + { 0x001E79A,0x067961C, 0x81,0x00, 0x4, +0 }, + { 0x4046306,0x005A902, 0xCA,0x08, 0x6, +0 }, + { 0x0045413,0x005A601, 0x51,0x08, 0xA, +0 }, + { 0x4D1F214,0x098F715, 0xA0,0x00, 0xC, +0 }, + { 0x008F312,0x004F600, 0x08,0xC8, 0x4, -12 }, + { 0x27CFA01,0x004F200, 0x08,0x08, 0x0, +0 }, + { 0x5C8FB00,0x0B7E601, 0x00,0x00, 0x0, +0 }, + { 0x2F0F00F,0x0F8F800, 0x00,0x40, 0xE, +12 }, + { 0x518F890,0x0E7F310, 0x00,0x00, 0x8, -12 }, + { 0x250F610,0x0E7F510, 0x00,0xC8, 0x6, +0 }, + { 0x2114109,0x51D2101, 0x05,0x80, 0xA, +0 }, + { 0x2114108,0x31D2101, 0x05,0x80, 0xA, +12 }, + { 0x0534313,0x7574A1F, 0x20,0x03, 0xE, -14 }, + { 0x00437D2,0x0343471, 0xA1,0x07, 0xC, +0 }, + { 0x0F0F00C,0x0F66700, 0x00,0xCD, 0xE, +0 }, + { 0x200C327,0x6021300, 0x80,0x12, 0xE, -23 }, + { 0x200C32B,0x6021300, 0x80,0x12, 0xE, -24 }, + { 0x003EBD7,0x06845D8, 0xD4,0x00, 0x7, +12 }, + { 0x62FDA20,0x614B009, 0x42,0x48, 0x4, -24 }, + { 0x62FDA20,0x614B009, 0x82,0x48, 0x4, -20 }, + { 0x101FE30,0x6142120, 0x00,0x00, 0xC, -36 }, + { 0x6019460,0x1142120, 0x26,0x00, 0xC, -14 }, + { 0x200832F,0x6044020, 0x80,0x00, 0xE, -36 }, + { 0x200832F,0x6044020, 0x80,0x00, 0xE, -35 }, + { 0x2305431,0x6E7F600, 0x00,0x00, 0xE, +0 }, + { 0x059F802,0x01CF600, 0x11,0x00, 0xC, +0 }, + { 0x2159506,0x65AB701, 0x00,0x04, 0xE, +0 }, + { 0x10F5F81,0x0164611, 0x00,0x0A, 0x6, +0 }, + { 0x00F5F01,0x20F5F00, 0x00,0x00, 0x8, +0 }, + { 0x0D6D725,0x3A9A909, 0x1F,0x00, 0xE, -9 }, + { 0x0F0A00F,0x0F8F80F, 0x00,0x0C, 0xE, +0 }, + { 0x2FDFD00,0x6FAFA00, 0x00,0x00, 0xE, +0 }, + { 0x4F1F103,0x6FAFA07, 0x00,0x00, 0x8, +0 }, + { 0x0F0F007,0x2F6F60F, 0x27,0x00, 0x0, +21 }, + { 0x559FA00,0x047F800, 0x00,0x00, 0x4, +0 }, + { 0x3F1F102,0x0078400, 0x00,0x26, 0xC, +0 }, + { 0x048FA00,0x008F900, 0x00,0x00, 0x6, +12 }, + { 0x287F702,0x678F802, 0x80,0x88, 0xE, +12 }, + { 0x2F7F602,0x0F8F802, 0x00,0x88, 0xE, +12 }, + { 0x008F700,0x007F609, 0x00,0x00, 0xD, -24 }, + { 0x0F1F105,0x0078407, 0x00,0x08, 0xC, -12 }, + { 0x05476C1,0x30892C5, 0x80,0x08, 0x0, +0 }, + { 0x05477C1,0x30892C5, 0x00,0x08, 0xA, -2 }, + { 0x007C604,0x007C604, 0x08,0x08, 0x1, +0 }, + { 0x201F302,0x057AB09, 0x03,0x07, 0xC, +12 }, + { 0x058F30B,0x308F90D, 0x04,0x08, 0x6, +0 }, + { 0x255F308,0x308F909, 0x04,0x08, 0x8, +4 }, + { 0x006C604,0x007C604, 0x08,0x08, 0x1, +0 }, + { 0x201F312,0x057AB09, 0x03,0x07, 0xC, +12 }, + { 0x254D307,0x3288905, 0x04,0x03, 0xA, -5 }, + { 0x0015500,0x007C716, 0x0C,0x00, 0x0, +0 }, + { 0x201F312,0x057AB09, 0x00,0x07, 0xC, +12 }, + { 0x0015500,0x007C718, 0x0C,0x00, 0x0, +0 }, + { 0x001F312,0x047BB05, 0x03,0x07, 0xC, +12 }, + { 0x0015500,0x007C71B, 0x0C,0x00, 0x0, +0 }, + { 0x201F312,0x047BB09, 0x03,0x07, 0xC, +12 }, + { 0x291F108,0x333F401, 0x00,0x00, 0x8, +12 }, + { 0x291F108,0x333F501, 0x00,0x00, 0x8, +12 }, + { 0x0015500,0x007C71F, 0x0C,0x00, 0x0, +0 }, + { 0x300F50C,0x605FE05, 0x07,0x8A, 0x0, +12 }, + { 0x310F508,0x604FE05, 0x86,0x8A, 0x0, +11 }, + { 0x2E1F11E,0x3F3F318, 0x04,0x00, 0x8, +0 }, + { 0x2777603,0x3679601, 0x87,0x08, 0x6, +12 }, + { 0x277C643,0x3679601, 0x87,0x08, 0xE, +12 }, + { 0x366F905,0x099F701, 0x00,0x00, 0xC, +12 }, + { 0x291F108,0x334F401, 0x00,0x00, 0x8, +12 }, + { 0x431A000,0x085B41A, 0x81,0x05, 0xA, +12 }, + { 0x459F640,0x185B418, 0x00,0x20, 0xB, +12 }, + { 0x300F50C,0x605FE04, 0x07,0x8A, 0x0, +12 }, + { 0x2A8F9E3,0x0779643, 0x1E,0x08, 0x2, +6 }, + { 0x0A5F7E8,0x0D89949, 0xDE,0x00, 0x0, +0 }, + { 0x2A8F9E3,0x0779643, 0x1E,0x00, 0xE, +12 }, + { 0x0A5F7E9,0x0D8994A, 0xDE,0x08, 0xC, +0 }, + { 0x0A8F7E9,0x5D8990A, 0x08,0x00, 0xC, +0 }, + { 0x0A5F7E9,0x0D8994A, 0x29,0x08, 0xC, +10 }, + { 0x2A8F9E2,0x0779642, 0x1E,0x00, 0xE, +8 }, + { 0x0A5F7E9,0x5D8994A, 0x08,0x00, 0xC, +0 }, + { 0x456FB02,0x017F700, 0x81,0x00, 0xC, +12 }, + { 0x556FA01,0x117F701, 0x00,0x0D, 0xA, +10 }, + { 0x556FB02,0x117F701, 0x81,0x0D, 0xA, +10 }, + { 0x367FE06,0x668F701, 0x09,0x08, 0x8, +12 }, + { 0x367FD10,0x098F901, 0x00,0x0D, 0x8, +6 }, + { 0x367FE05,0x678F701, 0x09,0x08, 0x8, +12 }, + { 0x367FD10,0x078F901, 0x00,0x0D, 0x8, +11 }, + { 0x098600F,0x3FC8590, 0x08,0xC0, 0xE, +12 }, + { 0x009F020,0x27DA788, 0x25,0x00, 0x0, +12 }, + { 0x00FC020,0x22DA388, 0x25,0x00, 0xA, +12 }, + { 0x0F00000,0x0F00000, 0x3F,0x3F, 0xC, +0 }, + { 0x000F020,0x40A8A00, 0x0A,0x00, 0xE, +0 }, + { 0x70F5F20,0x70F4F00, 0x00,0x00, 0x2, -12 }, + { 0x0D1F815,0x078F512, 0x44,0x00, 0x8, +12 }, + { 0x2D1F213,0x098F614, 0x9D,0x00, 0x0, +0 }, + { 0x2D1F213,0x098F614, 0x9D,0x21, 0x0, -2 }, + { 0x0985900,0x039870F, 0x07,0x00, 0x8, +13 }, + { 0x2F3F307,0x09C9B0F, 0x1D,0x00, 0x0, +13 }, + { 0x09C4B00,0x43A6705, 0x21,0x00, 0xC, +13 }, + { 0x0F7F907,0x2987805, 0x1C,0x00, 0x0, +13 }, + { 0x160F2C6,0x07AF4D4, 0x4F,0x80, 0x8, +12 }, + { 0x160F286,0x0B7F294, 0x4F,0x80, 0x8, +12 }, + { 0x227A305,0x36A560A, 0x87,0x08, 0xE, +12 }, + { 0x247C345,0x3697809, 0x87,0x08, 0xE, +12 }, + { 0x4755406,0x3667601, 0x87,0x08, 0x6, +12 }, + { 0x275A346,0x3667601, 0x87,0x08, 0x6, +12 }, + { 0x6E4840B,0x6E4B409, 0x12,0x09, 0x1, +0 }, + { 0x6E4440B,0x6E46407, 0x21,0x13, 0x1, +3 }, + { 0x037A309,0x06DF904, 0x11,0x00, 0xE, +0 }, + { 0x6F9A902,0x2F7C801, 0x00,0x40, 0x8, +0 }, + { 0x4F9F901,0x4F7C713, 0x1F,0x48, 0x0, -7 }, + { 0x4B7C720,0x1F3F300, 0x0B,0x00, 0x0, +0 }, + { 0x01BF4E0,0x018F3E0, 0x8D,0x23, 0xA, +12 }, + { 0x00FFFE4,0x00FFFE1, 0x8A,0xA9, 0x1, +0 }, + { 0x031FF10,0x004FF01, 0x07,0x25, 0xA, +12 }, + { 0x050F101,0x07CE401, 0x4F,0x22, 0x6, +12 }, + { 0x00361F0,0x02CE371, 0x86,0x1F, 0xA, +12 }, + { 0x00361B0,0x02CE3F3, 0x86,0x1F, 0x8, +12 }, + { 0x00331F2,0x02C53F4, 0x4B,0x21, 0x4, -12 }, + { 0x08FAEE2,0x02A8561, 0x11,0x23, 0xE, +12 }, + { 0x019D530,0x01B6171, 0x15,0x9B, 0xC, +12 }, + { 0x00B4131,0x03B9261, 0x1C,0x99, 0xE, +0 }, + { 0x01F61B1,0x01B9261, 0x1C,0x9D, 0xE, +0 }, + { 0x04C6321,0x00FC521, 0x18,0xA0, 0xC, +0 }, + { 0x060F207,0x072F212, 0x4F,0x21, 0x8, +0 }, + { 0x053F401,0x053F308, 0x40,0x64, 0x0, -6 }, + { 0x0FFF832,0x07FF511, 0x44,0x1F, 0xE, -18 }, + { 0x04CA700,0x04FC600, 0x00,0x22, 0x0, +12 }, + { 0x0F5F062,0x0F8F60E, 0x00,0x1F, 0xE, +12 }, + { 0x005FC4E,0x0F8F90C, 0x00,0x24, 0x0, +12 }, + { 0x005756E,0x0F8F601, 0x00,0x22, 0xE, +12 }, + { 0x011F131,0x043D418, 0x90,0xA5, 0x8, -12 }, + { 0x08FAEE0,0x00A8561, 0xE8,0x21, 0xE, +12 }, + { 0x02990F2,0x02C61F2, 0x16,0x22, 0xA, -12 }, + { 0x02BF4E0,0x048F3E0, 0x8D,0x1F, 0x8, +12 }, + { 0x023F331,0x09C4333, 0x45,0x25, 0x6, -12 }, + { 0x04CA700,0x04FC600, 0x00,0x2B, 0x0, -12 }, + { 0x0B5F704,0x002010C, 0x00,0x00, 0x8, +21 }, + { 0x050F113,0x076D201, 0x50,0x40, 0x6, +0 }, + { 0x050F113,0x076D201, 0x50,0x00, 0x6, +0 }, + { 0x054F113,0x076D201, 0x53,0x00, 0x6, +0 }, + { 0x054F113,0x076D201, 0x50,0x00, 0x6, +0 }, + { 0x0FFF92C,0x0FFC1A1, 0xD4,0x00, 0x0, +0 }, + { 0x050F101,0x07CD301, 0x4F,0x00, 0x6, +0 }, + { 0x030A131,0x074C216, 0x81,0x80, 0x8, +0 }, + { 0x0FFF201,0x0F8F101, 0x11,0x00, 0xA, +0 }, + { 0x011FAD6,0x0FCF161, 0x4D,0x00, 0x8, +0 }, + { 0x011FA16,0x0F1F1E1, 0x4D,0x00, 0x8, +0 }, + { 0x011FAD6,0x0F5F561, 0x4D,0x00, 0x8, +0 }, + { 0x015DA45,0x0F6F361, 0x4E,0x80, 0x0, +0 }, + { 0x0F0FE04,0x0B5F6C2, 0x00,0x00, 0xE, -12 }, + { 0x004FE11,0x0BDF211, 0x11,0x00, 0x8, +0 }, + { 0x00FFF24,0x00FFF21, 0x80,0x80, 0x1, -12 }, + { 0x0FFF92C,0x0FFC0A1, 0xD4,0x00, 0x0, -12 }, + { 0x0E5F8E2,0x00EC0E1, 0xCA,0x00, 0x8, +0 }, + { 0x0FD5524,0x02D5031, 0x54,0x00, 0xE, +0 }, + { 0x0C8F253,0x0C5F211, 0x16,0x40, 0x4, +0 }, + { 0x0C8F253,0x0C5F211, 0x20,0x00, 0x4, +0 }, + { 0x0FFF111,0x3FFF054, 0x43,0x00, 0x8, +0 }, + { 0x0FFF111,0x3FFF054, 0x43,0x40, 0x8, +0 }, + { 0x0F0F0CA,0x06859EC, 0x4E,0x00, 0xC, +0 }, + { 0x02CD321,0x02CC321, 0x15,0x80, 0xA, +0 }, + { 0x0F2D401,0x08AC421, 0x18,0x80, 0xA, +0 }, + { 0x07AB400,0x07CC301, 0x1D,0x00, 0x0, +12 }, + { 0x07E7330,0x09E8021, 0x16,0x00, 0xE, +12 }, + { 0x004FE11,0x0BDF211, 0x0A,0x00, 0x8, +0 }, + { 0x0035171,0x0175461, 0x20,0x00, 0xE, +0 }, + { 0x0035171,0x0175461, 0x1E,0x00, 0xE, +0 }, + { 0x0035171,0x0175423, 0x1C,0x00, 0xE, +0 }, + { 0x04CA800,0x04FD600, 0x0B,0x00, 0x0, +12 }, + { 0x075F502,0x0F3F201, 0x29,0x80, 0x0, +0 }, + { 0x0530900,0x094F702, 0x40,0x00, 0xE, -12 }, + { 0x01432F1,0x016F1E1, 0x18,0x00, 0x0, +0 }, + { 0x01432F1,0x01631E1, 0x18,0x00, 0x0, +0 }, + { 0x01132F1,0x014F1E1, 0x18,0x00, 0x0, +0 }, + { 0x0154011,0x03831F1, 0x92,0x00, 0x8, +0 }, + { 0x0948411,0x0F4F4E4, 0x03,0x40, 0x8, -12 }, + { 0x0577361,0x017A021, 0x19,0x00, 0xC, +0 }, + { 0x0585361,0x018A021, 0x19,0x00, 0xC, +0 }, + { 0x0565361,0x016A021, 0x19,0x00, 0xC, +0 }, + { 0x0035171,0x0675421, 0x1C,0x00, 0xE, +0 }, + { 0x0576361,0x017A021, 0x1C,0x00, 0xC, +0 }, + { 0x0176E70,0x00E6B22, 0x8D,0x00, 0x2, +12 }, + { 0x00E7170,0x00E7823, 0x16,0x07, 0xE, +12 }, + { 0x0178731,0x00E8B22, 0x45,0x00, 0x2, +0 }, + { 0x0195132,0x0396061, 0x9A,0x80, 0xC, +0 }, + { 0x02495A2,0x02A60E2, 0x1D,0x80, 0x2, -12 }, + { 0x0AFD6A1,0x02A60E2, 0x13,0x80, 0x2, +0 }, + { 0x02498A2,0x02A60E2, 0x1D,0x80, 0x2, -12 }, + { 0x04FD6A1,0x02A60E2, 0x13,0x80, 0x2, +0 }, + { 0x0BF7721,0x02A60A1, 0x19,0x80, 0x6, +0 }, + { 0x0E5F8E2,0x00E70E1, 0xCA,0x00, 0x8, +0 }, + { 0x30FF221,0x018F221, 0x1D,0x00, 0x0, +0 }, + { 0x0FFF041,0x0FFF001, 0x11,0x00, 0xA, +0 }, + { 0x0BDF101,0x39FF102, 0xCE,0x80, 0x0, +0 }, + { 0x0FFF141,0x0FFF001, 0x0E,0x09, 0xA, +0 }, + { 0x0867261,0x01450E1, 0xA7,0x80, 0x2, +0 }, + { 0x049F430,0x033F410, 0x90,0x00, 0xC, +12 }, + { 0x0F0F0CA,0x06459CC, 0x4E,0x00, 0xC, +0 }, + { 0x0152011,0x0F831F1, 0x43,0x00, 0x8, +0 }, + { 0x0152011,0x0F831F1, 0x92,0x00, 0x8, +0 }, + { 0x010FF34,0x004FF03, 0x91,0x00, 0xA, +0 }, + { 0x002A4B0,0x04240D7, 0x84,0x80, 0x0, +0 }, + { 0x032B6B3,0x031D1B0, 0x4A,0x00, 0xE, +12 }, + { 0x0978211,0x0F3F0E4, 0x03,0x40, 0x8, +0 }, + { 0x002A4B4,0x04240D7, 0x87,0x80, 0x6, +0 }, + { 0x0F0A133,0x0F37115, 0x85,0x80, 0x8, +0 }, + { 0x053F101,0x074F211, 0x4F,0x00, 0x6, +0 }, + { 0x0E8F80B,0x0F4C301, 0xCA,0x00, 0x0, +0 }, + { 0x0FFF001,0x0F8F001, 0x11,0x00, 0xA, +0 }, + { 0x0EE7130,0x01E8823, 0x16,0x00, 0xE, +0 }, + { 0x025DA09,0x015F101, 0x4E,0x00, 0xA, +0 }, + { 0x0FFF832,0x07FF511, 0x44,0x00, 0xE, +12 }, + { 0x0F33900,0x005FF00, 0x3F,0x00, 0x0, +12 }, + { 0x0FFF832,0x0F8F501, 0x44,0x00, 0xE, +0 }, + { 0x0F0F007,0x0DC5C00, 0x00,0x00, 0xE, +12 }, + { 0x002A4B0,0x04240D7, 0xC4,0x89, 0x0, +0 }, + { 0x1111EF0,0x11121E2, 0x00,0xC0, 0x8, -12 }, + { 0x0EFE800,0x0FFA500, 0x0D,0x00, 0x6, +12 }, + { 0x077F005,0x0EDFA00, 0x00,0x00, 0xE, +12 }, + { 0x0F0F006,0x0F7F700, 0x00,0x00, 0xE, +12 }, + { 0x1FFF005,0x0B9F800, 0x00,0x00, 0xE, +0 }, + { 0x0F33900,0x005FF00, 0x3F,0x00, 0x0, +0 }, + { 0x077F005,0x0FBFA00, 0x00,0x00, 0xE, +0 }, + { 0x077F005,0x0EAFA00, 0x00,0x00, 0xE, +12 }, + { 0x0FFF005,0x0FFF600, 0x00,0x06, 0xE, +0 }, + { 0x0C0F006,0x034C6CF, 0x0E,0x00, 0xE, +0 }, + { 0x360F207,0x352F212, 0x0A,0x0C, 0x0, +0 }, + { 0x360F207,0x352F212, 0x0A,0x0B, 0x0, +0 }, + { 0x0F0F406,0x0F78700, 0x00,0x0D, 0xE, +0 }, + { 0x1FFF005,0x0B9F800, 0x00,0x00, 0x8, +0 }, + { 0x0F0F000,0x0F5F500, 0x00,0x09, 0xA, +0 }, + { 0x0590900,0x097F700, 0x40,0x00, 0x0, +24 }, + { 0x052F301,0x194F700, 0x40,0x00, 0x0, +12 }, + { 0x0530907,0x096F605, 0x40,0x00, 0xE, +0 }, + { 0x070F005,0x0E57A00, 0x00,0x10, 0xE, +12 }, + { 0x070F005,0x0E59A00, 0x00,0x10, 0xE, +12 }, + { 0x070F005,0x0E55A00, 0x00,0x10, 0xE, +12 }, + { 0x07BF003,0x07BF502, 0x8A,0x80, 0x8, +0 }, + { 0x07BF003,0x07BF402, 0x8A,0x80, 0x8, +0 }, +}; +const struct adlinsdata adlins[4804] = +{ + { 0, 0, 0, 0, 9006, 133,0 }, + { 1, 1, 0, 0, 9206, 146,0 }, + { 2, 2, 0, 0, 9246, 240,0 }, + { 3, 3, 0, 0, 9440, 140,0 }, + { 4, 4, 0, 0, 8900, 120,0 }, + { 5, 5, 0, 0, 9400, 140,0 }, + { 6, 6, 0, 0, 7460, 380,0 }, + { 7, 7, 0, 0, 9226, 93,0 }, + { 8, 8, 0, 0, 4613, 420,0 }, + { 9, 9, 0, 0, 7286, 4713,0 }, + { 10, 10, 0, 0, 2280, 746,0 }, + { 11, 11, 0, 0, 9233, 240,0 }, + { 12, 12, 0, 0, 346, 153,0 }, + { 13, 13, 0, 0, 633, 233,0 }, + { 14, 14, 0, 0, 4660, 1573,0 }, + { 15, 15, 0, 0, 1166, 400,0 }, + { 16, 16, 0, 0, 40000, 126,0 }, + { 17, 17, 0, 0, 40000, 93,0 }, + { 18, 18, 0, 0, 40000, 93,0 }, + { 19, 19, 0, 0, 40000, 553,0 }, + { 20, 20, 0, 0, 40000, 660,0 }, + { 21, 21, 0, 0, 40000, 73,0 }, + { 22, 22, 0, 0, 40000, 146,0 }, + { 23, 23, 0, 0, 40000, 146,0 }, + { 24, 24, 0, 0, 4026, 100,0 }, + { 25, 25, 0, 0, 14286, 120,0 }, + { 26, 26, 0, 0, 9233, 106,0 }, + { 27, 27, 0, 0, 4480, 100,0 }, + { 28, 28, 0, 0, 40000, 60,0 }, + { 29, 29, 0, 0, 40000, 80,0 }, + { 30, 30, 0, 0, 40000, 80,0 }, + { 31, 31, 0, 0, 18226, 100,0 }, + { 32, 32, 0, 0, 40000, 0,0 }, + { 33, 33, 0, 0, 40000, 80,0 }, + { 34, 34, 0, 0, 40000, 0,0 }, + { 35, 35, 0, 0, 40000, 53,0 }, + { 36, 36, 0, 0, 40000, 0,0 }, + { 37, 37, 0, 0, 40000, 0,0 }, + { 38, 38, 0, 0, 40000, 0,0 }, + { 39, 39, 0, 0, 40000, 160,0 }, + { 40, 40, 0, 0, 40000, 233,0 }, + { 41, 41, 0, 0, 40000, 73,0 }, + { 42, 42, 0, 0, 40000, 233,0 }, + { 43, 43, 0, 0, 40000, 213,0 }, + { 44, 44, 0, 0, 1246, 453,0 }, + { 45, 45, 0, 0, 4580, 786,0 }, + { 46, 46, 0, 0, 6873, 1246,0 }, + { 47, 47, 0, 0, 40000, 100,0 }, + { 48, 48, 0, 0, 40000, 140,0 }, + { 49, 49, 0, 0, 40000, 393,0 }, + { 50, 50, 0, 0, 40000, 406,0 }, + { 51, 51, 0, 0, 40000, 373,0 }, + { 52, 52, 0, 0, 40000, 0,0 }, + { 53, 53, 0, 0, 40000, 360,0 }, + { 54, 54, 0, 0, 1060, 380,0 }, + { 55, 55, 0, 0, 40000, 80,0 }, + { 56, 56, 0, 0, 40000, 73,0 }, + { 57, 57, 0, 0, 40000, 66,0 }, + { 58, 58, 0, 0, 40000, 60,0 }, + { 59, 59, 0, 0, 40000, 73,0 }, + { 60, 60, 0, 0, 40000, 66,0 }, + { 61, 61, 0, 0, 40000, 86,0 }, + { 62, 62, 0, 0, 40000, 66,0 }, + { 63, 63, 0, 0, 40000, 73,0 }, + { 64, 64, 0, 0, 40000, 80,0 }, + { 65, 65, 0, 0, 40000, 80,0 }, + { 66, 66, 0, 0, 40000, 73,0 }, + { 67, 67, 0, 0, 40000, 73,0 }, + { 68, 68, 0, 0, 40000, 53,0 }, + { 69, 69, 0, 0, 40000, 73,0 }, + { 70, 70, 0, 0, 40000, 126,0 }, + { 71, 71, 0, 0, 40000, 73,0 }, + { 72, 72, 0, 0, 40000, 73,0 }, + { 73, 73, 0, 0, 40000, 73,0 }, + { 74, 74, 0, 0, 40000, 66,0 }, + { 75, 75, 0, 0, 40000, 153,0 }, + { 76, 76, 0, 0, 40000, 153,0 }, + { 77, 77, 0, 0, 40000, 146,0 }, + { 78, 78, 0, 0, 40000, 146,0 }, + { 79, 79, 0, 0, 40000, 66,0 }, + { 80, 80, 0, 0, 40000, 60,0 }, + { 81, 81, 0, 0, 40000, 86,0 }, + { 82, 82, 0, 0, 40000, 73,0 }, + { 83, 83, 0, 0, 40000, 66,0 }, + { 84, 84, 0, 0, 40000, 153,0 }, + { 85, 85, 0, 0, 40000, 233,0 }, + { 86, 86, 0, 0, 40000, 80,0 }, + { 87, 87, 0, 0, 40000, 400,0 }, + { 88, 88, 0, 0, 40000, 1373,0 }, + { 89, 89, 0, 0, 40000, 193,0 }, + { 90, 90, 0, 0, 40000, 1273,0 }, + { 91, 91, 0, 0, 40000, 186,0 }, + { 92, 92, 0, 0, 40000, 86,0 }, + { 93, 93, 0, 0, 40000, 286,0 }, + { 94, 94, 0, 0, 40000, 140,0 }, + { 95, 95, 0, 0, 7440, 2473,0 }, + { 96, 96, 0, 0, 40000, 1220,0 }, + { 97, 97, 0, 0, 4946, 2713,0 }, + { 98, 98, 0, 0, 40000, 160,0 }, + { 99, 99, 0, 0, 8966, 406,0 }, + { 100, 100, 0, 0, 40000, 1353,0 }, + { 101, 101, 0, 0, 40000, 1306,0 }, + { 102, 102, 0, 0, 40000, 933,0 }, + { 103, 103, 0, 0, 9086, 226,0 }, + { 104, 104, 0, 0, 7233, 326,0 }, + { 105, 105, 0, 0, 7286, 200,0 }, + { 106, 106, 0, 0, 14180, 4406,0 }, + { 107, 107, 0, 0, 1180, 406,0 }, + { 108, 108, 0, 0, 40000, 66,0 }, + { 109, 109, 0, 0, 40000, 213,0 }, + { 110, 110, 0, 0, 40000, 73,0 }, + { 111, 111, 0, 0, 4606, 413,0 }, + { 112, 112, 0, 0, 613, 240,0 }, + { 113, 113, 0, 0, 1166, 400,0 }, + { 114, 114, 0, 0, 200, 353,0 }, + { 115, 115, 0, 0, 4553, 1480,0 }, + { 116, 116, 0, 0, 3740, 1260,0 }, + { 117, 117, 0, 0, 7240, 2300,0 }, + { 118, 118, 0, 0, 3020, 73,0 }, + { 119, 119, 0, 0, 1626, 800,0 }, + { 120, 120, 0, 0, 2466, 620,0 }, + { 121, 121, 0, 0, 12053, 3160,0 }, + { 122, 122, 0, 0, 466, 120,0 }, + { 123, 123, 0, 0, 1000, 320,0 }, + { 124, 124, 0, 0, 380, 60,0 }, + { 125, 125, 0, 0, 40000, 200,0 }, + { 126, 126, 0, 0, 560, 86,0 }, + { 127, 127, 35, 0, 386, 160,0 }, + { 128, 128, 52, 0, 126, 26,0 }, + { 129, 129, 48, 0, 286, 126,0 }, + { 130, 130, 58, 0, 173, 93,0 }, + { 129, 129, 60, 0, 286, 126,0 }, + { 131, 131, 47, 0, 520, 200,0 }, + { 132, 132, 43, 0, 173, 93,0 }, + { 131, 131, 49, 0, 520, 200,0 }, + { 133, 133, 43, 0, 160, 80,0 }, + { 131, 131, 51, 0, 526, 206,0 }, + { 134, 134, 43, 0, 1860, 653,0 }, + { 131, 131, 54, 0, 520, 200,0 }, + { 131, 131, 57, 0, 520, 200,0 }, + { 135, 135, 72, 0, 1860, 633,0 }, + { 131, 131, 60, 0, 506, 200,0 }, + { 136, 136, 76, 0, 1566, 546,0 }, + { 137, 137, 84, 0, 1340, 466,0 }, + { 138, 138, 36, 0, 1220, 433,0 }, + { 139, 139, 65, 0, 293, 133,0 }, + { 140, 140, 84, 0, 1333, 460,0 }, + { 141, 141, 83, 0, 220, 113,0 }, + { 135, 135, 84, 0, 1366, 473,0 }, + { 142, 142, 24, 0, 1893, 633,0 }, + { 136, 136, 77, 0, 1586, 553,0 }, + { 143, 143, 60, 0, 173, 93,0 }, + { 144, 144, 65, 0, 213, 126,0 }, + { 145, 145, 59, 0, 173, 0,0 }, + { 146, 146, 51, 0, 173, 100,0 }, + { 147, 147, 45, 0, 260, 206,0 }, + { 148, 148, 71, 0, 433, 180,0 }, + { 149, 149, 60, 0, 280, 26,0 }, + { 150, 150, 58, 0, 500, 186,0 }, + { 151, 151, 53, 0, 513, 200,0 }, + { 152, 152, 64, 0, 220, 86,0 }, + { 153, 153, 71, 0, 106, 46,0 }, + { 154, 154, 61, 0, 993, 340,0 }, + { 155, 155, 61, 0, 1906, 640,0 }, + { 156, 156, 44, 0, 206, 86,0 }, + { 157, 157, 40, 0, 586, 140,0 }, + { 158, 158, 69, 0, 126, 140,0 }, + { 159, 159, 68, 0, 126, 140,0 }, + { 160, 160, 63, 0, 146, 166,0 }, + { 161, 161, 74, 0, 280, 100,0 }, + { 162, 162, 60, 0, 1026, 320,0 }, + { 163, 163, 80, 0, 226, 100,0 }, + { 164, 164, 64, 0, 2713, 913,0 }, + { 165, 165, 72, 0, 120, 66,0 }, + { 166, 166, 73, 0, 386, 80,0 }, + { 167, 167, 70, 0, 553, 306,0 }, + { 168, 168, 68, 0, 126, 140,0 }, + { 169, 169, 48, 0, 386, 373,0 }, + { 131, 131, 53, 0, 520, 206,0 }, + { 170, 170, 0, 0, 40000, 0,0 }, + { 171, 171, 0, 0, 40000, 73,0 }, + { 172, 173, 0, 4, 5886, 100,0 }, + { 174, 175, 0, 4, 6913, 0,0 }, + { 176, 177, 0, 4, 4873, 0,0 }, + { 178, 178, 0, 0, 40000, 0,0 }, + { 179, 180, 0, 4, 4653, 433,0 }, + { 181, 181, 0, 0, 2280, 746,0 }, + { 182, 182, 0, 0, 40000, 0,0 }, + { 183, 184, 0, 4, 626, 0,0 }, + { 185, 186, 0, 4, 4653, 1546,0 }, + { 187, 187, 0, 0, 1166, 400,0 }, + { 188, 189, 0, 4, 40000, 60,0 }, + { 190, 191, 0, 4, 40000, 60,0 }, + { 192, 193, 0, 4, 40000, 73,0 }, + { 194, 194, 0, 0, 40000, 73,0 }, + { 195, 196, 0, 4, 40000, 66,0 }, + { 197, 198, 0, 4, 40000, 86,0 }, + { 199, 200, 0, 4, 40000, 66,0 }, + { 201, 202, 0, 4, 3713, 100,0 }, + { 203, 204, 0, 4, 14753, 126,0 }, + { 205, 206, 0, 4, 9286, 146,0 }, + { 207, 208, 0, 4, 14713, 126,0 }, + { 209, 210, 0, 4, 4653, 0,0 }, + { 211, 212, 0, 4, 40000, 66,0 }, + { 213, 213, 0, 0, 40000, 73,0 }, + { 214, 215, 0, 4, 626, 0,0 }, + { 216, 217, 0, 4, 4066, 100,0 }, + { 218, 219, 0, 4, 14586, 193,0 }, + { 220, 221, 0, 4, 2813, 106,0 }, + { 222, 223, 0, 4, 500, 0,0 }, + { 224, 224, 0, 0, 40000, 0,0 }, + { 225, 226, 0, 4, 7993, 93,0 }, + { 227, 227, 0, 0, 40000, 0,0 }, + { 228, 228, 0, 0, 40000, 133,0 }, + { 229, 230, 0, 4, 720, 213,0 }, + { 231, 232, 0, 4, 40000, 146,0 }, + { 233, 234, 0, 4, 40000, 0,0 }, + { 235, 236, 0, 4, 1000, 340,0 }, + { 235, 237, 0, 4, 3280, 1120,0 }, + { 46, 238, 0, 4, 6920, 0,0 }, + { 239, 240, 0, 4, 40000, 140,0 }, + { 241, 242, 0, 4, 40000, 146,0 }, + { 243, 243, 0, 0, 40000, 100,0 }, + { 244, 244, 0, 0, 40000, 60,0 }, + { 245, 245, 0, 0, 40000, 73,0 }, + { 246, 247, 0, 4, 720, 106,0 }, + { 248, 249, 0, 4, 40000, 126,0 }, + { 250, 250, 0, 0, 40000, 0,0 }, + { 251, 251, 0, 0, 40000, 126,0 }, + { 252, 253, 0, 4, 40000, 66,0 }, + { 254, 255, 0, 4, 40000, 93,0 }, + { 256, 257, 0, 4, 40000, 73,0 }, + { 258, 259, 0, 4, 40000, 86,0 }, + { 260, 261, 0, 4, 40000, 93,0 }, + { 262, 263, 0, 4, 40000, 80,0 }, + { 264, 265, 0, 4, 40000, 200,0 }, + { 266, 267, 0, 4, 40000, 73,0 }, + { 268, 269, 0, 4, 40000, 80,0 }, + { 270, 271, 0, 4, 40000, 73,0 }, + { 272, 273, 0, 4, 40000, 126,0 }, + { 274, 275, 0, 4, 40000, 100,0 }, + { 276, 276, 0, 0, 40000, 113,0 }, + { 277, 278, 0, 4, 40000, 186,0 }, + { 279, 280, 0, 4, 40000, 160,0 }, + { 281, 282, 0, 4, 40000, 206,0 }, + { 283, 283, 0, 0, 40000, 80,0 }, + { 284, 285, 0, 4, 40000, 73,0 }, + { 286, 287, 0, 4, 40000, 73,0 }, + { 288, 288, 0, 0, 40000, 93,0 }, + { 289, 290, 0, 4, 40000, 66,0 }, + { 291, 292, 0, 4, 40000, 153,0 }, + { 293, 294, 0, 4, 40000, 153,0 }, + { 295, 296, 0, 4, 40000, 320,0 }, + { 88, 297, 0, 4, 40000, 1280,0 }, + { 298, 299, 0, 4, 40000, 266,0 }, + { 300, 301, 0, 4, 40000, 1180,0 }, + { 302, 302, 0, 0, 40000, 286,0 }, + { 303, 303, 0, 0, 40000, 140,0 }, + { 304, 304, 0, 0, 13246, 2473,0 }, + { 305, 306, 0, 4, 40000, 1073,0 }, + { 307, 307, 0, 0, 9233, 240,0 }, + { 308, 308, 0, 0, 1186, 406,0 }, + { 309, 309, 0, 0, 40000, 1306,0 }, + { 310, 310, 0, 0, 40000, 933,0 }, + { 311, 312, 0, 4, 9146, 240,0 }, + { 313, 314, 0, 4, 7306, 326,0 }, + { 315, 316, 0, 4, 3586, 326,0 }, + { 317, 318, 0, 4, 7180, 0,0 }, + { 107, 319, 0, 4, 1180, 406,0 }, + { 108, 320, 0, 4, 40000, 66,0 }, + { 109, 321, 0, 4, 720, 213,0 }, + { 322, 323, 0, 4, 40000, 73,0 }, + { 324, 325, 0, 4, 613, 246,0 }, + { 326, 327, 0, 4, 1213, 386,0 }, + { 328, 328, 0, 0, 173, 106,0 }, + { 329, 329, 0, 0, 966, 333,0 }, + { 330, 331, 0, 4, 1906, 320,0 }, + { 332, 332, 0, 0, 3120, 73,0 }, + { 333, 333, 0, 0, 226, 73,0 }, + { 334, 334, 0, 0, 6600, 806,0 }, + { 335, 335, 0, 0, 273, 60,0 }, + { 336, 336, 0, 0, 12053, 660,0 }, + { 337, 337, 0, 0, 40000, 240,0 }, + { 338, 339, 0, 6, 6, 0,0 }, + { 340, 341, 0, 4, 560, 0,0 }, + { 342, 342, 35, 0, 40000, 0,0 }, + { 343, 343, 0, 0, 180, 100,0 }, + { 344, 344, 35, 0, 340, 146,0 }, + { 345, 345, 35, 0, 213, 33,0 }, + { 346, 346, 50, 0, 306, 20,0 }, + { 347, 347, 18, 0, 420, 146,0 }, + { 348, 348, 72, 0, 173, 86,0 }, + { 349, 349, 74, 0, 160, 93,0 }, + { 350, 350, 35, 0, 380, 146,0 }, + { 351, 351, 16, 0, 1206, 420,0 }, + { 352, 352, 0, 2, 6, 0,0 }, + { 353, 353, 38, 0, 200, 106,0 }, + { 354, 354, 38, 0, 346, 146,0 }, + { 355, 355, 31, 0, 406, 20,0 }, + { 355, 355, 35, 0, 406, 66,0 }, + { 355, 355, 38, 0, 406, 66,0 }, + { 355, 355, 41, 0, 406, 66,0 }, + { 355, 355, 45, 0, 306, 73,0 }, + { 355, 355, 50, 0, 306, 73,0 }, + { 356, 356, 36, 0, 1373, 493,0 }, + { 357, 357, 36, 0, 146, 33,0 }, + { 358, 358, 48, 0, 213, 86,0 }, + { 358, 358, 36, 0, 246, 86,0 }, + { 359, 359, 36, 0, 113, 0,0 }, + { 360, 360, 0, 0, 133, 40,0 }, + { 361, 361, 61, 0, 180, 26,0 }, + { 362, 362, 96, 0, 706, 266,0 }, + { 363, 363, 38, 0, 520, 193,0 }, + { 127, 127, 16, 0, 620, 233,0 }, + { 364, 365, 18, 4, 200, 0,0 }, + { 366, 366, 30, 0, 406, 246,0 }, + { 367, 368, 35, 4, 200, 0,0 }, + { 129, 129, 0, 0, 353, 153,0 }, + { 369, 369, 0, 0, 213, 13,0 }, + { 370, 370, 88, 0, 333, 113,0 }, + { 371, 371, 88, 0, 140, 73,0 }, + { 372, 372, 79, 0, 2540, 1040,0 }, + { 135, 135, 14, 0, 9213, 3066,0 }, + { 373, 373, 46, 0, 1093, 60,0 }, + { 374, 375,129, 4, 1200, 433,0 }, + { 376, 376, 58, 0, 1600, 726,0 }, + { 377, 377,164, 0, 526, 820,0 }, + { 378, 378,142, 0, 9153, 3073,0 }, + { 379, 379, 9, 0, 200, 100,0 }, + { 380, 381, 35, 4, 2353, 813,0 }, + { 382, 382, 28, 0, 1060, 120,0 }, + { 383, 383, 46, 0, 953, 20,0 }, + { 384, 384, 60, 0, 440, 160,0 }, + { 384, 384, 54, 0, 513, 180,0 }, + { 385, 385, 72, 0, 253, 120,0 }, + { 385, 385, 67, 0, 253, 113,0 }, + { 385, 385, 60, 0, 253, 106,0 }, + { 386, 386, 1, 0, 966, 613,0 }, + { 387, 387, 77, 0, 340, 86,0 }, + { 387, 387, 72, 0, 340, 86,0 }, + { 388, 388, 90, 0, 213, 86,0 }, + { 389, 389, 39, 0, 266, 73,0 }, + { 390, 390, 36, 0, 593, 73,0 }, + { 391, 392, 35, 4, 173, 46,0 }, + { 391, 393, 35, 4, 460, 66,0 }, + { 394, 394, 60, 0, 173, 20,0 }, + { 328, 328, 7, 0, 173, 0,0 }, + { 395, 395, 90, 0, 193, 20,0 }, + { 396, 396, 90, 0, 793, 40,0 }, + { 397, 397, 35, 0, 253, 86,0 }, + { 398, 399, 5, 4, 1913, 226,0 }, + { 400, 400,103, 0, 713, 273,0 }, + { 401, 401, 3, 0, 100, 0,0 }, + { 169, 169, 1, 0, 466, 413,0 }, + { 131, 131, 0, 0, 613, 226,0 }, + { 402, 402, 36, 0, 273, 53,0 }, + { 403, 403, 60, 0, 40000, 73,0 }, + { 404, 404, 37, 0, 1193, 426,0 }, + { 405, 405, 36, 0, 406, 20,0 }, + { 406, 406, 32, 0, 146, 73,0 }, + { 407, 407, 50, 0, 40000, 0,0 }, + { 408, 408, 50, 0, 793, 346,0 }, + { 409, 409, 83, 0, 120, 13,0 }, + { 410, 410, 72, 0, 433, 0,0 }, + { 148, 148, 59, 0, 513, 200,0 }, + { 411, 411, 64, 0, 173, 93,0 }, + { 411, 411, 60, 0, 173, 93,0 }, + { 412, 412, 72, 0, 160, 93,0 }, + { 412, 412, 62, 0, 173, 93,0 }, + { 413, 413, 83, 0, 773, 60,0 }, + { 414, 414, 0, 0, 40000, 80,0 }, + { 415, 415, 0, 0, 40000, 0,0 }, + { 416, 416, 0, 0, 40000, 73,0 }, + { 417, 417, 0, 0, 40000, 86,0 }, + { 418, 418, 0, 0, 40000, 0,0 }, + { 419, 419, 0, 0, 3440, 100,0 }, + { 420, 420, 0, 0, 3913, 420,0 }, + { 421, 421, 0, 0, 13620, 4640,0 }, + { 422, 422, 0, 0, 9233, 240,0 }, + { 423, 423, 0, 0, 633, 233,0 }, + { 424, 424, 0, 0, 4660, 1573,0 }, + { 425, 425, 0, 0, 4480, 1413,0 }, + { 426, 426, 0, 0, 40000, 0,0 }, + { 427, 427, 0, 0, 40000, 86,0 }, + { 428, 428, 60, 2, 6, 0,0 }, + { 429, 429, 73, 0, 593, 86,0 }, + { 429, 429, 74, 0, 593, 86,0 }, + { 429, 429, 80, 0, 593, 86,0 }, + { 429, 429, 84, 0, 593, 86,0 }, + { 429, 429, 92, 0, 520, 86,0 }, + { 430, 430, 81, 0, 786, 80,0 }, + { 430, 430, 83, 0, 786, 80,0 }, + { 430, 430, 95, 0, 680, 80,0 }, + { 431, 431, 35, 0, 593, 140,0 }, + { 432, 432, 60, 0, 213, 133,0 }, + { 357, 357, 59, 0, 113, 0,0 }, + { 432, 432, 44, 0, 213, 133,0 }, + { 433, 433, 41, 0, 713, 273,0 }, + { 434, 434, 97, 0, 113, 46,0 }, + { 433, 433, 44, 0, 513, 206,0 }, + { 433, 433, 48, 0, 506, 200,0 }, + { 435, 435, 96, 0, 700, 86,0 }, + { 433, 433, 51, 0, 520, 200,0 }, + { 433, 433, 54, 0, 513, 206,0 }, + { 436, 436, 40, 0, 1506, 793,0 }, + { 433, 433, 57, 0, 380, 160,0 }, + { 437, 437, 58, 0, 1600, 726,0 }, + { 438, 438, 97, 0, 233, 106,0 }, + { 439, 439, 50, 0, 186, 93,0 }, + { 437, 437, 60, 0, 1573, 713,0 }, + { 440, 440, 53, 0, 180, 73,0 }, + { 441, 441, 46, 0, 173, 126,0 }, + { 440, 440, 57, 0, 180, 40,0 }, + { 442, 442, 42, 0, 640, 240,0 }, + { 442, 442, 37, 0, 633, 233,0 }, + { 443, 443, 41, 0, 626, 240,0 }, + { 443, 443, 37, 0, 620, 233,0 }, + { 444, 444, 77, 0, 173, 40,0 }, + { 444, 444, 72, 0, 173, 40,0 }, + { 445, 445, 70, 0, 233, 100,0 }, + { 445, 445, 90, 0, 233, 93,0 }, + { 446, 446, 46, 0, 133, 73,0 }, + { 447, 447, 48, 0, 333, 73,0 }, + { 448, 448, 85, 0, 106, 0,0 }, + { 449, 449, 66, 0, 180, 26,0 }, + { 449, 449, 61, 0, 180, 26,0 }, + { 450, 450, 41, 0, 200, 66,0 }, + { 451, 451, 41, 0, 253, 66,0 }, + { 452, 452, 81, 0, 253, 26,0 }, + { 400, 400, 81, 0, 820, 306,0 }, + { 400, 400, 76, 0, 813, 300,0 }, + { 359, 359, 60, 0, 100, 0,0 }, + { 453, 453, 53, 0, 40000, 0,0 }, + { 454, 454, 0, 2, 6, 0,0 }, + { 455, 455, 0, 0, 200, 20,0 }, + { 456, 456, 0, 0, 4480, 100,0 }, + { 457, 457, 0, 0, 1180, 406,0 }, + { 458, 458, 0, 0, 40000, 86,0 }, + { 459, 459, 0, 0, 40000, 73,0 }, + { 460, 460, 0, 0, 3700, 66,0 }, + { 461, 461, 0, 0, 40000, 0,0 }, + { 462, 462, 0, 0, 6746, 2606,0 }, + { 463, 463, 0, 0, 40000, 213,0 }, + { 464, 464, 0, 0, 40000, 66,0 }, + { 465, 465, 0, 0, 40000, 100,0 }, + { 466, 466, 0, 0, 40000, 100,0 }, + { 467, 467, 0, 0, 5840, 806,0 }, + { 468, 468, 0, 0, 40000, 0,0 }, + { 469, 469, 0, 0, 40000, 0,0 }, + { 470, 470, 0, 0, 40000, 73,0 }, + { 471, 471, 0, 0, 40000, 133,0 }, + { 472, 472, 0, 0, 3320, 800,0 }, + { 473, 473, 0, 0, 40000, 173,0 }, + { 474, 474, 0, 0, 40000, 193,0 }, + { 475, 475, 0, 0, 2373, 800,0 }, + { 476, 476, 0, 0, 40000, 4986,0 }, + { 477, 477, 0, 0, 1180, 413,0 }, + { 478, 478, 0, 0, 3673, 1200,0 }, + { 479, 479, 0, 0, 973, 800,0 }, + { 480, 480, 0, 0, 7233, 2286,0 }, + { 481, 481, 0, 0, 40000, 73,0 }, + { 482, 482, 0, 0, 2526, 73,0 }, + { 483, 483, 0, 0, 393, 126,0 }, + { 484, 484, 0, 0, 40000, 200,0 }, + { 485, 485, 0, 0, 40000, 546,0 }, + { 486, 486, 0, 0, 1186, 413,0 }, + { 487, 487, 0, 0, 14166, 320,0 }, + { 488, 488, 0, 0, 8326, 646,0 }, + { 489, 489, 0, 0, 513, 206,0 }, + { 490, 490, 0, 0, 40000, 93,0 }, + { 491, 491, 50, 0, 1406, 353,0 }, + { 492, 492, 37, 0, 1040, 400,0 }, + { 493, 493, 39, 0, 406, 73,0 }, + { 494, 494, 39, 0, 3746, 860,0 }, + { 495, 495, 86, 0, 2133, 173,0 }, + { 496, 496, 43, 0, 140, 66,0 }, + { 127, 127, 24, 0, 513, 206,0 }, + { 127, 127, 29, 0, 520, 206,0 }, + { 497, 497, 50, 0, 340, 20,0 }, + { 498, 498, 30, 0, 5306, 1266,0 }, + { 498, 498, 33, 0, 3773, 886,0 }, + { 498, 498, 38, 0, 3746, 860,0 }, + { 498, 498, 42, 0, 3793, 906,0 }, + { 499, 499, 24, 0, 266, 0,0 }, + { 499, 499, 27, 0, 260, 153,0 }, + { 499, 499, 29, 0, 260, 153,0 }, + { 499, 499, 32, 0, 260, 153,0 }, + { 500, 500, 32, 0, 106, 0,0 }, + { 501, 501, 53, 0, 373, 186,0 }, + { 501, 501, 57, 0, 380, 193,0 }, + { 502, 502, 60, 0, 286, 133,0 }, + { 503, 503, 55, 0, 460, 126,0 }, + { 486, 486, 85, 0, 813, 293,0 }, + { 504, 504, 90, 0, 1580, 546,0 }, + { 505, 505, 84, 0, 246, 120,0 }, + { 506, 506, 48, 0, 826, 646,0 }, + { 507, 507, 48, 0, 266, 213,0 }, + { 132, 132, 72, 0, 126, 66,0 }, + { 508, 508, 72, 0, 106, 0,0 }, + { 509, 509, 72, 0, 100, 0,0 }, + { 510, 510, 63, 0, 1860, 633,0 }, + { 510, 510, 65, 0, 1853, 633,0 }, + { 511, 511, 79, 0, 1573, 553,0 }, + { 512, 512, 38, 0, 520, 793,0 }, + { 513, 513, 94, 0, 380, 160,0 }, + { 514, 514, 87, 0, 433, 306,0 }, + { 514, 514, 94, 0, 380, 273,0 }, + { 515, 515, 80, 0, 546, 273,0 }, + { 516, 516, 47, 0, 506, 200,0 }, + { 517, 517, 61, 0, 286, 133,0 }, + { 517, 517, 68, 0, 246, 120,0 }, + { 518, 518, 61, 0, 513, 206,0 }, + { 518, 518, 68, 0, 433, 180,0 }, + { 499, 499, 60, 0, 220, 133,0 }, + { 519, 519, 60, 0, 153, 46,0 }, + { 520, 520, 36, 0, 200, 20,0 }, + { 520, 520, 60, 0, 173, 20,0 }, + { 521, 521, 60, 0, 173, 20,0 }, + { 522, 522, 68, 0, 126, 26,0 }, + { 523, 523, 71, 0, 160, 186,0 }, + { 523, 523, 72, 0, 160, 186,0 }, + { 524, 524,101, 0, 966, 353,0 }, + { 525, 525, 36, 0, 3333, 480,0 }, + { 526, 526, 25, 0, 40000, 2293,0 }, + { 527, 527, 37, 0, 2106, 426,0 }, + { 528, 528, 36, 0, 720, 266,0 }, + { 528, 528, 41, 0, 713, 266,0 }, + { 529, 529, 84, 0, 173, 60,0 }, + { 530, 530, 54, 0, 40000, 0,0 }, + { 481, 481, 48, 0, 40000, 73,0 }, + { 531, 531, 0, 0, 10060, 1266,0 }, + { 532, 532, 0, 0, 4600, 606,0 }, + { 533, 533, 0, 0, 40000, 253,0 }, + { 534, 534, 0, 0, 40000, 73,0 }, + { 535, 535, 0, 0, 40000, 66,0 }, + { 536, 536, 0, 0, 40000, 80,0 }, + { 537, 537, 0, 0, 9413, 1393,0 }, + { 538, 538, 0, 0, 9000, 66,0 }, + { 539, 539, 0, 0, 40000, 0,0 }, + { 540, 540, 0, 0, 40000, 80,0 }, + { 541, 541, 0, 0, 40000, 120,0 }, + { 542, 542, 0, 0, 253, 73,0 }, + { 543, 543, 0, 0, 40000, 73,0 }, + { 544, 544, 0, 0, 18280, 800,0 }, + { 545, 545, 0, 0, 40000, 1133,0 }, + { 546, 546, 0, 0, 40000, 1226,0 }, + { 547, 547, 0, 0, 40000, 153,0 }, + { 135, 135, 49, 0, 3633, 1186,0 }, + { 548, 548, 35, 0, 2193, 80,0 }, + { 549, 549, 41, 0, 73, 0,0 }, + { 366, 366, 38, 0, 406, 246,0 }, + { 550, 550, 39, 0, 106, 20,0 }, + { 551, 551, 49, 0, 200, 133,0 }, + { 408, 408, 59, 0, 780, 326,0 }, + { 552, 552, 24, 0, 40000, 0,0 }, + { 552, 552, 27, 0, 40000, 0,0 }, + { 552, 552, 29, 0, 40000, 0,0 }, + { 552, 552, 32, 0, 40000, 0,0 }, + { 553, 553, 84, 0, 200, 33,0 }, + { 512, 512, 79, 0, 346, 460,0 }, + { 554, 554, 61, 0, 400, 126,0 }, + { 554, 554, 68, 0, 353, 120,0 }, + { 555, 555, 36, 0, 146, 86,0 }, + { 555, 555, 60, 0, 113, 0,0 }, + { 556, 556, 36, 0, 273, 53,0 }, + { 115, 115, 37, 0, 4580, 1513,0 }, + { 557, 557, 0, 0, 3806, 73,0 }, + { 558, 558, 0, 0, 40000, 0,0 }, + { 559, 559, 0, 0, 40000, 66,0 }, + { 560, 560, 0, 0, 5886, 133,0 }, + { 561, 561, 0, 0, 253, 26,0 }, + { 562, 562, 0, 0, 3246, 753,0 }, + { 563, 563, 0, 0, 40000, 100,0 }, + { 564, 564, 0, 0, 1620, 366,0 }, + { 565, 565, 0, 0, 40000, 0,0 }, + { 566, 566, 0, 0, 40000, 0,0 }, + { 567, 567, 0, 0, 40000, 0,0 }, + { 568, 568, 0, 0, 40000, 80,0 }, + { 569, 569, 0, 0, 760, 340,0 }, + { 570, 570, 0, 0, 40000, 0,0 }, + { 571, 571, 0, 0, 40000, 0,0 }, + { 572, 572, 0, 0, 40000, 0,0 }, + { 356, 356, 0, 0, 1893, 646,0 }, + { 573, 573, 0, 0, 40000, 93,0 }, + { 574, 574, 0, 0, 40000, 93,0 }, + { 575, 575, 0, 0, 40000, 200,0 }, + { 576, 576, 0, 0, 40000, 200,0 }, + { 577, 577, 0, 0, 40000, 126,0 }, + { 578, 578, 0, 0, 40000, 353,0 }, + { 579, 579, 0, 0, 40000, 346,0 }, + { 580, 580, 0, 0, 40000, 353,0 }, + { 581, 581, 0, 0, 40000, 100,0 }, + { 582, 582, 0, 0, 40000, 133,0 }, + { 583, 583, 0, 0, 2286, 713,0 }, + { 584, 584, 0, 0, 40000, 193,0 }, + { 585, 585, 0, 0, 40000, 0,0 }, + { 516, 516, 0, 0, 633, 240,0 }, + { 586, 586, 0, 0, 40000, 73,0 }, + { 587, 587, 0, 0, 40000, 73,0 }, + { 588, 588, 0, 0, 40000, 73,0 }, + { 498, 498, 26, 0, 5293, 1253,0 }, + { 494, 494, 35, 0, 3800, 913,0 }, + { 350, 350, 41, 0, 380, 153,0 }, + { 353, 353, 48, 0, 173, 100,0 }, + { 354, 354, 67, 0, 246, 120,0 }, + { 502, 502, 24, 0, 340, 146,0 }, + { 346, 346, 36, 0, 406, 73,0 }, + { 346, 346, 38, 0, 406, 20,0 }, + { 346, 346, 40, 0, 406, 73,0 }, + { 346, 346, 42, 0, 406, 20,0 }, + { 346, 346, 44, 0, 306, 20,0 }, + { 510, 510, 55, 0, 1866, 646,0 }, + { 346, 346, 46, 0, 306, 20,0 }, + { 136, 136, 80, 0, 1600, 573,0 }, + { 486, 486, 24, 0, 1193, 426,0 }, + { 153, 153, 50, 0, 106, 40,0 }, + { 346, 346, 24, 0, 540, 73,0 }, + { 516, 516, 31, 0, 626, 240,0 }, + { 498, 498, 35, 0, 3760, 880,0 }, + { 517, 517, 60, 0, 286, 133,0 }, + { 530, 530, 36, 0, 40000, 0,0 }, + { 530, 530, 48, 0, 40000, 0,0 }, + { 589, 589, 0, 0, 40000, 0,0 }, + { 139, 139, 76, 0, 253, 106,0 }, + { 156, 156, 48, 0, 206, 80,0 }, + { 157, 157, 48, 0, 426, 106,0 }, + { 165, 165, 69, 0, 120, 66,0 }, + { 167, 167, 75, 0, 546, 306,0 }, + { 590, 590, 0, 0, 40000, 0,0 }, + { 591, 591, 0, 0, 15486, 1580,0 }, + { 592, 592, 0, 0, 3446, 106,0 }, + { 593, 593, 0, 0, 1926, 146,0 }, + { 594, 594, 0, 0, 7293, 2380,0 }, + { 595, 595, 0, 0, 7613, 1566,0 }, + { 596, 596, 0, 0, 1153, 460,0 }, + { 597, 597, 0, 0, 1166, 400,0 }, + { 598, 598, 0, 0, 40000, 73,0 }, + { 599, 599, 0, 0, 40000, 766,0 }, + { 600, 600, 0, 0, 40000, 80,0 }, + { 601, 601, 0, 0, 1840, 513,0 }, + { 602, 602, 0, 0, 40000, 0,0 }, + { 603, 603, 0, 0, 4480, 733,0 }, + { 604, 604, 0, 0, 18226, 786,0 }, + { 605, 605, 0, 0, 4333, 233,0 }, + { 606, 606, 0, 0, 40000, 106,0 }, + { 607, 607, 0, 0, 40000, 366,0 }, + { 608, 608, 0, 0, 40000, 200,0 }, + { 609, 609, 0, 0, 713, 200,0 }, + { 610, 610, 0, 0, 8866, 1366,0 }, + { 611, 611, 0, 0, 2300, 73,0 }, + { 612, 612, 0, 0, 40000, 126,0 }, + { 613, 613, 0, 0, 40000, 1413,0 }, + { 614, 614, 0, 0, 40000, 333,0 }, + { 615, 615, 0, 0, 40000, 333,0 }, + { 616, 616, 0, 0, 40000, 26,0 }, + { 617, 617, 0, 0, 40000, 40,0 }, + { 618, 618, 0, 0, 4240, 353,0 }, + { 619, 619, 0, 0, 40000, 0,0 }, + { 620, 620, 0, 0, 40000, 73,0 }, + { 621, 621, 0, 0, 9020, 60,0 }, + { 622, 622, 0, 0, 3020, 0,0 }, + { 623, 623, 0, 0, 40000, 60,0 }, + { 624, 624, 0, 0, 40000, 73,0 }, + { 625, 625, 0, 0, 40000, 60,0 }, + { 626, 626, 0, 0, 40000, 53,0 }, + { 627, 627, 0, 0, 40000, 0,0 }, + { 628, 628, 0, 0, 40000, 66,0 }, + { 629, 629, 0, 0, 40000, 66,0 }, + { 630, 630, 0, 0, 5913, 426,0 }, + { 631, 631, 0, 0, 40000, 246,0 }, + { 632, 632, 0, 0, 40000, 206,0 }, + { 633, 633, 0, 0, 40000, 0,0 }, + { 634, 634, 0, 0, 2453, 780,0 }, + { 635, 635, 0, 0, 4740, 240,0 }, + { 636, 636, 0, 0, 1840, 353,0 }, + { 637, 637, 0, 0, 40000, 86,0 }, + { 638, 638, 0, 0, 3446, 1786,0 }, + { 346, 346, 0, 0, 540, 20,0 }, + { 639, 639, 0, 0, 7406, 2486,0 }, + { 404, 404, 0, 0, 1220, 466,0 }, + { 506, 506, 0, 0, 1000, 813,0 }, + { 639, 639, 60, 0, 2666, 913,0 }, + { 639, 639, 79, 0, 1366, 486,0 }, + { 640, 640, 65, 0, 2053, 646,0 }, + { 486, 486, 31, 0, 1206, 440,0 }, + { 486, 486, 36, 0, 1200, 433,0 }, + { 640, 640, 72, 0, 1713, 520,0 }, + { 136, 136, 79, 0, 1580, 560,0 }, + { 148, 148, 57, 0, 520, 206,0 }, + { 150, 150, 53, 0, 500, 193,0 }, + { 641, 641, 84, 0, 226, 66,0 }, + { 520, 520, 66, 0, 173, 20,0 }, + { 642, 642, 31, 0, 40000, 113,0 }, + { 642, 642, 29, 0, 40000, 113,0 }, + { 356, 356, 31, 0, 1366, 486,0 }, + { 356, 356, 19, 0, 1866, 633,0 }, + { 643, 643, 31, 0, 40000, 73,0 }, + { 643, 643, 29, 0, 40000, 73,0 }, + { 644, 644, 31, 0, 2286, 400,0 }, + { 644, 644, 35, 0, 2313, 420,0 }, + { 644, 644, 40, 0, 2353, 433,0 }, + { 644, 644, 47, 0, 1860, 346,0 }, + { 516, 516, 32, 0, 626, 240,0 }, + { 516, 516, 43, 0, 506, 200,0 }, + { 495, 495, 26, 0, 3180, 240,0 }, + { 495, 495, 44, 0, 2553, 206,0 }, + { 496, 496, 26, 0, 160, 73,0 }, + { 496, 496, 51, 0, 146, 66,0 }, + { 496, 496, 39, 0, 160, 73,0 }, + { 495, 495, 30, 0, 3180, 240,0 }, + { 645, 645, 44, 0, 1880, 653,0 }, + { 645, 645, 43, 0, 1886, 653,0 }, + { 646, 646, 0, 0, 2393, 833,0 }, + { 647, 647, 0, 0, 4693, 26,0 }, + { 648, 648, 0, 0, 2306, 773,0 }, + { 649, 649, 0, 0, 40000, 120,0 }, + { 650, 650, 0, 0, 40000, 66,0 }, + { 651, 651, 0, 0, 5866, 1206,0 }, + { 652, 652, 0, 0, 40000, 426,0 }, + { 653, 653, 0, 0, 1873, 633,0 }, + { 654, 654, 0, 0, 40000, 66,0 }, + { 655, 655, 0, 0, 40000, 73,0 }, + { 656, 656, 0, 0, 40000, 73,0 }, + { 657, 657, 0, 0, 40000, 0,0 }, + { 658, 658, 0, 0, 2040, 380,0 }, + { 659, 659, 0, 0, 40000, 73,0 }, + { 660, 660, 0, 0, 3720, 1260,0 }, + { 661, 661, 0, 0, 4080, 1046,0 }, + { 662, 662, 0, 0, 8693, 4666,0 }, + { 663, 663, 0, 0, 1926, 73,0 }, + { 664, 664, 0, 0, 8326, 646,0 }, + { 665, 665, 0, 0, 40000, 240,0 }, + { 666, 666, 0, 0, 40000, 226,0 }, + { 667, 667, 0, 0, 40000, 220,0 }, + { 668, 668, 0, 0, 40000, 0,0 }, + { 669, 669, 0, 0, 40000, 193,0 }, + { 670, 670, 0, 0, 880, 20,0 }, + { 671, 671, 0, 0, 4873, 120,0 }, + { 672, 672, 0, 0, 40000, 413,0 }, + { 673, 673, 0, 0, 700, 106,0 }, + { 674, 674, 0, 0, 700, 100,0 }, + { 675, 675, 0, 0, 40000, 126,0 }, + { 676, 676, 0, 0, 8113, 806,0 }, + { 677, 677, 0, 0, 8900, 80,0 }, + { 678, 678, 0, 0, 1893, 653,0 }, + { 679, 679, 0, 0, 3973, 206,0 }, + { 680, 680, 0, 0, 40000, 173,0 }, + { 681, 681, 0, 0, 40000, 73,0 }, + { 682, 682, 0, 0, 40000, 93,0 }, + { 683, 683, 0, 0, 1606, 640,0 }, + { 684, 684, 0, 0, 15486, 1580,0 }, + { 685, 685, 0, 0, 40000, 346,0 }, + { 686, 686, 0, 0, 40000, 786,0 }, + { 687, 687, 0, 0, 386, 240,0 }, + { 688, 688, 0, 0, 40000, 2066,0 }, + { 689, 689, 0, 0, 15453, 73,0 }, + { 690, 690, 0, 0, 1206, 240,0 }, + { 691, 691, 0, 0, 8866, 1366,0 }, + { 692, 692, 0, 0, 5913, 2253,0 }, + { 693, 693, 0, 0, 773, 106,0 }, + { 694, 694, 0, 0, 3793, 73,0 }, + { 695, 695, 0, 0, 40000, 73,0 }, + { 645, 645, 0, 0, 3633, 1180,0 }, + { 696, 696, 0, 0, 40000, 80,0 }, + { 697, 697, 0, 0, 40000, 0,0 }, + { 698, 698, 0, 0, 40000, 66,0 }, + { 699, 699, 0, 0, 40000, 66,0 }, + { 700, 700, 0, 0, 106, 0,0 }, + { 701, 701, 0, 0, 40000, 200,0 }, + { 702, 702, 0, 0, 3913, 73,0 }, + { 703, 703, 0, 0, 40000, 73,0 }, + { 704, 704, 0, 0, 40000, 73,0 }, + { 705, 705, 0, 0, 40000, 73,0 }, + { 706, 706, 0, 0, 40000, 66,0 }, + { 707, 707, 0, 0, 40000, 313,0 }, + { 708, 708, 0, 0, 40000, 100,0 }, + { 709, 709, 0, 0, 40000, 213,0 }, + { 710, 710, 0, 0, 40000, 53,0 }, + { 711, 711, 0, 0, 40000, 40,0 }, + { 712, 712, 0, 0, 40000, 73,0 }, + { 713, 713, 0, 0, 40000, 140,0 }, + { 714, 714, 0, 0, 40000, 606,0 }, + { 715, 715, 0, 0, 40000, 226,0 }, + { 716, 716, 0, 0, 3746, 1273,0 }, + { 717, 717, 0, 0, 40000, 80,0 }, + { 718, 718, 0, 0, 2360, 806,0 }, + { 719, 719, 0, 0, 1186, 420,0 }, + { 720, 720, 0, 0, 12533, 1953,0 }, + { 721, 721, 0, 0, 973, 1280,0 }, + { 722, 722, 0, 0, 40000, 426,0 }, + { 723, 723, 0, 0, 40000, 53,0 }, + { 724, 724, 0, 0, 40000, 66,0 }, + { 725, 725, 0, 0, 1246, 73,0 }, + { 726, 726, 0, 0, 3726, 1246,0 }, + { 727, 727, 0, 0, 2346, 813,0 }, + { 728, 728, 0, 0, 1206, 433,0 }, + { 507, 507, 0, 0, 306, 246,0 }, + { 512, 512, 0, 0, 526, 840,0 }, + { 729, 729, 0, 0, 14793, 4933,0 }, + { 730, 730, 0, 0, 14640, 4806,0 }, + { 731, 731, 0, 0, 5233, 633,0 }, + { 732, 732, 0, 0, 40000, 2513,0 }, + { 733, 733, 0, 0, 40000, 820,0 }, + { 734, 734, 0, 0, 40000, 0,0 }, + { 735, 735, 0, 0, 1726, 793,0 }, + { 736, 736, 0, 0, 513, 20,0 }, + { 737, 737, 0, 2, 6, 0,0 }, + { 738, 738, 38, 0, 1020, 413,0 }, + { 739, 739, 44, 0, 220, 33,0 }, + { 500, 500, 58, 0, 100, 0,0 }, + { 740, 740, 24, 0, 513, 206,0 }, + { 741, 741, 60, 0, 220, 26,0 }, + { 736, 736, 44, 0, 286, 20,0 }, + { 742, 742, 25, 0, 626, 246,0 }, + { 743, 743, 60, 0, 146, 86,0 }, + { 742, 742, 30, 0, 626, 240,0 }, + { 377, 377, 60, 0, 446, 626,0 }, + { 742, 742, 33, 0, 620, 226,0 }, + { 744, 744, 60, 0, 220, 113,0 }, + { 742, 742, 35, 0, 620, 233,0 }, + { 742, 742, 37, 0, 633, 246,0 }, + { 745, 745, 0, 0, 1880, 640,0 }, + { 742, 742, 40, 0, 640, 260,0 }, + { 746, 746,102, 0, 960, 300,0 }, + { 747, 747, 80, 0, 1106, 126,0 }, + { 377, 377, 0, 0, 500, 760,0 }, + { 748, 748, 56, 0, 100, 0,0 }, + { 749, 749, 0, 0, 973, 1300,0 }, + { 746, 746,100, 0, 960, 340,0 }, + { 750, 750, 40, 0, 626, 240,0 }, + { 750, 750, 35, 0, 626, 240,0 }, + { 751, 751, 29, 0, 206, 106,0 }, + { 750, 750, 29, 0, 633, 240,0 }, + { 750, 750, 22, 0, 640, 233,0 }, + { 500, 500, 0, 0, 106, 0,0 }, + { 752, 752, 0, 0, 206, 26,0 }, + { 753, 753, 84, 0, 166, 20,0 }, + { 754, 754, 84, 0, 1580, 553,0 }, + { 755, 755, 0, 0, 633, 233,0 }, + { 755, 755, 71, 0, 440, 180,0 }, + { 755, 755, 53, 0, 513, 200,0 }, + { 755, 755, 48, 0, 520, 206,0 }, + { 756, 756, 95, 0, 286, 20,0 }, + { 757, 757, 95, 0, 1880, 20,0 }, + { 758, 758, 0, 0, 14413, 333,0 }, + { 759, 759, 0, 0, 14453, 360,0 }, + { 760, 760, 0, 0, 14940, 353,0 }, + { 761, 761, 0, 0, 7286, 340,0 }, + { 762, 762, 0, 0, 14700, 60,0 }, + { 763, 763, 0, 0, 14506, 340,0 }, + { 764, 764, 0, 0, 14706, 200,0 }, + { 765, 765, 0, 0, 40000, 0,0 }, + { 766, 766, 0, 0, 2900, 426,0 }, + { 767, 767, 0, 0, 2986, 753,0 }, + { 768, 768, 0, 0, 1706, 680,0 }, + { 769, 769, 0, 0, 14646, 1253,0 }, + { 770, 770, 0, 0, 1713, 486,0 }, + { 771, 771, 0, 0, 966, 346,0 }, + { 772, 772, 0, 0, 3453, 766,0 }, + { 773, 773, 0, 0, 2866, 486,0 }, + { 774, 774, 0, 0, 40000, 73,0 }, + { 775, 775, 0, 0, 40000, 73,0 }, + { 776, 776, 0, 0, 40000, 166,0 }, + { 777, 777, 0, 0, 40000, 126,0 }, + { 778, 778, 0, 0, 40000, 113,0 }, + { 779, 779, 0, 0, 40000, 113,0 }, + { 780, 780, 0, 0, 40000, 93,0 }, + { 781, 781, 0, 0, 40000, 200,0 }, + { 782, 782, 0, 0, 7186, 93,0 }, + { 783, 783, 0, 0, 6406, 120,0 }, + { 784, 784, 0, 0, 40000, 0,0 }, + { 785, 785, 0, 0, 40000, 0,0 }, + { 786, 786, 0, 0, 1220, 73,0 }, + { 787, 787, 0, 0, 40000, 0,0 }, + { 788, 788, 0, 0, 17566, 66,0 }, + { 789, 789, 0, 0, 2333, 26,0 }, + { 790, 790, 0, 0, 4560, 153,0 }, + { 791, 791, 0, 0, 40000, 0,0 }, + { 792, 792, 0, 0, 40000, 0,0 }, + { 793, 793, 0, 0, 40000, 0,0 }, + { 794, 794, 0, 0, 2506, 126,0 }, + { 795, 795, 0, 0, 2513, 126,0 }, + { 796, 796, 0, 0, 40000, 0,0 }, + { 797, 797, 0, 0, 3386, 80,0 }, + { 798, 798, 0, 0, 40000, 100,0 }, + { 799, 799, 0, 0, 40000, 100,0 }, + { 800, 800, 0, 0, 40000, 120,0 }, + { 801, 801, 0, 0, 40000, 0,0 }, + { 802, 802, 0, 0, 40000, 200,0 }, + { 803, 803, 0, 0, 1080, 180,0 }, + { 804, 804, 0, 0, 3620, 1166,0 }, + { 805, 805, 0, 0, 1186, 393,0 }, + { 806, 806, 0, 0, 40000, 213,0 }, + { 807, 807, 0, 0, 40000, 426,0 }, + { 808, 808, 0, 0, 40000, 146,0 }, + { 809, 809, 0, 0, 40000, 146,0 }, + { 810, 810, 0, 0, 40000, 60,0 }, + { 811, 811, 0, 0, 40000, 113,0 }, + { 812, 812, 0, 0, 40000, 93,0 }, + { 813, 813, 0, 0, 1186, 153,0 }, + { 814, 814, 0, 0, 40000, 0,0 }, + { 815, 815, 0, 0, 40000, 80,0 }, + { 816, 816, 0, 0, 40000, 80,0 }, + { 817, 817, 0, 0, 40000, 46,0 }, + { 818, 818, 0, 0, 40000, 0,0 }, + { 819, 819, 0, 0, 40000, 66,0 }, + { 820, 820, 0, 0, 40000, 126,0 }, + { 821, 821, 0, 0, 40000, 213,0 }, + { 822, 822, 0, 0, 40000, 80,0 }, + { 823, 823, 0, 0, 40000, 73,0 }, + { 824, 824, 0, 0, 40000, 73,0 }, + { 825, 825, 0, 0, 40000, 100,0 }, + { 826, 826, 0, 0, 40000, 93,0 }, + { 827, 827, 0, 0, 40000, 73,0 }, + { 828, 828, 0, 0, 40000, 73,0 }, + { 829, 829, 0, 0, 40000, 80,0 }, + { 830, 830, 0, 0, 40000, 80,0 }, + { 831, 831, 0, 0, 40000, 80,0 }, + { 832, 832, 0, 0, 40000, 73,0 }, + { 833, 833, 0, 0, 40000, 80,0 }, + { 834, 834, 0, 0, 40000, 86,0 }, + { 835, 835, 0, 0, 40000, 100,0 }, + { 836, 836, 0, 0, 40000, 100,0 }, + { 837, 837, 0, 0, 40000, 140,0 }, + { 838, 838, 0, 0, 40000, 73,0 }, + { 839, 839, 0, 0, 40000, 0,0 }, + { 840, 840, 0, 0, 40000, 93,0 }, + { 841, 841, 0, 0, 40000, 0,0 }, + { 842, 842, 0, 0, 40000, 0,0 }, + { 843, 843, 0, 0, 40000, 73,0 }, + { 844, 844, 0, 0, 40000, 66,0 }, + { 845, 845, 0, 0, 40000, 0,0 }, + { 846, 846, 0, 0, 40000, 193,0 }, + { 847, 847, 0, 0, 40000, 340,0 }, + { 848, 848, 0, 0, 40000, 233,0 }, + { 849, 849, 0, 0, 40000, 80,0 }, + { 850, 850, 0, 0, 40000, 186,0 }, + { 851, 851, 0, 0, 9973, 426,0 }, + { 852, 852, 0, 0, 40000, 200,0 }, + { 853, 853, 0, 0, 40000, 400,0 }, + { 854, 854, 0, 0, 14633, 200,0 }, + { 855, 855, 0, 0, 40000, 333,0 }, + { 856, 856, 0, 0, 4620, 800,0 }, + { 857, 857, 0, 0, 8940, 386,0 }, + { 858, 858, 0, 0, 8966, 740,0 }, + { 859, 859, 0, 0, 40000, 273,0 }, + { 860, 860, 0, 0, 40000, 126,0 }, + { 861, 861, 0, 0, 40000, 400,0 }, + { 862, 862, 0, 0, 4480, 213,0 }, + { 863, 863, 0, 0, 633, 100,0 }, + { 864, 864, 0, 0, 3740, 353,0 }, + { 865, 865, 0, 0, 2333, 406,0 }, + { 866, 866, 0, 0, 1933, 566,0 }, + { 867, 867, 0, 0, 40000, 93,0 }, + { 868, 868, 0, 0, 40000, 106,0 }, + { 869, 869, 0, 0, 40000, 100,0 }, + { 870, 870, 0, 0, 3093, 240,0 }, + { 871, 871, 0, 0, 513, 93,0 }, + { 872, 872, 0, 0, 700, 180,0 }, + { 361, 361, 0, 0, 373, 40,0 }, + { 873, 873, 0, 0, 1046, 446,0 }, + { 874, 874, 0, 0, 1886, 520,0 }, + { 875, 875, 0, 0, 1226, 366,0 }, + { 876, 876, 0, 0, 4193, 73,0 }, + { 877, 877, 0, 0, 826, 120,0 }, + { 878, 878, 0, 0, 280, 146,0 }, + { 879, 879, 0, 0, 5266, 806,0 }, + { 880, 880, 0, 0, 386, 80,0 }, + { 881, 881, 0, 0, 40000, 100,0 }, + { 882, 882, 0, 0, 40000, 413,0 }, + { 883, 883, 0, 0, 40000, 0,0 }, + { 884, 884, 36, 0, 233, 80,0 }, + { 885, 885, 48, 0, 193, 93,0 }, + { 885, 885, 36, 0, 226, 100,0 }, + { 886, 886, 36, 0, 113, 0,0 }, + { 887, 887, 32, 0, 133, 40,0 }, + { 767, 767, 96, 0, 1760, 480,0 }, + { 888, 888, 30, 0, 246, 40,0 }, + { 889, 889, 35, 0, 420, 140,0 }, + { 890, 890, 60, 0, 240, 60,0 }, + { 884, 884, 59, 0, 146, 20,0 }, + { 890, 890, 44, 0, 240, 60,0 }, + { 891, 891, 41, 0, 713, 273,0 }, + { 892, 892, 47, 0, 173, 93,0 }, + { 891, 891, 44, 0, 513, 206,0 }, + { 891, 891, 48, 0, 506, 200,0 }, + { 893, 893, 62, 0, 1926, 93,0 }, + { 891, 891, 51, 0, 520, 200,0 }, + { 891, 891, 54, 0, 513, 206,0 }, + { 894, 894, 40, 0, 1280, 793,0 }, + { 891, 891, 57, 0, 380, 160,0 }, + { 895, 895, 97, 0, 233, 106,0 }, + { 896, 896, 50, 0, 220, 93,0 }, + { 376, 376, 60, 0, 1573, 713,0 }, + { 897, 897, 53, 0, 126, 73,0 }, + { 898, 898, 46, 0, 173, 133,0 }, + { 897, 897, 57, 0, 126, 33,0 }, + { 899, 899, 42, 0, 626, 233,0 }, + { 899, 899, 37, 0, 633, 240,0 }, + { 900, 900, 41, 0, 626, 240,0 }, + { 900, 900, 37, 0, 626, 240,0 }, + { 871, 871, 77, 0, 173, 40,0 }, + { 871, 871, 72, 0, 173, 40,0 }, + { 388, 388, 70, 0, 213, 86,0 }, + { 901, 901, 39, 0, 260, 26,0 }, + { 902, 902, 36, 0, 1093, 73,0 }, + { 903, 903, 46, 0, 120, 73,0 }, + { 904, 904, 48, 0, 766, 80,0 }, + { 905, 905, 85, 0, 126, 26,0 }, + { 361, 361, 66, 0, 180, 26,0 }, + { 906, 906, 41, 0, 193, 73,0 }, + { 907, 907, 41, 0, 333, 106,0 }, + { 908, 908, 81, 0, 160, 26,0 }, + { 400, 400, 10, 0, 1186, 413,0 }, + { 886, 886, 60, 0, 100, 0,0 }, + { 873, 873, 53, 0, 846, 360,0 }, + { 909, 909, 0, 0, 5593, 340,0 }, + { 910, 910, 0, 0, 14646, 346,0 }, + { 911, 911, 0, 0, 6826, 280,0 }, + { 912, 912, 0, 0, 7000, 306,0 }, + { 913, 913, 0, 0, 8793, 133,0 }, + { 914, 914, 0, 0, 14680, 346,0 }, + { 915, 915, 0, 0, 7246, 126,0 }, + { 916, 916, 0, 0, 40000, 0,0 }, + { 917, 917, 0, 0, 1866, 433,0 }, + { 362, 362, 0, 0, 1106, 340,0 }, + { 918, 918, 0, 0, 1053, 273,0 }, + { 919, 919, 0, 0, 14513, 1213,0 }, + { 920, 920, 0, 0, 1886, 646,0 }, + { 921, 921, 0, 0, 926, 313,0 }, + { 922, 922, 0, 0, 2340, 806,0 }, + { 923, 923, 0, 0, 2966, 553,0 }, + { 924, 924, 0, 0, 40000, 66,0 }, + { 925, 925, 0, 0, 40000, 73,0 }, + { 926, 926, 0, 0, 40000, 0,0 }, + { 927, 927, 0, 0, 40000, 126,0 }, + { 928, 928, 0, 0, 40000, 113,0 }, + { 929, 929, 0, 0, 40000, 113,0 }, + { 930, 930, 0, 0, 40000, 93,0 }, + { 931, 931, 0, 0, 40000, 113,0 }, + { 932, 932, 0, 0, 7200, 86,0 }, + { 933, 933, 0, 0, 5373, 106,0 }, + { 934, 934, 0, 0, 40000, 0,0 }, + { 935, 935, 0, 0, 40000, 0,0 }, + { 936, 936, 0, 0, 2380, 73,0 }, + { 937, 937, 0, 0, 40000, 0,0 }, + { 938, 938, 0, 0, 40000, 0,0 }, + { 939, 939, 0, 0, 6013, 53,0 }, + { 940, 940, 0, 0, 3713, 126,0 }, + { 941, 941, 0, 0, 17566, 26,0 }, + { 942, 942, 0, 0, 40000, 0,0 }, + { 943, 943, 0, 0, 40000, 0,0 }, + { 944, 944, 0, 0, 2506, 126,0 }, + { 945, 945, 0, 0, 3733, 73,0 }, + { 946, 946, 0, 0, 40000, 0,0 }, + { 947, 947, 0, 0, 3386, 80,0 }, + { 948, 948, 0, 0, 40000, 100,0 }, + { 949, 949, 0, 0, 40000, 100,0 }, + { 950, 950, 0, 0, 40000, 113,0 }, + { 951, 951, 0, 0, 40000, 0,0 }, + { 952, 952, 0, 0, 40000, 200,0 }, + { 953, 953, 0, 0, 1140, 213,0 }, + { 954, 954, 0, 0, 2140, 400,0 }, + { 955, 955, 0, 0, 813, 240,0 }, + { 956, 956, 0, 0, 40000, 100,0 }, + { 957, 957, 0, 0, 40000, 426,0 }, + { 958, 958, 0, 0, 40000, 0,0 }, + { 959, 959, 0, 0, 40000, 146,0 }, + { 960, 960, 0, 0, 40000, 120,0 }, + { 961, 961, 0, 0, 40000, 93,0 }, + { 962, 962, 0, 0, 1193, 153,0 }, + { 963, 963, 0, 0, 40000, 46,0 }, + { 964, 964, 0, 0, 40000, 80,0 }, + { 965, 965, 0, 0, 40000, 80,0 }, + { 966, 966, 0, 0, 40000, 20,0 }, + { 967, 967, 0, 0, 40000, 0,0 }, + { 968, 968, 0, 0, 40000, 93,0 }, + { 969, 969, 0, 0, 40000, 86,0 }, + { 970, 970, 0, 0, 40000, 213,0 }, + { 971, 971, 0, 0, 40000, 80,0 }, + { 972, 972, 0, 0, 40000, 73,0 }, + { 973, 973, 0, 0, 40000, 0,0 }, + { 974, 974, 0, 0, 40000, 93,0 }, + { 975, 975, 0, 0, 40000, 73,0 }, + { 976, 976, 0, 0, 40000, 73,0 }, + { 977, 977, 0, 0, 40000, 66,0 }, + { 978, 978, 0, 0, 40000, 66,0 }, + { 979, 979, 0, 0, 40000, 100,0 }, + { 980, 980, 0, 0, 40000, 73,0 }, + { 981, 981, 0, 0, 40000, 73,0 }, + { 982, 982, 0, 0, 40000, 80,0 }, + { 983, 983, 0, 0, 40000, 100,0 }, + { 984, 984, 0, 0, 40000, 100,0 }, + { 985, 985, 0, 0, 40000, 100,0 }, + { 986, 986, 0, 0, 40000, 80,0 }, + { 987, 987, 0, 0, 40000, 73,0 }, + { 988, 988, 0, 0, 40000, 0,0 }, + { 989, 989, 0, 0, 40000, 86,0 }, + { 990, 990, 0, 0, 40000, 0,0 }, + { 991, 991, 0, 0, 40000, 0,0 }, + { 992, 992, 0, 0, 40000, 80,0 }, + { 993, 993, 0, 0, 40000, 86,0 }, + { 994, 994, 0, 0, 40000, 0,0 }, + { 995, 995, 0, 0, 40000, 0,0 }, + { 996, 996, 0, 0, 40000, 333,0 }, + { 997, 997, 0, 0, 40000, 180,0 }, + { 998, 998, 0, 0, 40000, 80,0 }, + { 999, 999, 0, 0, 40000, 120,0 }, + {1000,1000, 0, 0, 10006, 460,0 }, + {1001,1001, 0, 0, 40000, 186,0 }, + {1002,1002, 0, 0, 40000, 400,0 }, + {1003,1003, 0, 0, 20333, 260,0 }, + {1004,1004, 0, 0, 40000, 373,0 }, + {1005,1005, 0, 0, 4520, 400,0 }, + {1006,1006, 0, 0, 8213, 306,0 }, + {1007,1007, 0, 0, 8646, 360,0 }, + {1008,1008, 0, 0, 40000, 160,0 }, + {1009,1009, 0, 0, 40000, 133,0 }, + {1010,1010, 0, 0, 40000, 400,0 }, + {1011,1011, 0, 0, 4473, 193,0 }, + {1012,1012, 0, 0, 1813, 0,0 }, + {1013,1013, 0, 0, 3726, 353,0 }, + {1014,1014, 0, 0, 4400, 373,0 }, + {1015,1015, 0, 0, 953, 166,0 }, + {1016,1016, 0, 0, 40000, 73,0 }, + {1017,1017, 0, 0, 40000, 100,0 }, + {1018,1018, 0, 0, 40000, 100,0 }, + {1019,1019, 0, 0, 3100, 240,0 }, + { 444, 444, 0, 0, 513, 93,0 }, + {1020,1020, 0, 0, 626, 180,0 }, + { 449, 449, 0, 0, 373, 80,0 }, + { 453, 453, 0, 0, 40000, 0,0 }, + {1021,1021, 0, 0, 1020, 340,0 }, + {1022,1022, 0, 0, 1200, 366,0 }, + {1023,1023, 0, 0, 4193, 73,0 }, + {1024,1024, 0, 0, 820, 120,0 }, + {1025,1025, 0, 0, 680, 213,0 }, + {1026,1026, 0, 0, 5260, 806,0 }, + {1027,1027, 0, 0, 9193, 86,0 }, + {1028,1028, 0, 0, 40000, 100,0 }, + {1029,1029, 0, 0, 40000, 426,0 }, + {1030,1030, 0, 0, 40000, 260,0 }, + {1031,1031, 0, 0, 3480, 66,0 }, + {1032,1032, 32, 0, 133, 46,0 }, + {1033,1033, 30, 0, 200, 40,0 }, + {1034,1034, 96, 0, 146, 73,0 }, + {1035,1035, 60, 0, 553, 186,0 }, + {1036,1036, 0, 0, 13193, 260,0 }, + {1037,1037, 0, 0, 40000, 100,0 }, + {1038,1038, 0, 0, 7980, 66,0 }, + {1039,1039, 0, 0, 40000, 0,0 }, + {1040,1040, 0, 0, 980, 340,0 }, + {1041,1041, 0, 0, 7413, 2480,0 }, + {1042,1042, 0, 0, 2906, 520,0 }, + {1043,1043, 0, 0, 40000, 73,0 }, + {1044,1044, 0, 0, 40000, 53,0 }, + {1045,1045, 0, 0, 40000, 113,0 }, + {1046,1046, 0, 0, 5380, 113,0 }, + {1047,1047, 0, 0, 40000, 0,0 }, + {1048,1048, 0, 0, 2366, 73,0 }, + {1049,1049, 0, 0, 40000, 0,0 }, + {1050,1050, 0, 0, 18293, 80,0 }, + {1051,1051, 0, 0, 18466, 146,0 }, + {1052,1052, 0, 0, 9220, 73,0 }, + {1053,1053, 0, 0, 40000, 240,0 }, + {1054,1054, 0, 0, 40000, 0,0 }, + {1055,1055, 0, 0, 1086, 126,0 }, + {1056,1056, 0, 0, 3766, 73,0 }, + {1057,1057, 0, 0, 1186, 226,0 }, + {1058,1058, 0, 0, 3373, 73,0 }, + {1059,1059, 0, 0, 40000, 246,0 }, + {1060,1060, 0, 0, 340, 220,0 }, + {1061,1061, 0, 0, 1186, 386,0 }, + {1062,1062, 0, 0, 40000, 253,0 }, + {1063,1063, 0, 0, 40000, 440,0 }, + {1064,1064, 0, 0, 40000, 46,0 }, + {1065,1065, 0, 0, 40000, 80,0 }, + {1066,1066, 0, 0, 40000, 126,0 }, + {1067,1067, 0, 0, 40000, 133,0 }, + {1068,1068, 0, 0, 40000, 93,0 }, + {1069,1069, 0, 0, 40000, 86,0 }, + {1070,1070, 0, 0, 40000, 93,0 }, + {1071,1071, 0, 0, 40000, 66,0 }, + {1072,1072, 0, 0, 40000, 93,0 }, + {1073,1073, 0, 0, 40000, 73,0 }, + {1074,1074, 0, 0, 40000, 173,0 }, + {1075,1075, 0, 0, 586, 193,0 }, + {1076,1076, 0, 0, 40000, 146,0 }, + {1077,1077, 0, 0, 18460, 73,0 }, + {1078,1078, 0, 0, 846, 93,0 }, + {1079,1079, 0, 0, 40000, 0,0 }, + {1080,1080, 0, 0, 40000, 86,0 }, + {1081,1081, 0, 0, 40000, 0,0 }, + {1082,1082, 0, 0, 40000, 353,0 }, + {1083,1083, 0, 0, 40000, 300,0 }, + {1084,1084, 0, 0, 40000, 320,0 }, + {1085,1085, 0, 0, 9920, 1553,0 }, + {1086,1086, 0, 0, 40000, 386,0 }, + {1087,1087, 0, 0, 40000, 0,0 }, + {1088,1088, 0, 0, 9980, 873,0 }, + {1089,1089, 0, 0, 40000, 386,0 }, + {1090,1090, 0, 0, 966, 126,0 }, + {1091,1091, 0, 0, 40000, 820,0 }, + {1092,1092, 0, 0, 8620, 366,0 }, + {1093,1093, 0, 0, 40000, 826,0 }, + {1094,1094, 0, 0, 40000, 433,0 }, + {1095,1095, 0, 0, 633, 73,0 }, + {1096,1096, 0, 0, 3693, 126,0 }, + {1097,1097, 0, 0, 40000, 0,0 }, + {1098,1098, 0, 0, 40000, 153,0 }, + {1099,1099, 0, 0, 40000, 0,0 }, + {1100,1100, 0, 0, 40000, 0,0 }, + {1101,1101, 0, 0, 40000, 306,0 }, + {1102,1102, 0, 0, 3666, 3093,0 }, + {1103,1103, 0, 0, 1873, 653,0 }, + {1104,1104, 0, 0, 40000, 0,0 }, + {1105,1105, 0, 0, 11293, 886,0 }, + {1106,1106, 0, 0, 40000, 546,0 }, + { 430, 430, 0, 0, 1146, 80,0 }, + {1107,1107, 35, 0, 580, 80,0 }, + {1090,1090, 77, 0, 280, 60,0 }, + {1090,1090, 72, 0, 280, 60,0 }, + {1108,1108, 0, 0, 10180, 600,0 }, + {1109,1109, 0, 0, 10053, 353,0 }, + {1110,1111, 0, 1, 9940, 480,0 }, + {1112,1113, 0, 1, 10620, 473,0.03125 }, + {1114,1114, 0, 0, 40000, 0,0 }, + {1115,1116, 0, 1, 9833, 220,0 }, + {1117,1117, 0, 0, 10286, 473,0 }, + {1118,1118, 0, 0, 7686, 93,0 }, + {1119,1119, 0, 0, 7220, 613,0 }, + {1120,1120, 0, 0, 11513, 1666,0 }, + {1121,1121, 0, 0, 5200, 1700,0 }, + {1122,1122, 0, 0, 10173, 626,0 }, + {1123,1123, 0, 0, 1206, 380,0 }, + {1124,1124, 0, 0, 1953, 866,0 }, + {1125,1125, 0, 0, 4686, 1586,0 }, + {1126,1126, 0, 0, 3786, 893,0 }, + {1127,1127, 0, 0, 40000, 126,0 }, + {1128,1128, 0, 0, 40000, 120,0 }, + {1129,1130, 0, 1, 40000, 146,0.15625 }, + {1131,1131, 0, 0, 40000, 433,0 }, + {1132,1132, 0, 0, 40000, 133,0 }, + {1133,1134, 0, 1, 40000, 126,-0.046875 }, + {1135,1135, 0, 0, 40000, 113,0 }, + {1136,1137, 0, 1, 40000, 253,2.5e-05 }, + {1138,1138, 0, 0, 18440, 240,0 }, + {1139,1139, 0, 0, 5213, 886,0 }, + {1140,1140, 0, 0, 1446, 113,0 }, + {1141,1141, 0, 0, 5233, 106,0 }, + {1142,1142, 0, 0, 5286, 266,0 }, + {1143,1143, 0, 0, 40000, 66,0 }, + {1144,1144, 0, 0, 40000, 66,0 }, + {1145,1145, 0, 0, 10593, 106,0 }, + {1146,1146, 0, 0, 2733, 160,0 }, + {1147,1147, 0, 0, 10313, 93,0 }, + {1148,1148, 0, 0, 40000, 0,0 }, + {1149,1150, 0, 1, 40000, 0,-0.03125 }, + {1151,1151, 0, 0, 40000, 53,0 }, + {1152,1152, 0, 0, 10560, 246,0 }, + {1153,1153, 0, 0, 2700, 153,0 }, + {1154,1154, 0, 1, 40000, 100,-0.15625 }, + {1155,1155, 0, 0, 40000, 73,0 }, + {1156,1156, 0, 0, 40000, 220,0 }, + {1157,1157, 0, 0, 40000, 140,0 }, + {1158,1158, 0, 0, 40000, 380,0 }, + {1159,1160, 0, 1, 40000, 400,0.171875 }, + {1161,1161, 0, 0, 40000, 0,0 }, + {1162,1162, 0, 0, 40000, 0,0 }, + {1163,1163, 0, 0, 4733, 906,0 }, + {1164,1165, 0, 1, 40000, 393,-0.125 }, + {1166,1167, 0, 1, 40000, 366,0.078125 }, + {1168,1168, 0, 1, 40000, 2453,-0.078125 }, + {1169,1170, 0, 1, 40000, 546,0.0625 }, + {1171,1172, 0, 1, 40000, 786,0.15625 }, + {1173,1173, 0, 0, 40000, 0,0 }, + {1174,1174, 0, 0, 40000, 513,0 }, + {1175,1176, 0, 1, 2300, 533,0 }, + {1177,1177, 0, 0, 40000, 80,0 }, + {1178,1178, 0, 0, 40000, 60,0 }, + {1179,1179, 0, 0, 40000, 0,0 }, + {1180,1180, 0, 0, 10653, 86,0 }, + {1181,1182, 0, 1, 40000, 0,2.5e-05 }, + {1183,1184, 0, 1, 40000, 86,0.046875 }, + {1185,1186, 0, 1, 40000, 0,0.09375 }, + {1187,1188, 0, 1, 40000, 0,0.09375 }, + {1189,1189, 0, 0, 40000, 133,0 }, + {1190,1190, 0, 0, 40000, 140,0 }, + {1191,1191, 0, 0, 40000, 73,0 }, + {1192,1192, 0, 0, 40000, 60,0 }, + {1193,1193, 0, 0, 40000, 106,0 }, + {1194,1194, 0, 0, 40000, 93,0 }, + {1195,1195, 0, 0, 40000, 66,0 }, + {1196,1196, 0, 0, 40000, 93,0 }, + {1197,1197, 0, 0, 40000, 60,0 }, + {1198,1198, 0, 0, 40000, 66,0 }, + {1199,1199, 0, 0, 40000, 120,0 }, + {1200,1200, 0, 0, 40000, 100,0 }, + {1201,1201, 0, 0, 40000, 86,0 }, + {1202,1202, 0, 0, 40000, 0,0 }, + {1203,1203, 0, 0, 40000, 233,0 }, + {1204,1204, 0, 0, 40000, 100,0 }, + {1205,1206, 0, 1, 40000, 266,0.03125 }, + {1207,1208, 0, 1, 40000, 260,-2.5e-05 }, + {1209,1209, 0, 0, 40000, 146,0 }, + {1210,1211, 0, 1, 40000, 60,0.03125 }, + {1212,1212, 0, 0, 40000, 53,0 }, + {1213,1214, 0, 1, 40000, 706,-0.09375 }, + {1215,1216, 0, 1, 40000, 660,-0.046875 }, + {1217,1217, 0, 0, 40000, 133,0 }, + {1218,1219, 0, 1, 40000, 426,0.03125 }, + {1220,1220, 0, 1, 40000, 0,0.03125 }, + {1221,1222, 0, 1, 40000, 260,0.171875 }, + {1223,1223, 0, 0, 40000, 0,0 }, + {1224,1224, 0, 0, 6100, 1580,0 }, + {1225,1150, 0, 1, 40000, 73,-0.03125 }, + {1226,1226, 0, 0, 40000, 1580,0 }, + {1227,1227, 0, 0, 40000, 40,0 }, + {1228,1229, 0, 1, 40000, 113,0.125 }, + {1230,1230, 0, 0, 2666, 846,0 }, + {1231,1232, 0, 1, 40000, 0,-0.03125 }, + {1233,1234, 0, 1, 9233, 2413,-0.1875 }, + {1235,1235, 0, 0, 40000, 1020,0 }, + {1236,1236, 0, 0, 40000, 0,0 }, + {1237,1237, 0, 0, 9633, 3073,0 }, + {1238,1238, 0, 0, 40000, 0,0 }, + {1239,1239, 0, 0, 2446, 386,0 }, + {1240,1241, 0, 1, 3113, 1133,0 }, + {1242,1242, 0, 0, 18473, 813,0 }, + {1243,1243, 0, 0, 1206, 660,0 }, + {1244,1244, 0, 0, 40000, 153,0 }, + {1245,1245, 0, 0, 40000, 160,0 }, + {1246,1246, 0, 0, 40000, 133,0 }, + {1247,1247, 0, 0, 8660, 2386,0 }, + {1248,1248, 0, 0, 293, 106,0 }, + {1249,1249, 0, 0, 40000, 433,0 }, + {1250,1250, 0, 0, 426, 80,0 }, + {1251,1251, 0, 0, 973, 360,0 }, + {1252,1252, 0, 0, 573, 153,0 }, + {1253,1253, 0, 0, 3746, 126,0 }, + {1254,1254, 0, 0, 2313, 73,0 }, + {1255,1255, 0, 0, 1473, 106,0 }, + {1256,1256, 0, 0, 1500, 320,0 }, + {1257,1257, 0, 0, 5280, 1593,0 }, + {1258,1258, 0, 0, 40000, 60,0 }, + {1259,1259, 0, 0, 40000, 146,0 }, + {1260,1260, 29, 0, 40000, 300,0 }, + {1261,1261, 65, 0, 40000, 2040,0 }, + {1262,1262, 0, 0, 626, 240,0 }, + {1263,1263, 25, 0, 626, 226,0 }, + {1264,1264, 83, 0, 180, 80,0 }, + {1265,1265, 32, 0, 260, 140,0 }, + {1266,1266, 60, 0, 40000, 0,0 }, + {1267,1267, 36, 0, 286, 40,0 }, + {1268,1268, 27, 0, 573, 80,0 }, + {1269,1269, 31, 0, 693, 106,0 }, + {1270,1270, 21, 0, 500, 146,0 }, + {1270,1270, 26, 0, 493, 140,0 }, + {1270,1270, 28, 0, 500, 146,0 }, + {1271,1271, 60, 0, 2420, 1080,0 }, + {1270,1270, 32, 0, 413, 126,0 }, + {1272,1272, 60, 0, 806, 300,0 }, + {1273,1273, 96, 0, 1146, 493,0 }, + {1274,1274, 72, 0, 1246, 586,0 }, + {1275,1275, 79, 0, 286, 106,0 }, + {1276,1276, 69, 0, 1193, 1046,0 }, + {1277,1277, 71, 0, 340, 93,0 }, + {1278,1278, 22, 0, 1880, 653,0 }, + {1279,1279, 55, 0, 246, 120,0 }, + {1279,1279, 48, 0, 286, 133,0 }, + {1280,1280, 0, 0, 40, 0,0 }, + {1281,1281, 49, 2, 40, 0,0 }, + {1282,1282, 73, 0, 166, 33,0 }, + {1282,1282, 68, 0, 166, 33,0 }, + {1282,1282, 61, 0, 200, 40,0 }, + {1283,1283, 0, 0, 40, 0,0 }, + {1284,1284, 0, 0, 40000, 100,0 }, + {1285,1285, 0, 0, 40000, 60,0 }, + {1286,1286, 0, 0, 40000, 0,0 }, + {1287,1287, 0, 0, 10460, 153,0 }, + {1288,1289, 0, 1, 40000, 0,0 }, + {1290,1290, 0, 0, 40000, 0,0 }, + {1291,1292, 36, 1, 353, 153,0 }, + {1293,1293, 69, 0, 1206, 1060,0 }, + {1294,1294, 0, 0, 40000, 0,0 }, + {1295,1295, 0, 0, 40000, 73,0 }, + {1296,1296, 0, 0, 40000, 0,0 }, + {1297,1297, 22, 0, 1880, 653,0 }, + {1298,1298, 0, 0, 40000, 73,0 }, + {1299,1299, 0, 0, 3913, 420,0 }, + {1300,1300, 0, 0, 9233, 240,0 }, + {1301,1301, 0, 0, 4660, 1573,0 }, + {1302,1302, 0, 0, 1166, 400,0 }, + {1303,1303, 0, 0, 40000, 126,0 }, + {1304,1304, 0, 0, 40000, 93,0 }, + {1305,1305, 0, 0, 40000, 93,0 }, + {1306,1306, 0, 0, 40000, 553,0 }, + {1307,1307, 0, 0, 40000, 660,0 }, + {1308,1308, 0, 0, 40000, 73,0 }, + {1309,1309, 0, 0, 40000, 146,0 }, + {1310,1310, 0, 0, 40000, 146,0 }, + {1311,1311, 0, 0, 4026, 100,0 }, + {1312,1312, 0, 0, 18226, 100,0 }, + {1313,1313, 0, 0, 40000, 0,0 }, + {1314,1314, 0, 0, 40000, 73,0 }, + {1315,1315, 0, 0, 40000, 140,0 }, + {1316,1316, 0, 0, 40000, 393,0 }, + {1317,1317, 0, 0, 40000, 406,0 }, + {1318,1318, 0, 0, 40000, 373,0 }, + {1319,1319, 0, 0, 40000, 0,0 }, + {1320,1320, 0, 0, 40000, 360,0 }, + {1321,1321, 0, 0, 1060, 380,0 }, + {1322,1322, 0, 0, 40000, 66,0 }, + {1323,1323, 0, 0, 40000, 66,0 }, + {1324,1324, 0, 0, 40000, 86,0 }, + {1325,1325, 0, 0, 40000, 73,0 }, + { 260, 260, 0, 0, 40000, 80,0 }, + {1326,1326, 0, 0, 40000, 80,0 }, + {1327,1327, 0, 0, 40000, 73,0 }, + {1328,1328, 0, 0, 40000, 73,0 }, + {1329,1329, 0, 0, 40000, 153,0 }, + {1330,1330, 0, 0, 40000, 153,0 }, + {1331,1331, 0, 0, 40000, 146,0 }, + {1332,1332, 0, 0, 40000, 146,0 }, + {1333,1333, 0, 0, 40000, 73,0 }, + {1334,1334, 0, 0, 40000, 153,0 }, + {1335,1335, 0, 0, 40000, 233,0 }, + {1336,1336, 0, 0, 40000, 400,0 }, + {1337,1337, 0, 0, 40000, 1373,0 }, + {1338,1338, 0, 0, 40000, 193,0 }, + {1339,1339, 0, 0, 40000, 1273,0 }, + {1340,1340, 0, 0, 40000, 186,0 }, + {1341,1341, 0, 0, 40000, 86,0 }, + {1342,1342, 0, 0, 7440, 2473,0 }, + {1343,1343, 0, 0, 40000, 160,0 }, + {1344,1344, 0, 0, 8966, 406,0 }, + {1345,1345, 0, 0, 40000, 1353,0 }, + {1346,1346, 0, 0, 14180, 4406,0 }, + { 378, 378, 84, 0, 1333, 460,0 }, + {1347,1347, 24, 0, 1893, 633,0 }, + {1348,1348, 44, 0, 206, 86,0 }, + {1349,1349, 40, 0, 586, 140,0 }, + {1350,1350, 60, 0, 1026, 320,0 }, + {1351,1351, 0, 0, 6560, 33,0 }, + {1352,1352, 0, 0, 7373, 2453,0 }, + {1353,1353, 0, 0, 4660, 1573,0 }, + {1354,1354, 0, 0, 40000, 346,0 }, + {1355,1355, 0, 0, 7126, 86,0 }, + {1356,1356, 0, 0, 40000, 213,0 }, + {1357,1357, 0, 0, 1180, 340,0 }, + {1358,1358, 0, 0, 3893, 1466,0 }, + {1359,1359, 0, 0, 2053, 1173,0 }, + {1360,1360, 0, 0, 40000, 200,0 }, + {1361,1361, 0, 0, 40000, 353,0 }, + {1362,1362, 0, 0, 40000, 273,0 }, + {1363,1363, 0, 0, 40000, 433,0 }, + {1364,1364, 0, 0, 1940, 426,0 }, + {1365,1365, 0, 0, 40000, 80,0 }, + {1366,1366, 0, 0, 40000, 106,0 }, + {1367,1367, 0, 0, 40000, 60,0 }, + {1368,1368, 0, 0, 40000, 140,0 }, + {1369,1369, 0, 0, 40000, 93,0 }, + {1370,1370, 0, 0, 40000, 73,0 }, + {1371,1371, 0, 0, 40000, 73,0 }, + {1372,1372, 0, 0, 40000, 93,0 }, + {1373,1373, 0, 0, 40000, 73,0 }, + {1374,1374, 0, 0, 40000, 80,0 }, + {1375,1375, 0, 0, 40000, 746,0 }, + {1376,1376, 0, 0, 2360, 813,0 }, + {1377,1377, 0, 0, 340, 146,0 }, + {1378,1378, 35, 0, 713, 273,0 }, + {1379,1379, 49, 0, 173, 93,0 }, + {1377,1377, 48, 0, 286, 126,0 }, + {1380,1380, 58, 0, 173, 100,0 }, + {1377,1377, 60, 0, 286, 133,0 }, + {1381,1381, 47, 0, 973, 360,0 }, + {1382,1382, 60, 0, 146, 86,0 }, + {1381,1381, 49, 0, 966, 333,0 }, + {1383,1383, 72, 0, 506, 206,0 }, + {1381,1381, 51, 0, 953, 340,0 }, + {1384,1384, 84, 0, 1340, 480,0 }, + {1381,1381, 54, 0, 986, 360,0 }, + {1381,1381, 57, 0, 980, 346,0 }, + {1385,1385, 72, 0, 1573, 440,0 }, + {1381,1381, 60, 0, 953, 340,0 }, + {1386,1386, 36, 0, 2673, 900,0 }, + {1387,1387, 93, 0, 233, 106,0 }, + {1388,1388, 72, 0, 966, 353,0 }, + {1389,1389, 84, 0, 1366, 473,0 }, + {1390,1390, 36, 0, 1326, 446,0 }, + {1391,1391, 64, 0, 220, 86,0 }, + {1392,1392, 68, 0, 126, 220,0 }, + {1393,1393, 0, 0, 4513, 640,0 }, + {1394,1394, 0, 0, 40000, 353,0 }, + {1395,1395, 0, 0, 40000, 73,0 }, + {1396,1396, 0, 0, 2040, 380,0 }, + {1397,1397, 0, 0, 40000, 240,0 }, + {1398,1398, 0, 0, 3246, 753,0 }, + {1399,1399, 0, 0, 40000, 66,0 }, + {1400,1400, 0, 0, 40000, 0,0 }, + {1401,1401, 0, 0, 40000, 0,0 }, + {1402,1402, 0, 0, 7720, 1260,0 }, + {1403,1403, 0, 0, 213, 6420,0 }, + {1404,1404, 0, 0, 40000, 66,0 }, + {1405,1405, 0, 0, 40000, 73,0 }, + {1406,1406, 0, 0, 40000, 93,0 }, + {1407,1407, 0, 0, 1606, 640,0 }, + {1408,1408, 0, 0, 15486, 1580,0 }, + {1409,1409, 0, 0, 40000, 353,0 }, + {1410,1410, 0, 0, 40000, 2066,0 }, + {1411,1411, 0, 0, 40000, 0,0 }, + {1412,1412, 0, 0, 15453, 73,0 }, + {1413,1413, 0, 0, 3726, 1240,0 }, + {1414,1414, 0, 0, 40000, 86,0 }, + {1415,1415, 0, 0, 40000, 200,0 }, + {1416,1416, 0, 0, 40000, 53,0 }, + {1417,1417, 0, 0, 40000, 73,0 }, + {1418,1418, 0, 0, 40000, 66,0 }, + {1419,1419, 0, 0, 40000, 26,0 }, + {1420,1420, 0, 0, 40000, 53,0 }, + {1421,1421, 0, 0, 40000, 40,0 }, + {1422,1422, 0, 0, 40000, 126,0 }, + {1423,1423, 0, 0, 40000, 0,0 }, + {1424,1424, 0, 0, 13653, 0,0 }, + {1425,1425, 0, 0, 12533, 1953,0 }, + {1426,1426, 0, 0, 973, 1280,0 }, + {1427,1427, 0, 0, 40000, 426,0 }, + {1428,1428, 0, 0, 40000, 53,0 }, + {1429,1429, 0, 0, 40000, 66,0 }, + {1430,1430, 0, 0, 526, 840,0 }, + {1431,1431, 0, 0, 286, 1293,0 }, + {1432,1432, 0, 0, 14726, 4920,0 }, + {1433,1433, 0, 0, 5233, 633,0 }, + {1434,1434, 0, 0, 13226, 2500,0 }, + { 740, 740, 0, 0, 513, 200,0 }, + {1435,1435, 0, 0, 40000, 5666,0 }, + { 739, 739, 48, 0, 213, 20,0 }, + { 500, 500, 55, 0, 100, 0,0 }, + { 740, 740, 60, 0, 226, 113,0 }, + { 500, 500, 41, 0, 106, 0,0 }, + {1436,1436, 84, 0, 160, 26,0 }, + {1437,1437, 84, 0, 386, 493,0 }, + { 500, 500, 48, 0, 100, 0,0 }, + {1438,1438, 15, 0, 340, 140,0 }, + { 752, 752, 49, 0, 173, 20,0 }, + {1438,1438, 16, 0, 346, 146,0 }, + {1438,1438, 12, 0, 340, 140,0 }, + { 740, 740, 55, 0, 220, 113,0 }, + { 752, 752, 18, 0, 206, 20,0 }, + { 752, 752, 15, 0, 200, 20,0 }, + { 752, 752, 17, 0, 206, 20,0 }, + {1439,1440, 0, 4, 40000, 0,0 }, + {1441,1442, 0, 4, 7360, 200,0 }, + {1443,1444, 0, 4, 11840, 320,0 }, + {1445,1446, 0, 4, 9920, 326,0 }, + {1447,1448, 0, 4, 10213, 0,0 }, + {1449,1450, 0, 4, 7440, 2486,0 }, + { 181,1451, 0, 4, 2360, 733,0 }, + {1452,1453, 0, 4, 9260, 240,0 }, + {1454,1455, 0, 4, 40000, 0,0 }, + {1456,1457, 0, 4, 660, 126,0 }, + {1458,1459, 0, 4, 40000, 66,0 }, + { 190,1460, 0, 4, 40000, 60,0 }, + { 192,1461, 0, 4, 40000, 73,0 }, + {1462,1463, 0, 4, 40000, 353,0 }, + {1464,1465, 0, 4, 40000, 353,0 }, + {1466,1467, 0, 4, 40000, 66,0 }, + {1468,1469, 0, 4, 40000, 46,0 }, + { 35,1470, 0, 4, 40000, 46,0 }, + { 36,1471, 0, 4, 320, 0,0 }, + {1472,1473, 0, 4, 320, 0,0 }, + {1474,1475, 0, 4, 7986, 93,0 }, + { 39,1476, 0, 4, 1053, 226,0 }, + {1477,1476, 0, 4, 1060, 226,0 }, + {1478,1479, 0, 4, 40000, 453,0 }, + { 50,1480, 0, 4, 40000, 400,0 }, + {1481,1482, 0, 4, 40000, 133,0 }, + {1483,1484, 0, 4, 40000, 0,0 }, + {1485,1486, 0, 4, 40000, 226,0 }, + { 55,1487, 0, 4, 40000, 100,0 }, + {1488,1489, 0, 4, 40000, 93,0 }, + {1490,1491, 0, 4, 40000, 73,0 }, + {1492,1493, 0, 4, 40000, 73,0 }, + {1494,1495, 0, 4, 40000, 73,0 }, + {1496,1497, 0, 4, 40000, 80,0 }, + {1496,1498, 0, 4, 40000, 73,0 }, + {1499,1500, 0, 4, 40000, 66,0 }, + {1501,1502, 0, 4, 40000, 146,0 }, + {1503,1504, 0, 4, 40000, 93,0 }, + {1505,1506, 0, 4, 40000, 73,0 }, + { 86,1507, 0, 4, 40000, 80,0 }, + {1508,1509, 0, 4, 40000, 0,0 }, + {1510,1511, 0, 4, 40000, 60,0 }, + {1512,1513, 0, 4, 40000, 0,0 }, + {1514,1515, 0, 4, 40000, 0,0 }, + {1516,1517, 0, 4, 40000, 773,0 }, + {1518,1519, 0, 4, 5346, 2973,0 }, + {1520,1521, 0, 4, 40000, 406,0 }, + {1522,1523, 0, 4, 9080, 360,0 }, + {1524,1525, 0, 4, 40000, 1200,0 }, + {1526,1527, 0, 4, 40000, 800,0 }, + {1528,1529, 0, 4, 40000, 960,0 }, + { 111,1530, 0, 4, 1200, 433,0 }, + {1531,1532, 0, 4, 226, 386,0 }, + { 115,1533, 0, 4, 2433, 0,0 }, + {1534,1535, 0, 4, 1873, 646,0 }, + {1536,1537, 0, 4, 3013, 53,0 }, + {1538,1539, 0, 4, 1560, 720,0 }, + {1540, 339, 0, 6, 6, 0,0 }, + {1541, 339, 0, 6, 6, 0,0 }, + {1542,1543, 0, 4, 993, 93,0 }, + {1544,1545, 0, 4, 293, 86,0 }, + {1546,1547, 0, 4, 40000, 153,0 }, + { 364, 365, 44, 4, 120, 0,0 }, + { 129,1548, 48, 4, 173, 0,0 }, + { 367, 368, 58, 4, 173, 0,0 }, + { 129,1549, 60, 4, 173, 0,0 }, + {1550,1551, 48, 4, 520, 200,0 }, + { 132,1552, 43, 4, 173, 0,0 }, + {1550,1551, 49, 4, 520, 200,0 }, + {1553,1554, 43, 4, 160, 80,0 }, + {1550,1551, 51, 4, 513, 0,0 }, + { 134,1555, 43, 4, 1733, 0,0 }, + {1550,1551, 54, 4, 506, 0,0 }, + {1550,1551, 57, 4, 506, 0,0 }, + { 380, 381, 72, 4, 1580, 0,0 }, + {1550,1551, 60, 4, 520, 0,0 }, + {1556,1557, 70, 4, 826, 306,0 }, + { 374, 375, 60, 4, 973, 0,0 }, + {1558,1559, 36, 4, 1233, 0,0 }, + {1560,1561, 65, 4, 293, 133,0 }, + {1562,1563, 84, 4, 1360, 0,0 }, + {1564,1565, 59, 4, 380, 0,0 }, + {1566,1567, 84, 4, 1593, 566,0 }, + {1568,1569, 35, 4, 1353, 473,0 }, + {1570,1571, 44, 4, 413, 0,0 }, + {1572,1573, 67, 4, 246, 0,0 }, + {1574,1575, 66, 4, 293, 0,0 }, + { 145,1576, 59, 4, 146, 0,0 }, + {1577,1578, 51, 4, 360, 0,0 }, + {1579,1580, 45, 4, 246, 0,0 }, + {1581,1582, 71, 4, 433, 0,0 }, + { 149,1583, 60, 4, 280, 0,0 }, + {1584,1585, 58, 4, 173, 0,0 }, + {1586,1587, 53, 4, 173, 0,0 }, + { 397,1588, 64, 4, 220, 80,0 }, + {1589,1590, 71, 4, 106, 53,0 }, + {1591,1592, 61, 4, 1000, 340,0 }, + {1593,1594, 61, 4, 1000, 340,0 }, + { 391, 392, 48, 4, 160, 46,0 }, + { 391, 393, 48, 4, 380, 60,0 }, + {1595,1596, 69, 4, 120, 0,0 }, + { 159,1597, 68, 4, 120, 0,0 }, + { 159,1597, 63, 4, 140, 0,0 }, + {1598,1599, 74, 4, 893, 273,0 }, + {1600,1601, 60, 4, 1013, 306,0 }, + {1602,1603, 80, 4, 220, 0,0 }, + {1604,1605, 64, 4, 1366, 0,0 }, + {1606,1607, 69, 4, 120, 73,0 }, + { 398, 399, 55, 4, 1540, 193,0 }, + {1608,1609, 75, 4, 1573, 0,0 }, + {1610,1611, 68, 4, 120, 0,0 }, + {1612,1613, 48, 4, 360, 0,0 }, + {1614,1615, 53, 4, 606, 0,0 }, + {1616,1616, 0, 0, 40000, 1586,0 }, + {1617,1617, 0, 0, 40000, 1226,0 }, + {1618,1618, 0, 0, 4546, 766,0 }, + {1619,1619, 0, 0, 40000, 420,0 }, + {1620,1620, 0, 0, 40000, 1573,0 }, + {1621,1621, 0, 0, 3326, 806,0 }, + {1622,1622, 0, 0, 40000, 746,0 }, + {1623,1623, 0, 0, 40000, 900,0 }, + {1624,1624, 0, 0, 12166, 1573,0 }, + {1625,1625, 0, 0, 40000, 80,0 }, + {1626,1626, 0, 0, 40000, 80,0 }, + {1627,1627, 0, 0, 40000, 80,0 }, + {1628,1628, 0, 0, 40000, 2713,0 }, + {1629,1629, 0, 0, 40000, 86,0 }, + {1630,1630, 0, 0, 40000, 80,0 }, + {1631,1631, 0, 0, 40000, 80,0 }, + {1632,1632, 0, 0, 40000, 813,0 }, + {1633,1633, 0, 0, 40000, 80,0 }, + {1634,1634, 0, 0, 40000, 80,0 }, + {1635,1635, 0, 0, 40000, 80,0 }, + {1636,1636, 0, 0, 40000, 193,0 }, + {1637,1637, 0, 0, 2920, 733,0 }, + {1638,1638, 0, 0, 40000, 373,0 }, + {1639,1639, 0, 0, 2286, 226,0 }, + {1640,1640, 0, 0, 40000, 226,0 }, + {1641,1641, 0, 0, 40000, 226,0 }, + {1642,1642, 0, 0, 40000, 433,0 }, + {1643,1643, 0, 0, 40000, 813,0 }, + {1644,1644, 0, 0, 40000, 80,0 }, + {1645,1645, 0, 0, 40000, 80,0 }, + {1646,1646, 0, 0, 40000, 80,0 }, + {1647,1647, 0, 0, 40000, 80,0 }, + {1648,1648, 0, 0, 40000, 80,0 }, + {1649,1649, 0, 0, 40000, 80,0 }, + {1650,1650, 0, 0, 40000, 146,0 }, + {1651,1651, 0, 0, 40000, 1280,0 }, + {1652,1652, 0, 0, 40000, 513,0 }, + {1653,1653, 0, 0, 40000, 313,0 }, + {1654,1654, 0, 0, 40000, 773,0 }, + {1655,1655, 0, 0, 7400, 2480,0 }, + {1656,1656, 0, 0, 3760, 1253,0 }, + {1657,1657, 0, 0, 40000, 380,0 }, + {1658,1658, 0, 0, 40000, 333,0 }, + {1659,1659, 0, 0, 40000, 2926,0 }, + {1660,1660, 0, 0, 40000, 5666,0 }, + {1661,1661, 0, 0, 40000, 1613,0 }, + {1662,1662, 0, 0, 3746, 1273,0 }, + {1663,1663, 0, 0, 13653, 0,0 }, + {1664,1664, 0, 0, 4640, 1553,0 }, + {1665,1665, 0, 0, 40000, 680,0 }, + {1666,1666, 0, 0, 6393, 426,0 }, + {1667,1667, 0, 0, 40000, 713,0 }, + {1668,1668, 12, 0, 166, 20,0 }, + {1669,1669, 48, 0, 460, 193,0 }, + { 736, 736, 52, 0, 286, 20,0 }, + {1670,1670, 48, 0, 506, 200,0 }, + {1670,1670, 36, 0, 713, 260,0 }, + { 377, 377, 84, 0, 386, 493,0 }, + { 730, 730, 95, 0, 1886, 653,0 }, + {1669,1669, 84, 0, 386, 166,0 }, + { 755, 755, 20, 0, 633, 240,0 }, + { 755, 755, 22, 0, 626, 240,0 }, + { 755, 755, 24, 0, 633, 246,0 }, + {1671,1671, 0, 0, 2233, 220,0 }, + {1672,1672, 0, 0, 2233, 240,0 }, + {1673,1673, 0, 0, 2233, 206,0 }, + {1674,1674, 0, 0, 2126, 173,0 }, + {1675,1675, 0, 0, 7473, 73,0 }, + {1676,1676, 0, 0, 40000, 0,0 }, + {1677,1677, 0, 0, 3493, 193,0 }, + {1678,1678, 0, 0, 1746, 73,0 }, + {1679,1679, 0, 0, 1013, 400,0 }, + {1680,1680, 0, 0, 3473, 1560,0 }, + {1681,1681, 0, 0, 1073, 40,0 }, + {1682,1682, 0, 0, 40000, 380,0 }, + {1683,1683, 0, 0, 1166, 400,0 }, + {1684,1684, 0, 0, 606, 146,0 }, + {1685,1685, 0, 0, 4553, 1486,0 }, + {1686,1686, 0, 0, 1126, 80,0 }, + {1687,1687, 0, 0, 40000, 73,0 }, + {1688,1688, 0, 0, 40000, 60,0 }, + {1689,1689, 0, 0, 40000, 66,0 }, + {1690,1690, 0, 0, 40000, 73,0 }, + {1691,1691, 0, 0, 40000, 73,0 }, + {1692,1692, 0, 0, 40000, 73,0 }, + {1693,1693, 0, 0, 40000, 73,0 }, + {1694,1694, 0, 0, 6380, 53,0 }, + {1695,1695, 0, 0, 6380, 60,0 }, + {1696,1696, 0, 0, 40000, 53,0 }, + {1697,1697, 0, 0, 40000, 0,0 }, + {1698,1698, 0, 0, 1880, 80,0 }, + {1699,1699, 0, 0, 40000, 60,0 }, + {1700,1700, 0, 0, 40000, 60,0 }, + {1701,1701, 0, 0, 1460, 80,0 }, + {1702,1702, 0, 0, 40000, 73,0 }, + {1703,1703, 0, 0, 40000, 0,0 }, + {1704,1704, 0, 0, 40000, 146,0 }, + {1705,1705, 0, 0, 40000, 66,0 }, + {1706,1706, 0, 0, 40000, 73,0 }, + {1707,1707, 0, 0, 40000, 160,0 }, + {1708,1708, 0, 0, 40000, 73,0 }, + {1709,1709, 0, 0, 40000, 193,0 }, + {1710,1710, 0, 0, 3740, 1260,0 }, + {1711,1711, 0, 0, 40000, 180,0 }, + {1712,1712, 0, 0, 40000, 173,0 }, + {1713,1713, 0, 0, 40000, 113,0 }, + {1714,1714, 0, 0, 40000, 86,0 }, + {1715,1715, 0, 0, 1853, 633,0 }, + {1716,1716, 0, 0, 40000, 0,0 }, + {1717,1717, 0, 0, 1066, 306,0 }, + {1718,1718, 0, 0, 40000, 86,0 }, + {1719,1719, 0, 0, 40000, 586,0 }, + {1720,1720, 0, 0, 40000, 86,0 }, + {1721,1721, 0, 0, 40000, 93,0 }, + {1722,1722, 0, 0, 40000, 373,0 }, + {1723,1723, 0, 0, 40000, 113,0 }, + {1724,1724, 0, 0, 40000, 353,0 }, + {1725,1725, 0, 0, 420, 73,0 }, + {1726,1726, 0, 0, 40000, 66,0 }, + {1727,1727, 0, 0, 40000, 53,0 }, + {1728,1728, 0, 0, 40000, 66,0 }, + {1729,1729, 0, 0, 40000, 100,0 }, + {1730,1730, 0, 0, 40000, 93,0 }, + {1731,1731, 0, 0, 40000, 0,0 }, + {1732,1732, 0, 0, 40000, 73,0 }, + {1733,1733, 0, 0, 40000, 80,0 }, + {1734,1734, 0, 0, 40000, 80,0 }, + {1735,1735, 0, 0, 40000, 80,0 }, + {1736,1736, 0, 0, 40000, 80,0 }, + {1737,1737, 0, 0, 40000, 80,0 }, + {1738,1738, 0, 0, 40000, 73,0 }, + {1739,1739, 0, 0, 40000, 73,0 }, + {1740,1740, 0, 0, 40000, 106,0 }, + {1741,1741, 0, 0, 40000, 73,0 }, + {1742,1742, 0, 0, 40000, 73,0 }, + {1743,1743, 0, 0, 40000, 80,0 }, + {1744,1744, 0, 0, 40000, 0,0 }, + {1745,1745, 0, 0, 40000, 80,0 }, + {1746,1746, 0, 0, 40000, 66,0 }, + {1747,1747, 0, 0, 40000, 73,0 }, + {1748,1748, 0, 0, 40000, 0,0 }, + {1749,1749, 0, 0, 40000, 80,0 }, + {1750,1750, 0, 0, 40000, 66,0 }, + {1751,1751, 0, 0, 40000, 73,0 }, + {1752,1752, 0, 0, 40000, 80,0 }, + {1753,1753, 0, 0, 40000, 33,0 }, + {1754,1754, 0, 0, 40000, 0,0 }, + {1755,1755, 0, 0, 40000, 266,0 }, + {1756,1756, 0, 0, 40000, 160,0 }, + {1757,1757, 0, 0, 40000, 93,0 }, + {1758,1758, 0, 0, 40000, 660,0 }, + {1759,1759, 0, 0, 40000, 1453,0 }, + {1760,1760, 0, 0, 40000, 660,0 }, + {1761,1761, 0, 0, 40000, 120,0 }, + {1762,1762, 0, 0, 40000, 140,0 }, + {1763,1763, 0, 0, 9820, 393,0 }, + {1764,1764, 0, 0, 40000, 73,0 }, + {1765,1765, 0, 0, 3620, 1166,0 }, + {1766,1766, 0, 0, 40000, 0,0 }, + {1767,1767, 0, 0, 40000, 0,0 }, + {1768,1768, 0, 0, 40000, 813,0 }, + {1769,1769, 0, 0, 40000, 0,0 }, + {1770,1770, 0, 0, 40000, 2386,0 }, + {1771,1771, 0, 0, 4380, 400,0 }, + {1772,1772, 0, 0, 853, 0,0 }, + {1773,1773, 0, 0, 3700, 93,0 }, + {1774,1774, 0, 0, 1580, 300,0 }, + {1775,1775, 0, 0, 453, 140,0 }, + {1776,1776, 0, 0, 40000, 66,0 }, + {1777,1777, 0, 0, 40000, 73,0 }, + {1778,1778, 0, 0, 40000, 206,0 }, + {1779,1779, 0, 0, 4646, 1560,0 }, + {1780,1780, 0, 0, 353, 146,0 }, + {1781,1781, 0, 0, 1300, 400,0 }, + {1782,1782, 0, 0, 4593, 1546,0 }, + {1783,1783, 0, 0, 613, 226,0 }, + {1784,1784, 0, 0, 626, 233,0 }, + {1785,1785, 0, 0, 3020, 66,0 }, + {1786,1786, 0, 0, 1093, 186,0 }, + {1787,1787, 0, 0, 6053, 1240,0 }, + {1788,1788, 0, 0, 633, 126,0 }, + {1789,1789, 0, 0, 40000, 66,0 }, + {1790,1790, 0, 0, 40000, 73,0 }, + {1791,1791, 0, 0, 40000, 1253,0 }, + {1792,1792, 0, 0, 626, 246,0 }, + {1793,1793, 48, 0, 293, 120,0 }, + {1794,1794, 48, 0, 100, 0,0 }, + {1795,1795, 60, 0, 240, 133,0 }, + {1796,1796, 60, 0, 160, 66,0 }, + {1797,1797, 70, 0, 140, 33,0 }, + {1798,1798, 51, 0, 526, 206,0 }, + {1799,1799, 60, 0, 173, 93,0 }, + {1798,1798, 54, 0, 520, 200,0 }, + {1800,1800, 60, 0, 153, 80,0 }, + {1798,1798, 56, 0, 520, 206,0 }, + {1801,1801, 60, 0, 673, 206,0 }, + {1798,1798, 61, 0, 506, 200,0 }, + {1798,1798, 63, 0, 513, 206,0 }, + {1802,1802, 48, 0, 673, 200,0 }, + {1798,1798, 68, 0, 440, 180,0 }, + {1803,1803, 60, 0, 1873, 653,0 }, + {1804,1804, 60, 0, 673, 200,0 }, + {1805,1805, 66, 0, 306, 120,0 }, + {1806,1806, 60, 0, 673, 200,0 }, + { 379, 379, 59, 0, 173, 93,0 }, + {1802,1802, 64, 0, 673, 206,0 }, + {1807,1807, 48, 0, 1006, 20,0 }, + {1808,1808, 56, 0, 120, 40,0 }, + {1809,1809, 53, 0, 286, 133,0 }, + {1810,1810, 65, 0, 106, 0,0 }, + {1811,1811, 49, 0, 293, 133,0 }, + {1811,1811, 43, 0, 293, 133,0 }, + { 386, 386, 65, 0, 1013, 673,0 }, + { 386, 386, 60, 0, 1000, 660,0 }, + {1812,1812, 70, 0, 260, 113,0 }, + {1812,1812, 65, 0, 306, 120,0 }, + {1813,1813, 60, 0, 246, 106,0 }, + {1814,1814, 60, 0, 193, 120,0 }, + {1815,1815, 56, 0, 206, 13,0 }, + {1816,1816, 53, 0, 433, 73,0 }, + {1817,1817, 60, 0, 220, 113,0 }, + {1818,1818, 48, 0, 300, 66,0 }, + {1819,1819, 69, 0, 126, 0,0 }, + { 328, 328, 67, 0, 140, 93,0 }, + { 328, 328, 62, 0, 153, 100,0 }, + {1820,1820, 65, 0, 433, 100,0 }, + {1821,1821, 60, 0, 426, 100,0 }, + {1822,1822, 63, 0, 113, 46,0 }, + {1823,1823, 63, 0, 1866, 653,0 }, + {1824,1824, 67, 0, 273, 60,0 }, + {1825,1825, 60, 0, 973, 360,0 }, + {1825,1825, 72, 0, 806, 273,0 }, + { 401, 401, 62, 0, 46, 0,0 }, + {1826,1826, 48, 0, 126, 66,0 }, + {1827,1827, 53, 0, 980, 353,0 }, + {1828,1828, 60, 0, 293, 133,0 }, + {1829,1829, 60, 0, 160, 20,0 }, + {1830,1830, 60, 0, 126, 86,0 }, + {1831,1831, 60, 0, 173, 93,0 }, + {1832,1832, 0, 0, 40000, 106,0 }, + {1833,1833, 0, 0, 3780, 73,0 }, + {1834,1834, 0, 0, 3820, 1666,0 }, + {1835,1835, 0, 0, 40000, 73,0 }, + {1836,1836, 0, 0, 40000, 333,0 }, + {1837,1837, 0, 0, 40000, 220,0 }, + {1838,1838, 0, 0, 40000, 0,0 }, + {1839,1839, 0, 0, 40000, 53,0 }, + {1840,1840, 0, 0, 40000, 60,0 }, + {1841,1841, 0, 0, 5913, 2306,0 }, + {1842,1842, 0, 0, 7713, 2466,0 }, + { 525, 525, 0, 0, 4660, 660,0 }, + {1843,1843, 0, 0, 40000, 313,0 }, + {1844,1844, 0, 0, 40000, 0,0 }, + {1845,1845, 0, 0, 40000, 0,0 }, + {1846,1846, 0, 0, 1246, 453,0 }, + {1847,1847, 0, 0, 9600, 1580,0 }, + {1848,1848, 0, 0, 40000, 106,0 }, + {1849,1849, 0, 0, 2040, 400,0 }, + {1850,1850, 0, 0, 40000, 73,0 }, + {1851,1851, 0, 0, 4220, 620,0 }, + {1852,1852, 0, 0, 40000, 0,0 }, + {1853,1853, 0, 0, 40000, 433,0 }, + {1854,1854, 0, 0, 40000, 66,0 }, + {1855,1855, 0, 0, 40000, 46,0 }, + {1856,1856, 0, 0, 40000, 240,0 }, + {1857,1857, 0, 0, 40000, 313,0 }, + {1858,1858, 0, 0, 40000, 26,0 }, + {1859,1859, 0, 0, 40000, 0,0 }, + {1860,1860, 0, 0, 40000, 73,0 }, + {1861,1861, 0, 0, 6940, 66,0 }, + {1862,1862, 0, 0, 40000, 0,0 }, + {1863,1863, 0, 0, 40000, 60,0 }, + {1864,1864, 0, 0, 8140, 1440,0 }, + {1865,1865, 0, 0, 40000, 0,0 }, + {1866,1866, 0, 0, 40000, 613,0 }, + {1867,1867, 0, 0, 40000, 0,0 }, + {1868,1868, 0, 0, 633, 233,0 }, + {1869,1869, 0, 0, 40000, 226,0 }, + {1870,1870, 0, 0, 2280, 746,0 }, + {1871,1871, 0, 0, 1940, 633,0 }, + {1872,1872, 0, 0, 4220, 620,0 }, + {1873,1873, 0, 0, 40000, 133,0 }, + {1874,1874, 41, 0, 380, 153,0 }, + {1875,1875, 70, 0, 106, 0,0 }, + {1876,1876, 60, 0, 380, 206,0 }, + {1877,1877, 80, 0, 100, 0,0 }, + {1878,1878, 84, 0, 120, 0,0 }, + {1879,1879, 72, 0, 500, 433,0 }, + {1880,1880, 84, 0, 860, 553,0 }, + { 128, 128, 70, 0, 106, 0,0 }, + { 132, 132, 60, 0, 146, 86,0 }, + {1881,1882, 0, 4, 40000, 260,0 }, + {1883,1883, 0, 0, 40000, 0,0 }, + {1884,1885, 0, 4, 40000, 73,0 }, + {1886,1887, 0, 4, 40000, 86,0 }, + {1888,1889, 0, 4, 40000, 73,0 }, + {1890,1890, 0, 0, 40000, 300,0 }, + {1891,1891, 0, 0, 40000, 693,0 }, + {1892,1892, 0, 0, 40000, 586,0 }, + {1893,1893, 0, 0, 40000, 286,0 }, + {1894,1894, 0, 0, 1620, 773,0 }, + {1895,1895, 0, 0, 40000, 0,0 }, + {1896,1896, 0, 0, 40000, 193,0 }, + {1897,1897, 0, 0, 1873, 820,0 }, + {1898,1898, 0, 0, 4520, 753,0 }, + {1899,1899, 0, 0, 40000, 0,0 }, + {1900,1900, 0, 0, 40000, 220,0 }, + {1901,1901, 0, 0, 40000, 133,0 }, + {1902,1902, 0, 0, 40000, 73,0 }, + {1903,1903, 0, 0, 40000, 0,0 }, + {1904,1904, 0, 0, 7326, 2420,0 }, + {1905,1905, 0, 0, 1186, 446,0 }, + {1906,1906, 0, 0, 40000, 553,0 }, + {1907,1907, 0, 0, 40000, 293,0 }, + {1908,1908, 0, 0, 40000, 586,0 }, + {1909,1909, 0, 0, 2326, 793,0 }, + { 501, 501, 0, 0, 480, 226,0 }, + {1910,1910, 0, 0, 40000, 93,0 }, + {1911,1911, 0, 0, 620, 226,0 }, + {1912,1912, 0, 0, 2373, 800,0 }, + {1913,1913, 0, 0, 40000, 4986,0 }, + {1914,1914, 0, 0, 626, 240,0 }, + { 511, 511, 0, 0, 2326, 800,0 }, + {1915,1915, 0, 0, 340, 146,0 }, + {1910,1910, 60, 0, 40000, 93,0 }, + { 511, 511, 72, 0, 1566, 546,0 }, + {1915,1915, 84, 0, 246, 120,0 }, + {1916,1916, 0, 0, 40000, 0,0 }, + {1917,1917, 0, 0, 2713, 666,0 }, + {1918,1918, 0, 0, 40000, 0,0 }, + {1919,1919, 0, 0, 40000, 46,0 }, + {1920,1920, 0, 0, 40000, 0,0 }, + {1921,1921, 0, 0, 40000, 53,0 }, + {1922,1922, 0, 0, 40000, 33,0 }, + {1923,1923, 0, 0, 2073, 193,0 }, + {1924,1924, 0, 0, 40000, 146,0 }, + {1925,1925, 0, 0, 40000, 100,0 }, + {1926,1926, 0, 0, 40000, 93,0 }, + {1927,1927, 0, 0, 40000, 73,0 }, + {1928,1928, 0, 0, 40000, 540,0 }, + {1929,1929, 0, 0, 40000, 520,0 }, + {1930,1930, 0, 0, 40000, 506,0 }, + {1931,1931, 0, 0, 7406, 200,0 }, + {1932,1932, 0, 0, 5906, 133,0 }, + {1933,1933, 0, 0, 7426, 240,0 }, + {1934,1934, 0, 0, 7426, 240,0 }, + {1935,1935, 0, 0, 40000, 66,0 }, + {1936,1936, 0, 0, 40000, 66,0 }, + {1937,1937, 0, 0, 40000, 53,0 }, + {1938,1938, 0, 0, 40000, 66,0 }, + {1939,1939, 0, 0, 40000, 66,0 }, + {1940,1940, 0, 0, 40000, 53,0 }, + {1941,1941, 0, 0, 40000, 2146,0 }, + {1942,1942, 0, 0, 40000, 1126,0 }, + {1943,1943, 0, 0, 40000, 1020,0 }, + {1944,1944, 0, 0, 40000, 433,0 }, + {1945,1945, 0, 0, 40000, 0,0 }, + {1946,1946, 0, 0, 40000, 140,0 }, + {1947,1947, 0, 0, 4660, 660,0 }, + {1948,1948, 0, 0, 40000, 66,0 }, + {1949,1949, 0, 0, 40000, 4193,0 }, + {1950,1950, 0, 0, 7713, 2466,0 }, + {1951,1951, 0, 0, 40000, 73,0 }, + {1952,1952, 0, 0, 8100, 2093,0 }, + {1953,1953, 0, 0, 40000, 86,0 }, + {1954,1954, 0, 0, 40000, 80,0 }, + {1955,1955, 0, 0, 4113, 1526,0 }, + {1956,1956, 0, 0, 40000, 66,0 }, + {1957,1957, 0, 0, 40000, 100,0 }, + {1958,1958, 0, 0, 40000, 213,0 }, + {1959,1959, 0, 0, 40000, 100,0 }, + {1960,1960, 0, 0, 1186, 100,0 }, + {1961,1961, 0, 0, 40000, 433,0 }, + {1962,1962, 0, 0, 40000, 146,0 }, + {1963,1963, 0, 0, 40000, 400,0 }, + {1964,1964, 0, 0, 40000, 66,0 }, + {1965,1965, 0, 0, 40000, 193,0 }, + {1966,1966, 0, 0, 1153, 100,0 }, + {1967,1967, 0, 0, 4800, 1400,0 }, + {1968,1968, 0, 0, 2906, 713,0 }, + {1969,1969, 0, 0, 40000, 73,0 }, + {1970,1970, 0, 0, 2280, 746,0 }, + {1971,1971, 0, 0, 40000, 66,0 }, + {1972,1972, 0, 0, 40000, 86,0 }, + {1973,1973, 0, 0, 40000, 86,0 }, + {1974,1974, 0, 0, 40000, 66,0 }, + {1975,1975, 0, 0, 40000, 66,0 }, + {1976,1976, 0, 0, 40000, 66,0 }, + {1977,1977, 0, 0, 40000, 46,0 }, + {1978,1978, 0, 0, 40000, 73,0 }, + {1979,1979, 0, 0, 40000, 73,0 }, + {1980,1980, 0, 0, 40000, 66,0 }, + {1981,1981, 0, 0, 40000, 66,0 }, + {1982,1982, 0, 0, 40000, 66,0 }, + {1983,1983, 0, 0, 40000, 73,0 }, + {1984,1984, 0, 0, 40000, 73,0 }, + {1985,1985, 0, 0, 40000, 253,0 }, + {1986,1986, 0, 0, 40000, 126,0 }, + {1987,1987, 0, 0, 40000, 126,0 }, + {1988,1988, 0, 0, 40000, 66,0 }, + {1989,1989, 0, 0, 40000, 66,0 }, + {1990,1990, 0, 0, 40000, 53,0 }, + {1991,1991, 0, 0, 40000, 140,0 }, + {1992,1992, 0, 0, 40000, 40,0 }, + {1993,1993, 0, 0, 40000, 73,0 }, + {1994,1994, 0, 0, 40000, 66,0 }, + {1995,1995, 0, 0, 40000, 73,0 }, + {1996,1996, 0, 0, 40000, 73,0 }, + {1997,1997, 0, 0, 40000, 73,0 }, + {1998,1998, 0, 0, 40000, 73,0 }, + {1999,1999, 0, 0, 40000, 66,0 }, + {2000,2000, 0, 0, 40000, 433,0 }, + {2001,2001, 0, 0, 40000, 433,0 }, + {2002,2002, 0, 0, 2440, 706,0 }, + {2003,2003, 0, 0, 13960, 4800,0 }, + {2004,2004, 0, 0, 7393, 2480,0 }, + {2005,2005, 0, 0, 7220, 2073,0 }, + {2006,2006, 0, 0, 633, 233,0 }, + {2007,2007, 0, 0, 2326, 780,0 }, + {2008,2008, 0, 0, 40000, 73,0 }, + {2009,2009, 0, 0, 40000, 106,0 }, + {2010,2010, 0, 0, 40000, 126,0 }, + {2011,2011, 0, 0, 40000, 386,0 }, + {2012,2012, 0, 0, 40000, 66,0 }, + {2013,2013, 0, 0, 6893, 1273,0 }, + {2014,2014, 0, 0, 2546, 633,0 }, + {2015,2015, 0, 0, 206, 106,0 }, + {2016,2016, 0, 0, 213, 113,0 }, + {2017,2017, 0, 0, 360, 140,0 }, + {2018,2018, 0, 0, 1013, 193,0 }, + {2019,2019, 0, 0, 266, 66,0 }, + {2020,2020, 0, 0, 1880, 660,0 }, + {2021,2021, 0, 0, 286, 206,0 }, + {2022,2022, 0, 0, 3706, 1353,0 }, + {2023,2023, 0, 0, 1106, 380,0 }, + {2024,2024, 0, 0, 13220, 2466,0 }, + {2025,2025, 0, 0, 333, 26,0 }, + {2026,2026, 0, 0, 7346, 2440,0 }, + {2027,2027, 0, 0, 1273, 453,0 }, + { 352, 352, 51, 2, 6, 0,0 }, + {2028,2028, 35, 0, 700, 253,0 }, + {2028,2028, 36, 0, 706, 266,0 }, + {2029,2029, 47, 0, 100, 0,0 }, + {2030,2030, 38, 0, 346, 140,0 }, + {2019,2019, 39, 0, 220, 106,0 }, + {2031,2031, 45, 0, 286, 133,0 }, + { 492, 492, 41, 0, 1040, 406,0 }, + {2032,2032, 42, 0, 220, 106,0 }, + {2033,2033, 44, 0, 500, 193,0 }, + { 492, 492, 48, 0, 833, 346,0 }, + {2034,2034, 46, 0, 1866, 646,0 }, + { 492, 492, 53, 0, 873, 386,0 }, + { 167, 167, 56, 0, 646, 353,0 }, + {2035,2035, 61, 0, 366, 146,0 }, + {2036,2036, 56, 0, 1346, 473,0 }, + {2037,2037, 60, 0, 213, 126,0 }, + { 144, 144, 59, 0, 213, 0,0 }, + {2038,2038, 59, 0, 106, 0,0 }, + { 169, 169, 51, 0, 380, 366,0 }, + { 169, 169, 45, 0, 380, 366,0 }, + {2039,2039, 72, 0, 246, 20,0 }, + {2040,2040, 60, 0, 280, 20,0 }, + {2041,2041, 58, 0, 373, 360,0 }, + {2042,2042, 53, 0, 380, 366,0 }, + {2043,2043, 73, 0, 120, 26,0 }, + { 158, 158, 75, 0, 126, 140,0 }, + {2044,2044, 0, 0, 6786, 1073,0 }, + {2045,2045, 0, 0, 2046, 473,0 }, + {2046,2046, 0, 0, 3746, 1273,0 }, + {2047,2047, 0, 0, 1200, 3086,0 }, + {2048,2048, 0, 0, 1200, 3080,0 }, + {2049,2049, 0, 0, 40000, 2453,0 }, + {2050,2050, 0, 0, 40000, 413,0 }, + {2051,2051, 0, 0, 980, 2553,0 }, + {2052,2052, 0, 0, 40000, 2420,0 }, + {2053,2053, 0, 0, 40000, 2506,0 }, + {2054,2054, 0, 0, 40000, 380,0 }, + {2055,2055, 0, 0, 40000, 660,0 }, + {2056,2056, 0, 0, 40000, 73,0 }, + {2057,2057, 0, 0, 40000, 333,0 }, + {2058,2058, 0, 0, 833, 146,0 }, + {2059,2059, 0, 0, 1686, 620,0 }, + {2060,2060, 0, 0, 40000, 73,0 }, + {2061,2061, 0, 0, 40000, 0,0 }, + {2062,2062, 0, 0, 1873, 633,0 }, + {2063,2063, 0, 0, 40000, 380,0 }, + {2064,2064, 0, 0, 366, 286,0 }, + {2065,2065, 0, 0, 8866, 1366,0 }, + {2066,2066, 0, 0, 40000, 1513,0 }, + {2067,2067, 0, 0, 40000, 333,0 }, + {2068,2068, 0, 0, 9600, 1573,0 }, + {2069,2069, 0, 0, 3293, 746,0 }, + {2070,2070, 0, 0, 40000, 53,0 }, + {2071,2071, 0, 0, 40000, 73,0 }, + {2072,2072, 0, 0, 40000, 73,0 }, + {2073,2073, 0, 0, 40000, 240,0 }, + {2074,2074, 0, 0, 40000, 240,0 }, + {2075,2075, 0, 0, 40000, 140,0 }, + {2076,2076, 0, 0, 40000, 113,0 }, + {2077,2077, 0, 0, 40000, 240,0 }, + {2078,2078, 0, 0, 3613, 1146,0 }, + {2079,2079, 0, 0, 40000, 126,0 }, + {2080,2080, 0, 0, 40000, 0,0 }, + {2081,2081, 0, 0, 40000, 633,0 }, + {2082,2082, 0, 0, 40000, 453,0 }, + {2083,2083, 0, 0, 40000, 1146,0 }, + {2084,2084, 0, 0, 40000, 3600,0 }, + {2085,2085, 0, 0, 40000, 1586,0 }, + {2086,2086, 0, 0, 40000, 1586,0 }, + {2087,2087, 0, 0, 40000, 1586,0 }, + {2088,2088, 0, 0, 40000, 1646,0 }, + {2089,2089, 0, 0, 40000, 1580,0 }, + {2090,2090, 0, 0, 40000, 4393,0 }, + {2091,2091, 0, 0, 40000, 4540,0 }, + {2092,2092, 0, 0, 21373, 6160,0 }, + {2093,2093, 0, 0, 40000, 633,0 }, + {2094,2094, 0, 0, 18420, 6146,0 }, + {2095,2095, 0, 0, 2306, 813,0 }, + {2096,2096, 0, 0, 2813, 333,0 }, + {2097,2097, 0, 0, 3106, 600,0 }, + {2098,2098, 0, 0, 1026, 1580,0 }, + {2099,2099, 0, 0, 1873, 346,0 }, + {2100,2100, 0, 0, 40000, 73,0 }, + {2101,2101, 0, 0, 40000, 73,0 }, + {2102,2102, 0, 0, 1200, 1906,0 }, + {2103,2103, 0, 0, 980, 1313,0 }, + {2104,2104, 0, 0, 200, 20,0 }, + {2105,2105, 0, 0, 640, 253,0 }, + {2106,2106, 0, 0, 3120, 240,0 }, + {2107,2107, 0, 0, 753, 146,0 }, + {2108,2108, 0, 0, 40000, 3060,0 }, + {2109,2109, 0, 0, 40000, 233,0 }, + {2110,2110, 0, 0, 40000, 246,0 }, + {2111,2111, 0, 0, 40000, 240,0 }, + { 752, 752, 60, 0, 173, 20,0 }, + { 755, 755, 12, 0, 626, 240,0 }, + {2112,2112, 89, 0, 113, 0,0 }, + {2113,2113, 89, 0, 700, 266,0 }, + { 755, 755, 14, 0, 626, 240,0 }, + { 755, 755, 16, 0, 626, 246,0 }, + {2114,2114, 84, 0, 1593, 553,0 }, + { 755, 755, 19, 0, 626, 240,0 }, + {2115,2115, 38, 0, 220, 166,0 }, + {2116,2116, 36, 0, 1686, 760,0 }, + { 755, 755, 28, 0, 626, 240,0 }, + { 755, 755, 26, 0, 626, 240,0 }, + { 755, 755, 35, 0, 633, 246,0 }, + { 755, 755, 30, 0, 626, 240,0 }, + {2117,2117, 60, 0, 180, 53,0 }, + {2104,2104, 60, 0, 173, 20,0 }, + {2104,2104, 55, 0, 173, 20,0 }, + { 730, 730, 94, 0, 1886, 660,0 }, + {2118,2118, 0, 0, 1226, 73,0 }, + {2119,2119, 0, 0, 40000, 0,0 }, + {2120,2120, 0, 0, 40000, 146,0 }, + {2121,2121, 0, 0, 40000, 80,0 }, + {2122,2122, 0, 0, 40000, 80,0 }, + {2123,2123, 0, 0, 40000, 0,0 }, + {2124,2124, 0, 0, 40000, 126,0 }, + {2125,2125, 0, 0, 40000, 213,0 }, + {2126,2126, 0, 0, 40000, 80,0 }, + {2127,2127, 0, 0, 40000, 73,0 }, + {2128,2128, 0, 0, 40000, 73,0 }, + {2129,2129, 0, 0, 40000, 73,0 }, + {2130,2130, 0, 0, 40000, 80,0 }, + {2131,2131, 0, 0, 40000, 73,0 }, + {2132,2132, 0, 0, 40000, 73,0 }, + {2133,2133, 0, 0, 40000, 66,0 }, + {2134,2134, 0, 0, 40000, 186,0 }, + {2135,2135, 0, 0, 9966, 426,0 }, + {2136,2136, 0, 0, 40000, 400,0 }, + {2137,2137, 0, 0, 40000, 326,0 }, + {2138,2138, 0, 0, 386, 80,0 }, + {2139,2139, 0, 0, 40000, 246,0 }, + {2140,2140, 0, 0, 3473, 73,0 }, + {2141,2141, 60, 0, 160, 66,0 }, + {2141,2141, 44, 0, 160, 60,0 }, + {2142,2142, 47, 0, 173, 93,0 }, + {2143,2143, 47, 0, 186, 80,0 }, + {2144,2144, 62, 0, 1933, 93,0 }, + {2145,2145, 93, 0, 1146, 473,0 }, + {2146,2146, 50, 0, 286, 93,0 }, + {2145,2145, 40, 0, 2013, 840,0 }, + {2147,2147, 60, 0, 106, 73,0 }, + { 898, 898, 60, 0, 173, 133,0 }, + {2147,2147, 57, 0, 106, 73,0 }, + { 900, 900, 42, 0, 620, 240,0 }, + { 900, 900, 38, 0, 626, 240,0 }, + { 908, 908, 88, 0, 160, 26,0 }, + {2148,2148, 0, 0, 9440, 140,0 }, + {2149,2149, 0, 0, 40000, 73,0 }, + {2150,2150, 0, 0, 4613, 420,0 }, + {2151,2151, 0, 0, 40000, 86,0 }, + {2152,2152, 0, 0, 40000, 406,0 }, + {2153,2153, 0, 0, 40000, 440,0 }, + {2154,2154, 0, 0, 4340, 133,0 }, + {2155,2155, 0, 0, 4460, 706,0 }, + {2156,2156, 0, 0, 40000, 73,0 }, + {2157,2157, 0, 0, 4660, 1573,0 }, + {2158,2158, 0, 0, 966, 333,0 }, + {2159,2159, 0, 0, 1933, 640,0 }, + { 136, 136, 0, 0, 2326, 786,0 }, + { 168, 168, 0, 0, 286, 366,0 }, + { 164, 164, 0, 0, 7373, 2460,0 }, + { 167, 167, 0, 0, 793, 426,0 }, + {2160,2160, 65, 0, 166, 73,0 }, + {2161,2161, 21, 0, 480, 146,0 }, + {2162, 173, 0, 4, 4220, 80,0 }, + {2163,2164, 0, 4, 4640, 3066,0 }, + {2165,2166, 0, 4, 7273, 3920,0 }, + {2167,2168, 0, 4, 3766, 1253,0 }, + {2169,2170, 0, 4, 6266, 2400,0 }, + {2171,2172, 0, 4, 18213, 0,0 }, + {2173,2174, 0, 4, 40000, 713,0 }, + {2175,2174, 0, 4, 40000, 733,0 }, + {2176, 299, 0, 4, 40000, 273,0 }, + {2177,2178, 0, 4, 40000, 66,0 }, + {2179,2180, 0, 4, 40000, 393,0 }, + {2181,2182, 0, 4, 40000, 413,0 }, + {2183,2184, 0, 4, 7406, 200,0 }, + { 127, 127, 65, 0, 226, 120,0 }, + { 127, 127, 72, 0, 180, 100,0 }, + { 364, 365, 52, 4, 120, 0,0 }, + {2185,2186, 60, 4, 173, 0,0 }, + {1550,1551, 47, 4, 520, 0,0 }, + {1556,1557, 76, 4, 833, 0,0 }, + { 374, 375, 84, 4, 813, 0,0 }, + {1564,1565, 83, 4, 220, 0,0 }, + {1568,1569, 24, 4, 1840, 620,0 }, + {1556,1557, 77, 4, 820, 300,0 }, + {1572,1573, 60, 4, 286, 0,0 }, + {1574,1575, 65, 4, 293, 0,0 }, + { 391, 392, 44, 4, 160, 53,0 }, + { 391, 393, 40, 4, 460, 66,0 }, + {1606,1607, 72, 4, 120, 73,0 }, + { 398, 399, 73, 4, 1293, 173,0 }, + {1608,1609, 70, 4, 1580, 0,0 }, + {2187,2187, 0, 0, 40000, 353,0 }, + {2188,2188, 0, 0, 40000, 333,0 }, + {2189,2189, 0, 0, 5913, 2306,0 }, + {2190,2190, 0, 0, 7720, 1260,0 }, + {2191,2191, 0, 0, 213, 6420,0 }, + {2192,2192, 0, 0, 40000, 380,0 }, + {2193,2193, 0, 0, 1153, 760,0 }, + {2194,2194, 0, 0, 40000, 66,0 }, + {2195,2195, 0, 0, 4440, 66,0 }, + {2196,2196, 0, 0, 40000, 73,0 }, + {2197,2197, 0, 0, 40000, 53,0 }, + {2198,2198, 0, 0, 40000, 60,0 }, + {2199,2199, 0, 0, 40000, 60,0 }, + {2200,2200, 0, 0, 8133, 1433,0 }, + { 528, 528, 0, 0, 966, 346,0 }, + {2201,2201, 0, 0, 40000, 126,0 }, + {2202,2202, 0, 0, 286, 1293,0 }, + {2203,2203, 0, 0, 40000, 0,0 }, + {2204,2204, 41, 0, 246, 20,0 }, + {2205,2205, 84, 0, 160, 26,0 }, + {2206,2206, 72, 0, 440, 180,0 }, + { 741, 741, 48, 0, 220, 26,0 }, + {2207,2207, 0, 0, 2126, 173,0 }, + {2208,2208, 0, 0, 40000, 0,0 }, + {2209,2209, 0, 0, 40000, 380,0 }, + {2210,2210, 0, 0, 4553, 1486,0 }, + {2211,2211, 0, 0, 40000, 73,0 }, + {2212,2212, 0, 0, 40000, 73,0 }, + {2213,2213, 0, 0, 1460, 80,0 }, + {2214,2214, 0, 0, 40000, 66,0 }, + {2215,2215, 0, 0, 40000, 186,0 }, + {2216,2216, 0, 0, 40000, 180,0 }, + {2217,2217, 0, 0, 40000, 173,0 }, + {2218,2218, 0, 0, 40000, 113,0 }, + {2219,2219, 0, 0, 40000, 86,0 }, + {2220,2220, 0, 0, 40000, 373,0 }, + {2221,2221, 0, 0, 40000, 113,0 }, + {2222,2222, 0, 0, 40000, 353,0 }, + {2223,2223, 0, 0, 40000, 66,0 }, + {2224,2224, 0, 0, 40000, 53,0 }, + {2225,2225, 0, 0, 40000, 66,0 }, + {2226,2226, 0, 0, 40000, 100,0 }, + {2227,2227, 0, 0, 40000, 73,0 }, + {2228,2228, 0, 0, 40000, 73,0 }, + {2229,2229, 0, 0, 40000, 66,0 }, + {2230,2230, 0, 0, 40000, 66,0 }, + {2231,2231, 0, 0, 40000, 80,0 }, + {2232,2232, 0, 0, 40000, 66,0 }, + {2233,2233, 0, 0, 40000, 80,0 }, + {2234,2234, 0, 0, 40000, 660,0 }, + {2235,2235, 0, 0, 40000, 120,0 }, + {2236,2236, 0, 0, 9820, 393,0 }, + {2237,2237, 0, 0, 40000, 73,0 }, + {2238,2238, 0, 0, 3620, 1166,0 }, + {2239,2239, 0, 0, 40000, 0,0 }, + {2240,2240, 0, 0, 40000, 0,0 }, + {2241,2241, 0, 0, 3020, 66,0 }, + {2242,2242, 0, 0, 6053, 1240,0 }, + {2243,2243, 0, 0, 633, 126,0 }, + {2244,2244, 0, 0, 40000, 66,0 }, + {2245,2245, 0, 0, 40000, 73,0 }, + {2246,2246, 0, 0, 626, 246,0 }, + {2247,2247, 60, 0, 173, 93,0 }, + {2248,2248, 60, 0, 673, 206,0 }, + {2249,2249, 48, 0, 673, 200,0 }, + {2250,2250, 60, 0, 1873, 653,0 }, + {2251,2251, 60, 0, 673, 200,0 }, + {2252,2252, 66, 0, 306, 120,0 }, + {2253,2253, 60, 0, 673, 200,0 }, + {2249,2249, 64, 0, 673, 206,0 }, + {2254,2254, 60, 0, 246, 106,0 }, + {2255,2255, 60, 0, 193, 120,0 }, + {2256,2256, 56, 0, 206, 13,0 }, + {2257,2257, 53, 0, 433, 73,0 }, + {2258,2258, 60, 0, 220, 113,0 }, + {2259,2259, 48, 0, 300, 66,0 }, + {2260,2260, 67, 0, 273, 60,0 }, + {2261,2261, 60, 0, 973, 360,0 }, + {2261,2261, 72, 0, 806, 273,0 }, + {2262,2262, 60, 0, 173, 93,0 }, + {2263,2263, 0, 0, 2493, 866,0 }, + {2264,2264, 24, 0, 173, 93,0 }, + {2265,2265, 36, 0, 140, 0,0 }, + { 343, 343, 36, 0, 146, 80,0 }, + { 347, 347, 0, 0, 353, 133,0 }, + { 347, 347, 12, 0, 420, 146,0 }, + {2266,2266, 12, 0, 346, 100,0 }, + {2267,2267, 24, 0, 106, 46,0 }, + {2267,2267, 36, 0, 100, 0,0 }, + {2268,2268, 0, 0, 1006, 293,0 }, + {2266,2266, 24, 0, 293, 93,0 }, + {2269,2269, 88, 0, 1106, 120,0 }, + {2270,2270, 88, 0, 666, 120,0 }, + {2271,2271, 13, 0, 760, 360,0 }, + { 351, 351, 0, 0, 966, 346,0 }, + {2271,2271, 15, 0, 760, 420,0 }, + {2272,2272, 0, 0, 4513, 640,0 }, + {2273,2273, 0, 0, 15486, 1580,0 }, + {2274,2274, 0, 0, 6940, 66,0 }, + {2275,2275, 0, 0, 6866, 2380,0 }, + {2276,2276, 0, 0, 7613, 1566,0 }, + {2277,2277, 0, 0, 1186, 420,0 }, + {2278,2278, 0, 0, 1166, 400,0 }, + {2279,2279, 0, 0, 40000, 2940,0 }, + {2280,2280, 0, 0, 40000, 0,0 }, + {2281,2281, 0, 0, 18226, 786,0 }, + {2282,2282, 0, 0, 40000, 0,0 }, + {2283,2283, 0, 0, 713, 200,0 }, + {2284,2284, 0, 0, 40000, 126,0 }, + {2285,2285, 0, 0, 40000, 353,0 }, + {2286,2286, 0, 0, 40000, 333,0 }, + {2287,2287, 0, 0, 40000, 0,0 }, + {2288,2288, 0, 0, 40000, 0,0 }, + {2289,2289, 0, 0, 40000, 0,0 }, + {2290,2290, 0, 0, 40000, 0,0 }, + {2291,2291, 0, 0, 40000, 73,0 }, + {2292,2292, 0, 0, 40000, 66,0 }, + {2293,2293, 0, 0, 15893, 153,0 }, + {2294,2294, 0, 0, 40000, 253,0 }, + {2295,2295, 0, 0, 2813, 333,0 }, + {2296,2296, 0, 0, 40000, 3920,0 }, + {2297,2297, 79, 0, 113, 0,0 }, + {2297,2297, 72, 0, 126, 140,0 }, + {2298,2298, 72, 0, 100, 26,0 }, + {2298,2298, 79, 0, 100, 0,0 }, + { 554, 554, 60, 0, 400, 126,0 }, + {2299,2299, 72, 0, 793, 173,0 }, + {2300,2300, 84, 0, 226, 66,0 }, + { 555, 555, 66, 0, 113, 0,0 }, + {2301,2302, 35, 4, 2333, 800,0 }, + {2303,2304, 52, 4, 120, 0,0 }, + {2305,1548, 48, 4, 173, 0,0 }, + {1595,1595, 58, 0, 146, 166,0 }, + {2305,1548, 60, 4, 173, 0,0 }, + {2306,2307, 47, 4, 1893, 700,0 }, + {2306,2307, 43, 4, 1953, 740,0 }, + {2306,2307, 49, 4, 1880, 686,0 }, + {2306,2307, 51, 4, 1886, 706,0 }, + {2306,2307, 54, 4, 1906, 720,0 }, + {2306,2307, 57, 4, 1900, 720,0 }, + {2306,2307, 72, 4, 1593, 606,0 }, + {2306,2307, 60, 4, 1900, 720,0 }, + {2306,2307, 76, 4, 1593, 606,0 }, + {2306,2307, 84, 4, 1593, 613,0 }, + {2306,2307, 36, 4, 2386, 920,0 }, + {1560,2308, 65, 4, 293, 213,0 }, + {2309,2310, 84, 4, 1373, 306,0 }, + {1564,1564, 83, 0, 220, 113,0 }, + { 380, 381, 84, 4, 1593, 566,0 }, + {1568,1568, 24, 0, 1833, 613,0 }, + {2306,2307, 77, 4, 1593, 606,0 }, + {2311,2312, 60, 4, 286, 0,0 }, + {2313,2314, 65, 4, 513, 0,0 }, + {2315,2315, 59, 0, 106, 0,0 }, + {2316,2316, 51, 0, 386, 373,0 }, + {1612,1612, 45, 0, 393, 380,0 }, + {2317,2317, 71, 0, 446, 180,0 }, + {2318,2318, 60, 0, 280, 20,0 }, + {2319,2319, 58, 0, 393, 373,0 }, + {2320,2320, 53, 0, 393, 380,0 }, + { 397, 397, 64, 0, 220, 86,0 }, + {2321,2321, 71, 0, 106, 46,0 }, + {2322,2322, 61, 0, 986, 340,0 }, + {2323,2323, 61, 0, 1893, 633,0 }, + {2324, 392, 44, 4, 166, 46,0 }, + {2324, 393, 40, 4, 460, 60,0 }, + {1595,1595, 69, 0, 126, 140,0 }, + {1595,1595, 68, 0, 126, 140,0 }, + {1595,1595, 63, 0, 146, 166,0 }, + {2325,2326, 74, 4, 380, 106,0 }, + {2327,2328, 60, 4, 1026, 333,0 }, + {2329,2330, 80, 4, 40000, 0,0 }, + {2331,2332, 64, 4, 1900, 640,0 }, + { 397, 397, 72, 0, 193, 80,0 }, + {2333,2334, 78, 4, 820, 0,0 }, + {1608,1609, 82, 4, 1580, 0,0 }, + {2315,2315, 48, 0, 106, 0,0 }, + {2316,2316, 53, 0, 386, 373,0 }, + {2335,2335, 0, 0, 3586, 1133,0 }, + {2336,2337, 0, 4, 1186, 420,0 }, + {2338,2339, 0, 4, 40000, 320,0 }, + {2340,2340, 0, 0, 8826, 1346,0 }, + {2341,2341, 0, 0, 3440, 753,0 }, + {2342,2342, 0, 0, 40000, 360,0 }, + {2343,2343, 0, 0, 40000, 413,0 }, + {2344,2345, 0, 4, 40000, 60,0 }, + {2346,2346, 0, 0, 40000, 60,0 }, + {2347,2348, 0, 4, 40000, 126,0 }, + {2349,2350, 0, 4, 40000, 73,0 }, + {2351,2352, 0, 4, 40000, 73,0 }, + {2353,2354, 0, 4, 40000, 86,0 }, + {2355,2356, 0, 4, 40000, 453,0 }, + {2357,2357, 14, 0, 186, 20,0 }, + {2358,2358, 35, 0, 246, 73,0 }, + {2357,2357, 19, 0, 166, 26,0 }, + {2359,2359, 43, 0, 286, 133,0 }, + {2360,2360, 41, 0, 300, 113,0 }, + {2360,2360, 43, 0, 253, 106,0 }, + {2360,2360, 45, 0, 240, 100,0 }, + {2360,2360, 47, 0, 240, 100,0 }, + {2361,2362, 0, 4, 14720, 333,0 }, + {2363,2363, 0, 0, 7373, 1246,0 }, + {2364,2364, 0, 0, 4900, 233,0 }, + {2365,2365, 0, 0, 5106, 606,0 }, + {2366,2366, 0, 0, 1333, 153,0 }, + {2367,2367, 0, 0, 2093, 840,0 }, + {2368,2368, 0, 0, 3700, 226,0 }, + {2369,2369, 0, 0, 3546, 0,0 }, + {2370,2370, 0, 0, 4606, 420,0 }, + {2371,2371, 0, 0, 14366, 606,0 }, + {2372,2372, 0, 0, 40000, 426,0 }, + {2373,2373, 0, 0, 3700, 200,0 }, + {2374,2374, 0, 0, 880, 440,0 }, + {2375,2375, 0, 0, 4660, 660,0 }, + {2376,2376, 0, 0, 3600, 1153,0 }, + {2377,2377, 0, 0, 40000, 73,0 }, + {2378,2378, 0, 0, 40000, 53,0 }, + {2379,2379, 0, 0, 40000, 333,0 }, + {2380,2380, 0, 0, 40000, 73,0 }, + {2381,2381, 0, 0, 40000, 73,0 }, + {2382,2382, 0, 0, 40000, 66,0 }, + {2383,2383, 0, 0, 40000, 73,0 }, + {2384,2384, 0, 0, 40000, 73,0 }, + {2385,2385, 0, 0, 840, 226,0 }, + {2386,2386, 0, 0, 2093, 86,0 }, + {2387,2387, 0, 0, 906, 73,0 }, + { 402, 402, 0, 0, 273, 60,0 }, + {2388,2388, 0, 0, 40000, 820,0 }, + {2389,2389, 0, 0, 4740, 93,0 }, + {2390,2390, 0, 0, 706, 106,0 }, + {2391,2391, 0, 0, 40000, 0,0 }, + {2392,2392, 0, 0, 3840, 2306,0 }, + {2393,2393, 0, 0, 3400, 493,0 }, + {2394,2394, 0, 0, 40000, 53,0 }, + {2395,2395, 0, 0, 40000, 133,0 }, + {2396,2397, 0, 4, 3093, 1400,0 }, + {2398,2398, 0, 0, 1080, 580,0 }, + {2399,2400, 0, 4, 2220, 400,0 }, + {2401,2401, 0, 0, 40000, 193,0 }, + {2402,2402, 0, 0, 40000, 60,0 }, + {2403,2404, 0, 4, 40000, 146,0 }, + {2405,2406, 0, 4, 40000, 133,0 }, + {2407,2408, 0, 4, 40000, 66,0 }, + {2409,2409, 0, 0, 40000, 0,0 }, + {2410,2410, 0, 0, 40000, 73,0 }, + {2411,2411, 0, 0, 40000, 66,0 }, + {2412,2413, 0, 4, 40000, 153,0 }, + {2414,2414, 0, 0, 40000, 126,0 }, + {2415,2416, 0, 4, 40000, 466,0 }, + {2417,2418, 0, 4, 40000, 113,0 }, + {2419,2420, 0, 4, 1280, 73,0 }, + {2421,2422, 0, 4, 1113, 146,0 }, + {2423,2424, 0, 4, 3660, 113,0 }, + {2425,2426, 0, 4, 40000, 80,0 }, + {2427,2427, 33, 0, 300, 246,0 }, + {2428,2429, 38, 4, 53, 0,0 }, + {2430,2430, 38, 0, 106, 0,0 }, + {2431,2431, 38, 0, 340, 20,0 }, + {2432,2432, 40, 0, 73, 0,0 }, + {2433,2434, 41, 4, 300, 0,0 }, + {2435,2435, 0, 0, 133, 73,0 }, + {2435,2435, 41, 0, 133, 73,0 }, + {2360,2360, 48, 0, 240, 100,0 }, + {2436,2436, 17, 0, 4620, 1553,0 }, + {2360,2360, 50, 0, 240, 100,0 }, + {2435,2435, 45, 0, 126, 66,0 }, + {2437,2437,254, 2, 6, 0,0 }, + {2438,2438, 60, 0, 226, 93,0 }, + {2439,2439, 56, 0, 233, 93,0 }, + {2440,2440, 60, 0, 140, 66,0 }, + {2440,2440, 55, 0, 140, 60,0 }, + {2441,2441, 63, 0, 286, 126,0 }, + {2442,2442, 57, 0, 173, 93,0 }, + {2443,2443, 0, 0, 40000, 280,0 }, + {2444,2444, 0, 0, 40000, 0,0 }, + {2445,2445, 0, 0, 40000, 746,0 }, + {2446,2446, 0, 0, 40000, 353,0 }, + {2447,2447, 0, 0, 40000, 1173,0 }, + {2448,2448, 0, 0, 40000, 146,0 }, + {2449,2449, 0, 0, 40000, 1160,0 }, + {2450,2450, 0, 0, 40000, 353,0 }, + {2451,2451, 0, 0, 18313, 6046,0 }, + {2452,2452, 0, 0, 1206, 420,0 }, + { 752, 752, 55, 0, 173, 20,0 }, + {2453,2453, 0, 0, 2860, 806,0 }, + {2454,2454, 0, 0, 2506, 126,0 }, + {2455,2455, 0, 0, 520, 93,0 }, + {2456,2456, 0, 0, 1420, 160,0 }, + {2457,2457, 0, 0, 40000, 53,0 }, + {2458,2458, 0, 0, 9106, 100,0 }, + {2459,2459, 0, 0, 3706, 100,0 }, + {2460,2460, 0, 0, 17933, 100,0 }, + {2461,2461, 0, 0, 40000, 0,0 }, + {2462,2462, 0, 0, 40000, 66,0 }, + {2463,2463, 0, 0, 40000, 0,0 }, + { 884, 884, 0, 0, 306, 73,0 }, + { 884, 884, 28, 0, 306, 73,0 }, + {2464,2464, 29, 0, 226, 93,0 }, + { 886, 886, 31, 0, 113, 0,0 }, + { 360, 360, 32, 0, 133, 40,0 }, + { 361, 361, 33, 0, 286, 80,0 }, + {2453,2453, 34, 0, 2873, 813,0 }, + { 888, 888, 29, 0, 246, 46,0 }, + { 886, 886, 55, 0, 100, 0,0 }, + { 890, 890, 48, 0, 240, 60,0 }, + { 884, 884, 58, 0, 146, 26,0 }, + {2465,2465, 45, 0, 173, 93,0 }, + {2465,2465, 43, 0, 173, 93,0 }, + {2466,2466, 73, 0, 1633, 86,0 }, + {2467,2467, 72, 0, 866, 553,0 }, + {2468,2468, 76, 0, 1380, 0,0 }, + {2467,2467, 84, 0, 873, 560,0 }, + {2468,2468, 36, 0, 1933, 880,0 }, + {2469,2469, 65, 0, 300, 120,0 }, + {2470,2470, 83, 0, 193, 86,0 }, + {2471,2471, 50, 0, 966, 126,0 }, + {2468,2468, 77, 0, 1373, 620,0 }, + { 897, 897, 55, 0, 126, 40,0 }, + {2472,2472, 60, 0, 180, 140,0 }, + { 897, 897, 50, 0, 126, 40,0 }, + {2473,2473, 42, 0, 633, 240,0 }, + {2473,2473, 46, 0, 513, 200,0 }, + {2474,2474, 71, 0, 433, 180,0 }, + {2474,2474, 60, 0, 513, 206,0 }, + {2455,2455, 58, 0, 220, 46,0 }, + {2455,2455, 53, 0, 286, 60,0 }, + {2475,2475, 91, 0, 186, 100,0 }, + {2476,2476, 61, 0, 226, 26,0 }, + {2477,2477, 61, 0, 886, 73,0 }, + {2478,2478, 44, 0, 120, 73,0 }, + {2479,2479, 40, 0, 933, 73,0 }, + {2480,2480, 69, 0, 146, 33,0 }, + { 361, 361, 68, 0, 153, 26,0 }, + { 361, 361, 63, 0, 180, 26,0 }, + {2481,2481, 74, 0, 153, 73,0 }, + {2482,2482, 60, 0, 280, 100,0 }, + { 908, 908, 80, 0, 160, 26,0 }, + {2483,2483, 64, 0, 986, 353,0 }, + {2483,2483, 73, 0, 813, 306,0 }, + {2483,2483, 70, 0, 820, 306,0 }, + { 886, 886, 68, 0, 93, 0,0 }, + { 886, 886, 48, 0, 106, 0,0 }, + {2484,2484, 0, 0, 40000, 0,0 }, + {2485,2485, 0, 0, 3226, 753,0 }, + {2486,2486, 0, 0, 1773, 553,0 }, + {2487,2487, 0, 0, 7473, 2460,0 }, + {2488,2488, 0, 0, 40000, 0,0 }, + {2489,2489, 0, 0, 40000, 353,0 }, + {2490,2490, 0, 0, 40000, 206,0 }, + {2491,2491, 0, 0, 40000, 86,0 }, + {2492,2492, 0, 0, 4740, 86,0 }, + {2493,2493, 0, 0, 6193, 193,0 }, + {2494,2494, 0, 0, 6200, 240,0 }, + {2495,2495, 0, 0, 40000, 0,0 }, + {2496,2496, 0, 0, 1586, 73,0 }, + {2497,2497, 0, 0, 560, 73,0 }, + {2498,2498, 0, 0, 40000, 480,0 }, + {2499,2499, 0, 0, 40000, 80,0 }, + {2500,2500, 0, 0, 40000, 66,0 }, + {2501,2501, 0, 0, 40000, 380,0 }, + {2502,2502, 0, 0, 280, 100,0 }, + {2503,2503, 0, 0, 6193, 233,0 }, + {2504,2504, 0, 0, 40000, 380,0 }, + {2505,2505, 0, 0, 40000, 0,0 }, + {2506,2506, 0, 0, 40000, 380,0 }, + {2507,2507, 0, 0, 40000, 200,0 }, + {2508,2508, 0, 0, 40000, 320,0 }, + {2509,2509, 0, 0, 40000, 126,0 }, + {2510,2510, 0, 0, 40000, 293,0 }, + {2511,2511, 0, 0, 40000, 0,0 }, + {2512,2512, 0, 0, 40000, 40,0 }, + {2513,2513, 0, 0, 40000, 106,0 }, + {2514,2514, 0, 0, 3846, 73,0 }, + {2515,2515, 0, 0, 40000, 0,0 }, + {2516,2516, 0, 0, 40000, 73,0 }, + {2517,2517, 0, 0, 40000, 533,0 }, + {2518,2518, 0, 0, 40000, 1020,0 }, + {2519,2519, 0, 0, 40000, 73,0 }, + {2520,2520, 0, 0, 40000, 53,0 }, + {2521,2521, 0, 0, 6153, 1433,0 }, + {2522,2522, 0, 0, 18813, 773,0 }, + {2523,2523, 0, 0, 40000, 433,0 }, + {2524,2524, 0, 0, 40000, 0,0 }, + {2525,2525, 0, 0, 40000, 133,0 }, + {2526,2526, 0, 0, 4486, 73,0 }, + { 346, 346, 30, 0, 540, 33,0 }, + { 346, 346, 31, 0, 406, 20,0 }, + { 346, 346, 32, 0, 406, 20,0 }, + { 346, 346, 33, 0, 406, 73,0 }, + { 346, 346, 34, 0, 406, 20,0 }, + { 346, 346, 35, 0, 406, 20,0 }, + { 346, 346, 37, 0, 406, 73,0 }, + { 346, 346, 39, 0, 406, 73,0 }, + { 346, 346, 41, 0, 406, 20,0 }, + { 346, 346, 43, 0, 306, 20,0 }, + { 346, 346, 45, 0, 306, 20,0 }, + { 346, 346, 47, 0, 306, 20,0 }, + { 346, 346, 48, 0, 306, 20,0 }, + { 346, 346, 49, 0, 306, 20,0 }, + { 512, 512, 84, 0, 353, 466,0 }, + {2206,2206, 84, 0, 440, 180,0 }, + {2527,2527, 55, 0, 100, 0,0 }, + {2528,2528, 36, 0, 400, 160,0 }, + {2529,2529, 38, 0, 313, 226,0 }, + {2530,2530, 60, 0, 286, 133,0 }, + {2531,2531, 38, 0, 200, 100,0 }, + {2532,2532, 17, 0, 6186, 240,0 }, + {2532,2532, 18, 0, 6186, 240,0 }, + {2532,2532, 19, 0, 6193, 233,0 }, + {2532,2532, 20, 0, 6193, 193,0 }, + {2532,2532, 21, 0, 6193, 193,0 }, + {2532,2532, 22, 0, 6193, 193,0 }, + {2532,2532, 23, 0, 6193, 193,0 }, + {2532,2532, 24, 0, 6193, 193,0 }, + {2532,2532, 25, 0, 6193, 193,0 }, + {2532,2532, 26, 0, 6193, 193,0 }, + {2532,2532, 27, 0, 6193, 253,0 }, + {2532,2532, 28, 0, 6193, 246,0 }, + {2532,2532, 29, 0, 6193, 246,0 }, + {2533,2533, 84, 0, 433, 180,0 }, + {2534,2534, 48, 0, 280, 93,0 }, + {2535,2535, 65, 0, 1166, 360,0 }, + {2536,2536, 65, 0, 1853, 633,0 }, + {2537,2537, 55, 0, 453, 366,0 }, + {2537,2537, 41, 0, 540, 433,0 }, + { 346, 346, 63, 0, 240, 66,0 }, + { 346, 346, 55, 0, 240, 66,0 }, + {2538,2538, 55, 0, 2586, 200,0 }, + {2538,2538, 53, 0, 2586, 200,0 }, + {2534,2534, 50, 0, 280, 93,0 }, + { 506, 506, 84, 0, 693, 566,0 }, + { 506, 506, 74, 0, 693, 560,0 }, + { 504, 504, 84, 0, 1566, 546,0 }, + { 504, 504, 74, 0, 1586, 560,0 }, + {2539,2539, 84, 0, 440, 20,0 }, + {2540,2540, 74, 0, 126, 26,0 }, + {1911,1911, 48, 0, 500, 180,0 }, + {1911,1911, 36, 0, 606, 220,0 }, + {2541,2541, 74, 0, 686, 560,0 }, + {2542,2542, 0, 0, 7313, 13,0 }, + {2543,2543, 0, 0, 40000, 1306,0 }, + {2544,2544, 0, 0, 40000, 0,0 }, + {2545,2545, 0, 0, 4613, 13,0 }, + {2546,2547, 0, 4, 6933, 133,0 }, + {2548,2549, 0, 4, 40000, 86,0 }, + {2550,2550, 0, 0, 9233, 100,0 }, + {2551,2552, 0, 4, 4640, 73,0 }, + {2553,2553, 0, 0, 40000, 73,0 }, + {2554,2554, 0, 0, 40000, 0,0 }, + {2555,2556, 0, 4, 40000, 73,0 }, + {2557,2557, 0, 0, 40000, 60,0 }, + {2558,1467, 0, 4, 40000, 66,0 }, + {2559,2560, 0, 4, 40000, 40,0 }, + {2561,2561, 0, 0, 40000, 186,0 }, + {2562,2562, 0, 0, 4026, 66,0 }, + {2563,2564, 0, 4, 14586, 80,0 }, + {2565,2565, 0, 0, 40000, 0,0 }, + {2566,2567, 0, 4, 40000, 40,0 }, + {2568,2568, 0, 0, 4020, 73,0 }, + {2569,2569, 0, 0, 40000, 0,0 }, + {2570,2570, 0, 0, 40000, 0,0 }, + {2571,2572, 0, 4, 40000, 126,0 }, + {2573,2574, 0, 4, 40000, 100,0 }, + {2575,2575, 0, 0, 40000, 213,0 }, + { 229,2576, 0, 4, 40000, 166,0 }, + {2577,2577, 0, 0, 7366, 53,0 }, + { 239,2578, 0, 4, 40000, 133,0 }, + {2579,2579, 0, 0, 40000, 80,0 }, + {2580,2580, 0, 0, 40000, 140,0 }, + {2581,2582, 0, 4, 16980, 1173,0 }, + {2583,2584, 0, 4, 726, 100,0 }, + {2585,2586, 0, 4, 40000, 73,0 }, + {2587,2588, 0, 4, 40000, 73,0 }, + {2589,2589, 0, 0, 40000, 60,0 }, + {2590,2590, 0, 0, 40000, 80,0 }, + {2591,2592, 0, 4, 40000, 73,0 }, + {2593,2594, 0, 4, 40000, 60,0 }, + {2595,2595, 0, 0, 40000, 66,0 }, + {2596,2597, 0, 4, 40000, 66,0 }, + {2598,2599, 0, 4, 40000, 60,0 }, + {2600,2601, 0, 4, 40000, 173,0 }, + {2602,2602, 0, 0, 40000, 60,0 }, + {2603,2603, 0, 0, 40000, 73,0 }, + {2604,2604, 0, 0, 40000, 93,0 }, + {2605,2606, 0, 4, 40000, 73,0 }, + {2607,2607, 0, 0, 40000, 66,0 }, + {2608,2609, 0, 4, 40000, 66,0 }, + {2610,2610, 0, 0, 40000, 86,0 }, + {2611,2611, 0, 0, 40000, 60,0 }, + {2612,2612, 0, 0, 14286, 73,0 }, + {2613,2613, 0, 0, 40000, 0,0 }, + {2614,2615, 0, 4, 40000, 73,0 }, + {2616,2617, 0, 4, 40000, 66,0 }, + {2618,2619, 0, 4, 133, 0,0 }, + {2620,2621, 0, 4, 40000, 1280,0 }, + {2622,2623, 0, 4, 40000, 160,0 }, + {2624,2625, 0, 4, 40000, 0,0 }, + {2626,2627, 0, 4, 40000, 73,0 }, + {2628,2629, 0, 4, 40000, 0,0 }, + {1516,2630, 0, 4, 1193, 406,0 }, + {2631,2632, 0, 4, 40000, 553,0 }, + {2633,2633, 0, 0, 40000, 40,0 }, + {2634,2635, 0, 4, 40000, 773,0 }, + {2636,2636, 0, 0, 40000, 320,0 }, + {2637,2637, 0, 0, 1880, 73,0 }, + {2638,2639, 0, 4, 486, 0,0 }, + {2640,2641, 0, 4, 17020, 1193,0 }, + {2642,2642, 0, 0, 40000, 720,0 }, + {2643,2644, 0, 4, 1880, 40,0 }, + {2645,2645, 0, 0, 40000, 73,0 }, + {2646,2647, 0, 4, 40000, 46,0 }, + {2648,2648, 0, 0, 2466, 80,0 }, + {2649,2649, 0, 0, 40000, 193,0 }, + {2650,2651, 0, 4, 993, 73,0 }, + {2652,2652, 0, 0, 40000, 220,0 }, + {2653,2654, 0, 4, 40000, 46,0 }, + {2655,2656, 0, 4, 40000, 46,0 }, + {2657,2657, 0, 0, 40000, 66,0 }, + {2658,2658, 35, 0, 626, 20,0 }, + {2659,2659, 35, 0, 306, 26,0 }, + {2660,2660, 52, 0, 126, 26,0 }, + {2661,2661, 60, 0, 286, 20,0 }, + {2662,2662, 58, 0, 113, 0,0 }, + {2663,2663, 60, 0, 380, 20,0 }, + {2664,2664, 50, 0, 1640, 66,0 }, + {2665,2665, 43, 0, 153, 20,0 }, + {2664,2664, 55, 0, 1640, 20,0 }, + {1553,1553, 43, 0, 160, 80,0 }, + {2666,2666, 50, 0, 980, 20,0 }, + {2667,2667, 43, 0, 446, 73,0 }, + {2666,2666, 53, 0, 1000, 80,0 }, + {2666,2666, 57, 0, 700, 73,0 }, + {2668,2668, 72, 0, 773, 13,0 }, + {2666,2666, 60, 0, 686, 20,0 }, + { 373, 373, 76, 0, 826, 20,0 }, + {2669,2669, 84, 0, 713, 20,0 }, + {2670,2670, 42, 0, 1186, 20,0 }, + {2671,2671, 65, 0, 293, 33,0 }, + {2672,2672, 84, 0, 386, 33,0 }, + {2673,2673, 84, 0, 1366, 20,0 }, + {2674,2674, 24, 0, 960, 73,0 }, + { 383, 383, 77, 0, 800, 20,0 }, + {2675,2675, 58, 0, 426, 26,0 }, + {2676,2676, 53, 0, 426, 20,0 }, + {2677,2677, 64, 0, 200, 66,0 }, + {2678,2678, 71, 0, 113, 13,0 }, + {2679,2679, 44, 0, 766, 66,0 }, + {2680,2680, 40, 0, 460, 60,0 }, + {2681,2681, 69, 0, 126, 26,0 }, + {2682,2682, 60, 0, 573, 66,0 }, + {2683,2683, 80, 0, 226, 20,0 }, + {2684,2684, 64, 0, 2693, 20,0 }, + {2685,2685, 72, 0, 120, 66,0 }, + {2686,2686, 70, 0, 820, 20,0 }, + {2687,2687, 48, 0, 173, 20,0 }, + {2688,2688, 53, 0, 980, 33,0 }, + {2689,2690, 0, 4, 40000, 286,0 }, + {2691,2692, 0, 4, 2340, 100,0 }, + {2693,2694, 0, 4, 380, 80,0 }, + {2695,2696, 0, 4, 14793, 73,0 }, + {2697,2698, 0, 4, 40000, 40,0 }, + { 192,2699, 0, 4, 40000, 73,0 }, + {2700,2701, 0, 4, 973, 126,0 }, + {2702,2703, 0, 4, 4666, 106,0 }, + {2704,2705, 0, 4, 40000, 73,0 }, + {2706,2707, 0, 4, 40000, 73,0 }, + {2708,2709, 0, 4, 40000, 73,0 }, + {2710,2711, 0, 4, 2053, 0,0 }, + {2712,1473, 0, 4, 320, 26,0 }, + {2713,2714, 0, 4, 573, 93,0 }, + {2715,2716, 0, 4, 6513, 0,0 }, + {1478,2717, 0, 4, 40000, 146,0 }, + {2718,2719, 0, 4, 40000, 66,0 }, + { 286,2720, 0, 4, 40000, 73,0 }, + {2721,2722, 0, 4, 40000, 86,0 }, + {2723,2724, 0, 4, 40000, 60,0 }, + {2725,2726, 0, 4, 393, 73,0 }, + {2727,2724, 0, 4, 40000, 60,0 }, + {1514,2728, 0, 4, 40000, 180,0 }, + {2729,2730, 0, 4, 40000, 0,0 }, + {2731,2732, 0, 4, 486, 0,0 }, + {2733,2734, 0, 4, 733, 0,0 }, + {2735,2736, 0, 4, 286, 40,0 }, + {2737,2738, 0, 4, 40000, 73,0 }, + {2739,2740, 0, 4, 1326, 746,0 }, + {2741,2742, 0, 4, 1340, 700,0 }, + {2743,2744, 0, 4, 40000, 0,0 }, + {2745,2746, 0, 4, 2046, 0,0 }, + {2747,2747, 35, 0, 386, 166,0 }, + {2748,2748, 60, 0, 493, 193,0 }, + {2749,2749, 43, 0, 126, 66,0 }, + {2750,2750, 0, 0, 3740, 1260,0 }, + {2751,2752, 0, 4, 14846, 353,0 }, + {2753,2754, 0, 4, 10266, 0,0 }, + {2755,2756, 0, 4, 18286, 146,0 }, + {2757,2758, 0, 4, 14520, 333,0 }, + {2759,2760, 0, 4, 14686, 633,0 }, + {2761,2762, 0, 4, 14826, 300,0 }, + {2763,2764, 0, 4, 10493, 0,0 }, + {2765,2766, 0, 4, 40000, 60,0 }, + {2767,2768, 0, 4, 40000, 80,0 }, + {2769,2770, 0, 4, 40000, 80,0 }, + {2771,2772, 0, 4, 40000, 73,0 }, + {2773,2774, 0, 4, 40000, 73,0 }, + {2775,2776, 0, 4, 40000, 80,0 }, + {2777,2778, 0, 4, 40000, 73,0 }, + {2779,2780, 0, 4, 40000, 73,0 }, + {2781,2782, 0, 4, 40000, 66,0 }, + {2783,2784, 0, 4, 7260, 186,0 }, + {2785,2786, 0, 4, 10386, 0,0 }, + {2787,2788, 0, 4, 40000, 246,0 }, + {2789,2790, 0, 4, 9173, 746,0 }, + {2791,2792, 0, 4, 7440, 666,0 }, + {2793,2794, 0, 4, 40000, 0,0 }, + {2795,2796, 0, 4, 40000, 413,0 }, + {2795,2797, 0, 4, 40000, 1506,0 }, + {2798,2799, 0, 4, 40000, 60,0 }, + {2800,2801, 0, 4, 40000, 233,0 }, + {2802,2803, 0, 4, 40000, 80,0 }, + {2804,2805, 0, 4, 40000, 80,0 }, + {2806,2807, 0, 4, 4520, 80,0 }, + {2808,2809, 0, 4, 40000, 73,0 }, + {2810,2811, 0, 4, 1186, 100,0 }, + {2812,2813, 0, 4, 953, 153,0 }, + {2814,2815, 0, 4, 14786, 126,0 }, + {2816,2817, 0, 4, 14800, 193,0 }, + {2818,2819, 0, 4, 14573, 626,0 }, + {2820,2821, 0, 4, 2200, 73,0 }, + {2822,2823, 0, 4, 373, 86,0 }, + {2824,2825, 0, 4, 12780, 200,0 }, + {2826,2827, 0, 4, 40000, 73,0 }, + {2828,2829, 0, 4, 9193, 146,0 }, + {2830,2831, 0, 4, 2540, 326,0 }, + {2832,2833, 0, 4, 6933, 200,0 }, + {2834,2835, 0, 4, 40000, 413,0 }, + {2836,2837, 0, 4, 4826, 1313,0 }, + {2838,2839, 0, 4, 14740, 340,0 }, + {2840,2841, 0, 4, 1886, 653,0 }, + {2842,2843, 0, 4, 5280, 260,0 }, + {2844,2845, 0, 4, 40000, 240,0 }, + {2846,2847, 0, 4, 40000, 240,0 }, + {2848,2849, 0, 4, 40000, 240,0 }, + {2850,2851, 0, 4, 40000, 406,0 }, + {2852,2853, 0, 4, 40000, 406,0 }, + {2854,2855, 0, 4, 40000, 146,0 }, + {2856,2856, 0, 0, 2400, 1126,0 }, + {2857,2857, 0, 0, 2400, 1126,0 }, + {2858,2859, 0, 4, 4613, 73,0 }, + {2860,2861, 0, 4, 40000, 426,0 }, + {2862,2863, 0, 4, 4580, 100,0 }, + {2864,2865, 0, 4, 40000, 80,0 }, + {2866,2867, 0, 4, 5300, 53,0 }, + {2868,2869, 0, 4, 5313, 113,0 }, + {2870,2871, 0, 4, 7080, 186,0 }, + {2872,2873, 0, 4, 4720, 106,0 }, + {2874,2875, 0, 4, 40000, 73,0 }, + {2876,2877, 0, 4, 1640, 0,0 }, + {2878,2879, 0, 4, 7306, 186,0 }, + {2880,2881, 0, 4, 7373, 1246,0 }, + {2882,2883, 0, 4, 4620, 93,0 }, + {2884,2885, 0, 4, 3460, 926,0 }, + {2886,2887, 0, 4, 40000, 73,0 }, + {2888,2888, 0, 0, 18926, 426,0 }, + {2889,2889, 0, 0, 18520, 73,0 }, + {2890,2890, 0, 0, 18473, 73,0 }, + {2891,2892, 0, 4, 40000, 93,0 }, + {2893,2893, 0, 0, 8006, 133,0 }, + {2894,2894, 0, 0, 18533, 66,0 }, + {2895,2895, 0, 0, 14786, 4966,0 }, + {2896,2897, 0, 4, 40000, 80,0 }, + {2898,2899, 0, 4, 40000, 73,0 }, + {2353,2900, 0, 4, 18520, 86,0 }, + {2901,2901, 0, 0, 40000, 0,0 }, + {2902,2903, 0, 4, 40000, 100,0 }, + {2904,2905, 0, 4, 40000, 93,0 }, + {2906,2907, 0, 4, 40000, 73,0 }, + {2908,2909, 0, 4, 10720, 153,0 }, + {2910,2911, 0, 4, 40000, 73,0 }, + {2912,2912, 0, 0, 40000, 40,0 }, + {2913,2914, 0, 4, 8720, 446,0 }, + {2915,2916, 0, 4, 14706, 653,0 }, + {2917,2918, 0, 4, 9213, 426,0 }, + {2919,2920, 0, 4, 9286, 240,0 }, + {2921,2922, 0, 4, 8706, 413,0 }, + {2923,2924, 0, 4, 2233, 346,0 }, + {2925,2926, 0, 4, 2373, 426,0 }, + {2927,2928, 0, 4, 2353, 233,0 }, + {2929,2929, 0, 0, 40000, 140,0 }, + {2930,2931, 0, 4, 40000, 100,0 }, + {2932,2933, 0, 4, 40000, 73,0 }, + {2934,2935, 0, 4, 40000, 80,0 }, + {2936,2937, 0, 4, 40000, 80,0 }, + {2938,2939, 0, 4, 40000, 246,0 }, + {2940,2940, 0, 0, 553, 446,0 }, + {2941,2941, 0, 0, 40000, 193,0 }, + {2942,2943, 0, 4, 1206, 406,0 }, + {2944,2944, 0, 0, 7026, 1553,0 }, + {2945,2945, 0, 0, 3426, 360,0 }, + {2946,2947, 0, 4, 7313, 646,0 }, + {2948,2948, 0, 0, 40000, 386,0 }, + {2949,2949, 0, 0, 1953, 726,0 }, + {2950,2951, 0, 4, 14606, 106,0 }, + {2952,2953, 0, 4, 40000, 1566,0 }, + {2954,2954, 60, 2, 6, 0,0 }, + {2955,2956, 0, 4, 40000, 240,0 }, + {2957,2958, 0, 4, 40000, 80,0 }, + {2959,2960, 0, 4, 40000, 113,0 }, + {2961,2962, 0, 4, 40000, 240,0 }, + {2963,2963, 0, 0, 8506, 680,0 }, + {2964,2964, 0, 0, 40000, 1593,0 }, + {2436,2436, 49, 0, 1873, 633,0 }, + {2357,2357, 61, 0, 113, 20,0 }, + {2357,2357, 56, 0, 113, 26,0 }, + {2357,2357, 58, 0, 113, 26,0 }, + {2357,2357, 49, 0, 126, 26,0 }, + {2357,2357, 44, 0, 126, 26,0 }, + {2965,2965, 0, 0, 40000, 380,0 }, + {2966,2966, 0, 0, 4440, 66,0 }, + {2967,2967, 0, 0, 8133, 1433,0 }, + {2968,2968, 0, 0, 40000, 126,0 }, + {2969,2969, 0, 0, 40000, 0,0 }, + {2970,2970, 84, 0, 160, 26,0 }, + {2971,2971, 72, 0, 440, 180,0 }, + {2972,2972, 0, 0, 8313, 580,0 }, + {2973,2973, 0, 0, 40000, 160,0 }, + {2974,2974, 0, 0, 40000, 3000,0 }, + {2975,2975, 0, 0, 8300, 493,0 }, + {2976,2976, 0, 0, 973, 673,0 }, + {2977,2977, 0, 0, 40000, 73,0 }, + {2978,2978, 0, 0, 40000, 133,0 }, + {2979,2979, 0, 0, 40000, 140,0 }, + {2980,2980, 0, 0, 40000, 346,0 }, + {2981,2981, 0, 0, 40000, 1006,0 }, + {2982,2982, 0, 0, 40000, 966,0 }, + {2983,2983, 0, 0, 40000, 0,0 }, + {2984,2984, 0, 0, 40000, 0,0 }, + {2985,2985, 0, 0, 40000, 66,0 }, + {2986,2986, 0, 0, 40000, 66,0 }, + {2987,2987, 0, 0, 40000, 46,0 }, + {2988,2988, 0, 0, 40000, 533,0 }, + {2989,2989, 0, 0, 2400, 780,0 }, + {2990,2990, 0, 0, 820, 66,0 }, + {2991,2991, 0, 0, 40000, 240,0 }, + {2992,2992, 0, 0, 40000, 220,0 }, + {2993,2993, 0, 0, 40000, 0,0 }, + {2994,2994, 0, 0, 15100, 73,0 }, + {2995,2995, 0, 0, 40000, 200,0 }, + {2996,2996, 0, 0, 2426, 93,0 }, + {2997,2997, 0, 0, 4640, 1553,0 }, + {2998,2998, 0, 0, 40000, 73,0 }, + {2999,2999, 0, 0, 40000, 73,0 }, + {3000,3000, 0, 0, 1133, 633,0 }, + {3001,3001, 0, 0, 40000, 0,0 }, + {3002,3002, 0, 0, 40000, 1006,0 }, + {3003,3003, 0, 0, 4653, 653,0 }, + {3004,3004, 0, 0, 40000, 1000,0 }, + {3005,3005, 0, 0, 40000, 53,0 }, + {3006,3006, 0, 0, 40000, 60,0 }, + {3007,3007, 0, 0, 40000, 0,0 }, + { 350, 350, 0, 0, 513, 200,0 }, + {3008,3008, 0, 0, 213, 106,0 }, + {3009,3009, 0, 0, 280, 126,0 }, + {3010,3010, 0, 0, 1193, 426,0 }, + {3011,3011, 0, 0, 14653, 4906,0 }, + {3012,3012, 0, 0, 1040, 326,0 }, + {3013,3013, 0, 0, 5740, 2326,0 }, + {3014,3014, 0, 0, 40000, 73,0 }, + {3015,3015, 0, 0, 40000, 240,0 }, + { 350, 350, 36, 0, 380, 153,0 }, + { 369, 369, 37, 0, 213, 66,0 }, + {3008,3008, 38, 0, 213, 106,0 }, + { 369, 369, 24, 0, 193, 13,0 }, + {3008,3008, 32, 0, 206, 106,0 }, + { 369, 369, 48, 0, 186, 20,0 }, + {3009,3009, 42, 0, 220, 106,0 }, + { 369, 369, 50, 0, 186, 73,0 }, + { 369, 369, 52, 0, 186, 73,0 }, + { 369, 369, 54, 0, 186, 33,0 }, + { 369, 369, 55, 0, 186, 33,0 }, + { 369, 369, 57, 0, 180, 33,0 }, + {3010,3010, 51, 0, 966, 353,0 }, + { 144, 144, 61, 0, 213, 126,0 }, + {3016,3016, 0, 0, 8340, 520,0 }, + {3016,3016, 63, 0, 6106, 373,0 }, + {3016,3016, 64, 0, 6073, 380,0 }, + {3017,3017, 40, 0, 206, 100,0 }, + {3017,3017, 70, 0, 160, 93,0 }, + {3018,3018, 0, 0, 40000, 73,0 }, + {3019,3019, 0, 0, 40000, 73,0 }, + {3020,3020, 0, 0, 40000, 73,0 }, + {3021,3021, 0, 0, 40000, 73,0 }, + {3022,3022, 38, 0, 246, 33,0 }, + {2441,2441, 57, 0, 286, 126,0 }, + {3023,3023, 63, 0, 146, 126,0 }, + {3024,3024, 74, 0, 280, 73,0 }, + {3025,3025, 74, 0, 453, 100,0 }, + {3026,3026, 60, 0, 666, 33,0 }, + {1439,1440, 0, 0, 13566, 273,0 }, + {1593,1594, 35, 0, 2200, 673,0 }, + {1564,1565, 35, 0, 740, 280,0 }, + {1443,1444, 0, 0, 11886, 333,0 }, + {1481,1482, 0, 0, 40000, 133,0 }, + { 185, 186, 0, 0, 5980, 1540,0 }, + { 235, 237, 0, 0, 3366, 1093,0 }, + { 239, 240, 0, 0, 40000, 133,0 }, + {1477,1476, 0, 0, 40000, 160,0 }, + { 268, 269, 0, 0, 40000, 80,0 }, + { 176, 177, 0, 0, 40000, 0,0 }, + {1490,1491, 0, 0, 40000, 60,0 }, + { 231, 232, 0, 0, 40000, 146,0 }, + { 233, 234, 0, 0, 40000, 433,0 }, + { 254, 255, 0, 0, 40000, 93,0 }, + { 192, 193, 0, 0, 40000, 66,0 }, + { 252, 253, 0, 0, 40000, 73,0 }, + { 248,3027, 0, 0, 40000, 80,0 }, + { 39,1476, 0, 0, 40000, 160,0 }, + { 241, 242, 0, 0, 40000, 146,0 }, + {1508,1509, 0, 0, 40000, 0,0 }, + { 246, 247, 0, 0, 3966, 800,0 }, + { 181,1451, 0, 0, 2153, 640,0 }, + { 209, 210, 0, 0, 4453, 100,0 }, + { 270, 271, 0, 0, 40000, 80,0 }, + { 115,1533, 0, 0, 4260, 1720,0 }, + {1454,1455, 0, 0, 40000, 0,0 }, + { 107, 319, 0, 0, 1266, 413,0 }, + { 46, 238, 0, 0, 6873, 1246,0 }, + { 216, 217, 0, 0, 4046, 100,0 }, + { 272, 273, 0, 0, 40000, 126,0 }, + {1445,3028, 0, 0, 9966, 386,0 }, + { 172, 173, 0, 0, 7340, 100,0 }, + { 174, 175, 0, 0, 6913, 100,0 }, + {1447,3029, 0, 0, 10306, 80,0 }, + {1452,3030, 0, 0, 9240, 240,0 }, + { 183, 184, 0, 0, 586, 253,0 }, + {1456,1457, 0, 0, 1386, 180,0 }, + {1458,1459, 0, 0, 40000, 60,0 }, + { 190,1460, 0, 0, 40000, 46,0 }, + {1462,1463, 0, 0, 40000, 340,0 }, + {1464,1465, 0, 0, 40000, 360,0 }, + { 195, 196, 0, 0, 40000, 66,0 }, + { 197, 198, 0, 0, 40000, 86,0 }, + { 199, 200, 0, 0, 40000, 60,0 }, + { 201, 202, 0, 0, 3713, 100,0 }, + { 203, 204, 0, 0, 14633, 126,0 }, + { 205, 206, 0, 0, 9440, 153,0 }, + { 214, 215, 0, 0, 17020, 100,0 }, + { 218, 219, 0, 0, 14000, 180,0 }, + { 220, 221, 0, 0, 2846, 100,0 }, + {1472,1473, 0, 0, 8066, 66,0 }, + {1474,1475, 0, 0, 8040, 93,0 }, + { 225, 226, 0, 0, 8066, 106,0 }, + { 229, 230, 0, 0, 40000, 160,0 }, + {1478,1479, 0, 0, 40000, 413,0 }, + { 50,1480, 0, 0, 40000, 393,0 }, + {1485,1486, 0, 0, 40000, 226,0 }, + { 258, 259, 0, 0, 40000, 73,0 }, + { 262, 263, 0, 0, 40000, 160,0 }, + { 264, 265, 0, 0, 40000, 160,0 }, + {1494,1495, 0, 0, 40000, 80,0 }, + {1496,1497, 0, 0, 40000, 73,0 }, + { 274, 275, 0, 0, 40000, 100,0 }, + {1499,1500, 0, 0, 40000, 73,0 }, + { 277, 278, 0, 0, 40000, 173,0 }, + { 279, 280, 0, 0, 40000, 160,0 }, + { 281, 282, 0, 0, 40000, 173,0 }, + {1501,1502, 0, 0, 40000, 146,0 }, + { 284, 285, 0, 0, 40000, 66,0 }, + { 286, 287, 0, 0, 40000, 86,0 }, + {1503,1504, 0, 0, 40000, 86,0 }, + {1505,1506, 0, 0, 40000, 73,0 }, + { 289, 290, 0, 0, 40000, 66,0 }, + { 291, 292, 0, 0, 40000, 160,0 }, + { 293, 294, 0, 0, 40000, 200,0 }, + { 86,1507, 0, 0, 40000, 80,0 }, + { 88, 297, 0, 0, 40000, 1346,0 }, + { 298, 299, 0, 0, 40000, 320,0 }, + { 300, 301, 0, 0, 40000, 1273,0 }, + {1514,1515, 0, 0, 40000, 100,0 }, + {1518,1519, 0, 0, 4733, 0,0 }, + {1520,1521, 0, 0, 40000, 440,0 }, + {1524,1525, 0, 0, 40000, 1180,0 }, + {1526,1527, 0, 0, 40000, 746,0 }, + {1528,1529, 0, 0, 40000, 920,0 }, + { 311, 312, 0, 0, 15306, 213,0 }, + { 313, 314, 0, 0, 7280, 340,0 }, + { 315, 316, 0, 0, 3693, 346,0 }, + { 317, 318, 0, 0, 13720, 4033,0 }, + { 108, 320, 0, 0, 40000, 66,0 }, + { 109, 321, 0, 0, 40000, 180,0 }, + { 322, 323, 0, 0, 40000, 73,0 }, + { 111,1530, 0, 0, 4053, 426,0 }, + { 324, 325, 0, 0, 626, 260,0 }, + { 326, 327, 0, 0, 1166, 400,0 }, + {1531,1532, 0, 0, 186, 340,0 }, + {1534,1535, 0, 0, 3240, 440,0 }, + { 330, 331, 0, 0, 1920, 360,0 }, + {1536,1537, 0, 0, 3020, 0,0 }, + {1538,1539, 0, 0, 1660, 846,0 }, + {1541, 339, 0, 0, 9213, 813,0 }, + {1542,1543, 0, 0, 993, 100,0 }, + {1544,3031, 0, 0, 860, 180,0 }, + {1546,3032, 0, 0, 40000, 80,0 }, + { 338, 339, 0, 0, 40000, 200,0 }, + { 340, 341, 0, 0, 40000, 0,0 }, + {1441,1442, 0, 0, 7393, 186,0 }, + { 207, 208, 0, 0, 14373, 126,0 }, + {1466,1467, 0, 0, 40000, 66,0 }, + {1468,1469, 0, 0, 40000, 46,0 }, + { 179, 180, 0, 0, 4080, 346,0 }, + {1449,1450, 0, 0, 8313, 3373,0 }, + { 35,1470, 0, 0, 40000, 0,0 }, + { 36,1471, 0, 0, 8093, 40,0 }, + { 235, 236, 0, 0, 2393, 333,0 }, + {1483,1484, 0, 0, 40000, 0,0 }, + { 55,1487, 0, 0, 40000, 80,0 }, + {1488,1489, 0, 0, 40000, 80,0 }, + {1492,1493, 0, 0, 40000, 66,0 }, + { 256, 257, 0, 0, 40000, 53,0 }, + { 260, 261, 0, 0, 40000, 86,0 }, + {1512,1513, 0, 0, 40000, 40,0 }, + {1510,1511, 0, 0, 40000, 80,0 }, + {1496,1498, 0, 0, 40000, 73,0 }, + { 295, 296, 0, 0, 40000, 340,0 }, + {1540, 339, 0, 0, 2466, 633,0 }, + { 398, 399, 35, 0, 1860, 226,0 }, + {1516,1517, 0, 0, 40000, 660,0 }, + {1550,3033, 35, 0, 213, 26,0 }, + {1556,1557, 35, 0, 1200, 426,0 }, + {1558,1559, 35, 0, 1173, 406,0 }, + {1570,1571, 35, 0, 1160, 140,0 }, + {1608,1609, 35, 0, 2100, 320,0 }, + {1595,1596, 35, 0, 220, 273,0 }, + { 159,1597, 35, 0, 220, 266,0 }, + {1610,1611, 35, 0, 220, 273,0 }, + { 397,1588, 35, 0, 253, 86,0 }, + {1606,1607, 35, 0, 133, 66,0 }, + { 145,1576, 35, 0, 180, 0,0 }, + {1612,1613, 35, 0, 526, 400,0 }, + {1577,1578, 35, 0, 506, 453,0 }, + {1614,1615, 35, 0, 773, 933,0 }, + { 305, 306, 0, 0, 40000, 1160,0 }, + {1522,1523, 0, 0, 40000, 0,0 }, + {1550,1551, 35, 0, 526, 146,0 }, + { 364, 365, 35, 0, 186, 20,0 }, + { 129,1549, 35, 0, 326, 133,0 }, + { 132,1552, 35, 0, 226, 113,0 }, + {1553,1554, 35, 0, 206, 73,0 }, + { 129,1548, 35, 0, 326, 133,0 }, + { 134,1555, 35, 0, 2580, 893,0 }, + {1560,1561, 35, 0, 600, 366,0 }, + {1562,1563, 35, 0, 40000, 0,0 }, + {1572,1573, 35, 0, 340, 133,0 }, + {1574,1575, 35, 0, 406, 226,0 }, + {1581,1582, 35, 0, 640, 213,0 }, + { 149,1583, 35, 0, 326, 20,0 }, + {1584,1585, 35, 0, 633, 240,0 }, + {1591,1592, 35, 0, 1226, 413,0 }, + {1568,1569, 35, 0, 1313, 460,0 }, + {1579,1580, 35, 0, 326, 0,0 }, + {1586,1587, 35, 0, 606, 220,0 }, + {1589,1590, 35, 0, 120, 86,0 }, + {1600,1601, 35, 0, 1326, 420,0 }, + {1602,1603, 35, 0, 706, 266,0 }, + {1604,1605, 35, 0, 4540, 1326,0 }, + { 391, 392, 35, 0, 360, 153,0 }, + { 391, 393, 35, 0, 453, 153,0 }, + {1598,1599, 35, 0, 1246, 346,0 }, + { 367, 368, 35, 0, 600, 153,0 }, + { 380, 381, 35, 0, 40000, 0,0 }, + { 374, 375, 35, 0, 40000, 0,0 }, + {1566,1567, 35, 0, 40000, 0,0 }, + {2306,2307, 35, 0, 7660, 1560,0 }, + {3034, 339, 35, 0, 5860, 426,0 }, + {2301,2302, 35, 0, 2146, 753,0 }, + {2305,1548, 35, 0, 326, 133,0 }, + {1595,1595, 35, 0, 220, 273,0 }, + {2303,2304, 35, 0, 186, 20,0 }, + {1560,2308, 35, 0, 600, 373,0 }, + {2309,2310, 35, 0, 40000, 0,0 }, + {1568,1568, 35, 0, 1280, 453,0 }, + {2311,2312, 35, 0, 360, 106,0 }, + {2313,2314, 35, 0, 620, 0,0 }, + {2315,2315, 35, 0, 106, 0,0 }, + {2316,2316, 35, 0, 506, 453,0 }, + {1612,1612, 35, 0, 526, 400,0 }, + {2317,2317, 35, 0, 640, 253,0 }, + {2318,2318, 35, 0, 326, 20,0 }, + {2319,2319, 35, 0, 453, 446,0 }, + {2320,2320, 35, 0, 466, 453,0 }, + {2321,2321, 35, 0, 120, 26,0 }, + {2322,2322, 35, 0, 1220, 406,0 }, + {2323,2323, 35, 0, 2360, 786,0 }, + {2324, 392, 35, 0, 353, 146,0 }, + {2324, 393, 35, 0, 453, 146,0 }, + {2325,2326, 35, 0, 533, 140,0 }, + {2327,2328, 35, 0, 1273, 393,0 }, + {2329,2330, 35, 0, 40000, 0,0 }, + {2331,2332, 35, 0, 40000, 0,0 }, + {3035,3036, 35, 0, 4133, 433,0 }, + {3037,3038, 35, 0, 1740, 286,0 }, + {1564,1564, 35, 0, 713, 273,0 }, + {3039,3039, 0, 0, 40000, 0,0 }, + {3040,3040, 0, 0, 6100, 146,0 }, + {3041,3041, 0, 0, 2386, 26,0 }, + {3042,3042, 0, 0, 4320, 80,0 }, + {3043,3043, 0, 0, 3433, 313,0 }, + {3044,3044, 0, 0, 6620, 2446,0 }, + {3045,3045, 0, 0, 3726, 1253,0 }, + {3046,3046, 0, 0, 40000, 133,0 }, + {3047,3047, 0, 0, 4566, 1253,0 }, + {3048,3048, 0, 0, 40000, 813,0 }, + {3049,3049, 0, 0, 18513, 1560,0 }, + {3050,3050, 0, 0, 2186, 426,0 }, + {3051,3051, 0, 0, 1186, 420,0 }, + {3052,3052, 0, 0, 766, 420,0 }, + {3053,3053, 0, 0, 14513, 4713,0 }, + {3054,3054, 0, 0, 15493, 1580,0 }, + {3055,3055, 0, 0, 40000, 66,0 }, + {3056,3056, 0, 0, 40000, 60,0 }, + {3057,3057, 0, 0, 4740, 100,0 }, + {3058,3058, 0, 0, 40000, 66,0 }, + {3059,3059, 0, 0, 40000, 73,0 }, + {3060,3060, 0, 0, 40000, 73,0 }, + {3061,3061, 0, 0, 40000, 0,0 }, + {3062,3062, 0, 0, 8373, 633,0 }, + {3063,3063, 0, 0, 7560, 133,0 }, + {3064,3064, 0, 0, 40000, 0,0 }, + {3065,3065, 0, 0, 40000, 86,0 }, + {3066,3066, 0, 0, 340, 140,0 }, + {3067,3067, 0, 0, 40000, 0,0 }, + {3068,3068, 0, 0, 40000, 166,0 }, + {3069,3069, 0, 0, 4280, 1466,0 }, + {3070,3070, 0, 0, 2193, 73,0 }, + {3071,3071, 0, 0, 4846, 100,0 }, + {3072,3072, 0, 0, 12740, 93,0 }, + {3073,3073, 0, 0, 6953, 200,0 }, + {3074,3074, 0, 0, 13780, 73,0 }, + {3075,3075, 0, 0, 40000, 73,0 }, + {3076,3076, 0, 0, 5860, 600,0 }, + {3077,3077, 0, 0, 2206, 73,0 }, + {3078,3078, 0, 0, 40000, 140,0 }, + {3079,3079, 0, 0, 40000, 53,0 }, + {3080,3080, 0, 0, 40000, 120,0 }, + {3081,3081, 0, 0, 40000, 140,0 }, + {3082,3082, 0, 0, 40000, 126,0 }, + {3083,3083, 0, 0, 360, 140,0 }, + {3084,3084, 0, 0, 8880, 1373,0 }, + {3085,3085, 0, 0, 593, 73,0 }, + {3086,3086, 0, 0, 40000, 193,0 }, + {3087,3087, 0, 0, 40000, 200,0 }, + {3088,3088, 0, 0, 40000, 160,0 }, + {3089,3089, 0, 0, 40000, 200,0 }, + {3090,3090, 0, 0, 40000, 53,0 }, + {3091,3091, 0, 0, 40000, 73,0 }, + {3092,3092, 0, 0, 40000, 73,0 }, + {3093,3093, 0, 0, 760, 213,0 }, + {3094,3094, 0, 0, 40000, 133,0 }, + {3095,3095, 0, 0, 40000, 220,0 }, + {3096,3096, 0, 0, 40000, 100,0 }, + {3097,3097, 0, 0, 40000, 73,0 }, + {3098,3098, 0, 0, 40000, 140,0 }, + {3099,3099, 0, 0, 40000, 140,0 }, + {3100,3100, 0, 0, 40000, 140,0 }, + {3101,3101, 0, 0, 40000, 73,0 }, + {3102,3102, 0, 0, 40000, 73,0 }, + {3103,3103, 0, 0, 40000, 73,0 }, + {3104,3104, 0, 0, 40000, 73,0 }, + {3105,3105, 0, 0, 40000, 66,0 }, + {3106,3106, 0, 0, 40000, 66,0 }, + {3107,3107, 0, 0, 40000, 73,0 }, + {3108,3108, 0, 0, 40000, 73,0 }, + {3109,3109, 0, 0, 40000, 73,0 }, + {3110,3110, 0, 0, 40000, 73,0 }, + {3111,3111, 0, 0, 40000, 86,0 }, + {3112,3112, 0, 0, 5393, 100,0 }, + {3113,3113, 0, 0, 40000, 60,0 }, + {3114,3114, 0, 0, 18500, 73,0 }, + {3115,3115, 0, 0, 40000, 93,0 }, + {3116,3116, 0, 0, 40000, 86,0 }, + {3117,3117, 0, 0, 40000, 173,0 }, + {3118,3118, 0, 0, 40000, 1353,0 }, + {3119,3119, 0, 0, 17506, 73,0 }, + {3120,3120, 0, 0, 40000, 100,0 }, + {3121,3121, 0, 0, 40000, 73,0 }, + {3122,3122, 0, 0, 5620, 193,0 }, + {3123,3123, 0, 0, 3700, 80,0 }, + {3124,3124, 0, 0, 40000, 66,0 }, + {3125,3125, 0, 0, 2740, 80,0 }, + {3126,3126, 0, 0, 8333, 173,0 }, + {3127,3127, 0, 0, 2226, 466,0 }, + {3128,3128, 0, 0, 340, 146,0 }, + {3129,3129, 0, 0, 19980, 6280,0 }, + {3130,3130, 0, 0, 353, 73,0 }, + {3131,3131, 35, 0, 566, 233,0 }, + {3132,3132, 35, 0, 226, 46,0 }, + {3133,3133, 35, 0, 40000, 100,0 }, + {3134,3134, 35, 0, 40000, 100,0 }, + {3135,3135, 35, 0, 360, 146,0 }, + {3061,3061, 35, 0, 40000, 0,0 }, + {3136,3136, 35, 0, 366, 20,0 }, + { 739, 739, 35, 0, 246, 20,0 }, + {3137,3137, 35, 0, 333, 33,0 }, + {3138,3138, 35, 0, 420, 166,0 }, + {3139,3139, 35, 0, 626, 240,0 }, + {3140,3140, 35, 0, 233, 100,0 }, + {3141,3141, 35, 0, 1166, 440,0 }, + {3142,3142, 35, 0, 166, 66,0 }, + {3143,3143, 35, 0, 1166, 440,0 }, + {3144,3144, 35, 0, 813, 100,0 }, + {3145,3145, 35, 0, 1040, 440,0 }, + {3146,3146, 35, 0, 40000, 0,0 }, + {3147,3147, 35, 0, 40000, 0,0 }, + {3148,3148, 35, 0, 180, 40,0 }, + {3149,3149, 35, 0, 40000, 0,0 }, + {3150,3150, 0, 0, 40000, 0,0 }, + {3151,3151, 0, 0, 4900, 240,0 }, + {3152,3152, 0, 0, 3480, 80,0 }, + {3153,3153, 0, 0, 3586, 86,0 }, + {3154,3154, 0, 0, 4626, 633,0 }, + {3155,3155, 0, 0, 4293, 2286,0 }, + {3156,3156, 0, 0, 13653, 0,0 }, + {3157,3157, 0, 0, 1206, 426,0 }, + {3158,3158, 0, 0, 653, 426,0 }, + {3159,3159, 0, 0, 40000, 0,0 }, + {3160,3160, 0, 0, 4633, 633,0 }, + {3161,3161, 0, 0, 40000, 73,0 }, + {3162,3162, 0, 0, 40000, 60,0 }, + {3163,3163, 0, 0, 40000, 146,0 }, + {3164,3164, 0, 0, 40000, 73,0 }, + {3165,3165, 0, 0, 40000, 73,0 }, + {3166,3166, 0, 0, 40000, 0,0 }, + {3167,3167, 0, 0, 40000, 66,0 }, + {3168,3168, 0, 0, 3680, 1180,0 }, + {3169,3169, 0, 0, 2406, 846,0 }, + {3170,3170, 0, 0, 1560, 73,0 }, + {3171,3171, 0, 0, 1946, 226,0 }, + {3172,3172, 0, 0, 4333, 13,0 }, + {3173,3173, 0, 0, 40000, 0,0 }, + {3174,3174, 0, 0, 40000, 0,0 }, + {3175,3175, 0, 0, 40000, 66,0 }, + {3176,3176, 0, 0, 40000, 180,0 }, + {3177,3177, 0, 0, 15380, 80,0 }, + {3178,3178, 0, 0, 18213, 73,0 }, + {3179,3179, 0, 0, 1706, 0,0 }, + {3180,3180, 0, 0, 5733, 1266,0 }, + {3181,3181, 0, 0, 40000, 0,0 }, + {3182,3182, 0, 0, 40000, 366,0 }, + {3183,3183, 0, 0, 40000, 66,0 }, + {3184,3184, 0, 0, 4786, 73,0 }, + {3185,3185, 0, 0, 5660, 720,0 }, + {3186,3186, 0, 0, 1293, 406,0 }, + {3187,3187, 0, 0, 40000, 0,0 }, + {3188,3188, 0, 0, 2686, 233,0 }, + {3189,3189, 0, 0, 40000, 0,0 }, + {3190,3190, 0, 0, 40000, 73,0 }, + {3191,3191, 0, 0, 40000, 0,0 }, + {3192,3192, 0, 0, 40000, 73,0 }, + {3193,3193, 0, 0, 40000, 0,0 }, + {3194,3194, 0, 0, 3920, 73,0 }, + {3195,3195, 0, 0, 40000, 73,0 }, + {3196,3196, 0, 0, 40000, 66,0 }, + {3197,3197, 0, 0, 40000, 80,0 }, + {3198,3198, 0, 0, 40000, 86,0 }, + {3199,3199, 0, 0, 40000, 60,0 }, + {3200,3200, 0, 0, 40000, 0,0 }, + {3201,3201, 0, 0, 40000, 353,0 }, + {3202,3202, 0, 0, 3920, 73,0 }, + {3203,3203, 0, 0, 5833, 813,0 }, + {3204,3204, 0, 0, 40000, 60,0 }, + {3205,3205, 0, 0, 40000, 73,0 }, + {3206,3206, 0, 0, 1400, 406,0 }, + {3207,3207, 0, 0, 40000, 66,0 }, + {3208,3208, 0, 0, 9066, 2220,0 }, + {3209,3209, 0, 0, 1473, 773,0 }, + {3210,3210, 0, 0, 40000, 120,0 }, + {3211,3211, 0, 0, 40000, 306,0 }, + {3212,3212, 0, 0, 9306, 3013,0 }, + {3213,3213, 0, 0, 40000, 60,0 }, + {3214,3214, 0, 0, 40000, 73,0 }, + {3215,3215, 0, 0, 40000, 73,0 }, + {3216,3216, 0, 0, 40000, 453,0 }, + {3217,3217, 0, 0, 40000, 3460,0 }, + {3218,3218, 0, 0, 40000, 453,0 }, + {3219,3219, 0, 0, 40000, 40,0 }, + {3220,3220, 0, 0, 40000, 3926,0 }, + {3221,3221, 0, 0, 40000, 4506,0 }, + {3222,3222, 0, 0, 4646, 646,0 }, + {3223,3223, 0, 0, 773, 100,0 }, + {3224,3224, 0, 0, 40000, 73,0 }, + {3225,3225, 0, 0, 40000, 173,0 }, + {3226,3226, 0, 0, 1606, 653,0 }, + {3227,3227, 0, 0, 2353, 806,0 }, + {3228,3228, 0, 0, 980, 360,0 }, + {3229,3229, 0, 0, 1193, 413,0 }, + { 499, 499, 0, 0, 266, 0,0 }, + {3230,3230, 0, 0, 973, 360,0 }, + {3231,3231, 0, 0, 273, 53,0 }, + {3232,3232, 0, 0, 726, 220,0 }, + {3233,3233, 0, 0, 19933, 6093,0 }, + {3234,3234, 0, 0, 40000, 0,0 }, + { 403, 403, 0, 0, 40000, 73,0 }, + {3235,3235, 0, 0, 4966, 233,0 }, + {3236,3236, 0, 0, 4946, 240,0 }, + {3237,3237, 0, 0, 4946, 233,0 }, + {3238,3238, 0, 0, 4640, 1613,0 }, + {3239,3239, 0, 0, 2360, 806,0 }, + {3240,3240, 0, 0, 4466, 200,0 }, + {3241,3241, 0, 0, 40000, 73,0 }, + {3242,3242, 0, 0, 40000, 73,0 }, + {3243,3243, 0, 0, 40000, 73,0 }, + {3244,3244, 0, 0, 40000, 73,0 }, + {3245,3245, 0, 0, 40000, 240,0 }, + {3246,3246, 0, 0, 40000, 226,0 }, + {3247,3247, 0, 0, 40000, 233,0 }, + {3248,3248, 0, 0, 40000, 240,0 }, + {3249,3249, 0, 0, 4306, 1253,0 }, + {3250,3250, 0, 0, 3873, 1206,0 }, + {3251,3251, 0, 0, 4640, 633,0 }, + {3252,3252, 0, 0, 1233, 80,0 }, + {3253,3253, 0, 0, 1233, 26,0 }, + {3254,3254, 0, 0, 1233, 26,0 }, + {3255,3255, 0, 0, 4573, 1253,0 }, + {3256,3256, 0, 0, 3793, 1240,0 }, + {3257,3257, 0, 0, 40000, 73,0 }, + {3258,3258, 0, 0, 40000, 73,0 }, + {3259,3259, 0, 0, 40000, 140,0 }, + {3260,3260, 0, 0, 40000, 146,0 }, + {3261,3261, 0, 0, 40000, 80,0 }, + {3262,3262, 0, 0, 5953, 200,0 }, + {3263,3263, 0, 0, 5926, 200,0 }, + {3264,3264, 0, 0, 5866, 26,0 }, + {3265,3265, 0, 0, 18573, 6153,0 }, + {3266,3266, 0, 0, 40000, 2093,0 }, + {3267,3267, 0, 0, 40000, 73,0 }, + {3268,3268, 0, 0, 18626, 1553,0 }, + {3269,3269, 0, 0, 40000, 1820,0 }, + {3270,3270, 0, 0, 40000, 500,0 }, + {3271,3271, 0, 0, 18206, 5900,0 }, + {3272,3272, 0, 0, 14200, 93,0 }, + {3273,3273, 0, 0, 40000, 2873,0 }, + {3274,3274, 0, 0, 14960, 4913,0 }, + {3275,3275, 0, 0, 40000, 86,0 }, + {3276,3276, 0, 0, 40000, 826,0 }, + {3277,3277, 0, 0, 40000, 200,0 }, + {3278,3278, 0, 0, 40000, 340,0 }, + {3279,3279, 0, 0, 13220, 2500,0 }, + {3280,3280, 0, 0, 40000, 100,0 }, + {3281,3281, 0, 0, 40000, 1026,0 }, + {3282,3282, 0, 0, 40000, 366,0 }, + {3283,3283, 0, 0, 40000, 386,0 }, + {3284,3284, 0, 0, 40000, 0,0 }, + {3285,3285, 0, 0, 40000, 0,0 }, + {3286,3286, 0, 0, 40000, 140,0 }, + {3287,3287, 0, 0, 40000, 53,0 }, + {3288,3288, 0, 0, 40000, 120,0 }, + {3289,3289, 0, 0, 8866, 1366,0 }, + {3290,3290, 0, 0, 4193, 1400,0 }, + {3291,3291, 0, 0, 8353, 673,0 }, + {3292,3292, 0, 0, 8353, 673,0 }, + {3293,3293, 0, 0, 8400, 593,0 }, + {3294,3294, 0, 0, 8440, 666,0 }, + {3295,3295, 0, 0, 9600, 1580,0 }, + {3296,3296, 0, 0, 40000, 46,0 }, + {3297,3297, 0, 0, 40000, 0,0 }, + {3298,3298, 0, 0, 1653, 93,0 }, + {3299,3299, 0, 0, 2706, 73,0 }, + {3300,3300, 0, 0, 11680, 26,0 }, + {3301,3301, 0, 0, 6500, 340,0 }, + {3302,3302, 0, 0, 40000, 0,0 }, + {3303,3303, 0, 0, 40000, 0,0 }, + {3304,3304, 0, 0, 40000, 73,0 }, + {3305,3305, 0, 0, 40000, 73,0 }, + {3306,3306, 0, 0, 40000, 73,0 }, + {3307,3307, 0, 0, 40000, 73,0 }, + {3308,3308, 0, 0, 40000, 73,0 }, + {3309,3309, 0, 0, 40000, 73,0 }, + {3310,3310, 0, 0, 40000, 73,0 }, + {3311,3311, 0, 0, 40000, 73,0 }, + {3312,3312, 0, 0, 40000, 73,0 }, + {3313,3313, 0, 0, 40000, 133,0 }, + {3314,3314, 0, 0, 40000, 126,0 }, + {3315,3315, 0, 0, 40000, 73,0 }, + {3316,3316, 0, 0, 40000, 73,0 }, + {3317,3317, 0, 0, 40000, 73,0 }, + {3318,3318, 0, 0, 40000, 200,0 }, + {3319,3319, 0, 0, 40000, 133,0 }, + {3320,3320, 0, 0, 40000, 0,0 }, + {3321,3321, 0, 0, 40000, 240,0 }, + {3322,3322, 0, 0, 40000, 220,0 }, + {3323,3323, 0, 0, 40000, 226,0 }, + {3324,3324, 0, 0, 40000, 100,0 }, + {3325,3325, 0, 0, 40000, 140,0 }, + {3326,3326, 0, 0, 40000, 0,0 }, + {3327,3327, 0, 0, 40000, 426,0 }, + {3328,3328, 0, 0, 40000, 426,0 }, + {3329,3329, 0, 0, 3680, 1220,0 }, + {3330,3330, 0, 0, 40000, 533,0 }, + {3331,3331, 0, 0, 40000, 813,0 }, + {3332,3332, 0, 0, 14506, 4706,0 }, + {3333,3333, 0, 0, 766, 420,0 }, + {3334,3334, 0, 0, 40000, 1566,0 }, + {3335,3335, 0, 0, 40000, 120,0 }, + {3336,3336, 0, 0, 40000, 2380,0 }, + {3337,3337, 0, 0, 5666, 300,0 }, + {3338,3338, 0, 0, 40000, 73,0 }, + {3339,3339, 0, 0, 40000, 2513,0 }, + {3340,3340, 0, 0, 1260, 826,0 }, + {3341,3341, 0, 0, 2420, 413,0 }, + {3342,3342, 0, 0, 626, 240,0 }, + {3343,3343, 0, 0, 273, 60,0 }, + {3344,3344, 0, 0, 540, 20,0 }, + {3345,3345, 0, 0, 540, 20,0 }, + {3346,3346, 0, 0, 540, 20,0 }, + {3347,3347, 0, 0, 1153, 760,0 }, + {3348,3348, 0, 0, 40000, 100,0 }, + {3349,3349, 0, 0, 7326, 2380,0 }, + {3350,3350, 0, 0, 40000, 4426,0 }, + {3351,3351, 0, 0, 7413, 2493,0 }, + {3352,3352, 0, 0, 253, 20,0 }, + {3353,3353, 0, 0, 246, 33,0 }, + {3354,3354, 0, 0, 286, 13,0 }, + {3355,3355, 0, 0, 953, 13,0 }, + {3356,3356, 0, 0, 293, 20,0 }, + { 142, 142, 20, 0, 1893, 620,0 }, + {3357,1451, 0, 4, 2373, 780,0 }, + {3358,3359, 0, 4, 9260, 246,0 }, + {3360,1455, 0, 4, 40000, 0,0 }, + {3361,1463, 0, 4, 40000, 266,0 }, + { 225,3362, 0, 4, 7993, 100,0 }, + {3363,1545, 0, 4, 293, 86,0 }, + {3364,1547, 0, 4, 40000, 180,0 }, + {3365,3366, 39, 4, 66, 0,0 }, + {3367, 368, 58, 4, 173, 0,0 }, + {3368,1551, 48, 4, 520, 200,0 }, + {3368,3033, 49, 4, 53, 0,0 }, + {3368,3033, 51, 4, 53, 0,0 }, + {3368,3033, 54, 4, 60, 0,0 }, + {3368,3033, 57, 4, 60, 0,0 }, + {3368,3033, 60, 4, 60, 0,0 }, + {3369,3370, 70, 4, 840, 0,0 }, + {1564,1565, 80, 4, 220, 0,0 }, + {3371,1571, 44, 4, 420, 0,0 }, + {3372,3372, 0, 0, 8366, 666,0 }, + {3373,3373, 0, 0, 8366, 666,0 }, + {3374,3374, 0, 0, 3773, 73,0 }, + {3375,3375, 0, 0, 8366, 666,0 }, + {3376,3376, 0, 0, 4693, 26,0 }, + {3377,3377, 0, 0, 7400, 80,0 }, + {3378,3378, 0, 0, 3586, 80,0 }, + {3379,3379, 0, 0, 8366, 666,0 }, + {3380,3380, 0, 0, 3786, 1240,0 }, + {3381,3381, 0, 0, 9013, 1466,0 }, + {3382,3382, 0, 0, 1200, 73,0 }, + {3383,3383, 0, 0, 8146, 1446,0 }, + {3384,3384, 0, 0, 3660, 1206,0 }, + {3385,3385, 0, 0, 200, 100,0 }, + {3386,3386, 0, 0, 40000, 0,0 }, + {3387,3387, 0, 0, 1213, 426,0 }, + {3388,3388, 0, 0, 40000, 2573,0 }, + {3389,3389, 0, 0, 40000, 3446,0 }, + {3390,3390, 0, 0, 40000, 333,0 }, + {3391,3391, 0, 0, 40000, 73,0 }, + {3392,3392, 0, 0, 40000, 93,0 }, + {3393,3393, 0, 0, 40000, 73,0 }, + {3394,3394, 0, 0, 40000, 73,0 }, + {3395,3395, 0, 0, 40000, 73,0 }, + {3396,3396, 0, 0, 2193, 413,0 }, + {3397,3397, 0, 0, 14606, 2886,0 }, + {3398,3398, 0, 0, 10626, 4520,0 }, + {3399,3399, 0, 0, 2413, 100,0 }, + {3400,3400, 0, 0, 3593, 1140,0 }, + {3401,3401, 0, 0, 40000, 146,0 }, + {3402,3402, 0, 0, 40000, 86,0 }, + {3403,3403, 0, 0, 40000, 86,0 }, + {3404,3404, 0, 0, 9366, 106,0 }, + {3405,3405, 0, 0, 40000, 73,0 }, + {3406,3406, 0, 0, 40000, 0,0 }, + {3407,3407, 0, 0, 40000, 0,0 }, + {3408,3408, 0, 0, 1626, 400,0 }, + {3409,3409, 0, 0, 4473, 2933,0 }, + {3410,3410, 0, 0, 40000, 66,0 }, + {3411,3411, 0, 0, 40000, 0,0 }, + {3412,3412, 0, 0, 40000, 253,0 }, + {3413,3413, 0, 0, 40000, 233,0 }, + {3414,3414, 0, 0, 40000, 346,0 }, + {3415,3415, 0, 0, 1966, 26,0 }, + {3416,3416, 0, 0, 40000, 366,0 }, + {3417,3417, 0, 0, 2266, 386,0 }, + {3418,3418, 0, 0, 40000, 0,0 }, + {3419,3419, 0, 0, 2313, 766,0 }, + {3420,3420, 0, 0, 40000, 340,0 }, + {3421,3421, 0, 0, 40000, 346,0 }, + {3422,3422, 0, 0, 40000, 340,0 }, + {3423,3423, 0, 0, 40000, 353,0 }, + {3424,3424, 0, 0, 40000, 353,0 }, + {3425,3425, 0, 0, 40000, 226,0 }, + {3426,3426, 0, 0, 40000, 73,0 }, + {3427,3427, 0, 0, 940, 253,0 }, + {3428,3428, 0, 0, 40000, 73,0 }, + {3429,3429, 0, 0, 40000, 80,0 }, + {3430,3430, 0, 0, 40000, 240,0 }, + {3431,3431, 0, 0, 40000, 80,0 }, + {3432,3432, 0, 0, 40000, 73,0 }, + {3433,3433, 0, 0, 40000, 73,0 }, + {3434,3434, 0, 0, 40000, 73,0 }, + {3435,3435, 0, 0, 40000, 73,0 }, + {3436,3436, 0, 0, 40000, 73,0 }, + {3437,3437, 0, 0, 40000, 73,0 }, + {3438,3438, 0, 0, 40000, 73,0 }, + {3439,3439, 0, 0, 40000, 73,0 }, + {3440,3440, 0, 0, 40000, 73,0 }, + {3441,3441, 0, 0, 40000, 73,0 }, + {3442,3442, 0, 0, 40000, 66,0 }, + {3443,3443, 0, 0, 40000, 73,0 }, + {3444,3444, 0, 0, 40000, 80,0 }, + {3445,3445, 0, 0, 40000, 66,0 }, + {3446,3446, 0, 0, 40000, 66,0 }, + {3447,3447, 0, 0, 40000, 66,0 }, + {3448,3448, 0, 0, 40000, 66,0 }, + {3449,3449, 0, 0, 40000, 80,0 }, + {3450,3450, 0, 0, 40000, 353,0 }, + {3451,3451, 0, 0, 40000, 0,0 }, + {3452,3452, 0, 0, 18440, 100,0 }, + {3453,3453, 0, 0, 18086, 100,0 }, + {3454,3454, 0, 0, 266, 66,0 }, + {3455,3455, 0, 0, 40000, 80,0 }, + {3456,3456, 0, 0, 40000, 100,0 }, + {3457,3457, 0, 0, 40000, 80,0 }, + {3458,3458, 0, 0, 40000, 120,0 }, + {3459,3459, 0, 0, 40000, 93,0 }, + {3460,3460, 0, 0, 40000, 233,0 }, + {3461,3461, 0, 0, 40000, 0,0 }, + {3462,3462, 0, 0, 40000, 86,0 }, + {3463,3463, 0, 0, 40000, 820,0 }, + {3464,3464, 0, 0, 40000, 4986,0 }, + {3465,3465, 0, 0, 40000, 146,0 }, + {3466,3466, 0, 0, 40000, 100,0 }, + {3467,3467, 0, 0, 40000, 3346,0 }, + {3468,3468, 0, 0, 40000, 660,0 }, + {3469,3469, 0, 0, 40000, 366,0 }, + {3470,3470, 0, 0, 40000, 1480,0 }, + {3471,3471, 0, 0, 40000, 646,0 }, + {3472,3472, 0, 0, 40000, 2673,0 }, + {3473,3473, 0, 0, 40000, 2500,0 }, + {3474,3474, 0, 0, 40000, 2513,0 }, + {3475,3475, 0, 0, 40000, 66,0 }, + {3476,3476, 0, 0, 9600, 1580,0 }, + {3477,3477, 0, 0, 40000, 46,0 }, + {3478,3478, 0, 0, 10673, 100,0 }, + {3479,3479, 0, 0, 2333, 800,0 }, + {3480,3480, 0, 0, 3673, 1200,0 }, + {3481,3481, 0, 0, 40000, 73,0 }, + {3482,3482, 0, 0, 40000, 146,0 }, + {3483,3483, 0, 0, 40000, 73,0 }, + {3484,3484, 0, 0, 2266, 726,0 }, + {3485,3485, 0, 0, 333, 140,0 }, + {3486,3486, 0, 0, 2286, 746,0 }, + {3487,3487, 0, 0, 293, 126,0 }, + {3488,3488, 0, 0, 3700, 1213,0 }, + {3489,3489, 0, 0, 3773, 1186,0 }, + {3490,3490, 0, 0, 3646, 1200,0 }, + {3491,3491, 0, 0, 3020, 73,0 }, + {3492,3492, 0, 0, 786, 273,0 }, + {3493,3493, 0, 0, 40000, 146,0 }, + {3494,3494, 0, 0, 40000, 3093,0 }, + {3495,3495, 0, 0, 273, 60,0 }, + {3496,3496, 0, 0, 40000, 73,0 }, + {3497,3497, 0, 0, 40000, 73,0 }, + {3498,3498, 0, 0, 40000, 3093,0 }, + {3499,3499, 0, 0, 40000, 240,0 }, + {3500,3500, 0, 2, 6, 0,0 }, + { 739, 739, 46, 0, 220, 33,0 }, + {3501,3501, 47, 0, 973, 93,0 }, + {3502,3502, 64, 0, 126, 66,0 }, + {3503,3503, 40, 0, 340, 146,0 }, + {3504,3504, 48, 0, 100, 0,0 }, + {3505,3505, 48, 0, 286, 133,0 }, + {3506,3506, 46, 0, 466, 166,0 }, + {3507,3507,111, 0, 226, 113,0 }, + {3508,3508, 49, 0, 473, 166,0 }, + {3509,3509, 56, 0, 126, 40,0 }, + {3510,3510, 52, 0, 520, 206,0 }, + {3511,3511, 96, 0, 1346, 473,0 }, + {3510,3510, 54, 0, 513, 206,0 }, + {3512,3512, 57, 0, 973, 266,0 }, + {3513,3513, 82, 0, 1580, 553,0 }, + {3510,3510, 60, 0, 506, 200,0 }, + {3514,3514, 60, 0, 1886, 646,0 }, + {3515,3515, 92, 0, 1026, 520,0 }, + {3516,3516, 60, 0, 180, 93,0 }, + {3517,3517, 58, 0, 213, 213,0 }, + {3518,3518, 22, 0, 2300, 766,0 }, + {3519,3519, 60, 0, 1873, 653,0 }, + {3520,3520, 72, 0, 260, 93,0 }, + {3521,3521, 77, 0, 253, 93,0 }, + {3522,3522, 70, 0, 206, 93,0 }, + {3523,3523, 75, 0, 173, 93,0 }, + {3524,3524, 69, 0, 406, 113,0 }, + {3525,3525, 59, 0, 380, 160,0 }, + {3526,3526, 48, 0, 373, 40,0 }, + {3527,3527, 89, 0, 433, 180,0 }, + {3528,3528, 84, 0, 813, 180,0 }, + {3529,3529, 33, 0, 240, 53,0 }, + {3530,3530, 55, 0, 220, 86,0 }, + {3531,3531, 58, 0, 526, 200,0 }, + {3532,3532, 52, 0, 526, 193,0 }, + {3533,3533, 57, 0, 166, 80,0 }, + {3534,3534, 57, 0, 240, 100,0 }, + {3535,3535, 85, 0, 220, 113,0 }, + {3536,3536, 68, 0, 173, 93,0 }, + {3536,3536, 61, 0, 220, 113,0 }, + {3537,3537, 64, 0, 346, 53,0 }, + {3538,3538, 44, 0, 1080, 346,0 }, + {3539,3539,100, 0, 193, 20,0 }, + {3540,3540,100, 0, 793, 26,0 }, + {3541,3541, 0, 0, 14166, 320,0 }, + {3542,3542, 0, 0, 3873, 1613,0 }, + {3543,3543, 0, 0, 3586, 86,0 }, + {3544,3544, 0, 0, 7406, 2486,0 }, + {3545,3545, 0, 0, 4640, 1560,0 }, + {3546,3546, 0, 0, 446, 440,0 }, + {3547,3547, 0, 0, 9253, 3100,0 }, + {3548,3548, 0, 0, 4646, 646,0 }, + {3549,3549, 0, 0, 40000, 66,0 }, + {3550,3550, 0, 0, 40000, 73,0 }, + {3551,3551, 0, 0, 40000, 113,0 }, + {3552,3552, 0, 0, 40000, 73,0 }, + {3553,3553, 0, 0, 40000, 73,0 }, + {3554,3554, 0, 0, 40000, 0,0 }, + {3555,3555, 0, 0, 40000, 60,0 }, + {3556,3556, 0, 0, 3673, 1206,0 }, + {3557,3557, 0, 0, 3706, 1293,0 }, + {3558,3558, 0, 0, 5693, 1126,0 }, + {3559,3559, 0, 0, 2406, 846,0 }, + {3560,3560, 0, 0, 40000, 66,0 }, + {3561,3561, 0, 0, 40000, 73,0 }, + {3562,3562, 0, 0, 4333, 13,0 }, + {3563,3563, 0, 0, 3700, 66,0 }, + {3564,3564, 0, 0, 40000, 0,0 }, + {3565,3565, 0, 0, 3713, 1260,0 }, + {3566,3566, 0, 0, 1140, 126,0 }, + {3567,3567, 0, 0, 40000, 186,0 }, + {3568,3568, 0, 0, 40000, 0,0 }, + {3569,3569, 0, 0, 14400, 6,0 }, + {3570,3570, 0, 0, 14580, 66,0 }, + {3571,3571, 0, 0, 40000, 73,0 }, + {3572,3572, 0, 0, 40000, 353,0 }, + {3573,3573, 0, 0, 40000, 0,0 }, + {3574,3574, 0, 0, 40000, 173,0 }, + {3575,3575, 0, 0, 1833, 600,0 }, + {3576,3576, 0, 0, 40000, 0,0 }, + {3577,3577, 0, 0, 40000, 206,0 }, + {3578,3578, 0, 0, 40000, 46,0 }, + {3579,3579, 0, 0, 40000, 73,0 }, + {3580,3580, 0, 0, 9166, 2900,0 }, + {3581,3581, 0, 0, 5640, 680,0 }, + {3582,3582, 0, 0, 640, 220,0 }, + {3583,3583, 0, 0, 40000, 53,0 }, + {3584,3584, 0, 0, 40000, 26,0 }, + {3585,3585, 0, 0, 40000, 0,0 }, + {3586,3586, 0, 0, 40000, 66,0 }, + {3587,3587, 0, 0, 40000, 60,0 }, + {3588,3588, 0, 0, 40000, 0,0 }, + {3589,3589, 0, 0, 40000, 73,0 }, + {3590,3590, 0, 0, 40000, 0,0 }, + {3591,3591, 0, 0, 40000, 0,0 }, + {3592,3592, 0, 0, 3780, 73,0 }, + {3593,3593, 0, 0, 40000, 0,0 }, + {3594,3594, 0, 0, 3786, 73,0 }, + {3595,3595, 0, 0, 40000, 73,0 }, + {3596,3596, 0, 0, 40000, 66,0 }, + {3597,3597, 0, 0, 40000, 73,0 }, + {3598,3598, 0, 0, 40000, 53,0 }, + {3599,3599, 0, 0, 40000, 426,0 }, + {3600,3600, 0, 0, 40000, 133,0 }, + {3601,3601, 0, 0, 40000, 66,0 }, + {3602,3602, 0, 0, 40000, 433,0 }, + {3603,3603, 0, 0, 393, 126,0 }, + {3604,3604, 0, 0, 40000, 66,0 }, + {3605,3605, 0, 0, 40000, 353,0 }, + {3606,3606, 0, 0, 3813, 73,0 }, + {3607,3607, 0, 0, 5793, 780,0 }, + {3608,3608, 0, 0, 40000, 73,0 }, + {3609,3609, 0, 0, 40000, 86,0 }, + {3610,3610, 0, 0, 820, 206,0 }, + {3611,3611, 0, 0, 40000, 66,0 }, + {3612,3612, 0, 0, 40000, 200,0 }, + {3613,3613, 0, 0, 18186, 720,0 }, + {3614,3614, 0, 0, 40000, 0,0 }, + {3615,3615, 0, 0, 40000, 493,0 }, + {3616,3616, 0, 0, 40000, 306,0 }, + {3617,3617, 0, 0, 2166, 600,0 }, + {3618,3618, 0, 0, 40000, 73,0 }, + {3619,3619, 0, 0, 40000, 3073,0 }, + {3620,3620, 0, 0, 2333, 413,0 }, + {3621,3621, 0, 0, 14880, 73,0 }, + {3622,3622, 0, 0, 40000, 66,0 }, + {3623,3623, 0, 0, 40000, 73,0 }, + {3624,3624, 0, 0, 40000, 1873,0 }, + {3625,3625, 0, 0, 40000, 446,0 }, + {3626,3626, 0, 0, 40000, 3126,0 }, + {3627,3627, 0, 0, 18446, 6140,0 }, + {3628,3628, 0, 0, 1113, 240,0 }, + {3629,3629, 0, 0, 40000, 3600,0 }, + {3630,3630, 0, 0, 40000, 4726,0 }, + {3631,3631, 0, 0, 40000, 0,0 }, + {3632,3632, 0, 0, 2893, 606,0 }, + {3633,3633, 0, 0, 40000, 0,0 }, + {3634,3634, 0, 0, 40000, 0,0 }, + {3635,3635, 0, 0, 40000, 173,0 }, + {3636,3636, 0, 0, 40000, 60,0 }, + {3637,3637, 0, 0, 40000, 0,0 }, + {3638,3638, 0, 0, 986, 326,0 }, + {3639,3639, 0, 0, 1873, 646,0 }, + {3640,3640, 0, 0, 200, 260,0 }, + {3641,3641, 0, 0, 1180, 393,0 }, + {3642,3642, 0, 0, 266, 0,0 }, + {3643,3643, 0, 0, 313, 126,0 }, + {3644,3644, 0, 0, 406, 253,0 }, + {3645,3645, 0, 0, 1013, 813,0 }, + {3646,3646, 0, 0, 273, 53,0 }, + {3647,3647, 0, 0, 720, 213,0 }, + {3648,3648, 0, 0, 386, 120,0 }, + {3649,3649, 0, 0, 40000, 766,0 }, + {3650,3650, 0, 0, 40000, 66,0 }, + {3651,3651, 0, 0, 40000, 73,0 }, + {3652,3652, 0, 0, 1186, 426,0 }, + {3653,3653, 0, 0, 16720, 240,0 }, + {3654,3654, 0, 0, 8026, 246,0 }, + {3655,3655, 0, 0, 18186, 140,0 }, + {3656,3656, 0, 0, 14566, 200,0 }, + {3657,3657, 0, 0, 7973, 20,0 }, + {3658,3658, 0, 0, 4446, 86,0 }, + {3659,3659, 0, 0, 4473, 100,0 }, + {3660,3660, 0, 0, 8646, 153,0 }, + {3661,3661, 0, 0, 3726, 660,0 }, + {3662,3662, 0, 0, 1893, 653,0 }, + {3663,3663, 0, 0, 1933, 760,0 }, + {3664,3664, 0, 0, 9160, 240,0 }, + {3665,3665, 0, 0, 1133, 100,0 }, + {3666,3666, 0, 0, 633, 233,0 }, + {3667,3667, 0, 0, 9153, 3060,0 }, + {3668,3668, 0, 0, 2166, 406,0 }, + {3669,3669, 0, 0, 40000, 66,0 }, + {3670,3670, 0, 0, 40000, 73,0 }, + {3671,3671, 0, 0, 40000, 73,0 }, + {3672,3672, 0, 0, 40000, 73,0 }, + {3673,3673, 0, 0, 40000, 346,0 }, + {3674,3674, 0, 0, 40000, 353,0 }, + {3675,3675, 0, 0, 40000, 200,0 }, + {3676,3676, 0, 0, 40000, 320,0 }, + {3677,3677, 0, 0, 4646, 100,0 }, + {3678,3678, 0, 0, 4426, 133,0 }, + {3679,3679, 0, 0, 4633, 100,0 }, + {3680,3680, 0, 0, 2266, 133,0 }, + {3681,3681, 0, 0, 2346, 53,0 }, + {3682,3682, 0, 0, 40000, 66,0 }, + {3683,3683, 0, 0, 9686, 173,0 }, + {3684,3684, 0, 0, 14300, 66,0 }, + {3685,3685, 0, 0, 40000, 0,0 }, + {3686,3686, 0, 0, 40000, 0,0 }, + {3687,3687, 0, 0, 40000, 0,0 }, + {3688,3688, 0, 0, 8613, 73,0 }, + {3689,3689, 0, 0, 40000, 0,0 }, + {3690,3690, 0, 0, 40000, 0,0 }, + {3691,3691, 0, 0, 40000, 0,0 }, + {3692,3692, 0, 0, 40000, 0,0 }, + {3693,3693, 0, 0, 40000, 393,0 }, + {3694,3694, 0, 0, 40000, 126,0 }, + {3695,3695, 0, 0, 40000, 120,0 }, + {3696,3696, 0, 0, 40000, 0,0 }, + {3697,3697, 0, 0, 40000, 226,0 }, + {3698,3698, 0, 0, 7420, 1186,0 }, + {3699,3699, 0, 0, 3280, 1726,0 }, + {3700,3700, 0, 0, 3680, 1220,0 }, + {3701,3701, 0, 0, 40000, 480,0 }, + {3702,3702, 0, 0, 40000, 306,0 }, + {3703,3703, 0, 0, 40000, 433,0 }, + {3704,3704, 0, 0, 40000, 133,0 }, + {3705,3705, 0, 0, 40000, 0,0 }, + {3706,3706, 0, 0, 40000, 0,0 }, + {3707,3707, 0, 0, 1166, 380,0 }, + {3708,3708, 0, 0, 40000, 140,0 }, + {3709,3709, 0, 0, 40000, 126,0 }, + {3710,3710, 0, 0, 40000, 100,0 }, + {3711,3711, 0, 0, 40000, 66,0 }, + {3712,3712, 0, 0, 40000, 226,0 }, + {3713,3713, 0, 0, 40000, 133,0 }, + {3714,3714, 0, 0, 40000, 73,0 }, + {3715,3715, 0, 0, 40000, 226,0 }, + {3716,3716, 0, 0, 40000, 100,0 }, + {3717,3717, 0, 0, 40000, 80,0 }, + {3718,3718, 0, 0, 40000, 100,0 }, + {3719,3719, 0, 0, 40000, 73,0 }, + {3720,3720, 0, 0, 40000, 73,0 }, + {3721,3721, 0, 0, 40000, 73,0 }, + {3722,3722, 0, 0, 40000, 126,0 }, + {3723,3723, 0, 0, 40000, 80,0 }, + {3724,3724, 0, 0, 40000, 73,0 }, + {3725,3725, 0, 0, 40000, 86,0 }, + {3726,3726, 0, 0, 40000, 100,0 }, + {3727,3727, 0, 0, 40000, 0,0 }, + {3728,3728, 0, 0, 40000, 126,0 }, + {3729,3729, 0, 0, 40000, 133,0 }, + {3730,3730, 0, 0, 40000, 140,0 }, + {3731,3731, 0, 0, 40000, 73,0 }, + {3732,3732, 0, 0, 40000, 60,0 }, + {3733,3733, 0, 0, 40000, 93,0 }, + {3734,3734, 0, 0, 40000, 80,0 }, + {3735,3735, 0, 0, 40000, 66,0 }, + {3736,3736, 0, 0, 40000, 0,0 }, + {3737,3737, 0, 0, 40000, 220,0 }, + {3738,3738, 0, 0, 40000, 80,0 }, + {3739,3739, 0, 0, 40000, 400,0 }, + {3740,3740, 0, 0, 40000, 1373,0 }, + {3741,3741, 0, 0, 40000, 86,0 }, + {3742,3742, 0, 0, 40000, 1313,0 }, + {3743,3743, 0, 0, 40000, 0,0 }, + {3744,3744, 0, 0, 11486, 593,0 }, + {3745,3745, 0, 0, 40000, 1246,0 }, + {3746,3746, 0, 0, 40000, 140,0 }, + {3747,3747, 0, 0, 14386, 2680,0 }, + {3748,3748, 0, 0, 40000, 653,0 }, + {3749,3749, 0, 0, 2286, 713,0 }, + {3750,3750, 0, 0, 40000, 253,0 }, + {3751,3751, 0, 0, 6933, 406,0 }, + {3752,3752, 0, 0, 40000, 1313,0 }, + {3753,3753, 0, 0, 40000, 1440,0 }, + {3754,3754, 0, 0, 40000, 73,0 }, + {3755,3755, 0, 0, 11100, 420,0 }, + {3756,3756, 0, 0, 6493, 320,0 }, + {3757,3757, 0, 0, 3486, 126,0 }, + {3758,3758, 0, 0, 6620, 2133,0 }, + {3759,3759, 0, 0, 1180, 413,0 }, + {3760,3760, 0, 0, 40000, 73,0 }, + {3761,3761, 0, 0, 40000, 73,0 }, + {3762,3762, 0, 0, 40000, 66,0 }, + {3763,3763, 0, 0, 4580, 413,0 }, + {3764,3764, 0, 0, 340, 146,0 }, + {3765,3765, 0, 0, 1166, 400,0 }, + {3766,3766, 0, 0, 1346, 660,0 }, + {3767,3767, 0, 0, 1260, 393,0 }, + {3768,3768, 0, 0, 3646, 1186,0 }, + {3769,3769, 0, 0, 2713, 400,0 }, + {3770,3770, 0, 0, 1780, 73,0 }, + {3771,3771, 0, 0, 800, 213,0 }, + {3772,3772, 0, 0, 660, 173,0 }, + {3773,3773, 0, 0, 12146, 73,0 }, + {3774,3774, 0, 0, 273, 60,0 }, + {3775,3775, 0, 0, 40000, 73,0 }, + {3776,3776, 0, 0, 380, 53,0 }, + {3777,3777, 0, 0, 40000, 200,0 }, + {3778,3778, 0, 0, 586, 20,0 }, + {3779,3779, 0, 2, 6, 0,0 }, + { 738, 738, 44, 0, 840, 340,0 }, + {3780,3780, 36, 0, 7366, 140,0 }, + {3781,3781, 32, 0, 100, 0,0 }, + {2030,2030, 60, 0, 293, 126,0 }, + {3782,3782, 24, 0, 100, 0,0 }, + {3783,3783, 60, 0, 126, 73,0 }, + {3784,3784, 44, 0, 393, 93,0 }, + { 132, 132, 44, 0, 173, 100,0 }, + {3785,3785, 47, 0, 393, 93,0 }, + { 152, 152, 44, 0, 213, 86,0 }, + {3784,3784, 50, 0, 393, 93,0 }, + { 139, 139, 44, 0, 293, 100,0 }, + {3784,3784, 54, 0, 393, 93,0 }, + {3784,3784, 57, 0, 393, 93,0 }, + {3786,3786, 60, 0, 1900, 666,0 }, + {3784,3784, 60, 0, 393, 93,0 }, + {3787,3787, 60, 0, 1900, 666,0 }, + {3788,3788, 60, 0, 1886, 666,0 }, + {3789,3789, 60, 0, 1866, 653,0 }, + {3790,3790, 60, 0, 1873, 633,0 }, + {3791,3791, 44, 0, 946, 333,0 }, + {2037,2037, 44, 0, 213, 126,0 }, + { 144, 144, 44, 0, 213, 126,0 }, + {2038,2038, 44, 0, 106, 0,0 }, + {3792,3792, 44, 0, 380, 360,0 }, + {3793,3793, 44, 0, 520, 206,0 }, + {3794,3794, 45, 0, 273, 100,0 }, + {3795,3795, 33, 0, 326, 106,0 }, + {3796,3796, 56, 0, 506, 200,0 }, + {3796,3796, 51, 0, 506, 200,0 }, + {3797,3797, 44, 0, 126, 66,0 }, + {3798,3798, 44, 0, 553, 186,0 }, + {3534,3534, 56, 0, 240, 100,0 }, + { 158, 158, 68, 0, 126, 140,0 }, + {3799,3799, 51, 0, 513, 206,0 }, + {3800,3800, 46, 0, 506, 200,0 }, + {3801,3801, 44, 0, 513, 206,0 }, + {3802,3802, 44, 0, 3720, 1260,0 }, + { 152, 152, 45, 0, 220, 80,0 }, + {3803,3803, 0, 0, 40000, 86,0 }, + {3804,3804, 0, 0, 40000, 226,0 }, + {3805,3805, 0, 0, 40000, 73,0 }, + {3806,3806, 0, 0, 40000, 73,0 }, + {3807,3807, 0, 0, 40000, 93,0 }, + {3808,3808, 0, 0, 4653, 660,0 }, + {3809,3809, 0, 0, 6686, 2246,0 }, + {3810,3810, 0, 0, 1180, 413,0 }, + {3811,3811, 0, 0, 966, 293,0 }, + {3812,3812, 0, 0, 1780, 66,0 }, + {3780,3780, 45, 0, 5900, 113,0 }, + {3061,3061, 45, 0, 40000, 0,0 }, + {3813,3813, 60, 0, 126, 226,0 }, + {3781,3781, 60, 0, 93, 0,0 }, + {3814,3814, 44, 0, 393, 86,0 }, + {3815,3815, 57, 0, 166, 80,0 }, + {3816,3816, 56, 0, 240, 100,0 }, + {3817,3817, 60, 0, 113, 0,0 }, + {3818,3818, 60, 0, 113, 0,0 }, + {3517,3517, 45, 0, 213, 213,0 }, + {3819,3819, 0, 0, 4033, 100,0 }, + {3820,3820, 0, 0, 5200, 873,0 }, + {3821,3821, 0, 0, 40000, 0,0 }, + {3822,3822, 0, 0, 10493, 160,0 }, + {3823,3823, 0, 0, 40000, 740,0 }, + {3824,3825, 0, 1, 40000, 366,0.078125 }, + {3826,3826, 0, 0, 40000, 5100,0 }, + {3827,3827, 0, 0, 40000, 766,0 }, + {3828,1172, 0, 1, 40000, 780,0.15625 }, + {3829,3829, 0, 0, 40000, 60,0 }, + {3830,3830, 0, 0, 566, 133,0 }, + {3831,3831, 32, 0, 146, 0,0 }, + {3832,3832, 36, 0, 273, 0,0 }, + {3833,3833, 88, 0, 340, 120,0 }, + {3834,3834, 0, 0, 9006, 240,0 }, + {3835,3835, 0, 0, 9206, 246,0 }, + {3836,3836, 0, 0, 9246, 386,0 }, + {3837,3837, 0, 0, 9440, 220,0 }, + {3838,3838, 0, 0, 8900, 133,0 }, + {3839,3839, 0, 0, 9400, 253,0 }, + {3840,3840, 0, 0, 4613, 420,0 }, + {3841,3841, 0, 0, 9233, 426,0 }, + {3842,3842, 0, 0, 40000, 526,0 }, + {3843,3843, 0, 0, 40000, 640,0 }, + {3844,3844, 0, 0, 40000, 666,0 }, + {3845,3845, 0, 0, 40000, 1053,0 }, + {3846,3846, 0, 0, 40000, 173,0 }, + {3847,3847, 0, 0, 40000, 246,0 }, + {3848,3848, 0, 0, 40000, 226,0 }, + {3849,3849, 0, 0, 4073, 233,0 }, + {3850,3850, 0, 0, 14286, 326,0 }, + {3851,3851, 0, 0, 9233, 146,0 }, + {3852,3852, 0, 0, 4480, 133,0 }, + {3853,3853, 0, 0, 40000, 53,0 }, + {3854,3854, 0, 0, 40000, 126,0 }, + {3855,3855, 0, 0, 40000, 126,0 }, + {3856,3856, 0, 0, 18226, 146,0 }, + {3857,3857, 0, 0, 40000, 326,0 }, + {3858,3858, 0, 0, 40000, 0,0 }, + {3859,3859, 0, 0, 40000, 300,0 }, + {3860,3860, 0, 0, 40000, 0,0 }, + {3861,3861, 0, 0, 40000, 0,0 }, + {3862,3862, 0, 0, 40000, 0,0 }, + {3863,3863, 0, 0, 40000, 140,0 }, + {3864,3864, 0, 0, 40000, 153,0 }, + {3865,3865, 0, 0, 40000, 233,0 }, + {3866,3866, 0, 0, 40000, 186,0 }, + {3867,3867, 0, 0, 40000, 413,0 }, + {3868,3868, 0, 0, 40000, 373,0 }, + {3869,3869, 0, 0, 1246, 440,0 }, + {3870,3870, 0, 0, 4620, 1513,0 }, + {3871,3871, 0, 0, 40000, 433,0 }, + {3872,3872, 0, 0, 40000, 453,0 }, + {3873,3873, 0, 0, 40000, 1440,0 }, + {3874,3874, 0, 0, 40000, 480,0 }, + {3875,3875, 0, 0, 40000, 1360,0 }, + {3876,3876, 0, 0, 40000, 0,0 }, + {3877,3877, 0, 0, 40000, 353,0 }, + {3878,3878, 0, 0, 40000, 86,0 }, + {3879,3879, 0, 0, 40000, 126,0 }, + {3880,3880, 0, 0, 40000, 73,0 }, + {3881,3881, 0, 0, 40000, 80,0 }, + {3882,3882, 0, 0, 40000, 246,0 }, + {3883,3883, 0, 0, 40000, 93,0 }, + {3884,3884, 0, 0, 40000, 120,0 }, + {3885,3885, 0, 0, 40000, 180,0 }, + {3886,3886, 0, 0, 40000, 133,0 }, + {3887,3887, 0, 0, 40000, 133,0 }, + {3888,3888, 0, 0, 40000, 153,0 }, + {3889,3889, 0, 0, 40000, 93,0 }, + {3890,3890, 0, 0, 40000, 140,0 }, + {3891,3891, 0, 0, 40000, 100,0 }, + {3892,3892, 0, 0, 40000, 146,0 }, + {3893,3893, 0, 0, 40000, 126,0 }, + {3894,3894, 0, 0, 40000, 160,0 }, + {3895,3895, 0, 0, 40000, 226,0 }, + {3896,3896, 0, 0, 40000, 140,0 }, + {3897,3897, 0, 0, 40000, 200,0 }, + {3898,3898, 0, 0, 40000, 66,0 }, + {3899,3899, 0, 0, 40000, 446,0 }, + {3900,3900, 0, 0, 40000, 140,0 }, + {3901,3901, 0, 0, 40000, 400,0 }, + {3902,3902, 0, 0, 40000, 373,0 }, + {3903,3903, 0, 0, 40000, 1306,0 }, + {3904,3904, 0, 0, 40000, 186,0 }, + {3905,3905, 0, 0, 40000, 640,0 }, + {3906,3906, 0, 0, 40000, 346,0 }, + {3907,3907, 0, 0, 40000, 140,0 }, + {3908,3908, 0, 0, 40000, 253,0 }, + {3909,3909, 0, 0, 8980, 746,0 }, + {3910,3910, 0, 0, 40000, 1266,0 }, + {3911,3911, 0, 0, 40000, 1306,0 }, + {3912,3912, 0, 0, 7226, 593,0 }, + {3913,3913, 0, 0, 40000, 140,0 }, + {3914,3914, 0, 0, 40000, 220,0 }, + {3915,3915, 0, 0, 40000, 146,0 }, + {3916,3916, 0, 0, 4606, 1506,0 }, + {3917,3917, 0, 0, 40000, 80,0 }, + {3918,3918, 0, 0, 40000, 0,0 }, + {3919,3919, 0, 0, 40000, 0,0 }, + {3920,3920, 0, 0, 613, 226,0 }, + {3921,3921, 0, 0, 9073, 2946,0 }, + {3922,3922, 0, 0, 40000, 73,0 }, + {3923,3923, 0, 0, 3726, 200,0 }, + {3924,3924, 0, 0, 3680, 373,0 }, + {3925,3925, 0, 0, 7113, 186,0 }, + {3926,3926, 0, 0, 2406, 106,0 }, + {3927,3927, 0, 0, 40000, 0,0 }, + {3928,3928, 0, 0, 40000, 253,0 }, + {3929,3929, 0, 0, 40000, 0,0 }, + {3930,3930, 0, 0, 40000, 80,0 }, + {3931,3931, 0, 0, 40000, 86,0 }, + {3932,3932, 0, 0, 18186, 740,0 }, + {3933,3933, 0, 0, 18426, 813,0 }, + { 523, 523, 0, 0, 200, 260,0 }, + {3934,3934, 0, 0, 340, 146,0 }, + {3935,3935, 0, 0, 366, 260,0 }, + {3936,3936, 48, 0, 126, 0,0 }, + {3937,3937, 27, 0, 200, 106,0 }, + {3938,3938, 40, 0, 1073, 800,0 }, + {3939,3939, 48, 0, 100, 0,0 }, + {3938,3938, 45, 0, 933, 666,0 }, + {3940,3940, 48, 0, 140, 333,0 }, + {3938,3938, 47, 0, 933, 666,0 }, + {3941,3941, 48, 0, 1840, 0,0 }, + {3938,3938, 49, 0, 953, 686,0 }, + {3938,3938, 53, 0, 906, 686,0 }, + {3938,3938, 56, 0, 913, 693,0 }, + { 129, 129, 52, 0, 293, 126,0 }, + { 130, 130, 48, 0, 173, 93,0 }, + { 129, 129, 58, 0, 286, 126,0 }, + { 132, 132, 47, 0, 173, 100,0 }, + { 492, 492, 43, 0, 820, 306,0 }, + { 132, 132, 49, 0, 173, 93,0 }, + { 132, 132, 51, 0, 173, 93,0 }, + { 132, 132, 54, 0, 173, 93,0 }, + { 132, 132, 57, 0, 146, 86,0 }, + { 492, 492, 72, 0, 706, 300,0 }, + { 137, 137, 76, 0, 1900, 666,0 }, + { 138, 138, 84, 0, 740, 300,0 }, + { 139, 139, 36, 0, 353, 146,0 }, + { 140, 140, 76, 0, 1886, 680,0 }, + { 141, 141, 84, 0, 220, 113,0 }, + { 135, 135, 83, 0, 1353, 480,0 }, + { 142, 142, 84, 0, 386, 160,0 }, + {3942,3942, 24, 0, 2313, 780,0 }, + { 137, 137, 77, 0, 1893, 660,0 }, + { 144, 144, 60, 0, 213, 0,0 }, + { 145, 145, 65, 0, 180, 146,0 }, + { 146, 146, 59, 0, 173, 93,0 }, + { 147, 147, 51, 0, 266, 213,0 }, + { 148, 148, 45, 0, 513, 200,0 }, + { 149, 149, 71, 0, 246, 26,0 }, + { 150, 150, 60, 0, 500, 193,0 }, + { 151, 151, 58, 0, 513, 200,0 }, + { 152, 152, 53, 0, 220, 86,0 }, + { 153, 153, 64, 0, 113, 40,0 }, + { 154, 154, 71, 0, 840, 300,0 }, + { 156, 156, 61, 0, 166, 80,0 }, + { 158, 158, 48, 0, 173, 213,0 }, + { 159, 159, 69, 0, 126, 140,0 }, + { 160, 160, 68, 0, 126, 140,0 }, + { 161, 161, 63, 0, 326, 113,0 }, + { 162, 162, 74, 0, 860, 286,0 }, + { 163, 163, 60, 0, 386, 160,0 }, + { 164, 164, 80, 0, 1106, 273,0 }, + { 165, 165, 64, 0, 126, 66,0 }, + { 166, 166, 69, 0, 386, 80,0 }, + { 167, 167, 73, 0, 546, 306,0 }, + { 168, 168, 75, 0, 126, 140,0 }, + { 169, 169, 68, 0, 340, 320,0 }, + { 131, 131, 48, 0, 520, 200,0 }, + {3061,3061, 53, 0, 40000, 0,0 }, + {3943,3944, 0, 4, 2153, 0,0 }, + {3945,3946, 0, 4, 9060, 393,0 }, + { 174,3947, 0, 4, 6953, 0,0 }, + {3948,3949, 0, 4, 9386, 140,0 }, + { 9,3950, 0, 4, 1626, 426,0 }, + {3951,3952, 0, 4, 18466, 240,0 }, + {3953,3954, 0, 4, 4666, 1486,0 }, + { 15,3955, 0, 4, 5700, 1986,0 }, + {3956,3957, 0, 4, 40000, 100,0 }, + {3958,3959, 0, 4, 40000, 73,0 }, + {3960,3961, 0, 4, 40000, 73,0 }, + {3962,3963, 0, 4, 40000, 73,0 }, + {3964,3965, 0, 4, 18280, 153,0 }, + {3966,3965, 0, 4, 18686, 160,0 }, + { 31,3967, 0, 4, 40000, 0,0 }, + {3968,3969, 0, 4, 17966, 100,0 }, + {3970,3971, 0, 4, 40000, 66,0 }, + {3972,3971, 0, 4, 40000, 66,0 }, + {3973,3974, 0, 4, 40000, 46,0 }, + {3975,3976, 0, 4, 18693, 106,0 }, + {3977,3976, 0, 4, 18593, 106,0 }, + {3978,3979, 0, 4, 9366, 106,0 }, + {3980,3981, 0, 4, 9120, 226,0 }, + {3982,3983, 0, 4, 40000, 140,0 }, + {3984,3985, 0, 4, 40000, 800,0 }, + { 54,3986, 0, 4, 2520, 706,0 }, + {3987,3988, 0, 4, 40000, 86,0 }, + {3989,3990, 0, 4, 40000, 233,0 }, + {3991, 253, 0, 4, 40000, 66,0 }, + {3992,3992, 0, 0, 40000, 0,0 }, + {3993,3993, 0, 0, 40000, 120,0 }, + {3994,3995, 0, 4, 40000, 80,0 }, + {3996,3997, 0, 4, 40000, 73,0 }, + {3998,3999, 0, 4, 40000, 86,0 }, + {1503,4000, 0, 4, 40000, 93,0 }, + { 88,4001, 0, 4, 40000, 1220,0 }, + {3743,4002, 0, 4, 7706, 1260,0 }, + { 92,4003, 0, 4, 40000, 186,0 }, + { 93,4004, 0, 4, 40000, 813,0 }, + { 94,4005, 0, 4, 7720, 1260,0 }, + { 96,4006, 0, 4, 40000, 2460,0 }, + { 103,4007, 0, 4, 3720, 1240,0 }, + { 104,4008, 0, 4, 3020, 1226,0 }, + { 105,4009, 0, 4, 6086, 2453,0 }, + { 107,4010, 0, 4, 2100, 760,0 }, + { 108,4011, 0, 4, 40000, 73,0 }, + { 110,4012, 0, 4, 40000, 100,0 }, + { 111,4013, 0, 4, 2366, 820,0 }, + {4014,4015, 0, 4, 1026, 326,0 }, + { 115,4016, 0, 4, 1853, 0,0 }, + { 118,4017, 0, 4, 1553, 53,0 }, + { 119,4018, 0, 4, 593, 0,0 }, + { 120,4019, 0, 4, 2293, 1173,0 }, + { 121,4020, 0, 4, 10673, 3000,0 }, + { 123,4021, 0, 4, 7413, 2486,0 }, + { 124,4022, 0, 4, 40000, 1126,0 }, + { 125,4023, 0, 4, 40000, 1546,0 }, + {4024,4024, 35, 0, 706, 266,0 }, + {4025,4026, 38, 1, 273, 106,0 }, + {4027,4028, 38, 1, 366, 133,0 }, + {4029,4030, 48, 1, 280, 133,-1.90625 }, + {4031,4031, 51, 0, 113, 80,0 }, + {4032,4033, 48, 1, 953, 346,-1.90625 }, + {4034,4034, 61, 1, 3200, 540,0.09375 }, + {3369,1557, 70, 4, 833, 0,0 }, + {4035,4036, 79, 1, 1306, 513,0.078125 }, + {4037,4037, 62, 0, 5200, 466,0 }, + {4038,4039, 67, 1, 2153, 1080,0.078125 }, + {4040,4040, 62, 1, 3226, 573,0.09375 }, + {4041,4042, 54, 1, 286, 133,0 }, + {4041,4043, 48, 1, 286, 126,0 }, + { 389, 389, 42, 0, 266, 73,0 }, + {4044,4045, 48, 1, 280, 126,0 }, + {4046,4047, 48, 1, 380, 60,0 }, + {4048,4048, 16, 0, 180, 20,0 }, + {4049,4049, 16, 0, 740, 20,0 }, + {4050,4051, 64, 4, 1366, 0,0 }, + { 844, 844,244, 2, 6, 0,0 }, + { 855, 855,244, 2, 6, 0,0 }, + { 880, 880,232, 0, 253, 80,0 }, + { 882, 882,220, 0, 40000, 266,0 }, + { 887, 887, 35, 0, 133, 46,0 }, + { 884, 884, 35, 0, 233, 80,0 }, + { 885, 885, 35, 0, 226, 86,0 }, + { 886, 886, 35, 0, 113, 0,0 }, + { 361, 361, 35, 0, 286, 73,0 }, + { 767, 767, 35, 0, 3020, 786,0 }, + { 888, 888, 35, 0, 246, 53,0 }, + {2141,2141, 35, 0, 186, 73,0 }, + { 891, 891, 35, 0, 713, 266,0 }, + {2142,2142, 35, 0, 200, 100,0 }, + {2143,2143, 35, 0, 220, 80,0 }, + {2144,2144, 35, 0, 2393, 100,0 }, + {2145,2145, 35, 0, 1980, 813,0 }, + { 376, 376, 35, 0, 1880, 840,0 }, + { 895, 895, 35, 0, 366, 140,0 }, + {2146,2146, 35, 0, 346, 106,0 }, + { 382, 382, 35, 0, 1073, 113,0 }, + {2147,2147, 35, 0, 106, 80,0 }, + { 898, 898, 35, 0, 206, 153,0 }, + { 899, 899, 35, 0, 633, 240,0 }, + { 900, 900, 35, 0, 620, 240,0 }, + { 871, 871, 35, 0, 380, 73,0 }, + { 388, 388, 35, 0, 286, 80,0 }, + { 901, 901, 35, 0, 260, 26,0 }, + { 902, 902, 35, 0, 1093, 73,0 }, + { 903, 903, 35, 0, 126, 73,0 }, + {3500,3500, 35, 2, 6, 0,0 }, + {4052,4052, 0, 0, 14166, 320,0 }, + {4053,4053, 0, 0, 7413, 653,0 }, + {4054,4054, 0, 0, 40000, 146,0 }, + {4055,4055, 0, 0, 40000, 113,0 }, + {4056,4056, 0, 0, 16773, 193,0 }, + {4057,4057, 0, 0, 40000, 73,0 }, + {4058,4058, 0, 0, 40000, 0,0 }, + {4059,4059, 0, 0, 966, 373,0 }, + {4060,4060, 0, 0, 40000, 80,0 }, + {4061,4061, 0, 0, 40000, 80,0 }, + {4062,4062, 0, 0, 18473, 93,0 }, + {4063,4063, 0, 0, 40000, 60,0 }, + {4064,4064, 0, 0, 40000, 73,0 }, + {4065,4065, 0, 0, 40000, 0,0 }, + {4066,4066, 0, 0, 40000, 213,0 }, + {4067,4067, 0, 0, 40000, 66,0 }, + {4068,4068, 0, 0, 1413, 1026,0 }, + {4069,4069, 0, 0, 506, 200,0 }, + {4070,4070, 0, 0, 3793, 1106,0 }, + {4071,4071, 0, 0, 40000, 220,0 }, + {4072,4072, 0, 0, 40000, 46,0 }, + {4073,4073, 0, 0, 40000, 0,0 }, + {4074,4074, 0, 0, 40000, 60,0 }, + {4075,4075, 0, 0, 40000, 0,0 }, + {4076,4076, 0, 0, 40000, 33,0 }, + {4077,4077, 0, 0, 40000, 0,0 }, + {4078,4078, 0, 0, 40000, 146,0 }, + {4079,4079, 0, 0, 40000, 66,0 }, + {4080,4080, 0, 0, 40000, 353,0 }, + {4081,4081, 0, 0, 40000, 66,0 }, + {4082,4082, 0, 0, 40000, 53,0 }, + {4083,4083, 0, 0, 40000, 73,0 }, + {4084,4084, 0, 0, 40000, 66,0 }, + {4085,4085, 0, 0, 40000, 926,0 }, + {4086,4086, 0, 0, 2833, 200,0 }, + { 127, 127, 36, 0, 386, 166,0 }, + {4087,4087, 36, 0, 100, 0,0 }, + {2030,2030, 36, 0, 346, 140,0 }, + {3782,3782, 48, 0, 93, 0,0 }, + {3783,3783, 36, 0, 146, 0,0 }, + {4088,4088, 48, 0, 1886, 653,0 }, + { 132, 132, 69, 0, 126, 66,0 }, + {4088,4088, 52, 0, 1853, 626,0 }, + { 152, 152, 48, 0, 220, 86,0 }, + {4088,4088, 55, 0, 1886, 640,0 }, + { 139, 139, 57, 0, 293, 133,0 }, + {4088,4088, 58, 0, 1860, 633,0 }, + {4088,4088, 60, 0, 1886, 633,0 }, + {4089,4089, 62, 0, 2660, 900,0 }, + {4088,4088, 63, 0, 1880, 646,0 }, + { 134, 134, 70, 0, 966, 360,0 }, + {4090,4090, 70, 0, 973, 346,0 }, + {4091,4091, 53, 0, 1866, 640,0 }, + {3516,3516, 48, 0, 180, 93,0 }, + {4092,4092, 84, 0, 1360, 473,0 }, + {4093,4093, 43, 0, 513, 206,0 }, + {4094,4094, 56, 0, 1353, 480,0 }, + {3791,3791, 24, 0, 1866, 613,0 }, + { 134, 134, 65, 0, 1346, 486,0 }, + { 146, 146, 48, 0, 173, 93,0 }, + { 146, 146, 54, 0, 173, 93,0 }, + {4095,4095, 42, 0, 246, 140,0 }, + {4095,4095, 39, 0, 240, 133,0 }, + {3816,3816, 52, 0, 306, 113,0 }, + {4096,4096, 52, 0, 413, 86,0 }, + { 158, 158, 60, 0, 146, 166,0 }, + { 158, 158, 66, 0, 146, 166,0 }, + { 158, 158, 59, 0, 146, 166,0 }, + {3538,3538, 91, 0, 773, 233,0 }, + {3547,3547,109, 0, 5300, 1786,0 }, + {4097,4097, 79, 0, 560, 313,0 }, + {4098,4098, 0, 0, 10646, 73,0 }, + {4099,4100, 0, 1, 14166, 586,0.03125 }, + {4101,4102, 0, 1, 15553, 546,0.03125 }, + {4103,4104, 0, 1, 11746, 320,0.046875 }, + {4105,4106, 0, 1, 14706, 646,0.15625 }, + {4107,4108, 0, 1, 7320, 100,0.046875 }, + {4109,4110, 0, 1, 40000, 0,0.0625 }, + {4111,4112, 0, 1, 13660, 260,0 }, + {4113,4114, 0, 1, 15026, 133,0 }, + {4115,4116, 0, 1, 40000, 0,2.5e-05 }, + {4117,4118, 0, 1, 4980, 3400,0 }, + {4119,4120, 0, 1, 7840, 2660,0.046875 }, + {4121,4122, 0, 1, 8326, 180,0 }, + {4123,4124, 0, 1, 1093, 140,0 }, + {4125,4126, 0, 1, 2280, 400,0 }, + {4127,4128, 0, 1, 4553, 1486,0.03125 }, + {4129,4129, 0, 1, 40000, 0,0.03125 }, + {4130,4131, 0, 1, 40000, 60,0.15625 }, + {4132,4133, 0, 1, 40000, 93,0.078125 }, + {4134,4135, 0, 1, 40000, 86,0.15625 }, + {4136,4137, 0, 1, 40000, 520,0.03125 }, + {4138,4139, 0, 1, 40000, 140,0.0625 }, + {4140,4141, 0, 1, 40000, 133,0.140625 }, + {4142,4143, 0, 1, 40000, 73,0 }, + {4144,4145, 0, 1, 40000, 346,0.109375 }, + {4146,4147, 0, 1, 3693, 86,0 }, + {4148,4149, 0, 1, 6586, 460,2.5e-05 }, + {4150,4151, 0, 1, 4320, 93,0 }, + {4152,4153, 0, 1, 7346, 126,0.046875 }, + {4154,4155, 0, 1, 3633, 260,0 }, + {4156,4157, 0, 1, 40000, 126,-1.95312 }, + {4158,4159, 0, 1, 40000, 126,-1.9375 }, + {4160,4161, 0, 1, 40000, 46,0.234375 }, + {4162,4163, 0, 1, 40000, 0,0.03125 }, + {4164,4165, 0, 1, 10320, 86,0 }, + {4166,4167, 0, 1, 12933, 133,0 }, + {4168,4169, 0, 1, 11820, 240,0.046875 }, + {4170,4171, 0, 1, 3966, 166,0 }, + {4172,4173, 0, 1, 40000, 0,0 }, + {4174,4174, 0, 0, 2666, 160,0 }, + {4175,4176, 0, 1, 15046, 93,0.078125 }, + {4177,4178, 0, 1, 40000, 100,0 }, + {4179,4179, 0, 0, 40000, 260,0 }, + {4180,4181, 0, 1, 40000, 126,2.5e-05 }, + {4182,4182, 0, 0, 40000, 233,0 }, + {4183,4184, 0, 1, 40000, 440,0.078125 }, + {4185,4186, 0, 1, 2160, 606,0.109375 }, + {4187,4188, 0, 1, 14753, 2400,0.03125 }, + {4189,4190, 0, 1, 7680, 646,0.03125 }, + {4191,4192, 0, 1, 40000, 446,0.0625 }, + {4193,4194, 0, 1, 40000, 866,-0.0625 }, + {4195,4195, 0, 1, 40000, 1220,0.078125 }, + {4196,4196, 0, 1, 40000, 1960,0.0625 }, + {4197,4198, 0, 1, 40000, 433,0.125 }, + {4199,4200, 0, 1, 40000, 140,0.140625 }, + {4201,4202, 0, 1, 40000, 806,0.109375 }, + {4203,4204, 0, 1, 2040, 486,0.125 }, + {4205,4206, 0, 1, 40000, 86,0 }, + {4207,4208, 0, 1, 40000, 80,0.03125 }, + {4209,4209, 0, 0, 40000, 73,0 }, + {4210,4210, 0, 0, 40000, 126,0 }, + {4211,4212, 0, 1, 40000, 400,0.0625 }, + {4213,4214, 0, 1, 40000, 120,0.0625 }, + {4215,4216, 0, 1, 40000, 0,0.09375 }, + {4217,4217, 0, 1, 40000, 0,0.125 }, + {4218,4219, 0, 1, 40000, 186,0 }, + {4220,4220, 0, 0, 40000, 166,0 }, + {4221,4221, 0, 0, 40000, 73,0 }, + {4222,4222, 0, 0, 40000, 60,0 }, + {4223,4224, 0, 1, 40000, 140,0 }, + {4225,4225, 0, 0, 40000, 140,0 }, + {4226,4226, 0, 0, 40000, 66,0 }, + {4227,4228, 0, 1, 40000, 133,0 }, + {4229,4229, 0, 0, 40000, 86,0 }, + {4230,4230, 0, 0, 40000, 73,0 }, + {4231,4231, 0, 0, 40000, 106,0 }, + {4232,4233, 0, 1, 40000, 186,0.03125 }, + {4234,4235, 0, 1, 40000, 86,0.046875 }, + {4236,4237, 0, 1, 40000, 0,0.03125 }, + {4238,4238, 0, 0, 40000, 300,0 }, + {4239,4239, 0, 0, 40000, 66,0 }, + {4240,4241, 0, 1, 40000, 73,0.125 }, + {4242,4243, 0, 1, 40000, 86,0.109375 }, + {4244,4245, 0, 1, 40000, 146,0.109375 }, + {4246,4247, 0, 1, 40000, 66,-0.03125 }, + {4248,4248, 0, 0, 40000, 60,0 }, + {4249,4250, 0, 1, 40000, 213,0.15625 }, + {4251,4252, 0, 1, 40000, 66,0.125 }, + {4253,4254, 0, 1, 40000, 100,0.03125 }, + {4255,4256, 0, 1, 40000, 1513,0.078125 }, + {4257,4258, 0, 1, 40000, 353,0.109375 }, + {4259,4260, 0, 1, 40000, 133,0.078125 }, + {4261,4262, 0, 1, 40000, 746,0.140625 }, + {4263,4264, 0, 1, 40000, 0,0.109375 }, + {4265,4266, 0, 1, 5033, 1606,0.0625 }, + {4267,4268, 0, 1, 40000, 1146,0.09375 }, + {4269,4270, 0, 1, 40000, 1586,0.109375 }, + {4271,4272, 0, 1, 40000, 0,0.09375 }, + {4273,4274, 0, 1, 40000, 1006,0.125 }, + {4275,4275, 0, 1, 2680, 793,0.109375 }, + {4276,4277, 0, 1, 40000, 0,-0.046875 }, + {4278,4279, 0, 1, 9000, 3186,0.125 }, + {4280,4281, 0, 1, 40000, 1073,-0.078125 }, + {4282,4283, 0, 1, 40000, 2093,0.140625 }, + {4284,4285, 0, 1, 40000, 0,0.078125 }, + {4286,4287, 0, 1, 9580, 713,0.03125 }, + {4288,4289, 0, 1, 6286, 380,0 }, + {4290,4291, 0, 1, 2220, 426,0.03125 }, + {4292,4292, 0, 0, 1166, 760,0 }, + {4293,4294, 0, 1, 1186, 240,0 }, + {4295,4296, 0, 1, 40000, 100,0.0625 }, + {4297,4297, 0, 0, 40000, 160,0 }, + {4298,4298, 0, 0, 40000, 120,0 }, + {4299,4299, 0, 0, 8673, 2413,0 }, + {4300,4300, 0, 0, 393, 126,0 }, + {4301,4302, 0, 1, 1220, 393,0.03125 }, + {4303,4303, 0, 0, 246, 93,0 }, + {4304,4305, 0, 1, 1953, 393,0 }, + {4306,4307, 0, 1, 566, 146,0 }, + {4308,4309, 0, 1, 4220, 133,0 }, + {4310,4311, 0, 1, 2873, 73,0.109375 }, + {4312,4312, 0, 0, 613, 60,0 }, + {4313,4314, 0, 1, 40000, 186,0 }, + {4315,4316, 0, 1, 11880, 2993,0 }, + {4317,4317, 0, 0, 1573, 86,0 }, + {4318,4319, 0, 1, 40000, 793,0 }, + {4320,4321, 0, 1, 40000, 173,0 }, + {4322,4323, 0, 1, 40000, 793,0 }, + {4324,4324, 0, 0, 606, 133,0 }, + {4325,4325, 34, 0, 133, 40,0 }, + {4326,4326, 28, 0, 193, 46,0 }, + {4327,4328, 39, 1, 553, 126,0 }, + {4327,4328, 33, 1, 553, 126,0 }, + {4329,4330, 63, 1, 160, 80,0 }, + {4331,4331, 15, 0, 113, 0,0 }, + {4332,4332, 36, 0, 106, 0,0 }, + {4332,4333, 36, 1, 480, 173,0.40625 }, + {4334,4335, 25, 1, 313, 153,0 }, + {4336,4335, 25, 1, 206, 100,0 }, + {4337,4338, 61, 1, 153, 93,0 }, + {4339,4340, 38, 1, 340, 133,0 }, + {4341,4342, 37, 1, 206, 93,0 }, + {4343,4344, 15, 1, 346, 153,0 }, + {4345,4346,100, 1, 146, 0,0.140625 }, + {4347,4348, 19, 1, 553, 200,0 }, + {4349,4349, 48, 0, 180, 86,0 }, + {4350,4351, 15, 1, 333, 153,0 }, + {4352,4353, 12, 1, 340, 146,0 }, + {4354,4355, 11, 1, 346, 146,0 }, + {4356,4357, 61, 1, 2706, 1033,0.09375 }, + {4358,4355, 8, 1, 340, 146,0 }, + {4359,4360, 91, 1, 1166, 366,-0.046875 }, + {4361,4361, 70, 0, 966, 346,0 }, + {4362,4363, 80, 1, 300, 93,0.125 }, + {4364,4364, 58, 0, 206, 53,0 }, + {4365,4357, 62, 1, 2333, 820,0.09375 }, + {4366,4367, 31, 1, 773, 200,0 }, + {4368,4360, 91, 1, 1160, 360,-0.03125 }, + {4369,4370, 41, 1, 373, 113,0 }, + {4371,4372, 35, 1, 406, 126,0 }, + {4373,4374, 29, 1, 146, 106,0 }, + {4375,4376, 41, 1, 400, 126,0 }, + {4375,4376, 37, 1, 400, 126,0 }, + {4377,4378, 54, 1, 286, 133,0 }, + {4377,4379, 48, 1, 286, 126,0 }, + {4380,4381, 77, 1, 193, 93,0 }, + {4382,4383, 72, 1, 200, 93,0 }, + {4384,4384, 40, 0, 513, 0,0 }, + {4385,4385, 38, 0, 200, 20,0 }, + {4386,4386, 36, 0, 620, 20,0 }, + {4387,4388, 60, 1, 120, 80,0 }, + {4388,4389, 60, 1, 380, 80,0 }, + {4390,4390, 73, 0, 166, 33,0 }, + {4391,4392, 68, 1, 153, 40,0 }, + {4393,4394, 18, 1, 200, 80,0 }, + {4395,4396, 18, 1, 253, 73,0 }, + {4397,4397, 90, 0, 193, 20,0 }, + {4398,4398, 90, 0, 793, 40,0 }, + {4399,4400, 64, 1, 373, 73,0.03125 }, + {4401,4402, 80, 1, 406, 153,0.03125 }, + {4403,4404, 64, 1, 1866, 606,0 }, + {4405,4405, 67, 0, 106, 26,0 }, + {4406,4407, 50, 1, 173, 0,0 }, + {4408,4408, 36, 0, 4646, 0,0 }, + {4409,4409, 0, 0, 40000, 86,0 }, + {4410,4410, 0, 0, 40000, 73,0 }, + {4411,4411, 0, 0, 2433, 700,0 }, + {4412,4412, 0, 0, 1233, 26,0 }, + {4413,4413, 0, 0, 40000, 66,0 }, + {4414,4414, 0, 0, 40000, 60,0 }, + {4415,4415, 0, 0, 40000, 60,0 }, + {4416,4416, 0, 0, 40000, 66,0 }, + {4417,4417, 0, 0, 40000, 66,0 }, + {4418,4418, 0, 0, 40000, 0,0 }, + {4418,4418, 73, 0, 40000, 0,0 }, + {4419,4419, 0, 0, 40000, 60,0 }, + {4420,4420, 0, 0, 40000, 60,0 }, + {4421,4421, 0, 0, 7326, 2486,0 }, + {4422,4422, 0, 0, 4886, 1586,0 }, + {4423,4423, 0, 0, 646, 20,0 }, + {4424,4424, 0, 0, 253, 20,0 }, + {4424,4424, 12, 0, 253, 20,0 }, + {4425,4425, 0, 0, 640, 100,0 }, + {4425,4425, 1, 0, 640, 106,0 }, + {4426,4426, 0, 0, 133, 106,0 }, + {4426,4426, 23, 0, 133, 106,0 }, + {4427,4427, 0, 0, 653, 100,0 }, + {4428,4428, 0, 0, 4166, 1546,0 }, + {4429,4429, 0, 0, 40000, 73,0 }, + {4430,4430, 0, 0, 40000, 60,0 }, + {4431,4431, 0, 0, 40000, 53,0 }, + {4432,4432, 0, 0, 40000, 0,0 }, + {4433,4433, 0, 0, 246, 20,0 }, + {4434,4434, 0, 2, 6, 0,0 }, + {4435,4435, 0, 0, 4946, 233,0 }, + {4436,4436, 0, 0, 4946, 233,0 }, + {4437,4437, 0, 0, 4953, 240,0 }, + {4438,4438, 0, 0, 4946, 233,0 }, + {4439,4439, 0, 0, 18233, 46,0 }, + {4440,4440, 0, 0, 2386, 26,0 }, + {4441,4441, 0, 0, 4640, 633,0 }, + {4442,4442, 0, 0, 18466, 100,0 }, + {4443,4443, 0, 0, 18440, 66,-2 }, + {4444,4444, 0, 0, 18440, 6140,-2 }, + {4445,4445, 0, 0, 1206, 433,-2 }, + {4446,4446, 0, 0, 4626, 240,0 }, + {4447,4447, 0, 0, 726, 400,0 }, + {4448,4448, 0, 0, 5866, 73,0 }, + {4449,4449, 0, 0, 40000, 73,0 }, + {4450,4450, 0, 0, 40000, 73,0 }, + {4451,4451, 0, 0, 40000, 73,0 }, + {4452,4452, 0, 0, 40000, 73,0 }, + {4453,4453, 0, 0, 6500, 346,0 }, + {4454,4454, 0, 0, 6506, 346,0 }, + {4455,4455, 0, 0, 40000, 66,-2 }, + {4456,4456, 0, 0, 40000, 66,-2 }, + {4457,4457, 0, 0, 40000, 0,0 }, + {4458,4458, 0, 0, 40000, 46,0 }, + {4459,4459, 0, 0, 40000, 0,0 }, + {4459,4459, 0, 0, 40000, 0,-2 }, + {4460,4460, 0, 0, 2386, 26,0 }, + {4461,4461, 0, 0, 40000, 73,-2 }, + {4462,4462, 0, 0, 5866, 26,-2 }, + {4463,4463, 0, 0, 40000, 133,0 }, + {4464,4464, 0, 0, 40000, 133,0 }, + {4465,4465, 0, 0, 40000, 126,0 }, + {4466,4466, 0, 0, 253, 20,0 }, + {4467,4467, 0, 0, 8866, 1366,0 }, + {4468,4468, 0, 0, 1040, 766,0 }, + {4469,4469, 0, 0, 40000, 146,-2 }, + {4470,4470, 0, 0, 40000, 153,-2 }, + {4471,4471, 0, 0, 40000, 466,-2 }, + {4472,4472, 0, 0, 40000, 66,0 }, + {4473,4473, 0, 0, 2333, 566,0 }, + {4474,4474, 0, 0, 40000, 140,-2 }, + {4475,4475, 0, 0, 40000, 100,-2 }, + {4476,4476, 0, 0, 40000, 226,-2 }, + {4477,4477, 0, 0, 40000, 0,0 }, + {3712,3712, 0, 0, 40000, 226,-2 }, + {4478,4478, 0, 0, 40000, 140,-2 }, + {4479,4479, 0, 0, 40000, 66,0 }, + {4480,4480, 0, 0, 40000, 73,0 }, + {4481,4481, 0, 0, 40000, 73,0 }, + {4482,4482, 0, 0, 40000, 86,-2 }, + {4483,4483, 0, 0, 40000, 80,0 }, + {4484,4484, 0, 0, 40000, 73,-2 }, + {4485,4485, 0, 0, 40000, 80,-2 }, + {4486,4486, 0, 0, 40000, 73,-2 }, + {4487,4487, 0, 0, 40000, 73,0 }, + {4488,4488, 0, 0, 40000, 73,0 }, + {4489,4489, 0, 0, 40000, 93,0 }, + {4490,4490, 0, 0, 40000, 73,0 }, + {4491,4491, 0, 0, 11946, 13,0 }, + {4492,4492, 0, 0, 40000, 73,0 }, + {4462,4462, 0, 0, 5866, 26,0 }, + {4493,4493, 0, 0, 40000, 820,0 }, + {4494,4494, 0, 0, 2153, 873,0 }, + {1221,1221, 0, 0, 40000, 293,0.171875 }, + {4495,4495, 0, 0, 1620, 120,0 }, + {4496,4496, 0, 0, 15120, 93,0 }, + {4497,4497, 0, 0, 14613, 93,0 }, + {4498,4498, 0, 0, 2346, 793,0 }, + {4499,4499, 0, 0, 40000, 2380,0 }, + {4500,4500, 0, 0, 40000, 1280,0 }, + {4501,4501, 0, 0, 40000, 1460,0 }, + {4502,4502, 0, 0, 40000, 2513,0 }, + {4503,4503, 0, 0, 14840, 1266,0 }, + {4504,4504, 0, 0, 4513, 640,0 }, + {4505,4505, 0, 0, 4680, 806,0 }, + {4506,4506, 0, 0, 40000, 100,0 }, + {4507,4507, 0, 0, 40000, 66,0 }, + {4508,4508, 0, 0, 2420, 413,0 }, + {4509,4509, 0, 0, 406, 73,-2 }, + {4510,4510, 0, 0, 1166, 400,0 }, + {4511,4511, 0, 0, 1213, 106,0 }, + {4512,4512, 0, 0, 273, 60,-2 }, + {4513,4513, 0, 0, 40000, 2380,0 }, + {4514,4514, 0, 0, 40000, 440,0 }, + {1261,1261, 0, 0, 40000, 2960,0 }, + {4515,4515, 37, 0, 973, 73,-2 }, + {4516,4516, 48, 0, 106, 0,-2 }, + {4517,4517, 48, 0, 286, 133,-2 }, + {4518,4518, 62, 0, 166, 60,0 }, + {4519,4519, 44, 0, 980, 360,0 }, + {4520,4520, 80, 0, 100, 0,0 }, + {4519,4519, 50, 0, 980, 346,0 }, + {4521,4521, 48, 0, 106, 0,-2 }, + {4519,4519, 55, 0, 973, 360,0 }, + {4522,4522, 61, 0, 513, 20,0 }, + {4519,4519, 58, 0, 966, 353,0 }, + {4519,4519, 63, 0, 973, 353,0 }, + {4523,4523, 71, 0, 1366, 580,0 }, + {4519,4519, 72, 0, 820, 306,0 }, + {4524,4524, 70, 0, 1886, 666,0 }, + {4523,4523, 88, 0, 1353, 560,0 }, + {4525,4525, 76, 0, 1873, 653,0 }, + {4526,4526, 84, 0, 260, 113,0 }, + {4523,4523, 68, 0, 1366, 553,0 }, + {4527,4527, 72, 0, 153, 53,0 }, + {4528,4528, 28, 0, 1193, 413,0 }, + {4524,4524, 81, 0, 1353, 480,0 }, + {4529,4529, 58, 0, 246, 120,-2 }, + {4529,4529, 55, 0, 246, 120,-2 }, + {4529,4529, 44, 0, 246, 120,-2 }, + {4529,4529, 49, 0, 246, 120,-2 }, + {4529,4529, 40, 0, 286, 133,-2 }, + {4530,4530, 55, 0, 740, 560,-2 }, + {4530,4530, 48, 0, 893, 693,-2 }, + {4531,4531, 52, 0, 513, 206,0 }, + {4531,4531, 45, 0, 513, 206,0 }, + {4532,4532, 48, 0, 173, 100,-2 }, + {4533,4533, 48, 0, 120, 266,-2 }, + {4534,4534, 48, 0, 253, 60,-2 }, + {4509,4509, 73, 0, 160, 20,-2 }, + {4509,4509, 68, 0, 160, 20,-2 }, + {4509,4509, 63, 0, 193, 20,-2 }, + {4535,4535,108, 0, 406, 26,0 }, + {4536,4536,108, 0, 740, 26,0 }, +}; + + + +//Returns total number of generated banks +int maxAdlBanks() +{ + return 75; +} + +const char* const banknames[76] = +{ + "AIL (Star Control 3, Albion, Empire 2, etc.)", + "Bisqwit (selection of 4op and 2op)", + "HMI (Descent, Asterix)", + "HMI (Descent:: Int)", + "HMI (Descent:: Ham)", + "HMI (Descent:: Rick)", + "HMI (Descent 2)", + "HMI (Normality)", + "HMI (Shattered Steel)", + "HMI (Theme Park)", + "HMI (3d Table Sports, Battle Arena Toshinden)", + "HMI (Aces of the Deep)", + "HMI (Earthsiege)", + "HMI (Anvil of Dawn)", + "DMX (Doom 2)", + "DMX (Hexen, Heretic)", + "DMX (DOOM, MUS Play)", + "AIL (Discworld, Grandest Fleet, etc.)", + "AIL (Warcraft 2)", + "AIL (Syndicate)", + "AIL (Guilty, Orion Conspiracy, TNSFC ::4op)", + "AIL (Magic Carpet 2)", + "AIL (Nemesis)", + "AIL (Jagged Alliance)", + "AIL (When Two Worlds War :MISS-INS:)", + "AIL (Bards Tale Construction :MISS-INS:)", + "AIL (Return to Zork)", + "AIL (Theme Hospital)", + "AIL (National Hockey League PA)", + "AIL (Inherit The Earth)", + "AIL (Inherit The Earth, file two)", + "AIL (Little Big Adventure :: 4op)", + "AIL (Wreckin Crew)", + "AIL (Death Gate)", + "AIL (FIFA International Soccer)", + "AIL (Starship Invasion)", + "AIL (Super Street Fighter 2 :4op:)", + "AIL (Lords of the Realm :MISS-INS:)", + "AIL (SimFarm, SimHealth :: 4op)", + "AIL (SimFarm, Settlers, Serf City)", + "AIL (Caesar 2, :p4op::MISS-INS:)", + "AIL (Syndicate Wars)", + "AIL (Bubble Bobble Feat. Rainbow Islands, Z)", + "AIL (Warcraft)", + "AIL (Terra Nova Strike Force Centuri :p4op:)", + "AIL (System Shock :p4op:)", + "AIL (Advanced Civilization)", + "AIL (Battle Chess 4000 :p4op:)", + "AIL (Ultimate Soccer Manager :p4op:)", + "AIL (Air Bucks, Blue And The Gray, etc)", + "AIL (Ultima Underworld 2)", + "AIL (Kasparov's Gambit)", + "AIL (High Seas Trader :MISS-INS:)", + "AIL (Master of Magic, :4op: std percussion)", + "AIL (Master of Magic, :4op: orchestral percussion)", + "SB (Action Soccer)", + "SB (3d Cyberpuck :: melodic only)", + "SB (Simon the Sorcerer :: melodic only)", + "OP3 (The Fat Man 2op set)", + "OP3 (The Fat Man 4op set)", + "OP3 (JungleVision 2op set :: melodic only)", + "OP3 (Wallace 2op set, Nitemare 3D :: melodic only)", + "TMB (Duke Nukem 3D)", + "TMB (Shadow Warrior)", + "DMX (Raptor)", + "OP3 (Modded GMOPL by Wohlstand)", + "SB (Jammey O'Connel's bank)", + "TMB (Default bank of Apgee Sound System)", + "WOPL (4op bank by James Alan Nguyen and Wohlstand)", + "TMB (Blood)", + "TMB (Lee)", + "TMB (Nam)", + "WOPL (DMXOPL3 bank by Sneakernets)", + "EA (Cartooners)", + "WOPL (Apogee IMF 90-ish)", + NULL +}; +const unsigned short banks[75][256] = +{ + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 33, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 127, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, + 171, 172, 173, 174, 175, 176, 177, 178, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 0, 179, 2, 180, 181, 182, 183, 184, 185, 9, 186, 11, 187, 188, 189, 190, + 191, 192, 193, 194, 20, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, + 206, 207, 208, 35, 209, 34, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 50, 223, 224, 53, 225, 55, 56, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 71, 72, 240, 241, 242, 243, 244, 245, + 246, 247, 248, 82, 249, 250, 251, 86, 252, 253, 254, 255, 91, 92, 256, 257, + 258, 259, 260, 98, 99, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 111, 272, 273, 274, 115, 275, 276, 277, 278, 120, 279, 280, 281, 282, 295, 284, + 127, 132, 285, 286, 127, 287, 288, 289, 290, 291, 292, 127, 127, 293, 294, 295, + 289, 296, 297, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 318, 320, 318, 321, 318, + 318, 322, 318, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, + 336, 337, 337, 338, 339, 320, 340, 341, 342, 343, 344, 345, 346, 346, 169, 170, + 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, + 164, 363, 156, 364, 292, 365, 366, 367, 368, 178, 369, 369, 369, 369, 369, 369, + 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, + }, + { + 370, 179, 371, 180, 372, 373, 374, 375, 376, 377, 186, 378, 187, 379, 380, 190, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 381, 28, 370, 30, 31, + 32, 33, 34, 35, 36, 382, 38, 33, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 383, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 385, 386, 387, 388, 389, + 390, 391, 392, 369, 393, 308, 394, 395, 396, 397, 398, 399, 398, 400, 401, 402, + 403, 404, 405, 406, 404, 406, 407, 404, 408, 404, 330, 409, 410, 411, 412, 413, + 414, 415, 416, 417, 418, 419, 420, 341, 342, 421, 422, 423, 424, 425, 426, 427, + 428, 429, 419, 430, 351, 431, 308, 432, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 295, 434, 435, 28, 436, 31, 30, 437, 438, 439, 440, 441, 442, 38, 46, 443, + 79, 84, 444, 445, 49, 89, 92, 93, 105, 446, 447, 448, 449, 450, 451, 452, + 453, 454, 455, 456, 119, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, + 468, 469, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 304, 470, 471, 472, 473, + 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, + 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, + 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 345, 517, 518, 519, 520, + 521, 522, 523, 524, 525, 526, 527, 528, 355, 356, 357, 358, 359, 529, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 295, 434, 435, 28, 436, 31, 30, 437, 438, 439, 440, 441, 442, 38, 46, 443, + 79, 84, 444, 445, 49, 89, 92, 93, 105, 446, 447, 448, 449, 450, 451, 452, + 453, 454, 455, 456, 119, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, + 468, 295, 112, 99, 530, 531, 93, 532, 248, 107, 116, 533, 28, 78, 534, 535, + 536, 79, 94, 38, 33, 115, 537, 538, 539, 540, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 541, 542, 543, 544, 545, 546, 295, 295, + 547, 132, 134, 136, 138, 139, 141, 173, 156, 291, 292, 127, 548, 549, 550, 551, + 552, 362, 553, 143, 295, 295, 295, 295, 295, 295, 295, 304, 470, 471, 472, 473, + 474, 475, 476, 477, 478, 479, 480, 481, 482, 554, 555, 556, 557, 487, 488, 489, + 490, 491, 492, 493, 558, 495, 496, 497, 498, 499, 500, 501, 559, 503, 504, 505, + 506, 507, 508, 560, 561, 511, 512, 513, 514, 562, 563, 345, 517, 518, 519, 520, + 521, 522, 523, 524, 525, 526, 527, 528, 564, 356, 565, 358, 359, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 295, 434, 435, 28, 436, 31, 30, 437, 438, 439, 440, 441, 442, 38, 46, 443, + 79, 84, 444, 445, 49, 89, 92, 93, 105, 446, 447, 448, 449, 450, 451, 452, + 453, 454, 455, 456, 119, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, + 468, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 566, 567, 568, 569, + 570, 34, 571, 572, 437, 51, 52, 84, 573, 574, 575, 576, 577, 85, 530, 90, + 93, 94, 101, 578, 114, 119, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 293, 127, 548, + 550, 296, 297, 296, 297, 298, 299, 300, 301, 302, 303, 304, 470, 471, 472, 473, + 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, + 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, + 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 345, 517, 518, 519, 520, + 521, 522, 523, 524, 525, 526, 527, 528, 355, 356, 357, 358, 359, 360, 361, 362, + 164, 363, 156, 364, 292, 365, 366, 367, 368, 178, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 295, 434, 435, 28, 436, 31, 30, 437, 438, 439, 440, 441, 442, 38, 46, 443, + 79, 84, 444, 445, 49, 89, 92, 93, 105, 446, 447, 448, 449, 450, 451, 452, + 453, 454, 455, 456, 119, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, + 468, 295, 579, 580, 581, 295, 295, 582, 295, 295, 295, 295, 583, 584, 585, 586, + 587, 444, 588, 589, 590, 295, 591, 592, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 577, 566, + 437, 90, 460, 28, 593, 594, 595, 596, 31, 597, 598, 28, 441, 442, 567, 530, + 93, 572, 599, 279, 30, 435, 100, 94, 575, 38, 281, 437, 447, 51, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 356, 357, 358, 359, 304, 470, 471, 472, 473, + 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, + 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, + 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 345, 517, 518, 519, 520, + 521, 522, 523, 524, 525, 526, 527, 528, 355, 600, 601, 602, 127, 548, 296, 603, + 604, 550, 605, 606, 362, 607, 135, 608, 361, 609, 610, 611, 612, 507, 146, 613, + 547, 500, 614, 364, 173, 615, 366, 600, 616, 617, 618, 619, 620, 621, 295, 295, + }, + { + 622, 179, 371, 180, 372, 373, 374, 375, 376, 377, 186, 378, 187, 379, 380, 190, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 381, 28, 370, 30, 31, + 32, 33, 34, 35, 36, 382, 38, 33, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 383, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 623, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 624, 625, 166, 167, 168, 169, 170, + 171, 172, 626, 174, 627, 176, 177, 178, 178, 178, 178, 178, 178, 178, 178, 178, + 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, + 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, + }, + { + 628, 629, 371, 442, 372, 630, 571, 631, 376, 632, 186, 633, 634, 379, 380, 635, + 636, 598, 597, 637, 28, 638, 437, 437, 639, 640, 26, 641, 28, 572, 437, 642, + 643, 566, 570, 644, 567, 34, 568, 569, 212, 645, 646, 42, 43, 647, 648, 649, + 650, 651, 652, 653, 51, 52, 53, 573, 654, 655, 656, 657, 658, 574, 575, 576, + 659, 599, 660, 661, 662, 68, 663, 664, 71, 665, 666, 74, 75, 667, 668, 669, + 577, 85, 441, 442, 38, 84, 46, 86, 530, 88, 670, 90, 91, 92, 93, 94, + 258, 671, 97, 532, 99, 100, 101, 672, 536, 673, 674, 675, 676, 677, 464, 110, + 678, 578, 113, 114, 679, 116, 680, 599, 31, 462, 279, 125, 465, 281, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 681, 682, 683, 500, 503, 614, 684, 685, 296, 552, 550, 605, 600, + 548, 602, 127, 601, 293, 549, 604, 551, 603, 298, 362, 299, 135, 300, 361, 301, + 302, 547, 303, 142, 143, 144, 619, 146, 291, 686, 149, 687, 367, 368, 292, 365, + 366, 364, 688, 158, 689, 690, 615, 162, 163, 164, 363, 691, 167, 168, 169, 170, + 171, 172, 173, 174, 175, 176, 177, 178, 692, 693, 694, 695, 696, 697, 698, 699, + 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 712, 713, 714, 715, 716, 713, 717, 718, 719, 720, 721, 722, 723, 724, 715, 725, + 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 739, 739, + 740, 741, 742, 743, 744, 533, 745, 568, 678, 746, 747, 748, 739, 739, 749, 750, + 751, 752, 751, 753, 754, 212, 754, 755, 756, 757, 758, 759, 759, 760, 204, 761, + 762, 739, 739, 763, 762, 762, 764, 765, 766, 766, 766, 766, 767, 768, 767, 769, + 769, 770, 771, 664, 772, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, + 783, 784, 785, 786, 787, 788, 788, 789, 786, 753, 790, 780, 780, 780, 791, 791, + 792, 793, 794, 795, 796, 680, 680, 797, 798, 799, 800, 801, 802, 803, 742, 804, + 805, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, + 820, 821, 822, 823, 821, 824, 825, 821, 826, 821, 827, 828, 829, 830, 831, 832, + 833, 834, 834, 835, 835, 825, 825, 836, 837, 838, 295, 839, 840, 841, 295, 295, + 842, 843, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, + 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, + 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, + 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, + 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, + 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, + 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, + 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 971, 972, 973, 974, + 975, 310, 976, 977, 978, 974, 979, 980, 981, 982, 983, 984, 983, 985, 986, 987, + 988, 989, 990, 325, 989, 325, 991, 989, 992, 989, 330, 993, 994, 995, 996, 997, + 998, 999,1000,1001,1002,1003, 340,1004,1005,1006,1007,1008,1009, 310,1010,1011, +1012, 429,1003, 430,1013,1014, 974,1015, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1016,1017,1018,1019,1020,1021,1022,1023,1024,1025,1026,1027,1028,1029,1030,1031, +1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047, +1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063, +1064,1065,1066,1067, 223,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078, +1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094, +1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110, +1111,1112,1113,1114,1115,1116,1117,1118,1119,1120,1121,1122,1123,1124,1125,1126, +1127,1128,1129,1130,1131,1132,1133,1134,1135,1136,1137,1138,1139,1140,1141,1142, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 305, 306, 307, 308, +1143, 425, 311,1144, 393, 308, 394, 395, 396, 397,1145, 399,1145, 400,1146, 402, + 403, 404, 405, 406, 404, 406, 407, 404, 408, 404, 330, 409, 410, 411, 412, 413, + 414, 415, 416, 417, 418, 419, 420, 341, 342, 421, 422, 423, 424, 425, 426, 427, + 428, 429, 419, 430, 351, 431, 308, 432, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1147,1017,1018,1148,1149,1150,1022, 184,1024,1025,1026,1027,1028,1151,1152,1153, +1154,1033,1155,1035,1036,1156,1038,1039,1040,1157,1042,1158,1159,1160,1161,1047, +1162,1163,1164,1165,1166,1167,1168,1169,1056,1170,1058,1059,1060,1171,1062,1172, +1173,1065,1066,1174, 223,1068,1069,1070,1175,1176,1177,1074,1178,1179,1180,1078, +1079,1080,1081,1082,1181,1084,1182,1183,1087,1184,1089,1090,1185,1186, 922,1187, +1188,1189,1097,1098,1190,1100,1191,1192,1103,1193,1194,1195,1196,1197,1198,1199, +1111,1200,1201,1202,1203,1116,1204,1205,1119,1206,1207,1122,1123,1124,1125,1208, +1127,1209,1210,1130,1131,1211,1212,1213,1135,1214,1215,1138,1139,1216,1217,1218, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 385, 386, 387, 388, 389, + 390, 391, 392,1219,1219, 308, 394, 395, 396, 397, 398, 399, 398, 400, 401, 402, + 403, 404, 405, 406, 404, 406, 407, 404, 408, 404, 330, 409, 410, 411, 412, 413, + 414, 415, 416,1220,1221, 419, 420, 341, 342, 421, 422, 423, 424, 425, 426, 427, + 428, 429, 419, 430, 351, 431, 308, 432, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 33, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 623, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 624, 625, 166, 167, 168, 169, 170, + 171, 172, 626, 174, 627, 176, 177, 178, 178, 178, 178, 178, 178, 178, 178, 178, + 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, + 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, + }, + { +1222,1223,1224,1225,1226,1227,1228,1229,1230,1231,1232,1233,1234,1235,1236,1237, +1238,1239,1240,1241,1242,1243,1244,1245,1246,1247,1248,1249,1250,1251,1252,1253, +1254,1255,1256,1257,1258,1259,1260,1261,1262,1263,1264,1265,1266,1267,1268,1269, +1270,1271,1272,1273,1274,1275,1276,1277,1278,1279,1280,1281,1282,1283,1284,1285, +1286,1287,1288,1289,1290,1291,1292,1293,1294,1295,1296,1297,1298,1299,1300,1301, +1302,1303,1304,1305,1306,1307,1308,1309,1309,1310,1311,1312,1313,1314,1315,1316, +1317,1318,1319,1320,1321,1322,1323,1324,1325,1326,1327,1328,1329,1330,1331,1332, +1333,1334,1335,1336,1337,1338,1339,1340,1341,1342,1343,1344,1345,1346,1347,1348, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 312,1349,1350,1351,1352,1353,1354, 319,1355, 320,1356, 321,1357, +1358,1359,1360,1361,1362,1363,1364,1365,1366,1359,1367,1361, 332, 333, 334, 335, + 336,1368,1369, 338, 339, 320,1370, 295, 295, 295, 295,1372,1373,1374,1375, 295, + 347, 348, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1222,1223,1224,1225,1226,1227,1228,1229,1230,1231,1232,1233,1234,1235,1236,1237, +1238,1239,1240,1241,1242,1243,1244,1245,1246,1247,1248,1249,1250,1376,1377,1253, +1378,1255,1256,1379,1380,1259,1260,1261,1262,1263,1264,1265,1266,1267,1268,1269, +1270,1271,1272,1273,1274,1275,1276,1277,1278,1279,1381,1281,1282,1283,1284,1285, +1286,1287,1288,1289,1290,1291,1292,1293,1294,1295,1296,1297,1298,1299,1300,1301, +1302,1303,1304,1305,1306,1307,1308,1309,1309,1310,1311,1312,1313,1314,1315,1316, +1317,1318,1319,1320,1321,1322,1323,1324,1325,1326,1327,1328,1329,1330,1331,1332, +1333,1334,1335,1336,1337,1338,1339,1340,1341,1342,1343,1344,1345,1346,1347,1348, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 312,1349,1350,1351,1352,1382,1354, 319,1355, 320,1356, 321,1357, +1358,1359,1360,1361,1362,1363,1364,1383,1366,1359,1384,1361, 332, 333, 334, 335, + 336,1368,1369, 338, 339, 320,1370, 295, 295, 295, 295,1372,1373,1374,1375, 295, + 347, 348, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1222,1223,1224,1225,1226,1227,1228,1229,1230,1231,1232,1233,1234,1235,1236,1237, +1238,1239,1240,1241,1242,1243,1244,1245,1246,1247,1248,1249,1250,1385,1252,1253, +1386,1255,1256,1379,1380,1259,1260,1261,1262,1263,1264,1265,1266,1267,1268,1269, +1270,1271,1272,1273,1274,1275,1276,1277,1278,1279,1381,1281,1282,1283,1284,1285, +1286,1287,1288,1289,1290,1291,1292,1293,1294,1295,1296,1297,1298,1299,1300,1301, +1302,1303,1304,1305,1306,1307,1308,1309,1309,1310,1311,1312,1313,1314,1315,1316, +1317,1318,1319,1320,1321,1322,1323,1324,1325,1326,1327,1328,1329,1330,1331,1332, +1333,1334,1335,1336,1337,1338,1339,1340,1341,1342,1343,1344,1345,1346,1347,1348, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 312,1349,1350,1351,1352,1353,1354, 319,1355, 320,1356, 321,1357, +1358,1359,1360,1361,1362,1363,1364,1383,1366,1359,1387,1361, 332, 333, 334, 335, + 336,1368,1369, 338, 339, 320,1370, 295, 295, 295, 295,1372,1373,1374,1375, 295, + 347, 348, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 622, 179, 371,1388, 372, 373, 374, 375,1389, 377, 186,1390, 187, 379,1391,1392, +1393,1394,1395,1396,1397,1398,1399,1400,1401, 25, 26, 381, 28, 370, 30,1402, +1403, 33, 34, 35, 36, 382, 38, 33, 39, 40,1404, 42, 43, 44, 45, 46, + 222,1405,1406,1407,1408,1409,1410,1411, 55, 56,1412, 58, 59,1413,1414, 62, +1415,1416,1417,1418, 67, 68, 69, 70, 71, 72,1419, 74,1420,1421,1422,1423, + 79, 80, 81,1424, 83,1425,1426, 86,1427,1428,1429,1430,1431,1432, 256, 257, +1433, 96, 97,1434,1435,1436, 262, 263, 103, 104, 105,1437, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 660, 119, 120, 121, 122, 123, 124, 125, 126, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 127, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145,1438, 147, 148,1439, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163,1440,1441, 166, 167, 168, 169,1442, + 171, 172, 173, 174, 175, 176, 177, 178, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 0, 1, 2, 3, 4, 5,1443, 7, 8,1444, 10, 11, 12, 13,1445, 15, + 16, 17, 18,1446, 20, 21, 22, 23,1447, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 33, 39, 40, 41, 42,1448,1449,1450,1451, +1452,1453,1454, 50,1455, 52, 53,1456,1457,1458,1459, 58,1460,1461, 61, 62, + 63, 64, 65, 66,1462, 68,1463,1464,1465,1466, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93,1467, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114,1468, 116,1469, 277, 119, 120, 121, 122, 123, 124, 125, 126, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,1470,1470,1471,1472,1473,1474,1475,1476,1477,1478,1479,1480,1481, +1482,1483,1484, 142,1485, 144,1486,1487, 147,1488,1489, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159,1490, 161, 162, 163, 164, 165, 166,1491, 168, 169, 170, + 171, 172, 173, 174, 175, 176, 177, 178, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1492, 713, 714, 715, 716, 713, 717,1492, 719,1493, 721,1494, 723,1495, 715, 725, + 728, 728, 728, 729, 730,1496,1497,1497, 734,1498, 736,1499, 739, 739, 739, 739, + 740,1500,1501,1502,1500, 533, 745,1503, 678, 746,1504,1505, 739, 739,1506,1507, +1508, 781,1508, 753,1509,1510,1509,1511, 756, 757, 758, 759, 759,1512,1513, 761, + 762, 739, 739, 739, 762, 762, 764, 765, 766, 766, 766, 766, 767,1514, 769, 769, + 769, 770, 771,1515,1516,1516,1517, 774,1518,1518,1519,1520, 779,1521, 781,1522, + 783, 784,1523, 786,1524,1525,1525,1526, 786, 753,1527,1521,1521,1521,1528,1528, + 792, 793, 680, 795, 796, 680, 680,1529,1530,1531,1532,1533, 802, 803,1534,1535, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,1536,1536,1537,1538, 811,1537,1539,1540,1539,1541,1542,1541,1541, +1543,1544,1537, 295,1545,1546, 295, 295,1542, 295, 295, 295,1547,1539,1537,1548, +1542,1537,1539,1549,1550,1541,1541,1541,1541,1541, 295,1541, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1551,1552,1553,1554, 181, 182, 183,1555, 185,1556,1557,1558,1559, 188, 189,1560, +1561,1562,1563,1564,1565, 195, 196, 197, 198, 199, 200, 201, 202,1566,1567, 205, + 206, 207, 208,1568,1569,1570,1571, 211,1572,1573, 214, 215, 216, 217, 218, 219, + 220, 221,1574,1575,1576,1577,1578, 225,1579,1580, 226,1581,1582, 229, 230, 231, + 232, 233, 234, 235,1583, 237, 238, 239,1584,1585, 240,1586, 242, 243, 244,1587, + 246, 247,1588,1589, 249, 250, 251,1590, 252, 253, 254, 255,1591,1592,1593,1594, +1595, 259,1596,1597,1598,1599,1600,1601, 264, 265, 266, 267, 268, 269, 270, 271, +1602, 272, 273,1603,1604,1605, 276,1606,1607, 295, 295,1610,1611,1612, 295, 284, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 127, 127,1613,1614,1615,1616,1617,1618,1619,1620,1621,1622,1623, +1624,1625,1626,1627,1628,1629,1630,1631,1632,1633,1634,1635,1636,1637,1638,1639, +1640,1641,1642,1643,1644,1645,1646,1647,1648,1649,1650,1651,1652,1653,1654,1655, +1656,1657,1658,1659,1660,1661,1662,1663, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1492, 713, 714,1664,1665,1666, 717,1492, 719,1667,1668, 722, 723, 724, 715, 725, + 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736,1669,1670,1671, 739, 739, + 740,1672,1673,1674,1675,1676,1677,1678,1679, 746,1680, 748,1681,1682,1683,1684, +1685, 752,1686,1687,1688,1689, 732, 755, 756, 757, 758, 759, 759,1690, 204,1691, +1692,1693,1694,1695,1696,1697,1698,1699, 766,1700,1701, 766,1702, 768,1703,1704, +1705,1706,1707, 664, 772, 772, 773,1706,1690,1708,1708,1708,1708,1708,1709, 782, +1710, 784,1711,1712, 787, 788, 788, 789,1712, 753, 790, 780, 780, 780, 791, 791, + 792, 793,1713,1714, 796, 680, 680, 797, 798, 799, 800,1533,1715, 803,1534,1535, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,1716,1717,1718,1719,1720,1720,1539, 814,1539,1721,1542, 818,1721, +1543,1544,1537, 295,1545,1546, 295, 295,1542, 295, 295, 295,1547,1539,1537,1722, +1542,1537,1539,1549,1550,1721,1723,1721,1724,1725, 295,1726, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1727,1728,1729,1730,1731,1732,1733,1734,1735,1736,1737,1738,1739,1740,1741,1742, +1743,1744,1745, 194,1746,1747,1748,1749,1750,1751,1752,1753,1754,1755,1756,1757, +1758,1759,1760,1761,1762,1763,1764,1765,1766,1767,1768,1769,1770,1771,1772,1773, +1774,1775,1776,1777,1778,1779,1780,1781,1782,1783,1784,1785, 228,1786,1787,1788, +1789,1790,1791,1792,1793,1794,1795,1796,1797,1798,1799,1800,1801,1802,1766, 245, +1803,1804,1805,1806,1807,1808,1809,1810,1811,1812,1813,1814,1815,1816,1817,1818, +1819,1820,1821,1822,1823,1824,1825,1826,1827,1828,1829,1830,1831,1832,1833,1834, +1835,1836,1837, 274,1838,1839,1840,1841, 278,1842,1843,1844,1845,1846,1847,1848, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,1849,1849,1850,1851,1852,1853,1854,1855,1856,1857,1858,1859,1860, +1861,1862,1863,1864,1865,1864,1866,1867,1868,1869,1870,1864,1871,1872,1873,1874, +1875,1876,1877,1878,1879,1880,1881,1882,1883,1884,1885,1886,1887,1888,1889,1890, +1891,1892,1893,1894,1895,1896,1897,1898,1899,1900,1901,1902, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1903, 295,1904, 713,1905, 713, 717,1492, 598,1493,1493,1906,1907,1907,1907, 761, + 728, 728, 728, 730, 730, 730,1497,1497,1908,1909,1910,1910, 739, 739,1911, 739, +1912,1913,1501,1502,1913, 533,1914,1503, 678, 678,1504, 764, 739, 739,1506,1507, +1915,1916,1917,1918,1509,1510,1509,1511, 756, 757, 758, 759, 759,1512,1513,1919, +1909,1920,1921, 295,1910, 295, 764,1922,1923, 295, 295, 295,1924,1925, 295, 769, +1926,1927, 295, 295, 295, 295,1928,1929,1930,1518,1519,1931,1932,1932,1933,1934, +1935,1936,1937,1938,1939, 295,1525,1940,1937,1934, 295,1941, 295, 295, 295,1941, +1942, 295, 295, 295,1943,1944, 295,1945, 295, 295,1921, 295, 280,1903, 678, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295,1946,1947,1948,1949,1948,1539,1950,1539,1541,1542,1541,1542, +1537,1951,1537,1952, 295,1948,1953,1948, 295, 295, 295, 295,1537,1539,1537,1537, +1542,1537,1539,1948,1954,1541,1954,1541,1541,1541, 295,1541, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, +1955, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295,1956, 295, 295, 295, 295, 295, 295, 295, 295,1957, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295,1958, 295, 295,1959, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 456,1960, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,1961,1961,1961, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295,1962,1963, 295, 295, 295,1964, 295, 295, 295, 295, 295, 295, 295, 295, 295, +1965,1966, 295, 295, 295, 295, 295, 295, 295, 757,1967,1968,1968, 295, 295, 295, + 295, 295, 295, 295, 295, 295,1969,1969,1970,1971,1970, 295, 295, 295, 295, 295, + 295, 295,1972,1972, 295, 295, 295, 295, 295,1973,1520, 295, 779, 295, 295, 295, + 295, 295, 295, 295, 295, 295,1974,1964,1975,1976,1977,1978, 295, 295, 673,1979, + 459,1980,1981, 295, 295,1982, 295, 295,1983, 295, 295,1984,1985,1986,1987, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295,1988,1988,1988, 295, 295, 295, 295, 295, 295, 295, + 295,1989, 295, 295, 295, 295,1990, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1991,1992,1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,1398, +2006,2006,2006,2007,2007,2007,2008,2009,2010,2011,2012,2013, 34,2014, 33,2015, +2016,2017,2018,2019,2020,2021,2022,2023,2024,2025,2026,2027,2028,2029,2030,2031, +2032,2033,2034,2035,2036,2037,2038,2039,2040, 45, 45,2041,2042,2043,2044,2045, +2046, 739,2047,2048,2049,2050,2051,2051,2052,2053,2053,2054,2055,2056,2057,2058, +2059,2060,2061,2062,2063,2064,2065,2066,2067,2068,2069,2070,2071,2071,2072,2073, +2074,2075,2076,2077,2078,2079,2080, 275,2081,2082,1527,2083,2084,2085,2086,2087, +2088,2089,2090,2091,2092,2093,2094,2095,2096,2097,2098,2099, 122,2100,2101,2102, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,2104,2105,2106,2107,2108,2109,2110,2111,2110,2112,2113,2114,2113, +2115, 547,2115,2116, 295, 295,2117, 295,2118, 295, 295, 295,2119,2120,2121,2122, +2123,2124,2125,2126,2127, 160, 161, 162, 163,2128, 295,2129, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +2130,2130,2130,2130,2130,2130, 727,2131,2132,2133,2134,2135,2136,2137,2138,2139, +2140,2140,2140,2140,2141,2142,2143,2142,2144,2145,2144,2144,2144,2146, 204,2147, +2148,2148,2148,2148,2148,2148,2148,2148,2149,2149,2149,2148,2149,2150,2151,2152, +2149,2149,2153,2153, 754,2154,2154,2155, 227,2156,2157, 227,2158, 227, 227, 227, + 227, 227, 227, 227, 733,2158,2159,2160,2161,2161,2161,2162,2161,2161,2163,2163, +2164, 770,2165,2165,2130,2154,2166,2167,2168,2169,2170,2171,2172,2173,2174,2166, +2174,2175,2138,2176,2177,2178,2179,2180,2181,2182,2183,2184,2185,2186,2187,2187, +2132,2188,2189,2190,2191,2191,2191,2192,2193,2165,2194,2195,2196, 282,2197,1534, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,1716,1716,1716,1716,1716, +1544,1544,1544,1716,1536,2198,1538,1538,1547,2199,2200,2199,2201,2199,2201,2202, +2203,2204,2205, 814,2204, 814, 814, 816,2206,2204,2207, 814,2208,2209,1726,1725, +1724,2210,2211,1549,1550,2212,2212,1721,1724,1725,1725,2198,2213,2214,2214,2214, +1722,2215,2212,2215,2215,2198,1724,1726, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, + 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871,2216, 873, 874, 875, + 876, 877, 878,2217, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, + 892, 893, 894,2218, 896, 897, 898, 899, 900,2219,2220, 903,2221, 905,2222,2223, +2224, 909,2225, 911, 912,2226,2227, 915, 916,2228,2229, 919, 920, 921, 922, 923, +2230, 925, 926, 927, 928, 929,2231, 931, 932, 933, 934, 935,2232,2233, 938,2234, + 940,2235, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, + 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966,2236, 968,2237, 970,2238, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 971, 972, 973, 974, + 975, 310, 976, 977, 978, 974,2239, 980,2240, 982,2241, 984,2242, 985,2243, 987, + 988,2244, 990, 325,2244, 325, 991,2244,2245,2246, 330, 993,2247,2248,2249, 997, + 998,2250,2251,1001,1002,1003, 340,1004,1005,1006,1007,1008,1009, 310,1010,1011, +2252, 429,1003, 430,1013,1014, 974,1015, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 0, 1, 2, 295, 295, 295, 5,2253, 295, 295,1395, 295,1396,1396,2254,1398, + 295, 295, 295, 295, 295, 295,2255,2255,2256,2256, 62,2256, 33, 33, 38, 38, +1427,2257,1408,1408, 96,1434, 34,1410, 260,1433, 35,1433, 62, 295,1393, 79, +1406,1407,2258, 44, 295, 295,1404,1404, 42, 45, 45,2259, 25,2260, 295, 103, + 295,1403, 295, 34, 36, 37, 35, 35, 72, 72, 295, 295,2261, 74, 295, 295, +1420,1418, 70, 70, 67, 68, 69,1399, 295, 295, 56, 295, 59, 59,1412, 295, + 295, 12, 260, 295, 10, 9,2262, 13, 12,1437, 295,1421,1422,1420,1420,1423, +2263, 116, 295, 295, 295,2264, 115,2265,2266,2267,1411, 123, 122,2265,2268,2265, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 127, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144,2269,1438, 147, 148,1439, 150, 151, 152, 153, 154, + 155, 156,2270, 158, 159, 160, 161, 162, 163,1440,1441, 166, 167, 168, 169,1442, + 171, 172, 173, 174, 175, 176, 177, 178, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 0, 1, 2, 295, 295, 295, 5,2253, 295, 295,1395, 295,1396,1396,1396,1398, + 295, 295, 295, 295, 295, 295,2255,2255,2256,2256, 62,2256, 33, 33, 38, 38, +1427,1408,1408,1408, 96,1434, 34,1410, 260,1433, 35,1433, 62, 295,1393, 79, +1406,1407, 222, 44, 295, 295,1404,1404, 42, 45, 45,1401, 25, 27, 295, 103, + 295,1403, 295, 34, 36, 37, 35, 35, 72, 72, 295, 295,1419, 74, 295, 295, +1420,1418, 70, 70, 67, 68, 69,1399, 295, 295, 56, 295, 59, 59,1412, 295, + 295, 12, 260, 295, 10, 9,2262, 13, 12,1437, 295,1421,1422,1420,1420,1423, + 46, 116, 295, 295, 295, 115, 115,2265,2266,2267,1411, 123, 122,2265,2268,2265, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 127, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145,1438, 147, 148,1439, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163,1440,1441, 166, 167, 168, 169,1442, + 171, 172, 173, 174, 175, 176, 177, 178, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1551,1552,1553,1554,2271, 182, 183,1555,2272,1556,1557,2273,1559, 188,2274,1560, +1561,1562,1563,1564,1565, 195, 196, 197, 198, 199, 200, 201, 202,1566,1567, 205, + 206, 207, 208,1568,1569,1570,1571, 211,1572,1573, 214, 215, 216, 268,2275,2276, +2277,2277,2278,1575,1576,1577,1578, 225,1579,1580, 226,1581,2279,2280, 230, 254, + 232, 233, 234, 235,2281, 237, 238, 239,1584,1585,2282,1586, 242, 243, 244,1587, + 246, 247,1588,1589, 249, 250, 251,1590,2283, 253, 254, 255,1591,1592,1593,1594, +1595, 259,1596,1597,1598,1599,1600,1601, 264, 265, 266, 267, 268, 269, 270, 271, +1602, 272, 273,1603,1604,1605, 276,1606,1607, 295, 295,1610,1611,1612, 295, 284, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,2284, + 295, 295, 295,2284,2285,2286,1614,1615,2287,2288,1618,1619,1620,1621,1622,1623, +1624,1625,1626,2289,2290,1629,1630,1631,2291,1633,2292,2293,2294,2295,1638,1639, +1640,1641,1642,1643,1644,1645,1646,1647,1648,2296,2297,1651,1652,1653,1654,1655, +1656,1657,2298,2299,2300,1661,1662,1663, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1492, 713,1904, 713,1905, 713, 717,1492, 598,2301,2301, 636,2302,2302,2302, 761, + 728, 728, 728, 730, 730, 730,1497,1497,1499,1499,1499,1499, 739, 739, 739, 739, +2303,1913,2304,2305,1913, 533,1914, 568, 678, 678, 747, 764, 739, 739, 749,1507, +2306,1916,2306,2307, 754, 212, 754, 755, 756, 757, 758, 759, 759,1512,1513,1919, + 762, 739, 739, 739, 762, 762, 764, 764,2308,2308,2308,2308, 666, 666, 769, 769, + 769,2309,2310, 664, 772, 772, 773,1929, 654, 654, 777, 778, 779,2311,2312,1522, +2313,2314,1711, 786, 788, 788, 788,2315, 786,2307, 790,2316,2316,2316, 666, 666, + 792, 834, 680, 796, 796, 680, 680, 797,2317,1531, 800,1533, 280,1913, 678,2318, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,2319,2319,1537, 811, 811, 811,1539,2320,1539,1721,1542,1721,1542, +1537,2321,1537,1721,1721, 295,1721, 295,1542,2321, 295, 295,1537,1539,1537,1537, +1542,1537,1539, 811,2322,1721,1721,1721,1721,1721, 295,1721, 295, 295, 295, 295, +1721,1721,1721, 295, 295,1721, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1727,1728,1729,2323,1731,2324,1733,1734,1735,1736,1737,2325,1739,1740,2326,1742, +2327,1744,1745, 194,1746,1747,1748,2328,1750,1751,1752,1753,1754,1755,1756,2329, +1758,1759,1760,2330,1762,1763,1764,1765,2331,2332,2333,2334,2335,1771,1772,1773, +1774,1775,1776,1777,2336,2337,2338,1781,2339,2340,2341,2342, 228,1786,1787,1788, +1789,1790,1791,1792,1793,1794,1795,1796,2343,2344,1799,1800,1801,2345,2346, 245, +1803,1804,2347,2348,1807,2349,1809,1810,1811,1812,1813,2350,1815,1816,2351,1818, +2352,2353,2354,1822,2355,1824,2356,1826,1827,1828,1829,1830,1831,1832,1833,1834, +1835,1836,1837, 274,1838,1839,1840,2357, 278,1842,2358,2359,2360,2361,1847,2362, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,1849,1849,1850,1851,1852,1853,1854,2363,1856,1857,1858,2364,1860, +1861,2365,1863,2366,2367,2366,2368,2369,1868,2370,1870,2366,1871,1872,1873,1874, +1875,1876,1877,1878,1879,2371,2372,2373,2374,2375,2376,1886,1887,1888,1889,1890, +1891,1892,2377,2378,2379,1896,1897,1898,1899,1900,1901,2380, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 33, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,2381, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, +2382, 497,2383,2384,2385,2386,2387,2388,2389,2390,2391,2392,2393,2394,2395,2396, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 127, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, + 171, 172, 173, 174, 175, 176, 177, 178, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +2397,2398, 371,1388, 372, 373,2399, 631,1389,2400, 186,2401,2402, 379,1391,2403, +1906, 598,2404, 637,1397,1398,1929,1400, 531,2405, 26, 641, 28, 370, 30,2406, + 643,2407, 570, 644, 567, 34, 38, 33,1510, 645, 646, 42, 43,2408, 648, 649, +2409,2410, 652,2411,1408,1409,1410,2412,1518,1520, 656, 58,2413, 574,2414,2415, +1415,1416,1417,1418,2416, 68,1517,1515, 71,2417,1528, 74,1420,2418, 668,1423, + 79, 537, 248,1424, 533,1425,1426, 86, 530,1428, 670,1430,1431,1432, 256, 257, + 258, 671, 97,2419,1435,1436, 262, 263, 536,2420, 674,1437, 107, 108, 109, 110, + 678, 112, 113, 114, 115, 116, 680, 660, 119, 120, 279, 280, 281,2421, 125, 659, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,2422, +2423,2424,2425, 127, 548, 549, 550, 551, 552, 132, 362, 134, 135, 136, 361, 138, + 139, 547, 141, 142, 143, 144,2426,1438, 291,2427,1439, 150, 367, 368, 292, 365, + 366, 156, 364, 158, 689,2428, 615, 162, 163,1440, 363,2429, 167, 168, 169,1442, + 171, 172, 173, 174, 175, 176, 177, 178, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1551,1552,1553,1554, 181, 182, 183,1555, 185,1556,1557,1558,1559, 188, 189,1560, +1561,1562,1563,1564,1565, 195, 196, 197, 198, 199, 200, 201, 202,1566,1567, 205, + 206, 207, 208,1568,1569,1570,1571, 211,1572,1573, 214, 215, 216, 217, 218, 219, + 220, 221,1574,1575,1576,1577,1578, 225,1579,1580, 226,1581,1582, 229, 230, 231, + 232, 233, 234, 235,1583, 237, 238, 239,1584,1585, 240,1586, 242, 243, 244,1587, + 246, 247,1588,1589, 249, 250, 251,1590, 252, 253, 254, 255,1591,1592,1593,1594, +1595, 259,1596,1597,1598,1599,1600,1601, 264, 265, 266, 267, 268, 269, 270, 271, +1602, 272, 273,1603,1604,1605, 276,1606,1607, 295, 295,1610,1611,1612, 295, 284, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,2430,2430,2431,2432,2433,2434,2435,2436,2437,2436,2438,2436,2439, +2440,2441,2442,2443,2444,2445,2446,2447,2448,2449,2450,2451,2452,2453,2454,2455, +2456,2457,2458,2459,2460,2461,2462,2463,2464,2465,2466,2467,2468,2469,2470,2471, +2472,2473,2474,2475,2476,2468,2477,2478, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1492, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295,2479,2479,2480, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295,2481, 213, 295, 295, 295, 295,2482,2483, +2484,2485, 295, 295, 295, 295, 295, 295,2486, 777, 295,2487,2488, 576, 295, 295, + 295, 295, 295, 295,2489,2490,2491, 664,2308,2308,2492, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,2493, + 295, 295, 295,2494,2495, 295, 295, 295,2496,2497, 295,2498, 295,2499, 295,2500, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1551,1552,1553,1554, 181, 182, 183,1555, 185,1556,1557,1558,1559, 188, 189,1560, +1561,1562,1563,1564,1565, 195, 196, 197, 198, 199, 200, 201, 202,1566,1567, 205, + 206, 207, 208,1568,1569,1570,1571, 211,1572,1573, 214, 215, 216, 217, 218, 219, + 220, 221,1574,1575,1576,1577,1578, 225,1579,1580, 226,1581,1582, 229, 230, 231, + 232, 233, 234, 235,1583, 237, 238, 239,1584,1585, 240,1586, 242, 243, 244,1587, + 246, 247,1588,1589, 249, 250, 251,1590, 252, 253, 254, 255,1591,1592,1593,1594, +1595, 259,1596,1597,1598,1599,1600,1601, 264, 265, 266, 267, 268, 269, 270, 271, +1602, 272, 273,1603,1604,1605, 276,1606,1607, 295, 295,1610,1611,1612, 295, 284, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 127, 127,2286,1614,1615,1616,2288,1618,1619,1620,1621,1622,1623, +1624,1625,1626,2289,2290,1629,1630,1631,2291,1633,2292,2293,2294,2295,1638,1639, +1640,1641,1642,1643,1644,1645,1646,1647,1648,2296,2297,1651,1652,1653,1654,1655, +1656,1657,2298,2299,2300,1661,1662,1663, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 622, 179, 371,1388, 372, 373, 374, 375,1389, 377, 186,1390, 187, 379,1391,1392, +1393,1394,1395,1396,1397,1398,1399,1400,1401, 25, 26, 381, 28, 370, 30,1402, +1403, 33, 34, 35, 36, 382, 38, 33, 39, 40,1404, 42, 43, 44, 45, 46, + 222,1405,1406,1407,1408,1409,1410,1411, 55, 56,1412, 58, 59,1413,1414, 62, +1415,1416,1417,1418, 67, 68, 69, 70, 71, 72,1419, 74,1420,1421,1422,1423, + 79, 80, 81,1424, 83,1425,1426, 86,1427,1428,1429,1430,1431,1432, 256, 257, +1433, 96, 97,1434,1435,1436, 262, 263, 103, 104, 105,1437, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 127, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145,1438, 147, 148,1439, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163,1440,1441, 166, 167, 168, 169,1442, + 171, 172, 173, 174, 175, 176, 177, 178, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +2501,2502,2503,2504,2505,2506,2507, 730,2508,2509,2510,2511,2512,2513,2514,2515, +2516,2517,2518,2519,2520,2521,2522,2523, 639,2524,2525,1512,2526,2527,2527,2527, +2528,2529,2530,2531,2532,2532,2533,2534, 212,2535,2536, 295,2527,2537,2538,2483, +2539,2485,2527,2527,2527,2527,2527,2527,2540,2541,2542,2487,2543, 576, 575,1522, +2544,2527,2527,2527, 224,2545,2546,2547,2548,2308,2549,2527, 295,2550, 295, 295, + 295, 295, 295, 295,2551, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,2552,2553, 295, 295, 295,2554, + 295, 295, 295, 677,2483, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,2493, + 295,2555, 295,2556,2495,2557,2558,2559,2496,2497,2560,2498,2561,2499,2562,2500, +2563,2564,2565,2527, 295, 295,2566, 295, 295, 295, 295, 295,2570,2571, 295,2572, +2573, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1492, 713, 714,2574,2575,1666,2576,1492, 719,2577, 721,2578, 723, 724, 715, 725, + 726, 727, 728, 729, 730, 731, 732, 733, 734,1498, 736, 737,1670, 739, 739, 739, + 740,1672,2304,2305,1672, 533, 745, 568, 678, 746,1680, 748, 739, 739, 749, 750, +1686, 752,1686, 753, 754, 212, 754, 755, 756, 757, 758, 759,2579, 760, 204, 761, + 762, 739, 739, 739, 762, 762, 764, 765, 766, 766, 766, 766, 767, 768,2580,2580, +2580, 770, 771, 664, 772, 772, 773,1706,1690, 654, 777, 778, 779, 780, 781, 782, +2581, 784,2582,1712, 787, 788, 788, 789,1712, 753, 790, 780, 780, 780, 791, 791, + 792, 793, 680,2583, 796, 680, 680, 797, 798, 799, 800,1533, 802, 803,1534,1535, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,1716,1536,1718,1538, 811,1537,1539, 814,1539,1721,1542, 818,1721, +1543,1544,1537, 295,1545,1546, 295, 295,1542, 295, 295, 295,2584,1539,1537,1722, +1542,1537,1539,1549,1550,1721,1721,1721,1724,1725, 295,1726, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 844, 845, 846, 847, 848, 849, 850, 851, 852,2585, 854, 855, 856, 857, 858, 859, + 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, + 876, 877, 878, 879, 880,2586, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, + 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, + 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, + 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, + 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, + 956,2587, 958, 959, 960, 961, 962,2588,2589,2590,2591,2592,2593,2594,2595,2596, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,2597,2598,2598,2599, +2600,2601,2602,2603, 127,2604,2605,2606, 979, 132,2607, 134,2608, 136,2609, 138, + 139,2610, 141,2611,2612,2613,2614,2612,2615,2612,2616,2617,2618,2619,2620,2621, +2622,2623,2624,2625,2626, 160,2627,2628,2629,2630,2631,2632,2633,2634,2635,2636, +2637,2638, 173,2639,2640,2641,2642,1015, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +2397,1492,2643,1904, 630, 713,2644,2645,2646, 295, 295, 295, 295, 295,2647, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,2648,2649,2650, 295, 295, 295, +2651,2651, 739,2652,2653,2654,2655,2656,2657,2535,2658,2659,2660,2661, 757,2662, +2663, 651,2664,2665,2666,2667,2668,2669,2670,2671, 656,2672, 779, 575,1522, 576, + 295,2673,2674,2675, 747, 224,2546, 664,2316,2308, 241,2676,2677, 295, 666, 295, + 79, 295, 295,2678,2679, 295, 295, 295,2680, 295, 295, 295, 91,2681,2682, 94, + 295, 295, 295, 295, 295,2683, 295, 295, 295, 295, 295, 295, 295, 295,2684, 295, + 295, 295, 295, 295,1982, 295, 295, 295, 295, 295,2685, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295,2686,2687,2688,2689,2690,2691, 606,2692, 607, +2693, 608,2694, 609,2695, 610,2696, 612,2697,2698,2699, 289,2700, 295,2701, 295, + 295,2702, 295, 295,2703,2702,2704,2705,2706,2707,2708,2709,2710,2711,2712,2713, +2714,2715,2716,2717,2718,2719,2720, 295,2721,2722, 295,2723,2724,2725,2726,2727, +2698,2728,2729,2730,2721,2731,2732,2733,2734,2735, 295,2736, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295,2737,2738,2739, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1551,1552,2740,2741, 181, 182, 183,2742,2743,2744, 186,1558,2745,2746,2747, 190, + 191, 192,2748,1564,2749,2750, 196, 197, 198, 199, 200,2751, 202,2752,2753,2754, +2755,2756,2757,2758,2759,2760, 210,2761,1572,2762,2763, 215,2764,2765, 218,2766, +2767,2768,1574, 50,2769,2770,1578,2771,2772,2773,2774,2775,2776,2777, 59,2778, +2779,2780,2781, 235, 236,2782,2783,2784,2785,2786, 240,2787, 242,2788, 244,1587, +2789,2790,2791,1589, 249,2792,2793,2794, 252,2795,2796, 255,2797,2798,2799,1594, +2800,2801,1596,2802,1598,2803,2804,1601, 264, 265,2805,2806, 268, 269, 270, 271, +1602, 272, 273,2807,2808,2809, 276,2810,2811,2812,2813,2814,2815,2816,2817,2818, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,2819,2820,2821,2822,2823,2824,2825,2826,2827,2828,2829,2830,2831, +2832,2833,2834,2835,2836,2837,2838,2839, 147,2840,2841,2842, 151, 152, 153, 154, + 155, 156, 157,2843,2844,2845,2846, 162, 163,2847,2848,2849, 167, 168,2850,1442, +2851,2852,2853, 174,2854, 176,2855,2856, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 203,1566,2857,2858, 181, 182, 183,2859,2860,2861,1557,1558,1559, 188, 189,1560, +1561,1562,2862,1564,1565, 195, 196, 197,2861, 199, 200,2863,2864, 203,2865,2866, +2860,2867,2868,1568,1569,2869,2870, 207,1572,1573, 214, 215, 216, 217, 218,2871, + 220, 221,2872,1575,1576,1577,1578, 225,1579,1580, 226,1581,1582, 229, 230, 231, + 232, 233, 234, 235,1583, 237, 238, 239,1584,1585, 240,2873, 242, 243, 244,2874, + 246, 247,1588,1589, 249,2875,2876,2877,2878, 253, 254, 255,1591,1592,1593,2879, +1595, 259,1596,2880,1598,1599,1600,1601, 264, 265, 266,2881, 268, 108, 270,2859, +2882,2883, 273,1603,1604,1605, 276,2884,2885,2886, 295,1610,2887,1612,2888, 284, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,2889,2889, 128, 129, 130,2890, 132,2891, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145,1438, 147, 148,1439, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163,1440,1441, 166, 167, 168, 169,1442, + 171, 172, 173, 174, 175, 176, 177, 178, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1727,1728,1729,2323,1731,2324,1733,1734,1735,1736,1737,2325,1739,1740,2326,1742, +2327,1744,1745, 194,1746,1747,1748,2328,1750,1751,1752,1753,1754,1755,1756,2329, +1758,1759,1760,2330,1762,1763,1764,1765,2892,2332,2333,2334,2335,1771,1772,1773, +1774,1775,1776,1777,2336,2337,2338,1781,2339,2340,2341,2342, 228,1786,1787,1788, +1789,1790,1791,1792,1793,1794,1795,1796,2343,2344,1799,1800,1801,2345,2892, 245, +1803,1804,2347,2348,1807,2349,1809,1810,1811,1812,1813,2350,1815,1816,2351,1818, +2352,2353,2354,1822,2355,1824,2356,1826,1827,1828,1829,1830,1831,1832,1833,1834, +1835,1836,1837, 274,1838,1839,1840,2357, 278,1842,2358,2359,2360,2361,1847,2362, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,1849,1849,1850,1851,1852,1853,1854,2363,1856,1857,1858,2364,1860, +1861,2365,1863,2366,2367,2366,2368,2369,1868,2370,1870,2366,1871,1872,1873,1874, +1875,1876,1877,1878,1879,2371,2372,2373,2374,2375,2376,1886,1887,1888,1889,1890, +1891,1892,2377,2378,2379,1896,1897,1898,1899,1900,1901,2380, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +2893,2894,2895,2896,2897,2898,2899,2900,2901,2902,2903,2904,2905,2906,2907,2908, +2909,2910,2911,2912,2913,2914,2915,2916,2917,2918,2919,2920,2921,2922,2923,2924, +2925,2926,2927,2928, 209,2929,2930,2931,2932,2933,2934,2935,2936,2937,2938,2939, +2940,2941,2942,2940,2943,2944,2945,2946,2947,2948,2949,2950,2951,2952,2953, 437, +2954,2955,2956,2957,2958,2959,2960,2961,2962,2306,2963,2964,2965,2550,2966,2967, +2968,2969,2554,2970,2489,2971,2972,2973, 657, 575,2974,2975,2976, 779,2977,2978, +2979,2980,2981,2982,2983,2984,2985,2986,2987,2988,2553,2989,2990,2991,2992,2993, +2994,2995, 680, 796,2996, 797,2662,2997,2998,2999,2514,3000,3001,1913,3002,3003, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,1219,1219, 308, 394, 395, 396, 397, 398, 399, 398, 400, 401, 402, + 403, 404, 405, 406, 404, 406, 407, 404, 408, 404, 330, 409, 410, 411, 412, 413, + 414, 415, 416,1220,1221, 419, 420, 341, 342, 421, 422, 423, 424, 425, 426, 427, + 428, 429, 419, 430, 351, 431, 308, 432, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +2501,2502,2503,2504,2505,2506,2507, 730,2508,2509,2510,2511,2512,2513,2514,2515, +2516,2517,2518,2519,2520,2521,2522,2523, 639,2524,2525,1512,2526,2527,2527,2527, +2528,2529,2530,2531,2532,2954,2533,2534, 212, 213,2536, 295,2527,2537,2482,2483, +3005,2485,2527,2527,2527,2527,2527,2527,3006,2541,3007,2487,3008, 576, 575,1522, +2544,2527,2527,2527, 224,2545,2546,2547,2548,2308,2549, 241, 295,2550, 295, 295, +2304,3009, 295, 295,2551, 295, 295,3010, 295, 295,3010, 295, 295, 295, 295, 295, + 295, 295, 295,2898, 295, 295, 295, 295, 295, 295,2552,2553, 295, 295, 295,2554, + 295, 295, 295, 677,2483, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,2493, + 295,2555, 295,2556,2495,2557,2558,2559,2496,2497,2560,2498,2561,2499,2562,2500, +2563,3011,2565,2527, 295, 295,2566, 295, 295, 295, 295, 295,3012,3013,3014,3015, +3016,3015,3016, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1492, 713,1904, 713,1905, 713, 717,1492, 598,1493,1493,1906,1907,1907,1907, 761, + 728, 728, 728, 730, 730, 730,1497,1497,1499,1499,1499,1499, 739, 739, 739, 739, +1912,1913,1501,1502,1913, 533,1914,1503, 678, 678,1504, 764, 739, 739,1506,1507, +3017,1916,3017,2307,1509,1510,1509,1511, 756, 757, 758, 759, 759,1512,1513,1919, + 762, 739, 739, 739, 762, 762, 764, 764,2417,2417,2417,2417,1528,1528, 769, 769, + 769,3018,2310,1515,1516,1516,1517,1929,1518,1518,1519,1520, 779,2311,2312,1522, +1935,3019,1523, 786,1525,1525,1525,2315, 786,2307,1527,3020,3020,3020,1528,1528, + 792, 834, 680, 796, 796, 680, 680,1529,1530,1531,1532,1533, 280,1913, 678,3021, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,2319,2319,1537, 811, 811, 811,1539,3022,1539,1541,1542,1541,1542, +1537,3023,1537,1541, 295, 295,1541, 295,1542, 295, 295, 295,1537,1539,1537,1537, +1542,1537,1539, 811,2322,1541,1541,1541,1541,1541, 295,1541, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +3024,3024,3024,3025,3026,3027,3028,3029,3030,3031,3032,1906,3033,3034,3035,3036, +2006,2006,2006,2007,2007,2007,2008,2009,3037,3038,1499,3039, 739, 739, 739, 739, +3040,2017,2018,3041,2020, 533,2022,1503,2024,3042,1504,2027, 739, 739,1506,2031, +3043,2033,3044,2035,1509,3045,1509,3046,3047, 45, 45,3048,3049,3050,3051,3052, +2046, 739, 739, 739, 762, 762,2051,2051,2052,2053,2053,2054,2055,1528,3053,3054, +3055,3056,2061,2062,2063,2064,2065,1929,2067,2067,3057,3057,2071,2071,3058,1522, +1935,2075,2075, 786,1525,1525,1525,2315, 786,2307,1527,2083,3020,3020,1528,1528, +3059,3060,3061,3062,3063, 318, 680,3063, 318,3064,3065,1533,3066,3067,3068,3021, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 293,3069,3070,3071,3072,3073,3074,3075,3076,2112,3077,2114,3078, +3079, 547,3080,3081, 295, 295,2117, 295, 295, 295, 295, 295,2119,3082,3083,3084, +3085, 295, 295, 295, 295,3086,3087, 295, 295, 295, 295,2129, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1991,1992,1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,1398, +2006,2006,2006,2007,2007,2007,2008,2009,3088,2011,2012,2013, 34,2014, 33,2015, +2016,2017,2018,2019,2020,2021,2022,2023,2024,2025,2026,2027,2028,2029,2030,2031, +2032,2033,2034,2035,2036,2037,2038,2039,2040, 45, 45,2041,2042,2043,2044,2045, +2046, 739,2047,2048,2049,2050,2051,2051,2052,2053,2053,2054,2055,2056,2057,2058, +2059,2060,2061,2062,2063,2064,2065,2066,3089,2068,2069,2070,3090,3091,2072,2073, +2074,2075,2076,2077,2078,2079,2080, 275,2081,2082,1527,2083,2084,2085,2086,2087, +2088,2089,2090,2091,2092,2093,2094,2095,2096,2097,2098,2099, 122,2100,2101,2102, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,2104,2105,2106,2107,2108,2109,2110,2111,2110,2112,2113,2114,2113, +2115, 547,2115,2116, 295, 295,2117, 295,2118, 295, 295, 295,2119,2120,2121,2122, +2123,2124,2125,2126,2127, 160, 161, 162, 163,2128, 295,2129, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +2501,2502,2503,2504,2505,2506,2507, 730,2508,2509,2510,2511,2512,2513,2514,2515, +2516,2517,2518,2519,2520,2521,2522,2523, 639,2524,2525,1512,2526,2527,2527,2527, +2528,2529,2530,2531,2532,2532,2533,2534, 212, 213,2536, 295,2527,2537,2482,2483, +3005,2485,2527,2527,2527,2527,2527,2527,3006,2541,3007,2487,3008, 576, 575,1522, +2544,2527,2527,2527, 224,2545,2546,2547,2548,2308,2549,2527, 295,2550, 295, 295, + 295, 295, 295, 295,2551, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,2552,2553, 295, 295, 295,2554, + 295, 295, 295, 677,2483, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,2493, + 295,2555, 295,3092,2495,2557,2558,2559,2496,2497,2562,2498,2561,2499,2562,2500, +2563,3011,2565,2527, 295, 295,2566, 295, 295, 295, 295, 295,2570,2571, 295,2572, +3093, 295, 295, 295, 295, 295,3094, 295, 295,3095,3096, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295,3097, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +3098,3205,3101,3129,3130,3131,3108,3132,3209,3210,3120,3133,3124,3134,3103,3135, +3136,3137,3113,3138,3139,3140,3141,3142,3143,3144,3145,3206,3121,3207,3208,3146, +3127,3147,3148,3211,3212,3149,3150,3151,3116,3106,3152,3110,3111,3213,3104,3126, +3105,3117,3153,3154,3102,3214,3155,3119,3215,3216,3115,3109,3217,3114,3112,3218, +3156,3219,3157,3158,3159,3107,3122,3128,3160,3222,3161,3162,3163,3164,3165,3166, +3167,3168,3169,3170,3171,3172,3173,3174,3223,3175,3176,3177,3118,3221,3220,3178, +3226,3241,3179,3180,3242,3181,3182,3183,3184,3185,3186,3187,3125,3188,3189,3190, +3191,3192,3193,3194,3123,3195,3196,3197,3198,3224,3199,3200,3201,3202,3203,3204, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 127,3227,3244,3248,3268,3245,3243,3246,3227,3247,3227,3249,3227, +3227,3269,3227,3228,3270,3229,3250,3251,3100,3271,3258,3230,3252,3253,3237,3239, +3259,3254,3255,3256,3260,3235,3261,3257,3099,3265,3266,3232,3233,3233,3267,3262, +3263,3264,3236,3225,3231,3234,3238,3240, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +3098,3205,3101,3129,3130,3131,3108,3132,3209,3210,3120,3133,3124,3134,3103,3135, +3136,3137,3113,3138,3139,3140,3141,3142,3143,3144,3145,3206,3121,3207,3208,3146, +3127,3147,3148,3211,3212,3149,3150,3151,3116,3106,3152,3110,3111,3213,3104,3126, +3105,3117,3153,3154,3102,3214,3155,3119,3215,3216,3115,3109,3217,3114,3112,3218, +3156,3219,3157,3158,3159,3107,3122,3128,3160,3222,3161,3162,3163,3164,3165,3166, +3167,3168,3169,3170,3171,3172,3173,3174,3223,3175,3176,3177,3118,3221,3220,3178, +3226,3241,3179,3180,3242,3181,3182,3183,3184,3185,3186,3187,3125,3188,3189,3190, +3191,3192,3193,3194,3123,3195,3196,3197,3198,3224,3199,3200,3201,3202,3203,3204, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,3274,3274,3277,3275,3276,3275,3272,3272,3272,3272,3272,3272,3272, +3272,3272,3272,3272,3272,3272,3278,3279,3301,3269,3280,3269,3281,3282,3283,3284, +3285,3286,3287,3288,3289, 349,3290,3291,3292,3293,3294,3276,3276,3276,3295,3296, +3297,3298, 349,3299,3300,3276,3283,3284,3273, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +3302,3303,3304,3305,3306,3307,3308,3309,3310,3311,3312,3313,3314,3315,3316,3317, +3318,3319,3320,3321,3322,3323,3324,3324,3325,3326,3327,3328,3329,3330,3331,3332, +3333,3334,3335,3336,3337,3338,3339,3340,3341,3342,3343,3344,3345,3346,3347,3348, +3349,3350,3351,3352,3353,3354,3355,3356,3357,3358,3359,3324,3360,3361,3362,3363, +3324,3364,3365,3366,3324,3367,3324,3368,3324,3369,3324,3370,3324,3371,3372,3373, +3374,3375,3376,3324,3377,3378,3324,3379,3380,3381,3382,3383,3324,3324,3384,3324, +3385,3324,3386,3324,3387,3388,3324,3389,3324,3324,3390,3324,3324,3324,3324,3324, +3324,3324,3324,3391,3324,3324,3324,3324,3324,3324,3392,3393,3324,3324,3324,3324, +3394,3395, 285,3396,3397, 287, 288,3398,3399,3399,3399,3399,3399,3399,3399,3399, +3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399, +3399,3399,3399,3400,3401,3399,3402,3399,3403,3404,3405,3406,3407,3408,3409,3410, +3410,3411,3408,3412,3399,3399,3413,3414,3399,3399,3399,3399,3399,3399,3399,3399, +3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399, +3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399, +3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399, +3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399, + }, + { +3415,3416, 466,3417, 713,3324,3418, 467,3419, 632,3420,3421,3422,3423,3424,3425, +3426, 598,3427,3428,3429,3430,3431,3432,1512,3433,3422,3434, 759, 437, 437,3435, +3436,3437, 439,3438, 440,3439, 442, 441,3440,3441,3442,3443,2306,3444,3445, 582, +3446,3447, 452,3448,3449,3418,3450,3451,3452, 778,3453,2312, 779,3454,3455,3456, +3457, 769,3458,3459,3460,3461, 663, 664,3462,2316,3463,3464,3465,1299,3466,3467, +3468,3469,3470,3471,3472,3473,3474, 442,3475,3476,3477,3432,3478,1669,3479,3480, +3481,3482,3483,3484,3481, 261,3485,3486,1919,2420,3487,3488, 676,3489,3490,3491, +3492,3493,3494,3495, 677,3496, 677,3497,3498, 677,3499, 455,3500,2421,3501, 465, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +3502,3502,3503,3504,3505,3506,3506,3507,3508,3509,3510,3511,3512,3513,3514,3515, +3516,3517,3518,3519,3520,3521,3522,3523,3524,3525,3526,3527,3528,3529,3530,3531, +3532,3533,3534,3535,3536,3537,3538,3539,3540,3541,3542,3543,3544,3545,3546,3547, +3548,3549,3550,3551,3552,3553,3554,3555,3553,3556,3557,3558,3559,3560,3561,3562, +3563,3564,3565,3566,3567,3568,3569,3570,3571,3572,3573,3574,3463,3575,3576,3577, +3578,3579,3580,3581,3582,3583,3584,3585,3586,3587,3588,3589,3590,3590,3591,3592, +3593,3594,3595,3596,3597,3598,3599,3600,3601,3602,3603,3604,3605,3606,3607,3608, +3609,3610,3609,3611,3612,3613,3614,3615,3616,3617,3618,3619,3620,3621,3622,3623, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 33, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 127, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148,3624, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, + 171, 172, 173, 174, 175, 176, 177, 178, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +1551,1552,1553,1554, 181, 182, 183,1555, 185,1556,3625,3626,3627, 188, 189,1560, +1561,1562,1563,3628,1565, 195, 196, 197, 198, 199, 200, 201, 202,1566,1567, 205, + 206, 207, 208,1568,1569,1570,1571,3629,1572,1573, 214, 215, 216, 217, 218, 219, + 220, 221,1574,1575,1576,1577,1578, 225,1579,1580, 226,1581,1582, 229, 230, 231, + 232, 233, 234, 235,1583, 237, 238, 239,1584,1585, 240,1586, 242, 243, 244,1587, + 246, 247,1588,1589, 249, 250, 251,1590, 252, 253, 254, 255,1591,1592,1593,1594, +1595, 259,1596,1597,1598,1599,1600,1601, 264, 265, 266, 267, 268, 269, 270, 271, +1602, 272, 273,1603,1604,1605, 276,1606,1607, 295, 295,1610,3630,3631, 295, 284, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 127,3632,1613,1614,3633,1616,3634,1618,3635,1620,3636,1622,3637, +3638,1625,3639,3640,1628,1629,1630,1631,3641,1633,1634,3642,1636,1637,1638,1639, +1640,1641,1642,1643,1644,1645,1646,1647,1648,1649,1650,1651,1652,1653,1654,1655, +1656,1657,1658,1659,1660,1661,1662,1663, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +3643,3644,3645,3646,3647,3648,3649,3650,3651,3652,3653,3654,3655,3656,3657,3658, +3659,3660,3661,3662,3663,3664,3665,3666,3667,3668,3669,3670,3671,3672,3673,3674, +3675,3676,3677,3678,3679,3680,3681,3682,3683,3684,3685,3686,3687,3688,3689,3690, +3691,3692,3693,3694,3695,3696,3697,3698,3699,3700,3701,3702,3703,3704,3705,3706, +3707,3708,3709,3710,3711,3712,3713,3714,3715,3716,3717,3718,3719,3720,3721,3722, +3723,3724,3725,3726,3727,3728,3729,3730,3731,3732,3733,3734,3735,3736,3737,3738, +3739,3740,3741,3742,3743,3744,3745,3746,3747,3748,3749,3750,3751,3752,3753,3754, +3755,3756,3757,3758,3759,3760,3761,3762,3763,3764,3765,3766,3767,3768,3769,3770, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,3772,3773,3774,3775,3776,3777,3778,3779,3780,3781,3782,3783,3784, +3785,3786,3787,3788,3786,3789,3790,3786,3791,3786,3792,3793,3794,3795,3796,3797, +3798,3799,3800,3801,3802,3803,3804,3805,3806,3807,3808,3809,3810,3811,3812,3813, +3814,3815, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +3415,1492,3816,3417, 713,3817,3818, 730,1914,3819,1533,3421,3820,3821,3822,3823, +3426,3824,3825,3826,3827,3828,3829,3830,3831,3832,3833,3834, 759, 437,3835,3836, +3436,3837,3838,3839,3840,3841, 442,3842,3843,3844,3845,3846,3847,3848,3849,3850, +3851,2306,3852,3853,3854,3855,3856,3857,3858,3859,3860,3861,3862,3863,3864,3865, +3866,3867,3868,3869,3870,3871,3872,3873,3874,3875,3876,3877,3878,3879,3880,3881, +3882,3883,3884,3885,3886,3887,3888,3889,3890,3891,3892,3893,3894,3895,3896,3897, +3898,3899,3900,3901,3902,3903,3904,3905,3906,3907,3908,3909,3910,3911,3912,3913, +3914,3915,3916,3917,3918,3919,3920,3921,3922,3923,3499,3924,3925,2421,3926,3927, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,3772,3773,3774,3775,3776,3777,3778,3779,3780,3781,3782,3783,3784, +3785,3786,3787,3788,3786,3789,3790,3786,3791,3786,3792,3793,3794,3795,3796,3797, +3798,3799,3800,3801,3802,3803,3804,3805,3806,3807,3808,3809,3810,3811,3812,3813, +3814,3815, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +3928,3929,3930,3931,3932,3933,3934,3935,3936,3937,3938,3939,3940,3941,3942,3943, +3944,3945,3946,3947,3948,3949,3950,3951,3952,3953,3954,3955,3956,3957,3958,3959, +3960,3961,3962,3963,3964,3965,3966,3967,3968,3969,3970,3971,3972,3973,3974,3975, +3968,3976,3977,3978,3979,3980,3981,3982,3983,3984,3985,3986,3987,3988,3989,3990, +3991,3992,3992,3993,3994,3995,3996,3997,3998,3999,4000,4001,4002,4003,4004,4005, +4006,4007,4008,4009,4010,4011,4012,4013,4014,4015,4016,4017,4018,4019,4020,4021, +4022,4023,4024,4025,4026,4027,4028,4029,4030,4031,4032,4033,4034,4035,4036,4037, +4038,4039,4040,4041,4042,4043,4044,4045,4046,4047,4048,4049,4050,4051,4052,4053, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,4055,4056,4057,4058,4059,4060,4061,4062,4063,4064,4065,4066,4067, +4068,4069,4070,3788,4071,4072,3790,4073,3791,4074,4075,3793,4076,4077,4078,4079, +4080,4081,4082,4083,4084,4064,3804,4085,4086,3807,4087,4088,4089,4090,3812,3813, +4091,4092,4093, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +3928,3929,3930,3931,3932,3933,3934,3935,3936,3937,3938,3939,3940,3941,3942,3943, +3944,3945,3946,3947,3948,3949,3950,3951,3952,3953,3954,3955,3956,3958,4094,3959, +3960,3961,3962,3963,3964,3965,3966,3967,3968,3969,3970,3971,4095,3973,3974,3975, +3968,3976,3977,3978,3979,3980,3981,3982,3983,3984,3985,3986,3987,3988,4096,3990, +3991,3992,3992,3993,3994,3995,3996,3997,3998,3999,4000,4001,4002,4003,4004,4005, +4097,4007,4098,4009,4010,4011,4012,4013,4014,4015,4016,4017,4018,4019,4020,4021, +4022,4023,4024,4025,4026,4027,4028,4029,4030,4031,4099,4100,4101,4035,4036,4037, +4038,4039,4040,4041,4102,4043,4044,4103,4046,4047,4048,4049,4050,4051,4052,4053, +4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104, +4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4105,4105,4105,4105,4106, +4105,4105,4105,4055,4056,4107,4058,4059,4060,4108,4062,4063,4064,4065,4066,4067, +4068,4069,4070,3788,4071,4072,3790,4073,3791,4074,4075,3793,4076,4077,4078,4079, +4080,4081,4082,4083,4084,4064,3804,4085,4086,4109,4110,4088,4111,4112,3812,3813, +4091,4092,4093,4105,4105,4105,4105,4105,4105,4104,4113,4113,4113,4113,4113,4113, +4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113, +4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113, + }, + { +1222,1223,1224,1225,1226,1227,1228,1229,1230,1231,1232,1233,1234,1235,1236,1237, +1238,1239,1240,1241,1242,1243,1244,1245,4114,4115,4116,1249,1250,1376,1377,1253, +1378,1255,1256,4117,1380,1259,1260,1261,1262,1263,1264,1265,1266,1267,1268,4118, +1270,4119,4120,4121,4122,1275,1276,1277,1278,1279,1381,1281,1282,1283,1284,1285, +1286,1287,1288,1289,1290,1291,1292,1293,1294,1295,1296,1297,1298,1299,1300,1301, +1302,1303,1304,1305,4123,1307,1308,1309,1309,1310,1311,1312,1313,1314,1315,1316, +1317,1318,1319,1320,1321,1322,1323,1324,1325,1326,1327,1328,1329,1330,1331,1332, +1333,1334,1335,1336,1337,4124,1339,1340,1341,1342,1343,1344,1345,1346,1347,1348, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 312,1349,1350,4125,1352,4126,1354,4127,1355, 320,1356, 321,1357, +1358,1359,1360,1361,1362,1363,1364,1383,1366,1359,1384,1361, 332, 333, 334, 335, + 336,1368,1369, 338, 339, 320,1370, 295, 295, 295, 295,1372,1373,1374,1375, 295, + 347, 348, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +4128,4129,4130,4131,4132,4133, 6, 7,4134, 9, 10,4135, 12, 13, 14, 15, +4136,4137,4138,4139, 20,4140,4141,4142,4143,4144,4145,4146,4147,4148,4149,4150, + 32,4151,4152,4153,4154,4155,4156,4157,4158,4159,4160,4161,4162,4163,4164, 46, +4165,4166,4167,4168,4169,4170,4171, 54,4172,4173,4174,4175,4176,4177,4178,4179, +4180,4181,4182, 66,4183,4184, 69,4185,4186,4187,4188, 74, 75, 76, 77, 78, +4189,4190,4191, 82,4192,4193, 85,4194,4195, 88,4196,4197,4198,4199,4200,4201, + 95, 96, 97,4202,4203,4204,4205, 102, 103,4206, 105, 106, 107,4207,4208,4209, +4210, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,4211,4212,4213, 125,4214, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,3772,3773,3774,3775,3776,3777,3778,3779,3780,3781,3782,3783,3784, +3785,3786,3787,3788,3786,3789,3790,3786,3791,3786,3792,3793,3794,3795,3796,3797, +3798,3799,3800,3801,3802,3803,3804,3805,3806,3807,3808,3809,3810,3811,3812,3813, +3814,3815, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +3415,3416, 466,3417, 713,3324,3418, 467,3419, 632,3420,3421,3422,3423,4215,3425, +3426, 598,4216,3428,3429,3430,3431,3432,4217,4218,4219,4220, 759, 437, 438,3435, +3436,3437, 439,3438, 440,4221, 442,4222,3440,3441,3442,3443,2306,4223,3445, 582, +3446,3447, 452,3448,3449,4224,3450,3451,3452, 778,3453,2312, 779,3454,3455,3456, +3457, 769,3458,3459,3460,3461, 663, 664,3462,2316,3463,3464,3465,1299,3466,3467, +3468,3469,4225,3471,3472,3473,4226, 442,3475,3476,3477,3432,3478,1669,3479,3480, +3481,3482,3483,3484,4227, 261,3485,3486,1919,2420,3487,3488, 676,3489,3490,3491, +3492,4228,3494,3495,4229,4230, 680,3497,3498, 462,3499, 455,3500,2421,3501, 465, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,3772,3773,4231,3777,3776,4232,4233,4234,4235,4236,4237,4238,4239, +4240,3786,4241,3788,3786,3789,3790,3786,3791,3786,3792,2617,3794,3795,3796,3797, +3798,3799,3800,3801,3802, 160,3804,3805,3806,3807,3808,3809,3810,3811,3812,3813, +3814,3815, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 622, 179, 371, 180, 372, 373, 374, 375, 376, 377, 186, 378, 187, 379, 380, 190, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 381, 28, 370, 30, 31, + 32, 33, 34, 35, 36, 382, 38, 33, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 383, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127,4242,4243,4244, 141,4245,4246,4247,4246,4248,4246,4249,4250, +4251,1954,4252,4253,4254,4255,4256,4257,4258,4259,4260,4261,4262,4263,4264,4265, +4266,4267,4268,4269,4270,4271, 163,4272, 625,4273,4274,4275,4276,4277,4278,4279, +4280,4281,4282,4283,4284,4285,4286, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + }, + { +4287,4288,1553,1554, 181,4289,4290,1555, 185,4291,3625,4292,1559, 188,4293,4294, +4295,1562,4296,4297,1565, 195,4298, 197,4299,4300, 200, 201, 28,1566,1567,4301, +4302,4303,4304,4305,4306,4307,4308,4309,1572,1573, 214, 215, 216, 217, 218, 219, + 220,4310,4311,1575,1576,1577,1578,4312,1579,1580,4313,1581,4314,4315,4316,4317, + 232, 233, 234, 235,4318, 237, 238,4319,1584,1585, 240,1586, 242, 243, 244,1587, + 246,4320,4321,1589, 249, 250, 251,1590, 252,4322, 254, 255,4323,4324,4325,4326, +1595,4327,1596,1597,1598,1599,1600,1601,4328,4329,4330, 267,4331,4332, 270,4333, +4334,4335, 273,1603,4336,1605, 276,4337,4338,4339,4340,1610,4341,4342,4343, 126, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,4344,1470,1613,4345,1615,4346,1475,4347,1477,4348,1479,4349,1481, +1482,4350,1484,4351,4352,4353,1630,4354,2291,4355,1634,3642,1636,1637,1638,1639, +1640,4356,4357,1643,1644,1645,1646,4358, 342,4359,4360,1651,1652,1653,4361,4362, +1656,4363,1658,1659,1660,1661,1662,1663, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { + 295, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, + 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871,2216, 873, 874, 875, + 876, 877, 878,2217, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, + 892, 893, 894,2218, 896, 897, 898, 899, 900,2219,2220, 903,2221, 905,2222,2223, +2224, 909,2225, 911, 912,2226,2227, 915, 916,2228,2229, 919, 920, 921, 922, 923, +2230, 925, 926, 927, 928, 929, 295, 931, 932, 933, 934, 935,2232,2233, 938,2234, + 940, 295, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, + 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966,4366, 968,4367, 295, 295, +4368,4369,4370,4371,4372,4373,4374, 978,4375,4376,4377,4378,4379,4380,4381,4382, +4383,4384,4385,4386,4387,4388,4389,4390,4391,4392,4393, 975, 971, 972, 973, 974, + 975, 310, 976, 977, 978, 974,2239, 980,2240, 982,2241, 984,2242, 985,2243, 987, + 988,2244, 990, 325,2244, 325, 991,2244,2245,2246, 330, 993,2247,2248,2249, 997, + 998,2250,2251,1001,1002,1003, 340,1004,1005,1006,1007,1008,1009, 310,1010,1011, +2252, 429,1003, 430,1013,1014, 974,1015, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +3415,4395,3324,3324,4396,3324,3324,3324,3324,3324,3324, 378,3324,3324,3822,3324, +3324,4397,3324,4398,4399,4400,3324,3324,3324, 370,3324,4401,4402,4403,4404,4405, +3324,4406,4407,4408,3438,3438,4409, 442,4410,3324,3324,4411,4412,4411, 676,4413, +4414,3324,3324,4415, 51,3324,3324,3324,4416,3452,4417,3324, 59,4418,4416, 62, +3324,4419,4419,4420, 67,3324, 664,4421,3324,2316,3324,4422, 70,3324,4423, 78, +4424,4425, 81,3324,3324,3324,3324,3324,3324,3324,3324, 90,3324,4426, 93,4427, +3324,3324,3324,3324,3324,3324,3324,4428,3324,3324,3324,3324,3324,3324,3324,3324, +3324,3324,3324,3324,3324,4229,3324,4429,3324,3324,3324,3324,3324,3324,3324,3324, +3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324, +3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324, +3324,3324,3324,4430,4430,4431,4432,4433,4434,4435,4436,4437,4438,4439,4440,4441, +4442,4443,4444,4445,4446,4447,4448,4449,4450,4451,4452,4453,3324,3324,4454, 154, +4455,4456,4457,3324,3324, 160,3324,3324,3324,4458,4459,4460,4461,4462,4463,3324, +3324,4464, 160,4465,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324, +3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324, +3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324, + }, + { +3928,3929,3930,3931,3932,3933,3934,3935,3936,3937,3938,3939,3940,3941,3942,3943, +3944,3945,3946,3947,3948,3949,3950,3951,3952,3953,3954,3955,3956,3957,3958,3959, +3960,3961,3962,3963,3964,3965,3966,3967,3968,3969,3970,3971,3972,3973,3974,3975, +3968,3976,3977,3978,3979,3980,3981,3982,3983,3984,3985,3986,3987,3988,3989,3990, +3991,3992,3992,3993,3994,3995,3996,3997,3998,3999,4000,4001,4002,4003,4004,4005, +4006,4007,4008,4009,4010,4011,4012,4013,4014,4015,4016,4017,4018,4019,4020,4021, +4022,4023,4024,4025,4026,4027,4028,4029,4030,4031,4032,4033,4034,4035,4036,4037, +4038,4039,4040,4041,4042,4043,4044,4045,4046,4047,4466,4049,4050,4051,4052,4053, +4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104, +4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4105,4105,4105,4105,4105, +4105,4105,4105,4055,4056,4057,4058,4059,4060,4061,4062,4063,4064,4065,4066,4067, +4068,4069,4070,3788,4071,4072,3790,4073,3791,4074,4075,3793,4076,4077,4078,4079, +4080,4081,4082,4083,4084,4064,3804,4085,4086,3807,4087,4088,4089,4090,3812,3813, +4091,4092,4093,4105,4105,4105,4105,4105,4105,4104,4113,4113,4113,4113,4113,4113, +4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113, +4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113, + }, + { +4467,4468,4469,4470,4471,4472,4473,4474,4475,4476,4477,4478,4479,4480,4481,4482, +4483,4484,4485,4486,4487,4488,4489,4490,4491,4492,4493,4494,4495,4496,4497,4498, +4499,4500,4501,4502,4503,4504,4505,4506,4507,4508,4509,4510,4511,4512,4513,4514, +4515,4516,4517,4518,4519,4520,4521,4522,4523,4524,4525,4526,4527,4528,4529,4530, +4531,4532,4533,4534,4535,4536,4537,4538,4539,4540,4541,4542,4543,4544,4545,4546, +4547,4548,4549,4550,4551,4552,4553,4554,4555,4556,4557,4558,4559,4560,4561,4562, +4563,4564,4565,4566,4567,4568,4569,4570,4571,4572,4573,4574,4575,4576,4577,4578, +4579,4580,4581,4582,4583,4584,4585,4586,4587,4588,4589,4590,4591,4592,4593,4594, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,4595,4596,4597,4598,4599, +4600,4601,4602,4603,4604,4605,4606,4607,4346,4608,4609,4610,4611,4612,4349,4613, +4614,4615,4616,4617,4352,4618,4619,4354,4620,4621,4622,4623,4624,4625,4626,4627, +4628,4629,4630,4631,4632, 320,4633,4634,4635,4636,4637,4638,4639,1374,4640,4641, +4642,4643,4644,4645,4646,4647,4648,4649, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +4650,4651,4652,4653,4651,4654,4655,4656,4657,4658,4659,4661,4662,4663,4664,4665, +4666,4668,4670,4663,4672,4673,4674,4675,4676,4677,4678, 295, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 33, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 127,4667, 128,4669, 130, 131, 132,4671, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144,4660, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, + { +4680,4681,4682,4683,4684,4685,4686,4687,4688,4689,4690,4691,4692,3423,4689,4693, +3426,4694,4695,3426,4696,4697,4697,4697, 868, 869, 870,4698,4699,4700,4701,4702, +4703,4704,4705,4705,4706,4703,4707,4708,3970,4709,4710,4710,4711,4712,4713,4714, +4715,4716,4717,4717,4718,4224,3450,4719,4720,4721,4722,4723,4724,4720,4720,4725, +3457, 769,3458,3459,4726,4727,4728,4729,4730,4731,4732,4733,4734,4731,1300,4735, +4736,4737,4731,4696,4738,4720,4739,4740,3475,4741,4742,4743,4744,1314,4745,4746, +4747,4748,4749,4742,4742,1322,4750,4751,4752,4753,4754,4753,3423,4755,3970,4756, +4757,1334,4757,4758,4759,4760, 792,4761,1341,1342,1343,1344,4762,4763,4764,1348, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,4765,4765,4766,4767,4766,4768,4769,4770,4771,4772,4773,4774,4775, +4776,4777,4778,4779,4780,4781,4782,4783,4784,4777,4785,4786,4787,4788,4789,4790, +4791,4792,4793,4794,4795,4796,4797,4794,4795,4796,4798,4799,4800,4801,1375, 295, +4802,4803, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + }, +}; + +const AdlBankSetup adlbanksetup[75] = +{ + {0, 1, 1, 0, 0}, //Bank 0, AIL (Star Control 3, Albion, Empire 2, etc.) + {0, 1, 1, 0, 0}, //Bank 1, Bisqwit (selection of 4op and 2op) + {0, 0, 0, 0, 0}, //Bank 2, HMI (Descent, Asterix) + {0, 0, 0, 0, 0}, //Bank 3, HMI (Descent:: Int) + {0, 0, 0, 0, 0}, //Bank 4, HMI (Descent:: Ham) + {0, 0, 0, 0, 0}, //Bank 5, HMI (Descent:: Rick) + {0, 0, 0, 0, 0}, //Bank 6, HMI (Descent 2) + {0, 0, 0, 0, 0}, //Bank 7, HMI (Normality) + {0, 0, 0, 0, 0}, //Bank 8, HMI (Shattered Steel) + {3, 0, 0, 0, 0}, //Bank 9, HMI (Theme Park) + {0, 0, 0, 0, 0}, //Bank 10, HMI (3d Table Sports, Battle Arena Toshinden) + {0, 0, 0, 0, 0}, //Bank 11, HMI (Aces of the Deep) + {0, 0, 0, 0, 0}, //Bank 12, HMI (Earthsiege) + {0, 0, 0, 0, 0}, //Bank 13, HMI (Anvil of Dawn) + {2, 0, 0, 0, 0}, //Bank 14, DMX (Doom 2) + {2, 0, 0, 0, 0}, //Bank 15, DMX (Hexen, Heretic) + {2, 0, 0, 0, 0}, //Bank 16, DMX (DOOM, MUS Play) + {0, 1, 1, 0, 0}, //Bank 17, AIL (Discworld, Grandest Fleet, etc.) + {0, 1, 1, 0, 0}, //Bank 18, AIL (Warcraft 2) + {0, 1, 1, 0, 0}, //Bank 19, AIL (Syndicate) + {0, 1, 1, 0, 0}, //Bank 20, AIL (Guilty, Orion Conspiracy, TNSFC ::4op) + {0, 1, 1, 0, 0}, //Bank 21, AIL (Magic Carpet 2) + {0, 1, 1, 0, 0}, //Bank 22, AIL (Nemesis) + {0, 1, 1, 0, 0}, //Bank 23, AIL (Jagged Alliance) + {0, 1, 1, 0, 0}, //Bank 24, AIL (When Two Worlds War :MISS-INS:) + {0, 1, 1, 0, 0}, //Bank 25, AIL (Bards Tale Construction :MISS-INS:) + {0, 1, 1, 0, 0}, //Bank 26, AIL (Return to Zork) + {0, 1, 1, 0, 0}, //Bank 27, AIL (Theme Hospital) + {0, 1, 1, 0, 0}, //Bank 28, AIL (National Hockey League PA) + {0, 1, 1, 0, 0}, //Bank 29, AIL (Inherit The Earth) + {0, 1, 1, 0, 0}, //Bank 30, AIL (Inherit The Earth, file two) + {0, 1, 1, 0, 0}, //Bank 31, AIL (Little Big Adventure :: 4op) + {0, 1, 1, 0, 0}, //Bank 32, AIL (Wreckin Crew) + {0, 1, 1, 0, 0}, //Bank 33, AIL (Death Gate) + {0, 1, 1, 0, 0}, //Bank 34, AIL (FIFA International Soccer) + {0, 1, 1, 0, 0}, //Bank 35, AIL (Starship Invasion) + {0, 1, 1, 0, 0}, //Bank 36, AIL (Super Street Fighter 2 :4op:) + {0, 1, 1, 0, 0}, //Bank 37, AIL (Lords of the Realm :MISS-INS:) + {0, 1, 1, 0, 0}, //Bank 38, AIL (SimFarm, SimHealth :: 4op) + {0, 1, 1, 0, 0}, //Bank 39, AIL (SimFarm, Settlers, Serf City) + {0, 1, 1, 0, 0}, //Bank 40, AIL (Caesar 2, :p4op::MISS-INS:) + {0, 1, 1, 0, 0}, //Bank 41, AIL (Syndicate Wars) + {0, 1, 1, 0, 0}, //Bank 42, AIL (Bubble Bobble Feat. Rainbow Islands, Z) + {0, 1, 1, 0, 0}, //Bank 43, AIL (Warcraft) + {0, 1, 1, 0, 0}, //Bank 44, AIL (Terra Nova Strike Force Centuri :p4op:) + {0, 1, 1, 0, 0}, //Bank 45, AIL (System Shock :p4op:) + {0, 1, 1, 0, 0}, //Bank 46, AIL (Advanced Civilization) + {0, 1, 1, 0, 0}, //Bank 47, AIL (Battle Chess 4000 :p4op:) + {0, 1, 1, 0, 0}, //Bank 48, AIL (Ultimate Soccer Manager :p4op:) + {0, 1, 1, 0, 0}, //Bank 49, AIL (Air Bucks, Blue And The Gray, etc) + {0, 1, 1, 0, 0}, //Bank 50, AIL (Ultima Underworld 2) + {0, 1, 1, 0, 0}, //Bank 51, AIL (Kasparov's Gambit) + {0, 1, 1, 0, 0}, //Bank 52, AIL (High Seas Trader :MISS-INS:) + {0, 0, 0, 0, 0}, //Bank 53, AIL (Master of Magic, :4op: std percussion) + {0, 0, 0, 0, 0}, //Bank 54, AIL (Master of Magic, :4op: orchestral percussion) + {0, 0, 0, 0, 0}, //Bank 55, SB (Action Soccer) + {0, 0, 0, 0, 0}, //Bank 56, SB (3d Cyberpuck :: melodic only) + {0, 0, 0, 0, 0}, //Bank 57, SB (Simon the Sorcerer :: melodic only) + {4, 1, 1, 0, 0}, //Bank 58, OP3 (The Fat Man 2op set) + {0, 1, 1, 0, 0}, //Bank 59, OP3 (The Fat Man 4op set) + {4, 1, 1, 0, 0}, //Bank 60, OP3 (JungleVision 2op set :: melodic only) + {4, 1, 1, 0, 0}, //Bank 61, OP3 (Wallace 2op set, Nitemare 3D :: melodic only) + {3, 0, 0, 0, 0}, //Bank 62, TMB (Duke Nukem 3D) + {3, 0, 0, 0, 0}, //Bank 63, TMB (Shadow Warrior) + {2, 0, 0, 0, 0}, //Bank 64, DMX (Raptor) + {3, 0, 0, 0, 0}, //Bank 65, OP3 (Modded GMOPL by Wohlstand) + {3, 0, 0, 0, 0}, //Bank 66, SB (Jammey O'Connel's bank) + {3, 0, 0, 0, 0}, //Bank 67, TMB (Default bank of Apgee Sound System) + {0, 1, 1, 0, 0}, //Bank 68, WOPL (4op bank by James Alan Nguyen and Wohlstand) + {3, 0, 0, 0, 0}, //Bank 69, TMB (Blood) + {3, 0, 0, 0, 0}, //Bank 70, TMB (Lee) + {3, 0, 0, 0, 0}, //Bank 71, TMB (Nam) + {0, 0, 0, 0, 0}, //Bank 72, WOPL (DMXOPL3 bank by Sneakernets) + {1, 0, 0, 0, 0}, //Bank 73, EA (Cartooners) + {0, 0, 1, 0, 0} //Bank 74, WOPL (Apogee IMF 90-ish) +}; diff --git a/engine/src/Libraries/adlmidi/adldata.hh b/engine/src/Libraries/adlmidi/adldata.hh new file mode 100644 index 0000000..73c3d94 --- /dev/null +++ b/engine/src/Libraries/adlmidi/adldata.hh @@ -0,0 +1,129 @@ +/* + * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2016 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ADLDATA_H +#define ADLDATA_H + +#include +#include +#include + +#pragma pack(push, 1) +#define ADLDATA_BYTE_COMPARABLE(T) \ + inline bool operator==(const T &a, const T &b) \ + { return !memcmp(&a, &b, sizeof(T)); } \ + inline bool operator!=(const T &a, const T &b) \ + { return !operator==(a, b); } + +struct adldata +{ + uint32_t modulator_E862, carrier_E862; // See below + uint8_t modulator_40, carrier_40; // KSL/attenuation settings + uint8_t feedconn; // Feedback/connection bits for the channel + + int8_t finetune; +}; +ADLDATA_BYTE_COMPARABLE(struct adldata) + +struct adlinsdata +{ + enum { Flag_Pseudo4op = 0x01, Flag_NoSound = 0x02, Flag_Real4op = 0x04 }; + + uint16_t adlno1, adlno2; + uint8_t tone; + uint8_t flags; + uint16_t ms_sound_kon; // Number of milliseconds it produces sound; + uint16_t ms_sound_koff; + double voice2_fine_tune; +}; +ADLDATA_BYTE_COMPARABLE(struct adlinsdata) + +enum { adlNoteOnMaxTime = 40000 }; + +/** + * @brief Instrument data with operators included + */ +struct adlinsdata2 +{ + adldata adl[2]; + uint8_t tone; + uint8_t flags; + uint16_t ms_sound_kon; // Number of milliseconds it produces sound; + uint16_t ms_sound_koff; + double voice2_fine_tune; + adlinsdata2() {} + explicit adlinsdata2(const adlinsdata &d); +}; +ADLDATA_BYTE_COMPARABLE(struct adlinsdata2) + +#undef ADLDATA_BYTE_COMPARABLE +#pragma pack(pop) + +/** + * @brief Bank global setup + */ +struct AdlBankSetup +{ + int volumeModel; + bool deepTremolo; + bool deepVibrato; + bool adLibPercussions; + bool scaleModulators; +}; + +#ifndef DISABLE_EMBEDDED_BANKS +int maxAdlBanks(); +extern const adldata adl[]; +extern const adlinsdata adlins[]; +extern const unsigned short banks[][256]; +extern const char* const banknames[]; +extern const AdlBankSetup adlbanksetup[]; +#endif + +/** + * @brief Conversion of storage formats + */ +inline adlinsdata2::adlinsdata2(const adlinsdata &d) + : tone(d.tone), flags(d.flags), + ms_sound_kon(d.ms_sound_kon), ms_sound_koff(d.ms_sound_koff), + voice2_fine_tune(d.voice2_fine_tune) +{ +#ifdef DISABLE_EMBEDDED_BANKS + std::memset(adl, 0, sizeof(adldata) * 2); +#else + adl[0] = ::adl[d.adlno1]; + adl[1] = ::adl[d.adlno2]; +#endif +} + +/** + * @brief Convert external instrument to internal instrument + */ +void cvt_ADLI_to_FMIns(adlinsdata2 &dst, const struct ADL_Instrument &src); + +/** + * @brief Convert internal instrument to external instrument + */ +void cvt_FMIns_to_ADLI(struct ADL_Instrument &dst, const adlinsdata2 &src); + +#endif //ADLDATA_H diff --git a/engine/src/Libraries/adlmidi/adlmidi.cpp b/engine/src/Libraries/adlmidi/adlmidi.cpp new file mode 100644 index 0000000..20fc385 --- /dev/null +++ b/engine/src/Libraries/adlmidi/adlmidi.cpp @@ -0,0 +1,1562 @@ +/* + * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "adlmidi_private.hpp" + +#ifdef ADLMIDI_HW_OPL +#define MaxChips 1 +#define MaxChips_STR "1" //Why not just "#MaxCards" ? Watcom fails to pass this with "syntax error" :-P +#else +#define MaxChips 100 +#define MaxChips_STR "100" +#endif + +/* Unify MIDI player casting and interface between ADLMIDI and OPNMIDI */ +#define GET_MIDI_PLAYER(device) reinterpret_cast((device)->adl_midiPlayer) +typedef MIDIplay MidiPlayer; + +static ADL_Version adl_version = { + ADLMIDI_VERSION_MAJOR, + ADLMIDI_VERSION_MINOR, + ADLMIDI_VERSION_PATCHLEVEL +}; + +static const ADLMIDI_AudioFormat adl_DefaultAudioFormat = +{ + ADLMIDI_SampleType_S16, + sizeof(int16_t), + 2 * sizeof(int16_t), +}; + +/*---------------------------EXPORTS---------------------------*/ + +ADLMIDI_EXPORT struct ADL_MIDIPlayer *adl_init(long sample_rate) +{ + ADL_MIDIPlayer *midi_device; + midi_device = (ADL_MIDIPlayer *)malloc(sizeof(ADL_MIDIPlayer)); + if(!midi_device) + { + ADLMIDI_ErrorString = "Can't initialize ADLMIDI: out of memory!"; + return NULL; + } + + MIDIplay *player = new MIDIplay(static_cast(sample_rate)); + if(!player) + { + free(midi_device); + ADLMIDI_ErrorString = "Can't initialize ADLMIDI: out of memory!"; + return NULL; + } + midi_device->adl_midiPlayer = player; + adlRefreshNumCards(midi_device); + return midi_device; +} + +ADLMIDI_EXPORT void adl_close(struct ADL_MIDIPlayer *device) +{ + if(!device) + return; + MIDIplay * play = reinterpret_cast(device->adl_midiPlayer); + if(play) + delete play; + device->adl_midiPlayer = NULL; + free(device); + device = NULL; +} + +ADLMIDI_EXPORT void adl_setCallback(ADL_MIDIPlayer *device, void (*AdlMidiCallback)(void)) { + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(play) { + MidiSequencer &seq = play->m_sequencer; + seq.MidiCallback = AdlMidiCallback; + } +} + +ADLMIDI_EXPORT int adl_setDeviceIdentifier(ADL_MIDIPlayer *device, unsigned id) +{ + if(!device || id > 0x0f) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return -1; + play->setDeviceId(static_cast(id)); + return 0; +} + +ADLMIDI_EXPORT int adl_setNumChips(ADL_MIDIPlayer *device, int numChips) +{ + if(device == NULL) + return -2; + + MidiPlayer *play = GET_MIDI_PLAYER(device); +#ifdef ADLMIDI_HW_OPL + ADL_UNUSED(numChips); + play->m_setup.numChips = 1; +#else + play->m_setup.numChips = static_cast(numChips); +#endif + if(play->m_setup.numChips < 1 || play->m_setup.numChips > MaxChips) + { + play->setErrorString("number of chips may only be 1.." MaxChips_STR ".\n"); + return -1; + } + + play->m_synth.m_numChips = play->m_setup.numChips; + adl_reset(device); + + return adlRefreshNumCards(device); +} + +ADLMIDI_EXPORT int adl_getNumChips(struct ADL_MIDIPlayer *device) +{ + if(device == NULL) + return -2; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(play) + return (int)play->m_setup.numChips; + return -2; +} + +ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank) +{ +#ifdef DISABLE_EMBEDDED_BANKS + ADL_UNUSED(bank); + MidiPlayer *play = GET_MIDI_PLAYER(device); + play->setErrorString("This build of libADLMIDI has no embedded banks. " + "Please load banks by using adl_openBankFile() or " + "adl_openBankData() functions instead of adl_setBank()."); + return -1; +#else + const uint32_t NumBanks = static_cast(maxAdlBanks()); + int32_t bankno = bank; + + if(bankno < 0) + bankno = 0; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(static_cast(bankno) >= NumBanks) + { + char errBuf[150]; + snprintf(errBuf, 150, "Embedded bank number may only be 0..%u!\n", static_cast(NumBanks - 1)); + play->setErrorString(errBuf); + return -1; + } + + play->m_setup.bankId = static_cast(bankno); + play->m_synth.setEmbeddedBank(play->m_setup.bankId); + play->applySetup(); + + return adlRefreshNumCards(device); +#endif +} + +ADLMIDI_EXPORT int adl_getBanksCount() +{ +#ifndef DISABLE_EMBEDDED_BANKS + return maxAdlBanks(); +#else + return 0; +#endif +} + +ADLMIDI_EXPORT const char *const *adl_getBankNames() +{ +#ifndef DISABLE_EMBEDDED_BANKS + return banknames; +#else + return NULL; +#endif +} + +ADLMIDI_EXPORT int adl_reserveBanks(ADL_MIDIPlayer *device, unsigned banks) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + OPL3::BankMap &map = play->m_synth.m_insBanks; + map.reserve(banks); + return (int)map.capacity(); +} + +ADLMIDI_EXPORT int adl_getBank(ADL_MIDIPlayer *device, const ADL_BankId *idp, int flags, ADL_Bank *bank) +{ + if(!device || !idp || !bank) + return -1; + + ADL_BankId id = *idp; + if(id.lsb > 127 || id.msb > 127 || id.percussive > 1) + return -1; + size_t idnumber = ((id.msb << 8) | id.lsb | (id.percussive ? size_t(OPL3::PercussionTag) : 0)); + + MidiPlayer *play = GET_MIDI_PLAYER(device); + OPL3::BankMap &map = play->m_synth.m_insBanks; + + OPL3::BankMap::iterator it; + if(!(flags & ADLMIDI_Bank_Create)) + { + it = map.find(idnumber); + if(it == map.end()) + return -1; + } + else + { + std::pair value; + value.first = idnumber; + memset(&value.second, 0, sizeof(value.second)); + for (unsigned i = 0; i < 128; ++i) + value.second.ins[i].flags = adlinsdata::Flag_NoSound; + + std::pair ir; + if(flags & ADLMIDI_Bank_CreateRt) + { + ir = map.insert(value, OPL3::BankMap::do_not_expand_t()); + if(ir.first == map.end()) + return -1; + } + else + ir = map.insert(value); + it = ir.first; + } + + it.to_ptrs(bank->pointer); + return 0; +} + +ADLMIDI_EXPORT int adl_getBankId(ADL_MIDIPlayer *device, const ADL_Bank *bank, ADL_BankId *id) +{ + if(!device || !bank) + return -1; + + OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); + OPL3::BankMap::key_type idnumber = it->first; + id->msb = (idnumber >> 8) & 127; + id->lsb = idnumber & 127; + id->percussive = (idnumber & OPL3::PercussionTag) ? 1 : 0; + return 0; +} + +ADLMIDI_EXPORT int adl_removeBank(ADL_MIDIPlayer *device, ADL_Bank *bank) +{ + if(!device || !bank) + return -1; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + OPL3::BankMap &map = play->m_synth.m_insBanks; + OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); + size_t size = map.size(); + map.erase(it); + return (map.size() != size) ? 0 : -1; +} + +ADLMIDI_EXPORT int adl_getFirstBank(ADL_MIDIPlayer *device, ADL_Bank *bank) +{ + if(!device) + return -1; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + OPL3::BankMap &map = play->m_synth.m_insBanks; + + OPL3::BankMap::iterator it = map.begin(); + if(it == map.end()) + return -1; + + it.to_ptrs(bank->pointer); + return 0; +} + +ADLMIDI_EXPORT int adl_getNextBank(ADL_MIDIPlayer *device, ADL_Bank *bank) +{ + if(!device) + return -1; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + OPL3::BankMap &map = play->m_synth.m_insBanks; + + OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); + if(++it == map.end()) + return -1; + + it.to_ptrs(bank->pointer); + return 0; +} + +ADLMIDI_EXPORT int adl_getInstrument(ADL_MIDIPlayer *device, const ADL_Bank *bank, unsigned index, ADL_Instrument *ins) +{ + if(!device || !bank || index > 127 || !ins) + return 1; + + OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); + cvt_FMIns_to_ADLI(*ins, it->second.ins[index]); + ins->version = 0; + return 0; +} + +ADLMIDI_EXPORT int adl_setInstrument(ADL_MIDIPlayer *device, ADL_Bank *bank, unsigned index, const ADL_Instrument *ins) +{ + if(!device || !bank || index > 127 || !ins) + return 1; + + if(ins->version != 0) + return 1; + + OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); + cvt_ADLI_to_FMIns(it->second.ins[index], *ins); + return 0; +} + +ADLMIDI_EXPORT int adl_loadEmbeddedBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank, int num) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if (!play) + return -1; + +#ifdef DISABLE_EMBEDDED_BANKS + ADL_UNUSED(bank); + ADL_UNUSED(num); + play->setErrorString("This build of libADLMIDI has no embedded banks. " + "Please load banks by using adl_openBankFile() or " + "adl_openBankData() functions instead of adl_loadEmbeddedBank()."); + return -1; +#else + if(num < 0 || num >= maxAdlBanks()) + return -1; + + OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); + size_t id = it->first; + + for (unsigned i = 0; i < 128; ++i) { + size_t insno = i + ((id & OPL3::PercussionTag) ? 128 : 0); + size_t adlmeta = ::banks[num][insno]; + it->second.ins[i] = adlinsdata2(::adlins[adlmeta]); + } + return 0; +#endif +} + +ADLMIDI_EXPORT int adl_setNumFourOpsChn(ADL_MIDIPlayer *device, int ops4) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if((unsigned int)ops4 > 6 * play->m_setup.numChips) + { + char errBuff[250]; + snprintf(errBuff, 250, "number of four-op channels may only be 0..%u when %u OPL3 cards are used.\n", (6 * (play->m_setup.numChips)), play->m_setup.numChips); + play->setErrorString(errBuff); + return -1; + } + + play->m_setup.numFourOps = static_cast(ops4); + play->m_synth.m_numFourOps = play->m_setup.numFourOps; + play->m_synth.updateChannelCategories(); + + return 0; //adlRefreshNumCards(device); +} + +ADLMIDI_EXPORT int adl_getNumFourOpsChn(struct ADL_MIDIPlayer *device) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(play) + return (int)play->m_setup.numFourOps; + return -1; +} + +ADLMIDI_EXPORT void adl_setPercMode(ADL_MIDIPlayer *device, int percmod) +{ + if(!device) return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + play->m_setup.rhythmMode = percmod; + play->m_synth.m_rhythmMode = play->m_setup.rhythmMode < 0 ? + (play->m_synth.m_insBankSetup.adLibPercussions) : + (play->m_setup.rhythmMode != 0); + play->m_synth.updateChannelCategories(); +} + +ADLMIDI_EXPORT void adl_setHVibrato(ADL_MIDIPlayer *device, int hvibro) +{ + if(!device) return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + play->m_setup.deepVibratoMode = hvibro; + play->m_synth.m_deepVibratoMode = play->m_setup.deepVibratoMode < 0 ? + play->m_synth.m_insBankSetup.deepVibrato : + (play->m_setup.deepVibratoMode != 0); + play->m_synth.commitDeepFlags(); +} + +ADLMIDI_EXPORT void adl_setHTremolo(ADL_MIDIPlayer *device, int htremo) +{ + if(!device) return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + play->m_setup.deepTremoloMode = htremo; + play->m_synth.m_deepTremoloMode = play->m_setup.deepTremoloMode < 0 ? + play->m_synth.m_insBankSetup.deepTremolo : + (play->m_setup.deepTremoloMode != 0); + play->m_synth.commitDeepFlags(); +} + +ADLMIDI_EXPORT void adl_setScaleModulators(ADL_MIDIPlayer *device, int smod) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return; + play->m_setup.scaleModulators = smod; + play->m_synth.m_scaleModulators = play->m_setup.scaleModulators < 0 ? + play->m_synth.m_insBankSetup.scaleModulators : + (play->m_setup.scaleModulators != 0); +} + +ADLMIDI_EXPORT void adl_setFullRangeBrightness(struct ADL_MIDIPlayer *device, int fr_brightness) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return; + play->m_setup.fullRangeBrightnessCC74 = (fr_brightness != 0); +} + +ADLMIDI_EXPORT void adl_setLoopEnabled(ADL_MIDIPlayer *device, int loopEn) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return; +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + play->m_sequencer.setLoopEnabled(loopEn != 0); +#else + ADL_UNUSED(loopEn); +#endif +} + +/* !!!DEPRECATED!!! */ +ADLMIDI_EXPORT void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int logvol) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return; + play->m_setup.logarithmicVolumes = (logvol != 0); + if(play->m_setup.logarithmicVolumes) + play->m_synth.setVolumeScaleModel(ADLMIDI_VolumeModel_NativeOPL3); + else + play->m_synth.setVolumeScaleModel(static_cast(play->m_synth.m_volumeScale)); +} + +ADLMIDI_EXPORT void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int volumeModel) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return; + play->m_setup.volumeScaleModel = volumeModel; + if(play->m_setup.volumeScaleModel == ADLMIDI_VolumeModel_AUTO)//Use bank default volume model + play->m_synth.m_volumeScale = (OPL3::VolumesScale)play->m_synth.m_insBankSetup.volumeModel; + else + play->m_synth.setVolumeScaleModel(static_cast(volumeModel)); +} + +ADLMIDI_EXPORT int adl_openBankFile(struct ADL_MIDIPlayer *device, const char *filePath) +{ + if(device && device->adl_midiPlayer) + { + MidiPlayer *play = GET_MIDI_PLAYER(device); + play->m_setup.tick_skip_samples_delay = 0; + if(!play->LoadBank(filePath)) + { + std::string err = play->getErrorString(); + if(err.empty()) + play->setErrorString("ADL MIDI: Can't load file"); + return -1; + } + else return adlRefreshNumCards(device); + } + + ADLMIDI_ErrorString = "Can't load file: ADLMIDI is not initialized"; + return -1; +} + +ADLMIDI_EXPORT int adl_openBankData(struct ADL_MIDIPlayer *device, const void *mem, unsigned long size) +{ + if(device && device->adl_midiPlayer) + { + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return -1; + play->m_setup.tick_skip_samples_delay = 0; + if(!play->LoadBank(mem, static_cast(size))) + { + std::string err = play->getErrorString(); + if(err.empty()) + play->setErrorString("ADL MIDI: Can't load data from memory"); + return -1; + } + else return adlRefreshNumCards(device); + } + + ADLMIDI_ErrorString = "Can't load file: ADL MIDI is not initialized"; + return -1; +} + +ADLMIDI_EXPORT int adl_openFile(ADL_MIDIPlayer *device, const char *filePath) +{ + if(device && device->adl_midiPlayer) + { + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return -1; +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + play->m_setup.tick_skip_samples_delay = 0; + if(!play->LoadMIDI(filePath)) + { + std::string err = play->getErrorString(); + if(err.empty()) + play->setErrorString("ADL MIDI: Can't load file"); + return -1; + } + else return 0; +#else + ADL_UNUSED(filePath); + play->setErrorString("ADLMIDI: MIDI Sequencer is not supported in this build of library!"); + return -1; +#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER + } + + ADLMIDI_ErrorString = "Can't load file: ADL MIDI is not initialized"; + return -1; +} + +ADLMIDI_EXPORT int adl_openData(ADL_MIDIPlayer *device, const void *mem, unsigned long size) +{ + if(device && device->adl_midiPlayer) + { + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return -1; +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + play->m_setup.tick_skip_samples_delay = 0; + if(!play->LoadMIDI(mem, static_cast(size))) + { + std::string err = play->getErrorString(); + if(err.empty()) + play->setErrorString("ADL MIDI: Can't load data from memory"); + return -1; + } + else return 0; +#else + ADL_UNUSED(mem); + ADL_UNUSED(size); + play->setErrorString("ADLMIDI: MIDI Sequencer is not supported in this build of library!"); + return -1; +#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER + } + ADLMIDI_ErrorString = "Can't load file: ADL MIDI is not initialized"; + return -1; +} + + +ADLMIDI_EXPORT const char *adl_emulatorName() +{ + return ""; +} + +ADLMIDI_EXPORT const char *adl_chipEmulatorName(struct ADL_MIDIPlayer *device) +{ + if(device) + { +#ifndef ADLMIDI_HW_OPL + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(play && !play->m_synth.m_chips.empty()) + return play->m_synth.m_chips[0]->emulatorName(); + #else + return "Hardware OPL3 chip on 0x330"; +#endif + } + return "Unknown"; +} + +ADLMIDI_EXPORT int adl_switchEmulator(struct ADL_MIDIPlayer *device, int emulator) +{ + if(device) + { + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + if(!play) + return -1; + if((emulator >= 0) && (emulator < ADLMIDI_EMU_end)) + { + play->m_setup.emulator = emulator; + adl_reset(device); + return 0; + } + play->setErrorString("OPL3 MIDI: Unknown emulation core!"); + } + return -1; +} + + +ADLMIDI_EXPORT int adl_setRunAtPcmRate(ADL_MIDIPlayer *device, int enabled) +{ + if(device) + { + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(play) + { + play->m_setup.runAtPcmRate = (enabled != 0); + adl_reset(device); + return 0; + } + } + return -1; +} + + +ADLMIDI_EXPORT const char *adl_linkedLibraryVersion() +{ +#if !defined(ADLMIDI_ENABLE_HQ_RESAMPLER) + return ADLMIDI_VERSION; +#else + return ADLMIDI_VERSION " (HQ)"; +#endif +} + +ADLMIDI_EXPORT const ADL_Version *adl_linkedVersion() +{ + return &adl_version; +} + +ADLMIDI_EXPORT const char *adl_errorString() +{ + return ADLMIDI_ErrorString.c_str(); +} + +ADLMIDI_EXPORT const char *adl_errorInfo(struct ADL_MIDIPlayer *device) +{ + if(!device) + return adl_errorString(); + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return adl_errorString(); + return play->getErrorString().c_str(); +} + +ADLMIDI_EXPORT const char *adl_getMusicTitle(struct ADL_MIDIPlayer *device) +{ + return adl_metaMusicTitle(device); +} + +ADLMIDI_EXPORT void adl_reset(struct ADL_MIDIPlayer *device) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + play->m_setup.tick_skip_samples_delay = 0; + play->m_synth.m_runAtPcmRate = play->m_setup.runAtPcmRate; + play->m_synth.reset(play->m_setup.emulator, play->m_setup.PCM_RATE, play); + play->m_chipChannels.clear(); + play->m_chipChannels.resize((size_t)play->m_synth.m_numChannels); + play->resetMIDI(); +} + +ADLMIDI_EXPORT double adl_totalTimeLength(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1.0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return -1.0; + return play->m_sequencer.timeLength(); +#else + ADL_UNUSED(device); + return -1.0; +#endif +} + +ADLMIDI_EXPORT double adl_loopStartTime(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1.0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return -1.0; + return play->m_sequencer.getLoopStart(); +#else + ADL_UNUSED(device); + return -1.0; +#endif +} + +ADLMIDI_EXPORT double adl_loopEndTime(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1.0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return -1.0; + return play->m_sequencer.getLoopEnd(); +#else + ADL_UNUSED(device); + return -1.0; +#endif +} + +ADLMIDI_EXPORT double adl_positionTell(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1.0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return -1.0; + return play->m_sequencer.tell(); +#else + ADL_UNUSED(device); + return -1.0; +#endif +} + +ADLMIDI_EXPORT void adl_positionSeek(struct ADL_MIDIPlayer *device, double seconds) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(seconds < 0.0) + return;//Seeking negative position is forbidden! :-P + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return; + play->realTime_panic(); + play->m_setup.delay = play->m_sequencer.seek(seconds, play->m_setup.mindelay); + play->m_setup.carry = 0.0; +#else + ADL_UNUSED(device); + ADL_UNUSED(seconds); +#endif +} + +ADLMIDI_EXPORT void adl_positionRewind(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return; + play->realTime_panic(); + play->m_sequencer.rewind(); +#else + ADL_UNUSED(device); +#endif +} + +ADLMIDI_EXPORT void adl_setTempo(struct ADL_MIDIPlayer *device, double tempo) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device || (tempo <= 0.0)) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return; + play->m_sequencer.setTempo(tempo); +#else + ADL_UNUSED(device); + ADL_UNUSED(tempo); +#endif +} + + +ADLMIDI_EXPORT int adl_describeChannels(struct ADL_MIDIPlayer *device, char *str, char *attr, size_t size) +{ + if(!device) + return -1; + MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + if(!play) + return -1; + play->describeChannels(str, attr, size); + return 0; +} + + +ADLMIDI_EXPORT const char *adl_metaMusicTitle(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return ""; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return ""; + return play->m_sequencer.getMusicTitle().c_str(); +#else + ADL_UNUSED(device); + return ""; +#endif +} + + +ADLMIDI_EXPORT const char *adl_metaMusicCopyright(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return ""; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return ""; + return play->m_sequencer.getMusicCopyright().c_str(); +#else + ADL_UNUSED(device); + return ""; +#endif +} + +ADLMIDI_EXPORT size_t adl_metaTrackTitleCount(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return 0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return 0; + return play->m_sequencer.getTrackTitles().size(); +#else + ADL_UNUSED(device); + return 0; +#endif +} + +ADLMIDI_EXPORT const char *adl_metaTrackTitle(struct ADL_MIDIPlayer *device, size_t index) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return ""; + MidiPlayer *play = GET_MIDI_PLAYER(device); + const std::vector &titles = play->m_sequencer.getTrackTitles(); + if(index >= titles.size()) + return "INVALID"; + return titles[index].c_str(); +#else + ADL_UNUSED(device); + ADL_UNUSED(index); + return "NOT SUPPORTED"; +#endif +} + + +ADLMIDI_EXPORT size_t adl_metaMarkerCount(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return 0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return 0; + return play->m_sequencer.getMarkers().size(); +#else + ADL_UNUSED(device); + return 0; +#endif +} + +ADLMIDI_EXPORT Adl_MarkerEntry adl_metaMarker(struct ADL_MIDIPlayer *device, size_t index) +{ + struct Adl_MarkerEntry marker; +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + MidiPlayer *play = GET_MIDI_PLAYER(device); + const std::vector &markers = play->m_sequencer.getMarkers(); + if(!device || !play || (index >= markers.size())) + { + marker.label = "INVALID"; + marker.pos_time = 0.0; + marker.pos_ticks = 0; + return marker; + } + else + { + const MidiSequencer::MIDI_MarkerEntry &mk = markers[index]; + marker.label = mk.label.c_str(); + marker.pos_time = mk.pos_time; + marker.pos_ticks = (unsigned long)mk.pos_ticks; + } +#else + ADL_UNUSED(device); + ADL_UNUSED(index); + marker.label = "NOT SUPPORTED"; + marker.pos_time = 0.0; + marker.pos_ticks = 0; +#endif + return marker; +} + +ADLMIDI_EXPORT void adl_setRawEventHook(struct ADL_MIDIPlayer *device, ADL_RawEventHook rawEventHook, void *userData) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + play->m_sequencerInterface.onEvent = rawEventHook; + play->m_sequencerInterface.onEvent_userData = userData; +#else + ADL_UNUSED(device); + ADL_UNUSED(rawEventHook); + ADL_UNUSED(userData); +#endif +} + +/* Set note hook */ +ADLMIDI_EXPORT void adl_setNoteHook(struct ADL_MIDIPlayer *device, ADL_NoteHook noteHook, void *userData) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + play->hooks.onNote = noteHook; + play->hooks.onNote_userData = userData; +} + +/* Set debug message hook */ +ADLMIDI_EXPORT void adl_setDebugMessageHook(struct ADL_MIDIPlayer *device, ADL_DebugMessageHook debugMessageHook, void *userData) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + play->hooks.onDebugMessage = debugMessageHook; + play->hooks.onDebugMessage_userData = userData; +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + play->m_sequencerInterface.onDebugMessage = debugMessageHook; + play->m_sequencerInterface.onDebugMessage_userData = userData; +#endif +} + +#ifndef ADLMIDI_HW_OPL + +# ifndef __WATCOMC__ +template +static void CopySamplesRaw(ADL_UInt8 *dstLeft, ADL_UInt8 *dstRight, const int32_t *src, + size_t frameCount, unsigned sampleOffset) +{ + for(size_t i = 0; i < frameCount; ++i) { + *(Dst *)(dstLeft + (i * sampleOffset)) = src[2 * i]; + *(Dst *)(dstRight + (i * sampleOffset)) = src[(2 * i) + 1]; + } +} + +template +static void CopySamplesTransformed(ADL_UInt8 *dstLeft, ADL_UInt8 *dstRight, const int32_t *src, + size_t frameCount, unsigned sampleOffset, + Ret(&transform)(int32_t)) +{ + for(size_t i = 0; i < frameCount; ++i) { + *(Dst *)(dstLeft + (i * sampleOffset)) = static_cast(transform(src[2 * i])); + *(Dst *)(dstRight + (i * sampleOffset)) = static_cast(transform(src[(2 * i) + 1])); + } +} + +static int SendStereoAudio(int samples_requested, + ssize_t in_size, + int32_t *_in, + ssize_t out_pos, + ADL_UInt8 *left, + ADL_UInt8 *right, + const ADLMIDI_AudioFormat *format) +{ + if(!in_size) + return 0; + size_t outputOffset = static_cast(out_pos); + size_t inSamples = static_cast(in_size * 2); + size_t maxSamples = static_cast(samples_requested) - outputOffset; + size_t toCopy = std::min(maxSamples, inSamples); + + ADLMIDI_SampleType sampleType = format->type; + const unsigned containerSize = format->containerSize; + const unsigned sampleOffset = format->sampleOffset; + + left += (outputOffset / 2) * sampleOffset; + right += (outputOffset / 2) * sampleOffset; + + typedef int32_t(&pfnConvert)(int32_t); + typedef float(&ffnConvert)(int32_t); + typedef double(&dfnConvert)(int32_t); + + switch(sampleType) { + case ADLMIDI_SampleType_S8: + case ADLMIDI_SampleType_U8: + { + pfnConvert cvt = (sampleType == ADLMIDI_SampleType_S8) ? adl_cvtS8 : adl_cvtU8; + switch(containerSize) { + case sizeof(int8_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + case sizeof(int16_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + case sizeof(int32_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + default: + return -1; + } + break; + } + case ADLMIDI_SampleType_S16: + case ADLMIDI_SampleType_U16: + { + pfnConvert cvt = (sampleType == ADLMIDI_SampleType_S16) ? adl_cvtS16 : adl_cvtU16; + switch(containerSize) { + case sizeof(int16_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + case sizeof(int32_t): + CopySamplesRaw(left, right, _in, toCopy / 2, sampleOffset); + break; + default: + return -1; + } + break; + } + case ADLMIDI_SampleType_S24: + case ADLMIDI_SampleType_U24: + { + pfnConvert cvt = (sampleType == ADLMIDI_SampleType_S24) ? adl_cvtS24 : adl_cvtU24; + switch(containerSize) { + case sizeof(int32_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + default: + return -1; + } + break; + } + case ADLMIDI_SampleType_S32: + case ADLMIDI_SampleType_U32: + { + pfnConvert cvt = (sampleType == ADLMIDI_SampleType_S32) ? adl_cvtS32 : adl_cvtU32; + switch(containerSize) { + case sizeof(int32_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + default: + return -1; + } + break; + } + case ADLMIDI_SampleType_F32: + { + if(containerSize != sizeof(float)) + return -1; + ffnConvert cvt = adl_cvtReal; + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + } + case ADLMIDI_SampleType_F64: + { + if(containerSize != sizeof(double)) + return -1; + dfnConvert cvt = adl_cvtReal; + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + } + default: + return -1; + } + + return 0; +} +# else // __WATCOMC__ + +/* + Workaround for OpenWattcom where templates are declared above are causing compiler to be crashed +*/ +static void CopySamplesTransformed(ADL_UInt8 *dstLeft, ADL_UInt8 *dstRight, const int32_t *src, + size_t frameCount, unsigned sampleOffset, + int32_t(&transform)(int32_t)) +{ + for(size_t i = 0; i < frameCount; ++i) { + *(int16_t *)(dstLeft + (i * sampleOffset)) = (int16_t)transform(src[2 * i]); + *(int16_t *)(dstRight + (i * sampleOffset)) = (int16_t)transform(src[(2 * i) + 1]); + } +} + +static int SendStereoAudio(int samples_requested, + ssize_t in_size, + int32_t *_in, + ssize_t out_pos, + ADL_UInt8 *left, + ADL_UInt8 *right, + const ADLMIDI_AudioFormat *format) +{ + if(!in_size) + return 0; + size_t outputOffset = static_cast(out_pos); + size_t inSamples = static_cast(in_size * 2); + size_t maxSamples = static_cast(samples_requested) - outputOffset; + size_t toCopy = std::min(maxSamples, inSamples); + + ADLMIDI_SampleType sampleType = format->type; + const unsigned containerSize = format->containerSize; + const unsigned sampleOffset = format->sampleOffset; + + left += (outputOffset / 2) * sampleOffset; + right += (outputOffset / 2) * sampleOffset; + + if(sampleType == ADLMIDI_SampleType_U16) + { + switch(containerSize) { + case sizeof(int16_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, adl_cvtS16); + break; + default: + return -1; + } + } + else + return -1; + return 0; +} +# endif // __WATCOM__ + +#endif // ADLMIDI_HW_OPL + + +ADLMIDI_EXPORT int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short *out) +{ + return adl_playFormat(device, sampleCount, (ADL_UInt8 *)out, (ADL_UInt8 *)(out + 1), &adl_DefaultAudioFormat); +} + +ADLMIDI_EXPORT int adl_playFormat(ADL_MIDIPlayer *device, int sampleCount, + ADL_UInt8 *out_left, ADL_UInt8 *out_right, + const ADLMIDI_AudioFormat *format) +{ +#if defined(ADLMIDI_DISABLE_MIDI_SEQUENCER) || defined(ADLMIDI_HW_OPL) + ADL_UNUSED(device); + ADL_UNUSED(sampleCount); + ADL_UNUSED(out_left); + ADL_UNUSED(out_right); + ADL_UNUSED(format); + return 0; +#endif + +#if !defined(ADLMIDI_DISABLE_MIDI_SEQUENCER) && !defined(ADLMIDI_HW_OPL) + sampleCount -= sampleCount % 2; //Avoid even sample requests + if(sampleCount < 0) + return 0; + if(!device) + return 0; + + MidiPlayer *player = GET_MIDI_PLAYER(device); + MidiPlayer::Setup &setup = player->m_setup; + + ssize_t gotten_len = 0; + ssize_t n_periodCountStereo = 512; + //ssize_t n_periodCountPhys = n_periodCountStereo * 2; + int left = sampleCount; + bool hasSkipped = setup.tick_skip_samples_delay > 0; + + while(left > 0) + { + {//... + const double eat_delay = setup.delay < setup.maxdelay ? setup.delay : setup.maxdelay; + if(hasSkipped) + { + size_t samples = setup.tick_skip_samples_delay > sampleCount ? sampleCount : setup.tick_skip_samples_delay; + n_periodCountStereo = samples / 2; + } + else + { + setup.delay -= eat_delay; + setup.carry += double(setup.PCM_RATE) * eat_delay; + n_periodCountStereo = static_cast(setup.carry); + setup.carry -= double(n_periodCountStereo); + } + + //if(setup.SkipForward > 0) + // setup.SkipForward -= 1; + //else + { + if((player->m_sequencer.positionAtEnd()) && (setup.delay <= 0.0)) + break;//Stop to fetch samples at reaching the song end with disabled loop + + ssize_t leftSamples = left / 2; + if(n_periodCountStereo > leftSamples) + { + setup.tick_skip_samples_delay = (n_periodCountStereo - leftSamples) * 2; + n_periodCountStereo = leftSamples; + } + //! Count of stereo samples + ssize_t in_generatedStereo = (n_periodCountStereo > 512) ? 512 : n_periodCountStereo; + //! Total count of samples + ssize_t in_generatedPhys = in_generatedStereo * 2; + //! Unsigned total sample count + //fill buffer with zeros + int32_t *out_buf = player->m_outBuf; + std::memset(out_buf, 0, static_cast(in_generatedPhys) * sizeof(out_buf[0])); + unsigned int chips = player->m_synth.m_numChips; + if(chips == 1) + { + player->m_synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo); + } + else if(n_periodCountStereo > 0) + { + /* Generate data from every chip and mix result */ + for(size_t card = 0; card < chips; ++card) + player->m_synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); + } + + /* Process it */ + if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1) + return 0; + + left -= (int)in_generatedPhys; + gotten_len += (in_generatedPhys) /* - setup.stored_samples*/; + } + + if(hasSkipped) + { + setup.tick_skip_samples_delay -= n_periodCountStereo * 2; + hasSkipped = setup.tick_skip_samples_delay > 0; + } + else + setup.delay = player->Tick(eat_delay, setup.mindelay); + + }//... + } + + return static_cast(gotten_len); +#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER +} + + +ADLMIDI_EXPORT int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount, short *out) +{ + return adl_generateFormat(device, sampleCount, (ADL_UInt8 *)out, (ADL_UInt8 *)(out + 1), &adl_DefaultAudioFormat); +} + +ADLMIDI_EXPORT int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleCount, + ADL_UInt8 *out_left, ADL_UInt8 *out_right, + const ADLMIDI_AudioFormat *format) +{ +#ifdef ADLMIDI_HW_OPL + ADL_UNUSED(device); + ADL_UNUSED(sampleCount); + ADL_UNUSED(out_left); + ADL_UNUSED(out_right); + ADL_UNUSED(format); + return 0; +#else + sampleCount -= sampleCount % 2; //Avoid even sample requests + if(sampleCount < 0) + return 0; + if(!device) + return 0; + + MidiPlayer *player = GET_MIDI_PLAYER(device); + MidiPlayer::Setup &setup = player->m_setup; + + ssize_t gotten_len = 0; + ssize_t n_periodCountStereo = 512; + + int left = sampleCount; + double delay = double(sampleCount) / double(setup.PCM_RATE); + + while(left > 0) + { + {//... + const double eat_delay = delay < setup.maxdelay ? delay : setup.maxdelay; + delay -= eat_delay; + setup.carry += double(setup.PCM_RATE) * eat_delay; + n_periodCountStereo = static_cast(setup.carry); + setup.carry -= double(n_periodCountStereo); + + { + ssize_t leftSamples = left / 2; + if(n_periodCountStereo > leftSamples) + n_periodCountStereo = leftSamples; + //! Count of stereo samples + ssize_t in_generatedStereo = (n_periodCountStereo > 512) ? 512 : n_periodCountStereo; + //! Total count of samples + ssize_t in_generatedPhys = in_generatedStereo * 2; + //! Unsigned total sample count + //fill buffer with zeros + int32_t *out_buf = player->m_outBuf; + std::memset(out_buf, 0, static_cast(in_generatedPhys) * sizeof(out_buf[0])); + unsigned int chips = player->m_synth.m_numChips; + if(chips == 1) + player->m_synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo); + else if(n_periodCountStereo > 0) + { + /* Generate data from every chip and mix result */ + for(unsigned card = 0; card < chips; ++card) + player->m_synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); + } + /* Process it */ + if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1) + return 0; + + left -= (int)in_generatedPhys; + gotten_len += (in_generatedPhys) /* - setup.stored_samples*/; + } + + player->TickIterators(eat_delay); + }//... + } + + return static_cast(gotten_len); +#endif +} + +ADLMIDI_EXPORT double adl_tickEvents(struct ADL_MIDIPlayer *device, double seconds, double granulality) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1.0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return -1.0; + return play->Tick(seconds, granulality); +#else + ADL_UNUSED(device); + ADL_UNUSED(seconds); + ADL_UNUSED(granulality); + return -1.0; +#endif +} + +ADLMIDI_EXPORT int adl_atEnd(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return 1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return 1; + return (int)play->m_sequencer.positionAtEnd(); +#else + ADL_UNUSED(device); + return 1; +#endif +} + +ADLMIDI_EXPORT size_t adl_trackCount(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return 0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return 0; + return play->m_sequencer.getTrackCount(); +#else + ADL_UNUSED(device); + return 0; +#endif +} + +ADLMIDI_EXPORT int adl_setTrackOptions(struct ADL_MIDIPlayer *device, size_t trackNumber, unsigned trackOptions) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return -1; + MidiSequencer &seq = play->m_sequencer; + + unsigned enableFlag = trackOptions & 3; + trackOptions &= ~3u; + + // handle on/off/solo + switch(enableFlag) + { + default: + break; + case ADLMIDI_TrackOption_On: + case ADLMIDI_TrackOption_Off: + if(!seq.setTrackEnabled(trackNumber, enableFlag == ADLMIDI_TrackOption_On)) + return -1; + break; + case ADLMIDI_TrackOption_Solo: + seq.setSoloTrack(trackNumber); + break; + } + + // handle others... + if(trackOptions != 0) + return -1; + + return 0; + +#else + ADL_UNUSED(device); + ADL_UNUSED(trackNumber); + ADL_UNUSED(trackOptions); + return -1; +#endif +} + +ADLMIDI_EXPORT void adl_panic(struct ADL_MIDIPlayer *device) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return; + play->realTime_panic(); +} + +ADLMIDI_EXPORT void adl_rt_resetState(struct ADL_MIDIPlayer *device) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return; + play->realTime_ResetState(); +} + +ADLMIDI_EXPORT int adl_rt_noteOn(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note, ADL_UInt8 velocity) +{ + if(!device) + return 0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return 0; + return (int)play->realTime_NoteOn(channel, note, velocity); +} + +ADLMIDI_EXPORT void adl_rt_noteOff(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return; + play->realTime_NoteOff(channel, note); +} + +ADLMIDI_EXPORT void adl_rt_noteAfterTouch(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note, ADL_UInt8 atVal) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return; + play->realTime_NoteAfterTouch(channel, note, atVal); +} + +ADLMIDI_EXPORT void adl_rt_channelAfterTouch(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 atVal) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return; + play->realTime_ChannelAfterTouch(channel, atVal); +} + +ADLMIDI_EXPORT void adl_rt_controllerChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 type, ADL_UInt8 value) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return; + play->realTime_Controller(channel, type, value); +} + +ADLMIDI_EXPORT void adl_rt_patchChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 patch) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return; + play->realTime_PatchChange(channel, patch); +} + +ADLMIDI_EXPORT void adl_rt_pitchBend(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt16 pitch) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return; + play->realTime_PitchBend(channel, pitch); +} + +ADLMIDI_EXPORT void adl_rt_pitchBendML(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 msb, ADL_UInt8 lsb) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return; + play->realTime_PitchBend(channel, msb, lsb); +} + +ADLMIDI_EXPORT void adl_rt_bankChangeLSB(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 lsb) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return; + play->realTime_BankChangeLSB(channel, lsb); +} + +ADLMIDI_EXPORT void adl_rt_bankChangeMSB(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 msb) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return; + play->realTime_BankChangeMSB(channel, msb); +} + +ADLMIDI_EXPORT void adl_rt_bankChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_SInt16 bank) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return; + play->realTime_BankChange(channel, (uint16_t)bank); +} + +ADLMIDI_EXPORT int adl_rt_systemExclusive(struct ADL_MIDIPlayer *device, const ADL_UInt8 *msg, size_t size) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return -1; + return play->realTime_SysEx(msg, size); +} diff --git a/engine/src/Libraries/adlmidi/adlmidi_bankmap.h b/engine/src/Libraries/adlmidi/adlmidi_bankmap.h new file mode 100644 index 0000000..29643f1 --- /dev/null +++ b/engine/src/Libraries/adlmidi/adlmidi_bankmap.h @@ -0,0 +1,127 @@ +/* + * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ADLMIDI_BANKMAP_H +#define ADLMIDI_BANKMAP_H + +#include +#include +#include +#include + +#include "adlmidi_ptr.hpp" + +/** + * A simple hash map which accepts bank numbers as keys, can be reserved to a + * fixed size, offers O(1) search and insertion, has a hash function to + * optimize for the worst case, and has some good cache locality properties. + */ +template +class BasicBankMap +{ +public: + typedef size_t key_type; /* the bank identifier */ + typedef T mapped_type; + typedef std::pair value_type; + + BasicBankMap(); + void reserve(size_t capacity); + + size_t size() const + { return m_size; } + size_t capacity() const + { return m_capacity; } + bool empty() const + { return m_size == 0; } + + class iterator; + iterator begin() const; + iterator end() const; + + struct do_not_expand_t {}; + + iterator find(key_type key); + void erase(iterator it); + std::pair insert(const value_type &value); + std::pair insert(const value_type &value, do_not_expand_t); + void clear(); + + T &operator[](key_type key); + +private: + struct Slot; + enum { minimum_allocation = 4 }; + enum + { + hash_bits = 8, /* worst case # of collisions: 128^2/2^hash_bits */ + hash_buckets = 1 << hash_bits, + }; + +public: + class iterator + { + public: + iterator(); + value_type &operator*() const { return slot->value; } + value_type *operator->() const { return &slot->value; } + iterator &operator++(); + bool operator==(const iterator &o) const; + bool operator!=(const iterator &o) const; + void to_ptrs(void *ptrs[3]); + static iterator from_ptrs(void *const ptrs[3]); + private: + Slot **buckets; + Slot *slot; + size_t index; + iterator(Slot **buckets, Slot *slot, size_t index); +#ifdef _MSC_VER + template + friend class BasicBankMap; +#else + friend class BasicBankMap; +#endif + }; + +private: + struct Slot { + Slot *next, *prev; + value_type value; + Slot() : next(NULL), prev(NULL) {} + }; + AdlMIDI_SPtrArray m_buckets; + std::list< AdlMIDI_SPtrArray > m_allocations; + Slot *m_freeslots; + size_t m_size; + size_t m_capacity; + static size_t hash(key_type key); + Slot *allocate_slot(); + Slot *ensure_allocate_slot(); + void free_slot(Slot *slot); + Slot *bucket_find(size_t index, key_type key); + void bucket_add(size_t index, Slot *slot); + void bucket_remove(size_t index, Slot *slot); +}; + +#include "adlmidi_bankmap.tcc" + +#endif // ADLMIDI_BANKMAP_H diff --git a/engine/src/Libraries/adlmidi/adlmidi_bankmap.tcc b/engine/src/Libraries/adlmidi/adlmidi_bankmap.tcc new file mode 100644 index 0000000..90d8894 --- /dev/null +++ b/engine/src/Libraries/adlmidi/adlmidi_bankmap.tcc @@ -0,0 +1,283 @@ +/* + * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "adlmidi_bankmap.h" +#include + +template +inline BasicBankMap::BasicBankMap() + : m_freeslots(NULL), + m_size(0), + m_capacity(0) +{ + m_buckets.reset(new Slot *[hash_buckets]()); +} + +template +inline size_t BasicBankMap::hash(key_type key) +{ + // disregard the 0 high bit in LSB + key = key_type(key & 127) | key_type((key >> 8) << 7); + // take low part as hash value + return key & (hash_buckets - 1); +} + +template +void BasicBankMap::reserve(size_t capacity) +{ + if(m_capacity >= capacity) + return; + + size_t need = capacity - m_capacity; + const size_t minalloc = static_cast(minimum_allocation); + need = (need < minalloc) ? minalloc : need; + + AdlMIDI_SPtrArray slotz; + slotz.reset(new Slot[need]); + m_allocations.push_back(slotz); + m_capacity += need; + + for(size_t i = need; i-- > 0;) + free_slot(&slotz[i]); +} + +template +typename BasicBankMap::iterator +BasicBankMap::begin() const +{ + iterator it(m_buckets.get(), NULL, 0); + while(it.index < hash_buckets && !(it.slot = m_buckets[it.index])) + ++it.index; + return it; +} + +template +typename BasicBankMap::iterator +BasicBankMap::end() const +{ + iterator it(m_buckets.get(), NULL, hash_buckets); + return it; +} + +template +typename BasicBankMap::iterator BasicBankMap::find(key_type key) +{ + size_t index = hash(key); + Slot *slot = bucket_find(index, key); + if(!slot) + return end(); + return iterator(m_buckets.get(), slot, index); +} + +template +void BasicBankMap::erase(iterator it) +{ + bucket_remove(it.index, it.slot); + free_slot(it.slot); + --m_size; +} + +template +inline BasicBankMap::iterator::iterator() + : buckets(NULL), slot(NULL), index(0) +{ +} + +template +inline BasicBankMap::iterator::iterator(Slot **buckets, Slot *slot, size_t index) + : buckets(buckets), slot(slot), index(index) +{ +} + +template +typename BasicBankMap::iterator & +BasicBankMap::iterator::operator++() +{ + if(slot->next) + slot = slot->next; + else { + Slot *slot = NULL; + ++index; + while(index < hash_buckets && !(slot = buckets[index])) + ++index; + this->slot = slot; + } + return *this; +} + +template +bool BasicBankMap::iterator::operator==(const iterator &o) const +{ + return buckets == o.buckets && slot == o.slot && index == o.index; +} + +template +inline bool BasicBankMap::iterator::operator!=(const iterator &o) const +{ + return !operator==(o); +} + +template +void BasicBankMap::iterator::to_ptrs(void *ptrs[3]) +{ + ptrs[0] = buckets; + ptrs[1] = slot; + ptrs[2] = (void *)index; +} + +template +typename BasicBankMap::iterator +BasicBankMap::iterator::from_ptrs(void *const ptrs[3]) +{ + iterator it; + it.buckets = (Slot **)ptrs[0]; + it.slot = (Slot *)ptrs[1]; + it.index = (size_t)ptrs[2]; + return it; +} + +template +std::pair::iterator, bool> +BasicBankMap::insert(const value_type &value) +{ + size_t index = hash(value.first); + Slot *slot = bucket_find(index, value.first); + if(slot) + return std::make_pair(iterator(m_buckets.get(), slot, index), false); + slot = allocate_slot(); + if(!slot) { + reserve(m_capacity + minimum_allocation); + slot = ensure_allocate_slot(); + } + slot->value = value; + bucket_add(index, slot); + ++m_size; + return std::make_pair(iterator(m_buckets.get(), slot, index), true); +} + +template +std::pair::iterator, bool> +BasicBankMap::insert(const value_type &value, do_not_expand_t) +{ + size_t index = hash(value.first); + Slot *slot = bucket_find(index, value.first); + if(slot) + return std::make_pair(iterator(m_buckets.get(), slot, index), false); + slot = allocate_slot(); + if(!slot) + return std::make_pair(end(), false); + slot->value = value; + bucket_add(index, slot); + ++m_size; + return std::make_pair(iterator(m_buckets.get(), slot, index), true); +} + +template +void BasicBankMap::clear() +{ + for(size_t i = 0; i < hash_buckets; ++i) { + Slot *slot = m_buckets[i]; + while (Slot *cur = slot) { + slot = slot->next; + free_slot(cur); + } + m_buckets[i] = NULL; + } + m_size = 0; +} + +template +inline T &BasicBankMap::operator[](key_type key) +{ + return insert(value_type(key, T())).first->second; +} + +template +typename BasicBankMap::Slot * +BasicBankMap::allocate_slot() +{ + Slot *slot = m_freeslots; + if(!slot) + return NULL; + Slot *next = slot->next; + if(next) + next->prev = NULL; + m_freeslots = next; + return slot; +} + +template +inline typename BasicBankMap::Slot * +BasicBankMap::ensure_allocate_slot() +{ + Slot *slot = allocate_slot(); + assert(slot); + return slot; +} + +template +void BasicBankMap::free_slot(Slot *slot) +{ + Slot *next = m_freeslots; + if(next) + next->prev = slot; + slot->prev = NULL; + slot->next = next; + m_freeslots = slot; + m_freeslots->value.second = T(); +} + +template +typename BasicBankMap::Slot * +BasicBankMap::bucket_find(size_t index, key_type key) +{ + Slot *slot = m_buckets[index]; + while(slot && slot->value.first != key) + slot = slot->next; + return slot; +} + +template +void BasicBankMap::bucket_add(size_t index, Slot *slot) +{ + assert(slot); + Slot *next = m_buckets[index]; + if(next) + next->prev = slot; + slot->next = next; + m_buckets[index] = slot; +} + +template +void BasicBankMap::bucket_remove(size_t index, Slot *slot) +{ + assert(slot); + Slot *prev = slot->prev; + Slot *next = slot->next; + if(!prev) + m_buckets[index] = next; + else + prev->next = next; + if(next) + next->prev = prev; +} diff --git a/engine/src/Libraries/adlmidi/adlmidi_load.cpp b/engine/src/Libraries/adlmidi/adlmidi_load.cpp new file mode 100644 index 0000000..fcabd2b --- /dev/null +++ b/engine/src/Libraries/adlmidi/adlmidi_load.cpp @@ -0,0 +1,382 @@ +/* + * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "adlmidi_private.hpp" +#include "wopl/wopl_file.h" + +bool MIDIplay::LoadBank(const std::string &filename) +{ + FileAndMemReader file; + file.openFile(filename.c_str()); + return LoadBank(file); +} + +bool MIDIplay::LoadBank(const void *data, size_t size) +{ + FileAndMemReader file; + file.openData(data, size); + return LoadBank(file); +} + +template +static void cvt_generic_to_FMIns(adlinsdata2 &ins, const WOPLI &in) +{ + ins.voice2_fine_tune = 0.0; + int8_t voice2_fine_tune = in.second_voice_detune; + if(voice2_fine_tune != 0) + { + if(voice2_fine_tune == 1) + ins.voice2_fine_tune = 0.000025; + else if(voice2_fine_tune == -1) + ins.voice2_fine_tune = -0.000025; + else + ins.voice2_fine_tune = voice2_fine_tune * (15.625 / 1000.0); + } + + ins.tone = in.percussion_key_number; + ins.flags = (in.inst_flags & WOPL_Ins_4op) && (in.inst_flags & WOPL_Ins_Pseudo4op) ? adlinsdata::Flag_Pseudo4op : 0; + ins.flags|= (in.inst_flags & WOPL_Ins_4op) && ((in.inst_flags & WOPL_Ins_Pseudo4op) == 0) ? adlinsdata::Flag_Real4op : 0; + ins.flags|= (in.inst_flags & WOPL_Ins_IsBlank) ? adlinsdata::Flag_NoSound : 0; + + bool fourOps = (in.inst_flags & WOPL_Ins_4op) || (in.inst_flags & WOPL_Ins_Pseudo4op); + for(size_t op = 0, slt = 0; op < static_cast(fourOps ? 4 : 2); op++, slt++) + { + ins.adl[slt].carrier_E862 = + ((static_cast(in.operators[op].waveform_E0) << 24) & 0xFF000000) //WaveForm + | ((static_cast(in.operators[op].susrel_80) << 16) & 0x00FF0000) //SusRel + | ((static_cast(in.operators[op].atdec_60) << 8) & 0x0000FF00) //AtDec + | ((static_cast(in.operators[op].avekf_20) << 0) & 0x000000FF); //AVEKM + ins.adl[slt].carrier_40 = in.operators[op].ksl_l_40;//KSLL + + op++; + ins.adl[slt].modulator_E862 = + ((static_cast(in.operators[op].waveform_E0) << 24) & 0xFF000000) //WaveForm + | ((static_cast(in.operators[op].susrel_80) << 16) & 0x00FF0000) //SusRel + | ((static_cast(in.operators[op].atdec_60) << 8) & 0x0000FF00) //AtDec + | ((static_cast(in.operators[op].avekf_20) << 0) & 0x000000FF); //AVEKM + ins.adl[slt].modulator_40 = in.operators[op].ksl_l_40;//KSLL + } + + ins.adl[0].finetune = static_cast(in.note_offset1); + ins.adl[0].feedconn = in.fb_conn1_C0; + if(!fourOps) + ins.adl[1] = ins.adl[0]; + else + { + ins.adl[1].finetune = static_cast(in.note_offset2); + ins.adl[1].feedconn = in.fb_conn2_C0; + } + + ins.ms_sound_kon = in.delay_on_ms; + ins.ms_sound_koff = in.delay_off_ms; +} + +template +static void cvt_FMIns_to_generic(WOPLI &ins, const adlinsdata2 &in) +{ + ins.second_voice_detune = 0; + double voice2_fine_tune = in.voice2_fine_tune; + if(voice2_fine_tune != 0) + { + if(voice2_fine_tune > 0 && voice2_fine_tune <= 0.000025) + ins.second_voice_detune = 1; + else if(voice2_fine_tune < 0 && voice2_fine_tune >= -0.000025) + ins.second_voice_detune = -1; + else + { + long value = static_cast(round(voice2_fine_tune * (1000.0 / 15.625))); + value = (value < -128) ? -128 : value; + value = (value > +127) ? +127 : value; + ins.second_voice_detune = static_cast(value); + } + } + + ins.percussion_key_number = in.tone; + bool fourOps = (in.flags & adlinsdata::Flag_Pseudo4op) || in.adl[0] != in.adl[1]; + ins.inst_flags = fourOps ? WOPL_Ins_4op : 0; + ins.inst_flags|= (in.flags & adlinsdata::Flag_Pseudo4op) ? WOPL_Ins_Pseudo4op : 0; + ins.inst_flags|= (in.flags & adlinsdata::Flag_NoSound) ? WOPL_Ins_IsBlank : 0; + + for(size_t op = 0, slt = 0; op < static_cast(fourOps ? 4 : 2); op++, slt++) + { + ins.operators[op].waveform_E0 = static_cast(in.adl[slt].carrier_E862 >> 24); + ins.operators[op].susrel_80 = static_cast(in.adl[slt].carrier_E862 >> 16); + ins.operators[op].atdec_60 = static_cast(in.adl[slt].carrier_E862 >> 8); + ins.operators[op].avekf_20 = static_cast(in.adl[slt].carrier_E862 >> 0); + ins.operators[op].ksl_l_40 = in.adl[slt].carrier_40; + + op++; + ins.operators[op].waveform_E0 = static_cast(in.adl[slt].carrier_E862 >> 24); + ins.operators[op].susrel_80 = static_cast(in.adl[slt].carrier_E862 >> 16); + ins.operators[op].atdec_60 = static_cast(in.adl[slt].carrier_E862 >> 8); + ins.operators[op].avekf_20 = static_cast(in.adl[slt].carrier_E862 >> 0); + ins.operators[op].ksl_l_40 = in.adl[slt].carrier_40; + } + + ins.note_offset1 = in.adl[0].finetune; + ins.fb_conn1_C0 = in.adl[0].feedconn; + if(!fourOps) + { + ins.operators[2] = ins.operators[0]; + ins.operators[3] = ins.operators[1]; + } + else + { + ins.note_offset2 = in.adl[1].finetune; + ins.fb_conn2_C0 = in.adl[1].feedconn; + } + + ins.delay_on_ms = in.ms_sound_kon; + ins.delay_off_ms = in.ms_sound_koff; +} + +void cvt_ADLI_to_FMIns(adlinsdata2 &ins, const ADL_Instrument &in) +{ + return cvt_generic_to_FMIns(ins, in); +} + +void cvt_FMIns_to_ADLI(ADL_Instrument &ins, const adlinsdata2 &in) +{ + cvt_FMIns_to_generic(ins, in); +} + +bool MIDIplay::LoadBank(FileAndMemReader &fr) +{ + int err = 0; + WOPLFile *wopl = NULL; + char *raw_file_data = NULL; + size_t fsize; + if(!fr.isValid()) + { + errorStringOut = "Custom bank: Invalid data stream!"; + return false; + } + + // Read complete bank file into the memory + fsize = fr.fileSize(); + fr.seek(0, FileAndMemReader::SET); + // Allocate necessary memory block + raw_file_data = (char*)malloc(fsize); + if(!raw_file_data) + { + errorStringOut = "Custom bank: Out of memory before of read!"; + return false; + } + fr.read(raw_file_data, 1, fsize); + + // Parse bank file from the memory + wopl = WOPL_LoadBankFromMem((void*)raw_file_data, fsize, &err); + //Free the buffer no more needed + free(raw_file_data); + + // Check for any erros + if(!wopl) + { + switch(err) + { + case WOPL_ERR_BAD_MAGIC: + errorStringOut = "Custom bank: Invalid magic!"; + return false; + case WOPL_ERR_UNEXPECTED_ENDING: + errorStringOut = "Custom bank: Unexpected ending!"; + return false; + case WOPL_ERR_INVALID_BANKS_COUNT: + errorStringOut = "Custom bank: Invalid banks count!"; + return false; + case WOPL_ERR_NEWER_VERSION: + errorStringOut = "Custom bank: Version is newer than supported by this library!"; + return false; + case WOPL_ERR_OUT_OF_MEMORY: + errorStringOut = "Custom bank: Out of memory!"; + return false; + default: + errorStringOut = "Custom bank: Unknown error!"; + return false; + } + } + + m_synth.m_insBankSetup.adLibPercussions = false; + m_synth.m_insBankSetup.scaleModulators = false; + m_synth.m_insBankSetup.deepTremolo = (wopl->opl_flags & WOPL_FLAG_DEEP_TREMOLO) != 0; + m_synth.m_insBankSetup.deepVibrato = (wopl->opl_flags & WOPL_FLAG_DEEP_VIBRATO) != 0; + m_synth.m_insBankSetup.volumeModel = wopl->volume_model; + m_setup.deepTremoloMode = -1; + m_setup.deepVibratoMode = -1; + m_setup.volumeScaleModel = ADLMIDI_VolumeModel_AUTO; + + m_synth.setEmbeddedBank(m_setup.bankId); + + uint16_t slots_counts[2] = {wopl->banks_count_melodic, wopl->banks_count_percussion}; + WOPLBank *slots_src_ins[2] = { wopl->banks_melodic, wopl->banks_percussive }; + + for(size_t ss = 0; ss < 2; ss++) + { + for(size_t i = 0; i < slots_counts[ss]; i++) + { + size_t bankno = (slots_src_ins[ss][i].bank_midi_msb * 256) + + (slots_src_ins[ss][i].bank_midi_lsb) + + (ss ? size_t(OPL3::PercussionTag) : 0); + OPL3::Bank &bank = m_synth.m_insBanks[bankno]; + for(int j = 0; j < 128; j++) + { + adlinsdata2 &ins = bank.ins[j]; + std::memset(&ins, 0, sizeof(adlinsdata2)); + WOPLInstrument &inIns = slots_src_ins[ss][i].ins[j]; + cvt_generic_to_FMIns(ins, inIns); + } + } + } + + m_synth.m_embeddedBank = OPL3::CustomBankTag; // Use dynamic banks! + //Percussion offset is count of instruments multipled to count of melodic banks + applySetup(); + + WOPL_Free(wopl); + + return true; +} + +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + +bool MIDIplay::LoadMIDI_pre() +{ +#ifdef DISABLE_EMBEDDED_BANKS + if((m_synth.m_embeddedBank != OPL3::CustomBankTag) || m_synth.m_insBanks.empty()) + { + errorStringOut = "Bank is not set! Please load any instruments bank by using of adl_openBankFile() or adl_openBankData() functions!"; + return false; + } +#endif + /**** Set all properties BEFORE starting of actial file reading! ****/ + resetMIDI(); + applySetup(); + + return true; +} + +bool MIDIplay::LoadMIDI_post() +{ + MidiSequencer::FileFormat format = m_sequencer.getFormat(); + if(format == MidiSequencer::Format_CMF) + { + const std::vector &instruments = m_sequencer.getRawCmfInstruments(); + m_synth.m_insBanks.clear();//Clean up old banks + + uint16_t ins_count = static_cast(instruments.size()); + for(uint16_t i = 0; i < ins_count; ++i) + { + const uint8_t *InsData = instruments[i].data; + size_t bank = i / 256; + bank = ((bank & 127) + ((bank >> 7) << 8)); + if(bank > 127 + (127 << 8)) + break; + bank += (i % 256 < 128) ? 0 : size_t(OPL3::PercussionTag); + + /*std::printf("Ins %3u: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", + i, InsData[0],InsData[1],InsData[2],InsData[3], InsData[4],InsData[5],InsData[6],InsData[7], + InsData[8],InsData[9],InsData[10],InsData[11], InsData[12],InsData[13],InsData[14],InsData[15]);*/ + adlinsdata2 &adlins = m_synth.m_insBanks[bank].ins[i % 128]; + adldata adl; + adl.modulator_E862 = + ((static_cast(InsData[8] & 0x07) << 24) & 0xFF000000) //WaveForm + | ((static_cast(InsData[6]) << 16) & 0x00FF0000) //Sustain/Release + | ((static_cast(InsData[4]) << 8) & 0x0000FF00) //Attack/Decay + | ((static_cast(InsData[0]) << 0) & 0x000000FF); //MultKEVA + adl.carrier_E862 = + ((static_cast(InsData[9] & 0x07) << 24) & 0xFF000000) //WaveForm + | ((static_cast(InsData[7]) << 16) & 0x00FF0000) //Sustain/Release + | ((static_cast(InsData[5]) << 8) & 0x0000FF00) //Attack/Decay + | ((static_cast(InsData[1]) << 0) & 0x000000FF); //MultKEVA + adl.modulator_40 = InsData[2]; + adl.carrier_40 = InsData[3]; + adl.feedconn = InsData[10] & 0x0F; + adl.finetune = 0; + adlins.adl[0] = adl; + adlins.adl[1] = adl; + adlins.ms_sound_kon = 1000; + adlins.ms_sound_koff = 0; + adlins.tone = 0; + adlins.flags = 0; + adlins.voice2_fine_tune = 0.0; + } + + m_synth.m_embeddedBank = OPL3::CustomBankTag; // Ignore AdlBank number, use dynamic banks instead + //std::printf("CMF deltas %u ticks %u, basictempo = %u\n", deltas, ticks, basictempo); + m_synth.m_rhythmMode = true; + m_synth.m_musicMode = OPL3::MODE_CMF; + m_synth.m_volumeScale = OPL3::VOLUME_NATIVE; + } + else if(format == MidiSequencer::Format_RSXX) + { + //opl.CartoonersVolumes = true; + m_synth.m_musicMode = OPL3::MODE_RSXX; + m_synth.m_volumeScale = OPL3::VOLUME_NATIVE; + } + else if(format == MidiSequencer::Format_IMF) + { + //std::fprintf(stderr, "Done reading IMF file\n"); + m_synth.m_numFourOps = 0; //Don't use 4-operator channels for IMF playing! + m_synth.m_musicMode = OPL3::MODE_IMF; + } + + m_synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); // Reset OPL3 chip + //opl.Reset(); // ...twice (just in case someone misprogrammed OPL3 previously) + m_chipChannels.clear(); + m_chipChannels.resize(m_synth.m_numChannels); + + return true; +} + +bool MIDIplay::LoadMIDI(const std::string &filename) +{ + FileAndMemReader file; + file.openFile(filename.c_str()); + if(!LoadMIDI_pre()) + return false; + if(!m_sequencer.loadMIDI(file)) + { + errorStringOut = m_sequencer.getErrorString(); + return false; + } + if(!LoadMIDI_post()) + return false; + return true; +} + +bool MIDIplay::LoadMIDI(const void *data, size_t size) +{ + FileAndMemReader file; + file.openData(data, size); + if(!LoadMIDI_pre()) + return false; + if(!m_sequencer.loadMIDI(file)) + { + errorStringOut = m_sequencer.getErrorString(); + return false; + } + if(!LoadMIDI_post()) + return false; + return true; +} + +#endif /* ADLMIDI_DISABLE_MIDI_SEQUENCER */ diff --git a/engine/src/Libraries/adlmidi/adlmidi_midiplay.cpp b/engine/src/Libraries/adlmidi/adlmidi_midiplay.cpp new file mode 100644 index 0000000..b256d79 --- /dev/null +++ b/engine/src/Libraries/adlmidi/adlmidi_midiplay.cpp @@ -0,0 +1,2130 @@ +/* + * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "adlmidi_private.hpp" + +// Mapping from MIDI volume level to OPL level value. + +static const uint_fast32_t DMX_volume_mapping_table[128] = +{ + 0, 1, 3, 5, 6, 8, 10, 11, + 13, 14, 16, 17, 19, 20, 22, 23, + 25, 26, 27, 29, 30, 32, 33, 34, + 36, 37, 39, 41, 43, 45, 47, 49, + 50, 52, 54, 55, 57, 59, 60, 61, + 63, 64, 66, 67, 68, 69, 71, 72, + 73, 74, 75, 76, 77, 79, 80, 81, + 82, 83, 84, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 92, 93, 94, 95, + 96, 96, 97, 98, 99, 99, 100, 101, + 101, 102, 103, 103, 104, 105, 105, 106, + 107, 107, 108, 109, 109, 110, 110, 111, + 112, 112, 113, 113, 114, 114, 115, 115, + 116, 117, 117, 118, 118, 119, 119, 120, + 120, 121, 121, 122, 122, 123, 123, 123, + 124, 124, 125, 125, 126, 126, 127, 127, +}; + +static const uint_fast32_t W9X_volume_mapping_table[32] = +{ + 63, 63, 40, 36, 32, 28, 23, 21, + 19, 17, 15, 14, 13, 12, 11, 10, + 9, 8, 7, 6, 5, 5, 4, 4, + 3, 3, 2, 2, 1, 1, 0, 0 +}; + + +//static const char MIDIsymbols[256+1] = +//"PPPPPPhcckmvmxbd" // Ins 0-15 +//"oooooahoGGGGGGGG" // Ins 16-31 +//"BBBBBBBBVVVVVHHM" // Ins 32-47 +//"SSSSOOOcTTTTTTTT" // Ins 48-63 +//"XXXXTTTFFFFFFFFF" // Ins 64-79 +//"LLLLLLLLpppppppp" // Ins 80-95 +//"XXXXXXXXGGGGGTSS" // Ins 96-111 +//"bbbbMMMcGXXXXXXX" // Ins 112-127 +//"????????????????" // Prc 0-15 +//"????????????????" // Prc 16-31 +//"???DDshMhhhCCCbM" // Prc 32-47 +//"CBDMMDDDMMDDDDDD" // Prc 48-63 +//"DDDDDDDDDDDDDDDD" // Prc 64-79 +//"DD??????????????" // Prc 80-95 +//"????????????????" // Prc 96-111 +//"????????????????"; // Prc 112-127 + +static const uint8_t PercussionMap[256] = + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"//GM + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // 3 = bass drum + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // 4 = snare + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // 5 = tom + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // 6 = cymbal + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // 7 = hihat + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"//GP0 + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"//GP16 + //2 3 4 5 6 7 8 940 1 2 3 4 5 6 7 + "\0\0\0\3\3\0\0\7\0\5\7\5\0\5\7\5"//GP32 + //8 950 1 2 3 4 5 6 7 8 960 1 2 3 + "\5\6\5\0\6\0\5\6\0\6\0\6\5\5\5\5"//GP48 + //4 5 6 7 8 970 1 2 3 4 5 6 7 8 9 + "\5\0\0\0\0\0\7\0\0\0\0\0\0\0\0\0"//GP64 + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + +enum { MasterVolumeDefault = 127 }; + +inline bool isXgPercChannel(uint8_t msb, uint8_t lsb) +{ + return (msb == 0x7E || msb == 0x7F) && (lsb == 0); +} + +void MIDIplay::AdlChannel::addAge(int64_t ms) +{ + const int64_t neg = static_cast(-0x1FFFFFFFl); + if(users_empty()) + koff_time_until_neglible = std::max(int64_t(koff_time_until_neglible - ms), neg); + else + { + koff_time_until_neglible = 0; + for(LocationData *i = users_first; i; i = i->next) + { + if(!i->fixed_sustain) + i->kon_time_until_neglible = std::max(i->kon_time_until_neglible - ms, neg); + i->vibdelay += ms; + } + } +} + +MIDIplay::MIDIplay(unsigned long sampleRate): + m_cmfPercussionMode(false), + m_masterVolume(MasterVolumeDefault), + m_sysExDeviceId(0), + m_synthMode(Mode_XG), + m_arpeggioCounter(0) +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + , m_audioTickCounter(0) +#endif +{ + m_midiDevices.clear(); + + m_setup.emulator = ADLMIDI_EMU_NUKED; + m_setup.runAtPcmRate = false; + + m_setup.PCM_RATE = sampleRate; + m_setup.mindelay = 1.0 / (double)m_setup.PCM_RATE; + m_setup.maxdelay = 512.0 / (double)m_setup.PCM_RATE; + + m_setup.bankId = 0; + m_setup.numFourOps = 7; + m_setup.numChips = 2; + m_setup.deepTremoloMode = -1; + m_setup.deepVibratoMode = -1; + m_setup.rhythmMode = -1; + m_setup.logarithmicVolumes = false; + m_setup.volumeScaleModel = ADLMIDI_VolumeModel_AUTO; + //m_setup.SkipForward = 0; + m_setup.scaleModulators = -1; + m_setup.fullRangeBrightnessCC74 = false; + m_setup.delay = 0.0; + m_setup.carry = 0.0; + m_setup.tick_skip_samples_delay = 0; + +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + initSequencerInterface(); +#endif + resetMIDI(); + applySetup(); + realTime_ResetState(); +} + +void MIDIplay::applySetup() +{ + m_synth.m_musicMode = OPL3::MODE_MIDI; + + m_setup.tick_skip_samples_delay = 0; + + m_synth.m_runAtPcmRate = m_setup.runAtPcmRate; + +#ifndef DISABLE_EMBEDDED_BANKS + if(m_synth.m_embeddedBank != OPL3::CustomBankTag) + m_synth.m_insBankSetup = adlbanksetup[m_setup.bankId]; +#endif + + m_synth.m_deepTremoloMode = m_setup.deepTremoloMode < 0 ? + m_synth.m_insBankSetup.deepTremolo : + (m_setup.deepTremoloMode != 0); + m_synth.m_deepVibratoMode = m_setup.deepVibratoMode < 0 ? + m_synth.m_insBankSetup.deepVibrato : + (m_setup.deepVibratoMode != 0); + m_synth.m_rhythmMode = m_setup.rhythmMode < 0 ? + m_synth.m_insBankSetup.adLibPercussions : + (m_setup.rhythmMode != 0); + m_synth.m_scaleModulators = m_setup.scaleModulators < 0 ? + m_synth.m_insBankSetup.scaleModulators : + (m_setup.scaleModulators != 0); + + if(m_setup.logarithmicVolumes) + m_synth.setVolumeScaleModel(ADLMIDI_VolumeModel_NativeOPL3); + else + m_synth.setVolumeScaleModel(static_cast(m_setup.volumeScaleModel)); + + if(m_setup.volumeScaleModel == ADLMIDI_VolumeModel_AUTO)//Use bank default volume model + m_synth.m_volumeScale = (OPL3::VolumesScale)m_synth.m_insBankSetup.volumeModel; + + m_synth.m_numChips = m_setup.numChips; + m_synth.m_numFourOps = m_setup.numFourOps; + m_cmfPercussionMode = false; + + m_synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); + m_chipChannels.clear(); + m_chipChannels.resize(m_synth.m_numChannels); + + // Reset the arpeggio counter + m_arpeggioCounter = 0; +} + +void MIDIplay::resetMIDI() +{ + m_masterVolume = MasterVolumeDefault; + m_sysExDeviceId = 0; + m_synthMode = Mode_XG; + m_arpeggioCounter = 0; + + m_midiChannels.clear(); + m_midiChannels.resize(16, MIDIchannel()); + + caugh_missing_instruments.clear(); + caugh_missing_banks_melodic.clear(); + caugh_missing_banks_percussion.clear(); +} + +void MIDIplay::TickIterators(double s) +{ + for(uint16_t c = 0; c < m_synth.m_numChannels; ++c) + m_chipChannels[c].addAge(static_cast(s * 1000.0)); + updateVibrato(s); + updateArpeggio(s); +#if !defined(ADLMIDI_AUDIO_TICK_HANDLER) + updateGlide(s); +#endif +} + +void MIDIplay::realTime_ResetState() +{ + for(size_t ch = 0; ch < m_midiChannels.size(); ch++) + { + MIDIchannel &chan = m_midiChannels[ch]; + chan.resetAllControllers(); + chan.volume = (m_synth.m_musicMode == OPL3::MODE_RSXX) ? 127 : 100; + chan.vibpos = 0.0; + chan.lastlrpn = 0; + chan.lastmrpn = 0; + chan.nrpn = false; + if((m_synthMode & Mode_GS) != 0)// Reset custom drum channels on GS + chan.is_xg_percussion = false; + noteUpdateAll(uint16_t(ch), Upd_All); + noteUpdateAll(uint16_t(ch), Upd_Off); + } + m_masterVolume = MasterVolumeDefault; +} + +bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) +{ + if(note >= 128) + note = 127; + + if((m_synth.m_musicMode == OPL3::MODE_RSXX) && (velocity != 0)) + { + // Check if this is just a note after-touch + MIDIchannel::activenoteiterator i = m_midiChannels[channel].activenotes_find(note); + if(i) + { + i->vol = velocity; + noteUpdate(channel, i, Upd_Volume); + return false; + } + } + + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + noteOff(channel, note); + // On Note on, Keyoff the note first, just in case keyoff + // was omitted; this fixes Dance of sugar-plum fairy + // by Microsoft. Now that we've done a Keyoff, + // check if we still need to do a Keyon. + // vol=0 and event 8x are both Keyoff-only. + if(velocity == 0) + return false; + + MIDIchannel &midiChan = m_midiChannels[channel]; + + size_t midiins = midiChan.patch; + bool isPercussion = (channel % 16 == 9) || midiChan.is_xg_percussion; + + size_t bank = 0; + if(midiChan.bank_msb || midiChan.bank_lsb) + { + if((m_synthMode & Mode_GS) != 0) //in GS mode ignore LSB + bank = (midiChan.bank_msb * 256); + else + bank = (midiChan.bank_msb * 256) + midiChan.bank_lsb; + } + + if(isPercussion) + { + // == XG bank numbers == + // 0x7E00 - XG "SFX Kits" SFX1/SFX2 channel (16128 signed decimal) + // 0x7F00 - XG "Drum Kits" Percussion channel (16256 signed decimal) + + // MIDI instrument defines the patch: + if((m_synthMode & Mode_XG) != 0) + { + // Let XG SFX1/SFX2 bank will go in 128...255 range of LSB in WOPN file) + // Let XG Percussion bank will use (0...127 LSB range in WOPN file) + + // Choose: SFX or Drum Kits + bank = midiins + ((bank == 0x7E00) ? 128 : 0); + } + else + { + bank = midiins; + } + midiins = note; // Percussion instrument + } + + if(isPercussion) + bank += OPL3::PercussionTag; + + const adlinsdata2 *ains = &OPL3::m_emptyInstrument; + + //Set bank bank + const OPL3::Bank *bnk = NULL; + if((bank & ~(uint16_t)OPL3::PercussionTag) > 0) + { + OPL3::BankMap::iterator b = m_synth.m_insBanks.find(bank); + if(b != m_synth.m_insBanks.end()) + bnk = &b->second; + + if(bnk) + ains = &bnk->ins[midiins]; + else if(hooks.onDebugMessage) + { + std::set &missing = (isPercussion) ? + caugh_missing_banks_percussion : caugh_missing_banks_melodic; + const char *text = (isPercussion) ? + "percussion" : "melodic"; + if(missing.insert(bank).second) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Playing missing %s MIDI bank %i (patch %i)", channel, text, bank, midiins); + } + } + //Or fall back to first bank + if(ains->flags & adlinsdata::Flag_NoSound) + { + OPL3::BankMap::iterator b = m_synth.m_insBanks.find(bank & OPL3::PercussionTag); + if(b != m_synth.m_insBanks.end()) + bnk = &b->second; + + if(bnk) + ains = &bnk->ins[midiins]; + } + + int32_t tone = note; + if(!isPercussion && (bank > 0)) // For non-zero banks + { + if(ains->flags & adlinsdata::Flag_NoSound) + { + if(hooks.onDebugMessage) + { + if(caugh_missing_instruments.insert(static_cast(midiins)).second) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Caught a blank instrument %i (offset %i) in the MIDI bank %u", channel, m_midiChannels[channel].patch, midiins, bank); + } + bank = 0; + midiins = midiChan.patch; + } + } + + if(ains->tone) + { + /*if(ains->tone < 20) + tone += ains->tone; + else*/ + if(ains->tone < 128) + tone = ains->tone; + else + tone -= ains->tone - 128; + } + + //uint16_t i[2] = { ains->adlno1, ains->adlno2 }; + bool pseudo_4op = ains->flags & adlinsdata::Flag_Pseudo4op; +#ifndef __WATCOMC__ + MIDIchannel::NoteInfo::Phys voices[MIDIchannel::NoteInfo::MaxNumPhysChans] = + { + {0, ains->adl[0], false}, + {0, ains->adl[1], pseudo_4op} + }; +#else /* Unfortunately, WatCom can't brace-initialize structure that incluses structure fields */ + MIDIchannel::NoteInfo::Phys voices[MIDIchannel::NoteInfo::MaxNumPhysChans]; + voices[0].chip_chan = 0; + voices[0].ains = ains->adl[0]; + voices[0].pseudo4op = false; + voices[1].chip_chan = 0; + voices[1].ains = ains->adl[1]; + voices[1].pseudo4op = pseudo_4op; +#endif /* __WATCOMC__ */ + + if((m_synth.m_rhythmMode == 1) && PercussionMap[midiins & 0xFF]) + voices[1] = voices[0];//i[1] = i[0]; + + bool isBlankNote = (ains->flags & adlinsdata::Flag_NoSound) != 0; + + if(hooks.onDebugMessage) + { + if(isBlankNote && caugh_missing_instruments.insert(static_cast(midiins)).second) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Playing missing instrument %i", channel, midiins); + } + + if(isBlankNote) + { + // Don't even try to play the blank instrument! But, insert the dummy note. + std::pair + dummy = midiChan.activenotes_insert(note); + dummy.first->isBlank = true; + dummy.first->ains = NULL; + dummy.first->chip_channels_count = 0; + // Record the last note on MIDI channel as source of portamento + midiChan.portamentoSource = static_cast(note); + return false; + } + + // Allocate AdLib channel (the physical sound channel for the note) + int32_t adlchannel[MIDIchannel::NoteInfo::MaxNumPhysChans] = { -1, -1 }; + + for(uint32_t ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount) + { + if(ccount == 1) + { + if(voices[0] == voices[1]) + break; // No secondary channel + if(adlchannel[0] == -1) + break; // No secondary if primary failed + } + + int32_t c = -1; + int32_t bs = -0x7FFFFFFFl; + + for(size_t a = 0; a < (size_t)m_synth.m_numChannels; ++a) + { + if(ccount == 1 && static_cast(a) == adlchannel[0]) continue; + // ^ Don't use the same channel for primary&secondary + + if(voices[0].ains == voices[1].ains || pseudo_4op/*i[0] == i[1] || pseudo_4op*/) + { + // Only use regular channels + uint32_t expected_mode = 0; + + if(m_synth.m_rhythmMode) + { + if(m_cmfPercussionMode) + expected_mode = channel < 11 ? 0 : (3 + channel - 11); // CMF + else + expected_mode = PercussionMap[midiins & 0xFF]; + } + + if(m_synth.m_channelCategory[a] != expected_mode) + continue; + } + else + { + if(ccount == 0) + { + // Only use four-op master channels + if(m_synth.m_channelCategory[a] != OPL3::ChanCat_4op_Master) + continue; + } + else + { + // The secondary must be played on a specific channel. + if(a != static_cast(adlchannel[0]) + 3) + continue; + } + } + + int64_t s = calculateChipChannelGoodness(a, voices[ccount]); + if(s > bs) + { + bs = (int32_t)s; // Best candidate wins + c = static_cast(a); + } + } + + if(c < 0) + { + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, + "ignored unplaceable note [bank %i, inst %i, note %i, MIDI channel %i]", + bank, midiChan.patch, note, channel); + continue; // Could not play this note. Ignore it. + } + + prepareChipChannelForNewNote(static_cast(c), voices[ccount]); + adlchannel[ccount] = c; + } + + if(adlchannel[0] < 0 && adlchannel[1] < 0) + { + // The note could not be played, at all. + return false; + } + + //if(hooks.onDebugMessage) + // hooks.onDebugMessage(hooks.onDebugMessage_userData, "i1=%d:%d, i2=%d:%d", i[0],adlchannel[0], i[1],adlchannel[1]); + + if(midiChan.softPedal) // Apply Soft Pedal level reducing + velocity = static_cast(std::floor(static_cast(velocity) * 0.8f)); + + // Allocate active note for MIDI channel + std::pair + ir = midiChan.activenotes_insert(note); + ir.first->vol = velocity; + ir.first->vibrato = midiChan.noteAftertouch[note]; + ir.first->noteTone = static_cast(tone); + ir.first->currentTone = tone; + ir.first->glideRate = HUGE_VAL; + ir.first->midiins = midiins; + ir.first->isPercussion = isPercussion; + ir.first->isBlank = isBlankNote; + ir.first->ains = ains; + ir.first->chip_channels_count = 0; + + int8_t currentPortamentoSource = midiChan.portamentoSource; + double currentPortamentoRate = midiChan.portamentoRate; + bool portamentoEnable = + midiChan.portamentoEnable && currentPortamentoRate != HUGE_VAL && !isPercussion; + // Record the last note on MIDI channel as source of portamento + midiChan.portamentoSource = static_cast(note); + // midiChan.portamentoSource = portamentoEnable ? (int8_t)note : (int8_t)-1; + + // Enable gliding on portamento note + if (portamentoEnable && currentPortamentoSource >= 0) + { + ir.first->currentTone = currentPortamentoSource; + ir.first->glideRate = currentPortamentoRate; + ++midiChan.gliding_note_count; + } + + for(unsigned ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount) + { + int32_t c = adlchannel[ccount]; + if(c < 0) + continue; + uint16_t chipChan = static_cast(adlchannel[ccount]); + ir.first->phys_ensure_find_or_create(chipChan)->assign(voices[ccount]); + } + + noteUpdate(channel, ir.first, Upd_All | Upd_Patch); + return true; +} + +void MIDIplay::realTime_NoteOff(uint8_t channel, uint8_t note) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + noteOff(channel, note); +} + +void MIDIplay::realTime_NoteAfterTouch(uint8_t channel, uint8_t note, uint8_t atVal) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + MIDIchannel &chan = m_midiChannels[channel]; + MIDIchannel::activenoteiterator i = m_midiChannels[channel].activenotes_find(note); + if(i) + { + i->vibrato = atVal; + } + + uint8_t oldAtVal = chan.noteAftertouch[note % 128]; + if(atVal != oldAtVal) + { + chan.noteAftertouch[note % 128] = atVal; + bool inUse = atVal != 0; + for(unsigned n = 0; !inUse && n < 128; ++n) + inUse = chan.noteAftertouch[n] != 0; + chan.noteAfterTouchInUse = inUse; + } +} + +void MIDIplay::realTime_ChannelAfterTouch(uint8_t channel, uint8_t atVal) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].aftertouch = atVal; +} + +void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + switch(type) + { + case 1: // Adjust vibrato + //UI.PrintLn("%u:vibrato %d", MidCh,value); + m_midiChannels[channel].vibrato = value; + break; + + case 0: // Set bank msb (GM bank) + m_midiChannels[channel].bank_msb = value; + if((m_synthMode & Mode_GS) == 0)// Don't use XG drums on GS synth mode + m_midiChannels[channel].is_xg_percussion = isXgPercChannel(m_midiChannels[channel].bank_msb, m_midiChannels[channel].bank_lsb); + break; + + case 32: // Set bank lsb (XG bank) + m_midiChannels[channel].bank_lsb = value; + if((m_synthMode & Mode_GS) == 0)// Don't use XG drums on GS synth mode + m_midiChannels[channel].is_xg_percussion = isXgPercChannel(m_midiChannels[channel].bank_msb, m_midiChannels[channel].bank_lsb); + break; + + case 5: // Set portamento msb + m_midiChannels[channel].portamento = static_cast((m_midiChannels[channel].portamento & 0x007F) | (value << 7)); + updatePortamento(channel); + break; + + case 37: // Set portamento lsb + m_midiChannels[channel].portamento = static_cast((m_midiChannels[channel].portamento & 0x3F80) | (value)); + updatePortamento(channel); + break; + + case 65: // Enable/disable portamento + m_midiChannels[channel].portamentoEnable = value >= 64; + updatePortamento(channel); + break; + + case 7: // Change volume + m_midiChannels[channel].volume = value; + noteUpdateAll(channel, Upd_Volume); + break; + + case 74: // Change brightness + m_midiChannels[channel].brightness = value; + noteUpdateAll(channel, Upd_Volume); + break; + + case 64: // Enable/disable sustain + m_midiChannels[channel].sustain = (value >= 64); + if(!m_midiChannels[channel].sustain) + killSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_Pedal); + break; + + case 66: // Enable/disable sostenuto + if(value >= 64) //Find notes and mark them as sostenutoed + markSostenutoNotes(channel); + else + killSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_Sostenuto); + break; + + case 67: // Enable/disable soft-pedal + m_midiChannels[channel].softPedal = (value >= 64); + break; + + case 11: // Change expression (another volume factor) + m_midiChannels[channel].expression = value; + noteUpdateAll(channel, Upd_Volume); + break; + + case 10: // Change panning + m_midiChannels[channel].panning = 0x00; + if(value < 64 + 32) m_midiChannels[channel].panning |= OPL_PANNING_LEFT; + if(value >= 64 - 32) m_midiChannels[channel].panning |= OPL_PANNING_RIGHT; + + noteUpdateAll(channel, Upd_Pan); + break; + + case 121: // Reset all controllers + m_midiChannels[channel].resetAllControllers(); + noteUpdateAll(channel, Upd_Pan + Upd_Volume + Upd_Pitch); + // Kill all sustained notes + killSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_ANY); + break; + + case 120: // All sounds off + noteUpdateAll(channel, Upd_OffMute); + break; + + case 123: // All notes off + noteUpdateAll(channel, Upd_Off); + break; + + case 91: + break; // Reverb effect depth. We don't do per-channel reverb. + + case 92: + break; // Tremolo effect depth. We don't do... + + case 93: + break; // Chorus effect depth. We don't do. + + case 94: + break; // Celeste effect depth. We don't do. + + case 95: + break; // Phaser effect depth. We don't do. + + case 98: + m_midiChannels[channel].lastlrpn = value; + m_midiChannels[channel].nrpn = true; + break; + + case 99: + m_midiChannels[channel].lastmrpn = value; + m_midiChannels[channel].nrpn = true; + break; + + case 100: + m_midiChannels[channel].lastlrpn = value; + m_midiChannels[channel].nrpn = false; + break; + + case 101: + m_midiChannels[channel].lastmrpn = value; + m_midiChannels[channel].nrpn = false; + break; + + case 113: + break; // Related to pitch-bender, used by missimp.mid in Duke3D + + case 6: + setRPN(channel, value, true); + break; + + case 38: + setRPN(channel, value, false); + break; + + case 103: + if(m_synth.m_musicMode == OPL3::MODE_CMF) + m_cmfPercussionMode = (value != 0); + break; // CMF (ctrl 0x67) rhythm mode + + default: + break; + //UI.PrintLn("Ctrl %d <- %d (ch %u)", ctrlno, value, MidCh); + } +} + +void MIDIplay::realTime_PatchChange(uint8_t channel, uint8_t patch) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].patch = patch; +} + +void MIDIplay::realTime_PitchBend(uint8_t channel, uint16_t pitch) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bend = int(pitch) - 8192; + noteUpdateAll(channel, Upd_Pitch); +} + +void MIDIplay::realTime_PitchBend(uint8_t channel, uint8_t msb, uint8_t lsb) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bend = int(lsb) + int(msb) * 128 - 8192; + noteUpdateAll(channel, Upd_Pitch); +} + +void MIDIplay::realTime_BankChangeLSB(uint8_t channel, uint8_t lsb) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bank_lsb = lsb; +} + +void MIDIplay::realTime_BankChangeMSB(uint8_t channel, uint8_t msb) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bank_msb = msb; +} + +void MIDIplay::realTime_BankChange(uint8_t channel, uint16_t bank) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bank_lsb = uint8_t(bank & 0xFF); + m_midiChannels[channel].bank_msb = uint8_t((bank >> 8) & 0xFF); +} + +void MIDIplay::setDeviceId(uint8_t id) +{ + m_sysExDeviceId = id; +} + +bool MIDIplay::realTime_SysEx(const uint8_t *msg, size_t size) +{ + if(size < 4 || msg[0] != 0xF0 || msg[size - 1] != 0xF7) + return false; + + unsigned manufacturer = msg[1]; + unsigned dev = msg[2]; + msg += 3; + size -= 4; + + switch(manufacturer) + { + default: + break; + case Manufacturer_UniversalNonRealtime: + case Manufacturer_UniversalRealtime: + return doUniversalSysEx( + dev, manufacturer == Manufacturer_UniversalRealtime, msg, size); + case Manufacturer_Roland: + return doRolandSysEx(dev, msg, size); + case Manufacturer_Yamaha: + return doYamahaSysEx(dev, msg, size); + } + + return false; +} + +bool MIDIplay::doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data, size_t size) +{ + bool devicematch = dev == 0x7F || dev == m_sysExDeviceId; + if(size < 2 || !devicematch) + return false; + + unsigned address = + (((unsigned)data[0] & 0x7F) << 8) | + (((unsigned)data[1] & 0x7F)); + data += 2; + size -= 2; + + switch(((unsigned)realtime << 16) | address) + { + case (0 << 16) | 0x0901: // GM System On + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: GM System On"); + m_synthMode = Mode_GM; + realTime_ResetState(); + return true; + case (0 << 16) | 0x0902: // GM System Off + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: GM System Off"); + m_synthMode = Mode_XG;//TODO: TEMPORARY, make something RIGHT + realTime_ResetState(); + return true; + case (1 << 16) | 0x0401: // MIDI Master Volume + if(size != 2) + break; + unsigned volume = + (((unsigned)data[0] & 0x7F)) | + (((unsigned)data[1] & 0x7F) << 7); + m_masterVolume = static_cast(volume >> 7); + for(size_t ch = 0; ch < m_midiChannels.size(); ch++) + noteUpdateAll(uint16_t(ch), Upd_Volume); + return true; + } + + return false; +} + +bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, size_t size) +{ + bool devicematch = dev == 0x7F || (dev & 0x0F) == m_sysExDeviceId; + if(size < 6 || !devicematch) + return false; + + unsigned model = data[0] & 0x7F; + unsigned mode = data[1] & 0x7F; + unsigned checksum = data[size - 1] & 0x7F; + data += 2; + size -= 3; + +#if !defined(ADLMIDI_SKIP_ROLAND_CHECKSUM) + { + unsigned checkvalue = 0; + for(size_t i = 0; i < size; ++i) + checkvalue += data[i] & 0x7F; + checkvalue = (128 - (checkvalue & 127)) & 127; + if(checkvalue != checksum) + { + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught invalid roland SysEx message!"); + return false; + } + } +#endif + + unsigned address = + (((unsigned)data[0] & 0x7F) << 16) | + (((unsigned)data[1] & 0x7F) << 8) | + (((unsigned)data[2] & 0x7F)); + unsigned target_channel = 0; + + /* F0 41 10 42 12 40 00 7F 00 41 F7 */ + + if((address & 0xFFF0FF) == 0x401015) // Turn channel 1 into percussion + { + address = 0x401015; + target_channel = data[1] & 0x0F; + } + + data += 3; + size -= 3; + + if(mode != RolandMode_Send) // don't have MIDI-Out reply ability + return false; + + // Mode Set + // F0 {41 10 42 12} {40 00 7F} {00 41} F7 + + // Custom drum channels + // F0 {41 10 42 12} {40 1 15} { } F7 + + switch((model << 24) | address) + { + case (RolandModel_GS << 24) | 0x00007F: // System Mode Set + { + if(size != 1 || (dev & 0xF0) != 0x10) + break; + unsigned mode = data[0] & 0x7F; + ADL_UNUSED(mode);//TODO: Hook this correctly! + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Roland System Mode Set: %02X", mode); + m_synthMode = Mode_GS; + realTime_ResetState(); + return true; + } + case (RolandModel_GS << 24) | 0x40007F: // Mode Set + { + if(size != 1 || (dev & 0xF0) != 0x10) + break; + unsigned value = data[0] & 0x7F; + ADL_UNUSED(value);//TODO: Hook this correctly! + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Roland Mode Set: %02X", value); + m_synthMode = Mode_GS; + realTime_ResetState(); + return true; + } + case (RolandModel_GS << 24) | 0x401015: // Percussion channel + { + if(size != 1 || (dev & 0xF0) != 0x10) + break; + if(m_midiChannels.size() < 16) + break; + unsigned value = data[0] & 0x7F; + const uint8_t channels_map[16] = + { + 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15 + }; + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, + "SysEx: Caught Roland Percussion set: %02X on channel %u (from %X)", + value, channels_map[target_channel], target_channel); + m_midiChannels[channels_map[target_channel]].is_xg_percussion = ((value == 0x01)) || ((value == 0x02)); + return true; + } + } + + return false; +} + +bool MIDIplay::doYamahaSysEx(unsigned dev, const uint8_t *data, size_t size) +{ + bool devicematch = dev == 0x7F || (dev & 0x0F) == m_sysExDeviceId; + if(size < 1 || !devicematch) + return false; + + unsigned model = data[0] & 0x7F; + ++data; + --size; + + switch((model << 8) | (dev & 0xF0)) + { + case (YamahaModel_XG << 8) | 0x10: // parameter change + { + if(size < 3) + break; + + unsigned address = + (((unsigned)data[0] & 0x7F) << 16) | + (((unsigned)data[1] & 0x7F) << 8) | + (((unsigned)data[2] & 0x7F)); + data += 3; + size -= 3; + + switch(address) + { + case 0x00007E: // XG System On + if(size != 1) + break; + unsigned value = data[0] & 0x7F; + ADL_UNUSED(value);//TODO: Hook this correctly! + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Yamaha XG System On: %02X", value); + m_synthMode = Mode_XG; + realTime_ResetState(); + return true; + } + + break; + } + } + + return false; +} + +void MIDIplay::realTime_panic() +{ + panic(); + killSustainingNotes(-1, -1, AdlChannel::LocationData::Sustain_ANY); +} + +void MIDIplay::realTime_deviceSwitch(size_t track, const char *data, size_t length) +{ + const std::string indata(data, length); + m_currentMidiDevice[track] = chooseDevice(indata); +} + +size_t MIDIplay::realTime_currentDevice(size_t track) +{ + if(m_currentMidiDevice.empty()) + return 0; + return m_currentMidiDevice[track]; +} + +void MIDIplay::realTime_rawOPL(uint8_t reg, uint8_t value) +{ + if((reg & 0xF0) == 0xC0) + value |= 0x30; + //std::printf("OPL poke %02X, %02X\n", reg, value); + //std::fflush(stdout); + m_synth.writeReg(0, reg, value); +} + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) +void MIDIplay::AudioTick(uint32_t chipId, uint32_t rate) +{ + if(chipId != 0) // do first chip ticks only + return; + + uint32_t tickNumber = m_audioTickCounter++; + double timeDelta = 1.0 / rate; + + enum { portamentoInterval = 32 }; // for efficiency, set rate limit on pitch updates + + if(tickNumber % portamentoInterval == 0) + { + double portamentoDelta = timeDelta * portamentoInterval; + updateGlide(portamentoDelta); + } +} +#endif + +void MIDIplay::noteUpdate(size_t midCh, + MIDIplay::MIDIchannel::activenoteiterator i, + unsigned props_mask, + int32_t select_adlchn) +{ + MIDIchannel::NoteInfo &info = *i; + const int16_t noteTone = info.noteTone; + const double currentTone = info.currentTone; + const uint8_t vol = info.vol; + const int midiins = static_cast(info.midiins); + const adlinsdata2 &ains = *info.ains; + AdlChannel::Location my_loc; + my_loc.MidCh = static_cast(midCh); + my_loc.note = info.note; + + if(info.isBlank) + { + if(props_mask & Upd_Off) + m_midiChannels[midCh].activenotes_erase(i); + return; + } + + for(unsigned ccount = 0, ctotal = info.chip_channels_count; ccount < ctotal; ccount++) + { + const MIDIchannel::NoteInfo::Phys &ins = info.chip_channels[ccount]; + uint16_t c = ins.chip_chan; + + if(select_adlchn >= 0 && c != select_adlchn) continue; + + if(props_mask & Upd_Patch) + { + m_synth.setPatch(c, ins.ains); + AdlChannel::LocationData *d = m_chipChannels[c].users_find_or_create(my_loc); + if(d) // inserts if necessary + { + d->sustained = AdlChannel::LocationData::Sustain_None; + d->vibdelay = 0; + d->fixed_sustain = (ains.ms_sound_kon == static_cast(adlNoteOnMaxTime)); + d->kon_time_until_neglible = ains.ms_sound_kon; + d->ins = ins; + } + } + } + + for(unsigned ccount = 0; ccount < info.chip_channels_count; ccount++) + { + const MIDIchannel::NoteInfo::Phys &ins = info.chip_channels[ccount]; + uint16_t c = ins.chip_chan; + + if(select_adlchn >= 0 && c != select_adlchn) + continue; + + if(props_mask & Upd_Off) // note off + { + if(!m_midiChannels[midCh].sustain) + { + AdlChannel::LocationData *k = m_chipChannels[c].users_find(my_loc); + bool do_erase_user = (k && ((k->sustained & AdlChannel::LocationData::Sustain_Sostenuto) == 0)); + if(do_erase_user) + m_chipChannels[c].users_erase(k); + + if(hooks.onNote) + hooks.onNote(hooks.onNote_userData, c, noteTone, midiins, 0, 0.0); + + if(do_erase_user && m_chipChannels[c].users_empty()) + { + m_synth.noteOff(c); + if(props_mask & Upd_Mute) // Mute the note + { + m_synth.touchNote(c, 0); + m_chipChannels[c].koff_time_until_neglible = 0; + } + else + { + m_chipChannels[c].koff_time_until_neglible = ains.ms_sound_koff; + } + } + } + else + { + // Sustain: Forget about the note, but don't key it off. + // Also will avoid overwriting it very soon. + AdlChannel::LocationData *d = m_chipChannels[c].users_find_or_create(my_loc); + if(d) + d->sustained |= AdlChannel::LocationData::Sustain_Pedal; // note: not erased! + if(hooks.onNote) + hooks.onNote(hooks.onNote_userData, c, noteTone, midiins, -1, 0.0); + } + + info.phys_erase_at(&ins); // decrements channel count + --ccount; // adjusts index accordingly + continue; + } + + if(props_mask & Upd_Pan) + m_synth.setPan(c, m_midiChannels[midCh].panning); + + if(props_mask & Upd_Volume) + { + uint_fast32_t volume; + bool is_percussion = (midCh == 9) || m_midiChannels[midCh].is_xg_percussion; + uint_fast32_t brightness = is_percussion ? 127 : m_midiChannels[midCh].brightness; + + if(!m_setup.fullRangeBrightnessCC74) + { + // Simulate post-High-Pass filter result which affects sounding by half level only + if(brightness >= 64) + brightness = 127; + else + brightness *= 2; + } + + switch(m_synth.m_volumeScale) + { + default: + case OPL3::VOLUME_Generic: + { + volume = vol * m_masterVolume * m_midiChannels[midCh].volume * m_midiChannels[midCh].expression; + + /* If the channel has arpeggio, the effective volume of + * *this* instrument is actually lower due to timesharing. + * To compensate, add extra volume that corresponds to the + * time this note is *not* heard. + * Empirical tests however show that a full equal-proportion + * increment sounds wrong. Therefore, using the square root. + */ + //volume = (int)(volume * std::sqrt( (double) ch[c].users.size() )); + + // The formula below: SOLVE(V=127^4 * 2^( (A-63.49999) / 8), A) + volume = volume > (8725 * 127) ? static_cast(std::log(static_cast(volume)) * 11.541560327111707 - 1.601379199767093e+02) : 0; + // The incorrect formula below: SOLVE(V=127^4 * (2^(A/63)-1), A) + //opl.Touch_Real(c, volume>(11210*127) ? 91.61112 * std::log((4.8819E-7/127)*volume + 1.0)+0.5 : 0); + } + break; + + case OPL3::VOLUME_NATIVE: + { + volume = vol * m_midiChannels[midCh].volume * m_midiChannels[midCh].expression; + // volume = volume * m_masterVolume / (127 * 127 * 127) / 2; + volume = (volume * m_masterVolume) / 4096766; + } + break; + + case OPL3::VOLUME_DMX: + { + volume = 2 * (m_midiChannels[midCh].volume * m_midiChannels[midCh].expression * m_masterVolume / 16129) + 1; + //volume = 2 * (Ch[MidCh].volume) + 1; + volume = (DMX_volume_mapping_table[(vol < 128) ? vol : 127] * volume) >> 9; + } + break; + + case OPL3::VOLUME_APOGEE: + { + volume = (m_midiChannels[midCh].volume * m_midiChannels[midCh].expression * m_masterVolume / 16129); + volume = ((64 * (vol + 0x80)) * volume) >> 15; + //volume = ((63 * (vol + 0x80)) * Ch[MidCh].volume) >> 15; + } + break; + + case OPL3::VOLUME_9X: + { + //volume = 63 - W9X_volume_mapping_table[(((vol * Ch[MidCh].volume /** Ch[MidCh].expression*/) * m_masterVolume / 16129 /*2048383*/) >> 2)]; + volume = 63 - W9X_volume_mapping_table[((vol * m_midiChannels[midCh].volume * m_midiChannels[midCh].expression * m_masterVolume / 2048383) >> 2)]; + //volume = W9X_volume_mapping_table[vol >> 2] + volume; + } + break; + } + + m_synth.touchNote(c, static_cast(volume), static_cast(brightness)); + + /* DEBUG ONLY!!! + static uint32_t max = 0; + + if(volume == 0) + max = 0; + + if(volume > max) + max = volume; + + printf("%d\n", max); + fflush(stdout); + */ + } + + if(props_mask & Upd_Pitch) + { + AdlChannel::LocationData *d = m_chipChannels[c].users_find(my_loc); + + // Don't bend a sustained note + if(!d || (d->sustained == AdlChannel::LocationData::Sustain_None)) + { + double midibend = m_midiChannels[midCh].bend * m_midiChannels[midCh].bendsense; + double bend = midibend + ins.ains.finetune; + double phase = 0.0; + uint8_t vibrato = std::max(m_midiChannels[midCh].vibrato, m_midiChannels[midCh].aftertouch); + vibrato = std::max(vibrato, i->vibrato); + + if((ains.flags & adlinsdata::Flag_Pseudo4op) && ins.pseudo4op) + { + phase = ains.voice2_fine_tune;//0.125; // Detune the note slightly (this is what Doom does) + } + + if(vibrato && (!d || d->vibdelay >= m_midiChannels[midCh].vibdelay)) + bend += static_cast(vibrato) * m_midiChannels[midCh].vibdepth * std::sin(m_midiChannels[midCh].vibpos); + +#define BEND_COEFFICIENT 172.4387 + m_synth.noteOn(c, BEND_COEFFICIENT * std::exp(0.057762265 * (currentTone + bend + phase))); +#undef BEND_COEFFICIENT + if(hooks.onNote) + hooks.onNote(hooks.onNote_userData, c, noteTone, midiins, vol, midibend); + } + } + } + + if(info.chip_channels_count == 0) + { + if(i->glideRate != HUGE_VAL) + --m_midiChannels[midCh].gliding_note_count; + m_midiChannels[midCh].activenotes_erase(i); + } +} + +void MIDIplay::noteUpdateAll(size_t midCh, unsigned props_mask) +{ + for(MIDIchannel::activenoteiterator + i = m_midiChannels[midCh].activenotes_begin(); i;) + { + MIDIchannel::activenoteiterator j(i++); + noteUpdate(midCh, j, props_mask); + } +} + +const std::string &MIDIplay::getErrorString() +{ + return errorStringOut; +} + +void MIDIplay::setErrorString(const std::string &err) +{ + errorStringOut = err; +} + +int64_t MIDIplay::calculateChipChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins) const +{ + int64_t s = (m_synth.m_musicMode != OPL3::MODE_CMF) ? -m_chipChannels[c].koff_time_until_neglible : 0; + + // Same midi-instrument = some stability + //if(c == MidCh) s += 4; + for(AdlChannel::LocationData *j = m_chipChannels[c].users_first; j; j = j->next) + { + s -= 4000; + + if(j->sustained == AdlChannel::LocationData::Sustain_None) + s -= j->kon_time_until_neglible; + else + s -= (j->kon_time_until_neglible / 2); + + MIDIchannel::activenoteiterator + k = const_cast(m_midiChannels[j->loc.MidCh]).activenotes_find(j->loc.note); + + if(k) + { + // Same instrument = good + if(j->ins == ins) + { + s += 300; + // Arpeggio candidate = even better + if(j->vibdelay < 70 + || j->kon_time_until_neglible > 20000) + s += 0; + } + + // Percussion is inferior to melody + s += k->isPercussion ? 50 : 0; + /* + if(k->second.midiins >= 25 + && k->second.midiins < 40 + && j->second.ins != ins) + { + s -= 14000; // HACK: Don't clobber the bass or the guitar + } + */ + } + + // If there is another channel to which this note + // can be evacuated to in the case of congestion, + // increase the score slightly. + unsigned n_evacuation_stations = 0; + + for(size_t c2 = 0; c2 < static_cast(m_synth.m_numChannels); ++c2) + { + if(c2 == c) continue; + + if(m_synth.m_channelCategory[c2] + != m_synth.m_channelCategory[c]) continue; + + for(AdlChannel::LocationData *m = m_chipChannels[c2].users_first; m; m = m->next) + { + if(m->sustained != AdlChannel::LocationData::Sustain_None) continue; + if(m->vibdelay >= 200) continue; + if(m->ins != j->ins) continue; + n_evacuation_stations += 1; + } + } + + s += (int64_t)n_evacuation_stations * 4; + } + + return s; +} + + +void MIDIplay::prepareChipChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins) +{ + if(m_chipChannels[c].users_empty()) return; // Nothing to do + + //bool doing_arpeggio = false; + for(AdlChannel::LocationData *jnext = m_chipChannels[c].users_first; jnext;) + { + AdlChannel::LocationData *j = jnext; + jnext = jnext->next; + + if(j->sustained == AdlChannel::LocationData::Sustain_None) + { + // Collision: Kill old note, + // UNLESS we're going to do arpeggio + MIDIchannel::activenoteiterator i + (m_midiChannels[j->loc.MidCh].activenotes_ensure_find(j->loc.note)); + + // Check if we can do arpeggio. + if((j->vibdelay < 70 + || j->kon_time_until_neglible > 20000) + && j->ins == ins) + { + // Do arpeggio together with this note. + //doing_arpeggio = true; + continue; + } + + killOrEvacuate(c, j, i); + // ^ will also erase j from ch[c].users. + } + } + + // Kill all sustained notes on this channel + // Don't keep them for arpeggio, because arpeggio requires + // an intact "activenotes" record. This is a design flaw. + killSustainingNotes(-1, static_cast(c), AdlChannel::LocationData::Sustain_ANY); + + // Keyoff the channel so that it can be retriggered, + // unless the new note will be introduced as just an arpeggio. + if(m_chipChannels[c].users_empty()) + m_synth.noteOff(c); +} + +void MIDIplay::killOrEvacuate(size_t from_channel, + AdlChannel::LocationData *j, + MIDIplay::MIDIchannel::activenoteiterator i) +{ + // Before killing the note, check if it can be + // evacuated to another channel as an arpeggio + // instrument. This helps if e.g. all channels + // are full of strings and we want to do percussion. + // FIXME: This does not care about four-op entanglements. + for(uint32_t c = 0; c < m_synth.m_numChannels; ++c) + { + uint16_t cs = static_cast(c); + + if(c > std::numeric_limits::max()) + break; + if(c == from_channel) + continue; + if(m_synth.m_channelCategory[c] != m_synth.m_channelCategory[from_channel]) + continue; + + AdlChannel &adlch = m_chipChannels[c]; + if(adlch.users_size == AdlChannel::users_max) + continue; // no room for more arpeggio on channel + + for(AdlChannel::LocationData *m = adlch.users_first; m; m = m->next) + { + if(m->vibdelay >= 200 + && m->kon_time_until_neglible < 10000) continue; + if(m->ins != j->ins) + continue; + if(hooks.onNote) + { + hooks.onNote(hooks.onNote_userData, + (int)from_channel, + i->noteTone, + static_cast(i->midiins), 0, 0.0); + hooks.onNote(hooks.onNote_userData, + (int)c, + i->noteTone, + static_cast(i->midiins), + i->vol, 0.0); + } + + i->phys_erase(static_cast(from_channel)); + i->phys_ensure_find_or_create(cs)->assign(j->ins); + if(!m_chipChannels[cs].users_insert(*j)) + assert(false); + m_chipChannels[from_channel].users_erase(j); + return; + } + } + + /*UI.PrintLn( + "collision @%u: [%ld] <- ins[%3u]", + c, + //ch[c].midiins<128?'M':'P', ch[c].midiins&127, + ch[c].age, //adlins[ch[c].insmeta].ms_sound_kon, + ins + );*/ + // Kill it + noteUpdate(j->loc.MidCh, + i, + Upd_Off, + static_cast(from_channel)); +} + +void MIDIplay::panic() +{ + for(uint8_t chan = 0; chan < m_midiChannels.size(); chan++) + { + for(uint8_t note = 0; note < 128; note++) + realTime_NoteOff(chan, note); + } +} + +void MIDIplay::killSustainingNotes(int32_t midCh, int32_t this_adlchn, uint32_t sustain_type) +{ + uint32_t first = 0, last = m_synth.m_numChannels; + + if(this_adlchn >= 0) + { + first = static_cast(this_adlchn); + last = first + 1; + } + + for(uint32_t c = first; c < last; ++c) + { + if(m_chipChannels[c].users_empty()) + continue; // Nothing to do + + for(AdlChannel::LocationData *jnext = m_chipChannels[c].users_first; jnext;) + { + AdlChannel::LocationData *j = jnext; + jnext = jnext->next; + + if((midCh < 0 || j->loc.MidCh == midCh) + && ((j->sustained & sustain_type) != 0)) + { + int midiins = '?'; + if(hooks.onNote) + hooks.onNote(hooks.onNote_userData, (int)c, j->loc.note, midiins, 0, 0.0); + j->sustained &= ~sustain_type; + if(j->sustained == AdlChannel::LocationData::Sustain_None) + m_chipChannels[c].users_erase(j);//Remove only when note is clean from any holders + } + } + + // Keyoff the channel, if there are no users left. + if(m_chipChannels[c].users_empty()) + m_synth.noteOff(c); + } +} + +void MIDIplay::markSostenutoNotes(int32_t midCh) +{ + uint32_t first = 0, last = m_synth.m_numChannels; + for(uint32_t c = first; c < last; ++c) + { + if(m_chipChannels[c].users_empty()) + continue; // Nothing to do + + for(AdlChannel::LocationData *jnext = m_chipChannels[c].users_first; jnext;) + { + AdlChannel::LocationData *j = jnext; + jnext = jnext->next; + if((j->loc.MidCh == midCh) && (j->sustained == AdlChannel::LocationData::Sustain_None)) + j->sustained |= AdlChannel::LocationData::Sustain_Sostenuto; + } + } +} + +void MIDIplay::setRPN(size_t midCh, unsigned value, bool MSB) +{ + bool nrpn = m_midiChannels[midCh].nrpn; + unsigned addr = m_midiChannels[midCh].lastmrpn * 0x100 + m_midiChannels[midCh].lastlrpn; + + switch(addr + nrpn * 0x10000 + MSB * 0x20000) + { + case 0x0000 + 0*0x10000 + 1*0x20000: // Pitch-bender sensitivity + m_midiChannels[midCh].bendsense_msb = value; + m_midiChannels[midCh].updateBendSensitivity(); + break; + case 0x0000 + 0*0x10000 + 0*0x20000: // Pitch-bender sensitivity LSB + m_midiChannels[midCh].bendsense_lsb = value; + m_midiChannels[midCh].updateBendSensitivity(); + break; + case 0x0108 + 1*0x10000 + 1*0x20000: + if((m_synthMode & Mode_XG) != 0) // Vibrato speed + { + if(value == 64) m_midiChannels[midCh].vibspeed = 1.0; + else if(value < 100) m_midiChannels[midCh].vibspeed = 1.0 / (1.6e-2 * (value ? value : 1)); + else m_midiChannels[midCh].vibspeed = 1.0 / (0.051153846 * value - 3.4965385); + m_midiChannels[midCh].vibspeed *= 2 * 3.141592653 * 5.0; + } + break; + case 0x0109 + 1*0x10000 + 1*0x20000: + if((m_synthMode & Mode_XG) != 0) // Vibrato depth + { + m_midiChannels[midCh].vibdepth = ((value - 64) * 0.15) * 0.01; + } + break; + case 0x010A + 1*0x10000 + 1*0x20000: + if((m_synthMode & Mode_XG) != 0) // Vibrato delay in millisecons + { + m_midiChannels[midCh].vibdelay = value ? int64_t(0.2092 * std::exp(0.0795 * (double)value)) : 0; + } + break; + default:/* UI.PrintLn("%s %04X <- %d (%cSB) (ch %u)", + "NRPN"+!nrpn, addr, value, "LM"[MSB], MidCh);*/ + break; + } +} + +void MIDIplay::updatePortamento(size_t midCh) +{ + double rate = HUGE_VAL; + uint16_t midival = m_midiChannels[midCh].portamento; + if(m_midiChannels[midCh].portamentoEnable && midival > 0) + rate = 350.0 * std::pow(2.0, -0.062 * (1.0 / 128) * midival); + m_midiChannels[midCh].portamentoRate = rate; +} + + +void MIDIplay::noteOff(size_t midCh, uint8_t note) +{ + MIDIchannel::activenoteiterator + i = m_midiChannels[midCh].activenotes_find(note); + if(i) + noteUpdate(midCh, i, Upd_Off); +} + + +void MIDIplay::updateVibrato(double amount) +{ + for(size_t a = 0, b = m_midiChannels.size(); a < b; ++a) + { + if(m_midiChannels[a].hasVibrato() && !m_midiChannels[a].activenotes_empty()) + { + noteUpdateAll(static_cast(a), Upd_Pitch); + m_midiChannels[a].vibpos += amount * m_midiChannels[a].vibspeed; + } + else + m_midiChannels[a].vibpos = 0.0; + } +} + +size_t MIDIplay::chooseDevice(const std::string &name) +{ + std::map::iterator i = m_midiDevices.find(name); + + if(i != m_midiDevices.end()) + return i->second; + + size_t n = m_midiDevices.size() * 16; + m_midiDevices.insert(std::make_pair(name, n)); + m_midiChannels.resize(n + 16); + return n; +} + +void MIDIplay::updateArpeggio(double) // amount = amount of time passed +{ + // If there is an adlib channel that has multiple notes + // simulated on the same channel, arpeggio them. +#if 0 + const unsigned desired_arpeggio_rate = 40; // Hz (upper limit) +# if 1 + static unsigned cache = 0; + amount = amount; // Ignore amount. Assume we get a constant rate. + cache += MaxSamplesAtTime * desired_arpeggio_rate; + + if(cache < PCM_RATE) return; + + cache %= PCM_RATE; +# else + static double arpeggio_cache = 0; + arpeggio_cache += amount * desired_arpeggio_rate; + + if(arpeggio_cache < 1.0) return; + + arpeggio_cache = 0.0; +# endif +#endif + + ++m_arpeggioCounter; + + for(uint32_t c = 0; c < m_synth.m_numChannels; ++c) + { +retry_arpeggio: + if(c > uint32_t(std::numeric_limits::max())) + break; + + size_t n_users = m_chipChannels[c].users_size; + + if(n_users > 1) + { + AdlChannel::LocationData *i = m_chipChannels[c].users_first; + size_t rate_reduction = 3; + + if(n_users >= 3) + rate_reduction = 2; + + if(n_users >= 4) + rate_reduction = 1; + + for(size_t count = (m_arpeggioCounter / rate_reduction) % n_users, + n = 0; n < count; ++n) + i = i->next; + + if(i->sustained == AdlChannel::LocationData::Sustain_None) + { + if(i->kon_time_until_neglible <= 0l) + { + noteUpdate( + i->loc.MidCh, + m_midiChannels[ i->loc.MidCh ].activenotes_ensure_find(i->loc.note), + Upd_Off, + static_cast(c)); + goto retry_arpeggio; + } + + noteUpdate( + i->loc.MidCh, + m_midiChannels[ i->loc.MidCh ].activenotes_ensure_find(i->loc.note), + Upd_Pitch | Upd_Volume | Upd_Pan, + static_cast(c)); + } + } + } +} + +void MIDIplay::updateGlide(double amount) +{ + size_t num_channels = m_midiChannels.size(); + + for(size_t channel = 0; channel < num_channels; ++channel) + { + MIDIchannel &midiChan = m_midiChannels[channel]; + if(midiChan.gliding_note_count == 0) + continue; + + for(MIDIchannel::activenoteiterator it = midiChan.activenotes_begin(); + it; ++it) + { + double finalTone = it->noteTone; + double previousTone = it->currentTone; + + bool directionUp = previousTone < finalTone; + double toneIncr = amount * (directionUp ? +it->glideRate : -it->glideRate); + + double currentTone = previousTone + toneIncr; + bool glideFinished = !(directionUp ? (currentTone < finalTone) : (currentTone > finalTone)); + currentTone = glideFinished ? finalTone : currentTone; + + if(currentTone != previousTone) + { + it->currentTone = currentTone; + noteUpdate(static_cast(channel), it, Upd_Pitch); + } + } + } +} + +void MIDIplay::describeChannels(char *str, char *attr, size_t size) +{ + if (!str || size <= 0) + return; + + OPL3 &synth = m_synth; + uint32_t numChannels = synth.m_numChannels; + + uint32_t index = 0; + while(index < numChannels && index < size - 1) + { + const AdlChannel &adlChannel = m_chipChannels[index]; + + AdlChannel::LocationData *loc = adlChannel.users_first; + if(!loc) // off + { + str[index] = '-'; + } + else if(loc->next) // arpeggio + { + str[index] = '@'; + } + else // on + { + switch(synth.m_channelCategory[index]) + { + case OPL3::ChanCat_Regular: + str[index] = '+'; + break; + case OPL3::ChanCat_4op_Master: + case OPL3::ChanCat_4op_Slave: + str[index] = '#'; + break; + default: // rhythm-mode percussion + str[index] = 'r'; + break; + } + } + + uint8_t attribute = 0; + if (loc) // 4-bit color index of MIDI channel + attribute |= (uint8_t)(loc->loc.MidCh & 0xF); + + attr[index] = (char)attribute; + ++index; + } + + str[index] = 0; + attr[index] = 0; +} + +#ifndef ADLMIDI_DISABLE_CPP_EXTRAS + +struct AdlInstrumentTester::Impl +{ + uint32_t cur_gm; + uint32_t ins_idx; + std::vector adl_ins_list; + OPL3 *opl; + MIDIplay *play; +}; + +ADLMIDI_EXPORT AdlInstrumentTester::AdlInstrumentTester(ADL_MIDIPlayer *device) + : P(new Impl) +{ +#ifndef DISABLE_EMBEDDED_BANKS + MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + P->cur_gm = 0; + P->ins_idx = 0; + P->play = play; + P->opl = play ? &play->m_synth : NULL; +#else + ADL_UNUSED(device); +#endif +} + +ADLMIDI_EXPORT AdlInstrumentTester::~AdlInstrumentTester() +{ + delete P; +} + +ADLMIDI_EXPORT void AdlInstrumentTester::FindAdlList() +{ +#ifndef DISABLE_EMBEDDED_BANKS + const unsigned NumBanks = (unsigned)adl_getBanksCount(); + std::set adl_ins_set; + for(unsigned bankno = 0; bankno < NumBanks; ++bankno) + adl_ins_set.insert(banks[bankno][P->cur_gm]); + P->adl_ins_list.assign(adl_ins_set.begin(), adl_ins_set.end()); + P->ins_idx = 0; + NextAdl(0); + P->opl->silenceAll(); +#endif +} + + + +ADLMIDI_EXPORT void AdlInstrumentTester::Touch(unsigned c, unsigned volume) // Volume maxes at 127*127*127 +{ +#ifndef DISABLE_EMBEDDED_BANKS + OPL3 *opl = P->opl; + if(opl->m_volumeScale == OPL3::VOLUME_NATIVE) + opl->touchNote(c, static_cast(volume * 127 / (127 * 127 * 127) / 2)); + else + { + // The formula below: SOLVE(V=127^3 * 2^( (A-63.49999) / 8), A) + opl->touchNote(c, static_cast(volume > 8725 ? static_cast(std::log((double)volume) * 11.541561 + (0.5 - 104.22845)) : 0)); + // The incorrect formula below: SOLVE(V=127^3 * (2^(A/63)-1), A) + //Touch_Real(c, volume>11210 ? 91.61112 * std::log(4.8819E-7*volume + 1.0)+0.5 : 0); + } +#else + ADL_UNUSED(c); + ADL_UNUSED(volume); +#endif +} + +ADLMIDI_EXPORT void AdlInstrumentTester::DoNote(int note) +{ +#ifndef DISABLE_EMBEDDED_BANKS + MIDIplay *play = P->play; + OPL3 *opl = P->opl; + if(P->adl_ins_list.empty()) FindAdlList(); + const unsigned meta = P->adl_ins_list[P->ins_idx]; + const adlinsdata2 ains(adlins[meta]); + + int tone = (P->cur_gm & 128) ? (P->cur_gm & 127) : (note + 50); + if(ains.tone) + { + /*if(ains.tone < 20) + tone += ains.tone; + else */ + if(ains.tone < 128) + tone = ains.tone; + else + tone -= ains.tone - 128; + } + double hertz = 172.00093 * std::exp(0.057762265 * (tone + 0.0)); + int32_t adlchannel[2] = { 0, 3 }; + if(ains.adl[0] == ains.adl[1]) + { + adlchannel[1] = -1; + adlchannel[0] = 6; // single-op + if(play->hooks.onDebugMessage) + { + play->hooks.onDebugMessage(play->hooks.onDebugMessage_userData, + "noteon at %d for %g Hz\n", adlchannel[0], hertz); + } + } + else + { + if(play->hooks.onDebugMessage) + { + play->hooks.onDebugMessage(play->hooks.onDebugMessage_userData, + "noteon at %d and %d for %g Hz\n", adlchannel[0], adlchannel[1], hertz); + } + } + + opl->noteOff(0); + opl->noteOff(3); + opl->noteOff(6); + for(unsigned c = 0; c < 2; ++c) + { + if(adlchannel[c] < 0) continue; + opl->setPatch(static_cast(adlchannel[c]), ains.adl[c]); + opl->touchNote(static_cast(adlchannel[c]), 63); + opl->setPan(static_cast(adlchannel[c]), 0x30); + opl->noteOn(static_cast(adlchannel[c]), hertz); + } +#else + ADL_UNUSED(note); +#endif +} + +ADLMIDI_EXPORT void AdlInstrumentTester::NextGM(int offset) +{ +#ifndef DISABLE_EMBEDDED_BANKS + P->cur_gm = (P->cur_gm + 256 + (uint32_t)offset) & 0xFF; + FindAdlList(); +#else + ADL_UNUSED(offset); +#endif +} + +ADLMIDI_EXPORT void AdlInstrumentTester::NextAdl(int offset) +{ +#ifndef DISABLE_EMBEDDED_BANKS + //OPL3 *opl = P->opl; + if(P->adl_ins_list.empty()) FindAdlList(); + const unsigned NumBanks = (unsigned)adl_getBanksCount(); + P->ins_idx = (uint32_t)((int32_t)P->ins_idx + (int32_t)P->adl_ins_list.size() + offset) % (int32_t)P->adl_ins_list.size(); + +#if 0 + UI.Color(15); + std::fflush(stderr); + std::printf("SELECTED G%c%d\t%s\n", + cur_gm < 128 ? 'M' : 'P', cur_gm < 128 ? cur_gm + 1 : cur_gm - 128, + "<-> select GM, ^v select ins, qwe play note"); + std::fflush(stdout); + UI.Color(7); + std::fflush(stderr); +#endif + + for(size_t a = 0, n = P->adl_ins_list.size(); a < n; ++a) + { + const unsigned i = P->adl_ins_list[a]; + const adlinsdata2 ains(adlins[i]); + + char ToneIndication[8] = " "; + if(ains.tone) + { + /*if(ains.tone < 20) + snprintf(ToneIndication, 8, "+%-2d", ains.tone); + else*/ + if(ains.tone < 128) + snprintf(ToneIndication, 8, "=%-2d", ains.tone); + else + snprintf(ToneIndication, 8, "-%-2d", ains.tone - 128); + } + std::printf("%s%s%s%u\t", + ToneIndication, + ains.adl[0] != ains.adl[1] ? "[2]" : " ", + (P->ins_idx == a) ? "->" : "\t", + i + ); + + for(unsigned bankno = 0; bankno < NumBanks; ++bankno) + if(banks[bankno][P->cur_gm] == i) + std::printf(" %u", bankno); + + std::printf("\n"); + } +#else + ADL_UNUSED(offset); +#endif +} + +ADLMIDI_EXPORT bool AdlInstrumentTester::HandleInputChar(char ch) +{ +#ifndef DISABLE_EMBEDDED_BANKS + static const char notes[] = "zsxdcvgbhnjmq2w3er5t6y7ui9o0p"; + // c'd'ef'g'a'bC'D'EF'G'A'Bc'd'e + switch(ch) + { + case '/': + case 'H': + case 'A': + NextAdl(-1); + break; + case '*': + case 'P': + case 'B': + NextAdl(+1); + break; + case '-': + case 'K': + case 'D': + NextGM(-1); + break; + case '+': + case 'M': + case 'C': + NextGM(+1); + break; + case 3: +#if !((!defined(__WIN32__) || defined(__CYGWIN__)) && !defined(__DJGPP__)) + case 27: +#endif + return false; + default: + const char *p = std::strchr(notes, ch); + if(p && *p) + DoNote((int)(p - notes) - 12); + } +#else + ADL_UNUSED(ch); +#endif + return true; +} + +#endif /* ADLMIDI_DISABLE_CPP_EXTRAS */ + +// Implement the user map data structure. + +bool MIDIplay::AdlChannel::users_empty() const +{ + return !users_first; +} + +MIDIplay::AdlChannel::LocationData *MIDIplay::AdlChannel::users_find(Location loc) +{ + LocationData *user = NULL; + for(LocationData *curr = users_first; !user && curr; curr = curr->next) + if(curr->loc == loc) + user = curr; + return user; +} + +MIDIplay::AdlChannel::LocationData *MIDIplay::AdlChannel::users_allocate() +{ + // remove free cells front + LocationData *user = users_free_cells; + if(!user) + return NULL; + users_free_cells = user->next; + if(users_free_cells) + users_free_cells->prev = NULL; + // add to users front + if(users_first) + users_first->prev = user; + user->prev = NULL; + user->next = users_first; + users_first = user; + ++users_size; + return user; +} + +MIDIplay::AdlChannel::LocationData *MIDIplay::AdlChannel::users_find_or_create(Location loc) +{ + LocationData *user = users_find(loc); + if(!user) + { + user = users_allocate(); + if(!user) + return NULL; + LocationData *prev = user->prev, *next = user->next; + *user = LocationData(); + user->prev = prev; + user->next = next; + user->loc = loc; + } + return user; +} + +MIDIplay::AdlChannel::LocationData *MIDIplay::AdlChannel::users_insert(const LocationData &x) +{ + LocationData *user = users_find(x.loc); + if(!user) + { + user = users_allocate(); + if(!user) + return NULL; + LocationData *prev = user->prev, *next = user->next; + *user = x; + user->prev = prev; + user->next = next; + } + return user; +} + +void MIDIplay::AdlChannel::users_erase(LocationData *user) +{ + if(user->prev) + user->prev->next = user->next; + if(user->next) + user->next->prev = user->prev; + if(user == users_first) + users_first = user->next; + user->prev = NULL; + user->next = users_free_cells; + users_free_cells = user; + --users_size; +} + +void MIDIplay::AdlChannel::users_clear() +{ + users_first = NULL; + users_free_cells = users_cells; + users_size = 0; + for(size_t i = 0; i < users_max; ++i) + { + users_cells[i].prev = (i > 0) ? &users_cells[i - 1] : NULL; + users_cells[i].next = (i + 1 < users_max) ? &users_cells[i + 1] : NULL; + } +} + +void MIDIplay::AdlChannel::users_assign(const LocationData *users, size_t count) +{ + ADL_UNUSED(count);//Avoid warning for release builds + assert(count <= users_max); + if(users == users_first && users) + { + // self assignment + assert(users_size == count); + return; + } + users_clear(); + const LocationData *src_cell = users; + // move to the last + if(src_cell) + { + while(src_cell->next) + src_cell = src_cell->next; + } + // push cell copies in reverse order + while(src_cell) + { + LocationData *dst_cell = users_allocate(); + assert(dst_cell); + LocationData *prev = dst_cell->prev, *next = dst_cell->next; + *dst_cell = *src_cell; + dst_cell->prev = prev; + dst_cell->next = next; + src_cell = src_cell->prev; + } + assert(users_size == count); +} diff --git a/engine/src/Libraries/adlmidi/adlmidi_opl3.cpp b/engine/src/Libraries/adlmidi/adlmidi_opl3.cpp new file mode 100644 index 0000000..3e33e86 --- /dev/null +++ b/engine/src/Libraries/adlmidi/adlmidi_opl3.cpp @@ -0,0 +1,585 @@ +/* + * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "adlmidi_private.hpp" + +#ifdef ADLMIDI_HW_OPL +static const unsigned OPLBase = 0x388; +#else +# if defined(ADLMIDI_DISABLE_NUKED_EMULATOR) && defined(ADLMIDI_DISABLE_DOSBOX_EMULATOR) +# error "No emulators enabled. You must enable at least one emulator to use this library!" +# endif + +// Nuked OPL3 emulator, Most accurate, but requires the powerful CPU +# ifndef ADLMIDI_DISABLE_NUKED_EMULATOR +# include "chips/nuked_opl3.h" +# include "chips/nuked_opl3_v174.h" +# endif + +// DosBox 0.74 OPL3 emulator, Well-accurate and fast +# ifndef ADLMIDI_DISABLE_DOSBOX_EMULATOR +# include "chips/dosbox_opl3.h" +# endif +#endif + +//! Per-channel and per-operator registers map +static const uint16_t g_operatorsMap[23 * 2] = +{ + // Channels 0-2 + 0x000, 0x003, 0x001, 0x004, 0x002, 0x005, // operators 0, 3, 1, 4, 2, 5 + // Channels 3-5 + 0x008, 0x00B, 0x009, 0x00C, 0x00A, 0x00D, // operators 6, 9, 7,10, 8,11 + // Channels 6-8 + 0x010, 0x013, 0x011, 0x014, 0x012, 0x015, // operators 12,15, 13,16, 14,17 + // Same for second card + 0x100, 0x103, 0x101, 0x104, 0x102, 0x105, // operators 18,21, 19,22, 20,23 + 0x108, 0x10B, 0x109, 0x10C, 0x10A, 0x10D, // operators 24,27, 25,28, 26,29 + 0x110, 0x113, 0x111, 0x114, 0x112, 0x115, // operators 30,33, 31,34, 32,35 + // Channel 18 + 0x010, 0x013, // operators 12,15 + // Channel 19 + 0x014, 0xFFF, // operator 16 + // Channel 19 + 0x012, 0xFFF, // operator 14 + // Channel 19 + 0x015, 0xFFF, // operator 17 + // Channel 19 + 0x011, 0xFFF +}; // operator 13 + +//! Channel map to regoster offsets +static const uint16_t g_channelsMap[23] = +{ + 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, 0x008, // 0..8 + 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108, // 9..17 (secondary set) + 0x006, 0x007, 0x008, 0xFFF, 0xFFF +}; // <- hw percussions, 0xFFF = no support for pitch/pan + +/* + In OPL3 mode: + 0 1 2 6 7 8 9 10 11 16 17 18 + op0 op1 op2 op12 op13 op14 op18 op19 op20 op30 op31 op32 + op3 op4 op5 op15 op16 op17 op21 op22 op23 op33 op34 op35 + 3 4 5 13 14 15 + op6 op7 op8 op24 op25 op26 + op9 op10 op11 op27 op28 op29 + Ports: + +0 +1 +2 +10 +11 +12 +100 +101 +102 +110 +111 +112 + +3 +4 +5 +13 +14 +15 +103 +104 +105 +113 +114 +115 + +8 +9 +A +108 +109 +10A + +B +C +D +10B +10C +10D + + Percussion: + bassdrum = op(0): 0xBD bit 0x10, operators 12 (0x10) and 15 (0x13) / channels 6, 6b + snare = op(3): 0xBD bit 0x08, operators 16 (0x14) / channels 7b + tomtom = op(4): 0xBD bit 0x04, operators 14 (0x12) / channels 8 + cym = op(5): 0xBD bit 0x02, operators 17 (0x17) / channels 8b + hihat = op(2): 0xBD bit 0x01, operators 13 (0x11) / channels 7 + + + In OPTi mode ("extended FM" in 82C924, 82C925, 82C931 chips): + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + op0 op4 op6 op10 op12 op16 op18 op22 op24 op28 op30 op34 op36 op38 op40 op42 op44 op46 + op1 op5 op7 op11 op13 op17 op19 op23 op25 op29 op31 op35 op37 op39 op41 op43 op45 op47 + op2 op8 op14 op20 op26 op32 + op3 op9 op15 op21 op27 op33 for a total of 6 quad + 12 dual + Ports: ??? +*/ + +static adlinsdata2 makeEmptyInstrument() +{ + adlinsdata2 ins; + memset(&ins, 0, sizeof(adlinsdata2)); + ins.flags = adlinsdata::Flag_NoSound; + return ins; +} + +const adlinsdata2 OPL3::m_emptyInstrument = makeEmptyInstrument(); + +OPL3::OPL3() : + m_numChips(1), + m_numFourOps(0), + m_deepTremoloMode(false), + m_deepVibratoMode(false), + m_rhythmMode(false), + m_musicMode(MODE_MIDI), + m_volumeScale(VOLUME_Generic) +{ +#ifdef DISABLE_EMBEDDED_BANKS + m_embeddedBank = CustomBankTag; +#else + setEmbeddedBank(0); +#endif +} + +void OPL3::setEmbeddedBank(uint32_t bank) +{ +#ifndef DISABLE_EMBEDDED_BANKS + m_embeddedBank = bank; + //Embedded banks are supports 128:128 GM set only + m_insBanks.clear(); + + if(bank >= static_cast(maxAdlBanks())) + return; + + Bank *bank_pair[2] = + { + &m_insBanks[0], + &m_insBanks[PercussionTag] + }; + + for(unsigned i = 0; i < 256; ++i) + { + size_t meta = banks[bank][i]; + adlinsdata2 &ins = bank_pair[i / 128]->ins[i % 128]; + ins = adlinsdata2(adlins[meta]); + } +#else + ADL_UNUSED(bank); +#endif +} + +void OPL3::writeReg(size_t chip, uint16_t address, uint8_t value) +{ +#ifdef ADLMIDI_HW_OPL + ADL_UNUSED(chip); + unsigned o = address >> 8; + unsigned port = OPLBase + o * 2; + + #ifdef __DJGPP__ + outportb(port, address); + for(unsigned c = 0; c < 6; ++c) inportb(port); + outportb(port + 1, value); + for(unsigned c = 0; c < 35; ++c) inportb(port); + #endif + + #ifdef __WATCOMC__ + outp(port, address); + for(uint16_t c = 0; c < 6; ++c) inp(port); + outp(port + 1, value); + for(uint16_t c = 0; c < 35; ++c) inp(port); + #endif//__WATCOMC__ + +#else//ADLMIDI_HW_OPL + m_chips[chip]->writeReg(address, value); +#endif +} + +void OPL3::writeRegI(size_t chip, uint32_t address, uint32_t value) +{ +#ifdef ADLMIDI_HW_OPL + writeReg(chip, static_cast(address), static_cast(value)); +#else//ADLMIDI_HW_OPL + m_chips[chip]->writeReg(static_cast(address), static_cast(value)); +#endif +} + + +void OPL3::noteOff(size_t c) +{ + size_t chip = c / 23, cc = c % 23; + + if(cc >= 18) + { + m_regBD[chip] &= ~(0x10 >> (cc - 18)); + writeRegI(chip, 0xBD, m_regBD[chip]); + return; + } + + writeRegI(chip, 0xB0 + g_channelsMap[cc], m_keyBlockFNumCache[c] & 0xDF); +} + +void OPL3::noteOn(size_t c, double hertz) // Hertz range: 0..131071 +{ + size_t chip = c / 23, cc = c % 23; + uint32_t x = 0x2000; + + if(hertz < 0 || hertz > 131071) // Avoid infinite loop + return; + + while(hertz >= 1023.5) + { + hertz /= 2.0; // Calculate octave + x += 0x400; + } + + x += static_cast(hertz + 0.5); + uint32_t chn = g_channelsMap[cc]; + + if(cc >= 18) + { + m_regBD[chip ] |= (0x10 >> (cc - 18)); + writeRegI(chip , 0x0BD, m_regBD[chip ]); + x &= ~0x2000u; + //x |= 0x800; // for test + } + + if(chn != 0xFFF) + { + writeRegI(chip , 0xA0 + chn, (x & 0xFF)); + writeRegI(chip , 0xB0 + chn, (x >> 8)); + m_keyBlockFNumCache[c] = (x >> 8); + } +} + +void OPL3::touchNote(size_t c, uint8_t volume, uint8_t brightness) +{ + if(volume > 63) + volume = 63; + + size_t chip = c / 23, cc = c % 23; + const adldata &adli = m_insCache[c]; + uint16_t o1 = g_operatorsMap[cc * 2 + 0]; + uint16_t o2 = g_operatorsMap[cc * 2 + 1]; + uint8_t x = adli.modulator_40, y = adli.carrier_40; + uint32_t mode = 1; // 2-op AM + + if(m_channelCategory[c] == 0 || m_channelCategory[c] == 3) + { + mode = adli.feedconn & 1; // 2-op FM or 2-op AM + } + else if(m_channelCategory[c] == 1 || m_channelCategory[c] == 2) + { + const adldata *i0, *i1; + + if(m_channelCategory[c] == 1) + { + i0 = &adli; + i1 = &m_insCache[c + 3]; + mode = 2; // 4-op xx-xx ops 1&2 + } + else + { + i0 = &m_insCache[c - 3]; + i1 = &adli; + mode = 6; // 4-op xx-xx ops 3&4 + } + + mode += (i0->feedconn & 1) + (i1->feedconn & 1) * 2; + } + + static const bool do_ops[10][2] = + { + { false, true }, /* 2 op FM */ + { true, true }, /* 2 op AM */ + { false, false }, /* 4 op FM-FM ops 1&2 */ + { true, false }, /* 4 op AM-FM ops 1&2 */ + { false, true }, /* 4 op FM-AM ops 1&2 */ + { true, false }, /* 4 op AM-AM ops 1&2 */ + { false, true }, /* 4 op FM-FM ops 3&4 */ + { false, true }, /* 4 op AM-FM ops 3&4 */ + { false, true }, /* 4 op FM-AM ops 3&4 */ + { true, true } /* 4 op AM-AM ops 3&4 */ + }; + + if(m_musicMode == MODE_RSXX) + { + writeRegI(chip, 0x40 + o1, x); + if(o2 != 0xFFF) + writeRegI(chip, 0x40 + o2, y - volume / 2); + } + else + { + bool do_modulator = do_ops[ mode ][ 0 ] || m_scaleModulators; + bool do_carrier = do_ops[ mode ][ 1 ] || m_scaleModulators; + + uint32_t modulator = do_modulator ? (x | 63) - volume + volume * (x & 63) / 63 : x; + uint32_t carrier = do_carrier ? (y | 63) - volume + volume * (y & 63) / 63 : y; + + if(brightness != 127) + { + brightness = static_cast(::round(127.0 * ::sqrt((static_cast(brightness)) * (1.0 / 127.0))) / 2.0); + if(!do_modulator) + modulator = (modulator | 63) - brightness + brightness * (modulator & 63) / 63; + if(!do_carrier) + carrier = (carrier | 63) - brightness + brightness * (carrier & 63) / 63; + } + + writeRegI(chip, 0x40 + o1, modulator); + if(o2 != 0xFFF) + writeRegI(chip, 0x40 + o2, carrier); + } + + // Correct formula (ST3, AdPlug): + // 63-((63-(instrvol))/63)*chanvol + // Reduces to (tested identical): + // 63 - chanvol + chanvol*instrvol/63 + // Also (slower, floats): + // 63 + chanvol * (instrvol / 63.0 - 1) +} + +/* +void OPL3::Touch(unsigned c, unsigned volume) // Volume maxes at 127*127*127 +{ + if(LogarithmicVolumes) + Touch_Real(c, volume * 127 / (127 * 127 * 127) / 2); + else + { + // The formula below: SOLVE(V=127^3 * 2^( (A-63.49999) / 8), A) + Touch_Real(c, volume > 8725 ? static_cast(std::log(volume) * 11.541561 + (0.5 - 104.22845)) : 0); + // The incorrect formula below: SOLVE(V=127^3 * (2^(A/63)-1), A) + //Touch_Real(c, volume>11210 ? 91.61112 * std::log(4.8819E-7*volume + 1.0)+0.5 : 0); + } +}*/ + +void OPL3::setPatch(size_t c, const adldata &instrument) +{ + size_t chip = c / 23, cc = c % 23; + static const uint8_t data[4] = {0x20, 0x60, 0x80, 0xE0}; + m_insCache[c] = instrument; + uint16_t o1 = g_operatorsMap[cc * 2 + 0]; + uint16_t o2 = g_operatorsMap[cc * 2 + 1]; + unsigned x = instrument.modulator_E862, y = instrument.carrier_E862; + + for(size_t a = 0; a < 4; ++a, x >>= 8, y >>= 8) + { + writeRegI(chip, data[a] + o1, x & 0xFF); + if(o2 != 0xFFF) + writeRegI(chip, data[a] + o2, y & 0xFF); + } +} + +void OPL3::setPan(size_t c, uint8_t value) +{ + size_t chip = c / 23, cc = c % 23; + if(g_channelsMap[cc] != 0xFFF) + writeRegI(chip, 0xC0 + g_channelsMap[cc], m_insCache[c].feedconn | value); +} + +void OPL3::silenceAll() // Silence all OPL channels. +{ + for(size_t c = 0; c < m_numChannels; ++c) + { + noteOff(c); + touchNote(c, 0); + } +} + +void OPL3::updateChannelCategories() +{ + uint32_t fours = m_numFourOps; + + for(size_t chip = 0; chip < m_numChips; ++chip) + { + m_regBD[chip] = (m_deepTremoloMode * 0x80 + m_deepVibratoMode * 0x40 + m_rhythmMode * 0x20); + writeRegI(chip, 0x0BD, m_regBD[chip]); + uint32_t fours_this_chip = std::min(fours, static_cast(6u)); + writeRegI(chip, 0x104, (1 << fours_this_chip) - 1); + fours -= fours_this_chip; + } + + // Mark all channels that are reserved for four-operator function + if(m_rhythmMode == 1) + { + for(size_t a = 0; a < m_numChips; ++a) + { + for(size_t b = 0; b < 5; ++b) + m_channelCategory[a * 23 + 18 + b] = static_cast(b + 3); + for(size_t b = 0; b < 3; ++b) + m_channelCategory[a * 23 + 6 + b] = ChanCat_Rhythm_Slave; + } + } + + size_t nextfour = 0; + for(size_t a = 0; a < m_numFourOps; ++a) + { + m_channelCategory[nextfour] = ChanCat_4op_Master; + m_channelCategory[nextfour + 3] = ChanCat_4op_Slave; + + switch(a % 6) + { + case 0: + case 1: + nextfour += 1; + break; + case 2: + nextfour += 9 - 2; + break; + case 3: + case 4: + nextfour += 1; + break; + case 5: + nextfour += 23 - 9 - 2; + break; + } + } + +/**/ +/* + In two-op mode, channels 0..8 go as follows: + Op1[port] Op2[port] + Channel 0: 00 00 03 03 + Channel 1: 01 01 04 04 + Channel 2: 02 02 05 05 + Channel 3: 06 08 09 0B + Channel 4: 07 09 10 0C + Channel 5: 08 0A 11 0D + Channel 6: 12 10 15 13 + Channel 7: 13 11 16 14 + Channel 8: 14 12 17 15 + In four-op mode, channels 0..8 go as follows: + Op1[port] Op2[port] Op3[port] Op4[port] + Channel 0: 00 00 03 03 06 08 09 0B + Channel 1: 01 01 04 04 07 09 10 0C + Channel 2: 02 02 05 05 08 0A 11 0D + Channel 3: CHANNEL 0 SLAVE + Channel 4: CHANNEL 1 SLAVE + Channel 5: CHANNEL 2 SLAVE + Channel 6: 12 10 15 13 + Channel 7: 13 11 16 14 + Channel 8: 14 12 17 15 + Same goes principally for channels 9-17 respectively. + */ +} + +void OPL3::commitDeepFlags() +{ + for(size_t chip = 0; chip < m_numChips; ++chip) + { + m_regBD[chip] = (m_deepTremoloMode * 0x80 + m_deepVibratoMode * 0x40 + m_rhythmMode * 0x20); + writeRegI(chip, 0x0BD, m_regBD[chip]); + } +} + +void OPL3::setVolumeScaleModel(ADLMIDI_VolumeModels volumeModel) +{ + switch(volumeModel) + { + case ADLMIDI_VolumeModel_AUTO://Do nothing until restart playing + break; + + case ADLMIDI_VolumeModel_Generic: + m_volumeScale = OPL3::VOLUME_Generic; + break; + + case ADLMIDI_VolumeModel_NativeOPL3: + m_volumeScale = OPL3::VOLUME_NATIVE; + break; + + case ADLMIDI_VolumeModel_DMX: + m_volumeScale = OPL3::VOLUME_DMX; + break; + + case ADLMIDI_VolumeModel_APOGEE: + m_volumeScale = OPL3::VOLUME_APOGEE; + break; + + case ADLMIDI_VolumeModel_9X: + m_volumeScale = OPL3::VOLUME_9X; + break; + } +} + +#ifndef ADLMIDI_HW_OPL +void OPL3::clearChips() +{ + for(size_t i = 0; i < m_chips.size(); i++) + m_chips[i].reset(NULL); + m_chips.clear(); +} +#endif + +void OPL3::reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler) +{ +#ifndef ADLMIDI_HW_OPL + clearChips(); +#else + (void)emulator; + (void)PCM_RATE; +#endif +#if !defined(ADLMIDI_AUDIO_TICK_HANDLER) + (void)audioTickHandler; +#endif + m_insCache.clear(); + m_keyBlockFNumCache.clear(); + m_regBD.clear(); + +#ifndef ADLMIDI_HW_OPL + m_chips.resize(m_numChips, AdlMIDI_SPtr()); +#endif + + const struct adldata defaultInsCache = { 0x1557403,0x005B381, 0x49,0x80, 0x4, +0 }; + m_numChannels = m_numChips * 23; + m_insCache.resize(m_numChannels, defaultInsCache); + m_keyBlockFNumCache.resize(m_numChannels, 0); + m_regBD.resize(m_numChips, 0); + m_channelCategory.resize(m_numChannels, 0); + + for(size_t p = 0, a = 0; a < m_numChips; ++a) + { + for(size_t b = 0; b < 18; ++b) + m_channelCategory[p++] = 0; + for(size_t b = 0; b < 5; ++b) + m_channelCategory[p++] = ChanCat_Rhythm_Slave; + } + + static const uint16_t data[] = + { + 0x004, 96, 0x004, 128, // Pulse timer + 0x105, 0, 0x105, 1, 0x105, 0, // Pulse OPL3 enable + 0x001, 32, 0x105, 1 // Enable wave, OPL3 extensions + }; +// size_t fours = m_numFourOps; + + for(size_t i = 0; i < m_numChips; ++i) + { +#ifndef ADLMIDI_HW_OPL + OPLChipBase *chip; + switch(emulator) + { + default: +#ifndef ADLMIDI_DISABLE_NUKED_EMULATOR + case ADLMIDI_EMU_NUKED: /* Latest Nuked OPL3 */ + chip = new NukedOPL3; + break; + case ADLMIDI_EMU_NUKED_174: /* Old Nuked OPL3 1.4.7 modified and optimized */ + chip = new NukedOPL3v174; + break; +#endif +#ifndef ADLMIDI_DISABLE_DOSBOX_EMULATOR + case ADLMIDI_EMU_DOSBOX: + chip = new DosBoxOPL3; + break; +#endif + } + m_chips[i].reset(chip); + chip->setChipId((uint32_t)i); + chip->setRate((uint32_t)PCM_RATE); + if(m_runAtPcmRate) + chip->setRunningAtPcmRate(true); +# if defined(ADLMIDI_AUDIO_TICK_HANDLER) + chip->setAudioTickHandlerInstance(audioTickHandler); +# endif +#endif // ADLMIDI_HW_OPL + + /* Clean-up channels from any playing junk sounds */ + for(size_t a = 0; a < 18; ++a) + writeRegI(i, 0xB0 + g_channelsMap[a], 0x00); + for(size_t a = 0; a < sizeof(data) / sizeof(*data); a += 2) + writeRegI(i, data[a], (data[a + 1])); + } + + updateChannelCategories(); + silenceAll(); +} diff --git a/engine/src/Libraries/adlmidi/adlmidi_private.cpp b/engine/src/Libraries/adlmidi/adlmidi_private.cpp new file mode 100644 index 0000000..ecedd9e --- /dev/null +++ b/engine/src/Libraries/adlmidi/adlmidi_private.cpp @@ -0,0 +1,108 @@ +/* + * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "adlmidi_private.hpp" + +std::string ADLMIDI_ErrorString; + +// Generator callback on audio rate ticks + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) +void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate) +{ + reinterpret_cast(instance)->AudioTick(chipId, rate); +} +#endif + +int adlRefreshNumCards(ADL_MIDIPlayer *device) +{ + size_t n_fourop[2] = {0, 0}, n_total[2] = {0, 0}; + MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + + //Automatically calculate how much 4-operator channels is necessary +#ifndef DISABLE_EMBEDDED_BANKS + if(play->m_synth.m_embeddedBank == OPL3::CustomBankTag) +#endif + { + //For custom bank + OPL3::BankMap::iterator it = play->m_synth.m_insBanks.begin(); + OPL3::BankMap::iterator end = play->m_synth.m_insBanks.end(); + for(; it != end; ++it) + { + size_t bank = it->first; + size_t div = (bank & OPL3::PercussionTag) ? 1 : 0; + for(size_t i = 0; i < 128; ++i) + { + adlinsdata2 &ins = it->second.ins[i]; + if(ins.flags & adlinsdata::Flag_NoSound) + continue; + if((ins.adl[0] != ins.adl[1]) && ((ins.flags & adlinsdata::Flag_Pseudo4op) == 0)) + ++n_fourop[div]; + ++n_total[div]; + } + } + } +#ifndef DISABLE_EMBEDDED_BANKS + else + { + //For embedded bank + for(size_t a = 0; a < 256; ++a) + { + size_t insno = banks[play->m_setup.bankId][a]; + if(insno == 198) + continue; + ++n_total[a / 128]; + adlinsdata2 ins(adlins[insno]); + if(ins.flags & adlinsdata::Flag_Real4op) + ++n_fourop[a / 128]; + } + } +#endif + + size_t numFourOps = 0; + + // All 2ops (no 4ops) + if((n_fourop[0] == 0) && (n_fourop[1] == 0)) + numFourOps = 0; + // All 2op melodics and Some (or All) 4op drums + else if((n_fourop[0] == 0) && (n_fourop[1] > 0)) + numFourOps = 2; + // Many 4op melodics + else if((n_fourop[0] >= (n_total[0] * 7) / 8)) + numFourOps = 6; + // Few 4op melodics + else if(n_fourop[0] > 0) + numFourOps = 4; + +/* //Old formula + unsigned NumFourOps = ((n_fourop[0] == 0) && (n_fourop[1] == 0)) ? 0 + : (n_fourop[0] >= (n_total[0] * 7) / 8) ? play->m_setup.NumCards * 6 + : (play->m_setup.NumCards == 1 ? 1 : play->m_setup.NumCards * 4); +*/ + + play->m_synth.m_numFourOps = play->m_setup.numFourOps = static_cast(numFourOps * play->m_setup.numChips); + // Update channel categories and set up four-operator channels + play->m_synth.updateChannelCategories(); + + return 0; +} diff --git a/engine/src/Libraries/adlmidi/adlmidi_private.hpp b/engine/src/Libraries/adlmidi/adlmidi_private.hpp new file mode 100644 index 0000000..9687023 --- /dev/null +++ b/engine/src/Libraries/adlmidi/adlmidi_private.hpp @@ -0,0 +1,1464 @@ +/* + * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ADLMIDI_PRIVATE_HPP +#define ADLMIDI_PRIVATE_HPP + +// Setup compiler defines useful for exporting required public API symbols in gme.cpp +#ifndef ADLMIDI_EXPORT +# if defined (_WIN32) && defined(ADLMIDI_BUILD_DLL) +# define ADLMIDI_EXPORT __declspec(dllexport) +# elif defined (LIBADLMIDI_VISIBILITY) +# define ADLMIDI_EXPORT __attribute__((visibility ("default"))) +# else +# define ADLMIDI_EXPORT +# endif +#endif + + +#ifdef _WIN32 +#define NOMINMAX 1 +#endif + +#if defined(_WIN32) && !defined(__WATCOMC__) +# undef NO_OLDNAMES +# include +# ifdef _MSC_VER +# ifdef _WIN64 +typedef __int64 ssize_t; +# else +typedef __int32 ssize_t; +# endif +# define NOMINMAX 1 //Don't override std::min and std::max +# else +# ifdef _WIN64 +typedef int64_t ssize_t; +# else +typedef int32_t ssize_t; +# endif +# endif +# include +#endif + +#if defined(__DJGPP__) || (defined(__WATCOMC__) && (defined(__DOS__) || defined(__DOS4G__) || defined(__DOS4GNZ__))) +#define ADLMIDI_HW_OPL +#include +#ifdef __DJGPP__ +#include +#include +#include +#include +#include +#endif + +#endif + +#include +#include +#include +//#ifdef __WATCOMC__ +//#include //TODO: Implemnet a workaround for OpenWatcom to fix a crash while using those containers +//#include +//#else +#include +#include +//#endif +#include +#include +#include +#include +#include +#include +#include // vector +#include // deque +#include // exp, log, ceil +#if defined(__WATCOMC__) +#include // round, sqrt +#endif +#include +#include +#include +#include // numeric_limit + +#ifndef _WIN32 +#include +#endif + +#include +#include + +/* + * Workaround for some compilers are has no those macros in their headers! + */ +#ifndef INT8_MIN +#define INT8_MIN (-0x7f - 1) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-0x7fff - 1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-0x7fffffff - 1) +#endif +#ifndef INT8_MAX +#define INT8_MAX 0x7f +#endif +#ifndef INT16_MAX +#define INT16_MAX 0x7fff +#endif +#ifndef INT32_MAX +#define INT32_MAX 0x7fffffff +#endif + +#include "file_reader.hpp" + +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER +// Rename class to avoid ABI collisions +#define BW_MidiSequencer AdlMidiSequencer +#include "midi_sequencer.hpp" +typedef BW_MidiSequencer MidiSequencer; +#endif//ADLMIDI_DISABLE_MIDI_SEQUENCER + +#ifndef ADLMIDI_HW_OPL +#include "chips/opl_chip_base.h" +#endif + +#include "adldata.hh" + +#include "adlmidi.h" //Main API + +#ifndef ADLMIDI_DISABLE_CPP_EXTRAS +#include "adlmidi.hpp" //Extra C++ API +#endif + +#include "adlmidi_ptr.hpp" +#include "adlmidi_bankmap.h" + +#define ADL_UNUSED(x) (void)x + +#define OPL_PANNING_LEFT 0x10 +#define OPL_PANNING_RIGHT 0x20 +#define OPL_PANNING_BOTH 0x30 + +extern std::string ADLMIDI_ErrorString; + +/* + Sample conversions to various formats +*/ +template +inline Real adl_cvtReal(int32_t x) +{ + return static_cast(x) * (static_cast(1) / static_cast(INT16_MAX)); +} + +inline int32_t adl_cvtS16(int32_t x) +{ + x = (x < INT16_MIN) ? (INT16_MIN) : x; + x = (x > INT16_MAX) ? (INT16_MAX) : x; + return x; +} + +inline int32_t adl_cvtS8(int32_t x) +{ + return adl_cvtS16(x) / 256; +} +inline int32_t adl_cvtS24(int32_t x) +{ + return adl_cvtS16(x) * 256; +} +inline int32_t adl_cvtS32(int32_t x) +{ + return adl_cvtS16(x) * 65536; +} +inline int32_t adl_cvtU16(int32_t x) +{ + return adl_cvtS16(x) - INT16_MIN; +} +inline int32_t adl_cvtU8(int32_t x) +{ + return (adl_cvtS16(x) / 256) - INT8_MIN; +} +inline int32_t adl_cvtU24(int32_t x) +{ + enum { int24_min = -(1 << 23) }; + return adl_cvtS24(x) - int24_min; +} +inline int32_t adl_cvtU32(int32_t x) +{ + // unsigned operation because overflow on signed integers is undefined + return (uint32_t)adl_cvtS32(x) - (uint32_t)INT32_MIN; +} + +struct ADL_MIDIPlayer; +/** + * @brief OPL3 Chip management class + */ +class OPL3 +{ + friend class MIDIplay; + friend class AdlInstrumentTester; + friend int adlRefreshNumCards(ADL_MIDIPlayer *device); +public: + enum + { + PercussionTag = 1 << 15, + CustomBankTag = 0xFFFFFFFF + }; + + //! Total number of chip channels between all running emulators + uint32_t m_numChannels; + //! Just a padding. Reserved. + char _padding[4]; +#ifndef ADLMIDI_HW_OPL + //! Running chip emulators + std::vector > m_chips; +#endif + +private: + //! Cached patch data, needed by Touch() + std::vector m_insCache; + //! Value written to B0, cached, needed by NoteOff. + /*! Contains Key on/off state, octave block and frequency number values + */ + std::vector m_keyBlockFNumCache; + //! Cached BD registry value (flags register: DeepTremolo, DeepVibrato, and RhythmMode) + std::vector m_regBD; + +public: + /** + * @brief MIDI bank entry + */ + struct Bank + { + //! MIDI Bank instruments + adlinsdata2 ins[128]; + }; + typedef BasicBankMap BankMap; + //! MIDI bank instruments data + BankMap m_insBanks; + //! MIDI bank-wide setup + AdlBankSetup m_insBankSetup; + +public: + //! Blank instrument template + static const adlinsdata2 m_emptyInstrument; + //! Total number of running concurrent emulated chips + uint32_t m_numChips; + //! Currently running embedded bank number. "CustomBankTag" means usign of the custom bank. + uint32_t m_embeddedBank; + //! Total number of needed four-operator channels in all running chips + uint32_t m_numFourOps; + //! Turn global Deep Tremolo mode on + bool m_deepTremoloMode; + //! Turn global Deep Vibrato mode on + bool m_deepVibratoMode; + //! Use Rhythm Mode percussions + bool m_rhythmMode; + //! Carriers-only are scaled by default by volume level. This flag will tell to scale modulators too. + bool m_scaleModulators; + //! Run emulator at PCM rate if that possible. Reduces sounding accuracy, but decreases CPU usage on lower rates. + bool m_runAtPcmRate; + + //! Just a padding. Reserved. + char _padding2[3]; + + /** + * @brief Music playing mode + */ + enum MusicMode + { + //! MIDI mode + MODE_MIDI, + //! Id-Software Music mode + MODE_IMF, + //! Creative Music Files mode + MODE_CMF, + //! EA-MUS (a.k.a. RSXX) mode + MODE_RSXX + } m_musicMode; + + /** + * @brief Volume models enum + */ + enum VolumesScale + { + //! Generic volume model (linearization of logarithmic scale) + VOLUME_Generic, + //! OPL3 native logarithmic scale + VOLUME_NATIVE, + //! DMX volume scale logarithmic table + VOLUME_DMX, + //! Apoge Sound System volume scaling model + VOLUME_APOGEE, + //! Windows 9x driver volume scale table + VOLUME_9X + } m_volumeScale; + + //! Reserved + char _padding3[8]; + + /** + * @brief Channel categiry enumeration + */ + enum ChanCat + { + //! Regular melodic/percussion channel + ChanCat_Regular = 0, + //! Four-op master + ChanCat_4op_Master = 1, + //! Four-op slave + ChanCat_4op_Slave = 2, + //! Rhythm-mode Bass drum + ChanCat_Rhythm_Bass = 3, + //! Rhythm-mode Snare drum + ChanCat_Rhythm_Snare = 4, + //! Rhythm-mode Tom-Tom + ChanCat_Rhythm_Tom = 5, + //! Rhythm-mode Cymbal + ChanCat_Rhythm_Cymbal = 6, + //! Rhythm-mode Hi-Hat + ChanCat_Rhythm_HiHat = 7, + //! Rhythm-mode Slave channel + ChanCat_Rhythm_Slave = 8 + }; + + //! Category of the channel + /*! 1 = quad-master, 2 = quad-slave, 0 = regular + 3 = percussion BassDrum + 4 = percussion Snare + 5 = percussion Tom + 6 = percussion Crash cymbal + 7 = percussion Hihat + 8 = percussion slave + */ + std::vector m_channelCategory; + + + /** + * @brief C.O. Constructor + */ + OPL3(); + + /** + * @brief Choose one of embedded banks + * @param bank ID of the bank + */ + void setEmbeddedBank(uint32_t bank); + + /** + * @brief Write data to OPL3 chip register + * @param chip Index of emulated chip. In hardware OPL3 builds, this parameter is ignored + * @param address Register address to write + * @param value Value to write + */ + void writeReg(size_t chip, uint16_t address, uint8_t value); + + /** + * @brief Write data to OPL3 chip register + * @param chip Index of emulated chip. In hardware OPL3 builds, this parameter is ignored + * @param address Register address to write + * @param value Value to write + */ + void writeRegI(size_t chip, uint32_t address, uint32_t value); + + /** + * @brief Off the note in specified chip channel + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + */ + void noteOff(size_t c); + + /** + * @brief On the note in specified chip channel with specified frequency of the tone + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param hertz Frequency of the tone in hertzes + */ + void noteOn(size_t c, double hertz); + + /** + * @brief Change setup of instrument in specified chip channel + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param volume Volume level (from 0 to 63) + * @param brightness CC74 Brightness level (from 0 to 127) + */ + void touchNote(size_t c, uint8_t volume, uint8_t brightness = 127); + + /** + * @brief Set the instrument into specified chip channel + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param instrument Instrument data to set into the chip channel + */ + void setPatch(size_t c, const adldata &instrument); + + /** + * @brief Set panpot position + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param value 3-bit panpot value + */ + void setPan(size_t c, uint8_t value); + + /** + * @brief Shut up all chip channels + */ + void silenceAll(); + + /** + * @brief Commit updated flag states to chip registers + */ + void updateChannelCategories(); + + /** + * @brief commit deepTremolo and deepVibrato flags + */ + void commitDeepFlags(); + + /** + * @brief Set the volume scaling model + * @param volumeModel Type of volume scale model scale + */ + void setVolumeScaleModel(ADLMIDI_VolumeModels volumeModel); + + #ifndef ADLMIDI_HW_OPL + /** + * @brief Clean up all running emulated chip instances + */ + void clearChips(); + #endif + + /** + * @brief Reset chip properties and initialize them + * @param emulator Type of chip emulator + * @param PCM_RATE Output sample rate to generate on output + * @param audioTickHandler PCM-accurate clock hook + */ + void reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler); +}; + + +/** + * @brief Hooks of the internal events + */ +struct MIDIEventHooks +{ + MIDIEventHooks() : + onNote(NULL), + onNote_userData(NULL), + onDebugMessage(NULL), + onDebugMessage_userData(NULL) + {} + + //! Note on/off hooks + typedef void (*NoteHook)(void *userdata, int adlchn, int note, int ins, int pressure, double bend); + NoteHook onNote; + void *onNote_userData; + + //! Library internal debug messages + typedef void (*DebugMessageHook)(void *userdata, const char *fmt, ...); + DebugMessageHook onDebugMessage; + void *onDebugMessage_userData; +}; + + +class MIDIplay +{ + friend void adl_reset(struct ADL_MIDIPlayer*); +public: + explicit MIDIplay(unsigned long sampleRate = 22050); + + ~MIDIplay() + {} + + void applySetup(); + + void resetMIDI(); + + /**********************Internal structures and classes**********************/ + + /** + * @brief Persistent settings for each MIDI channel + */ + struct MIDIchannel + { + //! LSB Bank number + uint8_t bank_lsb, + //! MSB Bank number + bank_msb; + //! Current patch number + uint8_t patch; + //! Volume level + uint8_t volume, + //! Expression level + expression; + //! Panning level + uint8_t panning, + //! Vibrato level + vibrato, + //! Channel aftertouch level + aftertouch; + //! Portamento time + uint16_t portamento; + //! Is Pedal sustain active + bool sustain; + //! Is Soft pedal active + bool softPedal; + //! Is portamento enabled + bool portamentoEnable; + //! Source note number used by portamento + int8_t portamentoSource; // note number or -1 + //! Portamento rate + double portamentoRate; + //! Per note Aftertouch values + uint8_t noteAftertouch[128]; + //! Is note aftertouch has any non-zero value + bool noteAfterTouchInUse; + //! Reserved + char _padding[6]; + //! Pitch bend value + int bend; + //! Pitch bend sensitivity + double bendsense; + //! Pitch bend sensitivity LSB value + int bendsense_lsb, + //! Pitch bend sensitivity MSB value + bendsense_msb; + //! Vibrato position value + double vibpos, + //! Vibrato speed value + vibspeed, + //! Vibrato depth value + vibdepth; + //! Vibrato delay time + int64_t vibdelay; + //! Last LSB part of RPN value received + uint8_t lastlrpn, + //! Last MSB poart of RPN value received + lastmrpn; + //! Interpret RPN value as NRPN + bool nrpn; + //! Brightness level + uint8_t brightness; + + //! Is melodic channel turned into percussion + bool is_xg_percussion; + + /** + * @brief Per-Note information + */ + struct NoteInfo + { + //! Note number + uint8_t note; + //! Is note active + bool active; + //! Current pressure + uint8_t vol; + //! Note vibrato (a part of Note Aftertouch feature) + uint8_t vibrato; + //! Tone selected on noteon: + int16_t noteTone; + //! Current tone (!= noteTone if gliding note) + double currentTone; + //! Gliding rate + double glideRate; + //! Patch selected on noteon; index to bank.ins[] + size_t midiins; + //! Is note the percussion instrument + bool isPercussion; + //! Note that plays missing instrument. Doesn't using any chip channels + bool isBlank; + //! Patch selected + const adlinsdata2 *ains; + enum + { + MaxNumPhysChans = 2, + MaxNumPhysItemCount = MaxNumPhysChans, + }; + + /** + * @brief Reference to currently using chip channel + */ + struct Phys + { + //! Destination chip channel + uint16_t chip_chan; + //! ins, inde to adl[] + adldata ains; + //! Is this voice must be detunable? + bool pseudo4op; + + void assign(const Phys &oth) + { + ains = oth.ains; + pseudo4op = oth.pseudo4op; + } + bool operator==(const Phys &oth) const + { + return (ains == oth.ains) && (pseudo4op == oth.pseudo4op); + } + bool operator!=(const Phys &oth) const + { + return !operator==(oth); + } + }; + + //! List of OPL3 channels it is currently occupying. + Phys chip_channels[MaxNumPhysItemCount]; + //! Count of used channels. + unsigned chip_channels_count; + + Phys *phys_find(unsigned chip_chan) + { + Phys *ph = NULL; + for(unsigned i = 0; i < chip_channels_count && !ph; ++i) + if(chip_channels[i].chip_chan == chip_chan) + ph = &chip_channels[i]; + return ph; + } + Phys *phys_find_or_create(uint16_t chip_chan) + { + Phys *ph = phys_find(chip_chan); + if(!ph) { + if(chip_channels_count < MaxNumPhysItemCount) { + ph = &chip_channels[chip_channels_count++]; + ph->chip_chan = chip_chan; + } + } + return ph; + } + Phys *phys_ensure_find_or_create(uint16_t chip_chan) + { + Phys *ph = phys_find_or_create(chip_chan); + assert(ph); + return ph; + } + void phys_erase_at(const Phys *ph) + { + intptr_t pos = ph - chip_channels; + assert(pos < static_cast(chip_channels_count)); + for(intptr_t i = pos + 1; i < static_cast(chip_channels_count); ++i) + chip_channels[i - 1] = chip_channels[i]; + --chip_channels_count; + } + void phys_erase(unsigned chip_chan) + { + Phys *ph = phys_find(chip_chan); + if(ph) + phys_erase_at(ph); + } + }; + + //! Reserved + char _padding2[5]; + //! Count of gliding notes in this channel + unsigned gliding_note_count; + + //! Active notes in the channel + NoteInfo activenotes[128]; + + struct activenoteiterator + { + explicit activenoteiterator(NoteInfo *info = NULL) + : ptr(info) {} + activenoteiterator &operator++() + { + if(ptr->note == 127) + ptr = NULL; + else + for(++ptr; ptr && !ptr->active;) + ptr = (ptr->note == 127) ? NULL : (ptr + 1); + return *this; + } + activenoteiterator operator++(int) + { + activenoteiterator pos = *this; + ++*this; + return pos; + } + NoteInfo &operator*() const + { return *ptr; } + NoteInfo *operator->() const + { return ptr; } + bool operator==(activenoteiterator other) const + { return ptr == other.ptr; } + bool operator!=(activenoteiterator other) const + { return ptr != other.ptr; } + operator NoteInfo *() const + { return ptr; } + private: + NoteInfo *ptr; + }; + + activenoteiterator activenotes_begin() + { + activenoteiterator it(activenotes); + return (it->active) ? it : ++it; + } + + activenoteiterator activenotes_find(uint8_t note) + { + assert(note < 128); + return activenoteiterator( + activenotes[note].active ? &activenotes[note] : NULL); + } + + activenoteiterator activenotes_ensure_find(uint8_t note) + { + activenoteiterator it = activenotes_find(note); + assert(it); + return it; + } + + std::pair activenotes_insert(uint8_t note) + { + assert(note < 128); + NoteInfo &info = activenotes[note]; + bool inserted = !info.active; + if(inserted) info.active = true; + return std::pair(activenoteiterator(&info), inserted); + } + + void activenotes_erase(activenoteiterator pos) + { + if(pos) + pos->active = false; + } + + bool activenotes_empty() + { + return !activenotes_begin(); + } + + void activenotes_clear() + { + for(uint8_t i = 0; i < 128; ++i) { + activenotes[i].note = i; + activenotes[i].active = false; + } + } + + /** + * @brief Reset channel into initial state + */ + void reset() + { + resetAllControllers(); + patch = 0; + vibpos = 0; + bank_lsb = 0; + bank_msb = 0; + lastlrpn = 0; + lastmrpn = 0; + nrpn = false; + is_xg_percussion = false; + } + + /** + * @brief Reset all MIDI controllers into initial state + */ + void resetAllControllers() + { + bend = 0; + bendsense_msb = 2; + bendsense_lsb = 0; + updateBendSensitivity(); + volume = 100; + expression = 127; + sustain = false; + softPedal = false; + vibrato = 0; + aftertouch = 0; + std::memset(noteAftertouch, 0, 128); + noteAfterTouchInUse = false; + vibspeed = 2 * 3.141592653 * 5.0; + vibdepth = 0.5 / 127; + vibdelay = 0; + panning = OPL_PANNING_BOTH; + portamento = 0; + portamentoEnable = false; + portamentoSource = -1; + portamentoRate = HUGE_VAL; + brightness = 127; + } + + /** + * @brief Has channel vibrato to process + * @return + */ + bool hasVibrato() + { + return (vibrato > 0) || (aftertouch > 0) || noteAfterTouchInUse; + } + + /** + * @brief Commit pitch bend sensitivity value from MSB and LSB + */ + void updateBendSensitivity() + { + int cent = bendsense_msb * 128 + bendsense_lsb; + bendsense = cent * (1.0 / (128 * 8192)); + } + + MIDIchannel() + { + activenotes_clear(); + gliding_note_count = 0; + reset(); + } + }; + + /** + * @brief Additional information about OPL3 channels + */ + struct AdlChannel + { + struct Location + { + uint16_t MidCh; + uint8_t note; + bool operator==(const Location &l) const + { return MidCh == l.MidCh && note == l.note; } + bool operator!=(const Location &l) const + { return !operator==(l); } + }; + struct LocationData + { + LocationData *prev, *next; + Location loc; + enum { + Sustain_None = 0x00, + Sustain_Pedal = 0x01, + Sustain_Sostenuto = 0x02, + Sustain_ANY = Sustain_Pedal | Sustain_Sostenuto, + }; + uint32_t sustained; + char _padding[6]; + MIDIchannel::NoteInfo::Phys ins; // a copy of that in phys[] + //! Has fixed sustain, don't iterate "on" timeout + bool fixed_sustain; + //! Timeout until note will be allowed to be killed by channel manager while it is on + int64_t kon_time_until_neglible; + int64_t vibdelay; + }; + + //! Time left until sounding will be muted after key off + int64_t koff_time_until_neglible; + + enum { users_max = 128 }; + LocationData *users_first, *users_free_cells; + LocationData users_cells[users_max]; + unsigned users_size; + + bool users_empty() const; + LocationData *users_find(Location loc); + LocationData *users_allocate(); + LocationData *users_find_or_create(Location loc); + LocationData *users_insert(const LocationData &x); + void users_erase(LocationData *user); + void users_clear(); + void users_assign(const LocationData *users, size_t count); + + // For channel allocation: + AdlChannel(): koff_time_until_neglible(0) + { + users_clear(); + } + + AdlChannel(const AdlChannel &oth): koff_time_until_neglible(oth.koff_time_until_neglible) + { + if(oth.users_first) + { + users_first = NULL; + users_assign(oth.users_first, oth.users_size); + } + else + users_clear(); + } + + AdlChannel &operator=(const AdlChannel &oth) + { + koff_time_until_neglible = oth.koff_time_until_neglible; + users_assign(oth.users_first, oth.users_size); + return *this; + } + + /** + * @brief Increases age of active note in milliseconds time + * @param ms Amount time in milliseconds + */ + void addAge(int64_t ms); + }; + +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + /** + * @brief MIDI files player sequencer + */ + MidiSequencer m_sequencer; + + /** + * @brief Interface between MIDI sequencer and this library + */ + BW_MidiRtInterface m_sequencerInterface; + + /** + * @brief Initialize MIDI sequencer interface + */ + void initSequencerInterface(); +#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER + + struct Setup + { + int emulator; + bool runAtPcmRate; + unsigned int bankId; + unsigned int numFourOps; + unsigned int numChips; + int deepTremoloMode; + int deepVibratoMode; + int rhythmMode; + bool logarithmicVolumes; + int volumeScaleModel; + //unsigned int SkipForward; + int scaleModulators; + bool fullRangeBrightnessCC74; + + double delay; + double carry; + + /* The lag between visual content and audio content equals */ + /* the sum of these two buffers. */ + double mindelay; + double maxdelay; + + /* For internal usage */ + ssize_t tick_skip_samples_delay; /* Skip tick processing after samples count. */ + /* For internal usage */ + + unsigned long PCM_RATE; + }; + + /** + * @brief MIDI Marker entry + */ + struct MIDI_MarkerEntry + { + //! Label of marker + std::string label; + //! Absolute position in seconds + double pos_time; + //! Absolute position in ticks in the track + uint64_t pos_ticks; + }; + + //! Available MIDI Channels + std::vector m_midiChannels; + + //! CMF Rhythm mode + bool m_cmfPercussionMode; + + //! Master volume, controlled via SysEx + uint8_t m_masterVolume; + + //! SysEx device ID + uint8_t m_sysExDeviceId; + + /** + * @brief MIDI Synthesizer mode + */ + enum SynthMode + { + Mode_GM = 0x00, + Mode_GS = 0x01, + Mode_XG = 0x02, + Mode_GM2 = 0x04, + }; + //! MIDI Synthesizer mode + uint32_t m_synthMode; + + //! Installed function hooks + MIDIEventHooks hooks; + +private: + //! Per-track MIDI devices map + std::map m_midiDevices; + //! Current MIDI device per track + std::map m_currentMidiDevice; + + //! Padding to fix CLanc code model's warning + char _padding[7]; + + //! Chip channels map + std::vector m_chipChannels; + //! Counter of arpeggio processing + size_t m_arpeggioCounter; + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + //! Audio tick counter + uint32_t m_audioTickCounter; +#endif + + //! Local error string + std::string errorStringOut; + + //! Missing instruments catches + std::set caugh_missing_instruments; + //! Missing melodic banks catches + std::set caugh_missing_banks_melodic; + //! Missing percussion banks catches + std::set caugh_missing_banks_percussion; + +public: + + const std::string &getErrorString(); + void setErrorString(const std::string &err); + + //! OPL3 Chip manager + OPL3 m_synth; + + //! Generator output buffer + int32_t m_outBuf[1024]; + + //! Synthesizer setup + Setup m_setup; + + /** + * @brief Load custom bank from file + * @param filename Path to bank file + * @return true on succes + */ + bool LoadBank(const std::string &filename); + + /** + * @brief Load custom bank from memory block + * @param data Pointer to memory block where raw bank file is stored + * @param size Size of given memory block + * @return true on succes + */ + bool LoadBank(const void *data, size_t size); + + /** + * @brief Load custom bank from opened FileAndMemReader class + * @param fr Instance with opened file + * @return true on succes + */ + bool LoadBank(FileAndMemReader &fr); + +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + /** + * @brief MIDI file loading pre-process + * @return true on success, false on failure + */ + bool LoadMIDI_pre(); + + /** + * @brief MIDI file loading post-process + * @return true on success, false on failure + */ + bool LoadMIDI_post(); + + /** + * @brief Load music file from a file + * @param filename Path to music file + * @return true on success, false on failure + */ + + bool LoadMIDI(const std::string &filename); + + /** + * @brief Load music file from the memory block + * @param data pointer to the memory block + * @param size size of memory block + * @return true on success, false on failure + */ + bool LoadMIDI(const void *data, size_t size); + + /** + * @brief Periodic tick handler. + * @param s seconds since last call + * @param granularity don't expect intervals smaller than this, in seconds + * @return desired number of seconds until next call + */ + double Tick(double s, double granularity); +#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER + + /** + * @brief Process extra iterators like vibrato or arpeggio + * @param s seconds since last call + */ + void TickIterators(double s); + + + /* RealTime event triggers */ + /** + * @brief Reset state of all channels + */ + void realTime_ResetState(); + + /** + * @brief Note On event + * @param channel MIDI channel + * @param note Note key (from 0 to 127) + * @param velocity Velocity level (from 0 to 127) + * @return true if Note On event was accepted + */ + bool realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity); + + /** + * @brief Note Off event + * @param channel MIDI channel + * @param note Note key (from 0 to 127) + */ + void realTime_NoteOff(uint8_t channel, uint8_t note); + + /** + * @brief Note aftertouch event + * @param channel MIDI channel + * @param note Note key (from 0 to 127) + * @param atVal After-Touch level (from 0 to 127) + */ + void realTime_NoteAfterTouch(uint8_t channel, uint8_t note, uint8_t atVal); + + /** + * @brief Channel aftertouch event + * @param channel MIDI channel + * @param atVal After-Touch level (from 0 to 127) + */ + void realTime_ChannelAfterTouch(uint8_t channel, uint8_t atVal); + + /** + * @brief Controller Change event + * @param channel MIDI channel + * @param type Type of controller + * @param value Value of the controller (from 0 to 127) + */ + void realTime_Controller(uint8_t channel, uint8_t type, uint8_t value); + + /** + * @brief Patch change + * @param channel MIDI channel + * @param patch Patch Number (from 0 to 127) + */ + void realTime_PatchChange(uint8_t channel, uint8_t patch); + + /** + * @brief Pitch bend change + * @param channel MIDI channel + * @param pitch Concoctated raw pitch value + */ + void realTime_PitchBend(uint8_t channel, uint16_t pitch); + + /** + * @brief Pitch bend change + * @param channel MIDI channel + * @param msb MSB of pitch value + * @param lsb LSB of pitch value + */ + void realTime_PitchBend(uint8_t channel, uint8_t msb, uint8_t lsb); + + /** + * @brief LSB Bank Change CC + * @param channel MIDI channel + * @param lsb LSB value of bank number + */ + void realTime_BankChangeLSB(uint8_t channel, uint8_t lsb); + + /** + * @brief MSB Bank Change CC + * @param channel MIDI channel + * @param msb MSB value of bank number + */ + void realTime_BankChangeMSB(uint8_t channel, uint8_t msb); + + /** + * @brief Bank Change (united value) + * @param channel MIDI channel + * @param bank Bank number value + */ + void realTime_BankChange(uint8_t channel, uint16_t bank); + + /** + * @brief Sets the Device identifier + * @param id 7-bit Device identifier + */ + void setDeviceId(uint8_t id); + + /** + * @brief System Exclusive message + * @param msg Raw SysEx Message + * @param size Length of SysEx message + * @return true if message was passed successfully. False on any errors + */ + bool realTime_SysEx(const uint8_t *msg, size_t size); + + /** + * @brief Turn off all notes and mute the sound of releasing notes + */ + void realTime_panic(); + + /** + * @brief Device switch (to extend 16-channels limit of MIDI standard) + * @param track MIDI track index + * @param data Device name + * @param length Length of device name string + */ + void realTime_deviceSwitch(size_t track, const char *data, size_t length); + + /** + * @brief Currently selected device index + * @param track MIDI track index + * @return Multiple 16 value + */ + size_t realTime_currentDevice(size_t track); + + /** + * @brief Send raw OPL chip command + * @param reg OPL Register + * @param value Value to write + */ + void realTime_rawOPL(uint8_t reg, uint8_t value); + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + // Audio rate tick handler + void AudioTick(uint32_t chipId, uint32_t rate); +#endif + +private: + /** + * @brief Hardware manufacturer (Used for SysEx) + */ + enum + { + Manufacturer_Roland = 0x41, + Manufacturer_Yamaha = 0x43, + Manufacturer_UniversalNonRealtime = 0x7E, + Manufacturer_UniversalRealtime = 0x7F + }; + + /** + * @brief Roland Mode (Used for SysEx) + */ + enum + { + RolandMode_Request = 0x11, + RolandMode_Send = 0x12 + }; + + /** + * @brief Device model (Used for SysEx) + */ + enum + { + RolandModel_GS = 0x42, + RolandModel_SC55 = 0x45, + YamahaModel_XG = 0x4C + }; + + /** + * @brief Process generic SysEx events + * @param dev Device ID + * @param realtime Is real-time event + * @param data Raw SysEx data + * @param size Size of given SysEx data + * @return true when event was successfully handled + */ + bool doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data, size_t size); + + /** + * @brief Process events specific to Roland devices + * @param dev Device ID + * @param data Raw SysEx data + * @param size Size of given SysEx data + * @return true when event was successfully handled + */ + bool doRolandSysEx(unsigned dev, const uint8_t *data, size_t size); + + /** + * @brief Process events specific to Yamaha devices + * @param dev Device ID + * @param data Raw SysEx data + * @param size Size of given SysEx data + * @return true when event was successfully handled + */ + bool doYamahaSysEx(unsigned dev, const uint8_t *data, size_t size); + +private: + /** + * @brief Note Update properties + */ + enum + { + Upd_Patch = 0x1, + Upd_Pan = 0x2, + Upd_Volume = 0x4, + Upd_Pitch = 0x8, + Upd_All = Upd_Pan + Upd_Volume + Upd_Pitch, + Upd_Off = 0x20, + Upd_Mute = 0x40, + Upd_OffMute = Upd_Off + Upd_Mute + }; + + /** + * @brief Update active note + * @param MidCh MIDI Channel where note is processing + * @param i Iterator that points to active note in the MIDI channel + * @param props_mask Properties to update + * @param select_adlchn Specify chip channel, or -1 - all chip channels used by the note + */ + void noteUpdate(size_t midCh, + MIDIchannel::activenoteiterator i, + unsigned props_mask, + int32_t select_adlchn = -1); + + /** + * @brief Update all notes in specified MIDI channel + * @param midCh MIDI channel to update all notes in it + * @param props_mask Properties to update + */ + void noteUpdateAll(size_t midCh, unsigned props_mask); + + /** + * @brief Determine how good a candidate this adlchannel would be for playing a note from this instrument. + * @param c Wanted chip channel + * @param ins Instrument wanted to be used in this channel + * @return Calculated coodness points + */ + int64_t calculateChipChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins) const; + + /** + * @brief A new note will be played on this channel using this instrument. + * @param c Wanted chip channel + * @param ins Instrument wanted to be used in this channel + * Kill existing notes on this channel (or don't, if we do arpeggio) + */ + void prepareChipChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins); + + /** + * @brief Kills note that uses wanted channel. When arpeggio is possible, note is evaluating to another channel + * @param from_channel Wanted chip channel + * @param j Chip channel instance + * @param i MIDI Channel active note instance + */ + void killOrEvacuate( + size_t from_channel, + AdlChannel::LocationData *j, + MIDIchannel::activenoteiterator i); + + /** + * @brief Off all notes and silence sound + */ + void panic(); + + /** + * @brief Kill note, sustaining by pedal or sostenuto + * @param MidCh MIDI channel, -1 - all MIDI channels + * @param this_adlchn Chip channel, -1 - all chip channels + * @param sustain_type Type of systain to process + */ + void killSustainingNotes(int32_t midCh = -1, + int32_t this_adlchn = -1, + uint32_t sustain_type = AdlChannel::LocationData::Sustain_ANY); + /** + * @brief Find active notes and mark them as sostenuto-sustained + * @param MidCh MIDI channel, -1 - all MIDI channels + */ + void markSostenutoNotes(int32_t midCh = -1); + + /** + * @brief Set RPN event value + * @param MidCh MIDI channel + * @param value 1 byte part of RPN value + * @param MSB is MSB or LSB part of value + */ + void setRPN(size_t midCh, unsigned value, bool MSB); + + /** + * @brief Update portamento setup in MIDI channel + * @param midCh MIDI channel where portamento needed to be updated + */ + void updatePortamento(size_t midCh); + + /** + * @brief Off the note + * @param midCh MIDI channel + * @param note Note to off + */ + void noteOff(size_t midCh, uint8_t note); + + /** + * @brief Update processing of vibrato to amount of seconds + * @param amount Amount value in seconds + */ + void updateVibrato(double amount); + + /** + * @brief Update auto-arpeggio + * @param amount Amount value in seconds [UNUSED] + */ + void updateArpeggio(double /*amount*/); + + /** + * @brief Update Portamento gliding to amount of seconds + * @param amount Amount value in seconds + */ + void updateGlide(double amount); + +public: + /** + * @brief Checks was device name used or not + * @param name Name of MIDI device + * @return Offset of the MIDI Channels, multiple to 16 + */ + size_t chooseDevice(const std::string &name); + + /** + * @brief Gets a textual description of the state of chip channels + * @param text character pointer for text + * @param attr character pointer for text attributes + * @param size number of characters available to write + */ + void describeChannels(char *text, char *attr, size_t size); +}; + +// I think, this is useless inside of Library +/* +struct FourChars +{ + char ret[4]; + + FourChars(const char *s) + { + for(unsigned c = 0; c < 4; ++c) + ret[c] = s[c]; + } + FourChars(unsigned w) // Little-endian + { + for(unsigned c = 0; c < 4; ++c) + ret[c] = static_cast((w >>(c * 8)) & 0xFF); + } +}; +*/ + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) +extern void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate); +#endif +extern int adlRefreshNumCards(ADL_MIDIPlayer *device); + + +#endif // ADLMIDI_PRIVATE_HPP diff --git a/engine/src/Libraries/adlmidi/adlmidi_ptr.hpp b/engine/src/Libraries/adlmidi/adlmidi_ptr.hpp new file mode 100644 index 0000000..7d1086b --- /dev/null +++ b/engine/src/Libraries/adlmidi/adlmidi_ptr.hpp @@ -0,0 +1,217 @@ +/* + * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ADLMIDI_PTR_HPP_THING +#define ADLMIDI_PTR_HPP_THING + +#include // swap +#include +#include + +/* + Generic deleters for smart pointers + */ +template +struct ADLMIDI_DefaultDelete +{ + void operator()(T *x) { delete x; } +}; +template +struct ADLMIDI_DefaultArrayDelete +{ + void operator()(T *x) { delete[] x; } +}; +struct ADLMIDI_CDelete +{ + void operator()(void *x) { free(x); } +}; + +/* + Safe unique pointer for C++98, non-copyable but swappable. +*/ +template< class T, class Deleter = ADLMIDI_DefaultDelete > +class AdlMIDI_UPtr +{ + T *m_p; +public: + explicit AdlMIDI_UPtr(T *p) + : m_p(p) {} + ~AdlMIDI_UPtr() + { + reset(); + } + + void reset(T *p = NULL) + { + if(p != m_p) { + if(m_p) { + Deleter del; + del(m_p); + } + m_p = p; + } + } + + void swap(AdlMIDI_UPtr &other) + { + std::swap(m_p, other.m_p); + } + + T *get() const + { + return m_p; + } + T &operator*() const + { + return *m_p; + } + T *operator->() const + { + return m_p; + } + T &operator[](size_t index) const + { + return m_p[index]; + } +private: + AdlMIDI_UPtr(const AdlMIDI_UPtr &); + AdlMIDI_UPtr &operator=(const AdlMIDI_UPtr &); +}; + +template +void swap(AdlMIDI_UPtr &a, AdlMIDI_UPtr &b) +{ + a.swap(b); +} + +/** + Unique pointer for arrays. + */ +template +class AdlMIDI_UPtrArray : + public AdlMIDI_UPtr< T, ADLMIDI_DefaultArrayDelete > +{ +public: + explicit AdlMIDI_UPtrArray(T *p = NULL) + : AdlMIDI_UPtr< T, ADLMIDI_DefaultArrayDelete >(p) {} +}; + +/** + Unique pointer for C memory. + */ +template +class AdlMIDI_CPtr : + public AdlMIDI_UPtr< T, ADLMIDI_CDelete > +{ +public: + explicit AdlMIDI_CPtr(T *p = NULL) + : AdlMIDI_UPtr< T, ADLMIDI_CDelete >(p) {} +}; + +/* + Shared pointer with non-atomic counter + FAQ: Why not std::shared_ptr? Because of Android NDK now doesn't supports it +*/ +template< class T, class Deleter = ADLMIDI_DefaultDelete > +class AdlMIDI_SPtr +{ + T *m_p; + size_t *m_counter; +public: + explicit AdlMIDI_SPtr(T *p = NULL) + : m_p(p), m_counter(p ? new size_t(1) : NULL) {} + ~AdlMIDI_SPtr() + { + reset(NULL); + } + + AdlMIDI_SPtr(const AdlMIDI_SPtr &other) + : m_p(other.m_p), m_counter(other.m_counter) + { + if(m_counter) + ++*m_counter; + } + + AdlMIDI_SPtr &operator=(const AdlMIDI_SPtr &other) + { + if(this == &other) + return *this; + reset(); + m_p = other.m_p; + m_counter = other.m_counter; + if(m_counter) + ++*m_counter; + return *this; + } + + void reset(T *p = NULL) + { + if(p != m_p) { + if(m_p && --*m_counter == 0) { + Deleter del; + del(m_p); + if(!p) { + delete m_counter; + m_counter = NULL; + } + } + m_p = p; + if(p) { + if(!m_counter) + m_counter = new size_t; + *m_counter = 1; + } + } + } + + T *get() const + { + return m_p; + } + T &operator*() const + { + return *m_p; + } + T *operator->() const + { + return m_p; + } + T &operator[](size_t index) const + { + return m_p[index]; + } +}; + +/** + Shared pointer for arrays. + */ +template +class AdlMIDI_SPtrArray : + public AdlMIDI_SPtr< T, ADLMIDI_DefaultArrayDelete > +{ +public: + explicit AdlMIDI_SPtrArray(T *p = NULL) + : AdlMIDI_SPtr< T, ADLMIDI_DefaultArrayDelete >(p) {} +}; + +#endif //ADLMIDI_PTR_HPP_THING diff --git a/engine/src/Libraries/adlmidi/adlmidi_sequencer.cpp b/engine/src/Libraries/adlmidi/adlmidi_sequencer.cpp new file mode 100644 index 0000000..3c18cad --- /dev/null +++ b/engine/src/Libraries/adlmidi/adlmidi_sequencer.cpp @@ -0,0 +1,151 @@ +/* + * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + +// Rename class to avoid ABI collisions +#define BW_MidiSequencer AdlMidiSequencer +// Inlucde MIDI sequencer class implementation +#include "midi_sequencer_impl.hpp" + +#include "adlmidi_private.hpp" + +/**************************************************** + * Real-Time MIDI calls proxies * + ****************************************************/ + +static void rtNoteOn(void *userdata, uint8_t channel, uint8_t note, uint8_t velocity) +{ + MIDIplay *context = reinterpret_cast(userdata); + context->realTime_NoteOn(channel, note, velocity); +} + +static void rtNoteOff(void *userdata, uint8_t channel, uint8_t note) +{ + MIDIplay *context = reinterpret_cast(userdata); + context->realTime_NoteOff(channel, note); +} + +static void rtNoteAfterTouch(void *userdata, uint8_t channel, uint8_t note, uint8_t atVal) +{ + MIDIplay *context = reinterpret_cast(userdata); + context->realTime_NoteAfterTouch(channel, note, atVal); +} + +static void rtChannelAfterTouch(void *userdata, uint8_t channel, uint8_t atVal) +{ + MIDIplay *context = reinterpret_cast(userdata); + context->realTime_ChannelAfterTouch(channel, atVal); +} + +static void rtControllerChange(void *userdata, uint8_t channel, uint8_t type, uint8_t value) +{ + MIDIplay *context = reinterpret_cast(userdata); + context->realTime_Controller(channel, type, value); +} + +static void rtPatchChange(void *userdata, uint8_t channel, uint8_t patch) +{ + MIDIplay *context = reinterpret_cast(userdata); + context->realTime_PatchChange(channel, patch); +} + +static void rtPitchBend(void *userdata, uint8_t channel, uint8_t msb, uint8_t lsb) +{ + MIDIplay *context = reinterpret_cast(userdata); + context->realTime_PitchBend(channel, msb, lsb); +} + +static void rtSysEx(void *userdata, const uint8_t *msg, size_t size) +{ + MIDIplay *context = reinterpret_cast(userdata); + context->realTime_SysEx(msg, size); +} + + +/* NonStandard calls */ +static void rtRawOPL(void *userdata, uint8_t reg, uint8_t value) +{ + MIDIplay *context = reinterpret_cast(userdata); + return context->realTime_rawOPL(reg, value); +} + +static void rtDeviceSwitch(void *userdata, size_t track, const char *data, size_t length) +{ + MIDIplay *context = reinterpret_cast(userdata); + context->realTime_deviceSwitch(track, data, length); +} + +static size_t rtCurrentDevice(void *userdata, size_t track) +{ + MIDIplay *context = reinterpret_cast(userdata); + return context->realTime_currentDevice(track); +} +/* NonStandard calls End */ + + +void MIDIplay::initSequencerInterface() +{ + std::memset(&m_sequencerInterface, 0, sizeof(BW_MidiRtInterface)); + + m_sequencerInterface.onDebugMessage = hooks.onDebugMessage; + m_sequencerInterface.onDebugMessage_userData = hooks.onDebugMessage_userData; + + /* MIDI Real-Time calls */ + m_sequencerInterface.rtUserData = this; + m_sequencerInterface.rt_noteOn = rtNoteOn; + m_sequencerInterface.rt_noteOff = rtNoteOff; + m_sequencerInterface.rt_noteAfterTouch = rtNoteAfterTouch; + m_sequencerInterface.rt_channelAfterTouch = rtChannelAfterTouch; + m_sequencerInterface.rt_controllerChange = rtControllerChange; + m_sequencerInterface.rt_patchChange = rtPatchChange; + m_sequencerInterface.rt_pitchBend = rtPitchBend; + m_sequencerInterface.rt_systemExclusive = rtSysEx; + + /* NonStandard calls */ + m_sequencerInterface.rt_rawOPL = rtRawOPL; + m_sequencerInterface.rt_deviceSwitch = rtDeviceSwitch; + m_sequencerInterface.rt_currentDevice = rtCurrentDevice; + /* NonStandard calls End */ + + m_sequencer.setInterface(&m_sequencerInterface); +} + +double MIDIplay::Tick(double s, double granularity) +{ + double ret = m_sequencer.Tick(s, granularity); + + s *= m_sequencer.getTempoMultiplier(); + for(uint16_t c = 0; c < m_synth.m_numChannels; ++c) + m_chipChannels[c].addAge(static_cast(s * 1000.0)); + + updateVibrato(s); + updateArpeggio(s); +#if !defined(ADLMIDI_AUDIO_TICK_HANDLER) + updateGlide(s); +#endif + + return ret; +} + +#endif /* ADLMIDI_DISABLE_MIDI_SEQUENCER */ diff --git a/engine/src/Libraries/adlmidi/adlmidi_xmi2mid.hpp b/engine/src/Libraries/adlmidi/adlmidi_xmi2mid.hpp new file mode 100644 index 0000000..323c144 --- /dev/null +++ b/engine/src/Libraries/adlmidi/adlmidi_xmi2mid.hpp @@ -0,0 +1,1128 @@ +/* + * XMIDI: Miles XMIDI to MID Library + * + * Copyright (C) 2001 Ryan Nunn + * Copyright (C) 2014 Bret Curtis + * Copyright (C) WildMIDI Developers 2015-2016 + * Copyright (c) 2015-2018 Vitaly Novichkov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/* XMIDI Converter */ + +#include +#include +#include +#include +#include + +#ifdef __DJGPP__ +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed long int32_t; +typedef unsigned long uint32_t; +#endif + +/* Conversion types for Midi files */ +#define XMIDI_CONVERT_NOCONVERSION 0x00 +#define XMIDI_CONVERT_MT32_TO_GM 0x01 +#define XMIDI_CONVERT_MT32_TO_GS 0x02 +#define XMIDI_CONVERT_MT32_TO_GS127 0x03 /* This one is broken, don't use */ +#define XMIDI_CONVERT_MT32_TO_GS127DRUM 0x04 /* This one is broken, don't use */ +#define XMIDI_CONVERT_GS127_TO_GS 0x05 + +/* Midi Status Bytes */ +#define XMI2MID_MIDI_STATUS_NOTE_OFF 0x8 +#define XMI2MID_MIDI_STATUS_NOTE_ON 0x9 +#define XMI2MID_MIDI_STATUS_AFTERTOUCH 0xA +#define XMI2MID_MIDI_STATUS_CONTROLLER 0xB +#define XMI2MID_MIDI_STATUS_PROG_CHANGE 0xC +#define XMI2MID_MIDI_STATUS_PRESSURE 0xD +#define XMI2MID_MIDI_STATUS_PITCH_WHEEL 0xE +#define XMI2MID_MIDI_STATUS_SYSEX 0xF + +typedef struct _xmi2mid_midi_event { + int32_t time; + uint8_t status; + uint8_t data[2]; + uint32_t len; + uint8_t *buffer; + struct _xmi2mid_midi_event *next; +} midi_event; + +typedef struct { + uint16_t type; + uint16_t tracks; +} midi_descriptor; + +struct xmi2mid_xmi_ctx { + uint8_t *src, *src_ptr; + uint32_t srcsize; + uint32_t datastart; + uint8_t *dst, *dst_ptr; + uint32_t dstsize, dstrem; + uint32_t convert_type; + midi_descriptor info; + int bank127[16]; + midi_event **events; + int16_t *timing; + midi_event *list; + midi_event *current; +}; + +/* forward declarations of private functions */ +static void xmi2mid_DeleteEventList(midi_event *mlist); +static void xmi2mid_CreateNewEvent(struct xmi2mid_xmi_ctx *ctx, int32_t time); /* List manipulation */ +static int xmi2mid_GetVLQ(struct xmi2mid_xmi_ctx *ctx, uint32_t *quant); /* Variable length quantity */ +static int xmi2mid_GetVLQ2(struct xmi2mid_xmi_ctx *ctx, uint32_t *quant);/* Variable length quantity */ +static int xmi2mid_PutVLQ(struct xmi2mid_xmi_ctx *ctx, uint32_t value); /* Variable length quantity */ +static int xmi2mid_ConvertEvent(struct xmi2mid_xmi_ctx *ctx, + const int32_t time, const uint8_t status, const int size); +static int32_t xmi2mid_ConvertSystemMessage(struct xmi2mid_xmi_ctx *ctx, + const int32_t time, const uint8_t status); +static int32_t xmi2mid_ConvertFiletoList(struct xmi2mid_xmi_ctx *ctx); +static uint32_t xmi2mid_ConvertListToMTrk(struct xmi2mid_xmi_ctx *ctx, midi_event *mlist); +static int xmi2mid_ParseXMI(struct xmi2mid_xmi_ctx *ctx); +static int xmi2mid_ExtractTracks(struct xmi2mid_xmi_ctx *ctx); +static uint32_t xmi2mid_ExtractTracksFromXmi(struct xmi2mid_xmi_ctx *ctx); + +static uint32_t xmi2mid_read1(struct xmi2mid_xmi_ctx *ctx) +{ + uint8_t b0; + b0 = *ctx->src_ptr++; + return (b0); +} + +static uint32_t xmi2mid_read2(struct xmi2mid_xmi_ctx *ctx) +{ + uint8_t b0, b1; + b0 = *ctx->src_ptr++; + b1 = *ctx->src_ptr++; + return (b0 + ((uint32_t)b1 << 8)); +} + +static uint32_t xmi2mid_read4(struct xmi2mid_xmi_ctx *ctx) +{ + uint8_t b0, b1, b2, b3; + b3 = *ctx->src_ptr++; + b2 = *ctx->src_ptr++; + b1 = *ctx->src_ptr++; + b0 = *ctx->src_ptr++; + return (b0 + ((uint32_t)b1<<8) + ((uint32_t)b2<<16) + ((uint32_t)b3<<24)); +} + +static void xmi2mid_copy(struct xmi2mid_xmi_ctx *ctx, char *b, uint32_t len) +{ + memcpy(b, ctx->src_ptr, len); + ctx->src_ptr += len; +} + +#define DST_CHUNK 8192 +static void xmi2mid_resize_dst(struct xmi2mid_xmi_ctx *ctx) { + uint32_t pos = (uint32_t)(ctx->dst_ptr - ctx->dst); + ctx->dst = (uint8_t *)realloc(ctx->dst, ctx->dstsize + DST_CHUNK); + ctx->dstsize += DST_CHUNK; + ctx->dstrem += DST_CHUNK; + ctx->dst_ptr = ctx->dst + pos; +} + +static void xmi2mid_write1(struct xmi2mid_xmi_ctx *ctx, uint32_t val) +{ + if (ctx->dstrem < 1) + xmi2mid_resize_dst(ctx); + *ctx->dst_ptr++ = val & 0xff; + ctx->dstrem--; +} + +static void xmi2mid_write2(struct xmi2mid_xmi_ctx *ctx, uint32_t val) +{ + if (ctx->dstrem < 2) + xmi2mid_resize_dst(ctx); + *ctx->dst_ptr++ = (val>>8) & 0xff; + *ctx->dst_ptr++ = val & 0xff; + ctx->dstrem -= 2; +} + +static void xmi2mid_write4(struct xmi2mid_xmi_ctx *ctx, uint32_t val) +{ + if (ctx->dstrem < 4) + xmi2mid_resize_dst(ctx); + *ctx->dst_ptr++ = (val>>24)&0xff; + *ctx->dst_ptr++ = (val>>16)&0xff; + *ctx->dst_ptr++ = (val>>8) & 0xff; + *ctx->dst_ptr++ = val & 0xff; + ctx->dstrem -= 4; +} + +static void xmi2mid_seeksrc(struct xmi2mid_xmi_ctx *ctx, uint32_t pos) { + ctx->src_ptr = ctx->src + pos; +} + +static void xmi2mid_seekdst(struct xmi2mid_xmi_ctx *ctx, uint32_t pos) { + ctx->dst_ptr = ctx->dst + pos; + while (ctx->dstsize < pos) + xmi2mid_resize_dst(ctx); + ctx->dstrem = ctx->dstsize - pos; +} + +static void xmi2mid_skipsrc(struct xmi2mid_xmi_ctx *ctx, int32_t pos) { + ctx->src_ptr += pos; +} + +static void xmi2mid_skipdst(struct xmi2mid_xmi_ctx *ctx, int32_t pos) { + size_t newpos; + ctx->dst_ptr += pos; + newpos = ctx->dst_ptr - ctx->dst; + while (ctx->dstsize < newpos) + xmi2mid_resize_dst(ctx); + ctx->dstrem = (uint32_t)(ctx->dstsize - newpos); +} + +static uint32_t xmi2mid_getsrcsize(struct xmi2mid_xmi_ctx *ctx) { + return (ctx->srcsize); +} + +static uint32_t xmi2mid_getsrcpos(struct xmi2mid_xmi_ctx *ctx) { + return (uint32_t)(ctx->src_ptr - ctx->src); +} + +static uint32_t xmi2mid_getdstpos(struct xmi2mid_xmi_ctx *ctx) { + return (uint32_t)(ctx->dst_ptr - ctx->dst); +} + +/* This is a default set of patches to convert from MT32 to GM + * The index is the MT32 Patch number and the value is the GM Patch + * This is only suitable for music that doesn't do timbre changes + * XMIDIs that contain Timbre changes will not convert properly. + */ +static const char xmi2mid_mt32asgm[128] = { + 0, /* 0 Piano 1 */ + 1, /* 1 Piano 2 */ + 2, /* 2 Piano 3 (synth) */ + 4, /* 3 EPiano 1 */ + 4, /* 4 EPiano 2 */ + 5, /* 5 EPiano 3 */ + 5, /* 6 EPiano 4 */ + 3, /* 7 Honkytonk */ + 16, /* 8 Organ 1 */ + 17, /* 9 Organ 2 */ + 18, /* 10 Organ 3 */ + 16, /* 11 Organ 4 */ + 19, /* 12 Pipe Organ 1 */ + 19, /* 13 Pipe Organ 2 */ + 19, /* 14 Pipe Organ 3 */ + 21, /* 15 Accordion */ + 6, /* 16 Harpsichord 1 */ + 6, /* 17 Harpsichord 2 */ + 6, /* 18 Harpsichord 3 */ + 7, /* 19 Clavinet 1 */ + 7, /* 20 Clavinet 2 */ + 7, /* 21 Clavinet 3 */ + 8, /* 22 Celesta 1 */ + 8, /* 23 Celesta 2 */ + 62, /* 24 Synthbrass 1 (62) */ + 63, /* 25 Synthbrass 2 (63) */ + 62, /* 26 Synthbrass 3 Bank 8 */ + 63, /* 27 Synthbrass 4 Bank 8 */ + 38, /* 28 Synthbass 1 */ + 39, /* 29 Synthbass 2 */ + 38, /* 30 Synthbass 3 Bank 8 */ + 39, /* 31 Synthbass 4 Bank 8 */ + 88, /* 32 Fantasy */ + 90, /* 33 Harmonic Pan - No equiv closest is polysynth(90) :( */ + 52, /* 34 Choral ?? Currently set to SynthVox(54). Should it be ChoirAhhs(52)??? */ + 92, /* 35 Glass */ + 97, /* 36 Soundtrack */ + 99, /* 37 Atmosphere */ + 14, /* 38 Warmbell, sounds kind of like crystal(98) perhaps Tubular Bells(14) would be better. It is! */ + 54, /* 39 FunnyVox, sounds alot like Bagpipe(109) and Shania(111) */ + 98, /* 40 EchoBell, no real equiv, sounds like Crystal(98) */ + 96, /* 41 IceRain */ + 68, /* 42 Oboe 2001, no equiv, just patching it to normal oboe(68) */ + 95, /* 43 EchoPans, no equiv, setting to SweepPad */ + 81, /* 44 DoctorSolo Bank 8 */ + 87, /* 45 SchoolDaze, no real equiv */ + 112,/* 46 Bell Singer */ + 80, /* 47 SquareWave */ + 48, /* 48 Strings 1 */ + 48, /* 49 Strings 2 - should be 49 */ + 44, /* 50 Strings 3 (Synth) - Experimental set to Tremollo Strings - should be 50 */ + 45, /* 51 Pizzicato Strings */ + 40, /* 52 Violin 1 */ + 40, /* 53 Violin 2 ? Viola */ + 42, /* 54 Cello 1 */ + 42, /* 55 Cello 2 */ + 43, /* 56 Contrabass */ + 46, /* 57 Harp 1 */ + 46, /* 58 Harp 2 */ + 24, /* 59 Guitar 1 (Nylon) */ + 25, /* 60 Guitar 2 (Steel) */ + 26, /* 61 Elec Guitar 1 */ + 27, /* 62 Elec Guitar 2 */ + 104,/* 63 Sitar */ + 32, /* 64 Acou Bass 1 */ + 32, /* 65 Acou Bass 2 */ + 33, /* 66 Elec Bass 1 */ + 34, /* 67 Elec Bass 2 */ + 36, /* 68 Slap Bass 1 */ + 37, /* 69 Slap Bass 2 */ + 35, /* 70 Fretless Bass 1 */ + 35, /* 71 Fretless Bass 2 */ + 73, /* 72 Flute 1 */ + 73, /* 73 Flute 2 */ + 72, /* 74 Piccolo 1 */ + 72, /* 75 Piccolo 2 */ + 74, /* 76 Recorder */ + 75, /* 77 Pan Pipes */ + 64, /* 78 Sax 1 */ + 65, /* 79 Sax 2 */ + 66, /* 80 Sax 3 */ + 67, /* 81 Sax 4 */ + 71, /* 82 Clarinet 1 */ + 71, /* 83 Clarinet 2 */ + 68, /* 84 Oboe */ + 69, /* 85 English Horn (Cor Anglais) */ + 70, /* 86 Bassoon */ + 22, /* 87 Harmonica */ + 56, /* 88 Trumpet 1 */ + 56, /* 89 Trumpet 2 */ + 57, /* 90 Trombone 1 */ + 57, /* 91 Trombone 2 */ + 60, /* 92 French Horn 1 */ + 60, /* 93 French Horn 2 */ + 58, /* 94 Tuba */ + 61, /* 95 Brass Section 1 */ + 61, /* 96 Brass Section 2 */ + 11, /* 97 Vibes 1 */ + 11, /* 98 Vibes 2 */ + 99, /* 99 Syn Mallet Bank 1 */ + 112,/* 100 WindBell no real equiv Set to TinkleBell(112) */ + 9, /* 101 Glockenspiel */ + 14, /* 102 Tubular Bells */ + 13, /* 103 Xylophone */ + 12, /* 104 Marimba */ + 107,/* 105 Koto */ + 111,/* 106 Sho?? set to Shanai(111) */ + 77, /* 107 Shakauhachi */ + 78, /* 108 Whistle 1 */ + 78, /* 109 Whistle 2 */ + 76, /* 110 Bottle Blow */ + 76, /* 111 Breathpipe no real equiv set to bottle blow(76) */ + 47, /* 112 Timpani */ + 117,/* 113 Melodic Tom */ + 116,/* 114 Deap Snare no equiv, set to Taiko(116) */ + 118,/* 115 Electric Perc 1 */ + 118,/* 116 Electric Perc 2 */ + 116,/* 117 Taiko */ + 115,/* 118 Taiko Rim, no real equiv, set to Woodblock(115) */ + 119,/* 119 Cymbal, no real equiv, set to reverse cymbal(119) */ + 115,/* 120 Castanets, no real equiv, in GM set to Woodblock(115) */ + 112,/* 121 Triangle, no real equiv, set to TinkleBell(112) */ + 55, /* 122 Orchestral Hit */ + 124,/* 123 Telephone */ + 123,/* 124 BirdTweet */ + 94, /* 125 Big Notes Pad no equiv, set to halo pad (94) */ + 98, /* 126 Water Bell set to Crystal Pad(98) */ + 121 /* 127 Jungle Tune set to Breath Noise */ +}; + +/* Same as above, except include patch changes + * so GS instruments can be used */ +static const char xmi2mid_mt32asgs[256] = { + 0, 0, /* 0 Piano 1 */ + 1, 0, /* 1 Piano 2 */ + 2, 0, /* 2 Piano 3 (synth) */ + 4, 0, /* 3 EPiano 1 */ + 4, 0, /* 4 EPiano 2 */ + 5, 0, /* 5 EPiano 3 */ + 5, 0, /* 6 EPiano 4 */ + 3, 0, /* 7 Honkytonk */ + 16, 0, /* 8 Organ 1 */ + 17, 0, /* 9 Organ 2 */ + 18, 0, /* 10 Organ 3 */ + 16, 0, /* 11 Organ 4 */ + 19, 0, /* 12 Pipe Organ 1 */ + 19, 0, /* 13 Pipe Organ 2 */ + 19, 0, /* 14 Pipe Organ 3 */ + 21, 0, /* 15 Accordion */ + 6, 0, /* 16 Harpsichord 1 */ + 6, 0, /* 17 Harpsichord 2 */ + 6, 0, /* 18 Harpsichord 3 */ + 7, 0, /* 19 Clavinet 1 */ + 7, 0, /* 20 Clavinet 2 */ + 7, 0, /* 21 Clavinet 3 */ + 8, 0, /* 22 Celesta 1 */ + 8, 0, /* 23 Celesta 2 */ + 62, 0, /* 24 Synthbrass 1 (62) */ + 63, 0, /* 25 Synthbrass 2 (63) */ + 62, 0, /* 26 Synthbrass 3 Bank 8 */ + 63, 0, /* 27 Synthbrass 4 Bank 8 */ + 38, 0, /* 28 Synthbass 1 */ + 39, 0, /* 29 Synthbass 2 */ + 38, 0, /* 30 Synthbass 3 Bank 8 */ + 39, 0, /* 31 Synthbass 4 Bank 8 */ + 88, 0, /* 32 Fantasy */ + 90, 0, /* 33 Harmonic Pan - No equiv closest is polysynth(90) :( */ + 52, 0, /* 34 Choral ?? Currently set to SynthVox(54). Should it be ChoirAhhs(52)??? */ + 92, 0, /* 35 Glass */ + 97, 0, /* 36 Soundtrack */ + 99, 0, /* 37 Atmosphere */ + 14, 0, /* 38 Warmbell, sounds kind of like crystal(98) perhaps Tubular Bells(14) would be better. It is! */ + 54, 0, /* 39 FunnyVox, sounds alot like Bagpipe(109) and Shania(111) */ + 98, 0, /* 40 EchoBell, no real equiv, sounds like Crystal(98) */ + 96, 0, /* 41 IceRain */ + 68, 0, /* 42 Oboe 2001, no equiv, just patching it to normal oboe(68) */ + 95, 0, /* 43 EchoPans, no equiv, setting to SweepPad */ + 81, 0, /* 44 DoctorSolo Bank 8 */ + 87, 0, /* 45 SchoolDaze, no real equiv */ + 112, 0, /* 46 Bell Singer */ + 80, 0, /* 47 SquareWave */ + 48, 0, /* 48 Strings 1 */ + 48, 0, /* 49 Strings 2 - should be 49 */ + 44, 0, /* 50 Strings 3 (Synth) - Experimental set to Tremollo Strings - should be 50 */ + 45, 0, /* 51 Pizzicato Strings */ + 40, 0, /* 52 Violin 1 */ + 40, 0, /* 53 Violin 2 ? Viola */ + 42, 0, /* 54 Cello 1 */ + 42, 0, /* 55 Cello 2 */ + 43, 0, /* 56 Contrabass */ + 46, 0, /* 57 Harp 1 */ + 46, 0, /* 58 Harp 2 */ + 24, 0, /* 59 Guitar 1 (Nylon) */ + 25, 0, /* 60 Guitar 2 (Steel) */ + 26, 0, /* 61 Elec Guitar 1 */ + 27, 0, /* 62 Elec Guitar 2 */ + 104, 0, /* 63 Sitar */ + 32, 0, /* 64 Acou Bass 1 */ + 32, 0, /* 65 Acou Bass 2 */ + 33, 0, /* 66 Elec Bass 1 */ + 34, 0, /* 67 Elec Bass 2 */ + 36, 0, /* 68 Slap Bass 1 */ + 37, 0, /* 69 Slap Bass 2 */ + 35, 0, /* 70 Fretless Bass 1 */ + 35, 0, /* 71 Fretless Bass 2 */ + 73, 0, /* 72 Flute 1 */ + 73, 0, /* 73 Flute 2 */ + 72, 0, /* 74 Piccolo 1 */ + 72, 0, /* 75 Piccolo 2 */ + 74, 0, /* 76 Recorder */ + 75, 0, /* 77 Pan Pipes */ + 64, 0, /* 78 Sax 1 */ + 65, 0, /* 79 Sax 2 */ + 66, 0, /* 80 Sax 3 */ + 67, 0, /* 81 Sax 4 */ + 71, 0, /* 82 Clarinet 1 */ + 71, 0, /* 83 Clarinet 2 */ + 68, 0, /* 84 Oboe */ + 69, 0, /* 85 English Horn (Cor Anglais) */ + 70, 0, /* 86 Bassoon */ + 22, 0, /* 87 Harmonica */ + 56, 0, /* 88 Trumpet 1 */ + 56, 0, /* 89 Trumpet 2 */ + 57, 0, /* 90 Trombone 1 */ + 57, 0, /* 91 Trombone 2 */ + 60, 0, /* 92 French Horn 1 */ + 60, 0, /* 93 French Horn 2 */ + 58, 0, /* 94 Tuba */ + 61, 0, /* 95 Brass Section 1 */ + 61, 0, /* 96 Brass Section 2 */ + 11, 0, /* 97 Vibes 1 */ + 11, 0, /* 98 Vibes 2 */ + 99, 0, /* 99 Syn Mallet Bank 1 */ + 112, 0, /* 100 WindBell no real equiv Set to TinkleBell(112) */ + 9, 0, /* 101 Glockenspiel */ + 14, 0, /* 102 Tubular Bells */ + 13, 0, /* 103 Xylophone */ + 12, 0, /* 104 Marimba */ + 107, 0, /* 105 Koto */ + 111, 0, /* 106 Sho?? set to Shanai(111) */ + 77, 0, /* 107 Shakauhachi */ + 78, 0, /* 108 Whistle 1 */ + 78, 0, /* 109 Whistle 2 */ + 76, 0, /* 110 Bottle Blow */ + 76, 0, /* 111 Breathpipe no real equiv set to bottle blow(76) */ + 47, 0, /* 112 Timpani */ + 117, 0, /* 113 Melodic Tom */ + 116, 0, /* 114 Deap Snare no equiv, set to Taiko(116) */ + 118, 0, /* 115 Electric Perc 1 */ + 118, 0, /* 116 Electric Perc 2 */ + 116, 0, /* 117 Taiko */ + 115, 0, /* 118 Taiko Rim, no real equiv, set to Woodblock(115) */ + 119, 0, /* 119 Cymbal, no real equiv, set to reverse cymbal(119) */ + 115, 0, /* 120 Castanets, no real equiv, in GM set to Woodblock(115) */ + 112, 0, /* 121 Triangle, no real equiv, set to TinkleBell(112) */ + 55, 0, /* 122 Orchestral Hit */ + 124, 0, /* 123 Telephone */ + 123, 0, /* 124 BirdTweet */ + 94, 0, /* 125 Big Notes Pad no equiv, set to halo pad (94) */ + 98, 0, /* 126 Water Bell set to Crystal Pad(98) */ + 121, 0 /* 127 Jungle Tune set to Breath Noise */ +}; + +static int AdlMidi_xmi2midi(uint8_t *in, uint32_t insize, + uint8_t **out, uint32_t *outsize, + uint32_t convert_type) +{ + struct xmi2mid_xmi_ctx ctx; + unsigned int i; + int ret = -1; + + if (convert_type > XMIDI_CONVERT_MT32_TO_GS) { + /*_WM_ERROR_NEW("%s:%i: %d is an invalid conversion type.", __FUNCTION__, __LINE__, convert_type);*/ + return (ret); + } + + memset(&ctx, 0, sizeof(struct xmi2mid_xmi_ctx)); + ctx.src = ctx.src_ptr = in; + ctx.srcsize = insize; + ctx.convert_type = convert_type; + + if (xmi2mid_ParseXMI(&ctx) < 0) { + /*_WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_XMI, NULL, 0);*/ + goto _end; + } + + if (xmi2mid_ExtractTracks(&ctx) < 0) { + /*_WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_MIDI, NULL, 0);*/ + goto _end; + } + + ctx.dst = (uint8_t*)malloc(DST_CHUNK); + ctx.dst_ptr = ctx.dst; + ctx.dstsize = DST_CHUNK; + ctx.dstrem = DST_CHUNK; + + /* Header is 14 bytes long and add the rest as well */ + xmi2mid_write1(&ctx, 'M'); + xmi2mid_write1(&ctx, 'T'); + xmi2mid_write1(&ctx, 'h'); + xmi2mid_write1(&ctx, 'd'); + + xmi2mid_write4(&ctx, 6); + + xmi2mid_write2(&ctx, ctx.info.type); + xmi2mid_write2(&ctx, ctx.info.tracks); + xmi2mid_write2(&ctx, ctx.timing[0]);/* write divisions from track0 */ + + for (i = 0; i < ctx.info.tracks; i++) + xmi2mid_ConvertListToMTrk(&ctx, ctx.events[i]); + *out = ctx.dst; + *outsize = ctx.dstsize - ctx.dstrem; + ret = 0; + +_end: /* cleanup */ + if (ret < 0) { + free(ctx.dst); + *out = NULL; + *outsize = 0; + } + if (ctx.events) { + for (i = 0; i < ctx.info.tracks; i++) + xmi2mid_DeleteEventList(ctx.events[i]); + free(ctx.events); + } + free(ctx.timing); + + return (ret); +} + +static void xmi2mid_DeleteEventList(midi_event *mlist) { + midi_event *event; + midi_event *next; + + next = mlist; + + while ((event = next) != NULL) { + next = event->next; + free(event->buffer); + free(event); + } +} + +/* Sets current to the new event and updates list */ +static void xmi2mid_CreateNewEvent(struct xmi2mid_xmi_ctx *ctx, int32_t time) { + if (!ctx->list) { + ctx->list = ctx->current = (struct _xmi2mid_midi_event *)calloc(1, sizeof(midi_event)); + ctx->current->time = (time < 0)? 0 : time; + return; + } + + if (time < 0) { + midi_event *event = (midi_event *)calloc(1, sizeof(midi_event)); + event->next = ctx->list; + ctx->list = ctx->current = event; + return; + } + + if (ctx->current->time > time) + ctx->current = ctx->list; + + while (ctx->current->next) { + if (ctx->current->next->time > time) { + midi_event *event = (midi_event *)calloc(1, sizeof(midi_event)); + event->next = ctx->current->next; + ctx->current->next = event; + ctx->current = event; + ctx->current->time = time; + return; + } + + ctx->current = ctx->current->next; + } + + ctx->current->next = (struct _xmi2mid_midi_event *)calloc(1, sizeof(midi_event)); + ctx->current = ctx->current->next; + ctx->current->time = time; +} + +/* Conventional Variable Length Quantity */ +static int xmi2mid_GetVLQ(struct xmi2mid_xmi_ctx *ctx, uint32_t *quant) { + int i; + uint32_t data; + + *quant = 0; + for (i = 0; i < 4; i++) { + data = xmi2mid_read1(ctx); + *quant <<= 7; + *quant |= data & 0x7F; + + if (!(data & 0x80)) { + i++; + break; + } + } + return (i); +} + +/* XMIDI Delta Variable Length Quantity */ +static int xmi2mid_GetVLQ2(struct xmi2mid_xmi_ctx *ctx, uint32_t *quant) { + int i; + int32_t data; + + *quant = 0; + for (i = 0; i < 4; i++) { + data = xmi2mid_read1(ctx); + if (data & 0x80) { + xmi2mid_skipsrc(ctx, -1); + break; + } + *quant += data; + } + return (i); +} + +static int xmi2mid_PutVLQ(struct xmi2mid_xmi_ctx *ctx, uint32_t value) { + int32_t buffer; + int i = 1, j; + buffer = value & 0x7F; + while (value >>= 7) { + buffer <<= 8; + buffer |= ((value & 0x7F) | 0x80); + i++; + } + for (j = 0; j < i; j++) { + xmi2mid_write1(ctx, buffer & 0xFF); + buffer >>= 8; + } + + return (i); +} + +/* Converts Events + * + * Source is at the first data byte + * size 1 is single data byte + * size 2 is dual data byte + * size 3 is XMI Note on + * Returns bytes converted */ +static int xmi2mid_ConvertEvent(struct xmi2mid_xmi_ctx *ctx, const int32_t time, + const uint8_t status, const int size) { + uint32_t delta = 0; + int32_t data; + midi_event *prev; + int i; + + data = xmi2mid_read1(ctx); + + /*HACK!*/ + if (((status >> 4) == 0xB) && (status & 0xF) != 9 && (data == 114)) { + data = 32; /*Change XMI 114 controller into XG bank*/ + } + + /* Bank changes are handled here */ + if ((status >> 4) == 0xB && data == 0) { + data = xmi2mid_read1(ctx); + + ctx->bank127[status & 0xF] = 0; + + if ( ctx->convert_type == XMIDI_CONVERT_MT32_TO_GM || + ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS || + ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127 || + (ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127DRUM + && (status & 0xF) == 9) ) + return (2); + + xmi2mid_CreateNewEvent(ctx, time); + ctx->current->status = status; + ctx->current->data[0] = 0; + ctx->current->data[1] = data == 127 ? 0 : data;/*HACK:*/ + + if (ctx->convert_type == XMIDI_CONVERT_GS127_TO_GS && data == 127) + ctx->bank127[status & 0xF] = 1; + + return (2); + } + + /* Handling for patch change mt32 conversion, probably should go elsewhere */ + if ((status >> 4) == 0xC && (status&0xF) != 9 + && ctx->convert_type != XMIDI_CONVERT_NOCONVERSION) + { + if (ctx->convert_type == XMIDI_CONVERT_MT32_TO_GM) + { + data = xmi2mid_mt32asgm[data]; + } + else if ((ctx->convert_type == XMIDI_CONVERT_GS127_TO_GS && ctx->bank127[status&0xF]) || + ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS || + ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127DRUM) + { + xmi2mid_CreateNewEvent (ctx, time); + ctx->current->status = 0xB0 | (status&0xF); + ctx->current->data[0] = 0; + ctx->current->data[1] = xmi2mid_mt32asgs[data*2+1]; + + data = xmi2mid_mt32asgs[data*2]; + } + else if (ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127) + { + xmi2mid_CreateNewEvent (ctx, time); + ctx->current->status = 0xB0 | (status&0xF); + ctx->current->data[0] = 0; + ctx->current->data[1] = 127; + } + } + /* Drum track handling */ + else if ((status >> 4) == 0xC && (status&0xF) == 9 && + (ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127DRUM || ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127)) + { + xmi2mid_CreateNewEvent (ctx, time); + ctx->current->status = 0xB9; + ctx->current->data[0] = 0; + ctx->current->data[1] = 127; + } + + xmi2mid_CreateNewEvent(ctx, time); + ctx->current->status = status; + + ctx->current->data[0] = data; + + if (size == 1) + return (1); + + ctx->current->data[1] = xmi2mid_read1(ctx); + + if (size == 2) + return (2); + + /* XMI Note On handling */ + prev = ctx->current; + i = xmi2mid_GetVLQ(ctx, &delta); + xmi2mid_CreateNewEvent(ctx, time + delta * 3); + + ctx->current->status = status; + ctx->current->data[0] = data; + ctx->current->data[1] = 0; + ctx->current = prev; + + return (i + 2); +} + +/* Simple routine to convert system messages */ +static int32_t xmi2mid_ConvertSystemMessage(struct xmi2mid_xmi_ctx *ctx, const int32_t time, + const uint8_t status) { + int32_t i = 0; + + xmi2mid_CreateNewEvent(ctx, time); + ctx->current->status = status; + + /* Handling of Meta events */ + if (status == 0xFF) { + ctx->current->data[0] = xmi2mid_read1(ctx); + i++; + } + + i += xmi2mid_GetVLQ(ctx, &ctx->current->len); + + if (!ctx->current->len) + return (i); + + ctx->current->buffer = (uint8_t *)malloc(sizeof(uint8_t)*ctx->current->len); + xmi2mid_copy(ctx, (char *) ctx->current->buffer, ctx->current->len); + + return (i + ctx->current->len); +} + +/* XMIDI and Midi to List + * Returns XMIDI PPQN */ +static int32_t xmi2mid_ConvertFiletoList(struct xmi2mid_xmi_ctx *ctx) { + int32_t time = 0; + uint32_t data; + int32_t end = 0; + int32_t tempo = 500000; + int32_t tempo_set = 0; + uint32_t status = 0; + uint32_t file_size = xmi2mid_getsrcsize(ctx); + + /* Set Drum track to correct setting if required */ + if (ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127) { + xmi2mid_CreateNewEvent(ctx, 0); + ctx->current->status = 0xB9; + ctx->current->data[0] = 0; + ctx->current->data[1] = 127; + } + + while (!end && xmi2mid_getsrcpos(ctx) < file_size) { + xmi2mid_GetVLQ2(ctx, &data); + time += data * 3; + + status = xmi2mid_read1(ctx); + + switch (status >> 4) { + case XMI2MID_MIDI_STATUS_NOTE_ON: + xmi2mid_ConvertEvent(ctx, time, status, 3); + break; + + /* 2 byte data */ + case XMI2MID_MIDI_STATUS_NOTE_OFF: + case XMI2MID_MIDI_STATUS_AFTERTOUCH: + case XMI2MID_MIDI_STATUS_CONTROLLER: + case XMI2MID_MIDI_STATUS_PITCH_WHEEL: + xmi2mid_ConvertEvent(ctx, time, status, 2); + break; + + /* 1 byte data */ + case XMI2MID_MIDI_STATUS_PROG_CHANGE: + case XMI2MID_MIDI_STATUS_PRESSURE: + xmi2mid_ConvertEvent(ctx, time, status, 1); + break; + + case XMI2MID_MIDI_STATUS_SYSEX: + if (status == 0xFF) { + int32_t pos = xmi2mid_getsrcpos(ctx); + uint32_t dat = xmi2mid_read1(ctx); + + if (dat == 0x2F) /* End */ + end = 1; + else if (dat == 0x51 && !tempo_set) /* Tempo. Need it for PPQN */ + { + xmi2mid_skipsrc(ctx, 1); + tempo = xmi2mid_read1(ctx) << 16; + tempo += xmi2mid_read1(ctx) << 8; + tempo += xmi2mid_read1(ctx); + tempo *= 3; + tempo_set = 1; + } else if (dat == 0x51 && tempo_set) /* Skip any other tempo changes */ + { + xmi2mid_GetVLQ(ctx, &dat); + xmi2mid_skipsrc(ctx, dat); + break; + } + + xmi2mid_seeksrc(ctx, pos); + } + xmi2mid_ConvertSystemMessage(ctx, time, status); + break; + + default: + break; + } + } + return ((tempo * 3) / 25000); +} + +/* Converts and event list to a MTrk + * Returns bytes of the array + * buf can be NULL */ +static uint32_t xmi2mid_ConvertListToMTrk(struct xmi2mid_xmi_ctx *ctx, midi_event *mlist) { + int32_t time = 0; + midi_event *event; + uint32_t delta; + uint8_t last_status = 0; + uint32_t i = 8; + uint32_t j; + uint32_t size_pos, cur_pos; + int end = 0; + + xmi2mid_write1(ctx, 'M'); + xmi2mid_write1(ctx, 'T'); + xmi2mid_write1(ctx, 'r'); + xmi2mid_write1(ctx, 'k'); + + size_pos = xmi2mid_getdstpos(ctx); + xmi2mid_skipdst(ctx, 4); + + for (event = mlist; event && !end; event = event->next) { + delta = (event->time - time); + time = event->time; + + i += xmi2mid_PutVLQ(ctx, delta); + + if ((event->status != last_status) || (event->status >= 0xF0)) { + xmi2mid_write1(ctx, event->status); + i++; + } + + last_status = event->status; + + switch (event->status >> 4) { + /* 2 bytes data + * Note off, Note on, Aftertouch, Controller and Pitch Wheel */ + case 0x8: + case 0x9: + case 0xA: + case 0xB: + case 0xE: + xmi2mid_write1(ctx, event->data[0]); + xmi2mid_write1(ctx, event->data[1]); + i += 2; + break; + + /* 1 bytes data + * Program Change and Channel Pressure */ + case 0xC: + case 0xD: + xmi2mid_write1(ctx, event->data[0]); + i++; + break; + + /* Variable length + * SysEx */ + case 0xF: + if (event->status == 0xFF) { + if (event->data[0] == 0x2f) + end = 1; + xmi2mid_write1(ctx, event->data[0]); + i++; + } + i += xmi2mid_PutVLQ(ctx, event->len); + if (event->len) { + for (j = 0; j < event->len; j++) { + xmi2mid_write1(ctx, event->buffer[j]); + i++; + } + } + break; + + /* Never occur */ + default: + /*_WM_DEBUG_MSG("%s: unrecognized event", __FUNCTION__);*/ + break; + } + } + + cur_pos = xmi2mid_getdstpos(ctx); + xmi2mid_seekdst(ctx, size_pos); + xmi2mid_write4(ctx, i - 8); + xmi2mid_seekdst(ctx, cur_pos); + + return (i); +} + +/* Assumes correct xmidi */ +static uint32_t xmi2mid_ExtractTracksFromXmi(struct xmi2mid_xmi_ctx *ctx) { + uint32_t num = 0; + signed short ppqn; + uint32_t len = 0; + int32_t begin; + char buf[32]; + + while (xmi2mid_getsrcpos(ctx) < xmi2mid_getsrcsize(ctx) && num != ctx->info.tracks) { + /* Read first 4 bytes of name */ + xmi2mid_copy(ctx, buf, 4); + len = xmi2mid_read4(ctx); + + /* Skip the FORM entries */ + if (!memcmp(buf, "FORM", 4)) { + xmi2mid_skipsrc(ctx, 4); + xmi2mid_copy(ctx, buf, 4); + len = xmi2mid_read4(ctx); + } + + if (memcmp(buf, "EVNT", 4)) { + xmi2mid_skipsrc(ctx, (len + 1) & ~1); + continue; + } + + ctx->list = NULL; + begin = xmi2mid_getsrcpos(ctx); + + /* Convert it */ + if (!(ppqn = xmi2mid_ConvertFiletoList(ctx))) { + /*_WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, NULL, 0);*/ + break; + } + ctx->timing[num] = ppqn; + ctx->events[num] = ctx->list; + + /* Increment Counter */ + num++; + + /* go to start of next track */ + xmi2mid_seeksrc(ctx, begin + ((len + 1) & ~1)); + } + + /* Return how many were converted */ + return (num); +} + +static int xmi2mid_ParseXMI(struct xmi2mid_xmi_ctx *ctx) { + uint32_t i; + uint32_t start; + uint32_t len; + uint32_t chunk_len; + uint32_t file_size; + char buf[32]; + + file_size = xmi2mid_getsrcsize(ctx); + if (xmi2mid_getsrcpos(ctx) + 8 > file_size) { +badfile: /*_WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0);*/ + return (-1); + } + + /* Read first 4 bytes of header */ + xmi2mid_copy(ctx, buf, 4); + + /* Could be XMIDI */ + if (!memcmp(buf, "FORM", 4)) { + /* Read length of */ + len = xmi2mid_read4(ctx); + + start = xmi2mid_getsrcpos(ctx); + if (start + 4 > file_size) + goto badfile; + + /* Read 4 bytes of type */ + xmi2mid_copy(ctx, buf, 4); + + /* XDIRless XMIDI, we can handle them here. */ + if (!memcmp(buf, "XMID", 4)) { + /*_WM_DEBUG_MSG("Warning: XMIDI without XDIR");*/ + ctx->info.tracks = 1; + } + /* Not an XMIDI that we recognise */ + else if (memcmp(buf, "XDIR", 4)) { + goto badfile; + } + else { /* Seems Valid */ + ctx->info.tracks = 0; + + for (i = 4; i < len; i++) { + /* check too short files */ + if (xmi2mid_getsrcpos(ctx) + 10 > file_size) + break; + + /* Read 4 bytes of type */ + xmi2mid_copy(ctx, buf, 4); + + /* Read length of chunk */ + chunk_len = xmi2mid_read4(ctx); + + /* Add eight bytes */ + i += 8; + + if (memcmp(buf, "INFO", 4)) { + /* Must align */ + xmi2mid_skipsrc(ctx, (chunk_len + 1) & ~1); + i += (chunk_len + 1) & ~1; + continue; + } + + /* Must be at least 2 bytes long */ + if (chunk_len < 2) + break; + + ctx->info.tracks = xmi2mid_read2(ctx); + + if(ctx->info.tracks > 4) { + ctx->info.tracks = 4; + } + + break; + } + + /* Didn't get to fill the header */ + if (ctx->info.tracks == 0) { + goto badfile; + } + + /* Ok now to start part 2 + * Goto the right place */ + xmi2mid_seeksrc(ctx, start + ((len + 1) & ~1)); + if (xmi2mid_getsrcpos(ctx) + 12 > file_size) + goto badfile; + + /* Read 4 bytes of type */ + xmi2mid_copy(ctx, buf, 4); + + if (memcmp(buf, "CAT ", 4)) { + /*_WM_ERROR_NEW("XMI error: expected \"CAT \", found \"%c%c%c%c\".", + buf[0], buf[1], buf[2], buf[3]);*/ + return (-1); + } + + /* Now read length of this track */ + xmi2mid_read4(ctx); + + /* Read 4 bytes of type */ + xmi2mid_copy(ctx, buf, 4); + + if (memcmp(buf, "XMID", 4)) { + /*_WM_ERROR_NEW("XMI error: expected \"XMID\", found \"%c%c%c%c\".", + buf[0], buf[1], buf[2], buf[3]);*/ + return (-1); + } + + /* Valid XMID */ + ctx->datastart = xmi2mid_getsrcpos(ctx); + return (0); + } + } + + return (-1); +} + +static int xmi2mid_ExtractTracks(struct xmi2mid_xmi_ctx *ctx) { + uint32_t i; + + ctx->events = (midi_event **)calloc(ctx->info.tracks, sizeof(midi_event*)); + ctx->timing = (int16_t *)calloc(ctx->info.tracks, sizeof(int16_t)); + /* type-2 for multi-tracks, type-0 otherwise */ + ctx->info.type = (ctx->info.tracks > 1)? 2 : 0; + + xmi2mid_seeksrc(ctx, ctx->datastart); + i = xmi2mid_ExtractTracksFromXmi(ctx); + + if (i != ctx->info.tracks) { + /*_WM_ERROR_NEW("XMI error: extracted only %u out of %u tracks from XMIDI", + ctx->info.tracks, i);*/ + return (-1); + } + + return (0); +} + diff --git a/engine/src/Libraries/adlmidi/chips/dosbox/dbopl.cpp b/engine/src/Libraries/adlmidi/chips/dosbox/dbopl.cpp new file mode 100644 index 0000000..7d78c5f --- /dev/null +++ b/engine/src/Libraries/adlmidi/chips/dosbox/dbopl.cpp @@ -0,0 +1,1618 @@ +/* + * Copyright (C) 2002-2018 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + DOSBox implementation of a combined Yamaha YMF262 and Yamaha YM3812 emulator. + Enabling the opl3 bit will switch the emulator to stereo opl3 output instead of regular mono opl2 + Except for the table generation it's all integer math + Can choose different types of generators, using muls and bigger tables, try different ones for slower platforms + The generation was based on the MAME implementation but tried to have it use less memory and be faster in general + MAME uses much bigger envelope tables and this will be the biggest cause of it sounding different at times + + //TODO Don't delay first operator 1 sample in opl3 mode + //TODO Maybe not use class method pointers but a regular function pointers with operator as first parameter + //TODO Fix panning for the Percussion channels, would any opl3 player use it and actually really change it though? + //TODO Check if having the same accuracy in all frequency multipliers sounds better or not + + //DUNNO Keyon in 4op, switch to 2op without keyoff. +*/ + + + +#include +#include +#include +#include "dbopl.h" + +#if defined(__GNUC__) && __GNUC__ > 3 +#define INLINE inline __attribute__((__always_inline__)) +#elif defined(_MSC_VER) +#define INLINE __forceinline +#else +#define INLINE inline +#endif + +#if defined(__GNUC__) +#if !defined(__clang__) +#define GCC_LIKELY(x) __builtin_expect(x, 1) +#define GCC_UNLIKELY(x) __builtin_expect(x, 0) +#else // !defined(__clang__) +#if !defined (__c2__) && defined(__has_builtin) +#if __has_builtin(__builtin_expect) +#define GCC_LIKELY(x) __builtin_expect(x, 1) +#define GCC_UNLIKELY(x) __builtin_expect(x, 0) +#endif // __has_builtin(__builtin_expect) +#endif // !defined (__c2__) && defined(__has_builtin) +#endif // !defined(__clang__) +#endif // defined(__GNUC__) + +#if !defined(GCC_LIKELY) +#define GCC_LIKELY(x) (x) +#define GCC_UNLIKELY(x) (x) +#endif + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +namespace DBOPL { + +#define OPLRATE ((double)(14318180.0 / 288.0)) +#define TREMOLO_TABLE 52 + +//Try to use most precision for frequencies +//Else try to keep different waves in synch +//#define WAVE_PRECISION 1 +#ifndef WAVE_PRECISION +//Wave bits available in the top of the 32bit range +//Original adlib uses 10.10, we use 10.22 +#define WAVE_BITS 10 +#else +//Need some extra bits at the top to have room for octaves and frequency multiplier +//We support to 8 times lower rate +//128 * 15 * 8 = 15350, 2^13.9, so need 14 bits +#define WAVE_BITS 14 +#endif +#define WAVE_SH ( 32 - WAVE_BITS ) +#define WAVE_MASK ( ( 1 << WAVE_SH ) - 1 ) + +//Use the same accuracy as the waves +#define LFO_SH ( WAVE_SH - 10 ) +//LFO is controlled by our tremolo 256 sample limit +#define LFO_MAX ( 256 << ( LFO_SH ) ) + + +//Maximum amount of attenuation bits +//Envelope goes to 511, 9 bits +#if (DBOPL_WAVE == WAVE_TABLEMUL ) +//Uses the value directly +#define ENV_BITS ( 9 ) +#else +//Add 3 bits here for more accuracy and would have to be shifted up either way +#define ENV_BITS ( 9 ) +#endif +//Limits of the envelope with those bits and when the envelope goes silent +#define ENV_MIN 0 +#define ENV_EXTRA ( ENV_BITS - 9 ) +#define ENV_MAX ( 511 << ENV_EXTRA ) +#define ENV_LIMIT ( ( 12 * 256) >> ( 3 - ENV_EXTRA ) ) +#define ENV_SILENT( _X_ ) ( (_X_) >= ENV_LIMIT ) + +//Attack/decay/release rate counter shift +#define RATE_SH 24 +#define RATE_MASK ( ( 1 << RATE_SH ) - 1 ) +//Has to fit within 16bit lookuptable +#define MUL_SH 16 + +//Check some ranges +#if ENV_EXTRA > 3 +#error Too many envelope bits +#endif + + +//How much to substract from the base value for the final attenuation +static const Bit8u KslCreateTable[16] = { + //0 will always be be lower than 7 * 8 + 64, 32, 24, 19, + 16, 12, 11, 10, + 8, 6, 5, 4, + 3, 2, 1, 0, +}; + +#define M(_X_) ((Bit8u)( (_X_) * 2)) +static const Bit8u FreqCreateTable[16] = { + M(0.5), M(1 ), M(2 ), M(3 ), M(4 ), M(5 ), M(6 ), M(7 ), + M(8 ), M(9 ), M(10), M(10), M(12), M(12), M(15), M(15) +}; +#undef M + +//We're not including the highest attack rate, that gets a special value +static const Bit8u AttackSamplesTable[13] = { + 69, 55, 46, 40, + 35, 29, 23, 20, + 19, 15, 11, 10, + 9 +}; +//On a real opl these values take 8 samples to reach and are based upon larger tables +static const Bit8u EnvelopeIncreaseTable[13] = { + 4, 5, 6, 7, + 8, 10, 12, 14, + 16, 20, 24, 28, + 32, +}; + +#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG ) +static Bit16u ExpTable[ 256 ]; +#endif + +#if ( DBOPL_WAVE == WAVE_HANDLER ) +//PI table used by WAVEHANDLER +static Bit16u SinTable[ 512 ]; +#endif + +#if ( DBOPL_WAVE > WAVE_HANDLER ) +//Layout of the waveform table in 512 entry intervals +//With overlapping waves we reduce the table to half it's size + +// | |//\\|____|WAV7|//__|/\ |____|/\/\| +// |\\//| | |WAV7| | \/| | | +// |06 |0126|17 |7 |3 |4 |4 5 |5 | + +//6 is just 0 shifted and masked + +static Bit16s WaveTable[ 8 * 512 ]; +//Distance into WaveTable the wave starts +static const Bit16u WaveBaseTable[8] = { + 0x000, 0x200, 0x200, 0x800, + 0xa00, 0xc00, 0x100, 0x400, + +}; +//Mask the counter with this +static const Bit16u WaveMaskTable[8] = { + 1023, 1023, 511, 511, + 1023, 1023, 512, 1023, +}; + +//Where to start the counter on at keyon +static const Bit16u WaveStartTable[8] = { + 512, 0, 0, 0, + 0, 512, 512, 256, +}; +#endif + +#if ( DBOPL_WAVE == WAVE_TABLEMUL ) +static Bit16u MulTable[ 384 ]; +#endif + +static Bit8u KslTable[ 8 * 16 ]; +static Bit8u TremoloTable[ TREMOLO_TABLE ]; +//Start of a channel behind the chip struct start +static Bit16u ChanOffsetTable[32]; +//Start of an operator behind the chip struct start +static Bit16u OpOffsetTable[64]; + +//The lower bits are the shift of the operator vibrato value +//The highest bit is right shifted to generate -1 or 0 for negation +//So taking the highest input value of 7 this gives 3, 7, 3, 0, -3, -7, -3, 0 +static const Bit8s VibratoTable[ 8 ] = { + 1 - 0x00, 0 - 0x00, 1 - 0x00, 30 - 0x00, + 1 - 0x80, 0 - 0x80, 1 - 0x80, 30 - 0x80 +}; + +//Shift strength for the ksl value determined by ksl strength +static const Bit8u KslShiftTable[4] = { + 31,1,2,0 +}; + +//Generate a table index and table shift value using input value from a selected rate +static void EnvelopeSelect( Bit8u val, Bit8u& index, Bit8u& shift ) { + if ( val < 13 * 4 ) { //Rate 0 - 12 + shift = 12 - ( val >> 2 ); + index = val & 3; + } else if ( val < 15 * 4 ) { //rate 13 - 14 + shift = 0; + index = val - 12 * 4; + } else { //rate 15 and up + shift = 0; + index = 12; + } +} + +#if ( DBOPL_WAVE == WAVE_HANDLER ) +/* + Generate the different waveforms out of the sine/exponetial table using handlers +*/ +static inline Bits MakeVolume( Bitu wave, Bitu volume ) { + Bitu total = wave + volume; + Bitu index = total & 0xff; + Bitu sig = ExpTable[ index ]; + Bitu exp = total >> 8; +#if 0 + //Check if we overflow the 31 shift limit + if ( exp >= 32 ) { + LOG_MSG( "WTF %d %d", total, exp ); + } +#endif + return (sig >> exp); +}; + +static Bits DB_FASTCALL WaveForm0( Bitu i, Bitu volume ) { + Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 + Bitu wave = SinTable[i & 511]; + return (MakeVolume( wave, volume ) ^ neg) - neg; +} +static Bits DB_FASTCALL WaveForm1( Bitu i, Bitu volume ) { + Bit32u wave = SinTable[i & 511]; + wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); + return MakeVolume( wave, volume ); +} +static Bits DB_FASTCALL WaveForm2( Bitu i, Bitu volume ) { + Bitu wave = SinTable[i & 511]; + return MakeVolume( wave, volume ); +} +static Bits DB_FASTCALL WaveForm3( Bitu i, Bitu volume ) { + Bitu wave = SinTable[i & 255]; + wave |= ( ( (i ^ 256 ) & 256) - 1) >> ( 32 - 12 ); + return MakeVolume( wave, volume ); +} +static Bits DB_FASTCALL WaveForm4( Bitu i, Bitu volume ) { + //Twice as fast + i <<= 1; + Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 + Bitu wave = SinTable[i & 511]; + wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); + return (MakeVolume( wave, volume ) ^ neg) - neg; +} +static Bits DB_FASTCALL WaveForm5( Bitu i, Bitu volume ) { + //Twice as fast + i <<= 1; + Bitu wave = SinTable[i & 511]; + wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); + return MakeVolume( wave, volume ); +} +static Bits DB_FASTCALL WaveForm6( Bitu i, Bitu volume ) { + Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 + return (MakeVolume( 0, volume ) ^ neg) - neg; +} +static Bits DB_FASTCALL WaveForm7( Bitu i, Bitu volume ) { + //Negative is reversed here + Bits neg = (( i >> 9) & 1) - 1; + Bitu wave = (i << 3); + //When negative the volume also runs backwards + wave = ((wave ^ neg) - neg) & 4095; + return (MakeVolume( wave, volume ) ^ neg) - neg; +} + +static const WaveHandler WaveHandlerTable[8] = { + WaveForm0, WaveForm1, WaveForm2, WaveForm3, + WaveForm4, WaveForm5, WaveForm6, WaveForm7 +}; + +#endif + +/* + Operator +*/ + +//We zero out when rate == 0 +inline void Operator::UpdateAttack( const Chip* chip ) { + Bit8u rate = reg60 >> 4; + if ( rate ) { + Bit8u val = (rate << 2) + ksr; + attackAdd = chip->attackRates[ val ]; + rateZero &= ~(1 << ATTACK); + } else { + attackAdd = 0; + rateZero |= (1 << ATTACK); + } +} +inline void Operator::UpdateDecay( const Chip* chip ) { + Bit8u rate = reg60 & 0xf; + if ( rate ) { + Bit8u val = (rate << 2) + ksr; + decayAdd = chip->linearRates[ val ]; + rateZero &= ~(1 << DECAY); + } else { + decayAdd = 0; + rateZero |= (1 << DECAY); + } +} +inline void Operator::UpdateRelease( const Chip* chip ) { + Bit8u rate = reg80 & 0xf; + if ( rate ) { + Bit8u val = (rate << 2) + ksr; + releaseAdd = chip->linearRates[ val ]; + rateZero &= ~(1 << RELEASE); + if ( !(reg20 & MASK_SUSTAIN ) ) { + rateZero &= ~( 1 << SUSTAIN ); + } + } else { + rateZero |= (1 << RELEASE); + releaseAdd = 0; + if ( !(reg20 & MASK_SUSTAIN ) ) { + rateZero |= ( 1 << SUSTAIN ); + } + } +} + +inline void Operator::UpdateAttenuation( ) { + Bit8u kslBase = (Bit8u)((chanData >> SHIFT_KSLBASE) & 0xff); + Bit32u tl = reg40 & 0x3f; + Bit8u kslShift = KslShiftTable[ reg40 >> 6 ]; + //Make sure the attenuation goes to the right bits + totalLevel = tl << ( ENV_BITS - 7 ); //Total level goes 2 bits below max + totalLevel += ( kslBase << ENV_EXTRA ) >> kslShift; +} + +void Operator::UpdateFrequency( ) { + Bit32u freq = chanData & (( 1 << 10 ) - 1); + Bit32u block = (chanData >> 10) & 0xff; +#ifdef WAVE_PRECISION + block = 7 - block; + waveAdd = ( freq * freqMul ) >> block; +#else + waveAdd = ( freq << block ) * freqMul; +#endif + if ( reg20 & MASK_VIBRATO ) { + vibStrength = (Bit8u)(freq >> 7); + +#ifdef WAVE_PRECISION + vibrato = ( vibStrength * freqMul ) >> block; +#else + vibrato = ( vibStrength << block ) * freqMul; +#endif + } else { + vibStrength = 0; + vibrato = 0; + } +} + +void Operator::UpdateRates( const Chip* chip ) { + //Mame seems to reverse this where enabling ksr actually lowers + //the rate, but pdf manuals says otherwise? + Bit8u newKsr = (Bit8u)((chanData >> SHIFT_KEYCODE) & 0xff); + if ( !( reg20 & MASK_KSR ) ) { + newKsr >>= 2; + } + if ( ksr == newKsr ) + return; + ksr = newKsr; + UpdateAttack( chip ); + UpdateDecay( chip ); + UpdateRelease( chip ); +} + +INLINE Bit32s Operator::RateForward( Bit32u add ) { + rateIndex += add; + Bit32s ret = rateIndex >> RATE_SH; + rateIndex = rateIndex & RATE_MASK; + return ret; +} + +template< Operator::State yes> +Bits Operator::TemplateVolume( ) { + Bit32s vol = volume; + Bit32s change; + switch ( yes ) { + case OFF: + return ENV_MAX; + case ATTACK: + change = RateForward( attackAdd ); + if ( !change ) + return vol; + vol += ( (~vol) * change ) >> 3; + if ( vol < ENV_MIN ) { + volume = ENV_MIN; + rateIndex = 0; + SetState( DECAY ); + return ENV_MIN; + } + break; + case DECAY: + vol += RateForward( decayAdd ); + if ( GCC_UNLIKELY(vol >= sustainLevel) ) { + //Check if we didn't overshoot max attenuation, then just go off + if ( GCC_UNLIKELY(vol >= ENV_MAX) ) { + volume = ENV_MAX; + SetState( OFF ); + return ENV_MAX; + } + //Continue as sustain + rateIndex = 0; + SetState( SUSTAIN ); + } + break; + case SUSTAIN: + if ( reg20 & MASK_SUSTAIN ) { + return vol; + } + //In sustain phase, but not sustaining, do regular release + case RELEASE: + vol += RateForward( releaseAdd );; + if ( GCC_UNLIKELY(vol >= ENV_MAX) ) { + volume = ENV_MAX; + SetState( OFF ); + return ENV_MAX; + } + break; + } + volume = vol; + return vol; +} + +static const VolumeHandler VolumeHandlerTable[5] = { + &Operator::TemplateVolume< Operator::OFF >, + &Operator::TemplateVolume< Operator::RELEASE >, + &Operator::TemplateVolume< Operator::SUSTAIN >, + &Operator::TemplateVolume< Operator::DECAY >, + &Operator::TemplateVolume< Operator::ATTACK > +}; + +INLINE Bitu Operator::ForwardVolume() { + return currentLevel + (this->*volHandler)(); +} + + +INLINE Bitu Operator::ForwardWave() { + waveIndex += waveCurrent; + return waveIndex >> WAVE_SH; +} + +void Operator::Write20( const Chip* chip, Bit8u val ) { + Bit8u change = (reg20 ^ val ); + if ( !change ) + return; + reg20 = val; + //Shift the tremolo bit over the entire register, saved a branch, YES! + tremoloMask = (Bit8s)(val) >> 7; + tremoloMask &= ~(( 1 << ENV_EXTRA ) -1); + //Update specific features based on changes + if ( change & MASK_KSR ) { + UpdateRates( chip ); + } + //With sustain enable the volume doesn't change + if ( reg20 & MASK_SUSTAIN || ( !releaseAdd ) ) { + rateZero |= ( 1 << SUSTAIN ); + } else { + rateZero &= ~( 1 << SUSTAIN ); + } + //Frequency multiplier or vibrato changed + if ( change & (0xf | MASK_VIBRATO) ) { + freqMul = chip->freqMul[ val & 0xf ]; + UpdateFrequency(); + } +} + +void Operator::Write40( const Chip* /*chip*/, Bit8u val ) { + if (!(reg40 ^ val )) + return; + reg40 = val; + UpdateAttenuation( ); +} + +void Operator::Write60( const Chip* chip, Bit8u val ) { + Bit8u change = reg60 ^ val; + reg60 = val; + if ( change & 0x0f ) { + UpdateDecay( chip ); + } + if ( change & 0xf0 ) { + UpdateAttack( chip ); + } +} + +void Operator::Write80( const Chip* chip, Bit8u val ) { + Bit8u change = (reg80 ^ val ); + if ( !change ) + return; + reg80 = val; + Bit8u sustain = val >> 4; + //Turn 0xf into 0x1f + sustain |= ( sustain + 1) & 0x10; + sustainLevel = sustain << ( ENV_BITS - 5 ); + if ( change & 0x0f ) { + UpdateRelease( chip ); + } +} + +void Operator::WriteE0( const Chip* chip, Bit8u val ) { + if ( !(regE0 ^ val) ) + return; + //in opl3 mode you can always selet 7 waveforms regardless of waveformselect + Bit8u waveForm = val & ( ( 0x3 & chip->waveFormMask ) | (0x7 & chip->opl3Active ) ); + regE0 = val; +#if ( DBOPL_WAVE == WAVE_HANDLER ) + waveHandler = WaveHandlerTable[ waveForm ]; +#else + waveBase = WaveTable + WaveBaseTable[ waveForm ]; + waveStart = WaveStartTable[ waveForm ] << WAVE_SH; + waveMask = WaveMaskTable[ waveForm ]; +#endif +} + +INLINE void Operator::SetState( Bit8u s ) { + state = s; + volHandler = VolumeHandlerTable[ s ]; +} + +INLINE bool Operator::Silent() const { + if ( !ENV_SILENT( totalLevel + volume ) ) + return false; + if ( !(rateZero & ( 1 << state ) ) ) + return false; + return true; +} + +INLINE void Operator::Prepare( const Chip* chip ) { + currentLevel = totalLevel + (chip->tremoloValue & tremoloMask); + waveCurrent = waveAdd; + if ( vibStrength >> chip->vibratoShift ) { + Bit32s add = vibrato >> chip->vibratoShift; + //Sign extend over the shift value + Bit32s neg = chip->vibratoSign; + //Negate the add with -1 or 0 + add = ( add ^ neg ) - neg; + waveCurrent += add; + } +} + +void Operator::KeyOn( Bit8u mask ) { + if ( !keyOn ) { + //Restart the frequency generator +#if ( DBOPL_WAVE > WAVE_HANDLER ) + waveIndex = waveStart; +#else + waveIndex = 0; +#endif + rateIndex = 0; + SetState( ATTACK ); + } + keyOn |= mask; +} + +void Operator::KeyOff( Bit8u mask ) { + keyOn &= ~mask; + if ( !keyOn ) { + if ( state != OFF ) { + SetState( RELEASE ); + } + } +} + +INLINE Bits Operator::GetWave( Bitu index, Bitu vol ) { +#if ( DBOPL_WAVE == WAVE_HANDLER ) + return waveHandler( index, vol << ( 3 - ENV_EXTRA ) ); +#elif ( DBOPL_WAVE == WAVE_TABLEMUL ) + return (waveBase[ index & waveMask ] * MulTable[ vol >> ENV_EXTRA ]) >> MUL_SH; +#elif ( DBOPL_WAVE == WAVE_TABLELOG ) + Bit32s wave = waveBase[ index & waveMask ]; + Bit32u total = ( wave & 0x7fff ) + vol << ( 3 - ENV_EXTRA ); + Bit32s sig = ExpTable[ total & 0xff ]; + Bit32u exp = total >> 8; + Bit32s neg = wave >> 16; + return ((sig ^ neg) - neg) >> exp; +#else +#error "No valid wave routine" +#endif +} + +Bits INLINE Operator::GetSample( Bits modulation ) { + Bitu vol = ForwardVolume(); + if ( ENV_SILENT( vol ) ) { + //Simply forward the wave + waveIndex += waveCurrent; + return 0; + } else { + Bitu index = ForwardWave(); + index += modulation; + return GetWave( index, vol ); + } +} + +Operator::Operator() { + chanData = 0; + freqMul = 0; + waveIndex = 0; + waveAdd = 0; + waveCurrent = 0; + keyOn = 0; + ksr = 0; + reg20 = 0; + reg40 = 0; + reg60 = 0; + reg80 = 0; + regE0 = 0; + SetState( OFF ); + rateZero = (1 << OFF); + sustainLevel = ENV_MAX; + currentLevel = ENV_MAX; + totalLevel = ENV_MAX; + volume = ENV_MAX; + releaseAdd = 0; +} + +/* + Channel +*/ + +Channel::Channel() { + old[0] = old[1] = 0; + chanData = 0; + regB0 = 0; + regC0 = 0; + maskLeft = -1; + maskRight = -1; + feedback = 31; + fourMask = 0; + synthHandler = &Channel::BlockTemplate< sm2FM >; +} + +void Channel::SetChanData( const Chip* chip, Bit32u data ) { + Bit32u change = chanData ^ data; + chanData = data; + Op( 0 )->chanData = data; + Op( 1 )->chanData = data; + //Since a frequency update triggered this, always update frequency + Op( 0 )->UpdateFrequency(); + Op( 1 )->UpdateFrequency(); + if ( change & ( 0xff << SHIFT_KSLBASE ) ) { + Op( 0 )->UpdateAttenuation(); + Op( 1 )->UpdateAttenuation(); + } + if ( change & ( 0xff << SHIFT_KEYCODE ) ) { + Op( 0 )->UpdateRates( chip ); + Op( 1 )->UpdateRates( chip ); + } +} + +void Channel::UpdateFrequency( const Chip* chip, Bit8u fourOp ) { + //Extrace the frequency bits + Bit32u data = chanData & 0xffff; + Bit32u kslBase = KslTable[ data >> 6 ]; + Bit32u keyCode = ( data & 0x1c00) >> 9; + if ( chip->reg08 & 0x40 ) { + keyCode |= ( data & 0x100)>>8; /* notesel == 1 */ + } else { + keyCode |= ( data & 0x200)>>9; /* notesel == 0 */ + } + //Add the keycode and ksl into the highest bits of chanData + data |= (keyCode << SHIFT_KEYCODE) | ( kslBase << SHIFT_KSLBASE ); + ( this + 0 )->SetChanData( chip, data ); + if ( fourOp & 0x3f ) { + ( this + 1 )->SetChanData( chip, data ); + } +} + +void Channel::WriteA0( const Chip* chip, Bit8u val ) { + Bit8u fourOp = chip->reg104 & chip->opl3Active & fourMask; + //Don't handle writes to silent fourop channels + if ( fourOp > 0x80 ) + return; + Bit32u change = (chanData ^ val ) & 0xff; + if ( change ) { + chanData ^= change; + UpdateFrequency( chip, fourOp ); + } +} + +void Channel::WriteB0( const Chip* chip, Bit8u val ) { + Bit8u fourOp = chip->reg104 & chip->opl3Active & fourMask; + //Don't handle writes to silent fourop channels + if ( fourOp > 0x80 ) + return; + Bitu change = (chanData ^ ( val << 8 ) ) & 0x1f00; + if ( change ) { + chanData ^= change; + UpdateFrequency( chip, fourOp ); + } + //Check for a change in the keyon/off state + if ( !(( val ^ regB0) & 0x20)) + return; + regB0 = val; + if ( val & 0x20 ) { + Op(0)->KeyOn( 0x1 ); + Op(1)->KeyOn( 0x1 ); + if ( fourOp & 0x3f ) { + ( this + 1 )->Op(0)->KeyOn( 1 ); + ( this + 1 )->Op(1)->KeyOn( 1 ); + } + } else { + Op(0)->KeyOff( 0x1 ); + Op(1)->KeyOff( 0x1 ); + if ( fourOp & 0x3f ) { + ( this + 1 )->Op(0)->KeyOff( 1 ); + ( this + 1 )->Op(1)->KeyOff( 1 ); + } + } +} + +void Channel::WriteC0(const Chip* chip, Bit8u val) { + Bit8u change = val ^ regC0; + if (!change) + return; + regC0 = val; + feedback = (regC0 >> 1) & 7; + if (feedback) { + //We shift the input to the right 10 bit wave index value + feedback = 9 - feedback; + } + else { + feedback = 31; + } + UpdateSynth(chip); +} + +void Channel::UpdateSynth( const Chip* chip ) { + //Select the new synth mode + if ( chip->opl3Active ) { + //4-op mode enabled for this channel + if ( (chip->reg104 & fourMask) & 0x3f ) { + Channel* chan0, *chan1; + //Check if it's the 2nd channel in a 4-op + if ( !(fourMask & 0x80 ) ) { + chan0 = this; + chan1 = this + 1; + } else { + chan0 = this - 1; + chan1 = this; + } + + Bit8u synth = ( (chan0->regC0 & 1) << 0 )| (( chan1->regC0 & 1) << 1 ); + switch ( synth ) { + case 0: + chan0->synthHandler = &Channel::BlockTemplate< sm3FMFM >; + break; + case 1: + chan0->synthHandler = &Channel::BlockTemplate< sm3AMFM >; + break; + case 2: + chan0->synthHandler = &Channel::BlockTemplate< sm3FMAM >; + break; + case 3: + chan0->synthHandler = &Channel::BlockTemplate< sm3AMAM >; + break; + } + //Disable updating percussion channels + } else if ((fourMask & 0x40) && ( chip->regBD & 0x20) ) { + + //Regular dual op, am or fm + } else if (regC0 & 1 ) { + synthHandler = &Channel::BlockTemplate< sm3AM >; + } else { + synthHandler = &Channel::BlockTemplate< sm3FM >; + } + maskLeft = (regC0 & 0x10 ) ? -1 : 0; + maskRight = (regC0 & 0x20 ) ? -1 : 0; + //opl2 active + } else { + //Disable updating percussion channels + if ( (fourMask & 0x40) && ( chip->regBD & 0x20 ) ) { + + //Regular dual op, am or fm + } else if (regC0 & 1 ) { + synthHandler = &Channel::BlockTemplate< sm2AM >; + } else { + synthHandler = &Channel::BlockTemplate< sm2FM >; + } + } +} + +template< bool opl3Mode> +INLINE void Channel::GeneratePercussion( Chip* chip, Bit32s* output ) { + Channel* chan = this; + + //BassDrum + Bit32s mod = (Bit32u)((old[0] + old[1])) >> feedback; + old[0] = old[1]; + old[1] = static_cast(Op(0)->GetSample( mod )); + + //When bassdrum is in AM mode first operator is ignoed + if ( chan->regC0 & 1 ) { + mod = 0; + } else { + mod = old[0]; + } + Bit32s sample = static_cast(Op(1)->GetSample( mod )); + + + //Precalculate stuff used by other outputs + Bit32u noiseBit = chip->ForwardNoise() & 0x1; + Bit32u c2 = static_cast(Op(2)->ForwardWave()); + Bit32u c5 = static_cast(Op(5)->ForwardWave()); + Bit32u phaseBit = (((c2 & 0x88) ^ ((c2<<5) & 0x80)) | ((c5 ^ (c5<<2)) & 0x20)) ? 0x02 : 0x00; + + //Hi-Hat + Bit32u hhVol = static_cast(Op(2)->ForwardVolume()); + if ( !ENV_SILENT( hhVol ) ) { + Bit32u hhIndex = (phaseBit<<8) | (0x34 << ( phaseBit ^ (noiseBit << 1 ))); + sample += static_cast(Op(2)->GetWave( hhIndex, hhVol )); + } + //Snare Drum + Bit32u sdVol = static_cast(Op(3)->ForwardVolume()); + if ( !ENV_SILENT( sdVol ) ) { + Bit32u sdIndex = ( 0x100 + (c2 & 0x100) ) ^ ( noiseBit << 8 ); + sample += static_cast(Op(3)->GetWave( sdIndex, sdVol )); + } + //Tom-tom + sample += static_cast(Op(4)->GetSample( 0 )); + + //Top-Cymbal + Bit32u tcVol = static_cast(Op(5)->ForwardVolume()); + if ( !ENV_SILENT( tcVol ) ) { + Bit32u tcIndex = (1 + phaseBit) << 8; + sample += static_cast(Op(5)->GetWave( tcIndex, tcVol )); + } + sample <<= 1; + if ( opl3Mode ) { + output[0] += sample; + output[1] += sample; + } else { + output[0] += sample; + } +} + +template +Channel* Channel::BlockTemplate( Chip* chip, Bit32u samples, Bit32s* output ) { + switch( mode ) { + case sm2AM: + case sm3AM: + if ( Op(0)->Silent() && Op(1)->Silent() ) { + old[0] = old[1] = 0; + return (this + 1); + } + break; + case sm2FM: + case sm3FM: + if ( Op(1)->Silent() ) { + old[0] = old[1] = 0; + return (this + 1); + } + break; + case sm3FMFM: + if ( Op(3)->Silent() ) { + old[0] = old[1] = 0; + return (this + 2); + } + break; + case sm3AMFM: + if ( Op(0)->Silent() && Op(3)->Silent() ) { + old[0] = old[1] = 0; + return (this + 2); + } + break; + case sm3FMAM: + if ( Op(1)->Silent() && Op(3)->Silent() ) { + old[0] = old[1] = 0; + return (this + 2); + } + break; + case sm3AMAM: + if ( Op(0)->Silent() && Op(2)->Silent() && Op(3)->Silent() ) { + old[0] = old[1] = 0; + return (this + 2); + } + break; + default: + break; + } + //Init the operators with the the current vibrato and tremolo values + Op( 0 )->Prepare( chip ); + Op( 1 )->Prepare( chip ); + if ( mode > sm4Start ) { + Op( 2 )->Prepare( chip ); + Op( 3 )->Prepare( chip ); + } + if ( mode > sm6Start ) { + Op( 4 )->Prepare( chip ); + Op( 5 )->Prepare( chip ); + } + for ( Bitu i = 0; i < samples; i++ ) { + //Early out for percussion handlers + if ( mode == sm2Percussion ) { + GeneratePercussion( chip, output + i ); + continue; //Prevent some unitialized value bitching + } else if ( mode == sm3Percussion ) { + GeneratePercussion( chip, output + i * 2 ); + continue; //Prevent some unitialized value bitching + } + + //Do unsigned shift so we can shift out all bits but still stay in 10 bit range otherwise + Bit32s mod = (Bit32u)((old[0] + old[1])) >> feedback; + old[0] = old[1]; + old[1] = static_cast(Op(0)->GetSample( mod )); + Bit32s sample; + Bit32s out0 = old[0]; + if ( mode == sm2AM || mode == sm3AM ) { + sample = static_cast(out0 + Op(1)->GetSample( 0 )); + } else if ( mode == sm2FM || mode == sm3FM ) { + sample = static_cast(Op(1)->GetSample( out0 )); + } else if ( mode == sm3FMFM ) { + Bits next = Op(1)->GetSample( out0 ); + next = Op(2)->GetSample( next ); + sample = static_cast(Op(3)->GetSample( next )); + } else if ( mode == sm3AMFM ) { + sample = out0; + Bits next = Op(1)->GetSample( 0 ); + next = Op(2)->GetSample( next ); + sample += static_cast(Op(3)->GetSample( next )); + } else if ( mode == sm3FMAM ) { + sample = static_cast(Op(1)->GetSample( out0 )); + Bits next = Op(2)->GetSample( 0 ); + sample += static_cast(Op(3)->GetSample( next )); + } else if ( mode == sm3AMAM ) { + sample = out0; + Bits next = Op(1)->GetSample( 0 ); + sample += static_cast(Op(2)->GetSample( next )); + sample += static_cast(Op(3)->GetSample( 0 )); + } + switch( mode ) { + case sm2AM: + case sm2FM: + output[ i ] += sample; + break; + case sm3AM: + case sm3FM: + case sm3FMFM: + case sm3AMFM: + case sm3FMAM: + case sm3AMAM: + output[ i * 2 + 0 ] += sample & maskLeft; + output[ i * 2 + 1 ] += sample & maskRight; + break; + default: + break; + } + } + switch( mode ) { + case sm2AM: + case sm2FM: + case sm3AM: + case sm3FM: + return ( this + 1 ); + case sm3FMFM: + case sm3AMFM: + case sm3FMAM: + case sm3AMAM: + return( this + 2 ); + case sm2Percussion: + case sm3Percussion: + return( this + 3 ); + } + return 0; +} + +/* + Chip +*/ + +Chip::Chip() { + reg08 = 0; + reg04 = 0; + regBD = 0; + reg104 = 0; + opl3Active = 0; +} + +INLINE Bit32u Chip::ForwardNoise() { + noiseCounter += noiseAdd; + Bitu count = noiseCounter >> LFO_SH; + noiseCounter &= WAVE_MASK; + for ( ; count > 0; --count ) { + //Noise calculation from mame + noiseValue ^= ( 0x800302 ) & ( 0 - (noiseValue & 1 ) ); + noiseValue >>= 1; + } + return noiseValue; +} + +INLINE Bit32u Chip::ForwardLFO( Bit32u samples ) { + //Current vibrato value, runs 4x slower than tremolo + vibratoSign = ( VibratoTable[ vibratoIndex >> 2] ) >> 7; + vibratoShift = ( VibratoTable[ vibratoIndex >> 2] & 7) + vibratoStrength; + tremoloValue = TremoloTable[ tremoloIndex ] >> tremoloStrength; + + //Check hom many samples there can be done before the value changes + Bit32u todo = LFO_MAX - lfoCounter; + Bit32u count = (todo + lfoAdd - 1) / lfoAdd; + if ( count > samples ) { + count = samples; + lfoCounter += count * lfoAdd; + } else { + lfoCounter += count * lfoAdd; + lfoCounter &= (LFO_MAX - 1); + //Maximum of 7 vibrato value * 4 + vibratoIndex = ( vibratoIndex + 1 ) & 31; + //Clip tremolo to the the table size + if ( tremoloIndex + 1 < TREMOLO_TABLE ) + ++tremoloIndex; + else + tremoloIndex = 0; + } + return count; +} + + +void Chip::WriteBD( Bit8u val ) { + Bit8u change = regBD ^ val; + if ( !change ) + return; + regBD = val; + //TODO could do this with shift and xor? + vibratoStrength = (val & 0x40) ? 0x00 : 0x01; + tremoloStrength = (val & 0x80) ? 0x00 : 0x02; + if ( val & 0x20 ) { + //Drum was just enabled, make sure channel 6 has the right synth + if ( change & 0x20 ) { + if ( opl3Active ) { + chan[6].synthHandler = &Channel::BlockTemplate< sm3Percussion >; + } else { + chan[6].synthHandler = &Channel::BlockTemplate< sm2Percussion >; + } + } + //Bass Drum + if ( val & 0x10 ) { + chan[6].op[0].KeyOn( 0x2 ); + chan[6].op[1].KeyOn( 0x2 ); + } else { + chan[6].op[0].KeyOff( 0x2 ); + chan[6].op[1].KeyOff( 0x2 ); + } + //Hi-Hat + if ( val & 0x1 ) { + chan[7].op[0].KeyOn( 0x2 ); + } else { + chan[7].op[0].KeyOff( 0x2 ); + } + //Snare + if ( val & 0x8 ) { + chan[7].op[1].KeyOn( 0x2 ); + } else { + chan[7].op[1].KeyOff( 0x2 ); + } + //Tom-Tom + if ( val & 0x4 ) { + chan[8].op[0].KeyOn( 0x2 ); + } else { + chan[8].op[0].KeyOff( 0x2 ); + } + //Top Cymbal + if ( val & 0x2 ) { + chan[8].op[1].KeyOn( 0x2 ); + } else { + chan[8].op[1].KeyOff( 0x2 ); + } + //Toggle keyoffs when we turn off the percussion + } else if ( change & 0x20 ) { + //Trigger a reset to setup the original synth handler + //This makes it call + chan[6].UpdateSynth( this ); + chan[6].op[0].KeyOff( 0x2 ); + chan[6].op[1].KeyOff( 0x2 ); + chan[7].op[0].KeyOff( 0x2 ); + chan[7].op[1].KeyOff( 0x2 ); + chan[8].op[0].KeyOff( 0x2 ); + chan[8].op[1].KeyOff( 0x2 ); + } +} + + +#define REGOP( _FUNC_ ) \ + index = ( ( reg >> 3) & 0x20 ) | ( reg & 0x1f ); \ + if ( OpOffsetTable[ index ] ) { \ + Operator* regOp = (Operator*)( ((char *)this ) + OpOffsetTable[ index ] ); \ + regOp->_FUNC_( this, val ); \ + } + +#define REGCHAN( _FUNC_ ) \ + index = ( ( reg >> 4) & 0x10 ) | ( reg & 0xf ); \ + if ( ChanOffsetTable[ index ] ) { \ + Channel* regChan = (Channel*)( ((char *)this ) + ChanOffsetTable[ index ] ); \ + regChan->_FUNC_( this, val ); \ + } + +//Update the 0xc0 register for all channels to signal the switch to mono/stereo handlers +void Chip::UpdateSynths() { + for (int i = 0; i < 18; i++) { + chan[i].UpdateSynth(this); + } +} + + +void Chip::WriteReg( Bit32u reg, Bit8u val ) { + Bitu index; + switch ( (reg & 0xf0) >> 4 ) { + case 0x00 >> 4: + if ( reg == 0x01 ) { + waveFormMask = ( val & 0x20 ) ? 0x7 : 0x0; + } else if ( reg == 0x104 ) { + //Only detect changes in lowest 6 bits + if ( !((reg104 ^ val) & 0x3f) ) + return; + //Always keep the highest bit enabled, for checking > 0x80 + reg104 = 0x80 | ( val & 0x3f ); + //Switch synths when changing the 4op combinations + UpdateSynths(); + } else if ( reg == 0x105 ) { + //MAME says the real opl3 doesn't reset anything on opl3 disable/enable till the next write in another register + if ( !((opl3Active ^ val) & 1 ) ) + return; + opl3Active = ( val & 1 ) ? 0xff : 0; + //Just tupdate the synths now that opl3 most have been enabled + //This isn't how the real card handles it but need to switch to stereo generating handlers + UpdateSynths(); + } else if ( reg == 0x08 ) { + reg08 = val; + } + case 0x10 >> 4: + break; + case 0x20 >> 4: + case 0x30 >> 4: + REGOP( Write20 ); + break; + case 0x40 >> 4: + case 0x50 >> 4: + REGOP( Write40 ); + break; + case 0x60 >> 4: + case 0x70 >> 4: + REGOP( Write60 ); + break; + case 0x80 >> 4: + case 0x90 >> 4: + REGOP( Write80 ); + break; + case 0xa0 >> 4: + REGCHAN( WriteA0 ); + break; + case 0xb0 >> 4: + if ( reg == 0xbd ) { + WriteBD( val ); + } else { + REGCHAN( WriteB0 ); + } + break; + case 0xc0 >> 4: + REGCHAN( WriteC0 ); + case 0xd0 >> 4: + break; + case 0xe0 >> 4: + case 0xf0 >> 4: + REGOP( WriteE0 ); + break; + } +} + + +Bit32u Chip::WriteAddr( Bit32u port, Bit8u val ) { + switch ( port & 3 ) { + case 0: + return val; + case 2: + if ( opl3Active || (val == 0x05) ) + return 0x100 | val; + else + return val; + } + return 0; +} + +void Chip::GenerateBlock2( Bitu total, Bit32s* output ) { + while ( total > 0 ) { + Bit32u samples = ForwardLFO( static_cast(total) ); + memset(output, 0, sizeof(Bit32s) * samples); +// int count = 0; + for( Channel* ch = chan; ch < chan + 9; ) { +// count++; + ch = (ch->*(ch->synthHandler))( this, samples, output ); + } + total -= samples; + output += samples; + } +} + +void Chip::GenerateBlock2_Mix( Bitu total, Bit32s* output ) { + while ( total > 0 ) { + Bit32u samples = ForwardLFO( static_cast(total) ); +// int count = 0; + for( Channel* ch = chan; ch < chan + 9; ) { +// count++; + ch = (ch->*(ch->synthHandler))( this, samples, output ); + } + total -= samples; + output += samples; + } +} + +void Chip::GenerateBlock3( Bitu total, Bit32s* output ) { + while ( total > 0 ) { + Bit32u samples = ForwardLFO( static_cast(total) ); + memset(output, 0, sizeof(Bit32s) * samples *2); +// int count = 0; + for( Channel* ch = chan; ch < chan + 18; ) { +// count++; + ch = (ch->*(ch->synthHandler))( this, samples, output ); + } + total -= samples; + output += samples * 2; + } +} + +void Chip::GenerateBlock3_Mix( Bitu total, Bit32s* output ) { + while ( total > 0 ) { + Bit32u samples = ForwardLFO( static_cast(total) ); +// int count = 0; + for( Channel* ch = chan; ch < chan + 18; ) { +// count++; + ch = (ch->*(ch->synthHandler))( this, samples, output ); + } + total -= samples; + output += samples * 2; + } +} + +void Chip::Setup( Bit32u rate ) { + double original = OPLRATE; +// double original = rate; + double scale = original / (double)rate; + + //Noise counter is run at the same precision as general waves + noiseAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) ); + noiseCounter = 0; + noiseValue = 1; //Make sure it triggers the noise xor the first time + //The low frequency oscillation counter + //Every time his overflows vibrato and tremoloindex are increased + lfoAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) ); + lfoCounter = 0; + vibratoIndex = 0; + tremoloIndex = 0; + + //With higher octave this gets shifted up + //-1 since the freqCreateTable = *2 +#ifdef WAVE_PRECISION + double freqScale = ( 1 << 7 ) * scale * ( 1 << ( WAVE_SH - 1 - 10)); + for ( int i = 0; i < 16; i++ ) { + freqMul[i] = (Bit32u)( 0.5 + freqScale * FreqCreateTable[ i ] ); + } +#else + Bit32u freqScale = (Bit32u)( 0.5 + scale * ( 1 << ( WAVE_SH - 1 - 10))); + for ( int i = 0; i < 16; i++ ) { + freqMul[i] = freqScale * FreqCreateTable[ i ]; + } +#endif + + //-3 since the real envelope takes 8 steps to reach the single value we supply + for ( Bit8u i = 0; i < 76; i++ ) { + Bit8u index, shift; + EnvelopeSelect( i, index, shift ); + linearRates[i] = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH + ENV_EXTRA - shift - 3 ))); + } +// Bit32s attackDiffs[62]; + //Generate the best matching attack rate + for ( Bit8u i = 0; i < 62; i++ ) { + Bit8u index, shift; + EnvelopeSelect( i, index, shift ); + //Original amount of samples the attack would take + Bit32s original = (Bit32u)( (AttackSamplesTable[ index ] << shift) / scale); + + Bit32s guessAdd = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH - shift - 3 ))); + Bit32s bestAdd = guessAdd; + Bit32u bestDiff = 1 << 30; + for( Bit32u passes = 0; passes < 16; passes ++ ) { + Bit32s volume = ENV_MAX; + Bit32s samples = 0; + Bit32u count = 0; + while ( volume > 0 && samples < original * 2 ) { + count += guessAdd; + Bit32s change = count >> RATE_SH; + count &= RATE_MASK; + if ( GCC_UNLIKELY(change) ) { // less than 1 % + volume += ( ~volume * change ) >> 3; + } + samples++; + + } + Bit32s diff = original - samples; + Bit32u lDiff = labs( diff ); + //Init last on first pass + if ( lDiff < bestDiff ) { + bestDiff = lDiff; + bestAdd = guessAdd; + //We hit an exactly matching sample count + if ( !bestDiff ) + break; + } + //Linear correction factor, not exactly perfect but seems to work + double correct = (original - diff) / (double)original; + guessAdd = (Bit32u)(guessAdd * correct); + //Below our target + if ( diff < 0 ) { + //Always add one here for rounding, an overshoot will get corrected by another pass decreasing + guessAdd++; + } + } + attackRates[i] = bestAdd; + //Keep track of the diffs for some debugging +// attackDiffs[i] = bestDiff; + } + for ( Bit8u i = 62; i < 76; i++ ) { + //This should provide instant volume maximizing + attackRates[i] = 8 << RATE_SH; + } + //Setup the channels with the correct four op flags + //Channels are accessed through a table so they appear linear here + chan[ 0].fourMask = 0x00 | ( 1 << 0 ); + chan[ 1].fourMask = 0x80 | ( 1 << 0 ); + chan[ 2].fourMask = 0x00 | ( 1 << 1 ); + chan[ 3].fourMask = 0x80 | ( 1 << 1 ); + chan[ 4].fourMask = 0x00 | ( 1 << 2 ); + chan[ 5].fourMask = 0x80 | ( 1 << 2 ); + + chan[ 9].fourMask = 0x00 | ( 1 << 3 ); + chan[10].fourMask = 0x80 | ( 1 << 3 ); + chan[11].fourMask = 0x00 | ( 1 << 4 ); + chan[12].fourMask = 0x80 | ( 1 << 4 ); + chan[13].fourMask = 0x00 | ( 1 << 5 ); + chan[14].fourMask = 0x80 | ( 1 << 5 ); + + //mark the percussion channels + chan[ 6].fourMask = 0x40; + chan[ 7].fourMask = 0x40; + chan[ 8].fourMask = 0x40; + + //Clear Everything in opl3 mode + WriteReg( 0x105, 0x1 ); + for ( int i = 0; i < 512; i++ ) { + if ( i == 0x105 ) + continue; + WriteReg( i, 0xff ); + WriteReg( i, 0x0 ); + } + WriteReg( 0x105, 0x0 ); + //Clear everything in opl2 mode + for ( int i = 0; i < 255; i++ ) { + WriteReg( i, 0xff ); + WriteReg( i, 0x0 ); + } +} + +static bool doneTables = false; +void InitTables( void ) { + if ( doneTables ) + return; + doneTables = true; +#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG ) + //Exponential volume table, same as the real adlib + for ( int i = 0; i < 256; i++ ) { + //Save them in reverse + ExpTable[i] = (int)( 0.5 + ( pow(2.0, ( 255 - i) * ( 1.0 /256 ) )-1) * 1024 ); + ExpTable[i] += 1024; //or remove the -1 oh well :) + //Preshift to the left once so the final volume can shift to the right + ExpTable[i] *= 2; + } +#endif +#if ( DBOPL_WAVE == WAVE_HANDLER ) + //Add 0.5 for the trunc rounding of the integer cast + //Do a PI sinetable instead of the original 0.5 PI + for ( int i = 0; i < 512; i++ ) { + SinTable[i] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 ); + } +#endif +#if ( DBOPL_WAVE == WAVE_TABLEMUL ) + //Multiplication based tables + for ( int i = 0; i < 384; i++ ) { + int s = i * 8; + //TODO maybe keep some of the precision errors of the original table? + double val = ( 0.5 + ( pow(2.0, -1.0 + ( 255 - s) * ( 1.0 /256 ) )) * ( 1 << MUL_SH )); + MulTable[i] = (Bit16u)(val); + } + + //Sine Wave Base + for ( int i = 0; i < 512; i++ ) { + WaveTable[ 0x0200 + i ] = (Bit16s)(sin( (i + 0.5) * (PI / 512.0) ) * 4084); + WaveTable[ 0x0000 + i ] = -WaveTable[ 0x200 + i ]; + } + //Exponential wave + for ( int i = 0; i < 256; i++ ) { + WaveTable[ 0x700 + i ] = (Bit16s)( 0.5 + ( pow(2.0, -1.0 + ( 255 - i * 8) * ( 1.0 /256 ) ) ) * 4085 ); + WaveTable[ 0x6ff - i ] = -WaveTable[ 0x700 + i ]; + } +#endif +#if ( DBOPL_WAVE == WAVE_TABLELOG ) + //Sine Wave Base + for ( int i = 0; i < 512; i++ ) { + WaveTable[ 0x0200 + i ] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 ); + WaveTable[ 0x0000 + i ] = ((Bit16s)0x8000) | WaveTable[ 0x200 + i]; + } + //Exponential wave + for ( int i = 0; i < 256; i++ ) { + WaveTable[ 0x700 + i ] = i * 8; + WaveTable[ 0x6ff - i ] = ((Bit16s)0x8000) | i * 8; + } +#endif + + // | |//\\|____|WAV7|//__|/\ |____|/\/\| + // |\\//| | |WAV7| | \/| | | + // |06 |0126|27 |7 |3 |4 |4 5 |5 | + +#if (( DBOPL_WAVE == WAVE_TABLELOG ) || ( DBOPL_WAVE == WAVE_TABLEMUL )) + for ( int i = 0; i < 256; i++ ) { + //Fill silence gaps + WaveTable[ 0x400 + i ] = WaveTable[0]; + WaveTable[ 0x500 + i ] = WaveTable[0]; + WaveTable[ 0x900 + i ] = WaveTable[0]; + WaveTable[ 0xc00 + i ] = WaveTable[0]; + WaveTable[ 0xd00 + i ] = WaveTable[0]; + //Replicate sines in other pieces + WaveTable[ 0x800 + i ] = WaveTable[ 0x200 + i ]; + //double speed sines + WaveTable[ 0xa00 + i ] = WaveTable[ 0x200 + i * 2 ]; + WaveTable[ 0xb00 + i ] = WaveTable[ 0x000 + i * 2 ]; + WaveTable[ 0xe00 + i ] = WaveTable[ 0x200 + i * 2 ]; + WaveTable[ 0xf00 + i ] = WaveTable[ 0x200 + i * 2 ]; + } +#endif + + //Create the ksl table + for ( int oct = 0; oct < 8; oct++ ) { + int base = oct * 8; + for ( int i = 0; i < 16; i++ ) { + int val = base - KslCreateTable[i]; + if ( val < 0 ) + val = 0; + //*4 for the final range to match attenuation range + KslTable[ oct * 16 + i ] = val * 4; + } + } + //Create the Tremolo table, just increase and decrease a triangle wave + for ( Bit8u i = 0; i < TREMOLO_TABLE / 2; i++ ) { + Bit8u val = i << ENV_EXTRA; + TremoloTable[i] = val; + TremoloTable[TREMOLO_TABLE - 1 - i] = val; + } + //Create a table with offsets of the channels from the start of the chip + DBOPL::Chip* chip = 0; + for ( Bitu i = 0; i < 32; i++ ) { + Bitu index = i & 0xf; + if ( index >= 9 ) { + ChanOffsetTable[i] = 0; + continue; + } + //Make sure the four op channels follow eachother + if ( index < 6 ) { + index = (index % 3) * 2 + ( index / 3 ); + } + //Add back the bits for highest ones + if ( i >= 16 ) + index += 9; + Bitu blah = reinterpret_cast( &(chip->chan[ index ]) ); + ChanOffsetTable[i] = static_cast(blah); + } + //Same for operators + for ( Bitu i = 0; i < 64; i++ ) { + if ( i % 8 >= 6 || ( (i / 8) % 4 == 3 ) ) { + OpOffsetTable[i] = 0; + continue; + } + Bitu chNum = (i / 8) * 3 + (i % 8) % 3; + //Make sure we use 16 and up for the 2nd range to match the chanoffset gap + if ( chNum >= 12 ) + chNum += 16 - 12; + Bitu opNum = ( i % 8 ) / 3; + DBOPL::Channel* chan = 0; + Bitu blah = reinterpret_cast( &(chan->op[opNum]) ); + OpOffsetTable[i] = static_cast(ChanOffsetTable[ chNum ] + blah); + } +#if 0 + //Stupid checks if table's are correct + for ( Bitu i = 0; i < 18; i++ ) { + Bit32u find = (Bit16u)( &(chip->chan[ i ]) ); + for ( Bitu c = 0; c < 32; c++ ) { + if ( ChanOffsetTable[c] == find ) { + find = 0; + break; + } + } + if ( find ) { + find = find; + } + } + for ( Bitu i = 0; i < 36; i++ ) { + Bit32u find = (Bit16u)( &(chip->chan[ i / 2 ].op[i % 2]) ); + for ( Bitu c = 0; c < 64; c++ ) { + if ( OpOffsetTable[c] == find ) { + find = 0; + break; + } + } + if ( find ) { + find = find; + } + } +#endif +} + +Bit32u Handler::WriteAddr( Bit32u port, Bit8u val ) { + return chip.WriteAddr( port, val ); + +} +void Handler::WriteReg( Bit32u addr, Bit8u val ) { + chip.WriteReg( addr, val ); +} + +#define DB_MAX(x, y) ((x) > (y) ? (x) : (y)) +#define DB_MIN(x, y) ((x) < (y) ? (x) : (y)) + +#define DBOPL_CLAMP(V, MIN, MAX) DB_MAX(DB_MIN(V, (MAX)), (MIN)) + +void Handler::GenerateArr(Bit32s *out, Bitu *samples) +{ + if(GCC_UNLIKELY(*samples > 512)) + *samples = 512; + if(!chip.opl3Active) + chip.GenerateBlock2(*samples, out); + else + chip.GenerateBlock3(*samples, out); +} + +void Handler::GenerateArr(Bit16s *out, Bitu *samples) +{ + Bit32s out32[1024]; + if(GCC_UNLIKELY(*samples > 512)) + *samples = 512; + memset(out32, 0, sizeof(Bit32s) * 1024); + if(!chip.opl3Active) + chip.GenerateBlock2(*samples, out32); + else + chip.GenerateBlock3(*samples, out32); + Bitu sz = *samples * 2; + for(Bitu i = 0; i < sz; i++) + out[i] = static_cast(DBOPL_CLAMP(out32[i], INT16_MIN, INT16_MAX)); +} + +void Handler::GenerateArrMix(Bit32s *out, Bitu *samples) +{ + if(GCC_UNLIKELY(*samples > 512)) + *samples = 512; + if(!chip.opl3Active) + chip.GenerateBlock2_Mix(*samples, out); + else + chip.GenerateBlock3_Mix(*samples, out); +} + +void Handler::GenerateArrMix(Bit16s *out, Bitu *samples) +{ + Bit32s out32[1024]; + if(GCC_UNLIKELY(*samples > 512)) + *samples = 512; + memset(out32, 0, sizeof(Bit32s) * 1024); + if(!chip.opl3Active) + chip.GenerateBlock2(*samples, out32); + else + chip.GenerateBlock3(*samples, out32); + Bitu sz = *samples * 2; + for(Bitu i = 0; i < sz; i++) + out[i] += static_cast(DBOPL_CLAMP(out32[i], INT16_MIN, INT16_MAX)); +} + +void Handler::Init( Bitu rate ) { + InitTables(); + chip.Setup( static_cast(rate) ); +} + + +} //Namespace DBOPL diff --git a/engine/src/Libraries/adlmidi/chips/dosbox/dbopl.h b/engine/src/Libraries/adlmidi/chips/dosbox/dbopl.h new file mode 100644 index 0000000..73c0aa9 --- /dev/null +++ b/engine/src/Libraries/adlmidi/chips/dosbox/dbopl.h @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2002-2018 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#if defined(__GNUC__) && defined(__i386__) +#define DB_FASTCALL __attribute__((fastcall)) +#elif defined(_MSC_VER) +#define DB_FASTCALL __fastcall +#else +#define DB_FASTCALL +#endif + +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef uint64_t Bit64u; +typedef int64_t Bit64s; +typedef uint32_t Bit32u; +typedef int32_t Bit32s; +typedef uint16_t Bit16u; +typedef int16_t Bit16s; +typedef uint8_t Bit8u; +typedef int8_t Bit8s; + +//Use 8 handlers based on a small logatirmic wavetabe and an exponential table for volume +#define WAVE_HANDLER 10 +//Use a logarithmic wavetable with an exponential table for volume +#define WAVE_TABLELOG 11 +//Use a linear wavetable with a multiply table for volume +#define WAVE_TABLEMUL 12 + +//Select the type of wave generator routine +#define DBOPL_WAVE WAVE_TABLEMUL + +namespace DBOPL { + +struct Chip; +struct Operator; +struct Channel; + +#if (DBOPL_WAVE == WAVE_HANDLER) +typedef Bits ( DB_FASTCALL *WaveHandler) ( Bitu i, Bitu volume ); +#endif + +typedef Bits ( DBOPL::Operator::*VolumeHandler) ( ); +typedef Channel* ( DBOPL::Channel::*SynthHandler) ( Chip* chip, Bit32u samples, Bit32s* output ); + +//Different synth modes that can generate blocks of data +typedef enum { + sm2AM, + sm2FM, + sm3AM, + sm3FM, + sm4Start, + sm3FMFM, + sm3AMFM, + sm3FMAM, + sm3AMAM, + sm6Start, + sm2Percussion, + sm3Percussion, +} SynthMode; + +//Shifts for the values contained in chandata variable +enum { + SHIFT_KSLBASE = 16, + SHIFT_KEYCODE = 24, +}; + +struct Operator { +public: + //Masks for operator 20 values + enum { + MASK_KSR = 0x10, + MASK_SUSTAIN = 0x20, + MASK_VIBRATO = 0x40, + MASK_TREMOLO = 0x80, + }; + + typedef enum { + OFF, + RELEASE, + SUSTAIN, + DECAY, + ATTACK, + } State; + + VolumeHandler volHandler; + +#if (DBOPL_WAVE == WAVE_HANDLER) + WaveHandler waveHandler; //Routine that generate a wave +#else + Bit16s* waveBase; + Bit32u waveMask; + Bit32u waveStart; +#endif + Bit32u waveIndex; //WAVE_BITS shifted counter of the frequency index + Bit32u waveAdd; //The base frequency without vibrato + Bit32u waveCurrent; //waveAdd + vibratao + + Bit32u chanData; //Frequency/octave and derived data coming from whatever channel controls this + Bit32u freqMul; //Scale channel frequency with this, TODO maybe remove? + Bit32u vibrato; //Scaled up vibrato strength + Bit32s sustainLevel; //When stopping at sustain level stop here + Bit32s totalLevel; //totalLevel is added to every generated volume + Bit32u currentLevel; //totalLevel + tremolo + Bit32s volume; //The currently active volume + + Bit32u attackAdd; //Timers for the different states of the envelope + Bit32u decayAdd; + Bit32u releaseAdd; + Bit32u rateIndex; //Current position of the evenlope + + Bit8u rateZero; //Bits for the different states of the envelope having no changes + Bit8u keyOn; //Bitmask of different values that can generate keyon + //Registers, also used to check for changes + Bit8u reg20, reg40, reg60, reg80, regE0; + //Active part of the envelope we're in + Bit8u state; + //0xff when tremolo is enabled + Bit8u tremoloMask; + //Strength of the vibrato + Bit8u vibStrength; + //Keep track of the calculated KSR so we can check for changes + Bit8u ksr; +private: + void SetState( Bit8u s ); + void UpdateAttack( const Chip* chip ); + void UpdateRelease( const Chip* chip ); + void UpdateDecay( const Chip* chip ); +public: + void UpdateAttenuation(); + void UpdateRates( const Chip* chip ); + void UpdateFrequency( ); + + void Write20( const Chip* chip, Bit8u val ); + void Write40( const Chip* chip, Bit8u val ); + void Write60( const Chip* chip, Bit8u val ); + void Write80( const Chip* chip, Bit8u val ); + void WriteE0( const Chip* chip, Bit8u val ); + + bool Silent() const; + void Prepare( const Chip* chip ); + + void KeyOn( Bit8u mask); + void KeyOff( Bit8u mask); + + template< State state> + Bits TemplateVolume( ); + + Bit32s RateForward( Bit32u add ); + Bitu ForwardWave(); + Bitu ForwardVolume(); + + Bits GetSample( Bits modulation ); + Bits GetWave( Bitu index, Bitu vol ); +public: + Operator(); +}; + +struct Channel { + Operator op[2]; + inline Operator* Op( Bitu index ) { + return &( ( this + (index >> 1) )->op[ index & 1 ]); + } + SynthHandler synthHandler; + Bit32u chanData; //Frequency/octave and derived values + Bit32s old[2]; //Old data for feedback + + Bit8u feedback; //Feedback shift + Bit8u regB0; //Register values to check for changes + Bit8u regC0; + //This should correspond with reg104, bit 6 indicates a Percussion channel, bit 7 indicates a silent channel + Bit8u fourMask; + Bit8s maskLeft; //Sign extended values for both channel's panning + Bit8s maskRight; + + //Forward the channel data to the operators of the channel + void SetChanData( const Chip* chip, Bit32u data ); + //Change in the chandata, check for new values and if we have to forward to operators + void UpdateFrequency( const Chip* chip, Bit8u fourOp ); + void UpdateSynth(const Chip* chip); + void WriteA0( const Chip* chip, Bit8u val ); + void WriteB0( const Chip* chip, Bit8u val ); + void WriteC0( const Chip* chip, Bit8u val ); + + //call this for the first channel + template< bool opl3Mode > + void GeneratePercussion( Chip* chip, Bit32s* output ); + + //Generate blocks of data in specific modes + template + Channel* BlockTemplate( Chip* chip, Bit32u samples, Bit32s* output ); + Channel(); +}; + +struct Chip { + //This is used as the base counter for vibrato and tremolo + Bit32u lfoCounter; + Bit32u lfoAdd; + + + Bit32u noiseCounter; + Bit32u noiseAdd; + Bit32u noiseValue; + + //Frequency scales for the different multiplications + Bit32u freqMul[16]; + //Rates for decay and release for rate of this chip + Bit32u linearRates[76]; + //Best match attack rates for the rate of this chip + Bit32u attackRates[76]; + + //18 channels with 2 operators each + Channel chan[18]; + + Bit8u reg104; + Bit8u reg08; + Bit8u reg04; + Bit8u regBD; + Bit8u vibratoIndex; + Bit8u tremoloIndex; + Bit8s vibratoSign; + Bit8u vibratoShift; + Bit8u tremoloValue; + Bit8u vibratoStrength; + Bit8u tremoloStrength; + //Mask for allowed wave forms + Bit8u waveFormMask; + //0 or -1 when enabled + Bit8s opl3Active; + + //Return the maximum amount of samples before and LFO change + Bit32u ForwardLFO( Bit32u samples ); + Bit32u ForwardNoise(); + + void WriteBD( Bit8u val ); + void WriteReg(Bit32u reg, Bit8u val ); + + Bit32u WriteAddr( Bit32u port, Bit8u val ); + + void GenerateBlock2( Bitu samples, Bit32s* output ); + void GenerateBlock2_Mix( Bitu samples, Bit32s* output ); + void GenerateBlock3( Bitu samples, Bit32s* output ); + void GenerateBlock3_Mix( Bitu samples, Bit32s* output ); + + //Update the synth handlers in all channels + void UpdateSynths(); + void Generate( Bit32u samples ); + void Setup( Bit32u r ); + + Chip(); +}; + +struct Handler { + DBOPL::Chip chip; + Bit32u WriteAddr( Bit32u port, Bit8u val ); + void WriteReg( Bit32u addr, Bit8u val ); + void GenerateArr(Bit32s *out, Bitu *samples); + void GenerateArr(Bit16s *out, Bitu *samples); + void GenerateArrMix(Bit32s *out, Bitu *samples); + void GenerateArrMix(Bit16s *out, Bitu *samples); + void Init( Bitu rate ); +}; + + +} //Namespace diff --git a/engine/src/Libraries/adlmidi/chips/dosbox_opl3.cpp b/engine/src/Libraries/adlmidi/chips/dosbox_opl3.cpp new file mode 100644 index 0000000..f783afe --- /dev/null +++ b/engine/src/Libraries/adlmidi/chips/dosbox_opl3.cpp @@ -0,0 +1,74 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "dosbox_opl3.h" +#include "dosbox/dbopl.h" +#include +#include +#include + +DosBoxOPL3::DosBoxOPL3() : + OPLChipBaseBufferedT(), + m_chip(new DBOPL::Handler) +{ + reset(); +} + +DosBoxOPL3::~DosBoxOPL3() +{ + DBOPL::Handler *chip_r = reinterpret_cast(m_chip); + delete chip_r; +} + +void DosBoxOPL3::setRate(uint32_t rate) +{ + OPLChipBaseBufferedT::setRate(rate); + DBOPL::Handler *chip_r = reinterpret_cast(m_chip); + chip_r->~Handler(); + new(chip_r) DBOPL::Handler; + chip_r->Init(effectiveRate()); +} + +void DosBoxOPL3::reset() +{ + OPLChipBaseBufferedT::reset(); + DBOPL::Handler *chip_r = reinterpret_cast(m_chip); + chip_r->~Handler(); + new(chip_r) DBOPL::Handler; + chip_r->Init(effectiveRate()); +} + +void DosBoxOPL3::writeReg(uint16_t addr, uint8_t data) +{ + DBOPL::Handler *chip_r = reinterpret_cast(m_chip); + chip_r->WriteReg(static_cast(addr), data); +} + +void DosBoxOPL3::nativeGenerateN(int16_t *output, size_t frames) +{ + DBOPL::Handler *chip_r = reinterpret_cast(m_chip); + Bitu frames_i = frames; + chip_r->GenerateArr(output, &frames_i); +} + +const char *DosBoxOPL3::emulatorName() +{ + return "DosBox 0.74-r4111 OPL3"; +} diff --git a/engine/src/Libraries/adlmidi/chips/dosbox_opl3.h b/engine/src/Libraries/adlmidi/chips/dosbox_opl3.h new file mode 100644 index 0000000..f966393 --- /dev/null +++ b/engine/src/Libraries/adlmidi/chips/dosbox_opl3.h @@ -0,0 +1,43 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DOSBOX_OPL3_H +#define DOSBOX_OPL3_H + +#include "opl_chip_base.h" + +class DosBoxOPL3 final : public OPLChipBaseBufferedT +{ + void *m_chip; +public: + DosBoxOPL3(); + ~DosBoxOPL3() override; + + bool canRunAtPcmRate() const override { return true; } + void setRate(uint32_t rate) override; + void reset() override; + void writeReg(uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerateN(int16_t *output, size_t frames) override; + const char *emulatorName() override; +}; + +#endif // DOSBOX_OPL3_H diff --git a/engine/src/Libraries/adlmidi/chips/nuked/nukedopl3.c b/engine/src/Libraries/adlmidi/chips/nuked/nukedopl3.c new file mode 100644 index 0000000..fe2313c --- /dev/null +++ b/engine/src/Libraries/adlmidi/chips/nuked/nukedopl3.c @@ -0,0 +1,1394 @@ +/* + * Copyright (C) 2013-2018 Alexey Khokholov (Nuke.YKT) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Nuked OPL3 emulator. + * Thanks: + * MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + * Feedback and Rhythm part calculation information. + * forums.submarine.org.uk(carbon14, opl3): + * Tremolo and phase generator calculation information. + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * siliconpr0n.org(John McMaster, digshadow): + * YMF262 and VRC VII decaps and die shots. + * + * version: 1.8 + */ + +#include +#include +#include +#include "nukedopl3.h" + +#define RSM_FRAC 10 + +/* Channel types */ + +enum { + ch_2op = 0, + ch_4op = 1, + ch_4op2 = 2, + ch_drum = 3 +}; + +/* Envelope key types */ + +enum { + egk_norm = 0x01, + egk_drum = 0x02 +}; + + +/* + * logsin table + */ + +static const Bit16u logsinrom[256] = { + 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, + 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, + 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, + 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, + 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, + 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, + 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, + 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, + 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, + 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, + 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, + 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, + 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, + 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, + 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, + 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, + 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, + 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, + 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, + 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, + 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, + 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, + 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, + 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, + 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, + 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, + 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, + 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, + 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, + 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, + 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 +}; + +/* + * exp table + */ + +static const Bit16u exprom[256] = { + 0x7fa, 0x7f5, 0x7ef, 0x7ea, 0x7e4, 0x7df, 0x7da, 0x7d4, + 0x7cf, 0x7c9, 0x7c4, 0x7bf, 0x7b9, 0x7b4, 0x7ae, 0x7a9, + 0x7a4, 0x79f, 0x799, 0x794, 0x78f, 0x78a, 0x784, 0x77f, + 0x77a, 0x775, 0x770, 0x76a, 0x765, 0x760, 0x75b, 0x756, + 0x751, 0x74c, 0x747, 0x742, 0x73d, 0x738, 0x733, 0x72e, + 0x729, 0x724, 0x71f, 0x71a, 0x715, 0x710, 0x70b, 0x706, + 0x702, 0x6fd, 0x6f8, 0x6f3, 0x6ee, 0x6e9, 0x6e5, 0x6e0, + 0x6db, 0x6d6, 0x6d2, 0x6cd, 0x6c8, 0x6c4, 0x6bf, 0x6ba, + 0x6b5, 0x6b1, 0x6ac, 0x6a8, 0x6a3, 0x69e, 0x69a, 0x695, + 0x691, 0x68c, 0x688, 0x683, 0x67f, 0x67a, 0x676, 0x671, + 0x66d, 0x668, 0x664, 0x65f, 0x65b, 0x657, 0x652, 0x64e, + 0x649, 0x645, 0x641, 0x63c, 0x638, 0x634, 0x630, 0x62b, + 0x627, 0x623, 0x61e, 0x61a, 0x616, 0x612, 0x60e, 0x609, + 0x605, 0x601, 0x5fd, 0x5f9, 0x5f5, 0x5f0, 0x5ec, 0x5e8, + 0x5e4, 0x5e0, 0x5dc, 0x5d8, 0x5d4, 0x5d0, 0x5cc, 0x5c8, + 0x5c4, 0x5c0, 0x5bc, 0x5b8, 0x5b4, 0x5b0, 0x5ac, 0x5a8, + 0x5a4, 0x5a0, 0x59c, 0x599, 0x595, 0x591, 0x58d, 0x589, + 0x585, 0x581, 0x57e, 0x57a, 0x576, 0x572, 0x56f, 0x56b, + 0x567, 0x563, 0x560, 0x55c, 0x558, 0x554, 0x551, 0x54d, + 0x549, 0x546, 0x542, 0x53e, 0x53b, 0x537, 0x534, 0x530, + 0x52c, 0x529, 0x525, 0x522, 0x51e, 0x51b, 0x517, 0x514, + 0x510, 0x50c, 0x509, 0x506, 0x502, 0x4ff, 0x4fb, 0x4f8, + 0x4f4, 0x4f1, 0x4ed, 0x4ea, 0x4e7, 0x4e3, 0x4e0, 0x4dc, + 0x4d9, 0x4d6, 0x4d2, 0x4cf, 0x4cc, 0x4c8, 0x4c5, 0x4c2, + 0x4be, 0x4bb, 0x4b8, 0x4b5, 0x4b1, 0x4ae, 0x4ab, 0x4a8, + 0x4a4, 0x4a1, 0x49e, 0x49b, 0x498, 0x494, 0x491, 0x48e, + 0x48b, 0x488, 0x485, 0x482, 0x47e, 0x47b, 0x478, 0x475, + 0x472, 0x46f, 0x46c, 0x469, 0x466, 0x463, 0x460, 0x45d, + 0x45a, 0x457, 0x454, 0x451, 0x44e, 0x44b, 0x448, 0x445, + 0x442, 0x43f, 0x43c, 0x439, 0x436, 0x433, 0x430, 0x42d, + 0x42a, 0x428, 0x425, 0x422, 0x41f, 0x41c, 0x419, 0x416, + 0x414, 0x411, 0x40e, 0x40b, 0x408, 0x406, 0x403, 0x400 +}; + +/* + * freq mult table multiplied by 2 + * + * 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15 + */ + +static const Bit8u mt[16] = { + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 +}; + +/* + * ksl table + */ + +static const Bit8u kslrom[16] = { + 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64 +}; + +static const Bit8u kslshift[4] = { + 8, 1, 2, 0 +}; + +/* + * envelope generator constants + */ + +static const Bit8u eg_incstep[4][4] = { + { 0, 0, 0, 0 }, + { 1, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 1, 1, 1, 0 } +}; + +/* + * address decoding + */ + +static const Bit8s ad_slot[0x20] = { + 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, + 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static const Bit8u ch_slot[18] = { + 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 +}; + +/* + * Envelope generator + */ + +typedef Bit16s(*envelope_sinfunc)(Bit16u phase, Bit16u envelope); +typedef void(*envelope_genfunc)(opl3_slot *slott); + +static Bit16s OPL3_EnvelopeCalcExp(Bit32u level) +{ + if (level > 0x1fff) + { + level = 0x1fff; + } + return (exprom[level & 0xff] << 1) >> (level >> 8); +} + +static Bit16s OPL3_EnvelopeCalcSin0(Bit16u phase, Bit16u envelope) +{ + Bit16u out = 0; + Bit16u neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + } + if (phase & 0x100) + { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +static Bit16s OPL3_EnvelopeCalcSin1(Bit16u phase, Bit16u envelope) +{ + Bit16u out = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + out = 0x1000; + } + else if (phase & 0x100) + { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +static Bit16s OPL3_EnvelopeCalcSin2(Bit16u phase, Bit16u envelope) +{ + Bit16u out = 0; + phase &= 0x3ff; + if (phase & 0x100) + { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +static Bit16s OPL3_EnvelopeCalcSin3(Bit16u phase, Bit16u envelope) +{ + Bit16u out = 0; + phase &= 0x3ff; + if (phase & 0x100) + { + out = 0x1000; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +static Bit16s OPL3_EnvelopeCalcSin4(Bit16u phase, Bit16u envelope) +{ + Bit16u out = 0; + Bit16u neg = 0; + phase &= 0x3ff; + if ((phase & 0x300) == 0x100) + { + neg = 0xffff; + } + if (phase & 0x200) + { + out = 0x1000; + } + else if (phase & 0x80) + { + out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; + } + else + { + out = logsinrom[(phase << 1) & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +static Bit16s OPL3_EnvelopeCalcSin5(Bit16u phase, Bit16u envelope) +{ + Bit16u out = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + out = 0x1000; + } + else if (phase & 0x80) + { + out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; + } + else + { + out = logsinrom[(phase << 1) & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +static Bit16s OPL3_EnvelopeCalcSin6(Bit16u phase, Bit16u envelope) +{ + Bit16u neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + } + return OPL3_EnvelopeCalcExp(envelope << 3) ^ neg; +} + +static Bit16s OPL3_EnvelopeCalcSin7(Bit16u phase, Bit16u envelope) +{ + Bit16u out = 0; + Bit16u neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + phase = (phase & 0x1ff) ^ 0x1ff; + } + out = phase << 3; + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +static const envelope_sinfunc envelope_sin[8] = { + OPL3_EnvelopeCalcSin0, + OPL3_EnvelopeCalcSin1, + OPL3_EnvelopeCalcSin2, + OPL3_EnvelopeCalcSin3, + OPL3_EnvelopeCalcSin4, + OPL3_EnvelopeCalcSin5, + OPL3_EnvelopeCalcSin6, + OPL3_EnvelopeCalcSin7 +}; + +enum envelope_gen_num +{ + envelope_gen_num_attack = 0, + envelope_gen_num_decay = 1, + envelope_gen_num_sustain = 2, + envelope_gen_num_release = 3 +}; + +static void OPL3_EnvelopeUpdateKSL(opl3_slot *slot) +{ + Bit16s ksl = (kslrom[slot->channel->f_num >> 6] << 2) + - ((0x08 - slot->channel->block) << 5); + if (ksl < 0) + { + ksl = 0; + } + slot->eg_ksl = (Bit8u)ksl; +} + +static void OPL3_EnvelopeCalc(opl3_slot *slot) +{ + Bit8u nonzero; + Bit8u rate; + Bit8u rate_hi; + Bit8u rate_lo; + Bit8u reg_rate = 0; + Bit8u ks; + Bit8u eg_shift, shift; + Bit16u eg_rout; + Bit16s eg_inc; + Bit8u eg_off; + Bit8u reset = 0; + slot->eg_out = slot->eg_rout + (slot->reg_tl << 2) + + (slot->eg_ksl >> kslshift[slot->reg_ksl]) + *slot->trem; + if (slot->key && slot->eg_gen == envelope_gen_num_release) + { + reset = 1; + reg_rate = slot->reg_ar; + } + else + { + switch (slot->eg_gen) + { + case envelope_gen_num_attack: + reg_rate = slot->reg_ar; + break; + case envelope_gen_num_decay: + reg_rate = slot->reg_dr; + break; + case envelope_gen_num_sustain: + if (!slot->reg_type) + { + reg_rate = slot->reg_rr; + } + break; + case envelope_gen_num_release: + reg_rate = slot->reg_rr; + break; + } + } + slot->pg_reset = reset; + ks = slot->channel->ksv >> ((slot->reg_ksr ^ 1) << 1); + nonzero = (reg_rate != 0); + rate = ks + (reg_rate << 2); + rate_hi = rate >> 2; + rate_lo = rate & 0x03; + if (rate_hi & 0x10) + { + rate_hi = 0x0f; + } + eg_shift = rate_hi + slot->chip->eg_add; + shift = 0; + if (nonzero) + { + if (rate_hi < 12) + { + if (slot->chip->eg_state) + { + switch (eg_shift) + { + case 12: + shift = 1; + break; + case 13: + shift = (rate_lo >> 1) & 0x01; + break; + case 14: + shift = rate_lo & 0x01; + break; + default: + break; + } + } + } + else + { + shift = (rate_hi & 0x03) + eg_incstep[rate_lo][slot->chip->timer & 0x03]; + if (shift & 0x04) + { + shift = 0x03; + } + if (!shift) + { + shift = slot->chip->eg_state; + } + } + } + eg_rout = slot->eg_rout; + eg_inc = 0; + eg_off = 0; + /* Instant attack */ + if (reset && rate_hi == 0x0f) + { + eg_rout = 0x00; + } + /* Envelope off */ + if ((slot->eg_rout & 0x1f8) == 0x1f8) + { + eg_off = 1; + } + if (slot->eg_gen != envelope_gen_num_attack && !reset && eg_off) + { + eg_rout = 0x1ff; + } + switch (slot->eg_gen) + { + case envelope_gen_num_attack: + if (!slot->eg_rout) + { + slot->eg_gen = envelope_gen_num_decay; + } + else if (slot->key && shift > 0 && rate_hi != 0x0f) + { + eg_inc = ((~slot->eg_rout) << shift) >> 4; + } + break; + case envelope_gen_num_decay: + if ((slot->eg_rout >> 4) == slot->reg_sl) + { + slot->eg_gen = envelope_gen_num_sustain; + } + else if (!eg_off && !reset && shift > 0) + { + eg_inc = 1 << (shift - 1); + } + break; + case envelope_gen_num_sustain: + case envelope_gen_num_release: + if (!eg_off && !reset && shift > 0) + { + eg_inc = 1 << (shift - 1); + } + break; + } + slot->eg_rout = (eg_rout + eg_inc) & 0x1ff; + /* Key off */ + if (reset) + { + slot->eg_gen = envelope_gen_num_attack; + } + if (!slot->key) + { + slot->eg_gen = envelope_gen_num_release; + } +} + +static void OPL3_EnvelopeKeyOn(opl3_slot *slot, Bit8u type) +{ + slot->key |= type; +} + +static void OPL3_EnvelopeKeyOff(opl3_slot *slot, Bit8u type) +{ + slot->key &= ~type; +} + +/* + * Phase Generator + */ + +static void OPL3_PhaseGenerate(opl3_slot *slot) +{ + opl3_chip *chip; + Bit16u f_num; + Bit32u basefreq; + Bit8u rm_xor, n_bit; + Bit32u noise; + Bit16u phase; + + chip = slot->chip; + f_num = slot->channel->f_num; + if (slot->reg_vib) + { + Bit8s range; + Bit8u vibpos; + + range = (f_num >> 7) & 7; + vibpos = slot->chip->vibpos; + + if (!(vibpos & 3)) + { + range = 0; + } + else if (vibpos & 1) + { + range >>= 1; + } + range >>= slot->chip->vibshift; + + if (vibpos & 4) + { + range = -range; + } + f_num += range; + } + basefreq = (f_num << slot->channel->block) >> 1; + phase = (Bit16u)(slot->pg_phase >> 9); + if (slot->pg_reset) + { + slot->pg_phase = 0; + } + slot->pg_phase += (basefreq * mt[slot->reg_mult]) >> 1; + /* Rhythm mode */ + noise = chip->noise; + slot->pg_phase_out = phase; + if (slot->slot_num == 13) /* hh */ + { + chip->rm_hh_bit2 = (phase >> 2) & 1; + chip->rm_hh_bit3 = (phase >> 3) & 1; + chip->rm_hh_bit7 = (phase >> 7) & 1; + chip->rm_hh_bit8 = (phase >> 8) & 1; + } + if (slot->slot_num == 17 && (chip->rhy & 0x20)) /* tc */ + { + chip->rm_tc_bit3 = (phase >> 3) & 1; + chip->rm_tc_bit5 = (phase >> 5) & 1; + } + if (chip->rhy & 0x20) + { + rm_xor = (chip->rm_hh_bit2 ^ chip->rm_hh_bit7) + | (chip->rm_hh_bit3 ^ chip->rm_tc_bit5) + | (chip->rm_tc_bit3 ^ chip->rm_tc_bit5); + switch (slot->slot_num) + { + case 13: /* hh */ + slot->pg_phase_out = rm_xor << 9; + if (rm_xor ^ (noise & 1)) + { + slot->pg_phase_out |= 0xd0; + } + else + { + slot->pg_phase_out |= 0x34; + } + break; + case 16: /* sd */ + slot->pg_phase_out = (chip->rm_hh_bit8 << 9) + | ((chip->rm_hh_bit8 ^ (noise & 1)) << 8); + break; + case 17: /* tc */ + slot->pg_phase_out = (rm_xor << 9) | 0x80; + break; + default: + break; + } + } + n_bit = ((noise >> 14) ^ noise) & 0x01; + chip->noise = (noise >> 1) | (n_bit << 22); +} + +/* + * Slot + */ + +static void OPL3_SlotWrite20(opl3_slot *slot, Bit8u data) +{ + if ((data >> 7) & 0x01) + { + slot->trem = &slot->chip->tremolo; + } + else + { + slot->trem = (Bit8u*)&slot->chip->zeromod; + } + slot->reg_vib = (data >> 6) & 0x01; + slot->reg_type = (data >> 5) & 0x01; + slot->reg_ksr = (data >> 4) & 0x01; + slot->reg_mult = data & 0x0f; +} + +static void OPL3_SlotWrite40(opl3_slot *slot, Bit8u data) +{ + slot->reg_ksl = (data >> 6) & 0x03; + slot->reg_tl = data & 0x3f; + OPL3_EnvelopeUpdateKSL(slot); +} + +static void OPL3_SlotWrite60(opl3_slot *slot, Bit8u data) +{ + slot->reg_ar = (data >> 4) & 0x0f; + slot->reg_dr = data & 0x0f; +} + +static void OPL3_SlotWrite80(opl3_slot *slot, Bit8u data) +{ + slot->reg_sl = (data >> 4) & 0x0f; + if (slot->reg_sl == 0x0f) + { + slot->reg_sl = 0x1f; + } + slot->reg_rr = data & 0x0f; +} + +static void OPL3_SlotWriteE0(opl3_slot *slot, Bit8u data) +{ + slot->reg_wf = data & 0x07; + if (slot->chip->newm == 0x00) + { + slot->reg_wf &= 0x03; + } +} + +static void OPL3_SlotGenerate(opl3_slot *slot) +{ + slot->out = envelope_sin[slot->reg_wf](slot->pg_phase_out + *slot->mod, slot->eg_out); +} + +static void OPL3_SlotCalcFB(opl3_slot *slot) +{ + if (slot->channel->fb != 0x00) + { + slot->fbmod = (slot->prout + slot->out) >> (0x09 - slot->channel->fb); + } + else + { + slot->fbmod = 0; + } + slot->prout = slot->out; +} + +/* + * Channel + */ + +static void OPL3_ChannelSetupAlg(opl3_channel *channel); + +static void OPL3_ChannelUpdateRhythm(opl3_chip *chip, Bit8u data) +{ + opl3_channel *channel6; + opl3_channel *channel7; + opl3_channel *channel8; + Bit8u chnum; + + chip->rhy = data & 0x3f; + if (chip->rhy & 0x20) + { + channel6 = &chip->channel[6]; + channel7 = &chip->channel[7]; + channel8 = &chip->channel[8]; + channel6->out[0] = &channel6->slotz[1]->out; + channel6->out[1] = &channel6->slotz[1]->out; + channel6->out[2] = &chip->zeromod; + channel6->out[3] = &chip->zeromod; + channel7->out[0] = &channel7->slotz[0]->out; + channel7->out[1] = &channel7->slotz[0]->out; + channel7->out[2] = &channel7->slotz[1]->out; + channel7->out[3] = &channel7->slotz[1]->out; + channel8->out[0] = &channel8->slotz[0]->out; + channel8->out[1] = &channel8->slotz[0]->out; + channel8->out[2] = &channel8->slotz[1]->out; + channel8->out[3] = &channel8->slotz[1]->out; + for (chnum = 6; chnum < 9; chnum++) + { + chip->channel[chnum].chtype = ch_drum; + } + OPL3_ChannelSetupAlg(channel6); + OPL3_ChannelSetupAlg(channel7); + OPL3_ChannelSetupAlg(channel8); + /* hh */ + if (chip->rhy & 0x01) + { + OPL3_EnvelopeKeyOn(channel7->slotz[0], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel7->slotz[0], egk_drum); + } + /* tc */ + if (chip->rhy & 0x02) + { + OPL3_EnvelopeKeyOn(channel8->slotz[1], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel8->slotz[1], egk_drum); + } + /* tom */ + if (chip->rhy & 0x04) + { + OPL3_EnvelopeKeyOn(channel8->slotz[0], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel8->slotz[0], egk_drum); + } + /* sd */ + if (chip->rhy & 0x08) + { + OPL3_EnvelopeKeyOn(channel7->slotz[1], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel7->slotz[1], egk_drum); + } + /* bd */ + if (chip->rhy & 0x10) + { + OPL3_EnvelopeKeyOn(channel6->slotz[0], egk_drum); + OPL3_EnvelopeKeyOn(channel6->slotz[1], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel6->slotz[0], egk_drum); + OPL3_EnvelopeKeyOff(channel6->slotz[1], egk_drum); + } + } + else + { + for (chnum = 6; chnum < 9; chnum++) + { + chip->channel[chnum].chtype = ch_2op; + OPL3_ChannelSetupAlg(&chip->channel[chnum]); + OPL3_EnvelopeKeyOff(chip->channel[chnum].slotz[0], egk_drum); + OPL3_EnvelopeKeyOff(chip->channel[chnum].slotz[1], egk_drum); + } + } +} + +static void OPL3_ChannelWriteA0(opl3_channel *channel, Bit8u data) +{ + if (channel->chip->newm && channel->chtype == ch_4op2) + { + return; + } + channel->f_num = (channel->f_num & 0x300) | data; + channel->ksv = (channel->block << 1) + | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + OPL3_EnvelopeUpdateKSL(channel->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->slotz[1]); + if (channel->chip->newm && channel->chtype == ch_4op) + { + channel->pair->f_num = channel->f_num; + channel->pair->ksv = channel->ksv; + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[1]); + } +} + +static void OPL3_ChannelWriteB0(opl3_channel *channel, Bit8u data) +{ + if (channel->chip->newm && channel->chtype == ch_4op2) + { + return; + } + channel->f_num = (channel->f_num & 0xff) | ((data & 0x03) << 8); + channel->block = (data >> 2) & 0x07; + channel->ksv = (channel->block << 1) + | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + OPL3_EnvelopeUpdateKSL(channel->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->slotz[1]); + if (channel->chip->newm && channel->chtype == ch_4op) + { + channel->pair->f_num = channel->f_num; + channel->pair->block = channel->block; + channel->pair->ksv = channel->ksv; + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[1]); + } +} + +static void OPL3_ChannelSetupAlg(opl3_channel *channel) +{ + if (channel->chtype == ch_drum) + { + if (channel->ch_num == 7 || channel->ch_num == 8) + { + channel->slotz[0]->mod = &channel->chip->zeromod; + channel->slotz[1]->mod = &channel->chip->zeromod; + return; + } + switch (channel->alg & 0x01) + { + case 0x00: + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->slotz[0]->out; + break; + case 0x01: + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->chip->zeromod; + break; + } + return; + } + if (channel->alg & 0x08) + { + return; + } + if (channel->alg & 0x04) + { + channel->pair->out[0] = &channel->chip->zeromod; + channel->pair->out[1] = &channel->chip->zeromod; + channel->pair->out[2] = &channel->chip->zeromod; + channel->pair->out[3] = &channel->chip->zeromod; + switch (channel->alg & 0x03) + { + case 0x00: + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->pair->slotz[0]->out; + channel->slotz[0]->mod = &channel->pair->slotz[1]->out; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->slotz[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x01: + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->pair->slotz[0]->out; + channel->slotz[0]->mod = &channel->chip->zeromod; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->pair->slotz[1]->out; + channel->out[1] = &channel->slotz[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x02: + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->chip->zeromod; + channel->slotz[0]->mod = &channel->pair->slotz[1]->out; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->pair->slotz[0]->out; + channel->out[1] = &channel->slotz[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x03: + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->chip->zeromod; + channel->slotz[0]->mod = &channel->pair->slotz[1]->out; + channel->slotz[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->pair->slotz[0]->out; + channel->out[1] = &channel->slotz[0]->out; + channel->out[2] = &channel->slotz[1]->out; + channel->out[3] = &channel->chip->zeromod; + break; + } + } + else + { + switch (channel->alg & 0x01) + { + case 0x00: + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->slotz[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x01: + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->slotz[0]->out; + channel->out[1] = &channel->slotz[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + } + } +} + +static void OPL3_ChannelWriteC0(opl3_channel *channel, Bit8u data) +{ + channel->fb = (data & 0x0e) >> 1; + channel->con = data & 0x01; + channel->alg = channel->con; + if (channel->chip->newm) + { + if (channel->chtype == ch_4op) + { + channel->pair->alg = 0x04 | (channel->con << 1) | (channel->pair->con); + channel->alg = 0x08; + OPL3_ChannelSetupAlg(channel->pair); + } + else if (channel->chtype == ch_4op2) + { + channel->alg = 0x04 | (channel->pair->con << 1) | (channel->con); + channel->pair->alg = 0x08; + OPL3_ChannelSetupAlg(channel); + } + else + { + OPL3_ChannelSetupAlg(channel); + } + } + else + { + OPL3_ChannelSetupAlg(channel); + } + if (channel->chip->newm) + { + channel->cha = ((data >> 4) & 0x01) ? ~0 : 0; + channel->chb = ((data >> 5) & 0x01) ? ~0 : 0; + } + else + { + channel->cha = channel->chb = (Bit16u)~0; + } +} + +static void OPL3_ChannelKeyOn(opl3_channel *channel) +{ + if (channel->chip->newm) + { + if (channel->chtype == ch_4op) + { + OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm); + OPL3_EnvelopeKeyOn(channel->pair->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->pair->slotz[1], egk_norm); + } + else if (channel->chtype == ch_2op || channel->chtype == ch_drum) + { + OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm); + } + } + else + { + OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm); + } +} + +static void OPL3_ChannelKeyOff(opl3_channel *channel) +{ + if (channel->chip->newm) + { + if (channel->chtype == ch_4op) + { + OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm); + OPL3_EnvelopeKeyOff(channel->pair->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->pair->slotz[1], egk_norm); + } + else if (channel->chtype == ch_2op || channel->chtype == ch_drum) + { + OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm); + } + } + else + { + OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm); + } +} + +static void OPL3_ChannelSet4Op(opl3_chip *chip, Bit8u data) +{ + Bit8u bit; + Bit8u chnum; + for (bit = 0; bit < 6; bit++) + { + chnum = bit; + if (bit >= 3) + { + chnum += 9 - 3; + } + if ((data >> bit) & 0x01) + { + chip->channel[chnum].chtype = ch_4op; + chip->channel[chnum + 3].chtype = ch_4op2; + } + else + { + chip->channel[chnum].chtype = ch_2op; + chip->channel[chnum + 3].chtype = ch_2op; + } + } +} + +static Bit16s OPL3_ClipSample(Bit32s sample) +{ + if (sample > 32767) + { + sample = 32767; + } + else if (sample < -32768) + { + sample = -32768; + } + return (Bit16s)sample; +} + +void OPL3_Generate(opl3_chip *chip, Bit16s *buf) +{ + Bit8u ii; + Bit8u jj; + Bit16s accm; + Bit8u shift = 0; + + buf[1] = OPL3_ClipSample(chip->mixbuff[1]); + + for (ii = 0; ii < 15; ii++) + { + OPL3_SlotCalcFB(&chip->slot[ii]); + OPL3_EnvelopeCalc(&chip->slot[ii]); + OPL3_PhaseGenerate(&chip->slot[ii]); + OPL3_SlotGenerate(&chip->slot[ii]); + } + + chip->mixbuff[0] = 0; + for (ii = 0; ii < 18; ii++) + { + accm = 0; + for (jj = 0; jj < 4; jj++) + { + accm += *chip->channel[ii].out[jj]; + } + chip->mixbuff[0] += (Bit16s)(accm & chip->channel[ii].cha); + } + + for (ii = 15; ii < 18; ii++) + { + OPL3_SlotCalcFB(&chip->slot[ii]); + OPL3_EnvelopeCalc(&chip->slot[ii]); + OPL3_PhaseGenerate(&chip->slot[ii]); + OPL3_SlotGenerate(&chip->slot[ii]); + } + + buf[0] = OPL3_ClipSample(chip->mixbuff[0]); + + for (ii = 18; ii < 33; ii++) + { + OPL3_SlotCalcFB(&chip->slot[ii]); + OPL3_EnvelopeCalc(&chip->slot[ii]); + OPL3_PhaseGenerate(&chip->slot[ii]); + OPL3_SlotGenerate(&chip->slot[ii]); + } + + chip->mixbuff[1] = 0; + for (ii = 0; ii < 18; ii++) + { + accm = 0; + for (jj = 0; jj < 4; jj++) + { + accm += *chip->channel[ii].out[jj]; + } + chip->mixbuff[1] += (Bit16s)(accm & chip->channel[ii].chb); + } + + for (ii = 33; ii < 36; ii++) + { + OPL3_SlotCalcFB(&chip->slot[ii]); + OPL3_EnvelopeCalc(&chip->slot[ii]); + OPL3_PhaseGenerate(&chip->slot[ii]); + OPL3_SlotGenerate(&chip->slot[ii]); + } + + if ((chip->timer & 0x3f) == 0x3f) + { + chip->tremolopos = (chip->tremolopos + 1) % 210; + } + if (chip->tremolopos < 105) + { + chip->tremolo = chip->tremolopos >> chip->tremoloshift; + } + else + { + chip->tremolo = (210 - chip->tremolopos) >> chip->tremoloshift; + } + + if ((chip->timer & 0x3ff) == 0x3ff) + { + chip->vibpos = (chip->vibpos + 1) & 7; + } + + chip->timer++; + + chip->eg_add = 0; + if (chip->eg_timer) + { + while (shift < 36 && ((chip->eg_timer >> shift) & 1) == 0) + { + shift++; + } + if (shift > 12) + { + chip->eg_add = 0; + } + else + { + chip->eg_add = shift + 1; + } + } + + if (chip->eg_timerrem || chip->eg_state) + { + if (chip->eg_timer == (uint64_t)0xfffffffffU) + { + chip->eg_timer = 0; + chip->eg_timerrem = 1; + } + else + { + chip->eg_timer++; + chip->eg_timerrem = 0; + } + } + + chip->eg_state ^= 1; + + while (chip->writebuf[chip->writebuf_cur].time <= chip->writebuf_samplecnt) + { + if (!(chip->writebuf[chip->writebuf_cur].reg & 0x200)) + { + break; + } + chip->writebuf[chip->writebuf_cur].reg &= 0x1ff; + OPL3_WriteReg(chip, chip->writebuf[chip->writebuf_cur].reg, + chip->writebuf[chip->writebuf_cur].data); + chip->writebuf_cur = (chip->writebuf_cur + 1) % OPL_WRITEBUF_SIZE; + } + chip->writebuf_samplecnt++; +} + +void OPL3_GenerateResampled(opl3_chip *chip, Bit16s *buf) +{ + while (chip->samplecnt >= chip->rateratio) + { + chip->oldsamples[0] = chip->samples[0]; + chip->oldsamples[1] = chip->samples[1]; + OPL3_Generate(chip, chip->samples); + chip->samplecnt -= chip->rateratio; + } + buf[0] = (Bit16s)((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt) + + chip->samples[0] * chip->samplecnt) / chip->rateratio); + buf[1] = (Bit16s)((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt) + + chip->samples[1] * chip->samplecnt) / chip->rateratio); + chip->samplecnt += 1 << RSM_FRAC; +} + +void OPL3_Reset(opl3_chip *chip, Bit32u samplerate) +{ + Bit8u slotnum; + Bit8u channum; + + memset(chip, 0, sizeof(opl3_chip)); + for (slotnum = 0; slotnum < 36; slotnum++) + { + chip->slot[slotnum].chip = chip; + chip->slot[slotnum].mod = &chip->zeromod; + chip->slot[slotnum].eg_rout = 0x1ff; + chip->slot[slotnum].eg_out = 0x1ff; + chip->slot[slotnum].eg_gen = envelope_gen_num_release; + chip->slot[slotnum].trem = (Bit8u*)&chip->zeromod; + chip->slot[slotnum].slot_num = slotnum; + } + for (channum = 0; channum < 18; channum++) + { + chip->channel[channum].slotz[0] = &chip->slot[ch_slot[channum]]; + chip->channel[channum].slotz[1] = &chip->slot[ch_slot[channum] + 3]; + chip->slot[ch_slot[channum]].channel = &chip->channel[channum]; + chip->slot[ch_slot[channum] + 3].channel = &chip->channel[channum]; + if ((channum % 9) < 3) + { + chip->channel[channum].pair = &chip->channel[channum + 3]; + } + else if ((channum % 9) < 6) + { + chip->channel[channum].pair = &chip->channel[channum - 3]; + } + chip->channel[channum].chip = chip; + chip->channel[channum].out[0] = &chip->zeromod; + chip->channel[channum].out[1] = &chip->zeromod; + chip->channel[channum].out[2] = &chip->zeromod; + chip->channel[channum].out[3] = &chip->zeromod; + chip->channel[channum].chtype = ch_2op; + chip->channel[channum].cha = 0xffff; + chip->channel[channum].chb = 0xffff; + chip->channel[channum].ch_num = channum; + OPL3_ChannelSetupAlg(&chip->channel[channum]); + } + chip->noise = 1; + chip->rateratio = (samplerate << RSM_FRAC) / 49716; + chip->tremoloshift = 4; + chip->vibshift = 1; +} + +void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v) +{ + Bit8u high = (reg >> 8) & 0x01; + Bit8u regm = reg & 0xff; + switch (regm & 0xf0) + { + case 0x00: + if (high) + { + switch (regm & 0x0f) + { + case 0x04: + OPL3_ChannelSet4Op(chip, v); + break; + case 0x05: + chip->newm = v & 0x01; + break; + } + } + else + { + switch (regm & 0x0f) + { + case 0x08: + chip->nts = (v >> 6) & 0x01; + break; + } + } + break; + case 0x20: + case 0x30: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite20(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0x40: + case 0x50: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite40(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0x60: + case 0x70: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite60(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0x80: + case 0x90: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite80(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0xe0: + case 0xf0: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWriteE0(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0xa0: + if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteA0(&chip->channel[9 * high + (regm & 0x0f)], v); + } + break; + case 0xb0: + if (regm == 0xbd && !high) + { + chip->tremoloshift = (((v >> 7) ^ 1) << 1) + 2; + chip->vibshift = ((v >> 6) & 0x01) ^ 1; + OPL3_ChannelUpdateRhythm(chip, v); + } + else if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteB0(&chip->channel[9 * high + (regm & 0x0f)], v); + if (v & 0x20) + { + OPL3_ChannelKeyOn(&chip->channel[9 * high + (regm & 0x0f)]); + } + else + { + OPL3_ChannelKeyOff(&chip->channel[9 * high + (regm & 0x0f)]); + } + } + break; + case 0xc0: + if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteC0(&chip->channel[9 * high + (regm & 0x0f)], v); + } + break; + } +} + +void OPL3_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v) +{ + Bit64u time1, time2; + + if (chip->writebuf[chip->writebuf_last].reg & 0x200) + { + OPL3_WriteReg(chip, chip->writebuf[chip->writebuf_last].reg & 0x1ff, + chip->writebuf[chip->writebuf_last].data); + + chip->writebuf_cur = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE; + chip->writebuf_samplecnt = chip->writebuf[chip->writebuf_last].time; + } + + chip->writebuf[chip->writebuf_last].reg = reg | 0x200; + chip->writebuf[chip->writebuf_last].data = v; + time1 = chip->writebuf_lasttime + OPL_WRITEBUF_DELAY; + time2 = chip->writebuf_samplecnt; + + if (time1 < time2) + { + time1 = time2; + } + + chip->writebuf[chip->writebuf_last].time = time1; + chip->writebuf_lasttime = time1; + chip->writebuf_last = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE; +} + +void OPL3_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples) +{ + Bit32u i; + + for(i = 0; i < numsamples; i++) + { + OPL3_GenerateResampled(chip, sndptr); + sndptr += 2; + } +} + +void OPL3_GenerateStreamMix(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples) +{ + Bit32u i; + Bit16s sample[2]; + + for(i = 0; i < numsamples; i++) + { + OPL3_GenerateResampled(chip, sample); + sndptr[0] += sample[0]; + sndptr[1] += sample[1]; + sndptr += 2; + } +} diff --git a/engine/src/Libraries/adlmidi/chips/nuked/nukedopl3.h b/engine/src/Libraries/adlmidi/chips/nuked/nukedopl3.h new file mode 100644 index 0000000..8d3318a --- /dev/null +++ b/engine/src/Libraries/adlmidi/chips/nuked/nukedopl3.h @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2013-2018 Alexey Khokholov (Nuke.YKT) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Nuked OPL3 emulator. + * Thanks: + * MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + * Feedback and Rhythm part calculation information. + * forums.submarine.org.uk(carbon14, opl3): + * Tremolo and phase generator calculation information. + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * siliconpr0n.org(John McMaster, digshadow): + * YMF262 and VRC VII decaps and die shots. + * + * version: 1.8 + */ + +#ifndef OPL_OPL3_H +#define OPL_OPL3_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define OPL_WRITEBUF_SIZE 1024 +#define OPL_WRITEBUF_DELAY 2 + +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef uint64_t Bit64u; +typedef int64_t Bit64s; +typedef uint32_t Bit32u; +typedef int32_t Bit32s; +typedef uint16_t Bit16u; +typedef int16_t Bit16s; +typedef uint8_t Bit8u; +typedef int8_t Bit8s; + +typedef struct _opl3_slot opl3_slot; +typedef struct _opl3_channel opl3_channel; +typedef struct _opl3_chip opl3_chip; + +struct _opl3_slot { + opl3_channel *channel; + opl3_chip *chip; + Bit16s out; + Bit16s fbmod; + Bit16s *mod; + Bit16s prout; + Bit16s eg_rout; + Bit16s eg_out; + Bit8u eg_inc; + Bit8u eg_gen; + Bit8u eg_rate; + Bit8u eg_ksl; + Bit8u *trem; + Bit8u reg_vib; + Bit8u reg_type; + Bit8u reg_ksr; + Bit8u reg_mult; + Bit8u reg_ksl; + Bit8u reg_tl; + Bit8u reg_ar; + Bit8u reg_dr; + Bit8u reg_sl; + Bit8u reg_rr; + Bit8u reg_wf; + Bit8u key; + Bit32u pg_reset; + Bit32u pg_phase; + Bit16u pg_phase_out; + Bit8u slot_num; +}; + +struct _opl3_channel { + opl3_slot *slotz[2];/*Don't use "slots" keyword to avoid conflict with Qt applications*/ + opl3_channel *pair; + opl3_chip *chip; + Bit16s *out[4]; + Bit8u chtype; + Bit16u f_num; + Bit8u block; + Bit8u fb; + Bit8u con; + Bit8u alg; + Bit8u ksv; + Bit16u cha, chb; + Bit8u ch_num; +}; + +typedef struct _opl3_writebuf { + Bit64u time; + Bit16u reg; + Bit8u data; +} opl3_writebuf; + +struct _opl3_chip { + opl3_channel channel[18]; + opl3_slot slot[36]; + Bit16u timer; + Bit64u eg_timer; + Bit8u eg_timerrem; + Bit8u eg_state; + Bit8u eg_add; + Bit8u newm; + Bit8u nts; + Bit8u rhy; + Bit8u vibpos; + Bit8u vibshift; + Bit8u tremolo; + Bit8u tremolopos; + Bit8u tremoloshift; + Bit32u noise; + Bit16s zeromod; + Bit32s mixbuff[2]; + Bit8u rm_hh_bit2; + Bit8u rm_hh_bit3; + Bit8u rm_hh_bit7; + Bit8u rm_hh_bit8; + Bit8u rm_tc_bit3; + Bit8u rm_tc_bit5; + /* OPL3L */ + Bit32s rateratio; + Bit32s samplecnt; + Bit16s oldsamples[2]; + Bit16s samples[2]; + + Bit64u writebuf_samplecnt; + Bit32u writebuf_cur; + Bit32u writebuf_last; + Bit64u writebuf_lasttime; + opl3_writebuf writebuf[OPL_WRITEBUF_SIZE]; +}; + +void OPL3_Generate(opl3_chip *chip, Bit16s *buf); +void OPL3_GenerateResampled(opl3_chip *chip, Bit16s *buf); +void OPL3_Reset(opl3_chip *chip, Bit32u samplerate); +void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v); +void OPL3_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v); +void OPL3_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples); +void OPL3_GenerateStreamMix(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/engine/src/Libraries/adlmidi/chips/nuked/nukedopl3_174.c b/engine/src/Libraries/adlmidi/chips/nuked/nukedopl3_174.c new file mode 100644 index 0000000..99eab16 --- /dev/null +++ b/engine/src/Libraries/adlmidi/chips/nuked/nukedopl3_174.c @@ -0,0 +1,1391 @@ +/* + * Copyright (C) 2013-2016 Alexey Khokholov (Nuke.YKT) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Nuked OPL3 emulator. + * Thanks: + * MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + * Feedback and Rhythm part calculation information. + * forums.submarine.org.uk(carbon14, opl3): + * Tremolo and phase generator calculation information. + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * + * version: 1.7.4 + */ + +#include +#include +#include +#include "nukedopl3_174.h" + +#define RSM_FRAC 10 + +/* Channel types */ + +enum { + ch_2op = 0, + ch_4op = 1, + ch_4op2 = 2, + ch_drum = 3 +}; + +/* Envelope key types */ + +enum { + egk_norm = 0x01, + egk_drum = 0x02 +}; + + +/* + * logsin table + */ + +static const Bit16u logsinrom[512] = { + 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, + 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, + 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, + 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, + 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, + 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, + 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, + 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, + 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, + 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, + 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, + 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, + 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, + 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, + 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, + 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, + 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, + 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, + 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, + 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, + 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, + 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, + 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, + 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, + 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, + 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, + 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, + 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, + 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, + 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, + 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x002, + 0x002, 0x002, 0x002, 0x003, 0x003, 0x003, 0x004, 0x004, + 0x004, 0x005, 0x005, 0x005, 0x006, 0x006, 0x007, 0x007, + 0x007, 0x008, 0x008, 0x009, 0x009, 0x00a, 0x00a, 0x00b, + 0x00c, 0x00c, 0x00d, 0x00d, 0x00e, 0x00f, 0x00f, 0x010, + 0x011, 0x011, 0x012, 0x013, 0x014, 0x014, 0x015, 0x016, + 0x017, 0x017, 0x018, 0x019, 0x01a, 0x01b, 0x01c, 0x01d, + 0x01e, 0x01f, 0x020, 0x021, 0x022, 0x023, 0x024, 0x025, + 0x026, 0x027, 0x028, 0x029, 0x02a, 0x02b, 0x02d, 0x02e, + 0x02f, 0x030, 0x031, 0x033, 0x034, 0x035, 0x037, 0x038, + 0x039, 0x03b, 0x03c, 0x03e, 0x03f, 0x040, 0x042, 0x043, + 0x045, 0x046, 0x048, 0x04a, 0x04b, 0x04d, 0x04e, 0x050, + 0x052, 0x053, 0x055, 0x057, 0x059, 0x05b, 0x05c, 0x05e, + 0x060, 0x062, 0x064, 0x066, 0x068, 0x06a, 0x06c, 0x06e, + 0x070, 0x072, 0x074, 0x076, 0x078, 0x07a, 0x07d, 0x07f, + 0x081, 0x083, 0x086, 0x088, 0x08a, 0x08d, 0x08f, 0x092, + 0x094, 0x097, 0x099, 0x09c, 0x09f, 0x0a1, 0x0a4, 0x0a7, + 0x0a9, 0x0ac, 0x0af, 0x0b2, 0x0b5, 0x0b8, 0x0bb, 0x0be, + 0x0c1, 0x0c4, 0x0c7, 0x0ca, 0x0cd, 0x0d1, 0x0d4, 0x0d7, + 0x0db, 0x0de, 0x0e2, 0x0e5, 0x0e9, 0x0ec, 0x0f0, 0x0f4, + 0x0f8, 0x0fb, 0x0ff, 0x103, 0x107, 0x10b, 0x10f, 0x114, + 0x118, 0x11c, 0x121, 0x125, 0x129, 0x12e, 0x133, 0x137, + 0x13c, 0x141, 0x146, 0x14b, 0x150, 0x155, 0x15b, 0x160, + 0x166, 0x16b, 0x171, 0x177, 0x17c, 0x182, 0x188, 0x18f, + 0x195, 0x19b, 0x1a2, 0x1a9, 0x1b0, 0x1b7, 0x1be, 0x1c5, + 0x1cd, 0x1d4, 0x1dc, 0x1e4, 0x1ec, 0x1f5, 0x1fd, 0x206, + 0x20f, 0x218, 0x222, 0x22c, 0x236, 0x240, 0x24b, 0x256, + 0x261, 0x26d, 0x279, 0x286, 0x293, 0x2a0, 0x2af, 0x2bd, + 0x2cd, 0x2dc, 0x2ed, 0x2ff, 0x311, 0x324, 0x339, 0x34e, + 0x365, 0x37e, 0x398, 0x3b5, 0x3d3, 0x3f5, 0x41a, 0x443, + 0x471, 0x4a6, 0x4e4, 0x52e, 0x58b, 0x607, 0x6c3, 0x859 +}; + +/* + * exp table + */ + +static const Bit16u exprom[256] = { + 0xff4, 0xfea, 0xfde, 0xfd4, 0xfc8, 0xfbe, 0xfb4, 0xfa8, + 0xf9e, 0xf92, 0xf88, 0xf7e, 0xf72, 0xf68, 0xf5c, 0xf52, + 0xf48, 0xf3e, 0xf32, 0xf28, 0xf1e, 0xf14, 0xf08, 0xefe, + 0xef4, 0xeea, 0xee0, 0xed4, 0xeca, 0xec0, 0xeb6, 0xeac, + 0xea2, 0xe98, 0xe8e, 0xe84, 0xe7a, 0xe70, 0xe66, 0xe5c, + 0xe52, 0xe48, 0xe3e, 0xe34, 0xe2a, 0xe20, 0xe16, 0xe0c, + 0xe04, 0xdfa, 0xdf0, 0xde6, 0xddc, 0xdd2, 0xdca, 0xdc0, + 0xdb6, 0xdac, 0xda4, 0xd9a, 0xd90, 0xd88, 0xd7e, 0xd74, + 0xd6a, 0xd62, 0xd58, 0xd50, 0xd46, 0xd3c, 0xd34, 0xd2a, + 0xd22, 0xd18, 0xd10, 0xd06, 0xcfe, 0xcf4, 0xcec, 0xce2, + 0xcda, 0xcd0, 0xcc8, 0xcbe, 0xcb6, 0xcae, 0xca4, 0xc9c, + 0xc92, 0xc8a, 0xc82, 0xc78, 0xc70, 0xc68, 0xc60, 0xc56, + 0xc4e, 0xc46, 0xc3c, 0xc34, 0xc2c, 0xc24, 0xc1c, 0xc12, + 0xc0a, 0xc02, 0xbfa, 0xbf2, 0xbea, 0xbe0, 0xbd8, 0xbd0, + 0xbc8, 0xbc0, 0xbb8, 0xbb0, 0xba8, 0xba0, 0xb98, 0xb90, + 0xb88, 0xb80, 0xb78, 0xb70, 0xb68, 0xb60, 0xb58, 0xb50, + 0xb48, 0xb40, 0xb38, 0xb32, 0xb2a, 0xb22, 0xb1a, 0xb12, + 0xb0a, 0xb02, 0xafc, 0xaf4, 0xaec, 0xae4, 0xade, 0xad6, + 0xace, 0xac6, 0xac0, 0xab8, 0xab0, 0xaa8, 0xaa2, 0xa9a, + 0xa92, 0xa8c, 0xa84, 0xa7c, 0xa76, 0xa6e, 0xa68, 0xa60, + 0xa58, 0xa52, 0xa4a, 0xa44, 0xa3c, 0xa36, 0xa2e, 0xa28, + 0xa20, 0xa18, 0xa12, 0xa0c, 0xa04, 0x9fe, 0x9f6, 0x9f0, + 0x9e8, 0x9e2, 0x9da, 0x9d4, 0x9ce, 0x9c6, 0x9c0, 0x9b8, + 0x9b2, 0x9ac, 0x9a4, 0x99e, 0x998, 0x990, 0x98a, 0x984, + 0x97c, 0x976, 0x970, 0x96a, 0x962, 0x95c, 0x956, 0x950, + 0x948, 0x942, 0x93c, 0x936, 0x930, 0x928, 0x922, 0x91c, + 0x916, 0x910, 0x90a, 0x904, 0x8fc, 0x8f6, 0x8f0, 0x8ea, + 0x8e4, 0x8de, 0x8d8, 0x8d2, 0x8cc, 0x8c6, 0x8c0, 0x8ba, + 0x8b4, 0x8ae, 0x8a8, 0x8a2, 0x89c, 0x896, 0x890, 0x88a, + 0x884, 0x87e, 0x878, 0x872, 0x86c, 0x866, 0x860, 0x85a, + 0x854, 0x850, 0x84a, 0x844, 0x83e, 0x838, 0x832, 0x82c, + 0x828, 0x822, 0x81c, 0x816, 0x810, 0x80c, 0x806, 0x800 +}; + +/* + * freq mult table multiplied by 2 + * + * 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15 + */ + +static const Bit8u mt[16] = { + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 +}; + +/* + * ksl table + */ + +static const Bit8u kslrom[16] = { + 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64 +}; + +static const Bit8u kslshift[4] = { + 8, 1, 2, 0 +}; + +/* + * envelope generator constants + */ + +static const Bit8u eg_incstep[3][4][8] = { + { + { 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 } + }, + { + { 0, 1, 0, 1, 0, 1, 0, 1 }, + { 0, 1, 0, 1, 1, 1, 0, 1 }, + { 0, 1, 1, 1, 0, 1, 1, 1 }, + { 0, 1, 1, 1, 1, 1, 1, 1 } + }, + { + { 1, 1, 1, 1, 1, 1, 1, 1 }, + { 2, 2, 1, 1, 1, 1, 1, 1 }, + { 2, 2, 1, 1, 2, 2, 1, 1 }, + { 2, 2, 2, 2, 2, 2, 1, 1 } + } +}; + +static const Bit8u eg_incdesc[16] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2 +}; + +static const Bit8s eg_incsh[16] = { + 0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, -1, -2 +}; + +/* + * address decoding + */ + +static const Bit8s ad_slot[0x20] = { + 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, + 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static const Bit8u ch_slot[18] = { + 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 +}; + +/* + * Envelope generator + */ + +static void OPL3_EnvelopeGenOff(opl3_slot *slot); +static void OPL3_EnvelopeGenAttack(opl3_slot *slot); +static void OPL3_EnvelopeGenDecay(opl3_slot *slot); +static void OPL3_EnvelopeGenSustain(opl3_slot *slot); +static void OPL3_EnvelopeGenRelease(opl3_slot *slot); + +typedef void(*envelope_genfunc)(opl3_slot *slot); + +envelope_genfunc envelope_gen[5] = { + OPL3_EnvelopeGenOff, + OPL3_EnvelopeGenAttack, + OPL3_EnvelopeGenDecay, + OPL3_EnvelopeGenSustain, + OPL3_EnvelopeGenRelease +}; + +enum envelope_gen_num +{ + envelope_gen_num_off = 0, + envelope_gen_num_attack = 1, + envelope_gen_num_decay = 2, + envelope_gen_num_sustain = 3, + envelope_gen_num_release = 4 +}; + +static Bit8u OPL3_EnvelopeCalcRate(opl3_slot *slot, Bit8u reg_rate) +{ + Bit8u rate; + if (reg_rate == 0x00) + { + return 0x00; + } + rate = (reg_rate << 2) + + (slot->reg_ksr ? slot->channel->ksv : (slot->channel->ksv >> 2)); + if (rate > 0x3c) + { + rate = 0x3c; + } + return rate; +} + +static void OPL3_EnvelopeUpdateKSL(opl3_slot *slot) +{ + Bit16s ksl = (kslrom[slot->channel->f_num >> 6] << 2) + - ((0x08 - slot->channel->block) << 5); + if (ksl < 0) + { + ksl = 0; + } + slot->eg_ksl = (Bit8u)ksl; +} + +static void OPL3_EnvelopeUpdateRate(opl3_slot *slot) +{ + switch (slot->eg_gen) + { + case envelope_gen_num_off: + case envelope_gen_num_attack: + slot->eg_rate = OPL3_EnvelopeCalcRate(slot, slot->reg_ar); + break; + case envelope_gen_num_decay: + slot->eg_rate = OPL3_EnvelopeCalcRate(slot, slot->reg_dr); + break; + case envelope_gen_num_sustain: + case envelope_gen_num_release: + slot->eg_rate = OPL3_EnvelopeCalcRate(slot, slot->reg_rr); + break; + } +} + +static void OPL3_EnvelopeGenOff(opl3_slot *slot) +{ + slot->eg_rout = 0x1ff; +} + +static void OPL3_EnvelopeGenAttack(opl3_slot *slot) +{ + if (slot->eg_rout == 0x00) + { + slot->eg_gen = envelope_gen_num_decay; + OPL3_EnvelopeUpdateRate(slot); + return; + } + slot->eg_rout += ((~slot->eg_rout) * slot->eg_inc) >> 3; + if (slot->eg_rout < 0x00) + { + slot->eg_rout = 0x00; + } +} + +static void OPL3_EnvelopeGenDecay(opl3_slot *slot) +{ + if (slot->eg_rout >= slot->reg_sl << 4) + { + slot->eg_gen = envelope_gen_num_sustain; + OPL3_EnvelopeUpdateRate(slot); + return; + } + slot->eg_rout += slot->eg_inc; +} + +static void OPL3_EnvelopeGenSustain(opl3_slot *slot) +{ + if (!slot->reg_type) + { + OPL3_EnvelopeGenRelease(slot); + } +} + +static void OPL3_EnvelopeGenRelease(opl3_slot *slot) +{ + if (slot->eg_rout >= 0x1ff) + { + slot->eg_gen = envelope_gen_num_off; + slot->eg_rout = 0x1ff; + OPL3_EnvelopeUpdateRate(slot); + return; + } + slot->eg_rout += slot->eg_inc; +} + +static void OPL3_EnvelopeCalc(opl3_slot *slot) +{ + Bit8u rate_h, rate_l; + Bit8u inc = 0; + rate_h = slot->eg_rate >> 2; + rate_l = slot->eg_rate & 3; + if (eg_incsh[rate_h] > 0) + { + if ((slot->chip->timer & ((1 << eg_incsh[rate_h]) - 1)) == 0) + { + inc = eg_incstep[eg_incdesc[rate_h]][rate_l] + [((slot->chip->timer)>> eg_incsh[rate_h]) & 0x07]; + } + } + else + { + inc = eg_incstep[eg_incdesc[rate_h]][rate_l] + [slot->chip->timer & 0x07] << (-eg_incsh[rate_h]); + } + slot->eg_inc = inc; + slot->eg_out = slot->eg_rout + (slot->reg_tl << 2) + + (slot->eg_ksl >> kslshift[slot->reg_ksl]) + *slot->trem; + if (slot->eg_out > 0x1ff) /* TODO: Remove this if possible */ + { + slot->eg_out = 0x1ff; + } + slot->eg_out <<= 3; + + envelope_gen[slot->eg_gen](slot); +} + +static void OPL3_EnvelopeKeyOn(opl3_slot *slot, Bit8u type) +{ + if (!slot->key) + { + slot->eg_gen = envelope_gen_num_attack; + OPL3_EnvelopeUpdateRate(slot); + if ((slot->eg_rate >> 2) == 0x0f) + { + slot->eg_gen = envelope_gen_num_decay; + OPL3_EnvelopeUpdateRate(slot); + slot->eg_rout = 0x00; + } + slot->pg_phase = 0x00; + } + slot->key |= type; +} + +static void OPL3_EnvelopeKeyOff(opl3_slot *slot, Bit8u type) +{ + if (slot->key) + { + slot->key &= (~type); + if (!slot->key) + { + slot->eg_gen = envelope_gen_num_release; + OPL3_EnvelopeUpdateRate(slot); + } + } +} + +/* + * Phase Generator + */ + +static void OPL3_PhaseGenerate(opl3_slot *slot) +{ + Bit16u f_num; + Bit32u basefreq; + + f_num = slot->channel->f_num; + if (slot->reg_vib) + { + Bit8s range; + Bit8u vibpos; + + range = (f_num >> 7) & 7; + vibpos = slot->chip->vibpos; + + if (!(vibpos & 3)) + { + range = 0; + } + else if (vibpos & 1) + { + range >>= 1; + } + range >>= slot->chip->vibshift; + + if (vibpos & 4) + { + range = -range; + } + f_num += range; + } + basefreq = (f_num << slot->channel->block) >> 1; + slot->pg_phase += (basefreq * mt[slot->reg_mult]) >> 1; +} + +/* + * Noise Generator + */ + +static void OPL3_NoiseGenerate(opl3_chip *chip) +{ + if (chip->noise & 0x01) + { + chip->noise ^= 0x800302; + } + chip->noise >>= 1; +} + +/* + * Slot + */ + +static void OPL3_SlotWrite20(opl3_slot *slot, Bit8u data) +{ + if ((data >> 7) & 0x01) + { + slot->trem = &slot->chip->tremolo; + } + else + { + slot->trem = (Bit8u*)&slot->chip->zeromod; + } + slot->reg_vib = (data >> 6) & 0x01; + slot->reg_type = (data >> 5) & 0x01; + slot->reg_ksr = (data >> 4) & 0x01; + slot->reg_mult = data & 0x0f; + OPL3_EnvelopeUpdateRate(slot); +} + +static void OPL3_SlotWrite40(opl3_slot *slot, Bit8u data) +{ + slot->reg_ksl = (data >> 6) & 0x03; + slot->reg_tl = data & 0x3f; + OPL3_EnvelopeUpdateKSL(slot); +} + +static void OPL3_SlotWrite60(opl3_slot *slot, Bit8u data) +{ + slot->reg_ar = (data >> 4) & 0x0f; + slot->reg_dr = data & 0x0f; + OPL3_EnvelopeUpdateRate(slot); +} + +static void OPL3_SlotWrite80(opl3_slot *slot, Bit8u data) +{ + slot->reg_sl = (data >> 4) & 0x0f; + if (slot->reg_sl == 0x0f) + { + slot->reg_sl = 0x1f; + } + slot->reg_rr = data & 0x0f; + OPL3_EnvelopeUpdateRate(slot); +} + +static void OPL3_SlotWriteE0(opl3_slot *slot, Bit8u data) +{ + slot->reg_wf = data & 0x07; + if (slot->chip->newm == 0x00) + { + slot->reg_wf &= 0x03; + } + + switch (slot->reg_wf) + { + case 1: + case 4: + case 5: + slot->maskzero = 0x200; + break; + case 3: + slot->maskzero = 0x100; + break; + default: + slot->maskzero = 0; + break; + } + + switch (slot->reg_wf) + { + case 4: + slot->signpos = (31-8); /* sigext of (phase & 0x100) */ + break; + case 0: + case 6: + case 7: + slot->signpos = (31-9); /* sigext of (phase & 0x200) */ + break; + default: + slot->signpos = (31-16); /* set "neg" to zero */ + break; + } + + switch (slot->reg_wf) + { + case 4: + case 5: + slot->phaseshift = 1; + break; + case 6: + slot->phaseshift = 16; /* set phase to zero and flag for non-sin wave */ + break; + case 7: + slot->phaseshift = 32; /* no shift (work by mod 32), but flag for non-sin wave */ + break; + default: + slot->phaseshift = 0; + break; + } +} + +static void OPL3_SlotGeneratePhase(opl3_slot *slot, Bit16u phase) +{ + Bit32u neg, level; + Bit8u phaseshift; + + /* Fast paths for mute segments */ + if (phase & slot->maskzero) + { + slot->out = 0; + return; + } + + neg = (Bit32s)((Bit32u)phase << slot->signpos) >> 31; + phaseshift = slot->phaseshift; + level = slot->eg_out; + + phase <<= phaseshift; + if (phaseshift <= 1) + { + level += logsinrom[phase & 0x1ff]; + } + else + { + level += ((phase ^ neg) & 0x3ff) << 3; + } + slot->out = exprom[level & 0xff] >> (level >> 8) ^ neg; +} + +static void OPL3_SlotGenerate(opl3_slot *slot) +{ + OPL3_SlotGeneratePhase(slot, (Bit16u)(slot->pg_phase >> 9) + *slot->mod); +} + +static void OPL3_SlotGenerateZM(opl3_slot *slot) +{ + OPL3_SlotGeneratePhase(slot, (Bit16u)(slot->pg_phase >> 9)); +} + +static void OPL3_SlotCalcFB(opl3_slot *slot) +{ + if (slot->channel->fb != 0x00) + { + slot->fbmod = (slot->prout + slot->out) >> (0x09 - slot->channel->fb); + } + else + { + slot->fbmod = 0; + } + slot->prout = slot->out; +} + +/* + * Channel + */ + +static void OPL3_ChannelSetupAlg(opl3_channel *channel); + +static void OPL3_ChannelUpdateRhythm(opl3_chip *chip, Bit8u data) +{ + opl3_channel *channel6; + opl3_channel *channel7; + opl3_channel *channel8; + Bit8u chnum; + + chip->rhy = data & 0x3f; + if (chip->rhy & 0x20) + { + channel6 = &chip->channel[6]; + channel7 = &chip->channel[7]; + channel8 = &chip->channel[8]; + channel6->out[0] = &channel6->slotz[1]->out; + channel6->out[1] = &channel6->slotz[1]->out; + channel6->out[2] = &chip->zeromod; + channel6->out[3] = &chip->zeromod; + channel7->out[0] = &channel7->slotz[0]->out; + channel7->out[1] = &channel7->slotz[0]->out; + channel7->out[2] = &channel7->slotz[1]->out; + channel7->out[3] = &channel7->slotz[1]->out; + channel8->out[0] = &channel8->slotz[0]->out; + channel8->out[1] = &channel8->slotz[0]->out; + channel8->out[2] = &channel8->slotz[1]->out; + channel8->out[3] = &channel8->slotz[1]->out; + for (chnum = 6; chnum < 9; chnum++) + { + chip->channel[chnum].chtype = ch_drum; + } + OPL3_ChannelSetupAlg(channel6); + /*hh*/ + if (chip->rhy & 0x01) + { + OPL3_EnvelopeKeyOn(channel7->slotz[0], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel7->slotz[0], egk_drum); + } + /*tc*/ + if (chip->rhy & 0x02) + { + OPL3_EnvelopeKeyOn(channel8->slotz[1], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel8->slotz[1], egk_drum); + } + /*tom*/ + if (chip->rhy & 0x04) + { + OPL3_EnvelopeKeyOn(channel8->slotz[0], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel8->slotz[0], egk_drum); + } + /*sd*/ + if (chip->rhy & 0x08) + { + OPL3_EnvelopeKeyOn(channel7->slotz[1], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel7->slotz[1], egk_drum); + } + /*bd*/ + if (chip->rhy & 0x10) + { + OPL3_EnvelopeKeyOn(channel6->slotz[0], egk_drum); + OPL3_EnvelopeKeyOn(channel6->slotz[1], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel6->slotz[0], egk_drum); + OPL3_EnvelopeKeyOff(channel6->slotz[1], egk_drum); + } + } + else + { + for (chnum = 6; chnum < 9; chnum++) + { + chip->channel[chnum].chtype = ch_2op; + OPL3_ChannelSetupAlg(&chip->channel[chnum]); + OPL3_EnvelopeKeyOff(chip->channel[chnum].slotz[0], egk_drum); + OPL3_EnvelopeKeyOff(chip->channel[chnum].slotz[1], egk_drum); + } + } +} + +static void OPL3_ChannelWriteA0(opl3_channel *channel, Bit8u data) +{ + if (channel->chip->newm && channel->chtype == ch_4op2) + { + return; + } + channel->f_num = (channel->f_num & 0x300) | data; + channel->ksv = (channel->block << 1) + | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + OPL3_EnvelopeUpdateKSL(channel->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->slotz[1]); + OPL3_EnvelopeUpdateRate(channel->slotz[0]); + OPL3_EnvelopeUpdateRate(channel->slotz[1]); + if (channel->chip->newm && channel->chtype == ch_4op) + { + channel->pair->f_num = channel->f_num; + channel->pair->ksv = channel->ksv; + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[1]); + OPL3_EnvelopeUpdateRate(channel->pair->slotz[0]); + OPL3_EnvelopeUpdateRate(channel->pair->slotz[1]); + } +} + +static void OPL3_ChannelWriteB0(opl3_channel *channel, Bit8u data) +{ + if (channel->chip->newm && channel->chtype == ch_4op2) + { + return; + } + channel->f_num = (channel->f_num & 0xff) | ((data & 0x03) << 8); + channel->block = (data >> 2) & 0x07; + channel->ksv = (channel->block << 1) + | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + OPL3_EnvelopeUpdateKSL(channel->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->slotz[1]); + OPL3_EnvelopeUpdateRate(channel->slotz[0]); + OPL3_EnvelopeUpdateRate(channel->slotz[1]); + if (channel->chip->newm && channel->chtype == ch_4op) + { + channel->pair->f_num = channel->f_num; + channel->pair->block = channel->block; + channel->pair->ksv = channel->ksv; + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[1]); + OPL3_EnvelopeUpdateRate(channel->pair->slotz[0]); + OPL3_EnvelopeUpdateRate(channel->pair->slotz[1]); + } +} + +static void OPL3_ChannelSetupAlg(opl3_channel *channel) +{ + if (channel->chtype == ch_drum) + { + switch (channel->alg & 0x01) + { + case 0x00: + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->slotz[0]->out; + break; + case 0x01: + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->chip->zeromod; + break; + } + return; + } + if (channel->alg & 0x08) + { + return; + } + if (channel->alg & 0x04) + { + channel->pair->out[0] = &channel->chip->zeromod; + channel->pair->out[1] = &channel->chip->zeromod; + channel->pair->out[2] = &channel->chip->zeromod; + channel->pair->out[3] = &channel->chip->zeromod; + switch (channel->alg & 0x03) + { + case 0x00: + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->pair->slotz[0]->out; + channel->slotz[0]->mod = &channel->pair->slotz[1]->out; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->slotz[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x01: + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->pair->slotz[0]->out; + channel->slotz[0]->mod = &channel->chip->zeromod; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->pair->slotz[1]->out; + channel->out[1] = &channel->slotz[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x02: + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->chip->zeromod; + channel->slotz[0]->mod = &channel->pair->slotz[1]->out; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->pair->slotz[0]->out; + channel->out[1] = &channel->slotz[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x03: + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->chip->zeromod; + channel->slotz[0]->mod = &channel->pair->slotz[1]->out; + channel->slotz[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->pair->slotz[0]->out; + channel->out[1] = &channel->slotz[0]->out; + channel->out[2] = &channel->slotz[1]->out; + channel->out[3] = &channel->chip->zeromod; + break; + } + } + else + { + switch (channel->alg & 0x01) + { + case 0x00: + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->slotz[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x01: + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->slotz[0]->out; + channel->out[1] = &channel->slotz[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + } + } +} + +static void OPL3_ChannelWriteC0(opl3_channel *channel, Bit8u data) +{ + channel->fb = (data & 0x0e) >> 1; + channel->con = data & 0x01; + channel->alg = channel->con; + if (channel->chip->newm) + { + if (channel->chtype == ch_4op) + { + channel->pair->alg = 0x04 | (channel->con << 1) | (channel->pair->con); + channel->alg = 0x08; + OPL3_ChannelSetupAlg(channel->pair); + } + else if (channel->chtype == ch_4op2) + { + channel->alg = 0x04 | (channel->pair->con << 1) | (channel->con); + channel->pair->alg = 0x08; + OPL3_ChannelSetupAlg(channel); + } + else + { + OPL3_ChannelSetupAlg(channel); + } + } + else + { + OPL3_ChannelSetupAlg(channel); + } + if (channel->chip->newm) + { + channel->cha = ((data >> 4) & 0x01) ? ~0 : 0; + channel->chb = ((data >> 5) & 0x01) ? ~0 : 0; + } + else + { + channel->cha = channel->chb = ~0; + } +} + +static void OPL3_ChannelKeyOn(opl3_channel *channel) +{ + if (channel->chip->newm) + { + if (channel->chtype == ch_4op) + { + OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm); + OPL3_EnvelopeKeyOn(channel->pair->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->pair->slotz[1], egk_norm); + } + else if (channel->chtype == ch_2op || channel->chtype == ch_drum) + { + OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm); + } + } + else + { + OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm); + } +} + +static void OPL3_ChannelKeyOff(opl3_channel *channel) +{ + if (channel->chip->newm) + { + if (channel->chtype == ch_4op) + { + OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm); + OPL3_EnvelopeKeyOff(channel->pair->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->pair->slotz[1], egk_norm); + } + else if (channel->chtype == ch_2op || channel->chtype == ch_drum) + { + OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm); + } + } + else + { + OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm); + } +} + +static void OPL3_ChannelSet4Op(opl3_chip *chip, Bit8u data) +{ + Bit8u bit; + Bit8u chnum; + for (bit = 0; bit < 6; bit++) + { + chnum = bit; + if (bit >= 3) + { + chnum += 9 - 3; + } + if ((data >> bit) & 0x01) + { + chip->channel[chnum].chtype = ch_4op; + chip->channel[chnum + 3].chtype = ch_4op2; + } + else + { + chip->channel[chnum].chtype = ch_2op; + chip->channel[chnum + 3].chtype = ch_2op; + } + } +} + +static Bit16s OPL3_ClipSample(Bit32s sample) +{ + if (sample > 32767) + { + sample = 32767; + } + else if (sample < -32768) + { + sample = -32768; + } + return (Bit16s)sample; +} + +static void OPL3_GenerateRhythm1(opl3_chip *chip) +{ + opl3_channel *channel6; + opl3_channel *channel7; + opl3_channel *channel8; + Bit16u phase14; + Bit16u phase17; + Bit16u phase; + Bit16u phasebit; + + channel6 = &chip->channel[6]; + channel7 = &chip->channel[7]; + channel8 = &chip->channel[8]; + OPL3_SlotGenerate(channel6->slotz[0]); + phase14 = (channel7->slotz[0]->pg_phase >> 9) & 0x3ff; + phase17 = (channel8->slotz[1]->pg_phase >> 9) & 0x3ff; + phase = 0x00; + /*hh tc phase bit*/ + phasebit = ((phase14 & 0x08) | (((phase14 >> 5) ^ phase14) & 0x04) + | (((phase17 >> 2) ^ phase17) & 0x08)) ? 0x01 : 0x00; + /*hh*/ + phase = (phasebit << 9) + | (0x34 << ((phasebit ^ (chip->noise & 0x01)) << 1)); + OPL3_SlotGeneratePhase(channel7->slotz[0], phase); + /*tt*/ + OPL3_SlotGenerateZM(channel8->slotz[0]); +} + +static void OPL3_GenerateRhythm2(opl3_chip *chip) +{ + opl3_channel *channel6; + opl3_channel *channel7; + opl3_channel *channel8; + Bit16u phase14; + Bit16u phase17; + Bit16u phase; + Bit16u phasebit; + + channel6 = &chip->channel[6]; + channel7 = &chip->channel[7]; + channel8 = &chip->channel[8]; + OPL3_SlotGenerate(channel6->slotz[1]); + phase14 = (channel7->slotz[0]->pg_phase >> 9) & 0x3ff; + phase17 = (channel8->slotz[1]->pg_phase >> 9) & 0x3ff; + phase = 0x00; + /*hh tc phase bit*/ + phasebit = ((phase14 & 0x08) | (((phase14 >> 5) ^ phase14) & 0x04) + | (((phase17 >> 2) ^ phase17) & 0x08)) ? 0x01 : 0x00; + /*sd*/ + phase = (0x100 << ((phase14 >> 8) & 0x01)) ^ ((chip->noise & 0x01) << 8); + OPL3_SlotGeneratePhase(channel7->slotz[1], phase); + /*tc*/ + phase = 0x100 | (phasebit << 9); + OPL3_SlotGeneratePhase(channel8->slotz[1], phase); +} + +void OPL3v17_Generate(opl3_chip *chip, Bit16s *buf) +{ + Bit8u ii; + Bit8u jj; + Bit16s accm; + + buf[1] = OPL3_ClipSample(chip->mixbuff[1]); + + for (ii = 0; ii < 12; ii++) + { + OPL3_SlotCalcFB(&chip->chipslot[ii]); + OPL3_PhaseGenerate(&chip->chipslot[ii]); + OPL3_EnvelopeCalc(&chip->chipslot[ii]); + OPL3_SlotGenerate(&chip->chipslot[ii]); + } + + for (ii = 12; ii < 15; ii++) + { + OPL3_SlotCalcFB(&chip->chipslot[ii]); + OPL3_PhaseGenerate(&chip->chipslot[ii]); + OPL3_EnvelopeCalc(&chip->chipslot[ii]); + } + + if (chip->rhy & 0x20) + { + OPL3_GenerateRhythm1(chip); + } + else + { + OPL3_SlotGenerate(&chip->chipslot[12]); + OPL3_SlotGenerate(&chip->chipslot[13]); + OPL3_SlotGenerate(&chip->chipslot[14]); + } + + chip->mixbuff[0] = 0; + for (ii = 0; ii < 18; ii++) + { + accm = 0; + for (jj = 0; jj < 4; jj++) + { + accm += *chip->channel[ii].out[jj]; + } + chip->mixbuff[0] += (Bit16s)(accm & chip->channel[ii].cha); + } + + for (ii = 15; ii < 18; ii++) + { + OPL3_SlotCalcFB(&chip->chipslot[ii]); + OPL3_PhaseGenerate(&chip->chipslot[ii]); + OPL3_EnvelopeCalc(&chip->chipslot[ii]); + } + + if (chip->rhy & 0x20) + { + OPL3_GenerateRhythm2(chip); + } + else + { + OPL3_SlotGenerate(&chip->chipslot[15]); + OPL3_SlotGenerate(&chip->chipslot[16]); + OPL3_SlotGenerate(&chip->chipslot[17]); + } + + buf[0] = OPL3_ClipSample(chip->mixbuff[0]); + + for (ii = 18; ii < 33; ii++) + { + OPL3_SlotCalcFB(&chip->chipslot[ii]); + OPL3_PhaseGenerate(&chip->chipslot[ii]); + OPL3_EnvelopeCalc(&chip->chipslot[ii]); + OPL3_SlotGenerate(&chip->chipslot[ii]); + } + + chip->mixbuff[1] = 0; + for (ii = 0; ii < 18; ii++) + { + accm = 0; + for (jj = 0; jj < 4; jj++) + { + accm += *chip->channel[ii].out[jj]; + } + chip->mixbuff[1] += (Bit16s)(accm & chip->channel[ii].chb); + } + + for (ii = 33; ii < 36; ii++) + { + OPL3_SlotCalcFB(&chip->chipslot[ii]); + OPL3_PhaseGenerate(&chip->chipslot[ii]); + OPL3_EnvelopeCalc(&chip->chipslot[ii]); + OPL3_SlotGenerate(&chip->chipslot[ii]); + } + + OPL3_NoiseGenerate(chip); + + if ((chip->timer & 0x3f) == 0x3f) + { + chip->tremolopos = (chip->tremolopos + 1) % 210; + } + if (chip->tremolopos < 105) + { + chip->tremolo = chip->tremolopos >> chip->tremoloshift; + } + else + { + chip->tremolo = (210 - chip->tremolopos) >> chip->tremoloshift; + } + + if ((chip->timer & 0x3ff) == 0x3ff) + { + chip->vibpos = (chip->vibpos + 1) & 7; + } + + chip->timer++; + + while (chip->writebuf[chip->writebuf_cur].time <= chip->writebuf_samplecnt) + { + if (!(chip->writebuf[chip->writebuf_cur].reg & 0x200)) + { + break; + } + chip->writebuf[chip->writebuf_cur].reg &= 0x1ff; + OPL3v17_WriteReg(chip, chip->writebuf[chip->writebuf_cur].reg, + chip->writebuf[chip->writebuf_cur].data); + chip->writebuf_cur = (chip->writebuf_cur + 1) % OPL_WRITEBUF_SIZE; + } + chip->writebuf_samplecnt++; +} + +void OPL3v17_GenerateResampled(opl3_chip *chip, Bit16s *buf) +{ + while (chip->samplecnt >= chip->rateratio) + { + chip->oldsamples[0] = chip->samples[0]; + chip->oldsamples[1] = chip->samples[1]; + OPL3v17_Generate(chip, chip->samples); + chip->samplecnt -= chip->rateratio; + } + buf[0] = (Bit16s)((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt) + + chip->samples[0] * chip->samplecnt) / chip->rateratio); + buf[1] = (Bit16s)((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt) + + chip->samples[1] * chip->samplecnt) / chip->rateratio); + chip->samplecnt += 1 << RSM_FRAC; +} + +void OPL3v17_Reset(opl3_chip *chip, Bit32u samplerate) +{ + Bit8u slotnum; + Bit8u channum; + + memset(chip, 0, sizeof(opl3_chip)); + for (slotnum = 0; slotnum < 36; slotnum++) + { + chip->chipslot[slotnum].chip = chip; + chip->chipslot[slotnum].mod = &chip->zeromod; + chip->chipslot[slotnum].eg_rout = 0x1ff; + chip->chipslot[slotnum].eg_out = 0x1ff << 3; + chip->chipslot[slotnum].eg_gen = envelope_gen_num_off; + chip->chipslot[slotnum].trem = (Bit8u*)&chip->zeromod; + chip->chipslot[slotnum].signpos = (31-9); /* for wf=0 need use sigext of (phase & 0x200) */ + } + for (channum = 0; channum < 18; channum++) + { + chip->channel[channum].slotz[0] = &chip->chipslot[ch_slot[channum]]; + chip->channel[channum].slotz[1] = &chip->chipslot[ch_slot[channum] + 3]; + chip->chipslot[ch_slot[channum]].channel = &chip->channel[channum]; + chip->chipslot[ch_slot[channum] + 3].channel = &chip->channel[channum]; + if ((channum % 9) < 3) + { + chip->channel[channum].pair = &chip->channel[channum + 3]; + } + else if ((channum % 9) < 6) + { + chip->channel[channum].pair = &chip->channel[channum - 3]; + } + chip->channel[channum].chip = chip; + chip->channel[channum].out[0] = &chip->zeromod; + chip->channel[channum].out[1] = &chip->zeromod; + chip->channel[channum].out[2] = &chip->zeromod; + chip->channel[channum].out[3] = &chip->zeromod; + chip->channel[channum].chtype = ch_2op; + chip->channel[channum].cha = ~0; + chip->channel[channum].chb = ~0; + OPL3_ChannelSetupAlg(&chip->channel[channum]); + } + chip->noise = 0x306600; + chip->rateratio = (samplerate << RSM_FRAC) / 49716; + chip->tremoloshift = 4; + chip->vibshift = 1; +} + +void OPL3v17_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v) +{ + Bit8u high = (reg >> 8) & 0x01; + Bit8u regm = reg & 0xff; + switch (regm & 0xf0) + { + case 0x00: + if (high) + { + switch (regm & 0x0f) + { + case 0x04: + OPL3_ChannelSet4Op(chip, v); + break; + case 0x05: + chip->newm = v & 0x01; + break; + } + } + else + { + switch (regm & 0x0f) + { + case 0x08: + chip->nts = (v >> 6) & 0x01; + break; + } + } + break; + case 0x20: + case 0x30: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite20(&chip->chipslot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0x40: + case 0x50: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite40(&chip->chipslot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0x60: + case 0x70: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite60(&chip->chipslot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0x80: + case 0x90: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite80(&chip->chipslot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0xe0: + case 0xf0: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWriteE0(&chip->chipslot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0xa0: + if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteA0(&chip->channel[9 * high + (regm & 0x0f)], v); + } + break; + case 0xb0: + if (regm == 0xbd && !high) + { + chip->tremoloshift = (((v >> 7) ^ 1) << 1) + 2; + chip->vibshift = ((v >> 6) & 0x01) ^ 1; + OPL3_ChannelUpdateRhythm(chip, v); + } + else if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteB0(&chip->channel[9 * high + (regm & 0x0f)], v); + if (v & 0x20) + { + OPL3_ChannelKeyOn(&chip->channel[9 * high + (regm & 0x0f)]); + } + else + { + OPL3_ChannelKeyOff(&chip->channel[9 * high + (regm & 0x0f)]); + } + } + break; + case 0xc0: + if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteC0(&chip->channel[9 * high + (regm & 0x0f)], v); + } + break; + } +} + +void OPL3v17_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v) +{ + Bit64u time1, time2; + + if (chip->writebuf[chip->writebuf_last].reg & 0x200) + { + OPL3v17_WriteReg(chip, chip->writebuf[chip->writebuf_last].reg & 0x1ff, + chip->writebuf[chip->writebuf_last].data); + + chip->writebuf_cur = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE; + chip->writebuf_samplecnt = chip->writebuf[chip->writebuf_last].time; + } + + chip->writebuf[chip->writebuf_last].reg = reg | 0x200; + chip->writebuf[chip->writebuf_last].data = v; + time1 = chip->writebuf_lasttime + OPL_WRITEBUF_DELAY; + time2 = chip->writebuf_samplecnt; + + if (time1 < time2) + { + time1 = time2; + } + + chip->writebuf[chip->writebuf_last].time = time1; + chip->writebuf_lasttime = time1; + chip->writebuf_last = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE; +} + +void OPL3v17_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples) +{ + Bit32u i; + + for(i = 0; i < numsamples; i++) + { + OPL3v17_GenerateResampled(chip, sndptr); + sndptr += 2; + } +} + +#define OPL3_MIN(A, B) (((A) > (B)) ? (B) : (A)) +#define OPL3_MAX(A, B) (((A) < (B)) ? (B) : (A)) +#define OPL3_CLAMP(V, MIN, MAX) OPL3_MAX(OPL3_MIN(V, MAX), MIN) + +void OPL3v17_GenerateStreamMix(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples) +{ + Bit32u i; + Bit16s sample[2]; + Bit32s mix[2]; + + for(i = 0; i < numsamples; i++) + { + OPL3v17_GenerateResampled(chip, sample); + mix[0] = sndptr[0] + sample[0]; + mix[1] = sndptr[1] + sample[1]; + sndptr[0] = OPL3_CLAMP(mix[0], INT16_MIN, INT16_MAX); + sndptr[1] = OPL3_CLAMP(mix[1], INT16_MIN, INT16_MAX); + sndptr += 2; + } +} + diff --git a/engine/src/Libraries/adlmidi/chips/nuked/nukedopl3_174.h b/engine/src/Libraries/adlmidi/chips/nuked/nukedopl3_174.h new file mode 100644 index 0000000..240802f --- /dev/null +++ b/engine/src/Libraries/adlmidi/chips/nuked/nukedopl3_174.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2013-2016 Alexey Khokholov (Nuke.YKT) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Nuked OPL3 emulator. + * Thanks: + * MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + * Feedback and Rhythm part calculation information. + * forums.submarine.org.uk(carbon14, opl3): + * Tremolo and phase generator calculation information. + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * + * version: 1.7.4 + */ + +#ifndef OPL_OPL3_H +#define OPL_OPL3_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#define OPL_WRITEBUF_SIZE 1024 +#define OPL_WRITEBUF_DELAY 2 + +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef uint64_t Bit64u; +typedef int64_t Bit64s; +typedef uint32_t Bit32u; +typedef int32_t Bit32s; +typedef uint16_t Bit16u; +typedef int16_t Bit16s; +typedef uint8_t Bit8u; +typedef int8_t Bit8s; + +typedef struct _opl3_slot opl3_slot; +typedef struct _opl3_channel opl3_channel; +typedef struct _opl3_chip opl3_chip; + +struct _opl3_slot { + opl3_channel *channel; + opl3_chip *chip; + Bit16s out; + Bit16s fbmod; + Bit16s *mod; + Bit16s prout; + Bit16s eg_rout; + Bit16s eg_out; + Bit8u eg_inc; + Bit8u eg_gen; + Bit8u eg_rate; + Bit8u eg_ksl; + Bit8u *trem; + Bit8u reg_vib; + Bit8u reg_type; + Bit8u reg_ksr; + Bit8u reg_mult; + Bit8u reg_ksl; + Bit8u reg_tl; + Bit8u reg_ar; + Bit8u reg_dr; + Bit8u reg_sl; + Bit8u reg_rr; + Bit8u reg_wf; + Bit8u key; + Bit32u pg_phase; + Bit32u timer; + + Bit16u maskzero; + Bit8u signpos; + Bit8u phaseshift; +}; + +struct _opl3_channel { + opl3_slot *slotz[2];/*Don't use "slots" keyword to avoid conflict with Qt applications*/ + opl3_channel *pair; + opl3_chip *chip; + Bit16s *out[4]; + Bit8u chtype; + Bit16u f_num; + Bit8u block; + Bit8u fb; + Bit8u con; + Bit8u alg; + Bit8u ksv; + Bit16u cha, chb; +}; + +typedef struct _opl3_writebuf { + Bit64u time; + Bit16u reg; + Bit8u data; +} opl3_writebuf; + +struct _opl3_chip { + opl3_channel channel[18]; + opl3_slot chipslot[36]; + Bit16u timer; + Bit8u newm; + Bit8u nts; + Bit8u rhy; + Bit8u vibpos; + Bit8u vibshift; + Bit8u tremolo; + Bit8u tremolopos; + Bit8u tremoloshift; + Bit32u noise; + Bit16s zeromod; + Bit32s mixbuff[2]; + /* OPL3L */ + Bit32s rateratio; + Bit32s samplecnt; + Bit16s oldsamples[2]; + Bit16s samples[2]; + + Bit64u writebuf_samplecnt; + Bit32u writebuf_cur; + Bit32u writebuf_last; + Bit64u writebuf_lasttime; + opl3_writebuf writebuf[OPL_WRITEBUF_SIZE]; +}; + +void OPL3v17_Generate(opl3_chip *chip, Bit16s *buf); +void OPL3v17_GenerateResampled(opl3_chip *chip, Bit16s *buf); +void OPL3v17_Reset(opl3_chip *chip, Bit32u samplerate); +void OPL3v17_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v); +void OPL3v17_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v); +void OPL3v17_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples); +void OPL3v17_GenerateStreamMix(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/engine/src/Libraries/adlmidi/chips/nuked_opl3.cpp b/engine/src/Libraries/adlmidi/chips/nuked_opl3.cpp new file mode 100644 index 0000000..e4f9764 --- /dev/null +++ b/engine/src/Libraries/adlmidi/chips/nuked_opl3.cpp @@ -0,0 +1,69 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nuked_opl3.h" +#include "nuked/nukedopl3.h" +#include + +NukedOPL3::NukedOPL3() : + OPLChipBaseT() +{ + m_chip = new opl3_chip; + setRate(m_rate); +} + +NukedOPL3::~NukedOPL3() +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + delete chip_r; +} + +void NukedOPL3::setRate(uint32_t rate) +{ + OPLChipBaseT::setRate(rate); + opl3_chip *chip_r = reinterpret_cast(m_chip); + std::memset(chip_r, 0, sizeof(opl3_chip)); + OPL3_Reset(chip_r, rate); +} + +void NukedOPL3::reset() +{ + OPLChipBaseT::reset(); + opl3_chip *chip_r = reinterpret_cast(m_chip); + std::memset(chip_r, 0, sizeof(opl3_chip)); + OPL3_Reset(chip_r, m_rate); +} + +void NukedOPL3::writeReg(uint16_t addr, uint8_t data) +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + OPL3_WriteRegBuffered(chip_r, addr, data); +} + +void NukedOPL3::nativeGenerate(int16_t *frame) +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + OPL3_Generate(chip_r, frame); +} + +const char *NukedOPL3::emulatorName() +{ + return "Nuked OPL3 (v 1.8)"; +} diff --git a/engine/src/Libraries/adlmidi/chips/nuked_opl3.h b/engine/src/Libraries/adlmidi/chips/nuked_opl3.h new file mode 100644 index 0000000..4b14b8b --- /dev/null +++ b/engine/src/Libraries/adlmidi/chips/nuked_opl3.h @@ -0,0 +1,43 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef NUKED_OPL3_H +#define NUKED_OPL3_H + +#include "opl_chip_base.h" + +class NukedOPL3 final : public OPLChipBaseT +{ + void *m_chip; +public: + NukedOPL3(); + ~NukedOPL3() override; + + bool canRunAtPcmRate() const override { return false; } + void setRate(uint32_t rate) override; + void reset() override; + void writeReg(uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; +}; + +#endif // NUKED_OPL3_H diff --git a/engine/src/Libraries/adlmidi/chips/nuked_opl3_v174.cpp b/engine/src/Libraries/adlmidi/chips/nuked_opl3_v174.cpp new file mode 100644 index 0000000..793af93 --- /dev/null +++ b/engine/src/Libraries/adlmidi/chips/nuked_opl3_v174.cpp @@ -0,0 +1,69 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nuked_opl3_v174.h" +#include "nuked/nukedopl3_174.h" +#include + +NukedOPL3v174::NukedOPL3v174() : + OPLChipBaseT() +{ + m_chip = new opl3_chip; + setRate(m_rate); +} + +NukedOPL3v174::~NukedOPL3v174() +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + delete chip_r; +} + +void NukedOPL3v174::setRate(uint32_t rate) +{ + OPLChipBaseT::setRate(rate); + opl3_chip *chip_r = reinterpret_cast(m_chip); + std::memset(chip_r, 0, sizeof(opl3_chip)); + OPL3v17_Reset(chip_r, rate); +} + +void NukedOPL3v174::reset() +{ + OPLChipBaseT::reset(); + opl3_chip *chip_r = reinterpret_cast(m_chip); + std::memset(chip_r, 0, sizeof(opl3_chip)); + OPL3v17_Reset(chip_r, m_rate); +} + +void NukedOPL3v174::writeReg(uint16_t addr, uint8_t data) +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + OPL3v17_WriteReg(chip_r, addr, data); +} + +void NukedOPL3v174::nativeGenerate(int16_t *frame) +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + OPL3v17_Generate(chip_r, frame); +} + +const char *NukedOPL3v174::emulatorName() +{ + return "Nuked OPL3 (v 1.7.4)"; +} diff --git a/engine/src/Libraries/adlmidi/chips/nuked_opl3_v174.h b/engine/src/Libraries/adlmidi/chips/nuked_opl3_v174.h new file mode 100644 index 0000000..9463a02 --- /dev/null +++ b/engine/src/Libraries/adlmidi/chips/nuked_opl3_v174.h @@ -0,0 +1,43 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef NUKED_OPL3174_H +#define NUKED_OPL3174_H + +#include "opl_chip_base.h" + +class NukedOPL3v174 final : public OPLChipBaseT +{ + void *m_chip; +public: + NukedOPL3v174(); + ~NukedOPL3v174() override; + + bool canRunAtPcmRate() const override { return false; } + void setRate(uint32_t rate) override; + void reset() override; + void writeReg(uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; +}; + +#endif // NUKED_OPL3174_H diff --git a/engine/src/Libraries/adlmidi/chips/opl_chip_base.h b/engine/src/Libraries/adlmidi/chips/opl_chip_base.h new file mode 100644 index 0000000..8025d03 --- /dev/null +++ b/engine/src/Libraries/adlmidi/chips/opl_chip_base.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ONP_CHIP_BASE_H +#define ONP_CHIP_BASE_H + +#include +#include + +#if !defined(_MSC_VER) && (__cplusplus <= 199711L) +#define final +#define override +#endif + +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) +class VResampler; +#endif + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) +extern void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate); +#endif + +class OPLChipBase +{ +public: + enum { nativeRate = 49716 }; +protected: + uint32_t m_id; + uint32_t m_rate; +public: + OPLChipBase(); + virtual ~OPLChipBase(); + + uint32_t chipId() const { return m_id; } + void setChipId(uint32_t id) { m_id = id; } + + virtual bool canRunAtPcmRate() const = 0; + virtual bool isRunningAtPcmRate() const = 0; + virtual bool setRunningAtPcmRate(bool r) = 0; +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + virtual void setAudioTickHandlerInstance(void *instance) = 0; +#endif + + virtual void setRate(uint32_t rate) = 0; + virtual uint32_t effectiveRate() const = 0; + virtual void reset() = 0; + virtual void writeReg(uint16_t addr, uint8_t data) = 0; + + virtual void nativePreGenerate() = 0; + virtual void nativePostGenerate() = 0; + virtual void nativeGenerate(int16_t *frame) = 0; + + virtual void generate(int16_t *output, size_t frames) = 0; + virtual void generateAndMix(int16_t *output, size_t frames) = 0; + virtual void generate32(int32_t *output, size_t frames) = 0; + virtual void generateAndMix32(int32_t *output, size_t frames) = 0; + + virtual const char* emulatorName() = 0; +private: + OPLChipBase(const OPLChipBase &c); + OPLChipBase &operator=(const OPLChipBase &c); +}; + +// A base class providing F-bounded generic and efficient implementations, +// supporting resampling of chip outputs +template +class OPLChipBaseT : public OPLChipBase +{ +public: + OPLChipBaseT(); + virtual ~OPLChipBaseT(); + + bool isRunningAtPcmRate() const override; + bool setRunningAtPcmRate(bool r) override; +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + void setAudioTickHandlerInstance(void *instance); +#endif + + virtual void setRate(uint32_t rate) override; + uint32_t effectiveRate() const override; + virtual void reset() override; + void generate(int16_t *output, size_t frames) override; + void generateAndMix(int16_t *output, size_t frames) override; + void generate32(int32_t *output, size_t frames) override; + void generateAndMix32(int32_t *output, size_t frames) override; +private: + bool m_runningAtPcmRate; +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + void *m_audioTickHandlerInstance; +#endif + void nativeTick(int16_t *frame); + void setupResampler(uint32_t rate); + void resetResampler(); + void resampledGenerate(int32_t *output); +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) + VResampler *m_resampler; +#else + int32_t m_oldsamples[2]; + int32_t m_samples[2]; + int32_t m_samplecnt; + int32_t m_rateratio; + enum { rsm_frac = 10 }; +#endif + // amplitude scale factors in and out of resampler, varying for chips; + // values are OK to "redefine", the static polymorphism will accept it. + enum { resamplerPreAmplify = 1, resamplerPostAttenuate = 1 }; +}; + +// A base class which provides frame-by-frame interfaces on emulations which +// don't have a routine for it. It produces outputs in fixed size buffers. +// Fast register updates will suffer some latency because of buffering. +template +class OPLChipBaseBufferedT : public OPLChipBaseT +{ +public: + OPLChipBaseBufferedT() + : OPLChipBaseT(), m_bufferIndex(0) {} + virtual ~OPLChipBaseBufferedT() + {} +public: + void reset() override; + void nativeGenerate(int16_t *frame) override; +protected: + virtual void nativeGenerateN(int16_t *output, size_t frames) = 0; +private: + unsigned m_bufferIndex; + int16_t m_buffer[2 * Buffer]; +}; + +#include "opl_chip_base.tcc" + +#endif // ONP_CHIP_BASE_H diff --git a/engine/src/Libraries/adlmidi/chips/opl_chip_base.tcc b/engine/src/Libraries/adlmidi/chips/opl_chip_base.tcc new file mode 100644 index 0000000..a64aa7c --- /dev/null +++ b/engine/src/Libraries/adlmidi/chips/opl_chip_base.tcc @@ -0,0 +1,294 @@ +#include "opl_chip_base.h" +#include + +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) +#include +#endif + +#if !defined(LIKELY) && defined(__GNUC__) +#define LIKELY(x) __builtin_expect((x), 1) +#elif !defined(LIKELY) +#define LIKELY(x) (x) +#endif + +#if !defined(UNLIKELY) && defined(__GNUC__) +#define UNLIKELY(x) __builtin_expect((x), 0) +#elif !defined(UNLIKELY) +#define UNLIKELY(x) (x) +#endif + +/* OPLChipBase */ + +inline OPLChipBase::OPLChipBase() : + m_id(0), + m_rate(44100) +{ +} + +inline OPLChipBase::~OPLChipBase() +{ +} + +/* OPLChipBaseT */ + +template +OPLChipBaseT::OPLChipBaseT() + : OPLChipBase(), + m_runningAtPcmRate(false) +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + , + m_audioTickHandlerInstance(NULL) +#endif +{ +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) + m_resampler = new VResampler; +#endif + setupResampler(m_rate); +} + +template +OPLChipBaseT::~OPLChipBaseT() +{ +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) + delete m_resampler; +#endif +} + +template +bool OPLChipBaseT::isRunningAtPcmRate() const +{ + return m_runningAtPcmRate; +} + +template +bool OPLChipBaseT::setRunningAtPcmRate(bool r) +{ + if(r != m_runningAtPcmRate) + { + if(r && !static_cast(this)->canRunAtPcmRate()) + return false; + m_runningAtPcmRate = r; + static_cast(this)->setRate(m_rate); + } + return true; +} + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) +template +void OPLChipBaseT::setAudioTickHandlerInstance(void *instance) +{ + m_audioTickHandlerInstance = instance; +} +#endif + +template +void OPLChipBaseT::setRate(uint32_t rate) +{ + uint32_t oldRate = m_rate; + m_rate = rate; + if(rate != oldRate) + setupResampler(rate); + else + resetResampler(); +} + +template +uint32_t OPLChipBaseT::effectiveRate() const +{ + return m_runningAtPcmRate ? m_rate : (uint32_t)nativeRate; +} + +template +void OPLChipBaseT::reset() +{ + resetResampler(); +} + +template +void OPLChipBaseT::generate(int16_t *output, size_t frames) +{ + static_cast(this)->nativePreGenerate(); + for(size_t i = 0; i < frames; ++i) + { + int32_t frame[2]; + static_cast(this)->resampledGenerate(frame); + for (unsigned c = 0; c < 2; ++c) { + int32_t temp = frame[c]; + temp = (temp > -32768) ? temp : -32768; + temp = (temp < 32767) ? temp : 32767; + output[c] = (int16_t)temp; + } + output += 2; + } + static_cast(this)->nativePostGenerate(); +} + +template +void OPLChipBaseT::generateAndMix(int16_t *output, size_t frames) +{ + static_cast(this)->nativePreGenerate(); + for(size_t i = 0; i < frames; ++i) + { + int32_t frame[2]; + static_cast(this)->resampledGenerate(frame); + for (unsigned c = 0; c < 2; ++c) { + int32_t temp = (int32_t)output[c] + frame[c]; + temp = (temp > -32768) ? temp : -32768; + temp = (temp < 32767) ? temp : 32767; + output[c] = (int16_t)temp; + } + output += 2; + } + static_cast(this)->nativePostGenerate(); +} + +template +void OPLChipBaseT::generate32(int32_t *output, size_t frames) +{ + static_cast(this)->nativePreGenerate(); + for(size_t i = 0; i < frames; ++i) + { + static_cast(this)->resampledGenerate(output); + output += 2; + } + static_cast(this)->nativePostGenerate(); +} + +template +void OPLChipBaseT::generateAndMix32(int32_t *output, size_t frames) +{ + static_cast(this)->nativePreGenerate(); + for(size_t i = 0; i < frames; ++i) + { + int32_t frame[2]; + static_cast(this)->resampledGenerate(frame); + output[0] += frame[0]; + output[1] += frame[1]; + output += 2; + } + static_cast(this)->nativePostGenerate(); +} + +template +void OPLChipBaseT::nativeTick(int16_t *frame) +{ +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + adl_audioTickHandler(m_audioTickHandlerInstance, m_id, effectiveRate()); +#endif + static_cast(this)->nativeGenerate(frame); +} + +template +void OPLChipBaseT::setupResampler(uint32_t rate) +{ +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) + m_resampler->setup(rate * (1.0 / 49716), 2, 48); +#else + m_oldsamples[0] = m_oldsamples[1] = 0; + m_samples[0] = m_samples[1] = 0; + m_samplecnt = 0; + m_rateratio = (int32_t)((rate << rsm_frac) / 49716); +#endif +} + +template +void OPLChipBaseT::resetResampler() +{ +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) + m_resampler->reset(); +#else + m_oldsamples[0] = m_oldsamples[1] = 0; + m_samples[0] = m_samples[1] = 0; + m_samplecnt = 0; +#endif +} + +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) +template +void OPLChipBaseT::resampledGenerate(int32_t *output) +{ + if(UNLIKELY(m_runningAtPcmRate)) + { + int16_t in[2]; + static_cast(this)->nativeTick(in); + output[0] = (int32_t)in[0] * T::resamplerPreAmplify / T::resamplerPostAttenuate; + output[1] = (int32_t)in[1] * T::resamplerPreAmplify / T::resamplerPostAttenuate; + return; + } + + VResampler *rsm = m_resampler; + float scale = (float)T::resamplerPreAmplify / + (float)T::resamplerPostAttenuate; + float f_in[2]; + float f_out[2]; + rsm->inp_count = 0; + rsm->inp_data = f_in; + rsm->out_count = 1; + rsm->out_data = f_out; + while(rsm->process(), rsm->out_count != 0) + { + int16_t in[2]; + static_cast(this)->nativeTick(in); + f_in[0] = scale * (float)in[0]; + f_in[1] = scale * (float)in[1]; + rsm->inp_count = 1; + rsm->inp_data = f_in; + rsm->out_count = 1; + rsm->out_data = f_out; + } + output[0] = static_cast(std::lround(f_out[0])); + output[1] = static_cast(std::lround(f_out[1])); +} +#else +template +void OPLChipBaseT::resampledGenerate(int32_t *output) +{ + if(UNLIKELY(m_runningAtPcmRate)) + { + int16_t in[2]; + static_cast(this)->nativeTick(in); + output[0] = (int32_t)in[0] * T::resamplerPreAmplify / T::resamplerPostAttenuate; + output[1] = (int32_t)in[1] * T::resamplerPreAmplify / T::resamplerPostAttenuate; + return; + } + + int32_t samplecnt = m_samplecnt; + const int32_t rateratio = m_rateratio; + while(samplecnt >= rateratio) + { + m_oldsamples[0] = m_samples[0]; + m_oldsamples[1] = m_samples[1]; + int16_t buffer[2]; + static_cast(this)->nativeTick(buffer); + m_samples[0] = buffer[0] * T::resamplerPreAmplify; + m_samples[1] = buffer[1] * T::resamplerPreAmplify; + samplecnt -= rateratio; + } + output[0] = (int32_t)(((m_oldsamples[0] * (rateratio - samplecnt) + + m_samples[0] * samplecnt) / rateratio)/T::resamplerPostAttenuate); + output[1] = (int32_t)(((m_oldsamples[1] * (rateratio - samplecnt) + + m_samples[1] * samplecnt) / rateratio)/T::resamplerPostAttenuate); + m_samplecnt = samplecnt + (1 << rsm_frac); +} +#endif + +/* OPLChipBaseBufferedT */ + +template +void OPLChipBaseBufferedT::reset() +{ + OPLChipBaseT::reset(); + m_bufferIndex = 0; +} + +template +void OPLChipBaseBufferedT::nativeGenerate(int16_t *frame) +{ + unsigned bufferIndex = m_bufferIndex; + if(bufferIndex == 0) + static_cast(this)->nativeGenerateN(m_buffer, Buffer); + frame[0] = m_buffer[2 * bufferIndex]; + frame[1] = m_buffer[2 * bufferIndex + 1]; + bufferIndex = (bufferIndex + 1 < Buffer) ? (bufferIndex + 1) : 0; + m_bufferIndex = bufferIndex; +} diff --git a/engine/src/Libraries/adlmidi/cvt_mus2mid.hpp b/engine/src/Libraries/adlmidi/cvt_mus2mid.hpp new file mode 100644 index 0000000..b5096c6 --- /dev/null +++ b/engine/src/Libraries/adlmidi/cvt_mus2mid.hpp @@ -0,0 +1,461 @@ +/* + * MUS2MIDI: MUS to MIDI Library + * + * Copyright (C) 2014 Bret Curtis + * Copyright (C) WildMIDI Developers 2015-2016 + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +#ifdef __DJGPP__ +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed long int32_t; +typedef unsigned long uint32_t; +#endif + +#define MUS_FREQUENCY 140 /* default Hz or BPM */ + +#if 0 /* older units: */ +#define MUS_TEMPO 0x001aa309 /* MPQN: 60000000 / 34.37Hz = 1745673 */ +#define MUS_DIVISION 0x0059 /* 89 -- used by many mus2midi converters */ +#endif + +#define MUS_TEMPO 0x00068A1B /* MPQN: 60000000 / 140BPM (140Hz) = 428571 */ + /* 0x000D1436 -> MPQN: 60000000 / 70BPM (70Hz) = 857142 */ + +#define MUS_DIVISION 0x0101 /* 257 for 140Hz files with a 140MPQN */ + /* 0x0088 -> 136 for 70Hz files with a 140MPQN */ + /* 0x010B -> 267 for 70hz files with a 70MPQN */ + /* 0x01F9 -> 505 for 140hz files with a 70MPQN */ + +/* New + * QLS: MPQN/1000000 = 0.428571 + * TDPS: QLS/PPQN = 0.428571/136 = 0.003151257 + * PPQN: 136 + * + * QLS: MPQN/1000000 = 0.428571 + * TDPS: QLS/PPQN = 0.428571/257 = 0.001667591 + * PPQN: 257 + * + * QLS: MPQN/1000000 = 0.857142 + * TDPS: QLS/PPQN = 0.857142/267 = 0.00321027 + * PPQN: 267 + * + * QLS: MPQN/1000000 = 0.857142 + * TDPS: QLS/PPQN = 0.857142/505 = 0.001697311 + * PPQN: 505 + * + * Old + * QLS: MPQN/1000000 = 1.745673 + * TDPS: QLS/PPQN = 1.745673 / 89 = 0.019614303 (seconds per tick) + * PPQN: (TDPS = QLS/PPQN) (0.019614303 = 1.745673/PPQN) (0.019614303*PPQN = 1.745673) (PPQN = 89.000001682) + * + */ + +#define MUSEVENT_KEYOFF 0 +#define MUSEVENT_KEYON 1 +#define MUSEVENT_PITCHWHEEL 2 +#define MUSEVENT_CHANNELMODE 3 +#define MUSEVENT_CONTROLLERCHANGE 4 +#define MUSEVENT_END 6 + +#define MUS_MIDI_MAXCHANNELS 16 + +static char MUS_ID[] = { 'M', 'U', 'S', 0x1A }; + +static uint8_t mus_midimap[] = +{/* MIDI Number Description */ + 0, /* 0 program change */ + 0, /* 1 bank selection */ + 0x01, /* 2 Modulation pot (frequency vibrato depth) */ + 0x07, /* 3 Volume: 0-silent, ~100-normal, 127-loud */ + 0x0A, /* 4 Pan (balance) pot: 0-left, 64-center (default), 127-right */ + 0x0B, /* 5 Expression pot */ + 0x5B, /* 6 Reverb depth */ + 0x5D, /* 7 Chorus depth */ + 0x40, /* 8 Sustain pedal */ + 0x43, /* 9 Soft pedal */ + 0x78, /* 10 All sounds off */ + 0x7B, /* 11 All notes off */ + 0x7E, /* 12 Mono (use numchannels + 1) */ + 0x7F, /* 13 Poly */ + 0x79, /* 14 reset all controllers */ +}; + +typedef struct MUSHeader { + char ID[4]; /* identifier: "MUS" 0x1A */ + uint16_t scoreLen; + uint16_t scoreStart; + uint16_t channels; /* count of primary channels */ + uint16_t sec_channels; /* count of secondary channels */ + uint16_t instrCnt; +} MUSHeader ; +#define MUS_HEADERSIZE 14 + +typedef struct MidiHeaderChunk { + char name[4]; + int32_t length; + int16_t format; /* make 0 */ + int16_t ntracks;/* make 1 */ + int16_t division; /* 0xe250 ?? */ +} MidiHeaderChunk; +#define MIDI_HEADERSIZE 14 + +typedef struct MidiTrackChunk { + char name[4]; + int32_t length; +} MidiTrackChunk; +#define TRK_CHUNKSIZE 8 + +struct mus_ctx { + uint8_t *src, *src_ptr; + uint32_t srcsize; + uint32_t datastart; + uint8_t *dst, *dst_ptr; + uint32_t dstsize, dstrem; +}; + +#define DST_CHUNK 8192 +static void mus2mid_resize_dst(struct mus_ctx *ctx) { + uint32_t pos = (uint32_t)(ctx->dst_ptr - ctx->dst); + ctx->dst = (uint8_t *)realloc(ctx->dst, ctx->dstsize + DST_CHUNK); + ctx->dstsize += DST_CHUNK; + ctx->dstrem += DST_CHUNK; + ctx->dst_ptr = ctx->dst + pos; +} + +static void mus2mid_write1(struct mus_ctx *ctx, uint32_t val) +{ + if (ctx->dstrem < 1) + mus2mid_resize_dst(ctx); + *ctx->dst_ptr++ = val & 0xff; + ctx->dstrem--; +} + +static void mus2mid_write2(struct mus_ctx *ctx, uint32_t val) +{ + if (ctx->dstrem < 2) + mus2mid_resize_dst(ctx); + *ctx->dst_ptr++ = (val>>8) & 0xff; + *ctx->dst_ptr++ = val & 0xff; + ctx->dstrem -= 2; +} + +static void mus2mid_write4(struct mus_ctx *ctx, uint32_t val) +{ + if (ctx->dstrem < 4) + mus2mid_resize_dst(ctx); + *ctx->dst_ptr++ = (uint8_t)((val>>24)&0xff); + *ctx->dst_ptr++ = (uint8_t)((val>>16)&0xff); + *ctx->dst_ptr++ = (uint8_t)((val>>8) & 0xff); + *ctx->dst_ptr++ = (uint8_t)((val & 0xff)); + ctx->dstrem -= 4; +} + +static void mus2mid_seekdst(struct mus_ctx *ctx, uint32_t pos) { + ctx->dst_ptr = ctx->dst + pos; + while (ctx->dstsize < pos) + mus2mid_resize_dst(ctx); + ctx->dstrem = ctx->dstsize - pos; +} + +static void mus2mid_skipdst(struct mus_ctx *ctx, int32_t pos) { + size_t newpos; + ctx->dst_ptr += pos; + newpos = ctx->dst_ptr - ctx->dst; + while (ctx->dstsize < newpos) + mus2mid_resize_dst(ctx); + ctx->dstrem = (uint32_t)(ctx->dstsize - newpos); +} + +static uint32_t mus2mid_getdstpos(struct mus_ctx *ctx) { + return (uint32_t)(ctx->dst_ptr - ctx->dst); +} + +/* writes a variable length integer to a buffer, and returns bytes written */ +static int32_t mus2mid_writevarlen(int32_t value, uint8_t *out) +{ + int32_t buffer, count = 0; + + buffer = value & 0x7f; + while ((value >>= 7) > 0) { + buffer <<= 8; + buffer += 0x80; + buffer += (value & 0x7f); + } + + while (1) { + ++count; + *out = (uint8_t)buffer; + ++out; + if (buffer & 0x80) + buffer >>= 8; + else + break; + } + return (count); +} + +#define MUS_READ_INT16(b) ((b)[0] | ((b)[1] << 8)) +#define MUS_READ_INT32(b) ((b)[0] | ((b)[1] << 8) | ((b)[2] << 16) | ((b)[3] << 24)) + +static int Convert_mus2midi(uint8_t *in, uint32_t insize, + uint8_t **out, uint32_t *outsize, + uint16_t frequency) +{ + struct mus_ctx ctx; + MUSHeader header; + uint8_t *cur, *end; + uint32_t track_size_pos, begin_track_pos, current_pos; + int32_t delta_time;/* Delta time for midi event */ + int temp, ret = -1; + int channel_volume[MUS_MIDI_MAXCHANNELS]; + int channelMap[MUS_MIDI_MAXCHANNELS], currentChannel; + + if (insize < MUS_HEADERSIZE) { + /*_WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0);*/ + return (-1); + } + + if (!frequency) + frequency = MUS_FREQUENCY; + + /* read the MUS header and set our location */ + memcpy(header.ID, in, 4); + header.scoreLen = MUS_READ_INT16(&in[4]); + header.scoreStart = MUS_READ_INT16(&in[6]); + header.channels = MUS_READ_INT16(&in[8]); + header.sec_channels = MUS_READ_INT16(&in[10]); + header.instrCnt = MUS_READ_INT16(&in[12]); + + if (memcmp(header.ID, MUS_ID, 4)) { + /*_WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_MUS, NULL, 0);*/ + return (-1); + } + if (insize < (uint32_t)header.scoreLen + (uint32_t)header.scoreStart) { + /*_WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0);*/ + return (-1); + } + /* channel #15 should be excluded in the numchannels field: */ + if (header.channels > MUS_MIDI_MAXCHANNELS - 1) { + /*_WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, NULL, 0);*/ + return (-1); + } + + memset(&ctx, 0, sizeof(struct mus_ctx)); + ctx.src = ctx.src_ptr = in; + ctx.srcsize = insize; + + ctx.dst = (uint8_t*)calloc(DST_CHUNK, sizeof(uint8_t)); + ctx.dst_ptr = ctx.dst; + ctx.dstsize = DST_CHUNK; + ctx.dstrem = DST_CHUNK; + + /* Map channel 15 to 9 (percussions) */ + for (temp = 0; temp < MUS_MIDI_MAXCHANNELS; ++temp) { + channelMap[temp] = -1; + channel_volume[temp] = 0x40; + } + channelMap[15] = 9; + + /* Header is 14 bytes long and add the rest as well */ + mus2mid_write1(&ctx, 'M'); + mus2mid_write1(&ctx, 'T'); + mus2mid_write1(&ctx, 'h'); + mus2mid_write1(&ctx, 'd'); + mus2mid_write4(&ctx, 6); /* length of header */ + mus2mid_write2(&ctx, 0); /* MIDI type (always 0) */ + mus2mid_write2(&ctx, 1); /* MUS files only have 1 track */ + mus2mid_write2(&ctx, MUS_DIVISION); /* division */ + + /* Write out track header and track length position for later */ + begin_track_pos = mus2mid_getdstpos(&ctx); + mus2mid_write1(&ctx, 'M'); + mus2mid_write1(&ctx, 'T'); + mus2mid_write1(&ctx, 'r'); + mus2mid_write1(&ctx, 'k'); + track_size_pos = mus2mid_getdstpos(&ctx); + mus2mid_skipdst(&ctx, 4); + + /* write tempo: microseconds per quarter note */ + mus2mid_write1(&ctx, 0x00); /* delta time */ + mus2mid_write1(&ctx, 0xff); /* sys command */ + mus2mid_write2(&ctx, 0x5103); /* command - set tempo */ + mus2mid_write1(&ctx, MUS_TEMPO & 0x000000ff); + mus2mid_write1(&ctx, (MUS_TEMPO & 0x0000ff00) >> 8); + mus2mid_write1(&ctx, (MUS_TEMPO & 0x00ff0000) >> 16); + + /* Percussions channel starts out at full volume */ + mus2mid_write1(&ctx, 0x00); + mus2mid_write1(&ctx, 0xB9); + mus2mid_write1(&ctx, 0x07); + mus2mid_write1(&ctx, 127); + + /* get current position in source, and end of position */ + cur = in + header.scoreStart; + end = cur + header.scoreLen; + + currentChannel = 0; + delta_time = 0; + + /* main loop */ + while(cur < end){ + /*printf("LOOP DEBUG: %d\r\n",iterator++);*/ + uint8_t channel; + uint8_t event; + uint8_t temp_buffer[32]; /* temp buffer for current iterator */ + uint8_t *out_local = temp_buffer; + uint8_t status, bit1, bit2, bitc = 2; + + /* read in current bit */ + event = *cur++; + channel = (event & 15); /* current channel */ + + /* write variable length delta time */ + out_local += mus2mid_writevarlen(delta_time, out_local); + + /* set all channels to 127 (max) volume */ + if (channelMap[channel] < 0) { + *out_local++ = 0xB0 + currentChannel; + *out_local++ = 0x07; + *out_local++ = 127; + *out_local++ = 0x00; + channelMap[channel] = currentChannel++; + if (currentChannel == 9) + ++currentChannel; + } + status = channelMap[channel]; + + /* handle events */ + switch ((event & 122) >> 4){ + case MUSEVENT_KEYOFF: + status |= 0x80; + bit1 = *cur++; + bit2 = 0x40; + break; + case MUSEVENT_KEYON: + status |= 0x90; + bit1 = *cur & 127; + if (*cur++ & 128) /* volume bit? */ + channel_volume[channelMap[channel]] = *cur++; + bit2 = channel_volume[channelMap[channel]]; + break; + case MUSEVENT_PITCHWHEEL: + status |= 0xE0; + bit1 = (*cur & 1) >> 6; + bit2 = (*cur++ >> 1) & 127; + break; + case MUSEVENT_CHANNELMODE: + status |= 0xB0; + if (*cur >= sizeof(mus_midimap) / sizeof(mus_midimap[0])) { + /*_WM_ERROR_NEW("%s:%i: can't map %u to midi", + __FUNCTION__, __LINE__, *cur);*/ + goto _end; + } + bit1 = mus_midimap[*cur++]; + bit2 = (*cur++ == 12) ? header.channels + 1 : 0x00; + break; + case MUSEVENT_CONTROLLERCHANGE: + if (*cur == 0) { + cur++; + status |= 0xC0; + bit1 = *cur++; + bit2 = 0;/* silence bogus warnings */ + bitc = 1; + } else { + status |= 0xB0; + if (*cur >= sizeof(mus_midimap) / sizeof(mus_midimap[0])) { + /*_WM_ERROR_NEW("%s:%i: can't map %u to midi", + __FUNCTION__, __LINE__, *cur);*/ + goto _end; + } + bit1 = mus_midimap[*cur++]; + bit2 = *cur++; + } + break; + case MUSEVENT_END: /* End */ + status = 0xff; + bit1 = 0x2f; + bit2 = 0x00; + if (cur != end) { /* should we error here or report-only? */ + /*_WM_DEBUG_MSG("%s:%i: MUS buffer off by %ld bytes", + __FUNCTION__, __LINE__, (long)(cur - end));*/ + } + break; + case 5:/* Unknown */ + case 7:/* Unknown */ + default:/* shouldn't happen */ + /*_WM_ERROR_NEW("%s:%i: unrecognized event (%u)", + __FUNCTION__, __LINE__, event);*/ + goto _end; + } + + /* write it out */ + *out_local++ = status; + *out_local++ = bit1; + if (bitc == 2) + *out_local++ = bit2; + + /* write out our temp buffer */ + if (out_local != temp_buffer) + { + if (ctx.dstrem < sizeof(temp_buffer)) + mus2mid_resize_dst(&ctx); + + memcpy(ctx.dst_ptr, temp_buffer, out_local - temp_buffer); + ctx.dst_ptr += out_local - temp_buffer; + ctx.dstrem -= (uint32_t)(out_local - temp_buffer); + } + + if (event & 128) { + delta_time = 0; + do { + delta_time = (int32_t)((delta_time * 128 + (*cur & 127)) * (140.0 / (double)frequency)); + } while ((*cur++ & 128)); + } else { + delta_time = 0; + } + } + + /* write out track length */ + current_pos = mus2mid_getdstpos(&ctx); + mus2mid_seekdst(&ctx, track_size_pos); + mus2mid_write4(&ctx, current_pos - begin_track_pos - TRK_CHUNKSIZE); + mus2mid_seekdst(&ctx, current_pos); /* reseek to end position */ + + *out = ctx.dst; + *outsize = ctx.dstsize - ctx.dstrem; + ret = 0; + +_end: /* cleanup */ + if (ret < 0) { + free(ctx.dst); + *out = NULL; + *outsize = 0; + } + + return (ret); +} + diff --git a/engine/src/Libraries/adlmidi/cvt_xmi2mid.hpp b/engine/src/Libraries/adlmidi/cvt_xmi2mid.hpp new file mode 100644 index 0000000..2320670 --- /dev/null +++ b/engine/src/Libraries/adlmidi/cvt_xmi2mid.hpp @@ -0,0 +1,1234 @@ +/* + * XMIDI: Miles XMIDI to MID Library + * + * Copyright (C) 2001 Ryan Nunn + * Copyright (C) 2014 Bret Curtis + * Copyright (C) WildMIDI Developers 2015-2016 + * Copyright (c) 2015-2018 Vitaly Novichkov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/* XMIDI Converter */ + +#include +#include +#include +#include +#include + +#ifdef __DJGPP__ +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed long int32_t; +typedef unsigned long uint32_t; +#endif + +/* Conversion types for Midi files */ +#define XMIDI_CONVERT_NOCONVERSION 0x00 +#define XMIDI_CONVERT_MT32_TO_GM 0x01 +#define XMIDI_CONVERT_MT32_TO_GS 0x02 +#define XMIDI_CONVERT_MT32_TO_GS127 0x03 /* This one is broken, don't use */ +#define XMIDI_CONVERT_MT32_TO_GS127DRUM 0x04 /* This one is broken, don't use */ +#define XMIDI_CONVERT_GS127_TO_GS 0x05 + +/* Midi Status Bytes */ +#define XMI2MID_MIDI_STATUS_NOTE_OFF 0x8 +#define XMI2MID_MIDI_STATUS_NOTE_ON 0x9 +#define XMI2MID_MIDI_STATUS_AFTERTOUCH 0xA +#define XMI2MID_MIDI_STATUS_CONTROLLER 0xB +#define XMI2MID_MIDI_STATUS_PROG_CHANGE 0xC +#define XMI2MID_MIDI_STATUS_PRESSURE 0xD +#define XMI2MID_MIDI_STATUS_PITCH_WHEEL 0xE +#define XMI2MID_MIDI_STATUS_SYSEX 0xF + +#if 1 +#define XMI2MID_TRACE(...) +#else +#include +#define XMI2MID_TRACE(fmt, ...) \ + fprintf(stderr, "XMI2MID: " fmt "\n", ## __VA_ARGS__) +#endif + +typedef struct _xmi2mid_midi_event { + int32_t time; + uint8_t status; + uint8_t data[2]; + uint32_t len; + uint8_t *buffer; + struct _xmi2mid_midi_event *next; +} midi_event; + +typedef struct { + uint16_t type; + uint16_t tracks; +} midi_descriptor; + +struct xmi2mid_xmi_ctx { + uint8_t *src, *src_ptr; + uint32_t srcsize; + uint32_t datastart; + uint8_t *dst, *dst_ptr; + uint32_t dstsize, dstrem; + uint32_t convert_type; + midi_descriptor info; + int bank127[16]; + midi_event **events; + int16_t *timing; + midi_event *list; + midi_event *current; +}; + +typedef struct { + unsigned count; + uint8_t id[128]; + uint32_t offset[128]; +} xmi2mid_rbrn; + +/* forward declarations of private functions */ +static void xmi2mid_DeleteEventList(midi_event *mlist); +static void xmi2mid_CreateNewEvent(struct xmi2mid_xmi_ctx *ctx, int32_t time); /* List manipulation */ +static int xmi2mid_GetVLQ(struct xmi2mid_xmi_ctx *ctx, uint32_t *quant); /* Variable length quantity */ +static int xmi2mid_GetVLQ2(struct xmi2mid_xmi_ctx *ctx, uint32_t *quant);/* Variable length quantity */ +static int xmi2mid_PutVLQ(struct xmi2mid_xmi_ctx *ctx, uint32_t value); /* Variable length quantity */ +static int xmi2mid_ConvertEvent(struct xmi2mid_xmi_ctx *ctx, + const int32_t time, const uint8_t status, const int size); +static int32_t xmi2mid_ConvertSystemMessage(struct xmi2mid_xmi_ctx *ctx, + const int32_t time, const uint8_t status); +static int32_t xmi2mid_ConvertFiletoList(struct xmi2mid_xmi_ctx *ctx, const xmi2mid_rbrn *rbrn); +static uint32_t xmi2mid_ConvertListToMTrk(struct xmi2mid_xmi_ctx *ctx, midi_event *mlist); +static int xmi2mid_ParseXMI(struct xmi2mid_xmi_ctx *ctx); +static int xmi2mid_ExtractTracks(struct xmi2mid_xmi_ctx *ctx); +static uint32_t xmi2mid_ExtractTracksFromXmi(struct xmi2mid_xmi_ctx *ctx); + +static uint32_t xmi2mid_read1(struct xmi2mid_xmi_ctx *ctx) +{ + uint8_t b0; + b0 = *ctx->src_ptr++; + return (b0); +} + +static uint32_t xmi2mid_read2(struct xmi2mid_xmi_ctx *ctx) +{ + uint8_t b0, b1; + b0 = *ctx->src_ptr++; + b1 = *ctx->src_ptr++; + return (b0 + ((uint32_t)b1 << 8)); +} + +static uint32_t xmi2mid_read4(struct xmi2mid_xmi_ctx *ctx) +{ + uint8_t b0, b1, b2, b3; + b3 = *ctx->src_ptr++; + b2 = *ctx->src_ptr++; + b1 = *ctx->src_ptr++; + b0 = *ctx->src_ptr++; + return (b0 + ((uint32_t)b1<<8) + ((uint32_t)b2<<16) + ((uint32_t)b3<<24)); +} + +static uint32_t xmi2mid_read4le(struct xmi2mid_xmi_ctx *ctx) +{ + uint8_t b0, b1, b2, b3; + b3 = *ctx->src_ptr++; + b2 = *ctx->src_ptr++; + b1 = *ctx->src_ptr++; + b0 = *ctx->src_ptr++; + return (b3 + ((uint32_t)b2<<8) + ((uint32_t)b1<<16) + ((uint32_t)b0<<24)); +} + +static void xmi2mid_copy(struct xmi2mid_xmi_ctx *ctx, char *b, uint32_t len) +{ + memcpy(b, ctx->src_ptr, len); + ctx->src_ptr += len; +} + +#define DST_CHUNK 8192 +static void xmi2mid_resize_dst(struct xmi2mid_xmi_ctx *ctx) { + uint32_t pos = (uint32_t)(ctx->dst_ptr - ctx->dst); + ctx->dst = (uint8_t *)realloc(ctx->dst, ctx->dstsize + DST_CHUNK); + ctx->dstsize += DST_CHUNK; + ctx->dstrem += DST_CHUNK; + ctx->dst_ptr = ctx->dst + pos; +} + +static void xmi2mid_write1(struct xmi2mid_xmi_ctx *ctx, uint32_t val) +{ + if (ctx->dstrem < 1) + xmi2mid_resize_dst(ctx); + *ctx->dst_ptr++ = val & 0xff; + ctx->dstrem--; +} + +static void xmi2mid_write2(struct xmi2mid_xmi_ctx *ctx, uint32_t val) +{ + if (ctx->dstrem < 2) + xmi2mid_resize_dst(ctx); + *ctx->dst_ptr++ = (val>>8) & 0xff; + *ctx->dst_ptr++ = val & 0xff; + ctx->dstrem -= 2; +} + +static void xmi2mid_write4(struct xmi2mid_xmi_ctx *ctx, uint32_t val) +{ + if (ctx->dstrem < 4) + xmi2mid_resize_dst(ctx); + *ctx->dst_ptr++ = (val>>24)&0xff; + *ctx->dst_ptr++ = (val>>16)&0xff; + *ctx->dst_ptr++ = (val>>8) & 0xff; + *ctx->dst_ptr++ = val & 0xff; + ctx->dstrem -= 4; +} + +static void xmi2mid_seeksrc(struct xmi2mid_xmi_ctx *ctx, uint32_t pos) { + ctx->src_ptr = ctx->src + pos; +} + +static void xmi2mid_seekdst(struct xmi2mid_xmi_ctx *ctx, uint32_t pos) { + ctx->dst_ptr = ctx->dst + pos; + while (ctx->dstsize < pos) + xmi2mid_resize_dst(ctx); + ctx->dstrem = ctx->dstsize - pos; +} + +static void xmi2mid_skipsrc(struct xmi2mid_xmi_ctx *ctx, int32_t pos) { + ctx->src_ptr += pos; +} + +static void xmi2mid_skipdst(struct xmi2mid_xmi_ctx *ctx, int32_t pos) { + size_t newpos; + ctx->dst_ptr += pos; + newpos = ctx->dst_ptr - ctx->dst; + while (ctx->dstsize < newpos) + xmi2mid_resize_dst(ctx); + ctx->dstrem = (uint32_t)(ctx->dstsize - newpos); +} + +static uint32_t xmi2mid_getsrcsize(struct xmi2mid_xmi_ctx *ctx) { + return (ctx->srcsize); +} + +static uint32_t xmi2mid_getsrcpos(struct xmi2mid_xmi_ctx *ctx) { + return (uint32_t)(ctx->src_ptr - ctx->src); +} + +static uint32_t xmi2mid_getdstpos(struct xmi2mid_xmi_ctx *ctx) { + return (uint32_t)(ctx->dst_ptr - ctx->dst); +} + +/* This is a default set of patches to convert from MT32 to GM + * The index is the MT32 Patch number and the value is the GM Patch + * This is only suitable for music that doesn't do timbre changes + * XMIDIs that contain Timbre changes will not convert properly. + */ +static const char xmi2mid_mt32asgm[128] = { + 0, /* 0 Piano 1 */ + 1, /* 1 Piano 2 */ + 2, /* 2 Piano 3 (synth) */ + 4, /* 3 EPiano 1 */ + 4, /* 4 EPiano 2 */ + 5, /* 5 EPiano 3 */ + 5, /* 6 EPiano 4 */ + 3, /* 7 Honkytonk */ + 16, /* 8 Organ 1 */ + 17, /* 9 Organ 2 */ + 18, /* 10 Organ 3 */ + 16, /* 11 Organ 4 */ + 19, /* 12 Pipe Organ 1 */ + 19, /* 13 Pipe Organ 2 */ + 19, /* 14 Pipe Organ 3 */ + 21, /* 15 Accordion */ + 6, /* 16 Harpsichord 1 */ + 6, /* 17 Harpsichord 2 */ + 6, /* 18 Harpsichord 3 */ + 7, /* 19 Clavinet 1 */ + 7, /* 20 Clavinet 2 */ + 7, /* 21 Clavinet 3 */ + 8, /* 22 Celesta 1 */ + 8, /* 23 Celesta 2 */ + 62, /* 24 Synthbrass 1 (62) */ + 63, /* 25 Synthbrass 2 (63) */ + 62, /* 26 Synthbrass 3 Bank 8 */ + 63, /* 27 Synthbrass 4 Bank 8 */ + 38, /* 28 Synthbass 1 */ + 39, /* 29 Synthbass 2 */ + 38, /* 30 Synthbass 3 Bank 8 */ + 39, /* 31 Synthbass 4 Bank 8 */ + 88, /* 32 Fantasy */ + 90, /* 33 Harmonic Pan - No equiv closest is polysynth(90) :( */ + 52, /* 34 Choral ?? Currently set to SynthVox(54). Should it be ChoirAhhs(52)??? */ + 92, /* 35 Glass */ + 97, /* 36 Soundtrack */ + 99, /* 37 Atmosphere */ + 14, /* 38 Warmbell, sounds kind of like crystal(98) perhaps Tubular Bells(14) would be better. It is! */ + 54, /* 39 FunnyVox, sounds alot like Bagpipe(109) and Shania(111) */ + 98, /* 40 EchoBell, no real equiv, sounds like Crystal(98) */ + 96, /* 41 IceRain */ + 68, /* 42 Oboe 2001, no equiv, just patching it to normal oboe(68) */ + 95, /* 43 EchoPans, no equiv, setting to SweepPad */ + 81, /* 44 DoctorSolo Bank 8 */ + 87, /* 45 SchoolDaze, no real equiv */ + 112,/* 46 Bell Singer */ + 80, /* 47 SquareWave */ + 48, /* 48 Strings 1 */ + 48, /* 49 Strings 2 - should be 49 */ + 44, /* 50 Strings 3 (Synth) - Experimental set to Tremollo Strings - should be 50 */ + 45, /* 51 Pizzicato Strings */ + 40, /* 52 Violin 1 */ + 40, /* 53 Violin 2 ? Viola */ + 42, /* 54 Cello 1 */ + 42, /* 55 Cello 2 */ + 43, /* 56 Contrabass */ + 46, /* 57 Harp 1 */ + 46, /* 58 Harp 2 */ + 24, /* 59 Guitar 1 (Nylon) */ + 25, /* 60 Guitar 2 (Steel) */ + 26, /* 61 Elec Guitar 1 */ + 27, /* 62 Elec Guitar 2 */ + 104,/* 63 Sitar */ + 32, /* 64 Acou Bass 1 */ + 32, /* 65 Acou Bass 2 */ + 33, /* 66 Elec Bass 1 */ + 34, /* 67 Elec Bass 2 */ + 36, /* 68 Slap Bass 1 */ + 37, /* 69 Slap Bass 2 */ + 35, /* 70 Fretless Bass 1 */ + 35, /* 71 Fretless Bass 2 */ + 73, /* 72 Flute 1 */ + 73, /* 73 Flute 2 */ + 72, /* 74 Piccolo 1 */ + 72, /* 75 Piccolo 2 */ + 74, /* 76 Recorder */ + 75, /* 77 Pan Pipes */ + 64, /* 78 Sax 1 */ + 65, /* 79 Sax 2 */ + 66, /* 80 Sax 3 */ + 67, /* 81 Sax 4 */ + 71, /* 82 Clarinet 1 */ + 71, /* 83 Clarinet 2 */ + 68, /* 84 Oboe */ + 69, /* 85 English Horn (Cor Anglais) */ + 70, /* 86 Bassoon */ + 22, /* 87 Harmonica */ + 56, /* 88 Trumpet 1 */ + 56, /* 89 Trumpet 2 */ + 57, /* 90 Trombone 1 */ + 57, /* 91 Trombone 2 */ + 60, /* 92 French Horn 1 */ + 60, /* 93 French Horn 2 */ + 58, /* 94 Tuba */ + 61, /* 95 Brass Section 1 */ + 61, /* 96 Brass Section 2 */ + 11, /* 97 Vibes 1 */ + 11, /* 98 Vibes 2 */ + 99, /* 99 Syn Mallet Bank 1 */ + 112,/* 100 WindBell no real equiv Set to TinkleBell(112) */ + 9, /* 101 Glockenspiel */ + 14, /* 102 Tubular Bells */ + 13, /* 103 Xylophone */ + 12, /* 104 Marimba */ + 107,/* 105 Koto */ + 111,/* 106 Sho?? set to Shanai(111) */ + 77, /* 107 Shakauhachi */ + 78, /* 108 Whistle 1 */ + 78, /* 109 Whistle 2 */ + 76, /* 110 Bottle Blow */ + 76, /* 111 Breathpipe no real equiv set to bottle blow(76) */ + 47, /* 112 Timpani */ + 117,/* 113 Melodic Tom */ + 116,/* 114 Deap Snare no equiv, set to Taiko(116) */ + 118,/* 115 Electric Perc 1 */ + 118,/* 116 Electric Perc 2 */ + 116,/* 117 Taiko */ + 115,/* 118 Taiko Rim, no real equiv, set to Woodblock(115) */ + 119,/* 119 Cymbal, no real equiv, set to reverse cymbal(119) */ + 115,/* 120 Castanets, no real equiv, in GM set to Woodblock(115) */ + 112,/* 121 Triangle, no real equiv, set to TinkleBell(112) */ + 55, /* 122 Orchestral Hit */ + 124,/* 123 Telephone */ + 123,/* 124 BirdTweet */ + 94, /* 125 Big Notes Pad no equiv, set to halo pad (94) */ + 98, /* 126 Water Bell set to Crystal Pad(98) */ + 121 /* 127 Jungle Tune set to Breath Noise */ +}; + +/* Same as above, except include patch changes + * so GS instruments can be used */ +static const char xmi2mid_mt32asgs[256] = { + 0, 0, /* 0 Piano 1 */ + 1, 0, /* 1 Piano 2 */ + 2, 0, /* 2 Piano 3 (synth) */ + 4, 0, /* 3 EPiano 1 */ + 4, 0, /* 4 EPiano 2 */ + 5, 0, /* 5 EPiano 3 */ + 5, 0, /* 6 EPiano 4 */ + 3, 0, /* 7 Honkytonk */ + 16, 0, /* 8 Organ 1 */ + 17, 0, /* 9 Organ 2 */ + 18, 0, /* 10 Organ 3 */ + 16, 0, /* 11 Organ 4 */ + 19, 0, /* 12 Pipe Organ 1 */ + 19, 0, /* 13 Pipe Organ 2 */ + 19, 0, /* 14 Pipe Organ 3 */ + 21, 0, /* 15 Accordion */ + 6, 0, /* 16 Harpsichord 1 */ + 6, 0, /* 17 Harpsichord 2 */ + 6, 0, /* 18 Harpsichord 3 */ + 7, 0, /* 19 Clavinet 1 */ + 7, 0, /* 20 Clavinet 2 */ + 7, 0, /* 21 Clavinet 3 */ + 8, 0, /* 22 Celesta 1 */ + 8, 0, /* 23 Celesta 2 */ + 62, 0, /* 24 Synthbrass 1 (62) */ + 63, 0, /* 25 Synthbrass 2 (63) */ + 62, 0, /* 26 Synthbrass 3 Bank 8 */ + 63, 0, /* 27 Synthbrass 4 Bank 8 */ + 38, 0, /* 28 Synthbass 1 */ + 39, 0, /* 29 Synthbass 2 */ + 38, 0, /* 30 Synthbass 3 Bank 8 */ + 39, 0, /* 31 Synthbass 4 Bank 8 */ + 88, 0, /* 32 Fantasy */ + 90, 0, /* 33 Harmonic Pan - No equiv closest is polysynth(90) :( */ + 52, 0, /* 34 Choral ?? Currently set to SynthVox(54). Should it be ChoirAhhs(52)??? */ + 92, 0, /* 35 Glass */ + 97, 0, /* 36 Soundtrack */ + 99, 0, /* 37 Atmosphere */ + 14, 0, /* 38 Warmbell, sounds kind of like crystal(98) perhaps Tubular Bells(14) would be better. It is! */ + 54, 0, /* 39 FunnyVox, sounds alot like Bagpipe(109) and Shania(111) */ + 98, 0, /* 40 EchoBell, no real equiv, sounds like Crystal(98) */ + 96, 0, /* 41 IceRain */ + 68, 0, /* 42 Oboe 2001, no equiv, just patching it to normal oboe(68) */ + 95, 0, /* 43 EchoPans, no equiv, setting to SweepPad */ + 81, 0, /* 44 DoctorSolo Bank 8 */ + 87, 0, /* 45 SchoolDaze, no real equiv */ + 112, 0, /* 46 Bell Singer */ + 80, 0, /* 47 SquareWave */ + 48, 0, /* 48 Strings 1 */ + 48, 0, /* 49 Strings 2 - should be 49 */ + 44, 0, /* 50 Strings 3 (Synth) - Experimental set to Tremollo Strings - should be 50 */ + 45, 0, /* 51 Pizzicato Strings */ + 40, 0, /* 52 Violin 1 */ + 40, 0, /* 53 Violin 2 ? Viola */ + 42, 0, /* 54 Cello 1 */ + 42, 0, /* 55 Cello 2 */ + 43, 0, /* 56 Contrabass */ + 46, 0, /* 57 Harp 1 */ + 46, 0, /* 58 Harp 2 */ + 24, 0, /* 59 Guitar 1 (Nylon) */ + 25, 0, /* 60 Guitar 2 (Steel) */ + 26, 0, /* 61 Elec Guitar 1 */ + 27, 0, /* 62 Elec Guitar 2 */ + 104, 0, /* 63 Sitar */ + 32, 0, /* 64 Acou Bass 1 */ + 32, 0, /* 65 Acou Bass 2 */ + 33, 0, /* 66 Elec Bass 1 */ + 34, 0, /* 67 Elec Bass 2 */ + 36, 0, /* 68 Slap Bass 1 */ + 37, 0, /* 69 Slap Bass 2 */ + 35, 0, /* 70 Fretless Bass 1 */ + 35, 0, /* 71 Fretless Bass 2 */ + 73, 0, /* 72 Flute 1 */ + 73, 0, /* 73 Flute 2 */ + 72, 0, /* 74 Piccolo 1 */ + 72, 0, /* 75 Piccolo 2 */ + 74, 0, /* 76 Recorder */ + 75, 0, /* 77 Pan Pipes */ + 64, 0, /* 78 Sax 1 */ + 65, 0, /* 79 Sax 2 */ + 66, 0, /* 80 Sax 3 */ + 67, 0, /* 81 Sax 4 */ + 71, 0, /* 82 Clarinet 1 */ + 71, 0, /* 83 Clarinet 2 */ + 68, 0, /* 84 Oboe */ + 69, 0, /* 85 English Horn (Cor Anglais) */ + 70, 0, /* 86 Bassoon */ + 22, 0, /* 87 Harmonica */ + 56, 0, /* 88 Trumpet 1 */ + 56, 0, /* 89 Trumpet 2 */ + 57, 0, /* 90 Trombone 1 */ + 57, 0, /* 91 Trombone 2 */ + 60, 0, /* 92 French Horn 1 */ + 60, 0, /* 93 French Horn 2 */ + 58, 0, /* 94 Tuba */ + 61, 0, /* 95 Brass Section 1 */ + 61, 0, /* 96 Brass Section 2 */ + 11, 0, /* 97 Vibes 1 */ + 11, 0, /* 98 Vibes 2 */ + 99, 0, /* 99 Syn Mallet Bank 1 */ + 112, 0, /* 100 WindBell no real equiv Set to TinkleBell(112) */ + 9, 0, /* 101 Glockenspiel */ + 14, 0, /* 102 Tubular Bells */ + 13, 0, /* 103 Xylophone */ + 12, 0, /* 104 Marimba */ + 107, 0, /* 105 Koto */ + 111, 0, /* 106 Sho?? set to Shanai(111) */ + 77, 0, /* 107 Shakauhachi */ + 78, 0, /* 108 Whistle 1 */ + 78, 0, /* 109 Whistle 2 */ + 76, 0, /* 110 Bottle Blow */ + 76, 0, /* 111 Breathpipe no real equiv set to bottle blow(76) */ + 47, 0, /* 112 Timpani */ + 117, 0, /* 113 Melodic Tom */ + 116, 0, /* 114 Deap Snare no equiv, set to Taiko(116) */ + 118, 0, /* 115 Electric Perc 1 */ + 118, 0, /* 116 Electric Perc 2 */ + 116, 0, /* 117 Taiko */ + 115, 0, /* 118 Taiko Rim, no real equiv, set to Woodblock(115) */ + 119, 0, /* 119 Cymbal, no real equiv, set to reverse cymbal(119) */ + 115, 0, /* 120 Castanets, no real equiv, in GM set to Woodblock(115) */ + 112, 0, /* 121 Triangle, no real equiv, set to TinkleBell(112) */ + 55, 0, /* 122 Orchestral Hit */ + 124, 0, /* 123 Telephone */ + 123, 0, /* 124 BirdTweet */ + 94, 0, /* 125 Big Notes Pad no equiv, set to halo pad (94) */ + 98, 0, /* 126 Water Bell set to Crystal Pad(98) */ + 121, 0 /* 127 Jungle Tune set to Breath Noise */ +}; + +static int Convert_xmi2midi(uint8_t *in, uint32_t insize, + uint8_t **out, uint32_t *outsize, + uint32_t convert_type) +{ + struct xmi2mid_xmi_ctx ctx; + unsigned int i; + int ret = -1; + + if (convert_type > XMIDI_CONVERT_MT32_TO_GS) { + /*_WM_ERROR_NEW("%s:%i: %d is an invalid conversion type.", __FUNCTION__, __LINE__, convert_type);*/ + return (ret); + } + + memset(&ctx, 0, sizeof(struct xmi2mid_xmi_ctx)); + ctx.src = ctx.src_ptr = in; + ctx.srcsize = insize; + ctx.convert_type = convert_type; + + if (xmi2mid_ParseXMI(&ctx) < 0) { + /*_WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_XMI, NULL, 0);*/ + goto _end; + } + + if (xmi2mid_ExtractTracks(&ctx) < 0) { + /*_WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_MIDI, NULL, 0);*/ + goto _end; + } + + ctx.dst = (uint8_t*)malloc(DST_CHUNK); + ctx.dst_ptr = ctx.dst; + ctx.dstsize = DST_CHUNK; + ctx.dstrem = DST_CHUNK; + + /* Header is 14 bytes long and add the rest as well */ + xmi2mid_write1(&ctx, 'M'); + xmi2mid_write1(&ctx, 'T'); + xmi2mid_write1(&ctx, 'h'); + xmi2mid_write1(&ctx, 'd'); + + xmi2mid_write4(&ctx, 6); + + xmi2mid_write2(&ctx, ctx.info.type); + xmi2mid_write2(&ctx, ctx.info.tracks); + xmi2mid_write2(&ctx, ctx.timing[0]);/* write divisions from track0 */ + + for (i = 0; i < ctx.info.tracks; i++) + xmi2mid_ConvertListToMTrk(&ctx, ctx.events[i]); + *out = ctx.dst; + *outsize = ctx.dstsize - ctx.dstrem; + ret = 0; + +_end: /* cleanup */ + if (ret < 0) { + free(ctx.dst); + *out = NULL; + *outsize = 0; + } + if (ctx.events) { + for (i = 0; i < ctx.info.tracks; i++) + xmi2mid_DeleteEventList(ctx.events[i]); + free(ctx.events); + } + free(ctx.timing); + + return (ret); +} + +static void xmi2mid_DeleteEventList(midi_event *mlist) { + midi_event *event; + midi_event *next; + + next = mlist; + + while ((event = next) != NULL) { + next = event->next; + free(event->buffer); + free(event); + } +} + +/* Sets current to the new event and updates list */ +static void xmi2mid_CreateNewEvent(struct xmi2mid_xmi_ctx *ctx, int32_t time) { + if (!ctx->list) { + ctx->list = ctx->current = (struct _xmi2mid_midi_event *)calloc(1, sizeof(midi_event)); + ctx->current->time = (time < 0)? 0 : time; + return; + } + + if (time < 0) { + midi_event *event = (midi_event *)calloc(1, sizeof(midi_event)); + event->next = ctx->list; + ctx->list = ctx->current = event; + return; + } + + if (ctx->current->time > time) + ctx->current = ctx->list; + + while (ctx->current->next) { + if (ctx->current->next->time > time) { + midi_event *event = (midi_event *)calloc(1, sizeof(midi_event)); + event->next = ctx->current->next; + ctx->current->next = event; + ctx->current = event; + ctx->current->time = time; + return; + } + + ctx->current = ctx->current->next; + } + + ctx->current->next = (struct _xmi2mid_midi_event *)calloc(1, sizeof(midi_event)); + ctx->current = ctx->current->next; + ctx->current->time = time; +} + +/* Conventional Variable Length Quantity */ +static int xmi2mid_GetVLQ(struct xmi2mid_xmi_ctx *ctx, uint32_t *quant) { + int i; + uint32_t data; + + *quant = 0; + for (i = 0; i < 4; i++) { + data = xmi2mid_read1(ctx); + *quant <<= 7; + *quant |= data & 0x7F; + + if (!(data & 0x80)) { + i++; + break; + } + } + return (i); +} + +/* XMIDI Delta Variable Length Quantity */ +static int xmi2mid_GetVLQ2(struct xmi2mid_xmi_ctx *ctx, uint32_t *quant) { + int i; + int32_t data; + + *quant = 0; + for (i = 0; i < 4; i++) { + data = xmi2mid_read1(ctx); + if (data & 0x80) { + xmi2mid_skipsrc(ctx, -1); + break; + } + *quant += data; + } + return (i); +} + +static int xmi2mid_PutVLQ(struct xmi2mid_xmi_ctx *ctx, uint32_t value) { + int32_t buffer; + int i = 1, j; + buffer = value & 0x7F; + while (value >>= 7) { + buffer <<= 8; + buffer |= ((value & 0x7F) | 0x80); + i++; + } + for (j = 0; j < i; j++) { + xmi2mid_write1(ctx, buffer & 0xFF); + buffer >>= 8; + } + + return (i); +} + +/* Converts Events + * + * Source is at the first data byte + * size 1 is single data byte + * size 2 is dual data byte + * size 3 is XMI Note on + * Returns bytes converted */ +static int xmi2mid_ConvertEvent(struct xmi2mid_xmi_ctx *ctx, const int32_t time, + const uint8_t status, const int size) { + uint32_t delta = 0; + int32_t data; + midi_event *prev; + int i; + + data = xmi2mid_read1(ctx); + + // CC: XMI 119 Controller is a callback function! + if(data == 119) { + xmi2mid_CreateNewEvent(ctx, time); + ctx->current->status = status; + ctx->current->data[0] = 0; + ctx->current->data[1] = data; + return (2); + } + + /*HACK!*/ + if (((status >> 4) == 0xB) && (status & 0xF) != 9 && (data == 114)) { + data = 32; /*Change XMI 114 controller into XG bank*/ + } + + /* Bank changes are handled here */ + if ((status >> 4) == 0xB && data == 0) { + data = xmi2mid_read1(ctx); + + ctx->bank127[status & 0xF] = 0; + + if ( ctx->convert_type == XMIDI_CONVERT_MT32_TO_GM || + ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS || + ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127 || + (ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127DRUM + && (status & 0xF) == 9) ) + return (2); + + xmi2mid_CreateNewEvent(ctx, time); + ctx->current->status = status; + ctx->current->data[0] = 0; + ctx->current->data[1] = data == 127 ? 0 : data;/*HACK:*/ + + if (ctx->convert_type == XMIDI_CONVERT_GS127_TO_GS && data == 127) + ctx->bank127[status & 0xF] = 1; + + return (2); + } + + /* Handling for patch change mt32 conversion, probably should go elsewhere */ + if ((status >> 4) == 0xC && (status&0xF) != 9 + && ctx->convert_type != XMIDI_CONVERT_NOCONVERSION) + { + if (ctx->convert_type == XMIDI_CONVERT_MT32_TO_GM) + { + data = xmi2mid_mt32asgm[data]; + } + else if ((ctx->convert_type == XMIDI_CONVERT_GS127_TO_GS && ctx->bank127[status&0xF]) || + ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS || + ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127DRUM) + { + xmi2mid_CreateNewEvent (ctx, time); + ctx->current->status = 0xB0 | (status&0xF); + ctx->current->data[0] = 0; + ctx->current->data[1] = xmi2mid_mt32asgs[data*2+1]; + + data = xmi2mid_mt32asgs[data*2]; + } + else if (ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127) + { + xmi2mid_CreateNewEvent (ctx, time); + ctx->current->status = 0xB0 | (status&0xF); + ctx->current->data[0] = 0; + ctx->current->data[1] = 127; + } + } + /* Drum track handling */ + else if ((status >> 4) == 0xC && (status&0xF) == 9 && + (ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127DRUM || ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127)) + { + xmi2mid_CreateNewEvent (ctx, time); + ctx->current->status = 0xB9; + ctx->current->data[0] = 0; + ctx->current->data[1] = 127; + } + + xmi2mid_CreateNewEvent(ctx, time); + ctx->current->status = status; + + ctx->current->data[0] = data; + + if (size == 1) + return (1); + + ctx->current->data[1] = xmi2mid_read1(ctx); + + if (size == 2) + return (2); + + /* XMI Note On handling */ + prev = ctx->current; + i = xmi2mid_GetVLQ(ctx, &delta); + xmi2mid_CreateNewEvent(ctx, time + delta * 3); + + ctx->current->status = status; + ctx->current->data[0] = data; + ctx->current->data[1] = 0; + ctx->current = prev; + + return (i + 2); +} + +/* Simple routine to convert system messages */ +static int32_t xmi2mid_ConvertSystemMessage(struct xmi2mid_xmi_ctx *ctx, const int32_t time, + const uint8_t status) { + int32_t i = 0; + + xmi2mid_CreateNewEvent(ctx, time); + ctx->current->status = status; + + /* Handling of Meta events */ + if (status == 0xFF) { + ctx->current->data[0] = xmi2mid_read1(ctx); + i++; + } + + i += xmi2mid_GetVLQ(ctx, &ctx->current->len); + + if (!ctx->current->len) + return (i); + + ctx->current->buffer = (uint8_t *)malloc(sizeof(uint8_t)*ctx->current->len); + xmi2mid_copy(ctx, (char *) ctx->current->buffer, ctx->current->len); + + return (i + ctx->current->len); +} + +/* XMIDI and Midi to List + * Returns XMIDI PPQN */ +static int32_t xmi2mid_ConvertFiletoList(struct xmi2mid_xmi_ctx *ctx, const xmi2mid_rbrn *rbrn) { + int32_t time = 0; + uint32_t data; + int32_t end = 0; + int32_t tempo = 500000; + int32_t tempo_set = 0; + uint32_t status = 0; + uint32_t file_size = xmi2mid_getsrcsize(ctx); + uint32_t begin = xmi2mid_getsrcpos(ctx); + + /* Set Drum track to correct setting if required */ + if (ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127) { + xmi2mid_CreateNewEvent(ctx, 0); + ctx->current->status = 0xB9; + ctx->current->data[0] = 0; + ctx->current->data[1] = 127; + } + + while (!end && xmi2mid_getsrcpos(ctx) < file_size) { + uint32_t offset = xmi2mid_getsrcpos(ctx) - begin; + + /* search for branch to this offset */ + for (unsigned i = 0, n = rbrn->count; i < n; ++i) { + if (offset == rbrn->offset[i]) { + unsigned id = rbrn->id[i]; + + xmi2mid_CreateNewEvent(ctx, time); + + uint8_t *marker = (uint8_t *)malloc(sizeof(uint8_t)*8); + memcpy(marker, ":XBRN:", 6); + const char hex[] = "0123456789ABCDEF"; + marker[6] = hex[id >> 4]; + marker[7] = hex[id & 15]; + + XMI2MID_TRACE("Branch %u @ %u marker \"%.8s\"", + id, offset, marker); + + ctx->current->status = 0xFF; + ctx->current->data[0] = 0x06; + ctx->current->len = 8; + + ctx->current->buffer = marker; + } + } + + xmi2mid_GetVLQ2(ctx, &data); + time += data * 3; + + status = xmi2mid_read1(ctx); + + switch (status >> 4) { + case XMI2MID_MIDI_STATUS_NOTE_ON: + xmi2mid_ConvertEvent(ctx, time, status, 3); + break; + + /* 2 byte data */ + case XMI2MID_MIDI_STATUS_NOTE_OFF: + case XMI2MID_MIDI_STATUS_AFTERTOUCH: + case XMI2MID_MIDI_STATUS_CONTROLLER: + case XMI2MID_MIDI_STATUS_PITCH_WHEEL: + xmi2mid_ConvertEvent(ctx, time, status, 2); + break; + + /* 1 byte data */ + case XMI2MID_MIDI_STATUS_PROG_CHANGE: + case XMI2MID_MIDI_STATUS_PRESSURE: + xmi2mid_ConvertEvent(ctx, time, status, 1); + break; + + case XMI2MID_MIDI_STATUS_SYSEX: + if (status == 0xFF) { + int32_t pos = xmi2mid_getsrcpos(ctx); + uint32_t dat = xmi2mid_read1(ctx); + + if (dat == 0x2F) /* End */ + end = 1; + else if (dat == 0x51 && !tempo_set) /* Tempo. Need it for PPQN */ + { + xmi2mid_skipsrc(ctx, 1); + tempo = xmi2mid_read1(ctx) << 16; + tempo += xmi2mid_read1(ctx) << 8; + tempo += xmi2mid_read1(ctx); + tempo *= 3; + tempo_set = 1; + } else if (dat == 0x51 && tempo_set) /* Skip any other tempo changes */ + { + xmi2mid_GetVLQ(ctx, &dat); + xmi2mid_skipsrc(ctx, dat); + break; + } + + xmi2mid_seeksrc(ctx, pos); + } + xmi2mid_ConvertSystemMessage(ctx, time, status); + break; + + default: + break; + } + } + return ((tempo * 3) / 25000); +} + +/* Converts and event list to a MTrk + * Returns bytes of the array + * buf can be NULL */ +static uint32_t xmi2mid_ConvertListToMTrk(struct xmi2mid_xmi_ctx *ctx, midi_event *mlist) { + int32_t time = 0; + midi_event *event; + uint32_t delta; + uint8_t last_status = 0; + uint32_t i = 8; + uint32_t j; + uint32_t size_pos, cur_pos; + int end = 0; + + xmi2mid_write1(ctx, 'M'); + xmi2mid_write1(ctx, 'T'); + xmi2mid_write1(ctx, 'r'); + xmi2mid_write1(ctx, 'k'); + + size_pos = xmi2mid_getdstpos(ctx); + xmi2mid_skipdst(ctx, 4); + + for (event = mlist; event && !end; event = event->next) { + delta = (event->time - time); + time = event->time; + + i += xmi2mid_PutVLQ(ctx, delta); + + if ((event->status != last_status) || (event->status >= 0xF0)) { + xmi2mid_write1(ctx, event->status); + i++; + } + + last_status = event->status; + + switch (event->status >> 4) { + /* 2 bytes data + * Note off, Note on, Aftertouch, Controller and Pitch Wheel */ + case 0x8: + case 0x9: + case 0xA: + case 0xB: + case 0xE: + xmi2mid_write1(ctx, event->data[0]); + xmi2mid_write1(ctx, event->data[1]); + i += 2; + break; + + /* 1 bytes data + * Program Change and Channel Pressure */ + case 0xC: + case 0xD: + xmi2mid_write1(ctx, event->data[0]); + i++; + break; + + /* Variable length + * SysEx */ + case 0xF: + if (event->status == 0xFF) { + if (event->data[0] == 0x2f) + end = 1; + xmi2mid_write1(ctx, event->data[0]); + i++; + } + i += xmi2mid_PutVLQ(ctx, event->len); + if (event->len) { + for (j = 0; j < event->len; j++) { + xmi2mid_write1(ctx, event->buffer[j]); + i++; + } + } + break; + + /* Never occur */ + default: + /*_WM_DEBUG_MSG("%s: unrecognized event", __FUNCTION__);*/ + break; + } + } + + cur_pos = xmi2mid_getdstpos(ctx); + xmi2mid_seekdst(ctx, size_pos); + xmi2mid_write4(ctx, i - 8); + xmi2mid_seekdst(ctx, cur_pos); + + return (i); +} + +/* Assumes correct xmidi */ +static uint32_t xmi2mid_ExtractTracksFromXmi(struct xmi2mid_xmi_ctx *ctx) { + uint32_t num = 0; + signed short ppqn; + uint32_t len = 0; + int32_t begin; + char buf[32]; + uint32_t branch[128]; + + /* clear branch points */ + for (unsigned i = 0; i < 128; ++i) + branch[i] = ~0u; + + while (xmi2mid_getsrcpos(ctx) < xmi2mid_getsrcsize(ctx) && num != ctx->info.tracks) { + /* Read first 4 bytes of name */ + xmi2mid_copy(ctx, buf, 4); + len = xmi2mid_read4(ctx); + + /* Skip the FORM entries */ + if (!memcmp(buf, "FORM", 4)) { + xmi2mid_skipsrc(ctx, 4); + xmi2mid_copy(ctx, buf, 4); + len = xmi2mid_read4(ctx); + } + + if (!memcmp(buf, "RBRN", 4)) { + begin = xmi2mid_getsrcpos(ctx); + uint32_t count; + + if (len < 2) { + /* insufficient data */ + goto rbrn_nodata; + } + + count = xmi2mid_read2(ctx); + if (len - 2 < 6 * count) { + /* insufficient data */ + goto rbrn_nodata; + } + + for (uint32_t i = 0; i < count; ++i) { + /* read branch point as byte offset */ + uint32_t ctlvalue = xmi2mid_read2(ctx); + uint32_t evtoffset = xmi2mid_read4le(ctx); + if(ctlvalue < 128) + branch[ctlvalue] = evtoffset; + XMI2MID_TRACE("RBRN %u/%u: id %u -> offset %u", + i + 1, count, ctlvalue, evtoffset); + } + + rbrn_nodata: + xmi2mid_seeksrc(ctx, begin + ((len + 1) & ~1)); + continue; + } + + if (memcmp(buf, "EVNT", 4)) { + xmi2mid_skipsrc(ctx, (len + 1) & ~1); + continue; + } + + ctx->list = NULL; + begin = xmi2mid_getsrcpos(ctx); + + /* Rearrange branches as structure */ + xmi2mid_rbrn rbrn; + rbrn.count = 0; + for (unsigned i = 0; i < 128; ++i) { + if (branch[i] != ~0u) { + unsigned index = rbrn.count; + rbrn.id[index] = i; + rbrn.offset[index] = branch[i]; + rbrn.count = index + 1; + } + } + + /* Convert it */ + if (!(ppqn = xmi2mid_ConvertFiletoList(ctx, &rbrn))) { + /*_WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, NULL, 0);*/ + break; + } + ctx->timing[num] = ppqn; + ctx->events[num] = ctx->list; + + /* Increment Counter */ + num++; + + /* go to start of next track */ + xmi2mid_seeksrc(ctx, begin + ((len + 1) & ~1)); + + /* clear branch points */ + for (unsigned i = 0; i < 128; ++i) + branch[i] = ~0u; + } + + /* Return how many were converted */ + return (num); +} + +static int xmi2mid_ParseXMI(struct xmi2mid_xmi_ctx *ctx) { + uint32_t i; + uint32_t start; + uint32_t len; + uint32_t chunk_len; + uint32_t file_size; + char buf[32]; + + file_size = xmi2mid_getsrcsize(ctx); + if (xmi2mid_getsrcpos(ctx) + 8 > file_size) { +badfile: /*_WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0);*/ + return (-1); + } + + /* Read first 4 bytes of header */ + xmi2mid_copy(ctx, buf, 4); + + /* Could be XMIDI */ + if (!memcmp(buf, "FORM", 4)) { + /* Read length of */ + len = xmi2mid_read4(ctx); + + start = xmi2mid_getsrcpos(ctx); + if (start + 4 > file_size) + goto badfile; + + /* Read 4 bytes of type */ + xmi2mid_copy(ctx, buf, 4); + + /* XDIRless XMIDI, we can handle them here. */ + if (!memcmp(buf, "XMID", 4)) { + /*_WM_DEBUG_MSG("Warning: XMIDI without XDIR");*/ + ctx->info.tracks = 1; + } + /* Not an XMIDI that we recognise */ + else if (memcmp(buf, "XDIR", 4)) { + goto badfile; + } + else { /* Seems Valid */ + ctx->info.tracks = 0; + + for (i = 4; i < len; i++) { + /* check too short files */ + if (xmi2mid_getsrcpos(ctx) + 10 > file_size) + break; + + /* Read 4 bytes of type */ + xmi2mid_copy(ctx, buf, 4); + + /* Read length of chunk */ + chunk_len = xmi2mid_read4(ctx); + + /* Add eight bytes */ + i += 8; + + if (memcmp(buf, "INFO", 4)) { + /* Must align */ + xmi2mid_skipsrc(ctx, (chunk_len + 1) & ~1); + i += (chunk_len + 1) & ~1; + continue; + } + + /* Must be at least 2 bytes long */ + if (chunk_len < 2) + break; + + ctx->info.tracks = xmi2mid_read2(ctx); + break; + } + + /* Didn't get to fill the header */ + if (ctx->info.tracks == 0) { + goto badfile; + } + + /* Ok now to start part 2 + * Goto the right place */ + xmi2mid_seeksrc(ctx, start + ((len + 1) & ~1)); + if (xmi2mid_getsrcpos(ctx) + 12 > file_size) + goto badfile; + + /* Read 4 bytes of type */ + xmi2mid_copy(ctx, buf, 4); + + if (memcmp(buf, "CAT ", 4)) { + /*_WM_ERROR_NEW("XMI error: expected \"CAT \", found \"%c%c%c%c\".", + buf[0], buf[1], buf[2], buf[3]);*/ + return (-1); + } + + /* Now read length of this track */ + xmi2mid_read4(ctx); + + /* Read 4 bytes of type */ + xmi2mid_copy(ctx, buf, 4); + + if (memcmp(buf, "XMID", 4)) { + /*_WM_ERROR_NEW("XMI error: expected \"XMID\", found \"%c%c%c%c\".", + buf[0], buf[1], buf[2], buf[3]);*/ + return (-1); + } + + /* Valid XMID */ + ctx->datastart = xmi2mid_getsrcpos(ctx); + return (0); + } + } + + return (-1); +} + +static int xmi2mid_ExtractTracks(struct xmi2mid_xmi_ctx *ctx) { + uint32_t i; + + ctx->events = (midi_event **)calloc(ctx->info.tracks, sizeof(midi_event*)); + ctx->timing = (int16_t *)calloc(ctx->info.tracks, sizeof(int16_t)); + /* type-2 for multi-tracks, type-0 otherwise */ + ctx->info.type = (ctx->info.tracks > 1)? 2 : 0; + + xmi2mid_seeksrc(ctx, ctx->datastart); + i = xmi2mid_ExtractTracksFromXmi(ctx); + + if (i != ctx->info.tracks) { + /*_WM_ERROR_NEW("XMI error: extracted only %u out of %u tracks from XMIDI", + ctx->info.tracks, i);*/ + return (-1); + } + + return (0); +} + diff --git a/engine/src/Libraries/adlmidi/file_reader.hpp b/engine/src/Libraries/adlmidi/file_reader.hpp new file mode 100644 index 0000000..7d13262 --- /dev/null +++ b/engine/src/Libraries/adlmidi/file_reader.hpp @@ -0,0 +1,300 @@ +/* + * FileAndMemoryReader - a tiny helper to utify file reading from a disk and memory block + * + * Copyright (c) 2015-2018 Vitaly Novichkov + * + * 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. + */ + +#pragma once +#ifndef FILE_AND_MEM_READER_HHHH +#define FILE_AND_MEM_READER_HHHH + +#include // std::string +#include // std::fopen, std::fread, std::fseek, std::ftell, std::fclose, std::feof +#include // uint*_t +#include // size_t and friends +#ifdef _WIN32 +#define NOMINMAX 1 +#include // std::strlen +#include // MultiByteToWideChar +#endif + +/** + * @brief A little class gives able to read filedata from disk and also from a memory segment + */ +class FileAndMemReader +{ + //! Currently loaded filename (empty for a memory blocks) + std::string m_file_name; + //! File reader descriptor + std::FILE *m_fp; + + //! Memory pointer descriptor + const void *m_mp; + //! Size of memory block + size_t m_mp_size; + //! Cursor position in the memory block + size_t m_mp_tell; + +public: + /** + * @brief Relation direction + */ + enum relTo + { + //! At begin position + SET = SEEK_SET, + //! At current position + CUR = SEEK_CUR, + //! At end position + END = SEEK_END + }; + + /** + * @brief C.O.: It's a constructor! + */ + FileAndMemReader() : + m_fp(NULL), + m_mp(NULL), + m_mp_size(0), + m_mp_tell(0) + {} + + /** + * @brief C.O.: It's a destructor! + */ + ~FileAndMemReader() + { + close(); + } + + /** + * @brief Open file from a disk + * @param path Path to the file in UTF-8 (even on Windows!) + */ + void openFile(const char *path) + { + if(m_fp) + this->close();//Close previously opened file first! +#if !defined(_WIN32) || defined(__WATCOMC__) + m_fp = std::fopen(path, "rb"); +#else + wchar_t widePath[MAX_PATH]; + int size = MultiByteToWideChar(CP_UTF8, 0, path, static_cast(std::strlen(path)), widePath, MAX_PATH); + widePath[size] = '\0'; + m_fp = _wfopen(widePath, L"rb"); +#endif + m_file_name = path; + m_mp = NULL; + m_mp_size = 0; + m_mp_tell = 0; + } + + /** + * @brief Open file from memory block + * @param mem Pointer to the memory block + * @param lenght Size of given block + */ + void openData(const void *mem, size_t lenght) + { + if(m_fp) + this->close();//Close previously opened file first! + m_fp = NULL; + m_mp = mem; + m_mp_size = lenght; + m_mp_tell = 0; + } + + /** + * @brief Seek to given position + * @param pos Offset or position + * @param rel_to Relation (at begin, at current, or at end) + */ + void seek(long pos, int rel_to) + { + if(!this->isValid()) + return; + + if(m_fp)//If a file + { + std::fseek(m_fp, pos, rel_to); + } + else//If a memory block + { + switch(rel_to) + { + case SET: + m_mp_tell = static_cast(pos); + break; + + case END: + m_mp_tell = m_mp_size - static_cast(pos); + break; + + case CUR: + m_mp_tell = m_mp_tell + static_cast(pos); + break; + } + + if(m_mp_tell > m_mp_size) + m_mp_tell = m_mp_size; + } + } + + /** + * @brief Seek to given position (unsigned integer 64 as relation. Negative values not supported) + * @param pos Offset or position + * @param rel_to Relation (at begin, at current, or at end) + */ + inline void seeku(uint64_t pos, int rel_to) + { + this->seek(static_cast(pos), rel_to); + } + + /** + * @brief Read the buffer from a file + * @param buf Pointer to the destination memory block + * @param num Number of elements + * @param size Size of one element + * @return Size + */ + size_t read(void *buf, size_t num, size_t size) + { + if(!this->isValid()) + return 0; + if(m_fp) + return std::fread(buf, num, size, m_fp); + else + { + size_t pos = 0; + size_t maxSize = static_cast(size * num); + + while((pos < maxSize) && (m_mp_tell < m_mp_size)) + { + reinterpret_cast(buf)[pos] = reinterpret_cast(m_mp)[m_mp_tell]; + m_mp_tell++; + pos++; + } + + return pos / num; + } + } + + /** + * @brief Get one byte and seek forward + * @return Readed byte or EOF (a.k.a. -1) + */ + int getc() + { + if(!this->isValid()) + return -1; + if(m_fp)//If a file + { + return std::getc(m_fp); + } + else //If a memory block + { + if(m_mp_tell >= m_mp_size) + return -1; + int x = reinterpret_cast(m_mp)[m_mp_tell]; + m_mp_tell++; + return x; + } + } + + /** + * @brief Returns current offset of cursor in a file + * @return Offset position + */ + size_t tell() + { + if(!this->isValid()) + return 0; + if(m_fp)//If a file + return static_cast(std::ftell(m_fp)); + else//If a memory block + return m_mp_tell; + } + + /** + * @brief Close the file + */ + void close() + { + if(m_fp) + std::fclose(m_fp); + + m_fp = NULL; + m_mp = NULL; + m_mp_size = 0; + m_mp_tell = 0; + } + + /** + * @brief Is file instance valid + * @return true if vaild + */ + bool isValid() + { + return (m_fp) || (m_mp); + } + + /** + * @brief Is End Of File? + * @return true if end of file was reached + */ + bool eof() + { + if(!this->isValid()) + return true; + if(m_fp) + return (std::feof(m_fp) != 0); + else + return m_mp_tell >= m_mp_size; + } + + /** + * @brief Get a current file name + * @return File name of currently loaded file + */ + const std::string &fileName() + { + return m_file_name; + } + + /** + * @brief Retrieve file size + * @return Size of file in bytes + */ + size_t fileSize() + { + if(!this->isValid()) + return 0; + if(!m_fp) + return m_mp_size; //Size of memory block is well known + size_t old_pos = this->tell(); + seek(0l, FileAndMemReader::END); + size_t file_size = this->tell(); + seek(static_cast(old_pos), FileAndMemReader::SET); + return file_size; + } +}; + +#endif /* FILE_AND_MEM_READER_HHHH */ diff --git a/engine/src/Libraries/adlmidi/fraction.hpp b/engine/src/Libraries/adlmidi/fraction.hpp new file mode 100644 index 0000000..1c0a38d --- /dev/null +++ b/engine/src/Libraries/adlmidi/fraction.hpp @@ -0,0 +1,215 @@ +#ifndef bqw_fraction_h +#define bqw_fraction_h + +#include +#include + + +/* Fraction number handling. + * Copyright (C) 1992,2001 Bisqwit (http://iki.fi/bisqwit/) + */ + +template +class fraction +{ + inttype num1, num2; + typedef fraction self; + void Optim(); + + #if 1 + inline void Debug(char, const self &) { } + #else + inline void Debug(char op, const self &b) + { + cerr << nom() << '/' << denom() << ' ' << op + << ' ' << b.nom() << '/' << denom() + << ":\n"; + } + #endif +public: + void set(inttype n, inttype d) { num1=n; num2=d; Optim(); } + + fraction() : num1(0), num2(1) { } + fraction(inttype value) : num1(value), num2(1) { } + fraction(inttype n, inttype d) : num1(n), num2(d) { } + fraction(int value) : num1(value), num2(1) { } + template + explicit fraction(const floattype value) { operator= (value); } + inline double value() const {return nom() / (double)denom(); } + inline long double valuel() const {return nom() / (long double)denom(); } + self &operator+= (const inttype &value) { num1+=value*denom(); Optim(); return *this; } + self &operator-= (const inttype &value) { num1-=value*denom(); Optim(); return *this; } + self &operator*= (const inttype &value) { num1*=value; Optim(); return *this; } + self &operator/= (const inttype &value) { num2*=value; Optim(); return *this; } + self &operator+= (const self &b); + self &operator-= (const self &b); + self &operator*= (const self &b) { Debug('*',b);num1*=b.nom(); num2*=b.denom(); Optim(); return *this; } + self &operator/= (const self &b) { Debug('/',b);num1*=b.denom(); num2*=b.nom(); Optim(); return *this; } + self operator- () const { return self(-num1, num2); } + +#define fraction_blah_func(op1, op2) \ + self operator op1 (const self &b) const { self tmp(*this); tmp op2 b; return tmp; } + + fraction_blah_func( +, += ) + fraction_blah_func( -, -= ) + fraction_blah_func( /, /= ) + fraction_blah_func( *, *= ) + +#undef fraction_blah_func +#define fraction_blah_func(op) \ + bool operator op(const self &b) const { return value() op b.value(); } \ + bool operator op(inttype b) const { return value() op b; } + + fraction_blah_func( < ) + fraction_blah_func( > ) + fraction_blah_func( <= ) + fraction_blah_func( >= ) + +#undef fraction_blah_func + + const inttype &nom() const { return num1; } + const inttype &denom() const { return num2; } + inline bool operator == (inttype b) const { return denom() == 1 && nom() == b; } + inline bool operator != (inttype b) const { return denom() != 1 || nom() != b; } + inline bool operator == (const self &b) const { return denom()==b.denom() && nom()==b.nom(); } + inline bool operator != (const self &b) const { return denom()!=b.denom() || nom()!=b.nom(); } + //operator bool () const { return nom() != 0; } + inline bool negative() const { return (nom() < 0) ^ (denom() < 0); } + + self &operator= (const inttype value) { num2=1; num1=value; return *this; } + //self &operator= (int value) { num2=1; num1=value; return *this; } + + self &operator= (double orig) { return *this = (long double)orig; } + self &operator= (long double orig); +}; + +#ifdef _MSC_VER +#pragma warning(disable:4146) +#endif + +template +void fraction::Optim() +{ + /* Euclidean algorithm */ + inttype n1, n2, nn1, nn2; + + nn1 = std::numeric_limits::is_signed ? (num1 >= 0 ? num1 : -num1) : num1; + nn2 = std::numeric_limits::is_signed ? (num2 >= 0 ? num2 : -num2) : num2; + + if(nn1 < nn2) + n1 = num1, n2 = num2; + else + n1 = num2, n2 = num1; + + if(!num1) { num2 = 1; return; } + for(;;) + { + //fprintf(stderr, "%d/%d: n1=%d,n2=%d\n", nom(),denom(),n1,n2); + inttype tmp = n2 % n1; + if(!tmp)break; + n2 = n1; + n1 = tmp; + } + num1 /= n1; + num2 /= n1; + //fprintf(stderr, "result: %d/%d\n\n", nom(), denom()); +} + +#ifdef _MSC_VER +#pragma warning(default:4146) +#endif + +template +inline const fraction abs(const fraction &f) +{ + return fraction(abs(f.nom()), abs(f.denom())); +} + +#define fraction_blah_func(op) \ + template \ + fraction operator op \ + (const inttype bla, const fraction &b) \ + { return fraction (bla) op b; } +fraction_blah_func( + ) +fraction_blah_func( - ) +fraction_blah_func( * ) +fraction_blah_func( / ) +#undef fraction_blah_func + +#define fraction_blah_func(op1, op2) \ + template \ + fraction &fraction::operator op2 (const fraction &b) \ + { \ + inttype newnom = nom()*b.denom() op1 denom()*b.nom(); \ + num2 *= b.denom(); \ + num1 = newnom; \ + Optim(); \ + return *this; \ + } +fraction_blah_func( +, += ) +fraction_blah_func( -, -= ) +#undef fraction_blah_func + +template +fraction &fraction::operator= (long double orig) +{ + if(orig == 0.0) + { + set(0, 0); + return *this; + } + + inttype cf[25]; + for(int maxdepth=1; maxdepth<25; ++maxdepth) + { + inttype u,v; + long double virhe, a=orig; + int i, viim; + + for(i = 0; i < maxdepth; ++i) + { + cf[i] = (inttype)a; + if(cf[i]-1 > cf[i])break; + a = 1.0 / (a - cf[i]); + } + + for(viim=i-1; i < maxdepth; ++i) + cf[i] = 0; + + u = cf[viim]; + v = 1; + for(i = viim-1; i >= 0; --i) + { + inttype w = cf[i] * u + v; + v = u; + u = w; + } + + virhe = (orig - (u / (long double)v)) / orig; + + set(u, v); + //if(verbose > 4) + // cerr << "Guess: " << *this << " - error = " << virhe*100 << "%\n"; + + if(virhe < 1e-8 && virhe > -1e-8)break; + } + + //if(verbose > 4) + //{ + // cerr << "Fraction=" << orig << ": " << *this << endl; + //} + + return *this; +} + + +/* +template +ostream &operator << (ostream &dest, const fraction &m) +{ + if(m.denom() == (inttype)1) return dest << m.nom(); + return dest << m.nom() << '/' << m.denom(); +} +*/ + +#endif diff --git a/engine/src/Libraries/adlmidi/include/adlmidi.h b/engine/src/Libraries/adlmidi/include/adlmidi.h new file mode 100644 index 0000000..929f0ce --- /dev/null +++ b/engine/src/Libraries/adlmidi/include/adlmidi.h @@ -0,0 +1,1150 @@ +/* + * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ADLMIDI_H +#define ADLMIDI_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define ADLMIDI_VERSION_MAJOR 1 +#define ADLMIDI_VERSION_MINOR 4 +#define ADLMIDI_VERSION_PATCHLEVEL 0 + +#define ADLMIDI_TOSTR_I(s) #s +#define ADLMIDI_TOSTR(s) ADLMIDI_TOSTR_I(s) +#define ADLMIDI_VERSION \ + ADLMIDI_TOSTR(ADLMIDI_VERSION_MAJOR) "." \ + ADLMIDI_TOSTR(ADLMIDI_VERSION_MINOR) "." \ + ADLMIDI_TOSTR(ADLMIDI_VERSION_PATCHLEVEL) + + +#include + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +#include +typedef uint8_t ADL_UInt8; +typedef uint16_t ADL_UInt16; +typedef int8_t ADL_SInt8; +typedef int16_t ADL_SInt16; +#else +typedef unsigned char ADL_UInt8; +typedef unsigned short ADL_UInt16; +typedef char ADL_SInt8; +typedef short ADL_SInt16; +#endif + +/* == Deprecated function markers == */ + +#ifdef __GNUC__ +#define DEPRECATED(func) func __attribute__ ((deprecated)) +#elif defined(_MSC_VER) +#define DEPRECATED(func) __declspec(deprecated) func +#else +#define DEPRECATED(func) func +#endif + + +/** + * @brief Volume scaling models + */ +enum ADLMIDI_VolumeModels +{ + /*! Automatical choice by the specific bank */ + ADLMIDI_VolumeModel_AUTO = 0, + /*! Linearized scaling model, most standard */ + ADLMIDI_VolumeModel_Generic = 1, + /*! Native OPL3's logarithmic volume scale */ + ADLMIDI_VolumeModel_NativeOPL3 = 2, + /*! Native OPL3's logarithmic volume scale. Alias. */ + ADLMIDI_VolumeModel_CMF = ADLMIDI_VolumeModel_NativeOPL3, + /*! Logarithmic volume scale, using volume map table. Used in DMX. */ + ADLMIDI_VolumeModel_DMX = 3, + /*! Logarithmic volume scale, used in Apogee Sound System. */ + ADLMIDI_VolumeModel_APOGEE = 4, + /*! Aproximated and shorted volume map table. Similar to general, but has less granularity. */ + ADLMIDI_VolumeModel_9X = 5 +}; + +/** + * @brief Sound output format + */ +enum ADLMIDI_SampleType +{ + /*! signed PCM 16-bit */ + ADLMIDI_SampleType_S16 = 0, + /*! signed PCM 8-bit */ + ADLMIDI_SampleType_S8, + /*! float 32-bit */ + ADLMIDI_SampleType_F32, + /*! float 64-bit */ + ADLMIDI_SampleType_F64, + /*! signed PCM 24-bit */ + ADLMIDI_SampleType_S24, + /*! signed PCM 32-bit */ + ADLMIDI_SampleType_S32, + /*! unsigned PCM 8-bit */ + ADLMIDI_SampleType_U8, + /*! unsigned PCM 16-bit */ + ADLMIDI_SampleType_U16, + /*! unsigned PCM 24-bit */ + ADLMIDI_SampleType_U24, + /*! unsigned PCM 32-bit */ + ADLMIDI_SampleType_U32, + /*! Count of available sample format types */ + ADLMIDI_SampleType_Count, +}; + +/** + * @brief Sound output format context + */ +struct ADLMIDI_AudioFormat +{ + /*! type of sample */ + enum ADLMIDI_SampleType type; + /*! size in bytes of the storage type */ + unsigned containerSize; + /*! distance in bytes between consecutive samples */ + unsigned sampleOffset; +}; + +/** + * @brief Instance of the library + */ +struct ADL_MIDIPlayer +{ + /*! Private context descriptor */ + void *adl_midiPlayer; +}; + +/* DEPRECATED */ +#define adl_setNumCards adl_setNumChips + +/** + * @brief Sets number of emulated chips (from 1 to 100). Emulation of multiple chips extends polyphony limits + * @param device Instance of the library + * @param numChips Count of virtual chips to emulate + * @return 0 on success, <0 when any error has occurred + */ +extern int adl_setNumChips(struct ADL_MIDIPlayer *device, int numChips); + +/** + * @brief Get current number of emulated chips + * @param device Instance of the library + * @return Count of working chip emulators + */ +extern int adl_getNumChips(struct ADL_MIDIPlayer *device); + +/** + * @brief Sets a number of the patches bank from 0 to N banks. + * + * Is recommended to call adl_reset() to apply changes to already-loaded file player or real-time. + * + * @param device Instance of the library + * @param bank Number of embedded bank + * @return 0 on success, <0 when any error has occurred + */ +extern int adl_setBank(struct ADL_MIDIPlayer *device, int bank); + +/** + * @brief Returns total number of available banks + * @return Total number of available embedded banks + */ +extern int adl_getBanksCount(); + +/** + * @brief Returns pointer to array of names of every bank + * @return Array of strings containing the name of every embedded bank + */ +extern const char *const *adl_getBankNames(); + +/** + * @brief Reference to dynamic bank + */ +typedef struct ADL_Bank +{ + void *pointer[3]; +} ADL_Bank; + +/** + * @brief Identifier of dynamic bank + */ +typedef struct ADL_BankId +{ + /*! 0 if bank is melodic set, or 1 if bank is a percussion set */ + ADL_UInt8 percussive; + /*! Assign to MSB bank number */ + ADL_UInt8 msb; + /*! Assign to LSB bank number */ + ADL_UInt8 lsb; +} ADL_BankId; + +/** + * @brief Flags for dynamic bank access + */ +enum ADL_BankAccessFlags +{ + /*! create bank, allocating memory as needed */ + ADLMIDI_Bank_Create = 1, + /*! create bank, never allocating memory */ + ADLMIDI_Bank_CreateRt = 1|2, +}; + +/* ======== Instrument structures ======== */ + +/** + * @brief Version of the instrument data format + */ +enum +{ + ADLMIDI_InstrumentVersion = 0, +}; + +/** + * @brief Instrument flags + */ +typedef enum ADL_InstrumentFlags +{ + /*! Is two-operator single-voice instrument (no flags) */ + ADLMIDI_Ins_2op = 0x00, + /*! Is true four-operator instrument */ + ADLMIDI_Ins_4op = 0x01, + /*! Is pseudo four-operator (two 2-operator voices) instrument */ + ADLMIDI_Ins_Pseudo4op = 0x02, + /*! Is a blank instrument entry */ + ADLMIDI_Ins_IsBlank = 0x04, + + /*! RythmMode flags mask */ + ADLMIDI_Ins_RhythmModeMask = 0x38, + + /*! Mask of the flags range */ + ADLMIDI_Ins_ALL_MASK = 0x07, +} ADL_InstrumentFlags; + +/** + * @brief Rhythm-mode drum type + */ +typedef enum ADL_RhythmMode +{ + /*! RythmMode: BassDrum */ + ADLMIDI_RM_BassDrum = 0x08, + /*! RythmMode: Snare */ + ADLMIDI_RM_Snare = 0x10, + /*! RythmMode: TomTom */ + ADLMIDI_RM_TomTom = 0x18, + /*! RythmMode: Cymbal */ + ADLMIDI_RM_Cymbal = 0x20, + /*! RythmMode: HiHat */ + ADLMIDI_RM_HiHat = 0x28 +} ADL_RhythmMode; + + +/** + * @brief Operator structure, part of Instrument structure + */ +typedef struct ADL_Operator +{ + /*! AM/Vib/Env/Ksr/FMult characteristics */ + ADL_UInt8 avekf_20; + /*! Key Scale Level / Total level register data */ + ADL_UInt8 ksl_l_40; + /*! Attack / Decay */ + ADL_UInt8 atdec_60; + /*! Systain and Release register data */ + ADL_UInt8 susrel_80; + /*! Wave form */ + ADL_UInt8 waveform_E0; +} ADL_Operator; + +/** + * @brief Instrument structure + */ +typedef struct ADL_Instrument +{ + /*! Version of the instrument object */ + int version; + /*! MIDI note key (half-tone) offset for an instrument (or a first voice in pseudo-4-op mode) */ + ADL_SInt16 note_offset1; + /*! MIDI note key (half-tone) offset for a second voice in pseudo-4-op mode */ + ADL_SInt16 note_offset2; + /*! MIDI note velocity offset (taken from Apogee TMB format) */ + ADL_SInt8 midi_velocity_offset; + /*! Second voice detune level (taken from DMX OP2) */ + ADL_SInt8 second_voice_detune; + /*! Percussion MIDI base tone number at which this drum will be played */ + ADL_UInt8 percussion_key_number; + /** + * @var inst_flags + * @brief Instrument flags + * + * Enums: #ADL_InstrumentFlags and #ADL_RhythmMode + * + * Bitwise flags bit map: + * ``` + * [0EEEDCBA] + * A) 0x00 - 2-operator mode + * B) 0x01 - 4-operator mode + * C) 0x02 - pseudo-4-operator (two 2-operator voices) mode + * D) 0x04 - is 'blank' instrument (instrument which has no sound) + * E) 0x38 - Reserved for rhythm-mode percussion type number (three bits number) + * -> 0x00 - Melodic or Generic drum (rhythm-mode is disabled) + * -> 0x08 - is Bass drum + * -> 0x10 - is Snare + * -> 0x18 - is Tom-tom + * -> 0x20 - is Cymbal + * -> 0x28 - is Hi-hat + * 0) Reserved / Unused + * ``` + */ + ADL_UInt8 inst_flags; + /*! Feedback&Connection register for first and second operators */ + ADL_UInt8 fb_conn1_C0; + /*! Feedback&Connection register for third and fourth operators */ + ADL_UInt8 fb_conn2_C0; + /*! Operators register data */ + ADL_Operator operators[4]; + /*! Millisecond delay of sounding while key is on */ + ADL_UInt16 delay_on_ms; + /*! Millisecond delay of sounding after key off */ + ADL_UInt16 delay_off_ms; +} ADL_Instrument; + + +/* ======== Setup ======== */ + +/** + * @brief Preallocates a minimum number of bank slots. Returns the actual capacity + * @param device Instance of the library + * @param banks Count of bank slots to pre-allocate. + * @return actual capacity of reserved bank slots. + */ +extern int adl_reserveBanks(struct ADL_MIDIPlayer *device, unsigned banks); +/** + * @brief Gets the bank designated by the identifier, optionally creating if it does not exist + * @param device Instance of the library + * @param id Identifier of dynamic bank + * @param flags Flags for dynamic bank access (ADL_BankAccessFlags) + * @param bank Reference to dynamic bank + * @return 0 on success, <0 when any error has occurred + */ +extern int adl_getBank(struct ADL_MIDIPlayer *device, const ADL_BankId *id, int flags, ADL_Bank *bank); +/** + * @brief Gets the identifier of a bank + * @param device Instance of the library + * @param bank Reference to dynamic bank. + * @param id Identifier of dynamic bank + * @return 0 on success, <0 when any error has occurred + */ +extern int adl_getBankId(struct ADL_MIDIPlayer *device, const ADL_Bank *bank, ADL_BankId *id); +/** + * @brief Removes a bank + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @return 0 on success, <0 when any error has occurred + */ +extern int adl_removeBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank); +/** + * @brief Gets the first bank + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @return 0 on success, <0 when any error has occurred + */ +extern int adl_getFirstBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank); +/** + * @brief Iterates to the next bank + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @return 0 on success, <0 when any error has occurred or end has been reached. + */ +extern int adl_getNextBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank); +/** + * @brief Gets the nth intrument in the bank [0..127] + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @param index Index of the instrument + * @param ins Instrument entry + * @return 0 on success, <0 when any error has occurred + */ +extern int adl_getInstrument(struct ADL_MIDIPlayer *device, const ADL_Bank *bank, unsigned index, ADL_Instrument *ins); +/** + * @brief Sets the nth intrument in the bank [0..127] + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @param index Index of the instrument + * @param ins Instrument structure pointer + * @return 0 on success, <0 when any error has occurred + * + * This function allows to override an instrument on the fly + */ +extern int adl_setInstrument(struct ADL_MIDIPlayer *device, ADL_Bank *bank, unsigned index, const ADL_Instrument *ins); +/** + * @brief Loads the melodic or percussive part of the nth embedded bank + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @param num Number of embedded bank to load into the current bank array + * @return 0 on success, <0 when any error has occurred + */ +extern int adl_loadEmbeddedBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank, int num); + + + +/** + * @brief Sets number of 4-operator channels between all chips + * + * By default, it is automatically re-calculating every bank change. + * If you want to specify custom number of four operator channels, + * please call this function after bank change (adl_setBank() or adl_openBank()), + * otherwise, value will be overwritten by auto-calculated. + * + * @param device Instance of the library + * @param ops4 Count of four-op channels to allocate between all emulating chips + * @return 0 on success, <0 when any error has occurred + */ +extern int adl_setNumFourOpsChn(struct ADL_MIDIPlayer *device, int ops4); + +/** + * @brief Get current total count of 4-operator channels between all chips + * @param device Instance of the library + * @return 0 on success, <0 when any error has occurred + */ +extern int adl_getNumFourOpsChn(struct ADL_MIDIPlayer *device); + +/** + * @brief Override Enable(1) or Disable(0) AdLib percussion mode. -1 - use bank default AdLib percussion mode + * + * This function forces rhythm-mode on any bank. The result will work glitchy. + * + * @param device Instance of the library + * @param percmod 0 - disabled, 1 - enabled + */ +extern void adl_setPercMode(struct ADL_MIDIPlayer *device, int percmod); + +/** + * @brief Override Enable(1) or Disable(0) deep vibrato state. -1 - use bank default vibrato state + * @param device Instance of the library + * @param hvibro 0 - disabled, 1 - enabled + */ +extern void adl_setHVibrato(struct ADL_MIDIPlayer *device, int hvibro); + +/** + * @brief Override Enable(1) or Disable(0) deep tremolo state. -1 - use bank default tremolo state + * @param device Instance of the library + * @param htremo 0 - disabled, 1 - enabled + */ +extern void adl_setHTremolo(struct ADL_MIDIPlayer *device, int htremo); + +/** + * @brief Override Enable(1) or Disable(0) scaling of modulator volumes. -1 - use bank default scaling of modulator volumes + * @param device Instance of the library + * @param smod 0 - disabled, 1 - enabled + */ +extern void adl_setScaleModulators(struct ADL_MIDIPlayer *device, int smod); + +/** + * @brief Enable(1) or Disable(0) full-range brightness (MIDI CC74 used in XG music to filter result sounding) scaling + * + * By default, brightness affects sound between 0 and 64. + * When this option is enabled, the brightness will use full range from 0 up to 127. + * + * @param device Instance of the library + * @param fr_brightness 0 - disabled, 1 - enabled + */ +extern void adl_setFullRangeBrightness(struct ADL_MIDIPlayer *device, int fr_brightness); + +/** + * @brief Enable or disable built-in loop (built-in loop supports 'loopStart' and 'loopEnd' tags to loop specific part) + * @param device Instance of the library + * @param loopEn 0 - disabled, 1 - enabled + */ +extern void adl_setLoopEnabled(struct ADL_MIDIPlayer *device, int loopEn); + +/** + * @brief [DEPRECATED] Enable or disable Logarithmic volume changer + * + * This function is deprecated. Suggested replacement: `adl_setVolumeRangeModel` with `ADLMIDI_VolumeModel_NativeOPL3` volume model value; + */ +DEPRECATED(extern void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int logvol)); + +/** + * @brief Set different volume range model + * @param device Instance of the library + * @param volumeModel Volume model type (#ADLMIDI_VolumeModels) + */ +extern void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int volumeModel); + +/** + * @brief Load WOPL bank file from File System + * + * Is recommended to call adl_reset() to apply changes to already-loaded file player or real-time. + * + * @param device Instance of the library + * @param filePath Absolute or relative path to the WOPL bank file. UTF8 encoding is required, even on Windows. + * @return 0 on success, <0 when any error has occurred + */ +extern int adl_openBankFile(struct ADL_MIDIPlayer *device, const char *filePath); + +/** + * @brief Load WOPL bank file from memory data + * + * Is recommended to call adl_reset() to apply changes to already-loaded file player or real-time. + * + * @param device Instance of the library + * @param mem Pointer to memory block where is raw data of WOPL bank file is stored + * @param size Size of given memory block + * @return 0 on success, <0 when any error has occurred + */ +extern int adl_openBankData(struct ADL_MIDIPlayer *device, const void *mem, unsigned long size); + + +/** + * @brief [DEPRECATED] Dummy function + * + * This function is deprecated. Suggested replacement: `adl_chipEmulatorName` + * + * @return A string that contains a notice to use `adl_chipEmulatorName` instead of this function. + */ +DEPRECATED(extern const char *adl_emulatorName()); + +/** + * @brief Returns chip emulator name string + * @param device Instance of the library + * @return Understandable name of current OPL3 emulator + */ +extern const char *adl_chipEmulatorName(struct ADL_MIDIPlayer *device); + +/** + * @brief List of available OPL3 emulators + */ +enum ADL_Emulator +{ + /*! Nuked OPL3 v. 1.8 */ + ADLMIDI_EMU_NUKED = 0, + /*! Nuked OPL3 v. 1.7.4 */ + ADLMIDI_EMU_NUKED_174, + /*! DosBox */ + ADLMIDI_EMU_DOSBOX, + /*! Count instrument on the level */ + ADLMIDI_EMU_end +}; + +/** + * @brief Switch the emulation core + * @param device Instance of the library + * @param emulator Type of emulator (#ADL_Emulator) + * @return 0 on success, <0 when any error has occurred + */ +extern int adl_switchEmulator(struct ADL_MIDIPlayer *device, int emulator); + +/** + * @brief Library version context + */ +typedef struct { + ADL_UInt16 major; + ADL_UInt16 minor; + ADL_UInt16 patch; +} ADL_Version; + +/** + * @brief Run emulator with PCM rate to reduce CPU usage on slow devices. + * + * May decrease sounding accuracy on some chip emulators. + * + * @param device Instance of the library + * @param enabled 0 - disabled, 1 - enabled + * @return 0 on success, <0 when any error has occurred + */ +extern int adl_setRunAtPcmRate(struct ADL_MIDIPlayer *device, int enabled); + +/** + * @brief Set 4-bit device identifier. Used by the SysEx processor. + * @param device Instance of the library + * @param id 4-bit device identifier + * @return 0 on success, <0 when any error has occurred + */ +extern int adl_setDeviceIdentifier(struct ADL_MIDIPlayer *device, unsigned id); + +/** + * @section Information + */ + +/** + * @brief Returns string which contains a version number + * @return String which contains a version of the library + */ +extern const char *adl_linkedLibraryVersion(); + +/** + * @brief Returns structure which contains a version number of library + * @return Library version context structure which contains version number of the library + */ +extern const ADL_Version *adl_linkedVersion(); + + +/* ======== Error Info ======== */ + +/** + * @brief Returns string which contains last error message of initialization + * + * Don't use this function to get info on any function except of `adl_init`! + * Use `adl_errorInfo()` to get error information while workflow + * + * @return String with error message related to library initialization + */ +extern const char *adl_errorString(); + +/** + * @brief Returns string which contains last error message on specific device + * @param device Instance of the library + * @return String with error message related to last function call returned non-zero value. + */ +extern const char *adl_errorInfo(struct ADL_MIDIPlayer *device); + + + +/* ======== Initialization ======== */ + +/** + * @brief Initialize ADLMIDI Player device + * + * Tip 1: You can initialize multiple instances and run them in parallel + * Tip 2: Library is NOT thread-safe, therefore don't use same instance in different threads or use mutexes + * Tip 3: Changing of sample rate on the fly is not supported. Re-create the instance again. + * + * @param sample_rate Output sample rate + * @return Instance of the library. If NULL was returned, check the `adl_errorString` message for more info. + */ +extern struct ADL_MIDIPlayer *adl_init(long sample_rate); + +/** + * @brief Close and delete ADLMIDI device + * @param device Instance of the library + */ +extern void adl_close(struct ADL_MIDIPlayer *device); + + + +/* ======== MIDI Sequencer ======== */ + +/** + * @brief Load MIDI (or any other supported format) file from File System + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param filePath Absolute or relative path to the music file. UTF8 encoding is required, even on Windows. + * @return 0 on success, <0 when any error has occurred + */ +extern int adl_openFile(struct ADL_MIDIPlayer *device, const char *filePath); + +/** + * @brief Load MIDI (or any other supported format) file from memory data + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param mem Pointer to memory block where is raw data of music file is stored + * @param size Size of given memory block + * @return 0 on success, <0 when any error has occurred + */ +extern int adl_openData(struct ADL_MIDIPlayer *device, const void *mem, unsigned long size); + +/** + * @brief Resets MIDI player (per-channel setup) into initial state + * @param device Instance of the library + */ +extern void adl_reset(struct ADL_MIDIPlayer *device); + +/** + * @brief Get total time length of current song + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @return Total song length in seconds + */ +extern double adl_totalTimeLength(struct ADL_MIDIPlayer *device); + +/** + * @brief Get loop start time if presented. + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @return Time position in seconds of loop start point, or -1 when file has no loop points + */ +extern double adl_loopStartTime(struct ADL_MIDIPlayer *device); + +/** + * @brief Get loop endtime if presented. + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @return Time position in seconds of loop end point, or -1 when file has no loop points + */ +extern double adl_loopEndTime(struct ADL_MIDIPlayer *device); + +extern void adl_setCallback(struct ADL_MIDIPlayer *device, void (*AdlMidiCallback)(void)); + +/** + * @brief Get current time position in seconds + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @return Current time position in seconds + */ +extern double adl_positionTell(struct ADL_MIDIPlayer *device); + +/** + * @brief Jump to absolute time position in seconds + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param seconds Destination time position in seconds to seek + */ +extern void adl_positionSeek(struct ADL_MIDIPlayer *device, double seconds); + +/** + * @brief Reset MIDI track position to begin + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + */ +extern void adl_positionRewind(struct ADL_MIDIPlayer *device); + +/** + * @brief Set tempo multiplier + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param tempo Tempo multiplier value: 1.0 - original tempo, >1 - play faster, <1 - play slower + */ +extern void adl_setTempo(struct ADL_MIDIPlayer *device, double tempo); + +/** + * @brief Returns 1 if music position has reached end + * @param device Instance of the library + * @return 1 when end of sing has been reached, otherwise, 0 will be returned. <0 is returned on any error + */ +extern int adl_atEnd(struct ADL_MIDIPlayer *device); + +/** + * @brief Returns the number of tracks of the current sequence + * @param device Instance of the library + * @return Count of tracks in the current sequence + */ +extern size_t adl_trackCount(struct ADL_MIDIPlayer *device); + +/** + * @brief Track options + */ +enum ADLMIDI_TrackOptions +{ + /*! Enabled track */ + ADLMIDI_TrackOption_On = 1, + /*! Disabled track */ + ADLMIDI_TrackOption_Off = 2, + /*! Solo track */ + ADLMIDI_TrackOption_Solo = 3, +}; + +/** + * @brief Sets options on a track of the current sequence + * @param device Instance of the library + * @param trackNumber Identifier of the designated track. + * @return 0 on success, <0 when any error has occurred + */ +extern int adl_setTrackOptions(struct ADL_MIDIPlayer *device, size_t trackNumber, unsigned trackOptions); + + + +/* ======== Meta-Tags ======== */ + +/** + * @brief Returns string which contains a music title + * @param device Instance of the library + * @return A string that contains music title + */ +extern const char *adl_metaMusicTitle(struct ADL_MIDIPlayer *device); + +/** + * @brief Returns string which contains a copyright string* + * @param device Instance of the library + * @return A string that contains copyright notice, otherwise NULL + */ +extern const char *adl_metaMusicCopyright(struct ADL_MIDIPlayer *device); + +/** + * @brief Returns count of available track titles + * + * NOTE: There are CAN'T be associated with channel in any of event or note hooks + * + * @param device Instance of the library + * @return Count of available MIDI tracks, otherwise NULL + */ +extern size_t adl_metaTrackTitleCount(struct ADL_MIDIPlayer *device); + +/** + * @brief Get track title by index + * @param device Instance of the library + * @param index Index of the track to retreive the title + * @return A string that contains track title, otherwise NULL. + */ +extern const char *adl_metaTrackTitle(struct ADL_MIDIPlayer *device, size_t index); + +/** + * @brief MIDI Marker structure + */ +struct Adl_MarkerEntry +{ + /*! MIDI Marker title */ + const char *label; + /*! Absolute time position of the marker in seconds */ + double pos_time; + /*! Absolute time position of the marker in MIDI ticks */ + unsigned long pos_ticks; +}; + +/** + * @brief Returns count of available markers + * @param device Instance of the library + * @return Count of available MIDI markers + */ +extern size_t adl_metaMarkerCount(struct ADL_MIDIPlayer *device); + +/** + * @brief Returns the marker entry + * @param device Instance of the library + * @param index Index of the marker to retreive it. + * @return MIDI Marker description structure. + */ +extern struct Adl_MarkerEntry adl_metaMarker(struct ADL_MIDIPlayer *device, size_t index); + + + + +/* ======== Audio output Generation ======== */ + +/** + * @brief Generate PCM signed 16-bit stereo audio output and iterate MIDI timers + * + * Use this function when you are playing MIDI file loaded by `adl_openFile` or by `adl_openData` + * with using of built-in MIDI sequencer. + * + * Don't use count of frames, use instead count of samples. One frame is two samples. + * So, for example, if you want to take 10 frames, you must to request amount of 20 samples! + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param sampleCount Count of samples (not frames!) + * @param out Pointer to output with 16-bit stereo PCM output + * @return Count of given samples, otherwise, 0 or when catching an error while playing + */ +extern int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short *out); + +/** + * @brief Generate PCM stereo audio output in sample format declared by given context and iterate MIDI timers + * + * Use this function when you are playing MIDI file loaded by `adl_openFile` or by `adl_openData` + * with using of built-in MIDI sequencer. + * + * Don't use count of frames, use instead count of samples. One frame is two samples. + * So, for example, if you want to take 10 frames, you must to request amount of 20 samples! + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param sampleCount Count of samples (not frames!) + * @param left Left channel buffer output (Must be casted into bytes array) + * @param right Right channel buffer output (Must be casted into bytes array) + * @param format Destination PCM format format context + * @return Count of given samples, otherwise, 0 or when catching an error while playing + */ +extern int adl_playFormat(struct ADL_MIDIPlayer *device, int sampleCount, ADL_UInt8 *left, ADL_UInt8 *right, const struct ADLMIDI_AudioFormat *format); + +/** + * @brief Generate PCM signed 16-bit stereo audio output without iteration of MIDI timers + * + * Use this function when you are using library as Real-Time MIDI synthesizer or with + * an external MIDI sequencer. You must to request the amount of samples which is equal + * to the delta between of MIDI event rows. One MIDI row is a group of MIDI events + * are having zero delta/delay between each other. When you are receiving events in + * real time, request the minimal possible delay value. + * + * Don't use count of frames, use instead count of samples. One frame is two samples. + * So, for example, if you want to take 10 frames, you must to request amount of 20 samples! + * + * @param device Instance of the library + * @param sampleCount + * @param out Pointer to output with 16-bit stereo PCM output + * @return Count of given samples, otherwise, 0 or when catching an error while playing + */ +extern int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount, short *out); + +/** + * @brief Generate PCM stereo audio output in sample format declared by given context without iteration of MIDI timers + * + * Use this function when you are using library as Real-Time MIDI synthesizer or with + * an external MIDI sequencer. You must to request the amount of samples which is equal + * to the delta between of MIDI event rows. One MIDI row is a group of MIDI events + * are having zero delta/delay between each other. When you are receiving events in + * real time, request the minimal possible delay value. + * + * Don't use count of frames, use instead count of samples. One frame is two samples. + * So, for example, if you want to take 10 frames, you must to request amount of 20 samples! + * + * @param device Instance of the library + * @param sampleCount + * @param left Left channel buffer output (Must be casted into bytes array) + * @param right Right channel buffer output (Must be casted into bytes array) + * @param format Destination PCM format format context + * @return Count of given samples, otherwise, 0 or when catching an error while playing + */ +extern int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleCount, ADL_UInt8 *left, ADL_UInt8 *right, const struct ADLMIDI_AudioFormat *format); + +/** + * @brief Periodic tick handler. + * + * Notice: The function is provided to use it with Hardware OPL3 mode or for the purpose to iterate + * MIDI playback without of sound generation. + * + * DON'T USE IT TOGETHER WITH adl_play() and adl_playFormat() calls + * as there are all using this function internally!!! + * + * @param device Instance of the library + * @param seconds Previous delay. On a first moment, pass the `0.0` + * @param granulality Minimal size of one MIDI tick in seconds. + * @return desired number of seconds until next call. Pass this value into `seconds` field in next time + */ +extern double adl_tickEvents(struct ADL_MIDIPlayer *device, double seconds, double granulality); + + + + +/* ======== Real-Time MIDI ======== */ + +/** + * @brief Force Off all notes on all channels + * @param device Instance of the library + */ +extern void adl_panic(struct ADL_MIDIPlayer *device); + +/** + * @brief Reset states of all controllers on all MIDI channels + * @param device Instance of the library + */ +extern void adl_rt_resetState(struct ADL_MIDIPlayer *device); + +/** + * @brief Turn specific MIDI note ON + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param note Note number to on [Between 0 and 127] + * @param velocity Velocity level [Between 0 and 127] + * @return 1 when note was successfully started, 0 when note was rejected by any reason. + */ +extern int adl_rt_noteOn(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note, ADL_UInt8 velocity); + +/** + * @brief Turn specific MIDI note OFF + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param note Note number to off [Between 0 and 127] + */ +extern void adl_rt_noteOff(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note); + +/** + * @brief Set note after-touch + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param note Note number to affect by aftertouch event [Between 0 and 127] + * @param atVal After-Touch value [Between 0 and 127] + */ +extern void adl_rt_noteAfterTouch(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note, ADL_UInt8 atVal); + +/** + * @brief Set channel after-touch + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param atVal After-Touch level [Between 0 and 127] + */ +extern void adl_rt_channelAfterTouch(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 atVal); + +/** + * @brief Apply controller change + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param type Type of the controller [Between 0 and 255] + * @param value Value of the controller event [Between 0 and 127] + */ +extern void adl_rt_controllerChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 type, ADL_UInt8 value); + +/** + * @brief Apply patch change + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param patch Patch number [Between 0 and 127] + */ +extern void adl_rt_patchChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 patch); + +/** + * @brief Apply pitch bend change + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param pitch 24-bit pitch bend value + */ +extern void adl_rt_pitchBend(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt16 pitch); + +/** + * @brief Apply pitch bend change + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param msb MSB part of 24-bit pitch bend value + * @param lsb LSB part of 24-bit pitch bend value + */ +extern void adl_rt_pitchBendML(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 msb, ADL_UInt8 lsb); + +/** + * @brief Change LSB of the bank number (Alias to CC-32 event) + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param lsb LSB value of the MIDI bank number + */ +extern void adl_rt_bankChangeLSB(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 lsb); + +/** + * @brief Change MSB of the bank (Alias to CC-0 event) + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param msb MSB value of the MIDI bank number + */ +extern void adl_rt_bankChangeMSB(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 msb); + +/** + * @brief Change bank by absolute signed value + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param bank Bank number as concoctated signed 16-bit value of MSB and LSB parts. + */ + +extern void adl_rt_bankChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_SInt16 bank); + +/** + * @brief Perform a system exclusive message + * @param device Instance of the library + * @param msg Raw SysEx message buffer (must begin with 0xF0 and end with 0xF7) + * @param size Size of given SysEx message buffer + * @return 1 when SysEx message was successfully processed, 0 when SysEx message was rejected by any reason + */ +extern int adl_rt_systemExclusive(struct ADL_MIDIPlayer *device, const ADL_UInt8 *msg, size_t size); + + + + +/* ======== Hooks and debugging ======== */ + +/** + * @brief Raw event callback + * @param userdata Pointer to user data (usually, context of someting) + * @param type MIDI event type + * @param subtype MIDI event sub-type (special events only) + * @param channel MIDI channel + * @param data Raw event data + * @param len Length of event data + */ +typedef void (*ADL_RawEventHook)(void *userdata, ADL_UInt8 type, ADL_UInt8 subtype, ADL_UInt8 channel, const ADL_UInt8 *data, size_t len); + +/** + * @brief Note on/off callback + * @param userdata Pointer to user data (usually, context of someting) + * @param adlchn Chip channel where note was played + * @param note Note number [between 0 and 127] + * @param pressure Velocity level, or -1 when it's note off event + * @param bend Pitch bend offset value + */ +typedef void (*ADL_NoteHook)(void *userdata, int adlchn, int note, int ins, int pressure, double bend); + +/** + * @brief Debug messages callback + * @param userdata Pointer to user data (usually, context of someting) + * @param fmt Format strign output (in context of `printf()` standard function) + */ +typedef void (*ADL_DebugMessageHook)(void *userdata, const char *fmt, ...); + +/** + * @brief Set raw MIDI event hook + * @param device Instance of the library + * @param rawEventHook Pointer to the callback function which will be called on every MIDI event + * @param userData Pointer to user data which will be passed through the callback. + */ +extern void adl_setRawEventHook(struct ADL_MIDIPlayer *device, ADL_RawEventHook rawEventHook, void *userData); + +/** + * @brief Set note hook + * @param device Instance of the library + * @param noteHook Pointer to the callback function which will be called on every noteOn MIDI event + * @param userData Pointer to user data which will be passed through the callback. + */ +extern void adl_setNoteHook(struct ADL_MIDIPlayer *device, ADL_NoteHook noteHook, void *userData); + +/** + * @brief Set debug message hook + * @param device Instance of the library + * @param debugMessageHook Pointer to the callback function which will be called on every debug message + * @param userData Pointer to user data which will be passed through the callback. + */ +extern void adl_setDebugMessageHook(struct ADL_MIDIPlayer *device, ADL_DebugMessageHook debugMessageHook, void *userData); + +/** + * @brief Get a textual description of the channel state. For display only. + * @param device Instance of the library + * @param text Destination char buffer for channel usage state. Every entry is assigned to the chip channel. + * @param attr Destination char buffer for additional attributes like MIDI channel number that uses this chip channel. + * @param size Size of given buffers (both text and attr are must have same size!) + * @return 0 on success, <0 when any error has occurred + * + * Every character in the `text` buffer means the type of usage: + * ``` + * `-` - channel is unused (free) + * `+` - channel is used by two-operator voice + * `#` - channel is used by four-operator voice + * `@` - channel is used to play automatic arpeggio on chip channels overflow + * `r` - rhythm-mode channel note + * ``` + * + * The `attr` field receives the MIDI channel from which the chip channel is used. + * To get the valid MIDI channel you will need to apply the & 0x0F mask to every value. + */ +extern int adl_describeChannels(struct ADL_MIDIPlayer *device, char *text, char *attr, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* ADLMIDI_H */ diff --git a/engine/src/Libraries/adlmidi/include/adlmidi.hpp b/engine/src/Libraries/adlmidi/include/adlmidi.hpp new file mode 100644 index 0000000..6d01b8d --- /dev/null +++ b/engine/src/Libraries/adlmidi/include/adlmidi.hpp @@ -0,0 +1,52 @@ +/* + * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ADLMIDI_HPP +#define ADLMIDI_HPP + +struct ADL_MIDIPlayer; + +class AdlInstrumentTester +{ + struct Impl; + Impl *P; + +public: + explicit AdlInstrumentTester(ADL_MIDIPlayer *device); + virtual ~AdlInstrumentTester(); + + // Find list of adlib instruments that supposedly implement this GM + void FindAdlList(); + void Touch(unsigned c, unsigned volume); + void DoNote(int note); + void NextGM(int offset); + void NextAdl(int offset); + bool HandleInputChar(char ch); + +private: + AdlInstrumentTester(const AdlInstrumentTester &); + AdlInstrumentTester &operator=(const AdlInstrumentTester &); +}; + +#endif //ADLMIDI_HPP + diff --git a/engine/src/Libraries/adlmidi/midi_sequencer.h b/engine/src/Libraries/adlmidi/midi_sequencer.h new file mode 100644 index 0000000..c6069d7 --- /dev/null +++ b/engine/src/Libraries/adlmidi/midi_sequencer.h @@ -0,0 +1,134 @@ +/* + * BW_Midi_Sequencer - MIDI Sequencer for C++ + * + * Copyright (c) 2015-2018 Vitaly Novichkov + * + * 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. + */ + +#pragma once +#ifndef BISQUIT_AND_WOHLSTANDS_MIDI_SEQUENCER_HHHH +#define BISQUIT_AND_WOHLSTANDS_MIDI_SEQUENCER_HHHH + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** + \brief Real-Time MIDI interface between Sequencer and the Synthesizer + */ +typedef struct +{ + /*! Raw MIDI event hook */ + typedef void (*RawEventHook)(void *userdata, uint8_t type, uint8_t subtype, uint8_t channel, const uint8_t *data, size_t len); + /*! MIDI event hook which catches all MIDI events */ + RawEventHook onEvent; + /*! User data which will be passed through On-Event hook */ + void *onEvent_userData; + + /*! Library internal debug messages */ + typedef void (*DebugMessageHook)(void *userdata, const char *fmt, ...); + /*! Debug message hook */ + DebugMessageHook onDebugMessage; + /*! User data which will be passed through Debug Message hook */ + void *onDebugMessage_userData; + + /*! MIDI Run Time event calls user data */ + void *rtUserData; + + + /*************************************************** + * Standard MIDI events. All of them are required! * + ***************************************************/ + + /*! Note-On MIDI event */ + typedef void (*RtNoteOn)(void *userdata, uint8_t channel, uint8_t note, uint8_t velocity); + /*! Note-On MIDI event hook */ + RtNoteOn rt_noteOn; + + /*! Note-Off MIDI event */ + typedef void (*RtNoteOff)(void *userdata, uint8_t channel, uint8_t note); + /*! Note-Off MIDI event hook */ + RtNoteOff rt_noteOff; + + /*! Note aftertouch MIDI event */ + typedef void (*RtNoteAfterTouch)(void *userdata, uint8_t channel, uint8_t note, uint8_t atVal); + /*! Note aftertouch MIDI event hook */ + RtNoteAfterTouch rt_noteAfterTouch; + + /*! Channel aftertouch MIDI event */ + typedef void (*RtChannelAfterTouch)(void *userdata, uint8_t channel, uint8_t atVal); + /*! Channel aftertouch MIDI event hook */ + RtChannelAfterTouch rt_channelAfterTouch; + + /*! Controller change MIDI event */ + typedef void (*RtControlerChange)(void *userdata, uint8_t channel, uint8_t type, uint8_t value); + /*! Controller change MIDI event hook */ + RtControlerChange rt_controllerChange; + + /*! Patch change MIDI event */ + typedef void (*RtPatchChange)(void *userdata, uint8_t channel, uint8_t patch); + /*! Patch change MIDI event hook */ + RtPatchChange rt_patchChange; + + /*! Pitch bend MIDI event */ + typedef void (*RtPitchBend)(void *userdata, uint8_t channel, uint8_t msb, uint8_t lsb); + /*! Pitch bend MIDI event hook */ + RtPitchBend rt_pitchBend; + + /*! System Exclusive MIDI event */ + typedef void (*RtSysEx)(void *userdata, const uint8_t *msg, size_t size); + /*! System Exclusive MIDI event hook */ + RtSysEx rt_systemExclusive; + + + /******************* + * Optional events * + *******************/ + + /*! Device Switch MIDI event */ + typedef void (*RtDeviceSwitch)(void *userdata, size_t track, const char *data, size_t length); + /*! Device Switch MIDI event hook */ + RtDeviceSwitch rt_deviceSwitch; + + /*! Get the channels offset for current MIDI device */ + typedef size_t (*RtCurrentDevice)(void *userdata, size_t track); + /*! Get the channels offset for current MIDI device hook. Returms multiple to 16 value. */ + RtCurrentDevice rt_currentDevice; + + + /****************************************** + * NonStandard events. There are optional * + ******************************************/ + + /*! [Non-Standard] Pass raw OPL3 data to the chip (when playing IMF files) */ + typedef void (*RtRawOPL)(void *userdata, uint8_t reg, uint8_t value); + /*! [Non-Standard] Pass raw OPL3 data to the chip hook */ + RtRawOPL rt_rawOPL; + +} BW_MidiRtInterface; + +#ifdef __cplusplus +} +#endif + +#endif /* BISQUIT_AND_WOHLSTANDS_MIDI_SEQUENCER_HHHH */ diff --git a/engine/src/Libraries/adlmidi/midi_sequencer.hpp b/engine/src/Libraries/adlmidi/midi_sequencer.hpp new file mode 100644 index 0000000..80c6833 --- /dev/null +++ b/engine/src/Libraries/adlmidi/midi_sequencer.hpp @@ -0,0 +1,634 @@ +/* + * BW_Midi_Sequencer - MIDI Sequencer for C++ + * + * Copyright (c) 2015-2018 Vitaly Novichkov + * + * 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. + */ + +#pragma once +#ifndef BISQUIT_AND_WOHLSTANDS_MIDI_SEQUENCER_HHHHPPP +#define BISQUIT_AND_WOHLSTANDS_MIDI_SEQUENCER_HHHHPPP + +#include +#include + +#include "fraction.hpp" +#include "file_reader.hpp" +#include "midi_sequencer.h" + +//! Helper for unused values +#define BW_MidiSequencer_UNUSED(x) (void)x; + +class BW_MidiSequencer +{ + /** + * @brief MIDI Event utility container + */ + class MidiEvent + { + public: + MidiEvent(); + /** + * @brief Main MIDI event types + */ + enum Types + { + //! Unknown event + T_UNKNOWN = 0x00, + //! Note-Off event + T_NOTEOFF = 0x08,//size == 2 + //! Note-On event + T_NOTEON = 0x09,//size == 2 + //! Note After-Touch event + T_NOTETOUCH = 0x0A,//size == 2 + //! Controller change event + T_CTRLCHANGE = 0x0B,//size == 2 + //! Patch change event + T_PATCHCHANGE = 0x0C,//size == 1 + //! Channel After-Touch event + T_CHANAFTTOUCH = 0x0D,//size == 1 + //! Pitch-bend change event + T_WHEEL = 0x0E,//size == 2 + + //! System Exclusive message, type 1 + T_SYSEX = 0xF0,//size == len + //! Sys Com Song Position Pntr [LSB, MSB] + T_SYSCOMSPOSPTR = 0xF2,//size == 2 + //! Sys Com Song Select(Song #) [0-127] + T_SYSCOMSNGSEL = 0xF3,//size == 1 + //! System Exclusive message, type 2 + T_SYSEX2 = 0xF7,//size == len + //! Special event + T_SPECIAL = 0xFF + }; + /** + * @brief Special MIDI event sub-types + */ + enum SubTypes + { + //! Sequension number + ST_SEQNUMBER = 0x00,//size == 2 + //! Text label + ST_TEXT = 0x01,//size == len + //! Copyright notice + ST_COPYRIGHT = 0x02,//size == len + //! Sequence track title + ST_SQTRKTITLE = 0x03,//size == len + //! Instrument title + ST_INSTRTITLE = 0x04,//size == len + //! Lyrics text fragment + ST_LYRICS = 0x05,//size == len + //! MIDI Marker + ST_MARKER = 0x06,//size == len + //! Cue Point + ST_CUEPOINT = 0x07,//size == len + //! [Non-Standard] Device Switch + ST_DEVICESWITCH = 0x09,//size == len + //! MIDI Channel prefix + ST_MIDICHPREFIX = 0x20,//size == 1 + + //! End of Track event + ST_ENDTRACK = 0x2F,//size == 0 + //! Tempo change event + ST_TEMPOCHANGE = 0x51,//size == 3 + //! SMPTE offset + ST_SMPTEOFFSET = 0x54,//size == 5 + //! Time signature + ST_TIMESIGNATURE = 0x55, //size == 4 + //! Key signature + ST_KEYSIGNATURE = 0x59,//size == 2 + //! Sequencer specs + ST_SEQUENCERSPEC = 0x7F, //size == len + + /* Non-standard, internal ADLMIDI usage only */ + //! [Non-Standard] Loop Start point + ST_LOOPSTART = 0xE1,//size == 0 + //! [Non-Standard] Loop End point + ST_LOOPEND = 0xE2,//size == 0 + //! [Non-Standard] Raw OPL data + ST_RAWOPL = 0xE3,//size == 0 + + //! [Non-Standard] Loop Start point with support of multi-loops + ST_LOOPSTACK_BEGIN = 0xE4,//size == 1 + //! [Non-Standard] Loop End point with support of multi-loops + ST_LOOPSTACK_END = 0xE5,//size == 0 + //! [Non-Standard] Loop End point with support of multi-loops + ST_LOOPSTACK_BREAK = 0xE6,//size == 0 + }; + //! Main type of event + uint8_t type; + //! Sub-type of the event + uint8_t subtype; + //! Targeted MIDI channel + uint8_t channel; + //! Is valid event + uint8_t isValid; + //! Reserved 5 bytes padding + uint8_t __padding[4]; + //! Absolute tick position (Used for the tempo calculation only) + uint64_t absPosition; + //! Raw data of this event + std::vector data; + }; + + /** + * @brief A track position event contains a chain of MIDI events until next delay value + * + * Created with purpose to sort events by type in the same position + * (for example, to keep controllers always first than note on events or lower than note-off events) + */ + class MidiTrackRow + { + public: + MidiTrackRow(); + //! Clear MIDI row data + void clear(); + //! Absolute time position in seconds + double time; + //! Delay to next event in ticks + uint64_t delay; + //! Absolute position in ticks + uint64_t absPos; + //! Delay to next event in seconds + double timeDelay; + //! List of MIDI events in the current row + std::vector events; + /** + * @brief Sort events in this position + * @param noteStates Buffer of currently pressed/released note keys in the track + */ + void sortEvents(bool *noteStates = NULL); + }; + + /** + * @brief Tempo change point entry. Used in the MIDI data building function only. + */ + struct TempoChangePoint + { + uint64_t absPos; + fraction tempo; + }; + //P.S. I declared it here instead of local in-function because C++98 can't process templates with locally-declared structures + + typedef std::list MidiTrackQueue; + + /** + * @brief Song position context + */ + struct Position + { + //! Was track began playing + bool began; + //! Reserved + char __padding[7]; + //! Waiting time before next event in seconds + double wait; + //! Absolute time position on the track in seconds + double absTimePosition; + //! Track information + struct TrackInfo + { + //! Delay to next event in a track + uint64_t delay; + //! Last handled event type + int32_t lastHandledEvent; + //! Reserved + char __padding2[4]; + //! MIDI Events queue position iterator + MidiTrackQueue::iterator pos; + + TrackInfo() : + delay(0), + lastHandledEvent(0) + {} + }; + std::vector track; + Position(): began(false), wait(0.0), absTimePosition(0.0), track() + {} + }; + + //! MIDI Output interface context + const BW_MidiRtInterface *m_interface; + + /** + * @brief Build MIDI track data from the raw track data storage + * @return true if everything successfully processed, or false on any error + */ + bool buildTrackData(const std::vector > &trackData); + + /** + * @brief Parse one event from raw MIDI track stream + * @param [_inout] ptr pointer to pointer to current position on the raw data track + * @param [_in] end address to end of raw track data, needed to validate position and size + * @param [_inout] status status of the track processing + * @return Parsed MIDI event entry + */ + MidiEvent parseEvent(const uint8_t **ptr, const uint8_t *end, int &status); + + /** + * @brief Process MIDI events on the current tick moment + * @param isSeek is a seeking process + * @return returns false on reaching end of the song + */ + bool processEvents(bool isSeek = false); + + /** + * @brief Handle one event from the chain + * @param tk MIDI track + * @param evt MIDI event entry + * @param status Recent event type, -1 returned when end of track event was handled. + */ + void handleEvent(size_t tk, const MidiEvent &evt, int32_t &status); + +public: + + void (*MidiCallback)(void) = NULL; + + /** + * @brief MIDI marker entry + */ + struct MIDI_MarkerEntry + { + //! Label + std::string label; + //! Position time in seconds + double pos_time; + //! Position time in MIDI ticks + uint64_t pos_ticks; + }; + + /** + * @brief Container of one raw CMF instrument + */ + struct CmfInstrument + { + //! Raw CMF instrument data + uint8_t data[16]; + }; + + /** + * @brief The FileFormat enum + */ + enum FileFormat + { + //! MIDI format + Format_MIDI, + //! CMF format + Format_CMF, + //! Id-Software Music File + Format_IMF, + //! EA-MUS format + Format_RSXX, + //! AIL's XMIDI format (act same as MIDI, but with exceptions) + Format_XMIDI + }; + +private: + //! Music file format type. MIDI is default. + FileFormat m_format; + //! SMF format identifier. + unsigned m_smfFormat; + + //! Current position + Position m_currentPosition; + //! Track begin position + Position m_trackBeginPosition; + //! Loop start point + Position m_loopBeginPosition; + + //! Is looping enabled or not + bool m_loopEnabled; + + //! Full song length in seconds + double m_fullSongTimeLength; + //! Delay after song playd before rejecting the output stream requests + double m_postSongWaitDelay; + + //! Global loop start time + double m_loopStartTime; + //! Global loop end time + double m_loopEndTime; + + //! Pre-processed track data storage + std::vector m_trackData; + + //! CMF instruments + std::vector m_cmfInstruments; + + //! Title of music + std::string m_musTitle; + //! Copyright notice of music + std::string m_musCopyright; + //! List of track titles + std::vector m_musTrackTitles; + //! List of MIDI markers + std::vector m_musMarkers; + + //! Time of one tick + fraction m_invDeltaTicks; + //! Current tempo + fraction m_tempo; + + //! Tempo multiplier factor + double m_tempoMultiplier; + //! Is song at end + bool m_atEnd; + + /** + * @brief Loop stack entry + */ + struct LoopStackEntry + { + //! is infinite loop + bool infinity; + //! Count of loops left to break. <0 - infinite loop + int loops; + //! Start position snapshot to return back + Position startPosition; + //! Loop start tick + uint64_t start; + //! Loop end tick + uint64_t end; + }; + + struct LoopState + { + //! Loop start has reached + bool caughtStart; + //! Loop end has reached, reset on handling + bool caughtEnd; + + //! Loop start has reached + bool caughtStackStart; + //! Loop next has reached, reset on handling + bool caughtStackEnd; + //! Loop break has reached, reset on handling + bool caughtStackBreak; + //! Skip next stack loop start event handling + bool skipStackStart; + + //! Are loop points invalid? + bool invalidLoop; /*Loop points are invalid (loopStart after loopEnd or loopStart and loopEnd are on same place)*/ + + //! Stack of nested loops + std::vector stack; + //! Current level on the loop stack (<0 - out of loop, 0++ - the index in the loop stack) + int stackLevel; + + /** + * @brief Reset loop state to initial + */ + void reset() + { + caughtStart = false; + caughtEnd = false; + caughtStackStart = false; + caughtStackEnd = false; + caughtStackBreak = false; + skipStackStart = false; + } + + void fullReset() + { + reset(); + invalidLoop = false; + stack.clear(); + stackLevel = -1; + } + + bool isStackEnd() + { + if(caughtStackEnd && (stackLevel >= 0) && (stackLevel < static_cast(stack.size()))) + { + const LoopStackEntry &e = stack[stackLevel]; + if(e.infinity || (!e.infinity && e.loops > 0)) + return true; + } + return false; + } + + void stackUp(int count = 1) + { + stackLevel += count; + } + + void stackDown(int count = 1) + { + stackLevel -= count; + } + + LoopStackEntry &getCurStack() + { + if((stackLevel >= 0) && (stackLevel < static_cast(stack.size()))) + return stack[stackLevel]; + if(stack.empty()) + { + LoopStackEntry d; + d.loops = 0; + d.infinity = 0; + d.start = 0; + d.end = 0; + stack.push_back(d); + } + return stack[0]; + } + } m_loop; + + //! Whether the nth track has playback disabled + std::vector m_trackDisable; + //! Index of solo track, or max for disabled + size_t m_trackSolo; + + //! File parsing errors string (adding into m_errorString on aborting of the process) + std::string m_parsingErrorsString; + //! Common error string + std::string m_errorString; + +public: + BW_MidiSequencer(); + virtual ~BW_MidiSequencer(); + + /** + * @brief Sets the RT interface + * @param intrf Pre-Initialized interface structure (pointer will be taken) + */ + void setInterface(const BW_MidiRtInterface *intrf); + + /** + * @brief Returns file format type of currently loaded file + * @return File format type enumeration + */ + FileFormat getFormat(); + + /** + * @brief Returns the number of tracks + * @return Track count + */ + size_t getTrackCount() const; + + /** + * @brief Sets whether a track is playing + * @param track Track identifier + * @param enable Whether to enable track playback + * @return true on success, false if there was no such track + */ + bool setTrackEnabled(size_t track, bool enable); + + /** + * @brief Enables or disables solo on a track + * @param track Identifier of solo track, or max to disable + */ + void setSoloTrack(size_t track); + + /** + * @brief Get the list of CMF instruments (CMF only) + * @return Array of raw CMF instruments entries + */ + const std::vector getRawCmfInstruments(); + + /** + * @brief Get string that describes reason of error + * @return Error string + */ + const std::string &getErrorString(); + + /** + * @brief Check is loop enabled + * @return true if loop enabled + */ + bool getLoopEnabled(); + + /** + * @brief Switch loop on/off + * @param enabled Enable loop + */ + void setLoopEnabled(bool enabled); + + /** + * @brief Get music title + * @return music title string + */ + const std::string &getMusicTitle(); + + /** + * @brief Get music copyright notice + * @return music copyright notice string + */ + const std::string &getMusicCopyright(); + + /** + * @brief Get list of track titles + * @return array of track title strings + */ + const std::vector &getTrackTitles(); + + /** + * @brief Get list of MIDI markers + * @return Array of MIDI marker structures + */ + const std::vector &getMarkers(); + + /** + * @brief Is position of song at end + * @return true if end of song was reached + */ + bool positionAtEnd(); + + /** + * @brief Load MIDI file from path + * @param filename Path to file to open + * @return true if file successfully opened, false on any error + */ + bool loadMIDI(const std::string &filename); + + /** + * @brief Load MIDI file from a memory block + * @param data Pointer to memory block with MIDI data + * @param size Size of source memory block + * @return true if file successfully opened, false on any error + */ + bool loadMIDI(const void *data, size_t size); + + /** + * @brief Load MIDI file by using FileAndMemReader interface + * @param fr FileAndMemReader context with opened source file + * @return true if file successfully opened, false on any error + */ + bool loadMIDI(FileAndMemReader &fr); + + /** + * @brief Periodic tick handler. + * @param s seconds since last call + * @param granularity don't expect intervals smaller than this, in seconds + * @return desired number of seconds until next call + */ + double Tick(double s, double granularity); + + /** + * @brief Change current position to specified time position in seconds + * @param granularity don't expect intervals smaller than this, in seconds + * @param seconds Absolute time position in seconds + * @return desired number of seconds until next call of Tick() + */ + double seek(double seconds, const double granularity); + + /** + * @brief Gives current time position in seconds + * @return Current time position in seconds + */ + double tell(); + + /** + * @brief Gives time length of current song in seconds + * @return Time length of current song in seconds + */ + double timeLength(); + + /** + * @brief Gives loop start time position in seconds + * @return Loop start time position in seconds or -1 if song has no loop points + */ + double getLoopStart(); + + /** + * @brief Gives loop end time position in seconds + * @return Loop end time position in seconds or -1 if song has no loop points + */ + double getLoopEnd(); + + /** + * @brief Return to begin of current song + */ + void rewind(); + + /** + * @brief Get current tempor multiplier value + * @return + */ + double getTempoMultiplier(); + + /** + * @brief Set tempo multiplier + * @param tempo Tempo multiplier: 1.0 - original tempo. >1 - faster, <1 - slower + */ + void setTempo(double tempo); +}; + +#endif /* BISQUIT_AND_WOHLSTANDS_MIDI_SEQUENCER_HHHHPPP */ diff --git a/engine/src/Libraries/adlmidi/midi_sequencer_impl.hpp b/engine/src/Libraries/adlmidi/midi_sequencer_impl.hpp new file mode 100644 index 0000000..6da8489 --- /dev/null +++ b/engine/src/Libraries/adlmidi/midi_sequencer_impl.hpp @@ -0,0 +1,2180 @@ +/* + * BW_Midi_Sequencer - MIDI Sequencer for C++ + * + * Copyright (c) 2015-2018 Vitaly Novichkov + * + * 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. + */ + +#include "midi_sequencer.hpp" +#include +#include +#include +#include +#include // std::back_inserter +#include // std::copy +#include +#include + +#if defined(_WIN32) && !defined(__WATCOMC__) +# ifdef _MSC_VER +# ifdef _WIN64 +typedef __int64 ssize_t; +# else +typedef __int32 ssize_t; +# endif +# else +# ifdef _WIN64 +typedef int64_t ssize_t; +# else +typedef int32_t ssize_t; +# endif +# endif +#endif + +#ifndef BWMIDI_DISABLE_MUS_SUPPORT +#include "cvt_mus2mid.hpp" +#endif//MUS + +#ifndef BWMIDI_DISABLE_XMI_SUPPORT +#include "cvt_xmi2mid.hpp" +#endif//XMI + +/** + * @brief Utility function to read Big-Endian integer from raw binary data + * @param buffer Pointer to raw binary buffer + * @param nbytes Count of bytes to parse integer + * @return Extracted unsigned integer + */ +static inline uint64_t readBEint(const void *buffer, size_t nbytes) +{ + uint64_t result = 0; + const uint8_t *data = reinterpret_cast(buffer); + + for(size_t n = 0; n < nbytes; ++n) + result = (result << 8) + data[n]; + + return result; +} + +/** + * @brief Utility function to read Little-Endian integer from raw binary data + * @param buffer Pointer to raw binary buffer + * @param nbytes Count of bytes to parse integer + * @return Extracted unsigned integer + */ +static inline uint64_t readLEint(const void *buffer, size_t nbytes) +{ + uint64_t result = 0; + const uint8_t *data = reinterpret_cast(buffer); + + for(size_t n = 0; n < nbytes; ++n) + result = result + static_cast(data[n] << (n * 8)); + + return result; +} + +/** + * @brief Secure Standard MIDI Variable-Length numeric value parser with anti-out-of-range protection + * @param [_inout] ptr Pointer to memory block that contains begin of variable-length value, will be iterated forward + * @param [_in end Pointer to end of memory block where variable-length value is stored (after end of track) + * @param [_out] ok Reference to boolean which takes result of variable-length value parsing + * @return Unsigned integer that conains parsed variable-length value + */ +static inline uint64_t readVarLenEx(const uint8_t **ptr, const uint8_t *end, bool &ok) +{ + uint64_t result = 0; + ok = false; + + for(;;) + { + if(*ptr >= end) + return 2; + unsigned char byte = *((*ptr)++); + result = (result << 7) + (byte & 0x7F); + if(!(byte & 0x80)) + break; + } + + ok = true; + return result; +} + +BW_MidiSequencer::MidiEvent::MidiEvent() : + type(T_UNKNOWN), + subtype(T_UNKNOWN), + channel(0), + isValid(1), + absPosition(0) +{} + +BW_MidiSequencer::MidiTrackRow::MidiTrackRow() : + time(0.0), + delay(0), + absPos(0), + timeDelay(0.0) +{} + +void BW_MidiSequencer::MidiTrackRow::clear() +{ + time = 0.0; + delay = 0; + absPos = 0; + timeDelay = 0.0; + events.clear(); +} + +void BW_MidiSequencer::MidiTrackRow::sortEvents(bool *noteStates) +{ + typedef std::vector EvtArr; + EvtArr sysEx; + EvtArr metas; + EvtArr noteOffs; + EvtArr controllers; + EvtArr anyOther; + + for(size_t i = 0; i < events.size(); i++) + { + if(events[i].type == MidiEvent::T_NOTEOFF) + { + if(noteOffs.capacity() == 0) + noteOffs.reserve(events.size()); + noteOffs.push_back(events[i]); + } + else if(events[i].type == MidiEvent::T_SYSEX || + events[i].type == MidiEvent::T_SYSEX2) + { + if(sysEx.capacity() == 0) + sysEx.reserve(events.size()); + sysEx.push_back(events[i]); + } + else if((events[i].type == MidiEvent::T_CTRLCHANGE) + || (events[i].type == MidiEvent::T_PATCHCHANGE) + || (events[i].type == MidiEvent::T_WHEEL) + || (events[i].type == MidiEvent::T_CHANAFTTOUCH)) + { + if(controllers.capacity() == 0) + controllers.reserve(events.size()); + controllers.push_back(events[i]); + } + else if((events[i].type == MidiEvent::T_SPECIAL) && ( + (events[i].subtype == MidiEvent::ST_MARKER) || + (events[i].subtype == MidiEvent::ST_DEVICESWITCH) || + (events[i].subtype == MidiEvent::ST_LOOPSTART) || + (events[i].subtype == MidiEvent::ST_LOOPEND) || + (events[i].subtype == MidiEvent::ST_LOOPSTACK_BEGIN) || + (events[i].subtype == MidiEvent::ST_LOOPSTACK_END) || + (events[i].subtype == MidiEvent::ST_LOOPSTACK_BREAK) + )) + { + if(metas.capacity() == 0) + metas.reserve(events.size()); + metas.push_back(events[i]); + } + else + { + if(anyOther.capacity() == 0) + anyOther.reserve(events.size()); + anyOther.push_back(events[i]); + } + } + + /* + * If Note-Off and it's Note-On is on the same row - move this damned note off down! + */ + if(noteStates) + { + std::set markAsOn; + for(size_t i = 0; i < anyOther.size(); i++) + { + const MidiEvent e = anyOther[i]; + if(e.type == MidiEvent::T_NOTEON) + { + const size_t note_i = (e.channel * 255) + (e.data[0] & 0x7F); + //Check, was previously note is on or off + bool wasOn = noteStates[note_i]; + markAsOn.insert(note_i); + // Detect zero-length notes are following previously pressed note + int noteOffsOnSameNote = 0; + for(EvtArr::iterator j = noteOffs.begin(); j != noteOffs.end();) + { + //If note was off, and note-off on same row with note-on - move it down! + if( + ((*j).channel == e.channel) && + ((*j).data[0] == e.data[0]) + ) + { + //If note is already off OR more than one note-off on same row and same note + if(!wasOn || (noteOffsOnSameNote != 0)) + { + anyOther.push_back(*j); + j = noteOffs.erase(j); + markAsOn.erase(note_i); + continue; + } + else + { + //When same row has many note-offs on same row + //that means a zero-length note follows previous note + //it must be shuted down + noteOffsOnSameNote++; + } + } + j++; + } + } + } + + //Mark other notes as released + for(EvtArr::iterator j = noteOffs.begin(); j != noteOffs.end(); j++) + { + size_t note_i = (j->channel * 255) + (j->data[0] & 0x7F); + noteStates[note_i] = false; + } + + for(std::set::iterator j = markAsOn.begin(); j != markAsOn.end(); j++) + noteStates[*j] = true; + } + /***********************************************************************************/ + + events.clear(); + if(!sysEx.empty()) + events.insert(events.end(), sysEx.begin(), sysEx.end()); + if(!noteOffs.empty()) + events.insert(events.end(), noteOffs.begin(), noteOffs.end()); + if(!metas.empty()) + events.insert(events.end(), metas.begin(), metas.end()); + if(!controllers.empty()) + events.insert(events.end(), controllers.begin(), controllers.end()); + if(!anyOther.empty()) + events.insert(events.end(), anyOther.begin(), anyOther.end()); +} + +BW_MidiSequencer::BW_MidiSequencer() : + m_interface(NULL), + m_format(Format_MIDI), + m_smfFormat(0), + m_loopEnabled(false), + m_fullSongTimeLength(0.0), + m_postSongWaitDelay(1.0), + m_loopStartTime(-1.0), + m_loopEndTime(-1.0), + m_tempoMultiplier(1.0), + m_atEnd(false), + m_trackSolo(~(size_t)0) +{ + m_loop.reset(); + m_loop.invalidLoop = false; +} + +BW_MidiSequencer::~BW_MidiSequencer() +{} + +void BW_MidiSequencer::setInterface(const BW_MidiRtInterface *intrf) +{ + // Interface must NOT be NULL + assert(intrf); + + //Note ON hook is REQUIRED + assert(intrf->rt_noteOn); + //Note OFF hook is REQUIRED + assert(intrf->rt_noteOff); + //Note Aftertouch hook is REQUIRED + assert(intrf->rt_noteAfterTouch); + //Channel Aftertouch hook is REQUIRED + assert(intrf->rt_channelAfterTouch); + //Controller change hook is REQUIRED + assert(intrf->rt_controllerChange); + //Patch change hook is REQUIRED + assert(intrf->rt_patchChange); + //Pitch bend hook is REQUIRED + assert(intrf->rt_pitchBend); + //System Exclusive hook is REQUIRED + assert(intrf->rt_systemExclusive); + + m_interface = intrf; +} + +BW_MidiSequencer::FileFormat BW_MidiSequencer::getFormat() +{ + return m_format; +} + +size_t BW_MidiSequencer::getTrackCount() const +{ + return m_trackData.size(); +} + +bool BW_MidiSequencer::setTrackEnabled(size_t track, bool enable) +{ + size_t trackCount = m_trackData.size(); + if(track >= trackCount) + return false; + m_trackDisable[track] = !enable; + return true; +} + +void BW_MidiSequencer::setSoloTrack(size_t track) +{ + m_trackSolo = track; +} + +const std::vector BW_MidiSequencer::getRawCmfInstruments() +{ + return m_cmfInstruments; +} + +const std::string &BW_MidiSequencer::getErrorString() +{ + return m_errorString; +} + +bool BW_MidiSequencer::getLoopEnabled() +{ + return m_loopEnabled; +} + +void BW_MidiSequencer::setLoopEnabled(bool enabled) +{ + m_loopEnabled = enabled; +} + +const std::string &BW_MidiSequencer::getMusicTitle() +{ + return m_musTitle; +} + +const std::string &BW_MidiSequencer::getMusicCopyright() +{ + return m_musCopyright; +} + +const std::vector &BW_MidiSequencer::getTrackTitles() +{ + return m_musTrackTitles; +} + +const std::vector &BW_MidiSequencer::getMarkers() +{ + return m_musMarkers; +} + +bool BW_MidiSequencer::positionAtEnd() +{ + return m_atEnd; +} + +double BW_MidiSequencer::getTempoMultiplier() +{ + return m_tempoMultiplier; +} + +bool BW_MidiSequencer::buildTrackData(const std::vector > &trackData) +{ + m_fullSongTimeLength = 0.0; + m_loopStartTime = -1.0; + m_loopEndTime = -1.0; + m_trackDisable.clear(); + m_trackSolo = ~(size_t)0; + m_musTitle.clear(); + m_musCopyright.clear(); + m_musTrackTitles.clear(); + m_musMarkers.clear(); + m_trackData.clear(); + const size_t trackCount = trackData.size(); + m_trackData.resize(trackCount, MidiTrackQueue()); + m_trackDisable.resize(trackCount); + + m_loop.reset(); + m_loop.invalidLoop = false; + + bool gotGlobalLoopStart = false, + gotGlobalLoopEnd = false, + gotStackLoopStart = false, + gotLoopEventInThisRow = false; + //! Tick position of loop start tag + uint64_t loopStartTicks = 0; + //! Tick position of loop end tag + uint64_t loopEndTicks = 0; + //! Full length of song in ticks + uint64_t ticksSongLength = 0; + //! Cache for error message strign + char error[150]; + + m_currentPosition.track.clear(); + m_currentPosition.track.resize(trackCount); + + //! Caches note on/off states. + bool noteStates[16 * 255]; + /* This is required to carefully detect zero-length notes * + * and avoid a move of "note-off" event over "note-on" while sort. * + * Otherwise, after sort those notes will play infinite sound */ + + //Tempo change events + std::vector tempos; + + /* + * TODO: Make this be safer for memory in case of broken input data + * which may cause going away of available track data (and then give a crash!) + * + * POST: Check this more carefully for possible vulnuabilities are can crash this + */ + for(size_t tk = 0; tk < trackCount; ++tk) + { + uint64_t abs_position = 0; + int status = 0; + MidiEvent event; + bool ok = false; + const uint8_t *end = trackData[tk].data() + trackData[tk].size(); + const uint8_t *trackPtr = trackData[tk].data(); + std::memset(noteStates, 0, sizeof(noteStates)); + + //Time delay that follows the first event in the track + { + MidiTrackRow evtPos; + if(m_format == Format_RSXX) + ok = true; + else + evtPos.delay = readVarLenEx(&trackPtr, end, ok); + if(!ok) + { + int len = snprintf(error, 150, "buildTrackData: Can't read variable-length value at begin of track %d.\n", (int)tk); + if((len > 0) && (len < 150)) + m_parsingErrorsString += std::string(error, (size_t)len); + return false; + } + + //HACK: Begin every track with "Reset all controllers" event to avoid controllers state break came from end of song + for(uint8_t chan = 0; chan < 16; chan++) + { + MidiEvent event; + event.type = MidiEvent::T_CTRLCHANGE; + event.channel = chan; + event.data.push_back(121); + event.data.push_back(0); + evtPos.events.push_back(event); + } + + evtPos.absPos = abs_position; + abs_position += evtPos.delay; + m_trackData[tk].push_back(evtPos); + } + + MidiTrackRow evtPos; + do + { + event = parseEvent(&trackPtr, end, status); + if(!event.isValid) + { + int len = snprintf(error, 150, "buildTrackData: Fail to parse event in the track %d.\n", (int)tk); + if((len > 0) && (len < 150)) + m_parsingErrorsString += std::string(error, (size_t)len); + return false; + } + + evtPos.events.push_back(event); + if(event.type == MidiEvent::T_SPECIAL) + { + if(event.subtype == MidiEvent::ST_TEMPOCHANGE) + { + event.absPosition = abs_position; + tempos.push_back(event); + } + else if(!m_loop.invalidLoop && (event.subtype == MidiEvent::ST_LOOPSTART)) + { + /* + * loopStart is invalid when: + * - starts together with loopEnd + * - appears more than one time in same MIDI file + */ + if(gotGlobalLoopStart || gotLoopEventInThisRow) + m_loop.invalidLoop = true; + else + { + gotGlobalLoopStart = true; + loopStartTicks = abs_position; + } + //In this row we got loop event, register this! + gotLoopEventInThisRow = true; + } + else if(!m_loop.invalidLoop && (event.subtype == MidiEvent::ST_LOOPEND)) + { + /* + * loopEnd is invalid when: + * - starts before loopStart + * - starts together with loopStart + * - appars more than one time in same MIDI file + */ + if(gotGlobalLoopEnd || gotLoopEventInThisRow) + { + m_loop.invalidLoop = true; + if(m_interface->onDebugMessage) + { + m_interface->onDebugMessage( + m_interface->onDebugMessage_userData, + "== Invalid loop detected! %s %s ==", + (gotGlobalLoopEnd ? "[Caught more than 1 loopEnd!]" : ""), + (gotLoopEventInThisRow ? "[loopEnd in same row as loopStart!]" : "") + ); + } + } + else + { + gotGlobalLoopEnd = true; + loopEndTicks = abs_position; + } + // In this row we got loop event, register this! + gotLoopEventInThisRow = true; + } + else if(!m_loop.invalidLoop && (event.subtype == MidiEvent::ST_LOOPSTACK_BEGIN)) + { + if(!gotStackLoopStart) + { + if(!gotGlobalLoopStart) + loopStartTicks = abs_position; + gotStackLoopStart = true; + } + + m_loop.stackUp(); + if(m_loop.stackLevel >= static_cast(m_loop.stack.size())) + { + LoopStackEntry e; + e.loops = event.data[0]; + e.infinity = (event.data[0] == 0); + e.start = abs_position; + e.end = abs_position; + m_loop.stack.push_back(e); + } + } + else if(!m_loop.invalidLoop && + ((event.subtype == MidiEvent::ST_LOOPSTACK_END) || + (event.subtype == MidiEvent::ST_LOOPSTACK_BREAK)) + ) + { + if(m_loop.stackLevel <= -1) + { + m_loop.invalidLoop = true; // Caught loop end without of loop start! + if(m_interface->onDebugMessage) + { + m_interface->onDebugMessage( + m_interface->onDebugMessage_userData, + "== Invalid loop detected! [Caught loop end without of loop start] ==" + ); + } + } + else + { + if(loopEndTicks < abs_position) + loopEndTicks = abs_position; + m_loop.getCurStack().end = abs_position; + m_loop.stackDown(); + } + } + } + + if(event.subtype != MidiEvent::ST_ENDTRACK)//Don't try to read delta after EndOfTrack event! + { + evtPos.delay = readVarLenEx(&trackPtr, end, ok); + if(!ok) + { + /* End of track has been reached! However, there is no EOT event presented */ + event.type = MidiEvent::T_SPECIAL; + event.subtype = MidiEvent::ST_ENDTRACK; + } + } + + if((evtPos.delay > 0) || (event.subtype == MidiEvent::ST_ENDTRACK)) + { + evtPos.absPos = abs_position; + abs_position += evtPos.delay; + evtPos.sortEvents(noteStates); + m_trackData[tk].push_back(evtPos); + evtPos.clear(); + gotLoopEventInThisRow = false; + } + } + while((trackPtr <= end) && (event.subtype != MidiEvent::ST_ENDTRACK)); + + if(ticksSongLength < abs_position) + ticksSongLength = abs_position; + //Set the chain of events begin + if(m_trackData[tk].size() > 0) + m_currentPosition.track[tk].pos = m_trackData[tk].begin(); + } + + if(gotGlobalLoopStart && !gotGlobalLoopEnd) + { + gotGlobalLoopEnd = true; + loopEndTicks = ticksSongLength; + } + + //loopStart must be located before loopEnd! + if(loopStartTicks >= loopEndTicks) + { + m_loop.invalidLoop = true; + if(m_interface->onDebugMessage && (gotGlobalLoopStart || gotGlobalLoopEnd)) + { + m_interface->onDebugMessage( + m_interface->onDebugMessage_userData, + "== Invalid loop detected! [loopEnd is going before loopStart] ==" + ); + } + } + + /********************************************************************************/ + //Calculate time basing on collected tempo events + /********************************************************************************/ + for(size_t tk = 0; tk < trackCount; ++tk) + { + fraction currentTempo = m_tempo; + double time = 0.0; + uint64_t abs_position = 0; + size_t tempo_change_index = 0; + MidiTrackQueue &track = m_trackData[tk]; + if(track.empty()) + continue;//Empty track is useless! + +#ifdef DEBUG_TIME_CALCULATION + std::fprintf(stdout, "\n============Track %" PRIuPTR "=============\n", tk); + std::fflush(stdout); +#endif + + MidiTrackRow *posPrev = &(*(track.begin()));//First element + for(MidiTrackQueue::iterator it = track.begin(); it != track.end(); it++) + { +#ifdef DEBUG_TIME_CALCULATION + bool tempoChanged = false; +#endif + MidiTrackRow &pos = *it; + if((posPrev != &pos) && //Skip first event + (!tempos.empty()) && //Only when in-track tempo events are available + (tempo_change_index < tempos.size()) + ) + { + // If tempo event is going between of current and previous event + if(tempos[tempo_change_index].absPosition <= pos.absPos) + { + //Stop points: begin point and tempo change points are before end point + std::vector points; + fraction t; + TempoChangePoint firstPoint = {posPrev->absPos, currentTempo}; + points.push_back(firstPoint); + + //Collect tempo change points between previous and current events + do + { + TempoChangePoint tempoMarker; + MidiEvent &tempoPoint = tempos[tempo_change_index]; + tempoMarker.absPos = tempoPoint.absPosition; + tempoMarker.tempo = m_invDeltaTicks * fraction(readBEint(tempoPoint.data.data(), tempoPoint.data.size())); + points.push_back(tempoMarker); + tempo_change_index++; + } + while((tempo_change_index < tempos.size()) && + (tempos[tempo_change_index].absPosition <= pos.absPos)); + + // Re-calculate time delay of previous event + time -= posPrev->timeDelay; + posPrev->timeDelay = 0.0; + + for(size_t i = 0, j = 1; j < points.size(); i++, j++) + { + /* If one or more tempo events are appears between of two events, + * calculate delays between each tempo point, begin and end */ + uint64_t midDelay = 0; + //Delay between points + midDelay = points[j].absPos - points[i].absPos; + //Time delay between points + t = midDelay * currentTempo; + posPrev->timeDelay += t.value(); + + //Apply next tempo + currentTempo = points[j].tempo; +#ifdef DEBUG_TIME_CALCULATION + tempoChanged = true; +#endif + } + //Then calculate time between last tempo change point and end point + TempoChangePoint tailTempo = points.back(); + uint64_t postDelay = pos.absPos - tailTempo.absPos; + t = postDelay * currentTempo; + posPrev->timeDelay += t.value(); + + //Store Common time delay + posPrev->time = time; + time += posPrev->timeDelay; + } + } + + fraction t = pos.delay * currentTempo; + pos.timeDelay = t.value(); + pos.time = time; + time += pos.timeDelay; + + //Capture markers after time value calculation + for(size_t i = 0; i < pos.events.size(); i++) + { + MidiEvent &e = pos.events[i]; + if((e.type == MidiEvent::T_SPECIAL) && (e.subtype == MidiEvent::ST_MARKER)) + { + MIDI_MarkerEntry marker; + marker.label = std::string((char *)e.data.data(), e.data.size()); + marker.pos_ticks = pos.absPos; + marker.pos_time = pos.time; + m_musMarkers.push_back(marker); + } + } + + //Capture loop points time positions + if(!m_loop.invalidLoop) + { + // Set loop points times + if(loopStartTicks == pos.absPos) + m_loopStartTime = pos.time; + else if(loopEndTicks == pos.absPos) + m_loopEndTime = pos.time; + } + +#ifdef DEBUG_TIME_CALCULATION + std::fprintf(stdout, "= %10" PRId64 " = %10f%s\n", pos.absPos, pos.time, tempoChanged ? " <----TEMPO CHANGED" : ""); + std::fflush(stdout); +#endif + + abs_position += pos.delay; + posPrev = &pos; + } + + if(time > m_fullSongTimeLength) + m_fullSongTimeLength = time; + } + + m_fullSongTimeLength += m_postSongWaitDelay; + //Set begin of the music + m_trackBeginPosition = m_currentPosition; + //Initial loop position will begin at begin of track until passing of the loop point + m_loopBeginPosition = m_currentPosition; + + /********************************************************************************/ + //Resolve "hell of all times" of too short drum notes: + //move too short percussion note-offs far far away as possible + /********************************************************************************/ +#if 1 //Use this to record WAVEs for comparison before/after implementing of this + if(m_format == Format_MIDI)//Percussion fix is needed for MIDI only, not for IMF/RSXX or CMF + { + //! Minimal real time in seconds +#define DRUM_NOTE_MIN_TIME 0.03 + //! Minimal ticks count +#define DRUM_NOTE_MIN_TICKS 15 + struct NoteState + { + double delay; + uint64_t delayTicks; + bool isOn; + char ___pad[7]; + } drNotes[255]; + size_t banks[16]; + + for(size_t tk = 0; tk < trackCount; ++tk) + { + std::memset(drNotes, 0, sizeof(drNotes)); + std::memset(banks, 0, sizeof(banks)); + MidiTrackQueue &track = m_trackData[tk]; + if(track.empty()) + continue;//Empty track is useless! + + for(MidiTrackQueue::iterator it = track.begin(); it != track.end(); it++) + { + MidiTrackRow &pos = *it; + + for(ssize_t e = 0; e < (ssize_t)pos.events.size(); e++) + { + MidiEvent *et = &pos.events[(size_t)e]; + + /* Set MSB/LSB bank */ + if(et->type == MidiEvent::T_CTRLCHANGE) + { + uint8_t ctrlno = et->data[0]; + uint8_t value = et->data[1]; + switch(ctrlno) + { + case 0: // Set bank msb (GM bank) + banks[et->channel] = (value << 8) | (banks[et->channel] & 0x00FF); + break; + case 32: // Set bank lsb (XG bank) + banks[et->channel] = (banks[et->channel] & 0xFF00) | (value & 0x00FF); + break; + } + continue; + } + + bool percussion = (et->channel == 9) || + banks[et->channel] == 0x7E00 || //XG SFX1/SFX2 channel (16128 signed decimal) + banks[et->channel] == 0x7F00; //XG Percussion channel (16256 signed decimal) + if(!percussion) + continue; + + if(et->type == MidiEvent::T_NOTEON) + { + uint8_t note = et->data[0] & 0x7F; + NoteState &ns = drNotes[note]; + ns.isOn = true; + ns.delay = 0.0; + ns.delayTicks = 0; + } + else if(et->type == MidiEvent::T_NOTEOFF) + { + uint8_t note = et->data[0] & 0x7F; + NoteState &ns = drNotes[note]; + if(ns.isOn) + { + ns.isOn = false; + if(ns.delayTicks < DRUM_NOTE_MIN_TICKS || ns.delay < DRUM_NOTE_MIN_TIME)//If note is too short + { + //Move it into next event position if that possible + for(MidiTrackQueue::iterator itNext = it; + itNext != track.end(); + itNext++) + { + MidiTrackRow &posN = *itNext; + if(ns.delayTicks > DRUM_NOTE_MIN_TICKS && ns.delay > DRUM_NOTE_MIN_TIME) + { + //Put note-off into begin of next event list + posN.events.insert(posN.events.begin(), pos.events[(size_t)e]); + //Renive this event from a current row + pos.events.erase(pos.events.begin() + (int)e); + e--; + break; + } + ns.delay += posN.timeDelay; + ns.delayTicks += posN.delay; + } + } + ns.delay = 0.0; + ns.delayTicks = 0; + } + } + } + + //Append time delays to sustaining notes + for(size_t no = 0; no < 128; no++) + { + NoteState &ns = drNotes[no]; + if(ns.isOn) + { + ns.delay += pos.timeDelay; + ns.delayTicks += pos.delay; + } + } + } + } +#undef DRUM_NOTE_MIN_TIME +#undef DRUM_NOTE_MIN_TICKS + } +#endif + + return true; +} + +bool BW_MidiSequencer::processEvents(bool isSeek) +{ + if(m_currentPosition.track.size() == 0) + m_atEnd = true;//No MIDI track data to play + if(m_atEnd) + return false;//No more events in the queue + + m_loop.caughtEnd = false; + const size_t TrackCount = m_currentPosition.track.size(); + const Position rowBeginPosition(m_currentPosition); + bool doLoopJump = false; + unsigned caughLoopStart = 0; + unsigned caughLoopStackStart = 0; + unsigned caughLoopStackEnds = 0; + unsigned caughLoopStackBreaks = 0; + +#ifdef DEBUG_TIME_CALCULATION + double maxTime = 0.0; +#endif + + for(size_t tk = 0; tk < TrackCount; ++tk) + { + Position::TrackInfo &track = m_currentPosition.track[tk]; + if((track.lastHandledEvent >= 0) && (track.delay <= 0)) + { + //Check is an end of track has been reached + if(track.pos == m_trackData[tk].end()) + { + track.lastHandledEvent = -1; + break; + } + + // Handle event + for(size_t i = 0; i < track.pos->events.size(); i++) + { + const MidiEvent &evt = track.pos->events[i]; +#ifdef ENABLE_BEGIN_SILENCE_SKIPPING + if(!m_currentPosition.began && (evt.type == MidiEvent::T_NOTEON)) + m_currentPosition.began = true; +#endif + if(isSeek && (evt.type == MidiEvent::T_NOTEON)) + continue; + handleEvent(tk, evt, track.lastHandledEvent); + + if(m_loop.caughtStart) + { + caughLoopStart++; + m_loop.caughtStart = false; + } + + if(m_loop.caughtStackStart) + { + caughLoopStackStart++; + m_loop.caughtStackStart = false; + } + + if(m_loop.caughtStackBreak) + { + caughLoopStackBreaks++; + m_loop.caughtStackBreak = false; + } + + if(m_loop.caughtEnd || m_loop.isStackEnd() || m_loop.caughtStackEnd) + { + if(m_loop.caughtStackEnd) + { + m_loop.caughtStackEnd = false; + caughLoopStackEnds++; + } + doLoopJump = true; + break;//Stop event handling on catching loopEnd event! + } + } + +#ifdef DEBUG_TIME_CALCULATION + if(maxTime < track.pos->time) + maxTime = track.pos->time; +#endif + // Read next event time (unless the track just ended) + if(track.lastHandledEvent >= 0) + { + track.delay += track.pos->delay; + track.pos++; + } + + if(doLoopJump) + break; + } + } + +#ifdef DEBUG_TIME_CALCULATION + std::fprintf(stdout, " \r"); + std::fprintf(stdout, "Time: %10f; Audio: %10f\r", maxTime, m_currentPosition.absTimePosition); + std::fflush(stdout); +#endif + + // Find shortest delay from all track + uint64_t shortest = 0; + bool shortest_no = true; + + for(size_t tk = 0; tk < TrackCount; ++tk) + { + Position::TrackInfo &track = m_currentPosition.track[tk]; + if((track.lastHandledEvent >= 0) && (shortest_no || track.delay < shortest)) + { + shortest = track.delay; + shortest_no = false; + } + } + + //if(shortest > 0) UI.PrintLn("shortest: %ld", shortest); + + // Schedule the next playevent to be processed after that delay + for(size_t tk = 0; tk < TrackCount; ++tk) + m_currentPosition.track[tk].delay -= shortest; + + fraction t = shortest * m_tempo; + +#ifdef ENABLE_BEGIN_SILENCE_SKIPPING + if(m_currentPosition.began) +#endif + m_currentPosition.wait += t.value(); + + //if(shortest > 0) UI.PrintLn("Delay %ld (%g)", shortest, (double)t.valuel()); + if(caughLoopStart > 0) + m_loopBeginPosition = rowBeginPosition; + + if(caughLoopStackStart > 0) + { + while(caughLoopStackStart > 0) + { + m_loop.stackUp(); + LoopStackEntry &s = m_loop.getCurStack(); + s.startPosition = rowBeginPosition; + caughLoopStackStart--; + } + return true; + } + + if(caughLoopStackBreaks > 0) + { + while(caughLoopStackBreaks > 0) + { + LoopStackEntry &s = m_loop.getCurStack(); + s.loops = 0; + s.infinity = false; + // Quit the loop + m_loop.stackDown(); + caughLoopStackBreaks--; + } + } + + if(caughLoopStackEnds > 0) + { + while(caughLoopStackEnds > 0) + { + LoopStackEntry &s = m_loop.getCurStack(); + if(s.infinity) + { + m_currentPosition = s.startPosition; + m_loop.skipStackStart = true; + return true; + } + else + if(s.loops >= 0) + { + s.loops--; + if(s.loops > 0) + { + m_currentPosition = s.startPosition; + m_loop.skipStackStart = true; + return true; + } + else + { + // Quit the loop + m_loop.stackDown(); + } + } + else + { + // Quit the loop + m_loop.stackDown(); + } + caughLoopStackEnds--; + } + + return true; + } + + if(shortest_no || m_loop.caughtEnd) + { + //Loop if song end or loop end point has reached + m_loop.caughtEnd = false; + shortest = 0; + if(!m_loopEnabled) + { + m_atEnd = true; //Don't handle events anymore + m_currentPosition.wait += m_postSongWaitDelay;//One second delay until stop playing + return true;//We have caugh end here! + } + m_currentPosition = m_loopBeginPosition; + } + + return true;//Has events in queue +} + +BW_MidiSequencer::MidiEvent BW_MidiSequencer::parseEvent(const uint8_t **pptr, const uint8_t *end, int &status) +{ + const uint8_t *&ptr = *pptr; + BW_MidiSequencer::MidiEvent evt; + + if(ptr + 1 > end) + { + //When track doesn't ends on the middle of event data, it's must be fine + evt.type = MidiEvent::T_SPECIAL; + evt.subtype = MidiEvent::ST_ENDTRACK; + return evt; + } + + unsigned char byte = *(ptr++); + bool ok = false; + + if(byte == MidiEvent::T_SYSEX || byte == MidiEvent::T_SYSEX2)// Ignore SysEx + { + uint64_t length = readVarLenEx(pptr, end, ok); + if(!ok || (ptr + length > end)) + { + m_parsingErrorsString += "parseEvent: Can't read SysEx event - Unexpected end of track data.\n"; + evt.isValid = 0; + return evt; + } + evt.type = MidiEvent::T_SYSEX; + evt.data.clear(); + evt.data.push_back(byte); + std::copy(ptr, ptr + length, std::back_inserter(evt.data)); + ptr += (size_t)length; + return evt; + } + + if(byte == MidiEvent::T_SPECIAL) + { + // Special event FF + uint8_t evtype = *(ptr++); + uint64_t length = readVarLenEx(pptr, end, ok); + if(!ok || (ptr + length > end)) + { + m_parsingErrorsString += "parseEvent: Can't read Special event - Unexpected end of track data.\n"; + evt.isValid = 0; + return evt; + } + std::string data(length ? (const char *)ptr : 0, (size_t)length); + ptr += (size_t)length; + + evt.type = byte; + evt.subtype = evtype; + evt.data.insert(evt.data.begin(), data.begin(), data.end()); + +#if 0 /* Print all tempo events */ + if(evt.subtype == MidiEvent::ST_TEMPOCHANGE) + { + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "Temp Change: %02X%02X%02X", evt.data[0], evt.data[1], evt.data[2]); + } +#endif + + /* TODO: Store those meta-strings separately and give ability to read them + * by external functions (to display song title and copyright in the player) */ + if(evt.subtype == MidiEvent::ST_COPYRIGHT) + { + if(m_musCopyright.empty()) + { + m_musCopyright = std::string((const char *)evt.data.data(), evt.data.size()); + if(m_interface->onDebugMessage) + m_interface->onDebugMessage(m_interface->onDebugMessage_userData, "Music copyright: %s", m_musCopyright.c_str()); + } + else if(m_interface->onDebugMessage) + { + std::string str((const char *)evt.data.data(), evt.data.size()); + m_interface->onDebugMessage(m_interface->onDebugMessage_userData, "Extra copyright event: %s", str.c_str()); + } + } + else if(evt.subtype == MidiEvent::ST_SQTRKTITLE) + { + if(m_musTitle.empty()) + { + m_musTitle = std::string((const char *)evt.data.data(), evt.data.size()); + if(m_interface->onDebugMessage) + m_interface->onDebugMessage(m_interface->onDebugMessage_userData, "Music title: %s", m_musTitle.c_str()); + } + else if(m_interface->onDebugMessage) + { + //TODO: Store track titles and associate them with each track and make API to retreive them + std::string str((const char *)evt.data.data(), evt.data.size()); + m_musTrackTitles.push_back(str); + m_interface->onDebugMessage(m_interface->onDebugMessage_userData, "Track title: %s", str.c_str()); + } + } + else if(evt.subtype == MidiEvent::ST_INSTRTITLE) + { + if(m_interface->onDebugMessage) + { + std::string str((const char *)evt.data.data(), evt.data.size()); + m_interface->onDebugMessage(m_interface->onDebugMessage_userData, "Instrument: %s", str.c_str()); + } + } + else if(evt.subtype == MidiEvent::ST_MARKER) + { + //To lower + for(size_t i = 0; i < data.size(); i++) + { + if(data[i] <= 'Z' && data[i] >= 'A') + data[i] = static_cast(data[i] - ('Z' - 'z')); + } + + if(data == "loopstart") + { + //Return a custom Loop Start event instead of Marker + evt.subtype = MidiEvent::ST_LOOPSTART; + evt.data.clear();//Data is not needed + return evt; + } + + if(data == "loopend") + { + //Return a custom Loop End event instead of Marker + evt.subtype = MidiEvent::ST_LOOPEND; + evt.data.clear();//Data is not needed + return evt; + } + + if(!data.compare(0, 10, "loopstart=")) + { + evt.type = MidiEvent::T_SPECIAL; + evt.subtype = MidiEvent::ST_LOOPSTACK_BEGIN; + uint8_t loops = static_cast(atoi(data.substr(10).c_str())); + evt.data.clear(); + evt.data.push_back(loops); + + if(m_interface->onDebugMessage) + { + m_interface->onDebugMessage( + m_interface->onDebugMessage_userData, + "Stack Marker Loop Start at %d to %d level with %d loops", + m_loop.stackLevel, + m_loop.stackLevel + 1, + loops + ); + } + return evt; + } + + if(!data.compare(0, 8, "loopend=")) + { + evt.type = MidiEvent::T_SPECIAL; + evt.subtype = MidiEvent::ST_LOOPSTACK_END; + evt.data.clear(); + + if(m_interface->onDebugMessage) + { + m_interface->onDebugMessage( + m_interface->onDebugMessage_userData, + "Stack Marker Loop %s at %d to %d level", + (evt.subtype == MidiEvent::ST_LOOPSTACK_END ? "End" : "Break"), + m_loop.stackLevel, + m_loop.stackLevel - 1 + ); + } + return evt; + } + } + + if(evtype == MidiEvent::ST_ENDTRACK) + status = -1;//Finalize track + + return evt; + } + + // Any normal event (80..EF) + if(byte < 0x80) + { + byte = static_cast(status | 0x80); + ptr--; + } + + //Sys Com Song Select(Song #) [0-127] + if(byte == MidiEvent::T_SYSCOMSNGSEL) + { + if(ptr + 1 > end) + { + m_parsingErrorsString += "parseEvent: Can't read System Command Song Select event - Unexpected end of track data.\n"; + evt.isValid = 0; + return evt; + } + evt.type = byte; + evt.data.push_back(*(ptr++)); + return evt; + } + + //Sys Com Song Position Pntr [LSB, MSB] + if(byte == MidiEvent::T_SYSCOMSPOSPTR) + { + if(ptr + 2 > end) + { + m_parsingErrorsString += "parseEvent: Can't read System Command Position Pointer event - Unexpected end of track data.\n"; + evt.isValid = 0; + return evt; + } + evt.type = byte; + evt.data.push_back(*(ptr++)); + evt.data.push_back(*(ptr++)); + return evt; + } + + uint8_t midCh = byte & 0x0F, evType = (byte >> 4) & 0x0F; + status = byte; + evt.channel = midCh; + evt.type = evType; + + switch(evType) + { + case MidiEvent::T_NOTEOFF://2 byte length + case MidiEvent::T_NOTEON: + case MidiEvent::T_NOTETOUCH: + case MidiEvent::T_CTRLCHANGE: + case MidiEvent::T_WHEEL: + if(ptr + 2 > end) + { + m_parsingErrorsString += "parseEvent: Can't read regular 2-byte event - Unexpected end of track data.\n"; + evt.isValid = 0; + return evt; + } + + evt.data.push_back(*(ptr++)); + evt.data.push_back(*(ptr++)); + + if((evType == MidiEvent::T_NOTEON) && (evt.data[1] == 0)) + { + evt.type = MidiEvent::T_NOTEOFF; // Note ON with zero velocity is Note OFF! + } + else + if(evType == MidiEvent::T_CTRLCHANGE) + { + //111'th loopStart controller (RPG Maker and others) + if((m_format == Format_MIDI) && (evt.data[0] == 111)) + { + //Change event type to custom Loop Start event and clear data + evt.type = MidiEvent::T_SPECIAL; + evt.subtype = MidiEvent::ST_LOOPSTART; + evt.data.clear(); + } + + if(m_format == Format_XMIDI) + { + if(evt.data[0] == 116) + { + evt.type = MidiEvent::T_SPECIAL; + evt.subtype = MidiEvent::ST_LOOPSTACK_BEGIN; + evt.data[0] = evt.data[1]; + evt.data.pop_back(); + + if(m_interface->onDebugMessage) + { + m_interface->onDebugMessage( + m_interface->onDebugMessage_userData, + "Stack XMI Loop Start at %d to %d level with %d loops", + m_loop.stackLevel, + m_loop.stackLevel + 1, + evt.data[0] + ); + } + } + + if(evt.data[0] == 117) + { + evt.type = MidiEvent::T_SPECIAL; + evt.subtype = evt.data[1] < 64 ? + MidiEvent::ST_LOOPSTACK_BREAK : + MidiEvent::ST_LOOPSTACK_END; + evt.data.clear(); + + if(m_interface->onDebugMessage) + { + m_interface->onDebugMessage( + m_interface->onDebugMessage_userData, + "Stack XMI Loop %s at %d to %d level", + (evt.subtype == MidiEvent::ST_LOOPSTACK_END ? "End" : "Break"), + m_loop.stackLevel, + m_loop.stackLevel - 1 + ); + } + } + } + } + + return evt; + case MidiEvent::T_PATCHCHANGE://1 byte length + case MidiEvent::T_CHANAFTTOUCH: + if(ptr + 1 > end) + { + m_parsingErrorsString += "parseEvent: Can't read regular 1-byte event - Unexpected end of track data.\n"; + evt.isValid = 0; + return evt; + } + evt.data.push_back(*(ptr++)); + return evt; + } + + return evt; +} + +void BW_MidiSequencer::handleEvent(size_t track, const BW_MidiSequencer::MidiEvent &evt, int32_t &status) +{ + + // CC: Call the callback for XMI Controller + if(track == 0 && (evt.type == 0xB || evt.type == 0xC) && (evt.data[1] == 119)) + { + if(MidiCallback != NULL) { + MidiCallback(); + } + return; + } + + if(track == 0 && m_smfFormat < 2 && evt.type == MidiEvent::T_SPECIAL && + (evt.subtype == MidiEvent::ST_TEMPOCHANGE || evt.subtype == MidiEvent::ST_TIMESIGNATURE)) + { + /* never reject track 0 timing events on SMF format != 2 + note: multi-track XMI convert to format 2 SMF */ + } + else + { + if(m_trackSolo != ~(size_t)0 && track != m_trackSolo) + return; + if(m_trackDisable[track]) + return; + } + + if(m_interface->onEvent) + { + m_interface->onEvent(m_interface->onEvent_userData, + evt.type, evt.subtype, evt.channel, + evt.data.data(), evt.data.size()); + } + + if(evt.type == MidiEvent::T_SYSEX || evt.type == MidiEvent::T_SYSEX2) // Ignore SysEx + { + //std::string data( length?(const char*) &TrackData[track][CurrentPosition.track[track].ptr]:0, length ); + //UI.PrintLn("SysEx %02X: %u bytes", byte, length/*, data.c_str()*/); +#if 0 + std::fputs("SysEx:", stderr); + for(size_t i = 0; i < evt.data.size(); ++i) + std::fprintf(stderr, " %02X", evt.data[i]); + std::fputc('\n', stderr); +#endif + m_interface->rt_systemExclusive(m_interface->rtUserData, evt.data.data(), evt.data.size()); + return; + } + + if(evt.type == MidiEvent::T_SPECIAL) + { + // Special event FF + uint8_t evtype = evt.subtype; + uint64_t length = (uint64_t)evt.data.size(); + const char *data(length ? (const char *)evt.data.data() : ""); + + if(evtype == MidiEvent::ST_ENDTRACK)//End Of Track + { + status = -1; + return; + } + + if(evtype == MidiEvent::ST_TEMPOCHANGE)//Tempo change + { + m_tempo = m_invDeltaTicks * fraction(readBEint(evt.data.data(), evt.data.size())); + return; + } + + if(evtype == MidiEvent::ST_MARKER)//Meta event + { + //Do nothing! :-P + return; + } + + if(evtype == MidiEvent::ST_DEVICESWITCH) + { + if(m_interface->onDebugMessage) + m_interface->onDebugMessage(m_interface->onDebugMessage_userData, "Switching another device: %s", data); + if(m_interface->rt_deviceSwitch) + m_interface->rt_deviceSwitch(m_interface->rtUserData, track, data, length); + return; + } + + //if(evtype >= 1 && evtype <= 6) + // UI.PrintLn("Meta %d: %s", evtype, data.c_str()); + + //Turn on Loop handling when loop is enabled + if(m_loopEnabled && !m_loop.invalidLoop) + { + if(evtype == MidiEvent::ST_LOOPSTART) // Special non-spec MIDI loop Start point + { + m_loop.caughtStart = true; + return; + } + + if(evtype == MidiEvent::ST_LOOPEND) // Special non-spec MIDI loop End point + { + m_loop.caughtEnd = true; + return; + } + + if(evtype == MidiEvent::ST_LOOPSTACK_BEGIN) + { + if(m_loop.skipStackStart) + { + m_loop.skipStackStart = false; + return; + } + + LoopStackEntry &s = m_loop.stack[(m_loop.stackLevel + 1)]; + s.loops = (int)data[0]; + s.infinity = (data[0] == 0); + m_loop.caughtStackStart = true; + return; + } + + if(evtype == MidiEvent::ST_LOOPSTACK_END) + { + m_loop.caughtStackEnd = true; + return; + } + + if(evtype == MidiEvent::ST_LOOPSTACK_BREAK) + { + m_loop.caughtStackBreak = true; + return; + } + } + + if(evtype == MidiEvent::ST_RAWOPL) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib + { + if(m_interface->rt_rawOPL) + m_interface->rt_rawOPL(m_interface->rtUserData, static_cast(data[0]), static_cast(data[1])); + return; + } + + return; + } + + // Any normal event (80..EF) + // if(evt.type < 0x80) + // { + // byte = static_cast(CurrentPosition.track[track].status | 0x80); + // CurrentPosition.track[track].ptr--; + // } + + if(evt.type == MidiEvent::T_SYSCOMSNGSEL || + evt.type == MidiEvent::T_SYSCOMSPOSPTR) + return; + + /*UI.PrintLn("@%X Track %u: %02X %02X", + CurrentPosition.track[track].ptr-1, (unsigned)track, byte, + TrackData[track][CurrentPosition.track[track].ptr]);*/ + size_t midCh = evt.channel;//byte & 0x0F, EvType = byte >> 4; + if(m_interface->rt_currentDevice) + midCh += m_interface->rt_currentDevice(m_interface->rtUserData, track); + status = evt.type; + + switch(evt.type) + { + case MidiEvent::T_NOTEOFF: // Note off + { + uint8_t note = evt.data[0]; + m_interface->rt_noteOff(m_interface->rtUserData, static_cast(midCh), note); + break; + } + + case MidiEvent::T_NOTEON: // Note on + { + uint8_t note = evt.data[0]; + uint8_t vol = evt.data[1]; + m_interface->rt_noteOn(m_interface->rtUserData, static_cast(midCh), note, vol); + break; + } + + case MidiEvent::T_NOTETOUCH: // Note touch + { + uint8_t note = evt.data[0]; + uint8_t vol = evt.data[1]; + m_interface->rt_noteAfterTouch(m_interface->rtUserData, static_cast(midCh), note, vol); + break; + } + + case MidiEvent::T_CTRLCHANGE: // Controller change + { + uint8_t ctrlno = evt.data[0]; + uint8_t value = evt.data[1]; + m_interface->rt_controllerChange(m_interface->rtUserData, static_cast(midCh), ctrlno, value); + break; + } + + case MidiEvent::T_PATCHCHANGE: // Patch change + { + m_interface->rt_patchChange(m_interface->rtUserData, static_cast(midCh), evt.data[0]); + break; + } + + case MidiEvent::T_CHANAFTTOUCH: // Channel after-touch + { + uint8_t chanat = evt.data[0]; + m_interface->rt_channelAfterTouch(m_interface->rtUserData, static_cast(midCh), chanat); + break; + } + + case MidiEvent::T_WHEEL: // Wheel/pitch bend + { + uint8_t a = evt.data[0]; + uint8_t b = evt.data[1]; + m_interface->rt_pitchBend(m_interface->rtUserData, static_cast(midCh), b, a); + break; + } + }//switch +} + +double BW_MidiSequencer::Tick(double s, double granularity) +{ + assert(m_interface);// MIDI output interface must be defined! + + s *= m_tempoMultiplier; +#ifdef ENABLE_BEGIN_SILENCE_SKIPPING + if(CurrentPositionNew.began) +#endif + m_currentPosition.wait -= s; + m_currentPosition.absTimePosition += s; + + int antiFreezeCounter = 10000;//Limit 10000 loops to avoid freezing + while((m_currentPosition.wait <= granularity * 0.5) && (antiFreezeCounter > 0)) + { + //std::fprintf(stderr, "wait = %g...\n", CurrentPosition.wait); + if(!processEvents()) + break; + if(m_currentPosition.wait <= 0.0) + antiFreezeCounter--; + } + + if(antiFreezeCounter <= 0) + m_currentPosition.wait += 1.0;/* Add extra 1 second when over 10000 events + with zero delay are been detected */ + + if(m_currentPosition.wait < 0.0)//Avoid negative delay value! + return 0.0; + + return m_currentPosition.wait; +} + + +double BW_MidiSequencer::seek(double seconds, const double granularity) +{ + if(seconds < 0.0) + return 0.0;//Seeking negative position is forbidden! :-P + const double granualityHalf = granularity * 0.5, + s = seconds;//m_setup.delay < m_setup.maxdelay ? m_setup.delay : m_setup.maxdelay; + + /* Attempt to go away out of song end must rewind position to begin */ + if(seconds > m_fullSongTimeLength) + { + rewind(); + return 0.0; + } + + bool loopFlagState = m_loopEnabled; + // Turn loop pooints off because it causes wrong position rememberin on a quick seek + m_loopEnabled = false; + + /* + * Seeking search is similar to regular ticking, except of next things: + * - We don't processsing arpeggio and vibrato + * - To keep correctness of the state after seek, begin every search from begin + * - All sustaining notes must be killed + * - Ignore Note-On events + */ + rewind(); + + /* + * Set "loop Start" to false to prevent overwrite of loopStart position with + * seek destinition position + * + * TODO: Detect & set loopStart position on load time to don't break loop while seeking + */ + m_loop.caughtStart = false; + + while((m_currentPosition.absTimePosition < seconds) && + (m_currentPosition.absTimePosition < m_fullSongTimeLength)) + { + m_currentPosition.wait -= s; + m_currentPosition.absTimePosition += s; + int antiFreezeCounter = 10000;//Limit 10000 loops to avoid freezing + double dstWait = m_currentPosition.wait + granualityHalf; + while((m_currentPosition.wait <= granualityHalf)/*&& (antiFreezeCounter > 0)*/) + { + //std::fprintf(stderr, "wait = %g...\n", CurrentPosition.wait); + if(!processEvents(true)) + break; + //Avoid freeze because of no waiting increasing in more than 10000 cycles + if(m_currentPosition.wait <= dstWait) + antiFreezeCounter--; + else + { + dstWait = m_currentPosition.wait + granualityHalf; + antiFreezeCounter = 10000; + } + } + if(antiFreezeCounter <= 0) + m_currentPosition.wait += 1.0;/* Add extra 1 second when over 10000 events + with zero delay are been detected */ + } + + if(m_currentPosition.wait < 0.0) + m_currentPosition.wait = 0.0; + + m_loopEnabled = loopFlagState; + return m_currentPosition.wait; +} + +double BW_MidiSequencer::tell() +{ + return m_currentPosition.absTimePosition; +} + +double BW_MidiSequencer::timeLength() +{ + return m_fullSongTimeLength; +} + +double BW_MidiSequencer::getLoopStart() +{ + return m_loopStartTime; +} + +double BW_MidiSequencer::getLoopEnd() +{ + return m_loopEndTime; +} + +void BW_MidiSequencer::rewind() +{ + m_currentPosition = m_trackBeginPosition; + m_atEnd = false; + + m_loop.reset(); + m_loop.caughtStart = true; +} + +void BW_MidiSequencer::setTempo(double tempo) +{ + m_tempoMultiplier = tempo; +} + +bool BW_MidiSequencer::loadMIDI(const std::string &filename) +{ + FileAndMemReader file; + file.openFile(filename.c_str()); + if(!loadMIDI(file)) + return false; + return true; +} + +bool BW_MidiSequencer::loadMIDI(const void *data, size_t size) +{ + FileAndMemReader file; + file.openData(data, size); + return loadMIDI(file); +} + +template +class BufferGuard +{ + T *m_ptr; +public: + BufferGuard() : m_ptr(NULL) + {} + + ~BufferGuard() + { + set(); + } + + void set(T *p = NULL) + { + if(m_ptr) + free(m_ptr); + m_ptr = p; + } +}; + +bool BW_MidiSequencer::loadMIDI(FileAndMemReader &fr) +{ + size_t fsize; + BW_MidiSequencer_UNUSED(fsize); + std::vector > rawTrackData; + //! Temp buffer for conversion + BufferGuard cvt_buf; + m_parsingErrorsString.clear(); + + assert(m_interface);// MIDI output interface must be defined! + + if(!fr.isValid()) + { + m_errorString = "Invalid data stream!\n"; +#ifndef _WIN32 + m_errorString += std::strerror(errno); +#endif + return false; + } + + m_atEnd = false; + m_loop.fullReset(); + m_loop.caughtStart = true; + + m_format = Format_MIDI; + + bool is_GMF = false; // GMD/MUS files (ScummVM) + bool is_IMF = false; // IMF + bool is_CMF = false; // Creative Music format (CMF/CTMF) + bool is_RSXX = false; // RSXX, such as Cartooners + + const size_t headerSize = 4 + 4 + 2 + 2 + 2; // 14 + char headerBuf[headerSize] = ""; + size_t DeltaTicks = 192, TrackCount = 1; + unsigned smfFormat = 0; + +riffskip: + fsize = fr.read(headerBuf, 1, headerSize); + if(fsize < headerSize) + { + m_errorString = "Unexpected end of file at header!\n"; + return false; + } + + if(std::memcmp(headerBuf, "RIFF", 4) == 0) + { + fr.seek(6l, FileAndMemReader::CUR); + goto riffskip; + } + + if(std::memcmp(headerBuf, "GMF\x1", 4) == 0) + { + // GMD/MUS files (ScummVM) + fr.seek(7 - static_cast(headerSize), FileAndMemReader::CUR); + is_GMF = true; + } +#ifndef BWMIDI_DISABLE_MUS_SUPPORT + else if(std::memcmp(headerBuf, "MUS\x1A", 4) == 0) + { + // MUS/DMX files (Doom) + size_t mus_len = fr.fileSize(); + fr.seek(0, FileAndMemReader::SET); + uint8_t *mus = (uint8_t *)malloc(mus_len); + if(!mus) + { + m_errorString = "Out of memory!"; + return false; + } + fsize = fr.read(mus, 1, mus_len); + if(fsize < mus_len) + { + fr.close(); + m_errorString = "Failed to read MUS file data!\n"; + return false; + } + + //Close source stream + fr.close(); + + uint8_t *mid = NULL; + uint32_t mid_len = 0; + int m2mret = Convert_mus2midi(mus, static_cast(mus_len), + &mid, &mid_len, 0); + if(mus) + free(mus); + if(m2mret < 0) + { + m_errorString = "Invalid MUS/DMX data format!"; + return false; + } + cvt_buf.set(mid); + //Open converted MIDI file + fr.openData(mid, static_cast(mid_len)); + //Re-Read header again! + goto riffskip; + } +#endif //BWMIDI_DISABLE_MUS_SUPPORT + +#ifndef BWMIDI_DISABLE_XMI_SUPPORT + else if(std::memcmp(headerBuf, "FORM", 4) == 0) + { + if(std::memcmp(headerBuf + 8, "XDIR", 4) != 0) + { + fr.close(); + m_errorString = fr.fileName() + ": Invalid format\n"; + return false; + } + + size_t mus_len = fr.fileSize(); + fr.seek(0, FileAndMemReader::SET); + + uint8_t *mus = (uint8_t*)malloc(mus_len); + if(!mus) + { + m_errorString = "Out of memory!"; + return false; + } + fsize = fr.read(mus, 1, mus_len); + if(fsize < mus_len) + { + fr.close(); + m_errorString = "Failed to read XMI file data!\n"; + return false; + } + + //Close source stream + fr.close(); + + uint8_t *mid = NULL; + uint32_t mid_len = 0; + int m2mret = Convert_xmi2midi(mus, static_cast(mus_len), + &mid, &mid_len, XMIDI_CONVERT_NOCONVERSION); + if(mus) free(mus); + if(m2mret < 0) + { + m_errorString = "Invalid XMI data format!"; + return false; + } + cvt_buf.set(mid); + //Open converted MIDI file + fr.openData(mid, static_cast(mid_len)); + //Set format as XMIDI + m_format = Format_XMIDI; + //Re-Read header again! + goto riffskip; + } +#endif //BWMIDI_DISABLE_XMI_SUPPORT + + else if(std::memcmp(headerBuf, "CTMF", 4) == 0) + { + // Creative Music Format (CMF). + // When playing CTMF files, use the following commandline: + // adlmidi song8.ctmf -p -v 1 1 0 + // i.e. enable percussion mode, deeper vibrato, and use only 1 card. + is_CMF = true; + m_format = Format_CMF; + //unsigned version = ReadLEint(HeaderBuf+4, 2); + uint64_t ins_start = readLEint(headerBuf + 6, 2); + uint64_t mus_start = readLEint(headerBuf + 8, 2); + //unsigned deltas = ReadLEint(HeaderBuf+10, 2); + uint64_t ticks = readLEint(headerBuf + 12, 2); + // Read title, author, remarks start offsets in file + fsize = fr.read(headerBuf, 1, 6); + if(fsize < 6) + { + fr.close(); + m_errorString = "Unexpected file ending on attempt to read CTMF header!"; + return false; + } + + //unsigned long notes_starts[3] = {ReadLEint(HeaderBuf+0,2),ReadLEint(HeaderBuf+0,4),ReadLEint(HeaderBuf+0,6)}; + fr.seek(16, FileAndMemReader::CUR); // Skip the channels-in-use table + fsize = fr.read(headerBuf, 1, 4); + if(fsize < 4) + { + fr.close(); + m_errorString = "Unexpected file ending on attempt to read CMF instruments block header!"; + return false; + } + + uint64_t ins_count = readLEint(headerBuf + 0, 2); //, basictempo = ReadLEint(HeaderBuf+2, 2); + fr.seek(static_cast(ins_start), FileAndMemReader::SET); + + m_cmfInstruments.reserve(static_cast(ins_count)); + for(uint64_t i = 0; i < ins_count; ++i) + { + CmfInstrument inst; + fsize = fr.read(inst.data, 1, 16); + if(fsize < 16) + { + fr.close(); + m_errorString = "Unexpected file ending on attempt to read CMF instruments raw data!"; + return false; + } + m_cmfInstruments.push_back(inst); + } + + fr.seeku(mus_start, FileAndMemReader::SET); + TrackCount = 1; + DeltaTicks = (size_t)ticks; + } + else + { + // Try to identify RSXX format + if(headerBuf[0] == 0x7D) + { + fr.seek(0x6D, FileAndMemReader::SET); + fr.read(headerBuf, 1, 6); + if(std::memcmp(headerBuf, "rsxx}u", 6) == 0) + { + is_RSXX = true; + m_format = Format_RSXX; + fr.seek(0x7D, FileAndMemReader::SET); + TrackCount = 1; + DeltaTicks = 60; + } + } + + // Try parsing as an IMF file + if(!is_RSXX) + { + do + { + uint8_t raw[4]; + size_t end = static_cast(headerBuf[0]) + 256 * static_cast(headerBuf[1]); + + if(!end || (end & 3)) + break; + + size_t backup_pos = fr.tell(); + int64_t sum1 = 0, sum2 = 0; + fr.seek(2, FileAndMemReader::SET); + + for(unsigned n = 0; n < 42; ++n) + { + if(fr.read(raw, 1, 4) != 4) + break; + int64_t value1 = raw[0]; + value1 += raw[1] << 8; + sum1 += value1; + int64_t value2 = raw[2]; + value2 += raw[3] << 8; + sum2 += value2; + } + + fr.seek(static_cast(backup_pos), FileAndMemReader::SET); + + if(sum1 > sum2) + { + is_IMF = true; + m_format = Format_IMF; + DeltaTicks = 1; + } + } while(false); + } + + if(!is_IMF && !is_RSXX) + { + if(std::memcmp(headerBuf, "MThd\0\0\0\6", 8) != 0) + { + fr.close(); + m_errorString = fr.fileName() + ": Invalid format, Header signature is unknown!\n"; + return false; + } + + smfFormat = (unsigned)readBEint(headerBuf + 8, 2); + TrackCount = (size_t)readBEint(headerBuf + 10, 2); + DeltaTicks = (size_t)readBEint(headerBuf + 12, 2); + + if(smfFormat > 2) + smfFormat = 1; + } + } + + rawTrackData.clear(); + rawTrackData.resize(TrackCount, std::vector()); + m_invDeltaTicks = fraction(1, 1000000l * static_cast(DeltaTicks)); + if(is_CMF || is_RSXX) + m_tempo = fraction(1, static_cast(DeltaTicks)); + else + m_tempo = fraction(1, static_cast(DeltaTicks) * 2); + static const unsigned char EndTag[4] = {0xFF, 0x2F, 0x00, 0x00}; + size_t totalGotten = 0; + + for(size_t tk = 0; tk < TrackCount; ++tk) + { + // Read track header + size_t trackLength; + + if(is_IMF) + { + //std::fprintf(stderr, "Reading IMF file...\n"); + size_t end = static_cast(headerBuf[0]) + 256 * static_cast(headerBuf[1]); + unsigned IMF_tempo = 1428; + static const unsigned char imf_tempo[] = {0x0,//Zero delay! + MidiEvent::T_SPECIAL, MidiEvent::ST_TEMPOCHANGE, 0x4, + static_cast(IMF_tempo >> 24), + static_cast(IMF_tempo >> 16), + static_cast(IMF_tempo >> 8), + static_cast(IMF_tempo) + }; + rawTrackData[tk].insert(rawTrackData[tk].end(), imf_tempo, imf_tempo + sizeof(imf_tempo)); + rawTrackData[tk].push_back(0x00); + fr.seek(2, FileAndMemReader::SET); + + while(fr.tell() < end && !fr.eof()) + { + uint8_t special_event_buf[5]; + uint8_t raw[4]; + special_event_buf[0] = MidiEvent::T_SPECIAL; + special_event_buf[1] = MidiEvent::ST_RAWOPL; + special_event_buf[2] = 0x02; + if(fr.read(raw, 1, 4) != 4) + break; + special_event_buf[3] = raw[0]; // port index + special_event_buf[4] = raw[1]; // port value + uint32_t delay = static_cast(raw[2]); + delay += 256 * static_cast(raw[3]); + totalGotten += 4; + //if(special_event_buf[3] <= 8) continue; + //fprintf(stderr, "Put %02X <- %02X, plus %04X delay\n", special_event_buf[3],special_event_buf[4], delay); + rawTrackData[tk].insert(rawTrackData[tk].end(), special_event_buf, special_event_buf + 5); + //if(delay>>21) TrackData[tk].push_back( 0x80 | ((delay>>21) & 0x7F ) ); + if(delay >> 14) + rawTrackData[tk].push_back(static_cast(0x80 | ((delay >> 14) & 0x7F))); + if(delay >> 7) + rawTrackData[tk].push_back(static_cast(0x80 | ((delay >> 7) & 0x7F))); + rawTrackData[tk].push_back(static_cast(((delay >> 0) & 0x7F))); + } + + rawTrackData[tk].insert(rawTrackData[tk].end(), EndTag + 0, EndTag + 4); + } + else + { + // Take the rest of the file + if(is_GMF || is_CMF || is_RSXX) + { + size_t pos = fr.tell(); + fr.seek(0, FileAndMemReader::END); + trackLength = fr.tell() - pos; + fr.seek(static_cast(pos), FileAndMemReader::SET); + } + else + { + fsize = fr.read(headerBuf, 1, 8); + if((fsize < 8) || (std::memcmp(headerBuf, "MTrk", 4) != 0)) + { + fr.close(); + m_errorString = fr.fileName() + ": Invalid format, MTrk signature is not found!\n"; + return false; + } + trackLength = (size_t)readBEint(headerBuf + 4, 4); + } + + // Read track data + rawTrackData[tk].resize(trackLength); + fsize = fr.read(&rawTrackData[tk][0], 1, trackLength); + if(fsize < trackLength) + { + fr.close(); + m_errorString = fr.fileName() + ": Unexpected file ending while getting raw track data!\n"; + return false; + } + totalGotten += fsize; + + if(is_GMF/*|| is_MUS*/) // Note: CMF does include the track end tag. + rawTrackData[tk].insert(rawTrackData[tk].end(), EndTag + 0, EndTag + 4); + if(is_RSXX)//Finalize raw track data with a zero + rawTrackData[tk].push_back(0); + } + } + + for(size_t tk = 0; tk < TrackCount; ++tk) + totalGotten += rawTrackData[tk].size(); + + if(totalGotten == 0) + { + m_errorString = fr.fileName() + ": Empty track data"; + return false; + } + + // Build new MIDI events table + if(!buildTrackData(rawTrackData)) + { + m_errorString = fr.fileName() + ": MIDI data parsing error has occouped!\n" + m_parsingErrorsString; + return false; + } + + m_smfFormat = smfFormat; + m_loop.stackLevel = -1; + + return true; +} diff --git a/engine/src/Libraries/adlmidi/wopl/wopl_file.c b/engine/src/Libraries/adlmidi/wopl/wopl_file.c new file mode 100644 index 0000000..25b75be --- /dev/null +++ b/engine/src/Libraries/adlmidi/wopl/wopl_file.c @@ -0,0 +1,584 @@ +/* + * Wohlstand's OPL3 Bank File - a bank format to store OPL3 timbre data and setup + * + * Copyright (c) 2015-2018 Vitaly Novichkov + * + * 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. + */ + +#include "wopl_file.h" +#include +#include + +static const char *wopl3_magic = "WOPL3-BANK\0"; +static const char *wopli_magic = "WOPL3-INST\0"; + +static const uint16_t wopl_latest_version = 3; + +#define WOPL_INST_SIZE_V2 62 +#define WOPL_INST_SIZE_V3 66 + +static uint16_t toUint16LE(const uint8_t *arr) +{ + uint16_t num = arr[0]; + num |= ((arr[1] << 8) & 0xFF00); + return num; +} + +static uint16_t toUint16BE(const uint8_t *arr) +{ + uint16_t num = arr[1]; + num |= ((arr[0] << 8) & 0xFF00); + return num; +} + +static int16_t toSint16BE(const uint8_t *arr) +{ + int16_t num = *(const int8_t *)(&arr[0]); + num *= 1 << 8; + num |= arr[1]; + return num; +} + +static void fromUint16LE(uint16_t in, uint8_t *arr) +{ + arr[0] = in & 0x00FF; + arr[1] = (in >> 8) & 0x00FF; +} + +static void fromUint16BE(uint16_t in, uint8_t *arr) +{ + arr[1] = in & 0x00FF; + arr[0] = (in >> 8) & 0x00FF; +} + +static void fromSint16BE(int16_t in, uint8_t *arr) +{ + arr[1] = in & 0x00FF; + arr[0] = ((uint16_t)in >> 8) & 0x00FF; +} + + +WOPLFile *WOPL_Init(uint16_t melodic_banks, uint16_t percussive_banks) +{ + WOPLFile *file = NULL; + if(melodic_banks == 0) + return NULL; + if(percussive_banks == 0) + return NULL; + file = (WOPLFile*)calloc(1, sizeof(WOPLFile)); + if(!file) + return NULL; + file->banks_count_melodic = melodic_banks; + file->banks_count_percussion = percussive_banks; + file->banks_melodic = (WOPLBank*)calloc(1, sizeof(WOPLBank) * melodic_banks ); + file->banks_percussive = (WOPLBank*)calloc(1, sizeof(WOPLBank) * percussive_banks ); + return file; +} + +void WOPL_Free(WOPLFile *file) +{ + if(file) + { + if(file->banks_melodic) + free(file->banks_melodic); + if(file->banks_percussive) + free(file->banks_percussive); + free(file); + } +} + +int WOPL_BanksCmp(const WOPLFile *bank1, const WOPLFile *bank2) +{ + int res = 1; + + res &= (bank1->version == bank2->version); + res &= (bank1->opl_flags == bank2->opl_flags); + res &= (bank1->volume_model == bank2->volume_model); + res &= (bank1->banks_count_melodic == bank2->banks_count_melodic); + res &= (bank1->banks_count_percussion == bank2->banks_count_percussion); + + if(res) + { + int i; + for(i = 0; i < bank1->banks_count_melodic; i++) + res &= (memcmp(&bank1->banks_melodic[i], &bank2->banks_melodic[i], sizeof(WOPLBank)) == 0); + if(res) + { + for(i = 0; i < bank1->banks_count_percussion; i++) + res &= (memcmp(&bank1->banks_percussive[i], &bank2->banks_percussive[i], sizeof(WOPLBank)) == 0); + } + } + + return res; +} + +static void WOPL_parseInstrument(WOPLInstrument *ins, uint8_t *cursor, uint16_t version, uint8_t has_sounding_delays) +{ + int l; + strncpy(ins->inst_name, (const char*)cursor, 32); + ins->inst_name[32] = '\0'; + ins->note_offset1 = toSint16BE(cursor + 32); + ins->note_offset2 = toSint16BE(cursor + 34); + ins->midi_velocity_offset = (int8_t)cursor[36]; + ins->second_voice_detune = (int8_t)cursor[37]; + ins->percussion_key_number = cursor[38]; + ins->inst_flags = cursor[39]; + ins->fb_conn1_C0 = cursor[40]; + ins->fb_conn2_C0 = cursor[41]; + for(l = 0; l < 4; l++) + { + size_t off = 42 + (size_t)(l) * 5; + ins->operators[l].avekf_20 = cursor[off + 0]; + ins->operators[l].ksl_l_40 = cursor[off + 1]; + ins->operators[l].atdec_60 = cursor[off + 2]; + ins->operators[l].susrel_80 = cursor[off + 3]; + ins->operators[l].waveform_E0 = cursor[off + 4]; + } + if((version >= 3) && has_sounding_delays) + { + ins->delay_on_ms = toUint16BE(cursor + 62); + ins->delay_off_ms = toUint16BE(cursor + 64); + } +} + +static void WOPL_writeInstrument(WOPLInstrument *ins, uint8_t *cursor, uint16_t version, uint8_t has_sounding_delays) +{ + int l; + strncpy((char*)cursor, ins->inst_name, 32); + fromSint16BE(ins->note_offset1, cursor + 32); + fromSint16BE(ins->note_offset2, cursor + 34); + cursor[36] = (uint8_t)ins->midi_velocity_offset; + cursor[37] = (uint8_t)ins->second_voice_detune; + cursor[38] = ins->percussion_key_number; + cursor[39] = ins->inst_flags; + cursor[40] = ins->fb_conn1_C0; + cursor[41] = ins->fb_conn2_C0; + for(l = 0; l < 4; l++) + { + size_t off = 42 + (size_t)(l) * 5; + cursor[off + 0] = ins->operators[l].avekf_20; + cursor[off + 1] = ins->operators[l].ksl_l_40; + cursor[off + 2] = ins->operators[l].atdec_60; + cursor[off + 3] = ins->operators[l].susrel_80; + cursor[off + 4] = ins->operators[l].waveform_E0; + } + if((version >= 3) && has_sounding_delays) + { + fromUint16BE(ins->delay_on_ms, cursor + 62); + fromUint16BE(ins->delay_off_ms, cursor + 64); + } +} + +WOPLFile *WOPL_LoadBankFromMem(void *mem, size_t length, int *error) +{ + WOPLFile *outFile = NULL; + uint16_t i = 0, j = 0, k = 0; + uint16_t version = 0; + uint16_t count_melodic_banks = 1; + uint16_t count_percusive_banks = 1; + uint8_t *cursor = (uint8_t *)mem; + + WOPLBank *bankslots[2]; + uint16_t bankslots_sizes[2]; + +#define SET_ERROR(err) \ +{\ + WOPL_Free(outFile);\ + if(error)\ + {\ + *error = err;\ + }\ +} + +#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } + + if(!cursor) + { + SET_ERROR(WOPL_ERR_NULL_POINTER); + return NULL; + } + + {/* Magic number */ + if(length < 11) + { + SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING); + return NULL; + } + if(memcmp(cursor, wopl3_magic, 11) != 0) + { + SET_ERROR(WOPL_ERR_BAD_MAGIC); + return NULL; + } + GO_FORWARD(11); + } + + {/* Version code */ + if(length < 2) + { + SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING); + return NULL; + } + version = toUint16LE(cursor); + if(version > wopl_latest_version) + { + SET_ERROR(WOPL_ERR_NEWER_VERSION); + return NULL; + } + GO_FORWARD(2); + } + + {/* Header of WOPL */ + uint8_t head[6]; + if(length < 6) + { + SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING); + return NULL; + } + memcpy(head, cursor, 6); + count_melodic_banks = toUint16BE(head); + count_percusive_banks = toUint16BE(head + 2); + GO_FORWARD(6); + + outFile = WOPL_Init(count_melodic_banks, count_percusive_banks); + if(!outFile) + { + SET_ERROR(WOPL_ERR_OUT_OF_MEMORY); + return NULL; + } + + outFile->version = version; + outFile->opl_flags = head[4]; + outFile->volume_model = head[5]; + } + + bankslots_sizes[0] = count_melodic_banks; + bankslots[0] = outFile->banks_melodic; + bankslots_sizes[1] = count_percusive_banks; + bankslots[1] = outFile->banks_percussive; + + if(version >= 2) /* Bank names and LSB/MSB titles */ + { + for(i = 0; i < 2; i++) + { + for(j = 0; j < bankslots_sizes[i]; j++) + { + if(length < 34) + { + SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING); + return NULL; + } + strncpy(bankslots[i][j].bank_name, (const char*)cursor, 32); + bankslots[i][j].bank_name[32] = '\0'; + bankslots[i][j].bank_midi_lsb = cursor[32]; + bankslots[i][j].bank_midi_msb = cursor[33]; + GO_FORWARD(34); + } + } + } + + {/* Read instruments data */ + uint16_t insSize = 0; + if(version > 2) + insSize = WOPL_INST_SIZE_V3; + else + insSize = WOPL_INST_SIZE_V2; + for(i = 0; i < 2; i++) + { + if(length < (insSize * 128) * (size_t)bankslots_sizes[i]) + { + SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING); + return NULL; + } + + for(j = 0; j < bankslots_sizes[i]; j++) + { + for(k = 0; k < 128; k++) + { + WOPLInstrument *ins = &bankslots[i][j].ins[k]; + WOPL_parseInstrument(ins, cursor, version, 1); + GO_FORWARD(insSize); + } + } + } + } + +#undef GO_FORWARD +#undef SET_ERROR + + return outFile; +} + +int WOPL_LoadInstFromMem(WOPIFile *file, void *mem, size_t length) +{ + uint16_t version = 0; + uint8_t *cursor = (uint8_t *)mem; + uint16_t ins_size; + + if(!cursor) + return WOPL_ERR_NULL_POINTER; + +#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } + + {/* Magic number */ + if(length < 11) + return WOPL_ERR_UNEXPECTED_ENDING; + if(memcmp(cursor, wopli_magic, 11) != 0) + return WOPL_ERR_BAD_MAGIC; + GO_FORWARD(11); + } + + {/* Version code */ + if(length < 2) + return WOPL_ERR_UNEXPECTED_ENDING; + version = toUint16LE(cursor); + if(version > wopl_latest_version) + return WOPL_ERR_NEWER_VERSION; + GO_FORWARD(2); + } + + {/* is drum flag */ + if(length < 1) + return WOPL_ERR_UNEXPECTED_ENDING; + file->is_drum = *cursor; + GO_FORWARD(1); + } + + if(version > 2) + /* Skip sounding delays are not part of single-instrument file + * two sizes of uint16_t will be subtracted */ + ins_size = WOPL_INST_SIZE_V3 - (sizeof(uint16_t) * 2); + else + ins_size = WOPL_INST_SIZE_V2; + + if(length < ins_size) + return WOPL_ERR_UNEXPECTED_ENDING; + + WOPL_parseInstrument(&file->inst, cursor, version, 0); + GO_FORWARD(ins_size); + + return WOPL_ERR_OK; +#undef GO_FORWARD +} + +size_t WOPL_CalculateBankFileSize(WOPLFile *file, uint16_t version) +{ + size_t final_size = 0; + size_t ins_size = 0; + + if(version == 0) + version = wopl_latest_version; + + if(!file) + return 0; + final_size += 11 + 2 + 2 + 2 + 1 + 1; + /* + * Magic number, + * Version, + * Count of melodic banks, + * Count of percussive banks, + * Chip specific flags + * Volume Model + */ + + if(version >= 2) + { + /* Melodic banks meta-data */ + final_size += (32 + 1 + 1) * file->banks_count_melodic; + /* Percussive banks meta-data */ + final_size += (32 + 1 + 1) * file->banks_count_percussion; + } + + if(version >= 3) + ins_size = WOPL_INST_SIZE_V3; + else + ins_size = WOPL_INST_SIZE_V2; + /* Melodic instruments */ + final_size += (ins_size * 128) * file->banks_count_melodic; + /* Percusive instruments */ + final_size += (ins_size * 128) * file->banks_count_percussion; + + return final_size; +} + +size_t WOPL_CalculateInstFileSize(WOPIFile *file, uint16_t version) +{ + size_t final_size = 0; + size_t ins_size = 0; + + if(version == 0) + version = wopl_latest_version; + + if(!file) + return 0; + final_size += 11 + 2 + 1; + /* + * Magic number, + * version, + * is percussive instrument + */ + + if(version >= 3) + ins_size = WOPL_INST_SIZE_V3; + else + ins_size = WOPL_INST_SIZE_V2; + final_size += ins_size * 128; + + return final_size; +} + +int WOPL_SaveBankToMem(WOPLFile *file, void *dest_mem, size_t length, uint16_t version, uint16_t force_gm) +{ + uint8_t *cursor = (uint8_t *)dest_mem; + uint16_t ins_size = 0; + uint16_t i, j, k; + uint16_t banks_melodic = force_gm ? 1 : file->banks_count_melodic; + uint16_t banks_percusive = force_gm ? 1 : file->banks_count_percussion; + + WOPLBank *bankslots[2]; + uint16_t bankslots_sizes[2]; + + if(version == 0) + version = wopl_latest_version; + +#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } + + if(length < 11) + return WOPL_ERR_UNEXPECTED_ENDING; + memcpy(cursor, wopl3_magic, 11); + GO_FORWARD(11); + + if(length < 2) + return WOPL_ERR_UNEXPECTED_ENDING; + fromUint16LE(version, cursor); + GO_FORWARD(2); + + if(length < 2) + return WOPL_ERR_UNEXPECTED_ENDING; + fromUint16BE(banks_melodic, cursor); + GO_FORWARD(2); + + if(length < 2) + return WOPL_ERR_UNEXPECTED_ENDING; + fromUint16BE(banks_percusive, cursor); + GO_FORWARD(2); + + if(length < 2) + return WOPL_ERR_UNEXPECTED_ENDING; + cursor[0] = file->opl_flags; + cursor[1] = file->volume_model; + GO_FORWARD(2); + + bankslots[0] = file->banks_melodic; + bankslots_sizes[0] = banks_melodic; + bankslots[1] = file->banks_percussive; + bankslots_sizes[1] = banks_percusive; + + if(version >= 2) + { + for(i = 0; i < 2; i++) + { + for(j = 0; j < bankslots_sizes[i]; j++) + { + if(length < 34) + return WOPL_ERR_UNEXPECTED_ENDING; + strncpy((char*)cursor, bankslots[i][j].bank_name, 32); + cursor[32] = bankslots[i][j].bank_midi_lsb; + cursor[33] = bankslots[i][j].bank_midi_msb; + GO_FORWARD(34); + } + } + } + + {/* Write instruments data */ + if(version >= 3) + ins_size = WOPL_INST_SIZE_V3; + else + ins_size = WOPL_INST_SIZE_V2; + for(i = 0; i < 2; i++) + { + if(length < (ins_size * 128) * (size_t)bankslots_sizes[i]) + return WOPL_ERR_UNEXPECTED_ENDING; + + for(j = 0; j < bankslots_sizes[i]; j++) + { + for(k = 0; k < 128; k++) + { + WOPLInstrument *ins = &bankslots[i][j].ins[k]; + WOPL_writeInstrument(ins, cursor, version, 1); + GO_FORWARD(ins_size); + } + } + } + } + + return WOPL_ERR_OK; +#undef GO_FORWARD +} + +int WOPL_SaveInstToMem(WOPIFile *file, void *dest_mem, size_t length, uint16_t version) +{ + uint8_t *cursor = (uint8_t *)dest_mem; + uint16_t ins_size; + + if(!cursor) + return WOPL_ERR_NULL_POINTER; + + if(version == 0) + version = wopl_latest_version; + +#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } + + {/* Magic number */ + if(length < 11) + return WOPL_ERR_UNEXPECTED_ENDING; + memcpy(cursor, wopli_magic, 11); + GO_FORWARD(11); + } + + {/* Version code */ + if(length < 2) + return WOPL_ERR_UNEXPECTED_ENDING; + fromUint16LE(version, cursor); + GO_FORWARD(2); + } + + {/* is drum flag */ + if(length < 1) + return WOPL_ERR_UNEXPECTED_ENDING; + *cursor = file->is_drum; + GO_FORWARD(1); + } + + if(version > 2) + /* Skip sounding delays are not part of single-instrument file + * two sizes of uint16_t will be subtracted */ + ins_size = WOPL_INST_SIZE_V3 - (sizeof(uint16_t) * 2); + else + ins_size = WOPL_INST_SIZE_V2; + + if(length < ins_size) + return WOPL_ERR_UNEXPECTED_ENDING; + + WOPL_writeInstrument(&file->inst, cursor, version, 0); + GO_FORWARD(ins_size); + + return WOPL_ERR_OK; +#undef GO_FORWARD +} diff --git a/engine/src/Libraries/adlmidi/wopl/wopl_file.h b/engine/src/Libraries/adlmidi/wopl/wopl_file.h new file mode 100644 index 0000000..fa270b6 --- /dev/null +++ b/engine/src/Libraries/adlmidi/wopl/wopl_file.h @@ -0,0 +1,293 @@ +/* + * Wohlstand's OPL3 Bank File - a bank format to store OPL3 timbre data and setup + * + * Copyright (c) 2015-2018 Vitaly Novichkov + * + * 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. + */ + +#ifndef WOPL_FILE_H +#define WOPL_FILE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(__STDC_VERSION__) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ < 199901L)) \ + || defined(__STRICT_ANSI__) || !defined(__cplusplus) +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int int16_t; +typedef unsigned short int uint16_t; +#endif + +/* Global OPL flags */ +typedef enum WOPLFileFlags +{ + /* Enable Deep-Tremolo flag */ + WOPL_FLAG_DEEP_TREMOLO = 0x01, + /* Enable Deep-Vibrato flag */ + WOPL_FLAG_DEEP_VIBRATO = 0x02 +} WOPLFileFlags; + +/* Volume scaling model implemented in the libADLMIDI */ +typedef enum WOPL_VolumeModel +{ + WOPL_VM_Generic = 0, + WOPL_VM_Native, + WOPL_VM_DMX, + WOPL_VM_Apogee, + WOPL_VM_Win9x +} WOPL_VolumeModel; + +typedef enum WOPL_InstrumentFlags +{ + /* Is two-operator single-voice instrument (no flags) */ + WOPL_Ins_2op = 0x00, + /* Is true four-operator instrument */ + WOPL_Ins_4op = 0x01, + /* Is pseudo four-operator (two 2-operator voices) instrument */ + WOPL_Ins_Pseudo4op = 0x02, + /* Is a blank instrument entry */ + WOPL_Ins_IsBlank = 0x04, + + /* RythmMode flags mask */ + WOPL_RhythmModeMask = 0x38, + + /* Mask of the flags range */ + WOPL_Ins_ALL_MASK = 0x07 +} WOPL_InstrumentFlags; + +typedef enum WOPL_RhythmMode +{ + /* RythmMode: BassDrum */ + WOPL_RM_BassDrum = 0x08, + /* RythmMode: Snare */ + WOPL_RM_Snare = 0x10, + /* RythmMode: TomTom */ + WOPL_RM_TomTom = 0x18, + /* RythmMode: Cymbell */ + WOPL_RM_Cymbal = 0x20, + /* RythmMode: HiHat */ + WOPL_RM_HiHat = 0x28 +} WOPL_RhythmMode; + +/* DEPRECATED: It has typo. Don't use it! */ +typedef WOPL_RhythmMode WOPL_RythmMode; + +/* Error codes */ +typedef enum WOPL_ErrorCodes +{ + WOPL_ERR_OK = 0, + /* Magic number is not maching */ + WOPL_ERR_BAD_MAGIC, + /* Too short file */ + WOPL_ERR_UNEXPECTED_ENDING, + /* Zero banks count */ + WOPL_ERR_INVALID_BANKS_COUNT, + /* Version of file is newer than supported by current version of library */ + WOPL_ERR_NEWER_VERSION, + /* Out of memory */ + WOPL_ERR_OUT_OF_MEMORY, + /* Given null pointer memory data */ + WOPL_ERR_NULL_POINTER +} WOPL_ErrorCodes; + +/* Operator indeces inside of Instrument Entry */ +#define WOPL_OP_CARRIER1 0 +#define WOPL_OP_MODULATOR1 1 +#define WOPL_OP_CARRIER2 2 +#define WOPL_OP_MODULATOR2 3 + +/* OPL3 Oerators data */ +typedef struct WOPLOperator +{ + /* AM/Vib/Env/Ksr/FMult characteristics */ + uint8_t avekf_20; + /* Key Scale Level / Total level register data */ + uint8_t ksl_l_40; + /* Attack / Decay */ + uint8_t atdec_60; + /* Systain and Release register data */ + uint8_t susrel_80; + /* Wave form */ + uint8_t waveform_E0; +} WOPLOperator; + +/* Instrument entry */ +typedef struct WOPLInstrument +{ + /* Title of the instrument */ + char inst_name[34]; + /* MIDI note key (half-tone) offset for an instrument (or a first voice in pseudo-4-op mode) */ + int16_t note_offset1; + /* MIDI note key (half-tone) offset for a second voice in pseudo-4-op mode */ + int16_t note_offset2; + /* MIDI note velocity offset (taken from Apogee TMB format) */ + int8_t midi_velocity_offset; + /* Second voice detune level (taken from DMX OP2) */ + int8_t second_voice_detune; + /* Percussion MIDI base tone number at which this drum will be played */ + uint8_t percussion_key_number; + /* Enum WOPL_InstrumentFlags */ + uint8_t inst_flags; + /* Feedback&Connection register for first and second operators */ + uint8_t fb_conn1_C0; + /* Feedback&Connection register for third and fourth operators */ + uint8_t fb_conn2_C0; + /* Operators register data */ + WOPLOperator operators[4]; + /* Millisecond delay of sounding while key is on */ + uint16_t delay_on_ms; + /* Millisecond delay of sounding after key off */ + uint16_t delay_off_ms; +} WOPLInstrument; + +/* Bank entry */ +typedef struct WOPLBank +{ + /* Name of bank */ + char bank_name[33]; + /* MIDI Bank LSB code */ + uint8_t bank_midi_lsb; + /* MIDI Bank MSB code */ + uint8_t bank_midi_msb; + /* Instruments data of this bank */ + WOPLInstrument ins[128]; +} WOPLBank; + +/* Instrument data file */ +typedef struct WOPIFile +{ + /* Version of instrument file */ + uint16_t version; + /* Is this a percussion instrument */ + uint8_t is_drum; + /* Instrument data */ + WOPLInstrument inst; +} WOPIFile; + +/* Bank data file */ +typedef struct WOPLFile +{ + /* Version of bank file */ + uint16_t version; + /* Count of melodic banks in this file */ + uint16_t banks_count_melodic; + /* Count of percussion banks in this file */ + uint16_t banks_count_percussion; + /* Enum WOPLFileFlags */ + uint8_t opl_flags; + /* Enum WOPL_VolumeModel */ + uint8_t volume_model; + /* dynamically allocated data Melodic banks array */ + WOPLBank *banks_melodic; + /* dynamically allocated data Percussive banks array */ + WOPLBank *banks_percussive; +} WOPLFile; + + +/** + * @brief Initialize blank WOPL data structure with allocated bank data + * @param melodic_banks Count of melodic banks + * @param percussive_banks Count of percussive banks + * @return pointer to heap-allocated WOPL data structure or NULL when out of memory or incorrectly given banks counts + */ +extern WOPLFile *WOPL_Init(uint16_t melodic_banks, uint16_t percussive_banks); + +/** + * @brief Clean up WOPL data file (all allocated bank arrays will be fried too) + * @param file pointer to heap-allocated WOPL data structure + */ +extern void WOPL_Free(WOPLFile *file); + +/** + * @brief Compare two bank entries + * @param bank1 First bank + * @param bank2 Second bank + * @return 1 if banks are equal or 0 if there are different + */ +extern int WOPL_BanksCmp(const WOPLFile *bank1, const WOPLFile *bank2); + + +/** + * @brief Load WOPL bank file from the memory. + * WOPL data structure will be allocated. (don't forget to clear it with WOPL_Free() after use!) + * @param mem Pointer to memory block contains raw WOPL bank file data + * @param length Length of given memory block + * @param error pointer to integer to return an error code. Pass NULL if you don't want to use error codes. + * @return Heap-allocated WOPL file data structure or NULL if any error has occouped + */ +extern WOPLFile *WOPL_LoadBankFromMem(void *mem, size_t length, int *error); + +/** + * @brief Load WOPI instrument file from the memory. + * You must allocate WOPIFile structure by yourself and give the pointer to it. + * @param file Pointer to destinition WOPIFile structure to fill it with parsed data. + * @param mem Pointer to memory block contains raw WOPI instrument file data + * @param length Length of given memory block + * @return 0 if no errors occouped, or an error code of WOPL_ErrorCodes enumeration + */ +extern int WOPL_LoadInstFromMem(WOPIFile *file, void *mem, size_t length); + +/** + * @brief Calculate the size of the output memory block + * @param file Heap-allocated WOPL file data structure + * @param version Destinition version of the file + * @return Size of the raw WOPL file data + */ +extern size_t WOPL_CalculateBankFileSize(WOPLFile *file, uint16_t version); + +/** + * @brief Calculate the size of the output memory block + * @param file Pointer to WOPI file data structure + * @param version Destinition version of the file + * @return Size of the raw WOPI file data + */ +extern size_t WOPL_CalculateInstFileSize(WOPIFile *file, uint16_t version); + +/** + * @brief Write raw WOPL into given memory block + * @param file Heap-allocated WOPL file data structure + * @param dest_mem Destinition memory block pointer + * @param length Length of destinition memory block + * @param version Wanted WOPL version + * @param force_gm Force GM set in saved bank file + * @return Error code or 0 on success + */ +extern int WOPL_SaveBankToMem(WOPLFile *file, void *dest_mem, size_t length, uint16_t version, uint16_t force_gm); + +/** + * @brief Write raw WOPI into given memory block + * @param file Pointer to WOPI file data structure + * @param dest_mem Destinition memory block pointer + * @param length Length of destinition memory block + * @param version Wanted WOPI version + * @return Error code or 0 on success + */ +extern int WOPL_SaveInstToMem(WOPIFile *file, void *dest_mem, size_t length, uint16_t version); + +#ifdef __cplusplus +} +#endif + +#endif /* WOPL_FILE_H */ diff --git a/engine/src/MacSrc/InitMac.c b/engine/src/MacSrc/InitMac.c new file mode 100644 index 0000000..5884cc7 --- /dev/null +++ b/engine/src/MacSrc/InitMac.c @@ -0,0 +1,57 @@ +/* + +Copyright (C) 1994-1995 Looking Glass Technologies, Inc. +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// InitMac.c - Initialize Mac toolbox managers and setup the application's globals. + +//-------------------- +// Includes +//-------------------- +#include + +#include "InitMac.h" +#include "Shock.h" +#include "ShockBitmap.h" +#include "shockolate_version.h" + +// Globals + +intptr_t *gScreenAddress; +int32_t gScreenRowbytes; +int32_t gScreenWide, gScreenHigh; + +// Time Manager routines and globals + +uint32_t gShockTicks; +uint32_t *tmd_ticks; + +void InitMac(void) { + INFO("Starting %s", SHOCKOLATE_VERSION); + InstallShockTimers(); // needed for the tick pointer +} + +void InstallShockTimers(void) { + gShockTicks = 0; + tmd_ticks = &gShockTicks; +} + +void CleanupAndExit(void) { + /*Cleanup(); + ExitToShell();*/ +} diff --git a/engine/src/MacSrc/InitMac.h b/engine/src/MacSrc/InitMac.h new file mode 100644 index 0000000..777bc7c --- /dev/null +++ b/engine/src/MacSrc/InitMac.h @@ -0,0 +1,46 @@ +/* + +Copyright (C) 1994-1995 Looking Glass Technologies, Inc. +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// InitMac.h - Initialize Mac toolbox managers and setup the application's globals. + +// How many tick passed since game startup +extern uint32_t gShockTicks; + +// Pointer to screen +extern intptr_t *gScreenAddress; +extern int32_t gScreenRowbytes; +// Size of current window +extern int32_t gScreenWide, gScreenHigh; + +//-------------------- +// Prototypes +//-------------------- + +/// Initialize the Macintosh managers. +void InitMac(void); + +/** + * Normal cleanup when the program quits. + * @deprecated Actually does nothing + */ +void CleanupAndExit(void); + +/// Startup the SystemShock timer. +void InstallShockTimers(void); diff --git a/engine/src/MacSrc/MacTune.c b/engine/src/MacSrc/MacTune.c new file mode 100644 index 0000000..62f5a70 --- /dev/null +++ b/engine/src/MacSrc/MacTune.c @@ -0,0 +1,396 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +//============================================================================== +// +// System Shock - ©1994-1995 Looking Glass Technologies, Inc. +// +// MacTune.c - Rewrite of Shock's MLIMBS.C file to use QuickTime MIDI rather than AIL. +// +//============================================================================== + +#include + +#include "MacTune.h" +#include "miscqvar.h" +#include "Xmi.h" + +//----------------- +// GLOBALS +//----------------- +uchar mlimbs_on = FALSE; +char mlimbs_status = 0; + +// Handle gHeaderHdl, gTuneHdl, gOfsHdl; // Holds the tune-related data for the current +// theme file. +long *gOffsets; // Array of offsets for the beginning of each tune. +// TunePlayer gPlayer; // The Tune +// Player. +bool gTuneDone; // True when a sequence has finished playing (set by CB proc). +bool gReadyToQueue; // True when it's time to queue up a new sequence. +int gOverlayTime; // Amount of time (in millisecs) to wait for overlays. +int gQueueTime; // Amount of time (in millisecs) to wait to queue next tune. + +// TuneCallBackUPP gTuneCBProc; // Pointer to tune-finished callback +// proc. + +// CalcTuneTask gCalcTuneTask; // Global to hold task info. +// TimerUPP gCalcTuneProcPtr; // UPP for the 6-second time manager tune +// determiner task. + +//------------------- +// INTERNAL PROTOTYPES +//------------------- +// pascal void TuneEndCB(const TuneStatus *status, long refCon); + +//--------------------------------------------------------------- +// The following section is the time manager task for determining the next tune to play. +//--------------------------------------------------------------- +#pragma require_prototypes off + +//--------------------------------------------------------------- +void CalcTuneProc(void) { + gReadyToQueue = TRUE; // It's time to queue up another tune. +} +#pragma require_prototypes on + +//------------------------------------------------------------------------------ +// Initializes the MacTune system. +//------------------------------------------------------------------------------ +int MacTuneInit(void) { + /*if (mlimbs_status != 0) // If already inited, return + return 0; + +//¥¥¥ if (!music_card) return -1; +// Put something here to check for the existence of the QuickTime Musical Instruments. Or maybe +// in music_init. + + // See if there is enough memory to load the QuickTime Music stuff. If not, return an error. + Handle syshdl = NewHandleSys(819200); // Try to allocate an 800K handle in system heap. + if (!syshdl) // If it couldn't allocate the +memory, return 1; // return an error. else + DisposeHandle(syshdl); // We don't need this, it was just a +test. + + // Load up a player to play the tunes. + gPlayer = OpenDefaultComponent(kTunePlayerType, 0); + if (!gPlayer) + { + DebugString("Error: Could not open a tune player."); //¥¥¥ Handle this! + return 2; + } + + // Setup the end-of-tune callback proc. + gTuneCBProc = NewTuneCallBackProc(TuneEndCB); + + // Setup a time-manager task for queueing up new tunes. + gCalcTuneProcPtr = NewTimerProc(CalcTuneProc); // Make a UPP for the TM task + gCalcTuneTask.task.tmAddr = gCalcTuneProcPtr; // Insert the calc tune TM task + gCalcTuneTask.task.tmWakeUp = 0; + gCalcTuneTask.task.tmReserved = 0; +#ifndef __powerc + gCalcTuneTask.appA5 = SetCurrentA5(); +#endif + InsTime((QElemPtr)&gCalcTuneTask); + + // Initialize some globals. + gHeaderHdl = NULL; + gTuneHdl = NULL; + gOfsHdl = NULL; + mlimbs_status = 1;*/ + + return 0; +} + +//------------------------------------------------------------------------------ +// Shuts down the system so it stops playing music and releases all resources. +//------------------------------------------------------------------------------ +void MacTuneShutdown(void) { + /*if (mlimbs_status == 0) // If already shut down, do + nothing return; + + MacTunePurgeCurrentTheme(); // Kill the current theme (unloading theme data) + + RmvTime((QElemPtr)&gCalcTuneTask); // Stop the calc tune task + DisposeRoutineDescriptor(gCalcTuneProcPtr); // Dispose its UPP + + DisposeRoutineDescriptor(gTuneCBProc); // Cleanup callback and player + CloseComponent(gPlayer); + + mlimbs_status = 0;*/ +} + +//-------------------------------------------------------------------------- +// Call-back routine. Get's called when tune is finished. +//-------------------------------------------------------------------------- +/*pascal void TuneEndCB(const TuneStatus *s, long refCon) +{ + gTuneDone = TRUE; +}*/ + +//------------------------------------------------------------------------------ +// Loads all resources associated with a theme file. Stops and purges and currently loaded theme. Returns +// a 1 if successful, < 0 if an error. +//------------------------------------------------------------------------------ +/*int MacTuneLoadTheme(FSSpec *themeSpec, int themeID) +{ + short filenum; + Handle binHdl; + Ptr p; + + extern uchar track_table[NUM_SCORES][SUPERCHUNKS_PER_SCORE]; + extern uchar transition_table[NUM_TRANSITIONS]; + extern uchar layering_table[NUM_LAYERS][MAX_KEYS]; + extern uchar key_table[NUM_LAYERABLE_SUPERCHUNKS][KEY_BAR_RESOLUTION]; + + if (mlimbs_status == 0) +// Only do this if MacTune is inited. return (-1); + + MacTunePurgeCurrentTheme(); // Purge the current +theme. + + // Open the theme file. + filenum = FSpOpenResFile(themeSpec, fsRdPerm); + if (filenum == -1) + return (-2); + + // First, get the 'tbin' resource and copy its data to the MusicAI global arrays. + binHdl = GetResource('tbin', 128); + if (binHdl == NULL) + { + CloseResFile(filenum); +Debugger(); //¥¥¥ + return (-3); + } + HLock(binHdl); + p = *binHdl; + BlockMoveData(p, track_table, NUM_SCORES * SUPERCHUNKS_PER_SCORE); + p += NUM_SCORES * SUPERCHUNKS_PER_SCORE; + BlockMoveData(p, transition_table, NUM_TRANSITIONS); + p += NUM_TRANSITIONS; + BlockMoveData(p, layering_table, NUM_LAYERS * MAX_KEYS); + p += NUM_LAYERS * MAX_KEYS; + BlockMoveData(p, key_table, NUM_LAYERABLE_SUPERCHUNKS * KEY_BAR_RESOLUTION); + p += NUM_LAYERABLE_SUPERCHUNKS * KEY_BAR_RESOLUTION; + gOverlayTime = *(int *)p; + p += 4; + gQueueTime = *(int *)p; + HUnlock(binHdl); + ReleaseResource(binHdl); + + // Next, get the theme-related resources. + gHeaderHdl = GetResource('thdr', 128); + if (gHeaderHdl == NULL) + { + CloseResFile(filenum); + return (-4); + } + gTuneHdl = GetResource('them', 128); + if (gTuneHdl == NULL) + { + CloseResFile(filenum); + return (-5); + } + gOfsHdl = GetResource('tofs', 128); + if (gOfsHdl == NULL) + { + CloseResFile(filenum); + return (-6); + } + + DetachResource(gHeaderHdl); // Turn these into normal handles. + HLockHi(gHeaderHdl); + DetachResource(gTuneHdl); + HLockHi(gTuneHdl); + DetachResource(gOfsHdl); + HLockHi(gOfsHdl); + + CloseResFile(filenum); + + // Set the tune header (load instruments, etc, can take a second or two). + TuneSetHeader(gPlayer, (unsigned long *)*gHeaderHdl); + TunePreroll(gPlayer); + + // Setup the tune offset pointer. + gOffsets = (long *)*gOfsHdl; + + // Initialize our playtime globals. + gTuneDone = FALSE; + gReadyToQueue = FALSE; + + // Here's a big hack. If we're loading theme 0 (machine sounds only), then don't do an + // intro transition. + if (themeID == 0) + current_mode = NORMAL_MODE; + + return(1); +}*/ + +//------------------------------------------------------------------------------ +// If there is a theme loaded, start playing it. +//------------------------------------------------------------------------------ +void MacTuneStartCurrentTheme(void) { + /*if (mlimbs_status && gTuneHdl) // If MacTune is inited and there is a theme + loaded, + { + int pid = current_request[0].pieceID; + if (pid != 255) // If there is a tune + requested, + { + MacTunePlayTune(pid); // play it right now. + } + else + gTuneDone = TRUE; // else make sure we check again + soon. + + }*/ + + int track = 1 + current_request[0].pieceID; + if (track >= 0 && track < NumTracks) { + int i = 0; + + if (!IsPlaying(i)) { + // extern uchar curr_vol_lev; + // int volume = (int)curr_vol_lev * 127 / 100; //convert from 0-100 to 0-127 + StartTrack(i, track); + } + } +} + +//------------------------------------------------------------------------------ +// Stop the current tune playing, stop the tune queue timer task. +//------------------------------------------------------------------------------ +/*void MacTuneKillCurrentTheme(void) +{ + if (mlimbs_status && gTuneHdl) // Only do this if MacTune is inited and +there is a + { +// current theme. RmvTime((QElemPtr)&gCalcTuneTask); // Remove the TimeManager task that queues up + InsTime((QElemPtr)&gCalcTuneTask); // next tune, and re-insert to prevent any tasks +from + // triggering. + TuneStop(gPlayer, kStopTuneFade); // Stop the current tune (if any). + TuneFlush(gPlayer); // Flush the +queue. + + gReadyToQueue = FALSE; + } +}*/ + +//------------------------------------------------------------------------------ +// Stop the current tune playing, stop the tune queue timer task. +//------------------------------------------------------------------------------ +void MacTunePurgeCurrentTheme() { + /*MacTuneKillCurrentTheme(); // Kill the current theme. + + // Dispose of all the theme's data. + if (gHeaderHdl) + { + HUnlock(gHeaderHdl); + DisposeHandle(gHeaderHdl); + gHeaderHdl = NULL; + } + if (gTuneHdl) + { + HUnlock(gTuneHdl); + DisposeHandle(gTuneHdl); + gTuneHdl = NULL; + } + if (gOfsHdl) + { + HUnlock(gOfsHdl); + DisposeHandle(gOfsHdl); + gOfsHdl = NULL; + } + + // Free the tune player component, then open it back up again. This should make the + // game run much faster. + CloseComponent(gPlayer); + gPlayer = OpenDefaultComponent(kTunePlayerType, 0); + if (!gPlayer) + DebugString("Error: Could not open a tune player."); //¥¥¥ Handle this! + + // Clear our the current request array. + for (int i = 0; i < MLIMBS_MAX_SEQUENCES -1; i++) + { + current_request[i].pieceID = 255; + }*/ + mlimbs_counter = 0; +} + +//------------------------------------------------------------------------------ +// Play a tune right now (prime the TM task). Usually this is called when music is first started. +//------------------------------------------------------------------------------ +void MacTunePlayTune(int tune) { + /*if (tune == 255 || tune == -1) + DebugString("Eep Eep Invalid tune!"); //¥¥¥ + + if (gOffsets[tune] != -1) // If there really is a tune there, play it + now. + { + TuneQueue(gPlayer, (unsigned long *)(*gTuneHdl + gOffsets[tune]), 0x10000, + 0, 0x7FFFFFFF, kTuneStartNow, gTuneCBProc, 0); + PrimeTime((QElemPtr)&gCalcTuneTask, gOverlayTime + gQueueTime); + //¥¥¥ temp + // the above amount for PrimeTime is temporary because we're not doing overlays yet. + // so just queue up the next tune at queue time. + } + + // If there was no tune to play this time, set a flag so it will prime the timer again. + else + gTuneDone = TRUE;*/ +} + +//------------------------------------------------------------------------------ +// Add a tune to the tune queue. +//------------------------------------------------------------------------------ +void MacTuneQueueTune(int tune) { + /*if (tune == 255 || tune == -1) + DebugString("Eep Eep Invalid tune!"); //¥¥¥ + + if (gOffsets[tune] != -1) // If there really is a tune there, queue it + up. + { + TuneStatus tpStatus; + TuneGetStatus(gPlayer, &tpStatus); // We'll need this later. + + TuneQueue(gPlayer, (unsigned long *)(*gTuneHdl + gOffsets[tune]), 0x10000, + 0, 0x7FFFFFFF, 0, gTuneCBProc, 0); + + // Normally we don't prime it yet; we want to wait until the currently playing tune finishes. + // However, if tune was playing before we queued this one, go ahead and prime away. + if (tpStatus.queueTime == 0) + PrimeTime((QElemPtr)&gCalcTuneTask, gOverlayTime + gQueueTime); + } + + // If there was no tune to queue this time, set a flag so it will prime the timer again. + else + gTuneDone = TRUE;*/ +} + +//------------------------------------------------------------------------------ +// Prime the TM task to trigger the next tune queueing. +//------------------------------------------------------------------------------ +void MacTunePrimeTimer(void) { + // PrimeTime((QElemPtr)&gCalcTuneTask, gOverlayTime + gQueueTime); + //¥¥¥ temp + // the above amount for PrimeTime is temporary because we're not doing overlays yet. + // so just queue up the next tune at queue time. +} + +void MacTuneUpdateVolume(void) { UpdateVolumeXMI(); } diff --git a/engine/src/MacSrc/MacTune.h b/engine/src/MacSrc/MacTune.h new file mode 100644 index 0000000..37d5839 --- /dev/null +++ b/engine/src/MacSrc/MacTune.h @@ -0,0 +1,70 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +//============================================================================== +// +// System Shock - ©1994-1995 Looking Glass Technologies, Inc. +// +// MacTune.h - Rewrite of Shock's MLIMBS.H file to use QuickTime MIDI rather than AIL. +// +//============================================================================== + +//----------------- +// TYPES & DEFINES +//----------------- +typedef struct { + int pieceID; // Indexes an array of XMIDI_info structs. Specifies which piece to play. + int priority; // Priority of this request. + int loops; // Number of loops. -1 => until deliberately stopped. + uint rel_vol; // Specifies at what relative volume to play it at. (percent) + uint ramp_time; // Specifies the time to ramp in to the specified rel_vol, or ramp out to 0. + int pan; // Note that this pan value affects all channels. + uchar channel_prioritize; + char crossfade; // 0 - don't crossfade, <0 - crossfade out, >0 - crossfade in. + char ramp; // 0 - don't ramp, <0 - ramp out >0 - ramp in + uchar pad; +} mlimbs_request_info; + +#define MLIMBS_MAX_SEQUENCES 8 +#define MLIMBS_MAX_CHANNELS 8 + +#include "mlimbs.h" + +//----------------- +// EXTERN GLOBALS +//----------------- +extern bool gReadyToQueue; // True when it's time to queue up a new sequence. + +// extern TuneCallBackUPP gTuneCBProc; // The tune's callback proc. +// extern CalcTuneTask gCalcTuneTask; // Global to hold task info. +// extern TimerUPP gCalcTuneProcPtr; // UPP for the 6-second time manager +// tune determiner task. + +//----------------- +// PROTOTYPES +//----------------- +int MacTuneInit(void); +void MacTuneShutdown(void); +int MacTuneLoadTheme(char *theme, int themeID); +void MacTuneStartCurrentTheme(void); +void MacTuneKillCurrentTheme(void); +void MacTunePurgeCurrentTheme(void); +void MacTunePlayTune(int tune); +void MacTuneQueueTune(int tune); +void MacTunePrimeTimer(void); +void MacTuneUpdateVolume(void); diff --git a/engine/src/MacSrc/Modding.c b/engine/src/MacSrc/Modding.c new file mode 100644 index 0000000..e2f7a7f --- /dev/null +++ b/engine/src/MacSrc/Modding.c @@ -0,0 +1,147 @@ +/* + +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#include "Modding.h" +#include "lg.h" + +#include +#include + +char *modding_archive_override; +char *modding_additional_files[MAX_MOD_FILES]; +int num_mod_files; + +int StringEndsWith(char *src, char *dst) { + char *s = strrchr(src, '.'); + + if (s != NULL) + return (strcmp(s, dst)) == 0; + + return 0; +} + +int AddResourceFile(char *filename) { + printf("Found resource file: %s\n", filename); + + if (num_mod_files < MAX_MOD_FILES) { + char *f = (char *)malloc(strlen(filename) + 1); + strcpy(f, filename); + modding_additional_files[num_mod_files++] = f; + } + + return OK; +} + +int AddArchiveFile(char *filename) { + printf("Found archive file: %s\n", filename); + + char *f = (char *)malloc(strlen(filename) + 1); + strcpy(f, filename); + modding_archive_override = f; + return OK; +} + +int ProcessModFile(char *filename, uchar follow_dirs) { + printf("ProcessModFile: %s\n", filename); + + if (StringEndsWith(filename, ".dat")) { + AddArchiveFile(filename); + } else if (StringEndsWith(filename, ".res")) { + AddResourceFile(filename); + } else if (follow_dirs) { + ProcessModDirectory(filename); + } + return OK; +} + +int ProcessModArgs(int argc, char **argv) { + // Default things + num_mod_files = 0; + int mod_args_start = 1; + + // Skip arguments + for (int i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + mod_args_start++; + } + } + + // Default the mod list to empty + modding_archive_override = NULL; + for (int i = 0; i < MAX_MOD_FILES; i++) { + modding_additional_files[i] = NULL; + } + + // Now go process args + for (int i = mod_args_start; i < argc; i++) { + ProcessModFile(argv[i], TRUE); + } + + // shamaz: return value is not checked anyway + return OK; +} + +int ProcessModDirectory(char *dirname) { + // Check if this is a directory + + printf("ProcessModDirectory %s\n", dirname); + + DIR *dp = opendir(dirname); + if (dp != NULL) { + struct dirent *ep; + + // Loop through all files here, calling ProcessModFile for each + while ((ep = readdir(dp))) { + + printf("ep->d_name %s\n", ep->d_name); + char *buf = (char *)malloc(strlen(dirname) + strlen(ep->d_name) + 2); + + strcpy(buf, dirname); + +// Windows, why do you have to be weird? +#ifdef _WIN32 + strcat(buf, "\\"); +#else + strcat(buf, "/"); +#endif + + strcat(buf, ep->d_name); + + ProcessModFile(buf, FALSE); + free(buf); + } + + closedir(dp); + } + + // shamaz: Returned value is not checked + return OK; +} + +int LoadModFiles() { + for (int i = 0; i < num_mod_files; i++) { + if (modding_additional_files[i] != NULL) { + printf("Loading mod file %s\n", modding_additional_files[i]); + ResOpenFile(modding_additional_files[i]); + } + } + + // shamaz: Returned value is not checked + return OK; +} diff --git a/engine/src/MacSrc/Modding.h b/engine/src/MacSrc/Modding.h new file mode 100644 index 0000000..b023179 --- /dev/null +++ b/engine/src/MacSrc/Modding.h @@ -0,0 +1,52 @@ +/* + +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +#ifndef __MODDING_H +#define __MODDING_H + +//-------------------- +// Defines +//-------------------- + +#define MAX_MOD_FILES 100 + +//-------------------- +// Public Globals +//-------------------- + +// Let people override the default game archive +extern char *modding_archive_override; + +// Additional resource files to load +extern char *modding_additional_files[MAX_MOD_FILES]; + +extern int num_mod_files; + +//-------------------- +// Function Prototypes +//-------------------- + +int AddResourceFile(char *filename); +int AddArchiveFile(char *filename); +int ProcessModFile(char *filename, uchar follow_dirs); +int ProcessModArgs(int argc, char **argv); +int ProcessModDirectory(char *dirname); +int LoadModFiles(void); + +#endif //__MODDING_H diff --git a/engine/src/MacSrc/OpenGL.cc b/engine/src/MacSrc/OpenGL.cc new file mode 100644 index 0000000..47c1850 --- /dev/null +++ b/engine/src/MacSrc/OpenGL.cc @@ -0,0 +1,978 @@ +#ifdef USE_OPENGL + +#include +#include "OpenGL.h" + +#ifdef _WIN32 +#define GLEW_STATIC 1 +#include +#include +#else +#define GL_GLEXT_PROTOTYPES +#ifdef __APPLE__ +#include +#else +#include +#include +#endif + +#include +#include +#endif + +extern "C" { +#include "mainloop.h" +#include "map.h" +#include "frintern.h" +#include "frflags.h" +#include "player.h" +#include "textmaps.h" +#include "star.h" +#include "tools.h" +#include "Prefs.h" +#include "Shock.h" +#include "faketime.h" +#include "render.h" +#include "wares.h" + +extern SDL_Renderer *renderer; +extern SDL_Palette *sdlPalette; +} + +#include +#include + +struct CachedTexture { + SDL_Surface *bitmap; + SDL_Surface *converted; + GLuint texture; + long lastDrawTime; + bool locked; +}; + +struct Shader { + GLuint shaderProgram; + GLint uniView; + GLint uniProj; + GLint uniNightSight; + GLint uniMutant; + GLint tcAttrib; + GLint lightAttrib; + GLint colorAttrib; +}; + +struct FrameBuffer { + GLuint frameBuffer; + GLuint stencilBuffer; + GLuint texture; + int width; + int height; +}; + +#define MAX_CACHED_TEXTURES 1024 + +static Shader textureShaderProgram; +static Shader colorShaderProgram; +static Shader starShaderProgram; + +static FrameBuffer backupBuffer; + +static SDL_GLContext context; +static GLuint dynTexture; + +static SDL_Palette *opaquePalette; +static SDL_Palette *transparentPalette; + +// Texture cache to keep SDL surfaces and GL textures in memory +static std::map texturesByBitsPtr; + +static float view_scale; +static int phys_width; +static int phys_height; +static int phys_offset_x; +static int phys_offset_y; + +static int render_width; +static int render_height; + +static bool opengl_enabled = true; +static bool palette_dirty = false; +static bool blend_enabled = true; +static GLuint bound_texture = -1; + +// View matrix; Z offset experimentally tweaked for near-perfect alignment +// between GL projection and software projection (sprite screen coordinates) +static const float ViewMatrix[] = {1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, -0.01, 1.0}; + +// Projection matrix; experimentally tweaked for near-perfect alignment: +// FOV 89.5 deg, aspect ratio 1:1, near plane 0, far plane 100 +static const float ProjectionMatrix[] = {1.00876, 0.0, 0.0, 0.0, 0.0, 1.00876, 0.0, 0.0, + 0.0, 0.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0}; + +// Identity matrix for sprite rendering +static const float IdentityMatrix[] = {1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0}; + +static inline GLint get_texture_min_func() { + // convert prefs texture filtering mode to GL min func value + switch (gShockPrefs.doTextureFilter) { + case 0: + return GL_NEAREST; + case 1: + return GL_LINEAR; + // case 2: return GL_LINEAR_MIPMAP_LINEAR; + } + + WARN("gShockPrefs.doTextureFilter=%d is invalid; resetting to zero (unflitered)", gShockPrefs.doTextureFilter); + gShockPrefs.doTextureFilter = 0; + return GL_NEAREST; +} + +static inline GLint get_texture_mag_func() { + // convert prefs texture filtering mode to GL mag func value + switch (gShockPrefs.doTextureFilter) { + case 0: + return GL_NEAREST; + case 1: + return GL_LINEAR; + case 2: + return GL_LINEAR; + } + + WARN("gShockPrefs.doTextureFilter=%d is invalid; resetting to zero (unflitered)", gShockPrefs.doTextureFilter); + gShockPrefs.doTextureFilter = 0; + return GL_NEAREST; +} + +static void set_blend_mode(bool enabled) { + // change the blend mode, if not set already + if (blend_enabled != enabled) { + blend_enabled = enabled; + if (blend_enabled) { + glEnable(GL_BLEND); + } else { + glDisable(GL_BLEND); + } + } +} + +static void bind_texture(GLuint tex) { + // bind a texture, if not bound already + if (bound_texture != tex) { + bound_texture = tex; + glBindTexture(GL_TEXTURE_2D, bound_texture); + } +} + +static GLuint compileShader(GLenum type, const char *source) { + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, &source, nullptr); + glCompileShader(shader); + + GLint status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if (status != GL_TRUE) { + char buffer[512]; + glGetShaderInfoLog(shader, 512, nullptr, buffer); + ERROR("Error compiling shader: %s", buffer); + return 0; + } + + return shader; +} + +static GLuint loadShader(GLenum type, const char *filename) { + + DEBUG("Loading shader %s", filename); + +#ifdef __APPLE__ + char fb[1024]; + sprintf(fb, "%s/shaders/%s", SDL_GetBasePath(), filename); +#else + char fb[256]; + sprintf(fb, "shaders/%s", filename); +#endif + + FILE *file = fopen(fb, "r"); + if (file == nullptr) { + ERROR("Could not open shader file %s!", fb); + return 0; + } + + std::stringstream source; + char line[256]; + while (fgets(line, sizeof(line), file)) { + char *c = &line[strlen(line) - 1]; + while (c >= line && (*c == '\r' || *c == '\n')) + *(c--) = '\0'; + source << line << '\n'; + } + fclose(file); + + GLuint s = compileShader(type, source.str().c_str()); + + if (s == 0) { + ERROR("Could not compile shader %s", filename); + } + return s; +} + +static int CreateShader(const char *vertexShaderFile, const char *fragmentShaderFile, Shader *outShader) { + GLuint vertShader = loadShader(GL_VERTEX_SHADER, vertexShaderFile); + GLuint fragShader = loadShader(GL_FRAGMENT_SHADER, fragmentShaderFile); + + if (vertShader == 0 || fragShader == 0) { + ERROR("Could not create shader %s : %s", vertexShaderFile, fragmentShaderFile); + context = nullptr; + return 1; // Error! + } + + GLuint shaderProgram = glCreateProgram(); + glAttachShader(shaderProgram, vertShader); + glAttachShader(shaderProgram, fragShader); + glLinkProgram(shaderProgram); + glUseProgram(shaderProgram); + + outShader->shaderProgram = shaderProgram; + outShader->uniView = glGetUniformLocation(shaderProgram, "view"); + outShader->uniProj = glGetUniformLocation(shaderProgram, "proj"); + outShader->uniNightSight = glGetUniformLocation(shaderProgram, "nightsight"); + outShader->uniMutant = glGetUniformLocation(shaderProgram, "mutant"); + outShader->tcAttrib = glGetAttribLocation(shaderProgram, "texcoords"); + outShader->lightAttrib = glGetAttribLocation(shaderProgram, "light"); + outShader->colorAttrib = glGetAttribLocation(shaderProgram, "color"); + + glUniformMatrix4fv(outShader->uniView, 1, false, IdentityMatrix); + glUniformMatrix4fv(outShader->uniProj, 1, false, IdentityMatrix); + + return 0; +} + +static FrameBuffer CreateFrameBuffer(int width, int height) { + FrameBuffer newBuffer{}; + newBuffer.width = width; + newBuffer.height = height; + + // Make a frame buffer, texture for color, and render buffer for stencil + glGenFramebuffers(1, &newBuffer.frameBuffer); + glBindFramebuffer(GL_FRAMEBUFFER, newBuffer.frameBuffer); + + glGenRenderbuffers(1, &newBuffer.stencilBuffer); + glBindRenderbuffer(GL_RENDERBUFFER, newBuffer.stencilBuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, newBuffer.stencilBuffer); + + glGenTextures(1, &newBuffer.texture); + glBindTexture(GL_TEXTURE_2D, newBuffer.texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, newBuffer.texture, 0); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + ERROR("Could not make FrameBuffer!: %x \n", status); + context = nullptr; + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + return newBuffer; +} + +static void BindFrameBuffer(FrameBuffer *buffer) { + if (buffer == nullptr) { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + } else { + glBindFramebuffer(GL_FRAMEBUFFER, buffer->frameBuffer); + glBindRenderbuffer(GL_RENDERBUFFER, buffer->stencilBuffer); + glViewport(0, 0, buffer->width, buffer->height); + } +} + +int init_opengl() { + DEBUG("Initializing OpenGL"); + + // Are we running in OpenGL mode? + if (SDL_GL_GetCurrentContext() == nullptr) { + ERROR("No OpenGL context! Falling back to Software mode."); + return 1; + } + + // Can we create the world rendering context? + SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); + context = SDL_GL_CreateContext(window); + if (context == nullptr) { + ERROR("Could not create an OpenGL context! Falling back to Software mode."); + return 1; + } + +#ifdef _WIN32 + glewExperimental = GL_TRUE; + GLenum err = glewInit(); +#endif + + glEnable(GL_CULL_FACE); + glEnable(GL_BLEND); + glEnable(GL_ALPHA_TEST); + glEnable(GL_POINT_SPRITE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glAlphaFunc(GL_GEQUAL, 0.05f); + + CreateShader("main.vert", "texture.frag", &textureShaderProgram); + CreateShader("main.vert", "color.frag", &colorShaderProgram); + CreateShader("main.vert", "star.frag", &starShaderProgram); + + glGenTextures(1, &dynTexture); + + // Did the setup go okay? + if (context == nullptr) { + ERROR("OpenGL could not be initialized, falling back to Software mode."); + return 1; + } + + int width, height; + SDL_GetWindowSize(window, &width, &height); + opengl_resize(width, height); + + // Now make the palettes + opaquePalette = SDL_AllocPalette(256); + transparentPalette = SDL_AllocPalette(256); + + return 0; +} + +void opengl_resize(int width, int height) { + SDL_GL_MakeCurrent(window, context); + + int logical_width, logical_height; + SDL_RenderGetLogicalSize(renderer, &logical_width, &logical_height); + + float scale_x = (float)width / logical_width; + float scale_y = (float)height / logical_height; + + if (scale_x >= scale_y) { + // physical aspect ratio is wider; black borders left and right + view_scale = scale_y; + } else { + // physical aspect ratio is narrower; black borders at top and bottom + view_scale = scale_x; + } + + phys_width = view_scale * logical_width; + phys_height = view_scale * logical_height; + + int border_x = width - phys_width; + int border_y = height - phys_height; + phys_offset_x = border_x / 2; + phys_offset_y = border_y / 2; + + backupBuffer = CreateFrameBuffer(logical_width, logical_height); + + INFO("OpenGL Resize %i %i %i %i", width, height, phys_width, phys_height); + + // Redraw the options menu background in the new resolution + extern uchar wrapper_screenmode_hack; + if (wrapper_screenmode_hack) { + render_run(); + wrapper_screenmode_hack = false; + } +} + +void opengl_change_palette() { palette_dirty = true; } + +bool can_use_opengl() { return context != nullptr; } + +bool use_opengl() { + return can_use_opengl() && gShockPrefs.doUseOpenGL && opengl_enabled && + (_current_loop == GAME_LOOP || _current_loop == FULLSCREEN_LOOP) && !global_fullmap->cyber && + !(_fr_curflags & (FR_PICKUPM_MASK | FR_HACKCAM_MASK)); +} + +bool should_opengl_swap() { + return can_use_opengl() && gShockPrefs.doUseOpenGL && opengl_enabled && + (_current_loop == GAME_LOOP || _current_loop == FULLSCREEN_LOOP) && !global_fullmap->cyber; +} + +void opengl_end_frame() { + SDL_GL_MakeCurrent(window, context); + + // Done rendering to the frame buffer, reset back to normal + BindFrameBuffer(nullptr); + palette_dirty = false; +} + +static void updatePalette(SDL_Palette *palette, bool transparent) { + // Update from the base + SDL_SetPaletteColors(palette, sdlPalette->colors, 0, 256); + + if (transparent) + palette->colors[0].a = 0x00; + for (int i = 1; i < 256; i++) { + // colors 1..31, except 2: always at maximum light level + // colors 2, 32..255: no minimum brightness, use interpolated vertex light level + // encode emissive property in the top half of the alpha color, since we don't use those bytes + if (i < 32 && i != 2) + palette->colors[i].a = 0xff; + else + palette->colors[i].a = 0x7f; + } +} + +static bool nightsight_active() { return WareActive(player_struct.hardwarez_status[HARDWARE_GOGGLE_INFRARED]); } + +void opengl_start_frame() { + SDL_GL_MakeCurrent(window, context); + + // Start rendering to our frame buffer canvas + BindFrameBuffer(&backupBuffer); + + // Setup the render width + int logical_width, logical_height; + SDL_RenderGetLogicalSize(renderer, &logical_width, &logical_height); + + render_height = logical_height; + render_width = logical_width; + + // Update the palettes for this frame + updatePalette(opaquePalette, false); + updatePalette(transparentPalette, true); +} + +void get_hdpi_scaling(int *x_scale, int *y_scale) { + // We may need to scale up our OpenGL output to match some HDPI scaling + int output_width, output_height; + SDL_GetRendererOutputSize(renderer, &output_width, &output_height); + + int screen_width, screen_height; + SDL_GetWindowSize(window, &screen_width, &screen_height); + + *x_scale = output_width / screen_width; + *y_scale = output_height / screen_height; +} + +void opengl_swap_and_restore(SDL_Surface *ui) { + // restore the view backup (without HUD overlay) for incremental + // updates in the subsequent frame + SDL_GL_MakeCurrent(window, context); + glClear(GL_COLOR_BUFFER_BIT); + + int x_hdpi_scale, y_hdpi_scale; + get_hdpi_scaling(&x_hdpi_scale, &y_hdpi_scale); + + // Set the drawable area for the 3d view + glViewport(phys_offset_x * x_hdpi_scale, phys_offset_y * y_hdpi_scale, phys_width * x_hdpi_scale, + phys_height * y_hdpi_scale); + set_blend_mode(false); + + // Bind and setup our general shader program + glUseProgram(textureShaderProgram.shaderProgram); + GLint tcAttrib = textureShaderProgram.tcAttrib; + GLint lightAttrib = textureShaderProgram.lightAttrib; + + glUniform1i(textureShaderProgram.uniNightSight, nightsight_active()); + + glUniformMatrix4fv(textureShaderProgram.uniView, 1, false, IdentityMatrix); + glUniformMatrix4fv(textureShaderProgram.uniProj, 1, false, IdentityMatrix); + + // Draw the frame buffer to the screen as a quad + bind_texture(backupBuffer.texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glBegin(GL_TRIANGLE_STRIP); + glVertexAttrib2f(tcAttrib, 1.0f, 0.0f); + glVertexAttrib1f(lightAttrib, 1.0f); + glVertex3f(1.0f, -1.0f, 0.0f); + glVertexAttrib2f(tcAttrib, 1.0f, 1.0f); + glVertexAttrib1f(lightAttrib, 1.0f); + glVertex3f(1.0f, 1.0f, 0.0f); + glVertexAttrib2f(tcAttrib, 0.0f, 0.0f); + glVertexAttrib1f(lightAttrib, 1.0f); + glVertex3f(-1.0f, -1.0f, 0.0f); + glVertexAttrib2f(tcAttrib, 0.0f, 1.0f); + glVertexAttrib1f(lightAttrib, 1.0f); + glVertex3f(-1.0f, 1.0f, 0.0f); + glEnd(); + + // Finish drawing the 3d view + glFlush(); + + glUniform1i(textureShaderProgram.uniNightSight, false); + + // Check for OpenGL errors that might have happened + GLenum err = glGetError(); + if (err != GL_NO_ERROR) + ERROR("OpenGL error: %i", err); + + // Blit the UI canvas over the 3d view + SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, ui); + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + + SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_DestroyTexture(texture); + + // Finally, swap to the screen + SDL_RenderPresent(renderer); +} + +void toggle_opengl() { + if (gShockPrefs.doUseOpenGL) { + switch (gShockPrefs.doTextureFilter) { + case 0: { + message_info("Switching to OpenGL bilinear rendering"); + gShockPrefs.doTextureFilter = 1; + } break; + case 1: { + message_info("Switching to sofware rendering"); + gShockPrefs.doUseOpenGL = false; + gShockPrefs.doTextureFilter = 0; + } break; + } + } else { + message_info("Switching to OpenGL unfiltered"); + gShockPrefs.doUseOpenGL = true; + gShockPrefs.doTextureFilter = 0; + } + SavePrefs(); +} + +void opengl_set_viewport(int x, int y, int width, int height) { + render_width = width; + render_height = height; + + SDL_GL_MakeCurrent(window, context); + + int lw, lh; + SDL_RenderGetLogicalSize(renderer, &lw, &lh); + + int draw_y = lh - height - y; + glViewport(x, draw_y, width, height); + + // Make sure everything starts with a stencil of 0xFF + glEnable(GL_SCISSOR_TEST); + glScissor(x, draw_y, width, height); + glClearStencil(0xFF); + glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + + // Draw everything with a stencil of 0 + opengl_set_stencil(0x00); +} + +static bool opengl_cache_texture(CachedTexture toCache, grs_bitmap *bm) { + SDL_GL_MakeCurrent(window, context); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, bm->row); + + if (texturesByBitsPtr.size() < MAX_CACHED_TEXTURES) { + // We have enough room, generate the new texture + glGenTextures(1, &toCache.texture); + bind_texture(toCache.texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bm->w, bm->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, toCache.converted->pixels); + + texturesByBitsPtr[(uint64_t)bm->bits | ((uint64_t)bm->w << 32u) | ((uint64_t)bm->h << 48u)] = toCache; + return true; + } + + // Not enough room, just use the dynTexture + bind_texture(dynTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bm->w, bm->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, toCache.converted->pixels); + + return false; +} + +static CachedTexture *opengl_get_texture(grs_bitmap *bm) { + auto iter = texturesByBitsPtr.find((uint64_t)bm->bits | ((uint64_t)bm->w << 32u) | ((uint64_t)bm->h << 48u)); + if (iter != texturesByBitsPtr.end()) { + return &iter->second; + } + return nullptr; +} + +void opengl_clear_texture_cache() { + if (texturesByBitsPtr.empty()) + return; + + DEBUG("Clearing OpenGL texture cache."); + + SDL_GL_MakeCurrent(window, context); + + for (auto iter = texturesByBitsPtr.begin(); iter != texturesByBitsPtr.end();) { + // don't free locked surfaces + if (!iter->second.locked) { + SDL_FreeSurface(iter->second.bitmap); + SDL_FreeSurface(iter->second.converted); + glDeleteTextures(1, &iter->second.texture); + + iter = texturesByBitsPtr.erase(iter); + } else { + iter++; + } + } +} + +static void setPaletteForBitmap(grs_bitmap *bm, SDL_Surface *surface) { + if (bm->flags & BMF_TRANS) + SDL_SetSurfacePalette(surface, transparentPalette); + else + SDL_SetSurfacePalette(surface, opaquePalette); +} + +static void convert_texture(grs_bitmap *bm, bool locked) { + SDL_Surface *surface; + if (bm->type == BMT_RSD8) { + grs_bitmap decoded; + gr_rsd8_convert(bm, &decoded); + surface = SDL_CreateRGBSurfaceFrom(decoded.bits, bm->w, bm->h, 8, bm->row, 0, 0, 0, 0); + } else { + surface = SDL_CreateRGBSurfaceFrom(bm->bits, bm->w, bm->h, 8, bm->row, 0, 0, 0, 0); + } + + setPaletteForBitmap(bm, surface); + SDL_Surface *rgba = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_RGBA32, 0); + + // Cache this new surface. + CachedTexture ct{}; + ct.bitmap = surface; + ct.converted = rgba; + ct.lastDrawTime = *tmd_ticks; + ct.locked = locked; + + bool cached = opengl_cache_texture(ct, bm); + if (!cached) { + DEBUG("Not enough room to cache texture!"); + SDL_FreeSurface(surface); + SDL_FreeSurface(rgba); + } +} + +void opengl_cache_wall_texture(int idx, int size, grs_bitmap *bm) { + if (idx < NUM_LOADED_TEXTURES) { + CachedTexture *t = opengl_get_texture(bm); + if (t == nullptr) { + convert_texture(bm, true); + } else { + // Need to refresh this texture + t->lastDrawTime = -1; + palette_dirty = true; + } + } +} + +static void set_texture(grs_bitmap *bm) { + CachedTexture *t = opengl_get_texture(bm); + if (t == nullptr) { + // Not cached, have to make it + convert_texture(bm, false); + return; + } + + bool isDirty = false; + + if (t->locked) { + // Locked surfaces only need to update once + if (palette_dirty && t->lastDrawTime != *tmd_ticks) { + SDL_SetSurfacePalette(t->bitmap, transparentPalette); // Walls should show stars + SDL_BlitSurface(t->bitmap, nullptr, t->converted, nullptr); + + isDirty = true; + } + } else { + if (bm->type == BMT_RSD8) { + grs_bitmap decoded; + gr_rsd8_convert(bm, &decoded); + SDL_memmove(t->bitmap->pixels, decoded.bits, bm->w * bm->h); + } else { + SDL_memmove(t->bitmap->pixels, bm->bits, bm->w * bm->h); + } + + setPaletteForBitmap(bm, t->bitmap); + SDL_BlitSurface(t->bitmap, nullptr, t->converted, nullptr); + + isDirty = true; + } + + bind_texture(t->texture); + t->lastDrawTime = *tmd_ticks; + + if (isDirty) { + glPixelStorei(GL_UNPACK_ROW_LENGTH, bm->row); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bm->w, bm->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, t->converted->pixels); + } +} + +static void draw_vertex(const g3s_point &vertex, GLint tcAttrib, GLint lightAttrib) { + + // Default, per-vertex lighting + float light = 1.0f - (vertex.i / 4096.0f); + + if (nightsight_active()) { + light = 1.0f; + } else if (gr_get_fill_type() == FILL_CLUT) { + // Could be a CLUT color instead, use that for lighting + // Ugly hack: We don't get the original light value, so we have to + // recalculate it from the offset into the global lighting lookup + // table. + auto *clut = (uint8_t *)gr_get_fill_parm(); + light = 1.0f - (clut - grd_screen->ltab) / 4096.0f; + } + + if (tcAttrib >= 0) + glVertexAttrib2f(tcAttrib, vertex.uv.u / 256.0, vertex.uv.v / 256.0); + glVertexAttrib1f(lightAttrib, light); + glVertex3f(vertex.x / 65536.0f, vertex.y / 65536.0f, -vertex.z / 65536.0f); +} + +int opengl_draw_tmap(int n, g3s_phandle *vp, grs_bitmap *bm) { return opengl_light_tmap(n, vp, bm); } + +int opengl_light_tmap(int n, g3s_phandle *vp, grs_bitmap *bm) { + if (n != 3 && n != 4) { + WARN("Unexpected number of texture vertices (%d)", n); + return CLIP_ALL; + } + + SDL_GL_MakeCurrent(window, context); + set_blend_mode(bm->flags & BMF_TRANS); + + glUseProgram(textureShaderProgram.shaderProgram); + + set_texture(bm); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, get_texture_min_func()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, get_texture_mag_func()); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + GLint tcAttrib = textureShaderProgram.tcAttrib; + GLint lightAttrib = textureShaderProgram.lightAttrib; + + glUniformMatrix4fv(textureShaderProgram.uniView, 1, false, ViewMatrix); + glUniformMatrix4fv(textureShaderProgram.uniProj, 1, false, ProjectionMatrix); + + glBegin(GL_TRIANGLE_STRIP); + draw_vertex(*(vp[1]), tcAttrib, lightAttrib); + draw_vertex(*(vp[0]), tcAttrib, lightAttrib); + draw_vertex(*(vp[2]), tcAttrib, lightAttrib); + if (n > 3) + draw_vertex(*(vp[3]), tcAttrib, lightAttrib); + glEnd(); + + return CLIP_NONE; +} + +static float convx(float x) { return x / 32768.0f / render_width - 1; } + +static float convy(float y) { return -y / 32768.0f / render_height + 1; } + +int opengl_bitmap(grs_bitmap *bm, int n, grs_vertex **vpl, grs_tmap_info *ti) { + if (n != 4) { + WARN("Unexpected number of bitmap vertices (%d)", n); + return CLIP_ALL; + } + + SDL_GL_MakeCurrent(window, context); + set_blend_mode(bm->flags & BMF_TRANS); + + glUseProgram(textureShaderProgram.shaderProgram); + GLint tcAttrib = textureShaderProgram.tcAttrib; + GLint lightAttrib = textureShaderProgram.lightAttrib; + + glUniformMatrix4fv(textureShaderProgram.uniView, 1, false, IdentityMatrix); + glUniformMatrix4fv(textureShaderProgram.uniProj, 1, false, IdentityMatrix); + + set_texture(bm); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, get_texture_min_func()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, get_texture_mag_func()); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + + float light = 1.0f; + if ((ti->flags & TMF_CLUT) && !nightsight_active()) { + // Ugly hack: We don't get the original 'i' value, so we have to + // recalculate it from the offset into the global lighting lookup + // table. + light = 1.0 - (ti->clut - grd_screen->ltab) / 4096.0f; + } + + glUniform1i(textureShaderProgram.uniMutant, (bm->type == BMT_TLUC8) || (bm->flags == BMF_TLUC8)); + + glBegin(GL_TRIANGLE_STRIP); + glVertexAttrib2f(tcAttrib, 1.0f, 0.0f); + glVertexAttrib1f(lightAttrib, light); + glVertex3f(convx(vpl[1]->x), convy(vpl[1]->y), 0.0f); + glVertexAttrib2f(tcAttrib, 0.0f, 0.0f); + glVertexAttrib1f(lightAttrib, light); + glVertex3f(convx(vpl[0]->x), convy(vpl[0]->y), 0.0f); + glVertexAttrib2f(tcAttrib, 1.0f, 1.0f); + glVertexAttrib1f(lightAttrib, light); + glVertex3f(convx(vpl[2]->x), convy(vpl[2]->y), 0.0f); + glVertexAttrib2f(tcAttrib, 0.0f, 1.0f); + glVertexAttrib1f(lightAttrib, light); + glVertex3f(convx(vpl[3]->x), convy(vpl[3]->y), 0.0f); + glEnd(); + + glUniform1i(textureShaderProgram.uniMutant, false); + + return CLIP_NONE; +} + +static void set_color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) { + glVertexAttrib4f(colorShaderProgram.colorAttrib, red / 255.0f, green / 255.0f, blue / 255.0f, alpha / 255.0f); + set_blend_mode(alpha < 255); +} + +int opengl_draw_poly(long c, int n_verts, g3s_phandle *p, char gour_flag) { + if (n_verts < 3) { + WARN("Unexpected number of polygon vertices (%d)", n_verts); + return CLIP_ALL; + } + + SDL_GL_MakeCurrent(window, context); + + glUseProgram(colorShaderProgram.shaderProgram); + glUniformMatrix4fv(colorShaderProgram.uniView, 1, false, ViewMatrix); + glUniformMatrix4fv(colorShaderProgram.uniProj, 1, false, ProjectionMatrix); + + if (gour_flag == 1 || gour_flag == 3) { + // translucent; see init_pal_fx() for translucency parameters + switch (c) { + case 247: + set_color(120, 120, 120, 80); + break; // dark fog + case 248: + set_color(170, 170, 170, 80); + break; // medium fog + case 249: + set_color(255, 0, 0, 80); + break; // red fog + case 250: + set_color(0, 255, 0, 80); + break; // green fog + case 251: + set_color(0, 0, 255, 80); + break; // blue fog + case 252: + set_color(240, 240, 240, 80); + break; // light fog + case 253: + set_color(0, 0, 255, 128); + break; // blue force field + case 254: + set_color(0, 255, 0, 128); + break; // green force field + case 255: + set_color(255, 0, 0, 128); + break; // red force field + default: + return CLIP_ALL; + } + } else if (c == 255) { + // transparent + return CLIP_NONE; + } else { + // solid color + SDL_Color color = sdlPalette->colors[c]; + set_color(color.r, color.g, color.b, 255); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, get_texture_min_func()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, get_texture_mag_func()); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + GLint lightAttrib = colorShaderProgram.lightAttrib; + + long a = 0, b = n_verts - 1; + glBegin(GL_TRIANGLE_STRIP); + while (true) { + draw_vertex(*(p[a]), -1, lightAttrib); + if (a++ == b) + break; + draw_vertex(*(p[b]), -1, lightAttrib); + if (b-- == a) + break; + } + glEnd(); + + return CLIP_NONE; +} + +void opengl_set_stencil(int v) { + glEnable(GL_STENCIL_TEST); + glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); + glStencilFunc(GL_ALWAYS, v, ~0u); +} + +void opengl_begin_stars() { + SDL_GL_MakeCurrent(window, context); + + glPointSize(1.5 * (render_width / 320.0)); + + glUseProgram(starShaderProgram.shaderProgram); + glUniformMatrix4fv(starShaderProgram.uniView, 1, false, IdentityMatrix); + glUniformMatrix4fv(starShaderProgram.uniProj, 1, false, IdentityMatrix); + + // Only draw stars where the stencil value is 0xFF (Sky!) + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilFunc(GL_EQUAL, 0xFF, ~0u); + + set_blend_mode(true); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, get_texture_mag_func()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, get_texture_mag_func()); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glBegin(GL_POINTS); +} + +void opengl_end_stars() { + glEnd(); + + // Turn off the stencil test and blending + opengl_set_stencil(0x00); + set_blend_mode(false); +} + +int opengl_draw_star(fix star_x, fix star_y, int c, bool anti_alias) { + SDL_GL_MakeCurrent(window, context); + + GLint lightAttrib = starShaderProgram.lightAttrib; + + float x = fix_float(star_x); + float y = fix_float(star_y); + + // Lower screen resolutions should snap to pixels to avoid shimmering + if (!anti_alias) { + x = (int)x + 0.5f; + y = (int)y + 0.5f; + } + + x = (x / render_width) * 2.0 - 1.0; + y = (y / render_height) * -2.0 + 1.0; + + // rescale the color so that it's 0..255, 0 = dark, 255 = light + int std_color_base = 208; + int std_color_range = 16; + int color = (std_color_base + std_color_range - 1 - c); + color = (255 * color) / (std_color_range + 1); + + glVertexAttrib1f(lightAttrib, color / 255.0f); + glVertex3f(x, y, -0.25f); + + return CLIP_NONE; +} + +void opengl_begin_sensaround(uchar version) { + if (version == 1) { + // Version 1 of the sensaround is old tech, and should render in software mode :D + opengl_enabled = false; + } +} + +void opengl_end_sensaround() { opengl_enabled = true; } + +#endif // USE_OPENGL diff --git a/engine/src/MacSrc/OpenGL.h b/engine/src/MacSrc/OpenGL.h new file mode 100644 index 0000000..b11c893 --- /dev/null +++ b/engine/src/MacSrc/OpenGL.h @@ -0,0 +1,73 @@ +#ifndef __MACSRC_OPENGL_H +#define __MACSRC_OPENGL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <3d.h> +#include + +#ifdef USE_OPENGL + +int init_opengl(); +void opengl_cache_wall_texture(int idx, int size, grs_bitmap *bm); +void opengl_clear_texture_cache(); + +bool can_use_opengl(); +bool use_opengl(); +void toggle_opengl(); +void opengl_resize(int width, int height); +bool should_opengl_swap(); +void opengl_swap_and_restore(SDL_Surface *ui); +void opengl_change_palette(); + +void opengl_set_viewport(int x, int y, int width, int height); +int opengl_draw_tmap(int n, g3s_phandle *vp, grs_bitmap *bm); +int opengl_light_tmap(int n, g3s_phandle *vp, grs_bitmap *bm); +int opengl_bitmap(grs_bitmap *bm, int n, grs_vertex **vpl, grs_tmap_info *ti); +int opengl_draw_poly(long c, int n_verts, g3s_phandle *p, char gour_flag); +int opengl_draw_star(fix star_x, fix star_y, int c, bool anti_alias); +void opengl_begin_stars(); +void opengl_end_stars(); +void opengl_set_stencil(int v); +void opengl_start_frame(); +void opengl_end_frame(); +void opengl_begin_sensaround(uchar version); +void opengl_end_sensaround(); + +#else + +static int init_opengl() { return 0; } +static void opengl_cache_wall_texture(int idx, int size, grs_bitmap *bm) {} +static void opengl_clear_texture_cache(){}; + +static bool can_use_opengl() { return false; } +static bool use_opengl() { return false; } +static void toggle_opengl() {} +static void opengl_resize(int width, int height) {} +static bool should_opengl_swap() { return false; } +static void opengl_swap_and_restore(SDL_Surface *ui) {} +static void opengl_change_palette() {} + +static void opengl_set_viewport(int x, int y, int width, int height) {} +static int opengl_draw_tmap(int n, g3s_phandle *vp, grs_bitmap *bm) { return 0; } +static int opengl_light_tmap(int n, g3s_phandle *vp, grs_bitmap *bm) { return 0; } +static int opengl_bitmap(grs_bitmap *bm, int n, grs_vertex **vpl, grs_tmap_info *ti) { return 0; } +static int opengl_draw_poly(long c, int n_verts, g3s_phandle *p, char gour_flag) { return 0; } +static int opengl_draw_star(fix star_x, fix star_y, int c, bool anti_alias) { return 0; } +static void opengl_begin_stars() {} +static void opengl_end_stars() {} +static void opengl_set_stencil(int v) {} +static void opengl_start_frame() {} +static void opengl_end_frame() {} +static void opengl_begin_sensaround(uchar version) {} +static void opengl_end_sensaround() {} + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/engine/src/MacSrc/Prefs.c b/engine/src/MacSrc/Prefs.c new file mode 100644 index 0000000..e5c1d98 --- /dev/null +++ b/engine/src/MacSrc/Prefs.c @@ -0,0 +1,1185 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +//==================================================================================== +// +// System Shock - ©1994-1995 Looking Glass Technologies, Inc. +// +// Prefs.c - Handles saving and loading preferences. +// Also loads and sets default keybinds. +// +//==================================================================================== + +//-------------------- +// Includes +//-------------------- +#include "Shock.h" +#include "Prefs.h" + +#include "popups.h" +#include "olhext.h" +#include "hotkey.h" +#include "input.h" +#include "mainloop.h" +#include "movekeys.h" +#include "mfdext.h" + +extern uchar mfd_button_callback_kb(ushort keycode, uint32_t context, intptr_t data); +extern uchar hw_hotkey_callback(ushort keycode, uint32_t context, intptr_t data); + +//-------------------- +// Filenames +//-------------------- +static const char *PREFS_FILENAME = "prefs.txt"; +static const char *KEYBINDS_FILENAME = "keybinds.txt"; + +//-------------------- +// Globals +//-------------------- +ShockPrefs gShockPrefs; +char which_lang; +uchar sfx_on = TRUE; + +//-------------------- +// Externs +//-------------------- +extern int _fr_global_detail; +extern bool DoubleSize; +extern bool SkipLines; +extern short mode_id; + +extern uchar curr_vol_lev; +extern uchar curr_sfx_vol; +extern uchar curr_alog_vol; + +extern uchar audiolog_setting; + +static const char *PREF_LANGUAGE = "language"; +static const char *PREF_CAPTUREMOUSE = "capture-mouse"; +static const char *PREF_INVERTMOUSEY = "invert-mousey"; +static const char *PREF_MUSIC_VOL = "music-volume"; +static const char *PREF_SFX_VOL = "sfx-volume"; +static const char *PREF_ALOG_VOL = "alog-volume"; +static const char *PREF_VIDEOMODE = "video-mode"; +static const char *PREF_HALFRES = "half-resolution"; +static const char *PREF_DETAIL = "detail"; +static const char *PREF_USE_OPENGL = "use-opengl"; +static const char *PREF_TEX_FILTER = "texture-filter"; +static const char *PREF_ONSCR_HELP = "onscreen-help"; +static const char *PREF_GAMMA = "gamma"; +static const char *PREF_MSG_LENGTH = "message-length"; +static const char *PREF_ALOG_SETTING = "alog-setting"; +static const char *PREF_MIDI_BACKEND = "midi-backend"; +static const char *PREF_MIDI_OUTPUT = "midi-output"; + +static void SetShockGlobals(void); + +//-------------------------------------------------------------------- +// Initialize the preferences to their default settings. +//-------------------------------------------------------------------- +void SetDefaultPrefs(void) { + + gShockPrefs.prefVer = 0; + gShockPrefs.prefPlayIntro = 1; // First time through, play the intro + gShockPrefs.goPopupLabels = true; + gShockPrefs.soBackMusic = true; +#ifdef USE_FLUIDSYNTH + gShockPrefs.soMidiBackend = 2; // default to fluidsynth when available +#else + gShockPrefs.soMidiBackend = 0; // default to adlmidi +#endif + gShockPrefs.soMidiOutput = 0; // default to zero + gShockPrefs.soSoundFX = true; + gShockPrefs.doUseQD = false; + + // saved in prefs file + + gShockPrefs.goLanguage = 0; // English + gShockPrefs.goCaptureMouse = true; + gShockPrefs.goInvertMouseY = false; + gShockPrefs.soMusicVolume = 75; + gShockPrefs.soSfxVolume = 100; + gShockPrefs.soAudioLogVolume = 100; + gShockPrefs.doVideoMode = 3; + gShockPrefs.doResolution = 0; // High-res. + gShockPrefs.doDetail = 3; // Max detail. + gShockPrefs.doUseOpenGL = false; + gShockPrefs.doTextureFilter = 0; // unfiltered + gShockPrefs.goOnScreenHelp = true; + gShockPrefs.doGamma = 29; // Default gamma (29 out of 100). + gShockPrefs.goMsgLength = 0; // Normal + audiolog_setting = 1; + + SetShockGlobals(); +} + +static char *GetPrefsPathFilename(void) { + static char filename[512]; + + FILE *f = fopen(PREFS_FILENAME, "r"); + if (f != NULL) { + fclose(f); + strcpy(filename, PREFS_FILENAME); + } else { + char *p = SDL_GetPrefPath("Interrupt", "SystemShock"); + snprintf(filename, sizeof(filename), "%s%s", p, PREFS_FILENAME); + SDL_free(p); + } + + return filename; +} + +static char *trim(char *s) { + while (*s && isspace(*s)) + s++; + char *c = &s[strlen(s) - 1]; + while (c >= s && isspace(*c)) + *(c--) = '\0'; + return s; +} + +static bool is_true(const char *s) { + return strcasecmp(s, "yes") == 0 || strcasecmp(s, "true") == 0 || strcmp(s, "1") == 0; +} + +//-------------------------------------------------------------------- +// Locate the preferences file and load them to set our global pref settings. +//-------------------------------------------------------------------- +int16_t LoadPrefs(void) { + FILE *f = fopen(GetPrefsPathFilename(), "r"); + if (!f) { + // file can't be open, write default preferences + return SavePrefs(); + } + + char line[64]; + while (fgets(line, sizeof(line), f)) { + char *eq = strchr(line, '='); + if (!eq) + continue; + *eq = '\0'; + + const char *key = trim(line); + const char *value = trim(eq + 1); + + if (strcasecmp(key, PREF_LANGUAGE) == 0) { + int lang = atoi(value); + if (lang >= 0 && lang <= 2) + gShockPrefs.goLanguage = lang; + } else if (strcasecmp(key, PREF_CAPTUREMOUSE) == 0) { + gShockPrefs.goCaptureMouse = is_true(value); + } else if (strcasecmp(key, PREF_INVERTMOUSEY) == 0) { + gShockPrefs.goInvertMouseY = is_true(value); + } else if (strcasecmp(key, PREF_MUSIC_VOL) == 0) { + int vol = atoi(value); + if (vol >= 0 && vol <= 100) { + gShockPrefs.soBackMusic = vol > 0; + gShockPrefs.soMusicVolume = vol; + } + } else if (strcasecmp(key, PREF_SFX_VOL) == 0) { + int vol = atoi(value); + if (vol >= 0 && vol <= 100) { + gShockPrefs.soSoundFX = vol > 0; + gShockPrefs.soSfxVolume = vol; + } + } else if (strcasecmp(key, PREF_ALOG_VOL) == 0) { + int vol = atoi(value); + if (vol >= 0 && vol <= 100) + gShockPrefs.soAudioLogVolume = vol; + } else if (strcasecmp(key, PREF_VIDEOMODE) == 0) { + int mode = atoi(value); + if (mode >= 0 && mode <= 4) + gShockPrefs.doVideoMode = mode; + } else if (strcasecmp(key, PREF_HALFRES) == 0) { + gShockPrefs.doResolution = is_true(value); + } else if (strcasecmp(key, PREF_DETAIL) == 0) { + int detail = atoi(value); + if (detail >= 0 && detail <= 3) + gShockPrefs.doDetail = detail; + } else if (strcasecmp(key, PREF_USE_OPENGL) == 0) { + gShockPrefs.doUseOpenGL = is_true(value); + } else if (strcasecmp(key, PREF_TEX_FILTER) == 0) { + int mode = atoi(value); + if (mode >= 0 && mode <= 1) + gShockPrefs.doTextureFilter = (short)mode; + } else if (strcasecmp(key, PREF_ONSCR_HELP) == 0) { + gShockPrefs.goOnScreenHelp = is_true(value); + } else if (strcasecmp(key, PREF_GAMMA) == 0) { + int gamma = atoi(value); + if (gamma < 10) + gamma = 10; + if (gamma > 100) + gamma = 100; + gShockPrefs.doGamma = gamma; + } else if (strcasecmp(key, PREF_MSG_LENGTH) == 0) { + int ml = atoi(value); + if (ml >= 0 && ml <= 1) + gShockPrefs.goMsgLength = ml; + } else if (strcasecmp(key, PREF_ALOG_SETTING) == 0) { + int as = atoi(value); + if (as >= 0 && as <= 2) + audiolog_setting = as; + } else if (strcasecmp(key, PREF_MIDI_BACKEND) == 0) { + int mb = atoi(value); + if (mb >= 0 && mb <= 2) + gShockPrefs.soMidiBackend = (short)mb; + } else if (strcasecmp(key, PREF_MIDI_OUTPUT) == 0) { + int mo = atoi(value); + if (mo >= 0) + gShockPrefs.soMidiOutput = (short)mo; + } + } + + fclose(f); + SetShockGlobals(); + return 0; +} + +//-------------------------------------------------------------------- +// Save global settings in the preferences file. +//-------------------------------------------------------------------- +int16_t SavePrefs(void) { + INFO("Saving preferences"); + + FILE *f = fopen(GetPrefsPathFilename(), "w"); + if (!f) { + printf("ERROR: Failed to open preferences file\n"); + return -1; + } + + fprintf(f, "%s = %d\n", PREF_LANGUAGE, which_lang); + fprintf(f, "%s = %s\n", PREF_CAPTUREMOUSE, gShockPrefs.goCaptureMouse ? "yes" : "no"); + fprintf(f, "%s = %s\n", PREF_INVERTMOUSEY, gShockPrefs.goInvertMouseY ? "yes" : "no"); + fprintf(f, "%s = %d\n", PREF_MUSIC_VOL, curr_vol_lev); + fprintf(f, "%s = %d\n", PREF_SFX_VOL, sfx_on ? curr_sfx_vol : 0); + fprintf(f, "%s = %d\n", PREF_ALOG_VOL, curr_alog_vol); + fprintf(f, "%s = %d\n", PREF_VIDEOMODE, mode_id); + fprintf(f, "%s = %s\n", PREF_HALFRES, DoubleSize ? "yes" : "no"); + fprintf(f, "%s = %d\n", PREF_DETAIL, _fr_global_detail); + fprintf(f, "%s = %s\n", PREF_USE_OPENGL, gShockPrefs.doUseOpenGL ? "yes" : "no"); + fprintf(f, "%s = %d\n", PREF_TEX_FILTER, gShockPrefs.doTextureFilter); + fprintf(f, "%s = %s\n", PREF_ONSCR_HELP, gShockPrefs.goOnScreenHelp ? "yes" : "no"); + fprintf(f, "%s = %d\n", PREF_GAMMA, gShockPrefs.doGamma); + fprintf(f, "%s = %d\n", PREF_MSG_LENGTH, gShockPrefs.goMsgLength); + fprintf(f, "%s = %d\n", PREF_ALOG_SETTING, audiolog_setting); + fprintf(f, "%s = %d\n", PREF_MIDI_BACKEND, gShockPrefs.soMidiBackend); + fprintf(f, "%s = %d\n", PREF_MIDI_OUTPUT, gShockPrefs.soMidiOutput); + fclose(f); + return 0; +} + +//-------------------------------------------------------------------- +// Set the corresponding Shock globals from the prefs structure. +//-------------------------------------------------------------------- +static void SetShockGlobals(void) { + popup_cursors = gShockPrefs.goPopupLabels; + olh_active = gShockPrefs.goOnScreenHelp; + which_lang = gShockPrefs.goLanguage; + + sfx_on = gShockPrefs.soSoundFX; + curr_vol_lev = gShockPrefs.soMusicVolume; + curr_sfx_vol = gShockPrefs.soSfxVolume; + curr_alog_vol = gShockPrefs.soAudioLogVolume; + + mode_id = gShockPrefs.doVideoMode; + DoubleSize = (gShockPrefs.doResolution == 1); // Set this True for low-res. + SkipLines = gShockPrefs.doUseQD; + _fr_global_detail = gShockPrefs.doDetail; +} + +//************************************************************************************ + +//******** +// Keybinds +//******** + +// Note that Alt / Option (on Mac) modifier key won't work until it is implemented in sdl_events.c + +static struct { + const char *s; + int ch, code; +} KeyName2ChCode[] = {{"backspace ", 8, 0x33}, + {"tab ", 9, 0x30}, + {"enter ", 13, 0x24}, + {"escape ", 27, 0x35}, + {"space ", 32, 0x31}, + {"1 ", 49, 0x12}, + {"exclamation ", 33, 0x12}, + {"2 ", 50, 0x13}, + {"atsign ", 64, 0x13}, + {"3 ", 51, 0x14}, + {"numbersign ", 35, 0x14}, + {"4 ", 52, 0x15}, + {"dollar ", 36, 0x15}, + {"5 ", 53, 0x17}, + {"percent ", 37, 0x17}, + {"6 ", 54, 0x16}, + {"caret ", 94, 0x16}, + {"7 ", 55, 0x1A}, + {"ampersand ", 38, 0x1A}, + {"8 ", 56, 0x1C}, + {"asterisk ", 42, 0x1C}, + {"9 ", 57, 0x19}, + {"lparenthesis ", 40, 0x19}, + {"0 ", 48, 0x1D}, + {"rparenthesis ", 41, 0x1D}, + {"equals ", 61, 0x18}, + {"plus ", 43, 0x18}, + {"comma ", 44, 0x2B}, + {"lessthan ", 60, 0x2B}, + {"minus ", 45, 0x1B}, + {"underscore ", 95, 0x1B}, + {"period ", 46, 0x2F}, + {"greaterthan ", 62, 0x2F}, + {"slash ", 47, 0x2C}, + {"questionmark ", 63, 0x2C}, + {"quote ", 39, 0x27}, + {"doublequote ", 34, 0x27}, + {"semicolon ", 59, 0x29}, + {"colon ", 58, 0x29}, + {"a ", 97, 0x00}, + {"b ", 98, 0x0B}, + {"c ", 99, 0x08}, + {"d ", 100, 0x02}, + {"e ", 101, 0x0E}, + {"f ", 102, 0x03}, + {"g ", 103, 0x05}, + {"h ", 104, 0x04}, + {"i ", 105, 0x22}, + {"j ", 106, 0x26}, + {"k ", 107, 0x28}, + {"l ", 108, 0x25}, + {"m ", 109, 0x2E}, + {"n ", 110, 0x2D}, + {"o ", 111, 0x1F}, + {"p ", 112, 0x23}, + {"q ", 113, 0x0C}, + {"r ", 114, 0x0F}, + {"s ", 115, 0x01}, + {"t ", 116, 0x11}, + {"u ", 117, 0x20}, + {"v ", 118, 0x09}, + {"w ", 119, 0x0D}, + {"x ", 120, 0x07}, + {"y ", 121, 0x10}, + {"z ", 122, 0x06}, + {"A ", 65, 0x00}, + {"B ", 66, 0x0B}, + {"C ", 67, 0x08}, + {"D ", 68, 0x02}, + {"E ", 69, 0x0E}, + {"F ", 70, 0x03}, + {"G ", 71, 0x05}, + {"H ", 72, 0x04}, + {"I ", 73, 0x22}, + {"J ", 74, 0x26}, + {"K ", 75, 0x28}, + {"L ", 76, 0x25}, + {"M ", 77, 0x2E}, + {"N ", 78, 0x2D}, + {"O ", 79, 0x1F}, + {"P ", 80, 0x23}, + {"Q ", 81, 0x0C}, + {"R ", 82, 0x0F}, + {"S ", 83, 0x01}, + {"T ", 84, 0x11}, + {"U ", 85, 0x20}, + {"V ", 86, 0x09}, + {"W ", 87, 0x0D}, + {"X ", 88, 0x07}, + {"Y ", 89, 0x10}, + {"Z ", 90, 0x06}, + {"lbracket ", 91, 0x21}, + {"lcurbrace ", 123, 0x21}, + {"backslash ", 92, 0x2A}, + {"vertline ", 124, 0x2A}, + {"rbracket ", 93, 0x1E}, + {"rcurbrace ", 125, 0x1E}, + {"backquote ", 96, 0x32}, + {"tilde ", 126, 0x32}, + {"delete ", 127, 0x33}, + + // use these invented "ascii" codes for hotkey system + // see sdl_events.c + {"f1 ", 128 + 0, 0x7A}, + {"f2 ", 128 + 1, 0x78}, + {"f3 ", 128 + 2, 0x63}, + {"f4 ", 128 + 3, 0x76}, + {"f5 ", 128 + 4, 0x60}, + {"f6 ", 128 + 5, 0x61}, + {"f7 ", 128 + 6, 0x62}, + {"f8 ", 128 + 7, 0x64}, + {"f9 ", 128 + 8, 0x65}, + {"f10 ", 128 + 9, 0x6D}, + {"f11 ", 128 + 10, 0x67}, + {"f12 ", 128 + 11, 0x6F}, + {"keypad_divide ", 128 + 12, 0x4B}, + {"keypad_multiply ", 128 + 13, 0x43}, + {"keypad_minus ", 128 + 14, 0x4E}, + {"keypad_plus ", 128 + 15, 0x45}, + {"keypad_enter ", 128 + 16, 0x4C}, + {"keypad_decimal ", 128 + 17, 0x41}, + {"keypad_0 ", 128 + 18, 0x52}, + + // these have no invented "ascii" codes so they can't be used as hotkeys, only move keys + {"keypad_home ", 0, 0x59}, + {"keypad_up ", 0, 0x5B}, + {"keypad_pgup ", 0, 0x5C}, + {"keypad_left ", 0, 0x56}, + {"keypad_5 ", 0, 0x57}, + {"keypad_right ", 0, 0x58}, + {"keypad_end ", 0, 0x53}, + {"keypad_down ", 0, 0x54}, + {"keypad_pgdn ", 0, 0x55}, + {"home ", 0, 0x73}, + {"up ", 0, 0x7E}, + {"pageup ", 0, 0x74}, + {"left ", 0, 0x7B}, + {"right ", 0, 0x7C}, + {"end ", 0, 0x77}, + {"down ", 0, 0x7D}, + {"pagedown ", 0, 0x79}, + + {NULL, 0, 0}}; + +// lower cases all characters in string p +// also converts tabs to spaces +static void LowerCaseInPlace(char *p) { + while (*p) { + if (*p >= 'A' && *p <= 'Z') + *p = *p - 'A' + 'a'; // convert upper to lower case + if (*p == '\t') + *p = ' '; // convert tab to space + p++; + } +} + +//********************************* +// Set hotkey keybinds (see input.c) +// Also handles fire keybinds +//********************************* + +#ifdef AUDIOLOGS +extern uchar audiolog_cancel_func(ushort keycode, uint32_t context, intptr_t data); +#endif +extern uchar posture_hotkey_func(ushort keycode, uint32_t context, intptr_t data); +extern uchar toggle_mouse_look(ushort keycode, uint32_t context, intptr_t data); +extern uchar change_mode_func(ushort keycode, uint32_t context, intptr_t data); +extern uchar clear_fullscreen_func(ushort keycode, uint32_t context, intptr_t data); +extern uchar saveload_hotkey_func(ushort keycode, uint32_t context, intptr_t data); +extern uchar pause_game_func(ushort keycode, uint32_t context, intptr_t data); +extern uchar reload_weapon_hotkey(ushort keycode, uint32_t context, intptr_t data); +extern uchar select_grenade_hotkey(ushort keycode, uint32_t context, intptr_t data); +extern uchar toggle_olh_func(ushort keycode, uint32_t context, intptr_t data); +extern uchar select_drug_hotkey(ushort keycode, uint32_t context, intptr_t data); +extern uchar toggle_music_func(ushort keycode, uint32_t context, intptr_t data); +extern uchar demo_quit_func(ushort keycode, uint32_t context, intptr_t data); +extern uchar cycle_weapons_func(ushort keycode, uint32_t context, intptr_t data); +extern uchar MacDetailFunc(ushort keycode, uint32_t context, intptr_t data); +extern uchar toggle_opengl_func(ushort keycode, uint32_t context, intptr_t data); +extern uchar arm_grenade_hotkey(ushort keycode, uint32_t context, intptr_t data); +extern uchar use_drug_hotkey(ushort keycode, uint32_t context, intptr_t data); +extern uchar hud_color_bank_cycle(ushort keycode, uint32_t context, intptr_t data); +extern uchar olh_overlay_func(ushort keycode, uint32_t context, intptr_t data); +extern uchar keypad_hotkey_func(ushort keycode, uint32_t context, intptr_t data); +extern uchar MacHelpFunc(ushort keycode, uint32_t context, intptr_t data); +extern uchar wrapper_options_func(ushort keycode, uint32_t context, intptr_t data); +extern uchar toggle_giveall_func(ushort keycode, uint32_t context, intptr_t data); +extern uchar toggle_physics_func(ushort keycode, uint32_t context, intptr_t data); +extern uchar toggle_up_level_func(ushort keycode, uint32_t context, intptr_t data); +extern uchar toggle_down_level_func(ushort keycode, uint32_t context, intptr_t data); + +#define TAB_KEY (KEY_TAB | KB_FLAG_DOWN) +#define S_TAB_KEY (KEY_TAB | KB_FLAG_DOWN | KB_FLAG_SHIFT) + +#define DOWN(x) ((x) | KB_FLAG_DOWN) +#define SHIFT(x) (DOWN(x) | KB_FLAG_SHIFT) +#define CTRL(x) (DOWN(x) | KB_FLAG_CTRL) +#define ALT(x) (DOWN(x) | KB_FLAG_ALT) + +typedef struct HOTKEYLOOKUP_STRUCT { + const char *s; + intptr_t contexts; + hotkey_callback func; + intptr_t state; + bool used; + int def1, def2; +} HOTKEYLOOKUP; + +HOTKEYLOOKUP HotKeyLookup[] = { +// name contexts func state used default key 1,2 +#ifdef AUDIOLOGS + {"\"audiolog_cancel\"", DEMO_CONTEXT, audiolog_cancel_func, 0, 0, CTRL('.'), 0}, +#endif + {"\"stand\"", DEMO_CONTEXT, posture_hotkey_func, 0, 0, DOWN('t'), SHIFT('t')}, + {"\"crouch\"", DEMO_CONTEXT, posture_hotkey_func, 1, 0, DOWN('g'), SHIFT('g')}, + {"\"prone\"", DEMO_CONTEXT, posture_hotkey_func, 2, 0, DOWN('b'), SHIFT('b')}, + {"\"toggle_freelook\"", DEMO_CONTEXT, toggle_mouse_look, TRUE, 0, DOWN('f'), 0}, + {"\"full_view\"", DEMO_CONTEXT, change_mode_func, FULLSCREEN_LOOP, 0, CTRL('f'), 0}, + {"\"normal_view\"", DEMO_CONTEXT, change_mode_func, GAME_LOOP, 0, CTRL('d'), 0}, + {"\"map_view\"", DEMO_CONTEXT, change_mode_func, AUTOMAP_LOOP, 0, CTRL('a'), 0}, + {"\"clear_fullscreen\"", DEMO_CONTEXT, clear_fullscreen_func, 0, 0, DOWN(KEY_BS), 0}, + {"\"save_game\"", DEMO_CONTEXT, saveload_hotkey_func, FALSE, 0, CTRL('s'), 0}, + {"\"load_game\"", DEMO_CONTEXT, saveload_hotkey_func, TRUE, 0, CTRL('l'), 0}, + {"\"pause\"", DEMO_CONTEXT, pause_game_func, TRUE, 0, DOWN('p'), 0}, + {"\"reload_weapon 1\"", DEMO_CONTEXT, reload_weapon_hotkey, 1, 0, CTRL(KEY_BS), 0}, + {"\"reload_weapon 0\"", DEMO_CONTEXT, reload_weapon_hotkey, 0, 0, ALT(KEY_BS), 0}, + {"\"select_grenade\"", DEMO_CONTEXT, select_grenade_hotkey, 0, 0, CTRL('\''), 0}, + {"\"toggle_olh\"", DEMO_CONTEXT, toggle_olh_func, 0, 0, CTRL('h'), 0}, + {"\"select_drug\"", DEMO_CONTEXT, select_drug_hotkey, 0, 0, CTRL(';'), 0}, + {"\"toggle_music\"", DEMO_CONTEXT, toggle_music_func, 0, 0, CTRL('m'), 0}, + {"\"quit\"", DEMO_CONTEXT, demo_quit_func, 0, 0, CTRL('q'), 0}, + {"\"cycle_weapons 1\"", DEMO_CONTEXT, cycle_weapons_func, 1, 0, TAB_KEY, 0}, + {"\"cycle_weapons -1\"", DEMO_CONTEXT, cycle_weapons_func, -1, 0, S_TAB_KEY, 0}, + {"\"cycle_detail\"", DEMO_CONTEXT, MacDetailFunc, 0, 0, CTRL('1'), 0}, + {"\"toggle_opengl\"", EVERY_CONTEXT, toggle_opengl_func, 0, 0, CTRL('g'), 0}, + {"\"arm_grenade\"", DEMO_CONTEXT, arm_grenade_hotkey, 0, 0, ALT('\''), 0}, + {"\"use_drug\"", DEMO_CONTEXT, use_drug_hotkey, 0, 0, ALT(';'), 0}, + {"\"hud_color\"", DEMO_CONTEXT, hud_color_bank_cycle, 0, 0, ALT('h'), 0}, + {"\"showhelp\"", DEMO_CONTEXT, olh_overlay_func, (intptr_t)&olh_overlay_on, 0, ALT('o'), 0}, + {"\"bio scan\"", DEMO_CONTEXT, hw_hotkey_callback, 5, 0, 49, 0}, + {"\"fullscreen\"", DEMO_CONTEXT, hw_hotkey_callback, 10, 0, 50, 0}, + {"\"360 view\"", DEMO_CONTEXT, hw_hotkey_callback, 2, 0, 51, 0}, + {"\"lantern\"", DEMO_CONTEXT, hw_hotkey_callback, 9, 0, 52, 0}, + {"\"shield\"", DEMO_CONTEXT, hw_hotkey_callback, 7, 0, 53, 0}, + {"\"infrared\"", DEMO_CONTEXT, hw_hotkey_callback, 0, 0, 54, 0}, + {"\"nav unit\"", DEMO_CONTEXT, hw_hotkey_callback, 6, 0, 55, 0}, + {"\"data reader\"", DEMO_CONTEXT, hw_hotkey_callback, 8, 0, 56, 0}, + {"\"booster\"", DEMO_CONTEXT, hw_hotkey_callback, 12, 0, 57, 0}, + {"\"jumpjets\"", DEMO_CONTEXT, hw_hotkey_callback, 13, 0, 48, 0}, + {"\"mfd left 1\"", DEMO_CONTEXT, mfd_button_callback_kb, ENCODE_MFD_SELECTION(MFD_LEFT, MFD_WEAPON_SLOT), 0, KEY_F1, 0}, + {"\"mfd left 2\"", DEMO_CONTEXT, mfd_button_callback_kb, ENCODE_MFD_SELECTION(MFD_LEFT, MFD_ITEM_SLOT), 0, KEY_F2, 0}, + {"\"mfd left 3\"", DEMO_CONTEXT, mfd_button_callback_kb, ENCODE_MFD_SELECTION(MFD_LEFT, MFD_MAP_SLOT), 0, KEY_F3, 0}, + {"\"mfd left 4\"", DEMO_CONTEXT, mfd_button_callback_kb, ENCODE_MFD_SELECTION(MFD_LEFT, MFD_TARGET_SLOT), 0, KEY_F4, 0}, + {"\"mfd left 5\"", DEMO_CONTEXT, mfd_button_callback_kb, ENCODE_MFD_SELECTION(MFD_LEFT, MFD_INFO_SLOT), 0, KEY_F5, 0}, + {"\"mfd right 1\"", DEMO_CONTEXT, mfd_button_callback_kb, ENCODE_MFD_SELECTION(MFD_RIGHT, MFD_WEAPON_SLOT), 0, KEY_F6, 0}, + {"\"mfd right 2\"", DEMO_CONTEXT, mfd_button_callback_kb, ENCODE_MFD_SELECTION(MFD_RIGHT, MFD_ITEM_SLOT), 0, KEY_F7, 0}, + {"\"mfd right 3\"", DEMO_CONTEXT, mfd_button_callback_kb, ENCODE_MFD_SELECTION(MFD_RIGHT, MFD_MAP_SLOT), 0, KEY_F8, 0}, + {"\"mfd right 4\"", DEMO_CONTEXT, mfd_button_callback_kb, ENCODE_MFD_SELECTION(MFD_RIGHT, MFD_TARGET_SLOT), 0, KEY_F9, 0}, + {"\"mfd right 5\"", DEMO_CONTEXT, mfd_button_callback_kb, ENCODE_MFD_SELECTION(MFD_RIGHT, MFD_INFO_SLOT), 0, KEY_F10, 0}, + {"\"keypad 0\"", DEMO_CONTEXT, keypad_hotkey_func, 0, 0, DOWN('0'), 0}, + {"\"keypad 1\"", DEMO_CONTEXT, keypad_hotkey_func, 0, 0, DOWN('1'), 0}, + {"\"keypad 2\"", DEMO_CONTEXT, keypad_hotkey_func, 0, 0, DOWN('2'), 0}, + {"\"keypad 3\"", DEMO_CONTEXT, keypad_hotkey_func, 0, 0, DOWN('3'), 0}, + {"\"keypad 4\"", DEMO_CONTEXT, keypad_hotkey_func, 0, 0, DOWN('4'), 0}, + {"\"keypad 5\"", DEMO_CONTEXT, keypad_hotkey_func, 0, 0, DOWN('5'), 0}, + {"\"keypad 6\"", DEMO_CONTEXT, keypad_hotkey_func, 0, 0, DOWN('6'), 0}, + {"\"keypad 7\"", DEMO_CONTEXT, keypad_hotkey_func, 0, 0, DOWN('7'), 0}, + {"\"keypad 8\"", DEMO_CONTEXT, keypad_hotkey_func, 0, 0, DOWN('8'), 0}, + {"\"keypad 9\"", DEMO_CONTEXT, keypad_hotkey_func, 0, 0, DOWN('9'), 0}, + // { "\"mac_help\"", DEMO_CONTEXT, MacHelpFunc, 0 , 0, CTRL('/'), 0 + // }, + {"\"toggle_options\"", DEMO_CONTEXT, wrapper_options_func, TRUE, 0, DOWN(KEY_ESC), 0}, + {"\"cheat_give_all\"", DEMO_CONTEXT, toggle_giveall_func, TRUE, 0, CTRL('2'), 0}, + {"\"cheat_physics\"", DEMO_CONTEXT, toggle_physics_func, TRUE, 0, CTRL('3'), 0}, + {"\"cheat_up_level\"", DEMO_CONTEXT, toggle_up_level_func, TRUE, 0, CTRL('4'), 0}, + {"\"cheat_down_level\"", DEMO_CONTEXT, toggle_down_level_func, TRUE, 0, CTRL('5'), 0}, + + {NULL, 0, 0, 0}}; + +// ought to be enough for anybody +#define MAX_FIRE_KEYS 16 + +int FireKeys[MAX_FIRE_KEYS + 1]; // see input.c + +static char *GetKeybindsPathFilename(void) { + static char filename[512]; + + FILE *f = fopen(KEYBINDS_FILENAME, "r"); + if (f != NULL) { + fclose(f); + strcpy(filename, KEYBINDS_FILENAME); + } else { + char *p = SDL_GetPrefPath("Interrupt", "SystemShock"); + snprintf(filename, sizeof(filename), "%s%s", p, KEYBINDS_FILENAME); + SDL_free(p); + } + + return filename; +} + +// all hotkey initialization and hotkey_add()s are done in this function +// also handles setting fire keybinds +void LoadHotkeyKeybinds(void) { + FILE *f; + char temp[512], *p; + const char *string; + int len, i, flags, ch, fire_key_index = 0; + + hotkey_init(NUM_HOTKEYS); + + // clear hotkey used flags so we can tell which weren't specified in file + // later we can add default key chars for them + i = 0; + while (HotKeyLookup[i].s != NULL) { + HotKeyLookup[i].used = FALSE; + i++; + } + + f = fopen(GetKeybindsPathFilename(), "r"); + if (f) { + // scan keybinds file line by line + while (fgets(temp, sizeof(temp), f)) { + LowerCaseInPlace(temp); + p = temp; + + while (*p && isspace(*p)) + p++; // skip leading spaces + + string = "bind"; + len = strlen(string); + if (strncmp(p, string, len)) + continue; + p += len; + + while (*p && isspace(*p)) + p++; // skip leading spaces + + flags = KB_FLAG_DOWN; + ch = 0; + + string = "shift+"; + len = strlen(string); + if (!strncmp(p, string, len)) { + p += len; + flags |= KB_FLAG_SHIFT; + } + + string = "ctrl+"; + len = strlen(string); + if (!strncmp(p, string, len)) { + p += len; + flags |= KB_FLAG_CTRL; + } + + string = "alt+"; + len = strlen(string); + if (!strncmp(p, string, len)) { + p += len; + flags |= KB_FLAG_ALT; + } + + // get ascii char from key name + i = 0; + while (KeyName2ChCode[i].s != NULL) { + string = KeyName2ChCode[i].s; + len = strlen(string); + if (!strncmp(p, string, len)) { + p += len; + ch = KeyName2ChCode[i].ch | flags; + break; + } + i++; + } + if (ch == 0) + continue; + + while (*p && isspace(*p)) + p++; // skip leading spaces + + // lookup and add specified hotkey info + i = 0; + while (HotKeyLookup[i].s != NULL) { + string = HotKeyLookup[i].s; + len = strlen(string); + if (!strncmp(p, string, len)) { + hotkey_add(ch, HotKeyLookup[i].contexts, HotKeyLookup[i].func, HotKeyLookup[i].state); + HotKeyLookup[i].used = TRUE; + break; + } + i++; + } + + // special case for fire keys + string = "\"fire\""; + len = strlen(string); + if (!strncmp(p, string, len)) { + if (fire_key_index < MAX_FIRE_KEYS) + FireKeys[fire_key_index++] = (ch & ~KB_FLAG_DOWN); + } + } + + fclose(f); + } + + // add defaults for unused hotkeys + i = 0; + while (HotKeyLookup[i].s != NULL) { + if (!HotKeyLookup[i].used) { + // add default 1 + ch = HotKeyLookup[i].def1; + if (ch) + hotkey_add(ch, HotKeyLookup[i].contexts, HotKeyLookup[i].func, HotKeyLookup[i].state); + + // add default 2 + ch = HotKeyLookup[i].def2; + if (ch) + hotkey_add(ch, HotKeyLookup[i].contexts, HotKeyLookup[i].func, HotKeyLookup[i].state); + } + i++; + } + + // add default fire key if none were specified + if (fire_key_index == 0) + FireKeys[fire_key_index++] = KEY_ENTER; + + // signal end of fire key list + FireKeys[fire_key_index] = 0; +} + +//************************************************************************************ + +//********************************** +// Set move keybinds (see movekeys.c) +//********************************** + +extern MOVE_KEYBIND MoveKeybinds[MAX_MOVE_KEYBINDS + 1]; +extern MOVE_KEYBIND MoveCyberKeybinds[MAX_MOVE_KEYBINDS + 1]; + +static MOVE_KEYBIND MoveKeybindsDefault[] = +{ + { CODE_W | KB_FLAG_SHIFT, M_RUNFORWARD }, + { CODE_UP | KB_FLAG_SHIFT, M_RUNFORWARD }, + { CODE_UP | KB_FLAG_ALT , M_RUNFORWARD }, + { CODE_KP_UP | KB_FLAG_SHIFT, M_RUNFORWARD }, + { CODE_KP_UP | KB_FLAG_ALT , M_RUNFORWARD }, + { CODE_W , M_FORWARD }, + { CODE_UP , M_FORWARD }, + { CODE_KP_UP , M_FORWARD }, + { CODE_Z | KB_FLAG_SHIFT, M_FASTTURNLEFT }, + { CODE_LEFT | KB_FLAG_SHIFT, M_FASTTURNLEFT }, + { CODE_KP_LEFT | KB_FLAG_SHIFT, M_FASTTURNLEFT }, + { CODE_Z , M_TURNLEFT }, + { CODE_LEFT , M_TURNLEFT }, + { CODE_KP_LEFT , M_TURNLEFT }, + { CODE_C | KB_FLAG_SHIFT, M_FASTTURNRIGHT }, + { CODE_RIGHT | KB_FLAG_SHIFT, M_FASTTURNRIGHT }, + { CODE_KP_RIGHT | KB_FLAG_SHIFT, M_FASTTURNRIGHT }, + { CODE_C , M_TURNRIGHT }, + { CODE_RIGHT , M_TURNRIGHT }, + { CODE_KP_RIGHT , M_TURNRIGHT }, + { CODE_S , M_BACK }, + { CODE_S | KB_FLAG_SHIFT, M_BACK }, + { CODE_DOWN , M_BACK }, + { CODE_DOWN | KB_FLAG_SHIFT, M_BACK }, + { CODE_DOWN | KB_FLAG_ALT , M_BACK }, + { CODE_KP_DOWN , M_BACK }, + { CODE_KP_DOWN | KB_FLAG_SHIFT, M_BACK }, + { CODE_KP_DOWN | KB_FLAG_ALT , M_BACK }, + { CODE_A , M_SLIDELEFT }, + { CODE_A | KB_FLAG_SHIFT, M_SLIDELEFT }, + { CODE_LEFT | KB_FLAG_ALT , M_SLIDELEFT }, + { CODE_KP_LEFT | KB_FLAG_ALT , M_SLIDELEFT }, + { CODE_KP_END , M_SLIDELEFT }, + { CODE_D , M_SLIDERIGHT }, + { CODE_D | KB_FLAG_SHIFT, M_SLIDERIGHT }, + { CODE_RIGHT | KB_FLAG_ALT , M_SLIDERIGHT }, + { CODE_KP_RIGHT | KB_FLAG_ALT , M_SLIDERIGHT }, + { CODE_KP_PGDN , M_SLIDERIGHT }, + { CODE_J , M_JUMP }, + { CODE_J | KB_FLAG_SHIFT, M_JUMP }, + { CODE_SPACE , M_JUMP }, + { CODE_SPACE | KB_FLAG_SHIFT, M_JUMP }, + { CODE_SPACE | KB_FLAG_CTRL , M_JUMP }, + { CODE_SPACE | KB_FLAG_ALT , M_JUMP }, + { CODE_X , M_LEANUP }, + { CODE_X | KB_FLAG_SHIFT, M_LEANUP }, + { CODE_X | KB_FLAG_CTRL , M_LEANUP }, + { CODE_X | KB_FLAG_ALT , M_LEANUP }, + { CODE_Q , M_LEANLEFT }, + { CODE_Q | KB_FLAG_SHIFT, M_LEANLEFT }, + { CODE_LEFT | KB_FLAG_CTRL , M_LEANLEFT }, + { CODE_KP_LEFT | KB_FLAG_CTRL , M_LEANLEFT }, + { CODE_E , M_LEANRIGHT }, + { CODE_E | KB_FLAG_SHIFT, M_LEANRIGHT }, + { CODE_RIGHT | KB_FLAG_CTRL , M_LEANRIGHT }, + { CODE_KP_RIGHT | KB_FLAG_CTRL , M_LEANRIGHT }, + { CODE_R , M_LOOKUP }, + { CODE_R | KB_FLAG_SHIFT, M_LOOKUP }, + { CODE_R | KB_FLAG_CTRL , M_LOOKUP }, + { CODE_UP | KB_FLAG_CTRL , M_LOOKUP }, + { CODE_KP_UP | KB_FLAG_CTRL , M_LOOKUP }, + { CODE_V , M_LOOKDOWN }, + { CODE_V | KB_FLAG_SHIFT, M_LOOKDOWN }, + { CODE_V | KB_FLAG_CTRL , M_LOOKDOWN }, + { CODE_DOWN | KB_FLAG_CTRL , M_LOOKDOWN }, + { CODE_KP_DOWN | KB_FLAG_CTRL , M_LOOKDOWN }, + { CODE_KP_HOME , M_RUNLEFT }, + { CODE_KP_PGUP , M_RUNRIGHT }, + { CODE_S , M_THRUST }, //cyber start + { CODE_S | KB_FLAG_SHIFT, M_THRUST }, + { CODE_KP_5 , M_THRUST }, + { CODE_W , M_CLIMB }, + { CODE_W | KB_FLAG_SHIFT, M_CLIMB }, + { CODE_UP , M_CLIMB }, + { CODE_UP | KB_FLAG_SHIFT, M_CLIMB }, + { CODE_UP | KB_FLAG_CTRL , M_CLIMB }, + { CODE_UP | KB_FLAG_ALT , M_CLIMB }, + { CODE_KP_UP , M_CLIMB }, + { CODE_KP_UP | KB_FLAG_SHIFT, M_CLIMB }, + { CODE_KP_UP | KB_FLAG_CTRL , M_CLIMB }, + { CODE_KP_UP | KB_FLAG_ALT , M_CLIMB }, + { CODE_A , M_BANKLEFT }, + { CODE_A | KB_FLAG_SHIFT, M_BANKLEFT }, + { CODE_KP_LEFT , M_BANKLEFT }, + { CODE_KP_LEFT | KB_FLAG_SHIFT, M_BANKLEFT }, + { CODE_KP_LEFT | KB_FLAG_CTRL , M_BANKLEFT }, + { CODE_KP_LEFT | KB_FLAG_ALT , M_BANKLEFT }, + { CODE_D , M_BANKRIGHT }, + { CODE_D | KB_FLAG_SHIFT, M_BANKRIGHT }, + { CODE_KP_RIGHT , M_BANKRIGHT }, + { CODE_KP_RIGHT | KB_FLAG_SHIFT, M_BANKRIGHT }, + { CODE_KP_RIGHT | KB_FLAG_CTRL , M_BANKRIGHT }, + { CODE_KP_RIGHT | KB_FLAG_ALT , M_BANKRIGHT }, + { CODE_X , M_DIVE }, + { CODE_X | KB_FLAG_SHIFT, M_DIVE }, + { CODE_DOWN , M_DIVE }, + { CODE_DOWN | KB_FLAG_SHIFT, M_DIVE }, + { CODE_DOWN | KB_FLAG_CTRL , M_DIVE }, + { CODE_DOWN | KB_FLAG_ALT , M_DIVE }, + { CODE_KP_DOWN , M_DIVE }, + { CODE_KP_DOWN | KB_FLAG_SHIFT, M_DIVE }, + { CODE_KP_DOWN | KB_FLAG_CTRL , M_DIVE }, + { CODE_KP_DOWN | KB_FLAG_ALT , M_DIVE }, + { CODE_Q , M_ROLLRIGHT }, + { CODE_Q | KB_FLAG_SHIFT, M_ROLLRIGHT }, + { CODE_Z , M_ROLLRIGHT }, + { CODE_Z | KB_FLAG_SHIFT, M_ROLLRIGHT }, + { CODE_E , M_ROLLLEFT }, + { CODE_E | KB_FLAG_SHIFT, M_ROLLLEFT }, + { CODE_C , M_ROLLLEFT }, + { CODE_C | KB_FLAG_SHIFT, M_ROLLLEFT }, + { CODE_KP_HOME , M_CLIMBLEFT }, + { CODE_KP_HOME | KB_FLAG_SHIFT, M_CLIMBLEFT }, + { CODE_KP_HOME | KB_FLAG_CTRL , M_CLIMBLEFT }, + { CODE_KP_HOME | KB_FLAG_ALT , M_CLIMBLEFT }, + { CODE_KP_PGUP , M_CLIMBRIGHT }, + { CODE_KP_PGUP | KB_FLAG_SHIFT, M_CLIMBRIGHT }, + { CODE_KP_PGUP | KB_FLAG_CTRL , M_CLIMBRIGHT }, + { CODE_KP_PGUP | KB_FLAG_ALT , M_CLIMBRIGHT }, + { CODE_KP_PGDN , M_DIVERIGHT }, + { CODE_KP_PGDN | KB_FLAG_SHIFT, M_DIVERIGHT }, + { CODE_KP_PGDN | KB_FLAG_CTRL , M_DIVERIGHT }, + { CODE_KP_PGDN | KB_FLAG_ALT , M_DIVERIGHT }, + { CODE_KP_END , M_DIVELEFT }, + { CODE_KP_END | KB_FLAG_SHIFT, M_DIVELEFT }, + { CODE_KP_END | KB_FLAG_CTRL , M_DIVELEFT }, + { CODE_KP_END | KB_FLAG_ALT , M_DIVELEFT }, + + {255, -1}}; + +static struct { + const char *s; + int move; +} MoveName2Move[] = {{"\"runforward\"", M_RUNFORWARD}, + {"\"forward\"", M_FORWARD}, + {"\"fastturnleft\"", M_FASTTURNLEFT}, + {"\"turnleft\"", M_TURNLEFT}, + {"\"fastturnright\"", M_FASTTURNRIGHT}, + {"\"turnright\"", M_TURNRIGHT}, + {"\"back\"", M_BACK}, + {"\"slideleft\"", M_SLIDELEFT}, + {"\"slideright\"", M_SLIDERIGHT}, + {"\"jump\"", M_JUMP}, + {"\"leanup\"", M_LEANUP}, + {"\"leanleft\"", M_LEANLEFT}, + {"\"leanright\"", M_LEANRIGHT}, + {"\"lookup\"", M_LOOKUP}, + {"\"lookdown\"", M_LOOKDOWN}, + {"\"runleft\"", M_RUNLEFT}, + {"\"runright\"", M_RUNRIGHT}, + {"\"thrust\"", M_THRUST}, // cyber start + {"\"climb\"", M_CLIMB}, + {"\"bankleft\"", M_BANKLEFT}, + {"\"bankright\"", M_BANKRIGHT}, + {"\"dive\"", M_DIVE}, + {"\"rollright\"", M_ROLLRIGHT}, + {"\"rollleft\"", M_ROLLLEFT}, + {"\"climbleft\"", M_CLIMBLEFT}, + {"\"climbright\"", M_CLIMBRIGHT}, + {"\"diveright\"", M_DIVERIGHT}, + {"\"diveleft\"", M_DIVELEFT}, + + {NULL, 0}}; + +void LoadMoveKeybinds(void) { + FILE *f; + char temp[512], *p, move_used[NUM_MOVES]; + const char *string; + int len, i, flags, code, move, num_bound = 0, num_cyber_bound = 0; + + // keep track of which moves are specified so we can add default ones for those that are missing + memset(move_used, 0, NUM_MOVES); + + f = fopen(GetKeybindsPathFilename(), "r"); + if (f) { + // scan keybinds file line by line + while (fgets(temp, sizeof(temp), f)) { + LowerCaseInPlace(temp); + p = temp; + + while (*p && isspace(*p)) + p++; // skip leading spaces + + string = "bind"; + len = strlen(string); + if (strncmp(p, string, len)) + continue; + p += len; + + while (*p && isspace(*p)) + p++; // skip leading spaces + + flags = 0; + code = 255; + + string = "shift+"; + len = strlen(string); + if (!strncmp(p, string, len)) { + p += len; + flags |= KB_FLAG_SHIFT; + } + + string = "ctrl+"; + len = strlen(string); + if (!strncmp(p, string, len)) { + p += len; + flags |= KB_FLAG_CTRL; + } + + string = "alt+"; + len = strlen(string); + if (!strncmp(p, string, len)) { + p += len; + flags |= KB_FLAG_ALT; + } + + // get code from key name + i = 0; + while (KeyName2ChCode[i].s != NULL) { + string = KeyName2ChCode[i].s; + len = strlen(string); + if (!strncmp(p, string, len)) { + p += len; + code = KeyName2ChCode[i].code | flags; + break; + } + i++; + } + if (code == 255) + continue; + + while (*p && isspace(*p)) + p++; // skip leading spaces + + // lookup move + i = 0; + while (MoveName2Move[i].s != NULL) { + string = MoveName2Move[i].s; + len = strlen(string); + if (!strncmp(p, string, len)) { + move = MoveName2Move[i].move; + move_used[move] = 1; + + if (move < M_THRUST) // non-cyber + { + if (num_bound < MAX_MOVE_KEYBINDS) { + // add keybind to list + MoveKeybinds[num_bound].code = code; + MoveKeybinds[num_bound].move = move; + num_bound++; + } + } else { + if (num_cyber_bound < MAX_MOVE_KEYBINDS) { + // add cyber keybind to list + MoveCyberKeybinds[num_cyber_bound].code = code; + MoveCyberKeybinds[num_cyber_bound].move = move; + num_cyber_bound++; + } + } + + break; + } + i++; + } + } + + fclose(f); + } + + // for moves that weren't referenced in file, bind default codes to them + for (move = 0; move < NUM_MOVES; move++) + if (!move_used[move]) { + i = 0; + while (MoveKeybindsDefault[i].code != 255) { + if (MoveKeybindsDefault[i].move == move) { + code = MoveKeybindsDefault[i].code; + + if (move < M_THRUST) // non-cyber + { + if (num_bound < MAX_MOVE_KEYBINDS) { + // add keybind to list + MoveKeybinds[num_bound].code = code; + MoveKeybinds[num_bound].move = move; + num_bound++; + } + } else { + if (num_cyber_bound < MAX_MOVE_KEYBINDS) { + // add cyber keybind to list + MoveCyberKeybinds[num_cyber_bound].code = code; + MoveCyberKeybinds[num_cyber_bound].move = move; + num_cyber_bound++; + } + } + } + i++; + } + } + + // signal end of lists + MoveKeybinds[num_bound].code = 255; + MoveCyberKeybinds[num_cyber_bound].code = 255; + + extern void init_motion_polling(void); // see movekeys.c + init_motion_polling(); +} + +//************************************************************************************ + +//******************************* +// Create default keybinds file +//******************************* + +#define JUSTIFY_COLUMN 30 + +// if ch is 0, use code instead +static bool WriteKeyName(int ch, int code, FILE *f) { + int i = 0, len = 0; + + while (KeyName2ChCode[i].s != NULL) { + if (ch) { + if (KeyName2ChCode[i].ch == (ch & 255)) + break; + } else { + if (KeyName2ChCode[i].code == (code & 255)) + break; + } + i++; + } + if (KeyName2ChCode[i].s == NULL) + return 0; + + fputs("bind ", f); + len += 6; + + if ((ch ? ch : code) & KB_FLAG_SHIFT) { + fputs("shift+", f); + len += 6; + } + if ((ch ? ch : code) & KB_FLAG_CTRL) { + fputs("ctrl+", f); + len += 5; + } + if ((ch ? ch : code) & KB_FLAG_ALT) { + fputs("alt+", f); + len += 4; + } + + fputs(KeyName2ChCode[i].s, f); + len += strlen(KeyName2ChCode[i].s); + + // add spaces to justify following text + while (len < JUSTIFY_COLUMN) { + fputc(' ', f); + len++; + } + + return 1; +} + +static void WriteMoveName(int move, FILE *f) { + int i; + + // find move name that matches move + i = 0; + while (MoveName2Move[i].s != NULL) { + if (MoveName2Move[i].move == move) + break; + i++; + } + + if (MoveName2Move[i].s != NULL) { + fputs(MoveName2Move[i].s, f); + fputs("\n", f); + } +} + +// create default keybinds file if it doesn't already exist +void CreateDefaultKeybindsFile(void) { + FILE *f; + char *filename = GetKeybindsPathFilename(); + int i, ch; + + // check if file already exists; if so, return + f = fopen(filename, "r"); + if (f != NULL) { + fclose(f); + return; + } + + // open new file for writing + f = fopen(filename, "w"); + if (f == NULL) + return; + + // write default hotkey keybinds + i = 0; + while (HotKeyLookup[i].s) { + // default 1 if it exists (it should) + ch = HotKeyLookup[i].def1; + if (ch && WriteKeyName(ch, 0, f)) { + fputs(HotKeyLookup[i].s, f); + fputs("\n", f); + } + + // default 2 if it exists (it might not) + ch = HotKeyLookup[i].def2; + if (ch && WriteKeyName(ch, 0, f)) { + fputs(HotKeyLookup[i].s, f); + fputs("\n", f); + } + + i++; + } + + // write default fire keybind + fputs("\n", f); + WriteKeyName(KEY_ENTER, 0, f); + fputs("\"fire\"\n\n", f); + + // write default move keybinds + i = 0; + while (MoveKeybindsDefault[i].code != 255) { + if (WriteKeyName(0, MoveKeybindsDefault[i].code, f)) + WriteMoveName(MoveKeybindsDefault[i].move, f); + + i++; + } + + fclose(f); +} diff --git a/engine/src/MacSrc/Prefs.h b/engine/src/MacSrc/Prefs.h new file mode 100644 index 0000000..d18a9ea --- /dev/null +++ b/engine/src/MacSrc/Prefs.h @@ -0,0 +1,86 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +//==================================================================================== +// +// System Shock - ©1994-1995 Looking Glass Technologies, Inc. +// +// Prefs.h - Handles saving and loading preferences. +// +//==================================================================================== + +//-------------------- +// Types +//-------------------- +typedef struct { + short prefVer; // Version - set to 0 for now. + short prefPlayIntro; // Play intro at startup if non-zero. + + // Game Options + short goMsgLength; // 0 - normal, 1 - brief + bool goPopupLabels; + bool goOnScreenHelp; + short goLanguage; // 0 - English, 1 - French, 2 - German + bool goCaptureMouse; + bool goInvertMouseY; + + // Sound Options + bool soBackMusic; + bool soSoundFX; + short soMusicVolume; + short soSfxVolume; + short soAudioLogVolume; + short soMidiBackend; // 0 => adlmidi, 1 => native, 2 => fluidsynth + short soMidiOutput; // which of the MIDI backend's outputs to use + + // Display Options + short doVideoMode; + short doResolution; // 0 - High, 1 - Low + short doDetail; // 0 - Min, 1-Low, 2-High, 3-Max + short doGamma; + bool doUseQD; + bool doUseOpenGL; + // 0 => unfiltered + // 1 => bilinear + // TODO: add trilinear, anisotropic? + short doTextureFilter; +} ShockPrefs; + +//-------------------- +// Globals +//-------------------- +extern ShockPrefs gShockPrefs; + +//-------------------- +// Prototypes +//-------------------- +void SetDefaultPrefs(void); +int16_t LoadPrefs(void); +int16_t SavePrefs(void); + +//------------------- +// Enums +//------------------- +enum OPT_SEQ_ { // Must be in the same order as in wraper.h + OPT_SEQ_ADLMIDI = 0, + OPT_SEQ_NativeMI, +#ifdef USE_FLUIDSYNTH + OPT_SEQ_FluidSyn, +#endif // USE_FLUIDSYNTH + OPT_SEQ_Max +}; diff --git a/engine/src/MacSrc/SDLSound.c b/engine/src/MacSrc/SDLSound.c new file mode 100644 index 0000000..fdb3047 --- /dev/null +++ b/engine/src/MacSrc/SDLSound.c @@ -0,0 +1,198 @@ +#include "Xmi.h" +#include "MusicDevice.h" + +static snd_digi_parms digi_parms_by_channel[SND_MAX_SAMPLES]; + +#ifdef USE_SDL_MIXER + +#include + +static Mix_Chunk *samples_by_channel[SND_MAX_SAMPLES]; + +extern SDL_AudioStream *cutscene_audiostream; +extern struct MusicDevice *MusicDev; + +extern void AudioStreamCallback(void *userdata, unsigned char *stream, int len); +extern void MusicCallback(void *userdata, Uint8 *stream, int len); + +int snd_start_digital(void) { + + // Startup the sound system + + SDL_AudioSpec spec, obtained; + spec.freq = 48000; + spec.format = AUDIO_S16SYS; + spec.channels = 2; + spec.samples = 2048; + spec.callback = AudioStreamCallback; + spec.userdata = (void *)&cutscene_audiostream; + + extern SDL_AudioDeviceID device; + device = SDL_OpenAudioDevice(NULL, 0, &spec, &obtained, 0); + + if (device == 0) { + ERROR("Could not open SDL audio: %s", SDL_GetError()); + } else { + INFO("Opened Music Stream, deviceID %d, freq %d, size %d, format %d, channels %d, samples %d", device, + obtained.freq, obtained.size, obtained.format, obtained.channels, obtained.samples); + } + + if (Mix_Init(MIX_INIT_MP3) < 0) { + ERROR("%s: Init failed", __FUNCTION__); + } + + if (Mix_OpenAudio(48000, AUDIO_S16SYS, 2, 2048) < 0) { + ERROR("%s: Couldn't open audio device", __FUNCTION__); + } + + Mix_AllocateChannels(SND_MAX_SAMPLES); + + Mix_HookMusic(MusicCallback, (void *)&MusicDev); + Mix_VolumeMusic(MIX_MAX_VOLUME); // use max volume for music stream + + InitReadXMI(); + + atexit(Mix_CloseAudio); + atexit(SDL_CloseAudio); + + return OK; +} + +int snd_sample_play(int snd_ref, int len, uchar *smp, struct snd_digi_parms *dprm) { + + // Play one of the VOC format sounds + + Mix_Chunk *sample = Mix_LoadWAV_RW(SDL_RWFromConstMem(smp, len), 1); + if (sample == NULL) { + DEBUG("%s: Failed to load sample", __FUNCTION__); + return ERR_NOEFFECT; + } + + int loops = dprm->loops > 0 ? dprm->loops - 1 : -1; + int channel = Mix_PlayChannel(-1, sample, loops); + if (channel < 0) { + DEBUG("%s: Failed to play sample", __FUNCTION__); + Mix_FreeChunk(sample); + return ERR_NOEFFECT; + } + + if (samples_by_channel[channel]) + Mix_FreeChunk(samples_by_channel[channel]); + + samples_by_channel[channel] = sample; + digi_parms_by_channel[channel] = *dprm; + snd_sample_reload_parms(&digi_parms_by_channel[channel]); + + return channel; +} + +void snd_end_sample(int hnd_id) { + Mix_HaltChannel(hnd_id); + if (samples_by_channel[hnd_id]) { + Mix_FreeChunk(samples_by_channel[hnd_id]); + samples_by_channel[hnd_id] = NULL; + } +} + +bool snd_sample_playing(int hnd_id) { return Mix_Playing(hnd_id); } + +snd_digi_parms *snd_sample_parms(int hnd_id) { return &digi_parms_by_channel[hnd_id]; } + +void snd_kill_all_samples(void) { + for (int channel = 0; channel < SND_MAX_SAMPLES; channel++) { + snd_end_sample(channel); + } + + // assume we want these too + // StopTheMusic(); // no, don't stop the music + if (cutscene_audiostream != NULL) + SDL_AudioStreamClear(cutscene_audiostream); +} + +void snd_sample_reload_parms(snd_digi_parms *sdp) { + // ignore if *sdp is not one of the items in digi_parms_by_channel[] + if (sdp < digi_parms_by_channel || sdp > digi_parms_by_channel + SND_MAX_SAMPLES) + return; + int channel = sdp - digi_parms_by_channel; + + if (!Mix_Playing(channel)) + return; + + // sdp->vol ranges from 0..255 + Mix_Volume(channel, (sdp->vol * 128) / 100); + + // sdp->pan ranges from 1 (left) to 127 (right) + uint8_t right = 2 * sdp->pan; + Mix_SetPanning(channel, 254 - right, right); +} + +int is_playing = 0; + +int MacTuneLoadTheme(char *theme_base, int themeID) { + char filename[40]; + FILE *f; + int i; + +#define NUM_SCORES 8 +#define SUPERCHUNKS_PER_SCORE 4 +#define NUM_TRANSITIONS 9 +#define NUM_LAYERS 32 +#define MAX_KEYS 10 +#define NUM_LAYERABLE_SUPERCHUNKS 22 +#define KEY_BAR_RESOLUTION 2 + + extern uchar track_table[NUM_SCORES][SUPERCHUNKS_PER_SCORE]; + extern uchar transition_table[NUM_TRANSITIONS]; + extern uchar layering_table[NUM_LAYERS][MAX_KEYS]; + extern uchar key_table[NUM_LAYERABLE_SUPERCHUNKS][KEY_BAR_RESOLUTION]; + + StopTheMusic(); + + FreeXMI(); + + if (strncmp(theme_base, "thm", 3)) { + sprintf(filename, "res/sound/%s/%s.xmi", MusicDev->musicType, theme_base); + ReadXMI(filename); + } else { + sprintf(filename, "res/sound/%s/thm%i.xmi", MusicDev->musicType, themeID); + ReadXMI(filename); + + sprintf(filename, "res/sound/thm%i.bin", themeID); + extern FILE *fopen_caseless(const char *path, const char *mode); // see caseless.c + f = fopen_caseless(filename, "rb"); + if (f != 0) { + fread(track_table, NUM_SCORES * SUPERCHUNKS_PER_SCORE, 1, f); + fread(transition_table, NUM_TRANSITIONS, 1, f); + fread(layering_table, NUM_LAYERS * MAX_KEYS, 1, f); + fread(key_table, NUM_LAYERABLE_SUPERCHUNKS * KEY_BAR_RESOLUTION, 1, f); + + fclose(f); + } + } + + return OK; +} + +void MacTuneKillCurrentTheme(void) { StopTheMusic(); } + +#else + +// Sound stubs that do nothing, when SDL Mixer is not found + +int snd_start_digital(void) { return OK; } +int snd_sample_play(int snd_ref, int len, uchar *smp, struct snd_digi_parms *dprm) { return OK; } +int snd_alog_play(int snd_ref, int len, uchar *smp, struct snd_digi_parms *dprm) { return OK; } +void snd_end_sample(int hnd_id) {} +void snd_kill_all_samples(void) {} +int MacTuneLoadTheme(char *theme_base, int themeID) { return OK; } +void MacTuneKillCurrentTheme(void) {} +snd_digi_parms *snd_sample_parms(int hnd_id) { return &digi_parms_by_channel[0]; } +bool snd_sample_playing(int hnd_id) { return false; } +void snd_sample_reload_parms(snd_digi_parms *sdp) {} + +#endif + +// Unimplemented sound stubs + +void snd_startup(void) {} +int snd_stop_digital(void) { return 1; } diff --git a/engine/src/MacSrc/Shock.c b/engine/src/MacSrc/Shock.c new file mode 100644 index 0000000..7b3f909 --- /dev/null +++ b/engine/src/MacSrc/Shock.c @@ -0,0 +1,321 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +//==================================================================================== +// +// System Shock - ©1994-1995 Looking Glass Technologies, Inc. +// +// Shock.c - Mac-specific initialization and main event loop. +// +//==================================================================================== + +//-------------------- +// Includes +//-------------------- +#include +#include + +#include "InitMac.h" +#include "Modding.h" +#include "OpenGL.h" +#include "Prefs.h" +#include "Shock.h" +#include "ShockBitmap.h" + +#include "amaploop.h" +#include "gr2ss.h" +#include "hkeyfunc.h" +#include "mainloop.h" +#include "setup.h" +#include "shockolate_version.h" +#include "status.h" +#include "version.h" + +//-------------------- +// Globals +//-------------------- +bool gPlayingGame; + +grs_screen *cit_screen; +SDL_Window *window; +SDL_Palette *sdlPalette; +SDL_Renderer *renderer; + +SDL_AudioDeviceID device; + +int num_args; +char **arg_values; + +extern grs_screen *svga_screen; +extern frc *svga_render_context; + +//-------------------- +// Prototypes +//-------------------- +extern void init_all(void); +extern void inv_change_fullscreen(uchar on); +extern void object_data_flush(void); +extern errtype load_da_palette(void); + +// see Prefs.c +extern void CreateDefaultKeybindsFile(void); +extern void LoadHotkeyKeybinds(void); +extern void LoadMoveKeybinds(void); + +//------------------------------------------------------------------------------------ +// Main function. +//------------------------------------------------------------------------------------ +int main(int argc, char **argv) { + // Save the arguments for later + + num_args = argc; + arg_values = argv; + + // FIXME externalize this + log_set_quiet(0); + log_set_level(LOG_INFO); + + INFO("Logger initialized"); + + // init mac managers + + InitMac(); + + // Initialize the preferences file. + + SetDefaultPrefs(); + LoadPrefs(); + + // see Prefs.c + CreateDefaultKeybindsFile(); // only if it doesn't already exist + // even if keybinds file still doesn't exist, defaults will be set here + LoadHotkeyKeybinds(); + LoadMoveKeybinds(); + + // Process some startup arguments + + bool show_splash = !CheckArgument("-nosplash"); + + // CC: Modding support! This is so exciting. + + ProcessModArgs(argc, argv); + + // Initialize + + init_all(); + setup_init(); + + gPlayingGame = true; + + load_da_palette(); + gr_clear(0xFF); + + // Draw the splash screen + + INFO("Showing splash screen"); + splash_draw(show_splash); + + // Start in the Main Menu loop + + _new_mode = _current_loop = SETUP_LOOP; + loopmode_enter(SETUP_LOOP); + + // Start the main loop + + INFO("Showing main menu, starting game loop"); + mainloop(argc, argv); + + status_bio_end(); + stop_music(); + + return 0; +} + +bool CheckArgument(char *arg) { + if (arg == NULL) + return false; + + for (int i = 1; i < num_args; i++) { + if (strcmp(arg_values[i], arg) == 0) { + return true; + } + } + + return false; +} + +void InitSDL() { + SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1"); + SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO) < 0) { + DEBUG("%s: Init failed", __FUNCTION__); + } + + // TODO: figure out some universal set of settings that work... + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32); + + gr_init(); + + extern short svga_mode_data[]; + gr_set_mode(svga_mode_data[gShockPrefs.doVideoMode], TRUE); + + INFO("Setting up screen and render contexts"); + + // Create a canvas to draw to + + SetupOffscreenBitmaps(grd_cap->w, grd_cap->h); + + // Open our window! + char window_title[128]; + sprintf(window_title, "System Shock - %s", SHOCKOLATE_VERSION); + + window = SDL_CreateWindow(window_title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, grd_cap->w, grd_cap->h, + SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_OPENGL); + + // Create the palette + + sdlPalette = SDL_AllocPalette(256); + + // Setup the screen + + svga_screen = cit_screen = gr_alloc_screen(grd_cap->w, grd_cap->h); + gr_set_screen(svga_screen); + + gr_alloc_ipal(); + + SDL_ShowCursor(SDL_DISABLE); + + atexit(SDL_Quit); + + SDL_RaiseWindow(window); + + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC); + SDL_RenderSetLogicalSize(renderer, grd_cap->w, grd_cap->h); + + // Startup OpenGL + + init_opengl(); + + SDLDraw(); + + SDL_ShowWindow(window); +} + +SDL_Color gamePalette[256]; +bool UseCutscenePalette = FALSE; // see cutsloop.c +void SetSDLPalette(int index, int count, uchar *pal) { + static bool gammalut_init = 0; + static uchar gammalut[100 - 10 + 1][256]; + if (!gammalut_init) { + double factor = (use_opengl() ? 1.0 : 2.2); // OpenGL uses 2.2 + int i, j; + for (i = 10; i <= 100; i++) { + double gamma = (double)i * 1.0 / 100; + gamma = 1 - gamma; + gamma *= gamma; + gamma = 1 - gamma; + gamma = 1 / (gamma * factor); + for (j = 0; j < 256; j++) + gammalut[i - 10][j] = (uchar)(pow((double)j / 255, gamma) * 255); + } + gammalut_init = 1; + INFO("Gamma LUT init\'ed"); + } + + int gam = gShockPrefs.doGamma; + if (gam < 10) + gam = 10; + if (gam > 100) + gam = 100; + gam -= 10; + + for (int i = index; i < index + count; i++) { + gamePalette[i].r = gammalut[gam][*pal++]; + gamePalette[i].g = gammalut[gam][*pal++]; + gamePalette[i].b = gammalut[gam][*pal++]; + gamePalette[i].a = 0xff; + } + + if (!UseCutscenePalette) { + // Hack black! + gamePalette[255].r = 0x0; + gamePalette[255].g = 0x0; + gamePalette[255].b = 0x0; + gamePalette[255].a = 0xff; + } + + SDL_SetPaletteColors(sdlPalette, gamePalette, 0, 256); + SDL_SetSurfacePalette(drawSurface, sdlPalette); + SDL_SetSurfacePalette(offscreenDrawSurface, sdlPalette); + + if (should_opengl_swap()) + opengl_change_palette(); +} + +void SDLDraw() { + if (should_opengl_swap()) { + // We want the UI background to be transparent! + sdlPalette->colors[255].a = 0x00; + + // Draw the OpenGL view + opengl_swap_and_restore(drawSurface); + + // Set the palette back, and we are done + sdlPalette->colors[255].a = 0xff; + return; + } + + // Clear the screen! + SDL_RenderClear(renderer); + SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, drawSurface); + + // Blit to the screen by drawing the surface + SDL_Rect srcRect = {0, 0, gScreenWide, gScreenHigh}; + SDL_RenderCopy(renderer, texture, &srcRect, NULL); + SDL_DestroyTexture(texture); + + // Show everything we've drawn + SDL_RenderPresent(renderer); +} + +bool MouseCaptured = FALSE; + +extern int mlook_enabled; + +void CaptureMouse(bool capture) { + MouseCaptured = (capture && gShockPrefs.goCaptureMouse); + + if (!MouseCaptured && mlook_enabled && SDL_GetRelativeMouseMode() == SDL_TRUE) { + SDL_SetRelativeMouseMode(SDL_FALSE); + + int w, h; + SDL_GetWindowSize(window, &w, &h); + SDL_WarpMouseInWindow(window, w / 2, h / 2); + } else + SDL_SetRelativeMouseMode(MouseCaptured ? SDL_TRUE : SDL_FALSE); +} diff --git a/engine/src/MacSrc/Shock.h b/engine/src/MacSrc/Shock.h new file mode 100644 index 0000000..12d7d88 --- /dev/null +++ b/engine/src/MacSrc/Shock.h @@ -0,0 +1,50 @@ +/* + +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +//==================================================================================== +// +// System Shock - ©1994-1995 Looking Glass Technologies, Inc. +// +// Shock.h - Mac-specific initialization and main event loop. +// +//==================================================================================== + +#include + +//-------------------- +// Function Prototypes +//-------------------- +int main(int argc, char **argv); + +void InitSDL(); +void SetSDLPalette(int index, int count, uchar *pal); +void SDLDraw(); +void CaptureMouse(bool capture); +bool CheckArgument(char *name); + +//-------------------- +// Public Globals +//-------------------- + +// Is game being playing? +extern bool gPlayingGame; + +extern grs_screen *cit_screen; +extern SDL_Renderer *renderer; +extern SDL_Window *window; diff --git a/engine/src/MacSrc/ShockBitmap.c b/engine/src/MacSrc/ShockBitmap.c new file mode 100644 index 0000000..cb5cfd6 --- /dev/null +++ b/engine/src/MacSrc/ShockBitmap.c @@ -0,0 +1,89 @@ +/* + +Copyright (C) 1994-1995 Looking Glass Technologies, Inc. +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// ShockBitmap.c - Manages off-screen bitmaps and palettes. + +//-------------------- +// Includes +//-------------------- +#include "InitMac.h" +#include "Shock.h" +#include "ShockBitmap.h" +#include "2d.h" + +//-------------------- +// Globals +//-------------------- +SDL_Surface *drawSurface; +SDL_Surface *offscreenDrawSurface; + +void ChangeScreenSize(int width, int height) { + if (gScreenWide == width && gScreenHigh == height) + return; + + INFO("ChangeScreenSize"); + + SDL_RenderClear(renderer); + + extern bool fullscreenActive; + SDL_SetWindowFullscreen(window, fullscreenActive ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + + SDL_SetWindowSize(window, width, height); + SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + + SDL_RenderSetLogicalSize(renderer, width, height); + + SetupOffscreenBitmaps(width, height); + + gScreenWide = width; + gScreenHigh = height; +} + +//------------------------------------------------------------------------------------ +// Setup the main offscreen bitmaps. +//------------------------------------------------------------------------------------ +void SetupOffscreenBitmaps(int width, int height) { + DEBUG("SetupOffscreenBitmaps %i %i", width, height); + + if (drawSurface != NULL) { + SDL_FreeSurface(drawSurface); + } + if (offscreenDrawSurface != NULL) { + SDL_FreeSurface(offscreenDrawSurface); + } + + drawSurface = SDL_CreateRGBSurface(0, width, height, 8, 0, 0, 0, 0); + if (!drawSurface) { + ERROR("SDL: Failed to create draw surface"); + return; + } + + offscreenDrawSurface = SDL_CreateRGBSurface(0, width, height, 8, 0, 0, 0, 0); + if (!offscreenDrawSurface) { + ERROR("SDL: Failed to create offscreen draw surface"); + return; + } + + // Point the renderer at the screen bytes + gScreenRowbytes = drawSurface->w; + gScreenAddress = drawSurface->pixels; + + grd_mode_cap.vbase = gScreenAddress; +} diff --git a/engine/src/MacSrc/ShockBitmap.h b/engine/src/MacSrc/ShockBitmap.h new file mode 100644 index 0000000..e9f89ae --- /dev/null +++ b/engine/src/MacSrc/ShockBitmap.h @@ -0,0 +1,38 @@ +/* + +Copyright (C) 1994-1995 Looking Glass Technologies, Inc. +Copyright (C) 2015-2018 Night Dive Studios, LLC. +Copyright (C) 2018-2020 Shockolate Project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ +// ShockBitmap.c - Manages off-screen bitmaps and palettes. + +#include + +// Globals + +// Current draw surface +extern SDL_Surface *drawSurface; +// Offscreen draw surface +extern SDL_Surface *offscreenDrawSurface; + +// Prototypes + +/// Change screen size. +void ChangeScreenSize(int width, int height); + +/// Setup the main offscreen bitmaps. +void SetupOffscreenBitmaps(int width, int height); diff --git a/engine/src/MacSrc/Xmi.c b/engine/src/MacSrc/Xmi.c new file mode 100644 index 0000000..17654f3 --- /dev/null +++ b/engine/src/MacSrc/Xmi.c @@ -0,0 +1,889 @@ +#include + +#include "Xmi.h" +#include "MusicDevice.h" +#include "Prefs.h" + +unsigned int NumTracks; +char ChannelThread[16]; // 16 device channels +int NumUsedChannels; // number of in-use device channels + +MIDI_EVENT **TrackEvents; +short *TrackTiming; +unsigned short *TrackUsedChannels; + +MIDI_EVENT *ThreadEventList[NUM_THREADS]; +int ThreadTiming[NUM_THREADS]; +char ThreadChannelRemap[16 * NUM_THREADS]; +SDL_atomic_t DeviceChannelVolume[16]; // only msb: 0-127 +SDL_atomic_t ThreadPlaying[NUM_THREADS]; +SDL_atomic_t ThreadCommand[NUM_THREADS]; + +MusicDevice *MusicDev; +static SDL_mutex *MyMutex; + +void MusicCallback(void *userdata, Uint8 *stream, int len) { + MusicDevice *dev; + + SDL_LockMutex(MyMutex); + dev = *(MusicDevice **)userdata; + if (!dev || !dev->isOpen) { + SDL_UnlockMutex(MyMutex); + return; + } + + SDL_memset(stream, 0, (size_t)len); // in case we don't get anything + dev->generate(dev, (short *)((void *)stream), len / (int)(2 * sizeof(short))); + SDL_UnlockMutex(MyMutex); +} + +void FreeXMI(void) { + unsigned int track; + MIDI_EVENT *event, *next; + + for (track = 0; track < NumTracks; track++) { + event = TrackEvents[track]; + while (event) { + next = event->next; + if (event->buffer) + free(event->buffer); + free(event); + event = next; + } + } + + if (TrackEvents) { + free(TrackEvents); + TrackEvents = 0; + } + if (TrackTiming) { + free(TrackTiming); + TrackTiming = 0; + } + if (TrackUsedChannels) { + free(TrackUsedChannels); + TrackUsedChannels = 0; + } + + NumTracks = 0; +} + +MIDI_EVENT *NewMIDIEvent(MIDI_EVENT **eventlist, MIDI_EVENT *curevent, int time) { + if (*eventlist == 0) { + *eventlist = curevent = (MIDI_EVENT *)malloc(sizeof(MIDI_EVENT)); + + curevent->next = 0; + + if (time < 0) + curevent->time = 0; + else + curevent->time = time; + curevent->buffer = 0; + curevent->len = 0; + + return curevent; + } + + if (time < 0) { + MIDI_EVENT *event = (MIDI_EVENT *)malloc(sizeof(MIDI_EVENT)); + + event->next = *eventlist; + *eventlist = curevent = event; + + curevent->time = 0; + curevent->buffer = 0; + curevent->len = 0; + + return curevent; + } + + if (curevent->time > time) + curevent = *eventlist; + + while (curevent->next) { + if (curevent->next->time > time) { + MIDI_EVENT *event = (MIDI_EVENT *)malloc(sizeof(MIDI_EVENT)); + + event->next = curevent->next; + curevent->next = event; + curevent = event; + + curevent->time = time; + curevent->buffer = 0; + curevent->len = 0; + + return curevent; + } + + curevent = curevent->next; + } + + curevent->next = (MIDI_EVENT *)malloc(sizeof(MIDI_EVENT)); + + curevent = curevent->next; + curevent->next = 0; + + curevent->time = time; + curevent->buffer = 0; + curevent->len = 0; + + return curevent; +} + +int ReadXMI(const char *filename) { + FILE *f; + int size, start, begin, pos, time, end, tempo, tempo_set; + unsigned int i, count, len, chunk_len, quant, status, delta, b0, b1, b2, b3; + unsigned char *data, *p; + short ppqn; + unsigned short used_channels; + MIDI_EVENT *eventlist, *curevent, *prev; + char buf[32]; + MusicMode mode = Music_GeneralMidi; + + INFO("Reading XMI %s", filename); + + extern FILE *fopen_caseless(const char *path, const char *mode); // see caseless.c + f = fopen_caseless(filename, "rb"); + if (f == 0) { + ERROR("Could not read XMI"); + return 0; + } + + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + data = (unsigned char *)malloc(size); + if (fread(data, size, 1, f) != 1) { + free(data); + fclose(f); + return 0; + } + fclose(f); + + p = data; + + memcpy(buf, p, 4); + p += 4; + if (memcmp(buf, "FORM", 4)) { + free(data); + return 0; + } // is not an xmi + + b3 = *p++; + b2 = *p++; + b1 = *p++; + b0 = *p++; + len = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); + + start = p - data; + + memcpy(buf, p, 4); + p += 4; + if (!memcmp(buf, "XMID", 4)) + NumTracks = 1; // XMI doesn't have XDIR, so there's only one track + else if (memcmp(buf, "XDIR", 4)) { + free(data); + return 0; + } // invalid XMI format + else { + NumTracks = 0; + + for (i = 4; i < len; i++) { + memcpy(buf, p, 4); + p += 4; + + b3 = *p++; + b2 = *p++; + b1 = *p++; + b0 = *p++; + chunk_len = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); + + i += 8; + + if (memcmp(buf, "INFO", 4)) { + p += ((chunk_len + 1) & ~1); + i += ((chunk_len + 1) & ~1); + continue; + } + + if (chunk_len < 2) + break; + + b0 = *p++; + b1 = *p++; + NumTracks = b0 | (b1 << 8); + + break; + } + + if (NumTracks == 0) { + free(data); + return 0; + } // xmi must have at least one track + + p = data + start + ((len + 1) & ~1); + + memcpy(buf, p, 4); + p += 4; + if (memcmp(buf, "CAT ", 4)) { + free(data); + return 0; + } // invalid XMI format + + b3 = *p++; + b2 = *p++; + b1 = *p++; + b0 = *p++; + len = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); + + memcpy(buf, p, 4); + p += 4; + if (memcmp(buf, "XMID", 4)) { + free(data); + return 0; + } // invalid XMI format + } + + INFO("NumTracks: %i", NumTracks); + + TrackEvents = (MIDI_EVENT **)malloc(NumTracks * sizeof(MIDI_EVENT *)); + TrackTiming = (short *)malloc(NumTracks * sizeof(short)); + TrackUsedChannels = (unsigned short *)malloc(NumTracks * sizeof(unsigned short)); + + for (i = 0; i < NumTracks; i++) + TrackEvents[i] = 0; + + count = 0; + + while (p - data < size && count != NumTracks) { + memcpy(buf, p, 4); + p += 4; + + b3 = *p++; + b2 = *p++; + b1 = *p++; + b0 = *p++; + len = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); + + if (!memcmp(buf, "FORM", 4)) { + p += 4; + + memcpy(buf, p, 4); + p += 4; + + b3 = *p++; + b2 = *p++; + b1 = *p++; + b0 = *p++; + len = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); + } + + if (memcmp(buf, "EVNT", 4)) { + p += ((len + 1) & ~1); + continue; + } + + eventlist = 0; + curevent = 0; + time = 0; + end = 0; + tempo = 500000; + tempo_set = 0; + status = 0; + used_channels = 0; + + begin = p - data; + + while (!end && p - data < size) { + quant = 0; + for (i = 0; i < 4; i++) { + b0 = *p++; + if (b0 & 0x80) { + p--; + break; + } + quant += b0; + } + time += quant * 3; + + status = *p++; + switch (status >> 4) { + case 0x9: // note on + used_channels |= (1 << (status & 15)); + b0 = *p++; + curevent = NewMIDIEvent(&eventlist, curevent, time); + curevent->status = status; + curevent->data[0] = b0; + curevent->data[1] = *p++; + delta = 0; + for (i = 0; i < 4; i++) { + b1 = *p++; + delta <<= 7; + delta |= b1 & 0x7F; + if (!(b1 & 0x80)) + break; + } + prev = curevent; + curevent = NewMIDIEvent(&eventlist, curevent, time + delta * 3); + curevent->status = status; + curevent->data[0] = b0; + curevent->data[1] = 0; + curevent = prev; + break; + + case 0x8: + case 0xA: + case 0xB: + case 0xE: // note off, aftertouch, controller, pitch wheel + used_channels |= (1 << (status & 15)); + curevent = NewMIDIEvent(&eventlist, curevent, time); + curevent->status = status; + curevent->data[0] = *p++; + curevent->data[1] = *p++; + break; + + case 0xC: + case 0xD: // program change, pressure + used_channels |= (1 << (status & 15)); + curevent = NewMIDIEvent(&eventlist, curevent, time); + curevent->status = status; + curevent->data[0] = *p++; + break; + + case 0xF: // sysex + if (status == 0xFF) { + pos = p - data; + b0 = *p++; + if (b0 == 0x2F) + end = 1; + else if (b0 == 0x51 && !tempo_set) { + p++; + b3 = *p++; + b2 = *p++; + b1 = *p++; + tempo = (b1 | (b2 << 8) | (b3 << 16)) * 3; + tempo_set = 1; + } else if (b0 == 0x51 && tempo_set) { + quant = 0; + for (i = 0; i < 4; i++) { + b1 = *p++; + quant <<= 7; + quant |= b1 & 0x7F; + if (!(b1 & 0x80)) + break; + } + p += quant; + break; + } + p = data + pos; + } + curevent = NewMIDIEvent(&eventlist, curevent, time); + curevent->status = status; + if (status == 0xFF) + curevent->data[0] = *p++; + quant = 0; + for (i = 0; i < 4; i++) { + b0 = *p++; + quant <<= 7; + quant |= b0 & 0x7F; + if (!(b0 & 0x80)) + break; + } + curevent->len = quant; + if (curevent->len) { + curevent->buffer = (unsigned char *)malloc(curevent->len); + memcpy(curevent->buffer, p, curevent->len); + p += curevent->len; + } + break; + + default: + break; + } + } + + ppqn = (tempo * 3) / 25000; + if (!ppqn) + break; // unable to convert data + + TrackEvents[count] = eventlist; + TrackTiming[count] = ppqn; + TrackUsedChannels[count] = used_channels; + count++; + + p = data + begin + ((len + 1) & ~1); + } + + if (count != NumTracks) // failed to extract all tracks from XMI + { + free(data); + NumTracks = count; + FreeXMI(); + return 0; + } + + free(data); + + // Setup a sound bank for res/sound/sblaster, or res/sound/genmidi + if (strstr(filename, "sblaster") != NULL) + mode = Music_SoundBlaster; + SDL_LockMutex(MyMutex); + if (MusicDev) { + MusicDev->setupMode(MusicDev, mode); + } + SDL_UnlockMutex(MyMutex); + switch (mode) { + case Music_GeneralMidi: + INFO("Set General MIDI mode"); + break; + case Music_SoundBlaster: + INFO("Set Sound Blaster mode"); + break; + } + + return 1; // success +} + +int MyThread(void *arg) { + int i; + + MIDI_EVENT *event[NUM_THREADS]; + int ppqn[NUM_THREADS]; + double Ippqn[NUM_THREADS]; + int tempo[NUM_THREADS]; + double tick[NUM_THREADS]; + double last_tick[NUM_THREADS]; + double last_time[NUM_THREADS]; + unsigned int start[NUM_THREADS]; + + for (i = 0; i < NUM_THREADS; i++) { + event[i] = 0; + ppqn[i] = 1; + Ippqn[i] = 1; + tempo[i] = 0x07A120; + tick[i] = 1; + last_tick[i] = 0; + last_time[i] = 0; + start[i] = 0; + + SDL_AtomicSet(&ThreadPlaying[i], 0); + SDL_AtomicSet(&ThreadCommand[i], THREAD_READY); + } + + for (;;) { + int delay = 1; + + for (i = 0; i < NUM_THREADS; i++) { + if (event[i] && SDL_AtomicGet(&ThreadCommand[i]) != THREAD_STOPTRACK) { + double aim = last_time[i] + (event[i]->time - last_tick[i]) * tick[i]; + double diff = aim - ((SDL_GetTicks() - start[i]) * 1000.0); + + if (diff > 0) { + if (diff < 1200.0) + delay = 0; + continue; + } + delay = 0; + + last_tick[i] = event[i]->time; + last_time[i] = aim; + + if (event[i]->status == 0xFF && event[i]->data[0] == 0x51) // tempo change + { + tempo[i] = (event[i]->buffer[0] << 16) | (event[i]->buffer[1] << 8) | event[i]->buffer[2]; + tick[i] = tempo[i] * Ippqn[i]; + } else if (event[i]->status >= 0x80 && event[i]->status < 0xF0) { + int channel = (event[i]->status & 15); + + if (channel != 9) + channel = ThreadChannelRemap[channel + 16 * i]; // remap channel, except 9 (percussion) + + uint8_t p1 = event[i]->data[0]; + uint8_t p2 = event[i]->data[1]; + + if ((event[i]->status & ~15) == 0xB0 && event[i]->data[0] == 0x07) { + // set volume msb + // store in array in case global volume changes later + SDL_AtomicSet(&DeviceChannelVolume[channel], p2); // 0-127 + // send volume change to device + SDL_LockMutex(MyMutex); + if (MusicDev && MusicDev->isOpen) { + // scale new volume according to global music volume + extern uchar curr_vol_lev; // 0-100 + const int scaledVolume = ((int)p2 * (int)curr_vol_lev) / 100; + MusicDev->sendControllerChange(MusicDev, channel, p1, scaledVolume); + } + SDL_UnlockMutex(MyMutex); + } else { + SDL_LockMutex(MyMutex); + if (MusicDev && MusicDev->isOpen) { + switch (event[i]->status & ~15) { + case 0x80: + MusicDev->sendNoteOff(MusicDev, channel, p1, p2); + break; + case 0x90: + MusicDev->sendNoteOn(MusicDev, channel, p1, p2); + break; + case 0xA0: + MusicDev->sendNoteAfterTouch(MusicDev, channel, p1, p2); + break; + case 0xB0: + MusicDev->sendControllerChange(MusicDev, channel, p1, p2); + break; + case 0xC0: + MusicDev->sendProgramChange(MusicDev, channel, p1); + break; + case 0xD0: + MusicDev->sendChannelAfterTouch(MusicDev, channel, p1); + break; + case 0xE0: + MusicDev->sendPitchBendML(MusicDev, channel, p2, p1); + break; + } + } + SDL_UnlockMutex(MyMutex); + } + } + + event[i] = event[i]->next; + if (event[i] == 0) + SDL_AtomicSet(&ThreadCommand[i], THREAD_STOPTRACK); + } + + if (SDL_AtomicGet(&ThreadCommand[i]) == THREAD_STOPTRACK) { + int channel; + + delay = 0; + + event[i] = 0; + last_tick[i] = 0; + last_time[i] = 0; + start[i] = SDL_GetTicks(); + + // here we should turn off all notes for these channels plus 9 (percussion) + for (channel = 0; channel < 16; channel++) + if (ChannelThread[channel] == i) { + ChannelThread[channel] = -1; + NumUsedChannels--; + SDL_AtomicSet(&DeviceChannelVolume[channel], 0); + } + + SDL_AtomicSet(&ThreadPlaying[i], 0); + SDL_AtomicSet(&ThreadCommand[i], THREAD_READY); + } + + if (SDL_AtomicGet(&ThreadCommand[i]) == THREAD_PLAYTRACK) { + delay = 0; + + event[i] = ThreadEventList[i]; + ppqn[i] = ThreadTiming[i]; + Ippqn[i] = 1.0 / ppqn[i]; + tempo[i] = 0x07A120; + tick[i] = tempo[i] * Ippqn[i]; + last_tick[i] = 0; + last_time[i] = 0; + start[i] = SDL_GetTicks(); + + SDL_AtomicSet(&ThreadPlaying[i], 1); + SDL_AtomicSet(&ThreadCommand[i], THREAD_READY); + } + + if (SDL_AtomicGet(&ThreadCommand[i]) == THREAD_EXIT) + return 0; + } + + SDL_Delay(delay); + } + + return 0; +} + +int GetTrackNumChannels(unsigned int track) { + int num = 0, channel; + + // count channels used by track (could be zero if only percussion channel (9) is used) + for (channel = 0; channel < 16; channel++) + if (channel != 9 && (TrackUsedChannels[track] & (1 << channel))) + num++; + + return num; +} + +void StartTrack(int thread, unsigned int track) { + int num, trackChannel, deviceChannel; + char channel_remap[16]; + + if (track >= NumTracks) + return; + + num = GetTrackNumChannels(track); + + while (SDL_AtomicGet(&ThreadCommand[thread]) != THREAD_READY) + SDL_Delay(1); + + // check if enough device channels free; 16 channels available except one (percussion) + if (NumUsedChannels + num <= 16 - 1) { + NumUsedChannels += num; + + memset(channel_remap, 0, 16); + + // assign channels used by track to device channels that are currently free + for (trackChannel = 0; trackChannel < 16; trackChannel++) { + // only map used, non-percussion channels + if (trackChannel != 9 && (TrackUsedChannels[track] & (1 << trackChannel))) { + // find first unassigned device channel + for (deviceChannel = 0; deviceChannel < 16; deviceChannel++) { + if (deviceChannel != 9 && ChannelThread[deviceChannel] == -1) + break; + } + channel_remap[trackChannel] = deviceChannel; + ChannelThread[deviceChannel] = thread; + // default to full volume + SDL_AtomicSet(&DeviceChannelVolume[deviceChannel], 127); + } + } + + ThreadEventList[thread] = TrackEvents[track]; + ThreadTiming[thread] = TrackTiming[track]; + memcpy(ThreadChannelRemap + 16 * thread, channel_remap, 16); + + SDL_AtomicSet(&ThreadCommand[thread], THREAD_PLAYTRACK); + + while (SDL_AtomicGet(&ThreadCommand[thread]) != THREAD_READY) + SDL_Delay(1); + } +} + +void StopTrack(int i) { + if (!SDL_AtomicGet(&ThreadPlaying[i])) + return; + + while (SDL_AtomicGet(&ThreadCommand[i]) != THREAD_READY) + SDL_Delay(1); + + SDL_AtomicSet(&ThreadCommand[i], THREAD_STOPTRACK); + + while (SDL_AtomicGet(&ThreadCommand[i]) != THREAD_READY) + SDL_Delay(1); +} + +void StopTheMusic(void) { + int i; + + for (i = 0; i < NUM_THREADS; i++) + StopTrack(i); + + SDL_LockMutex(MyMutex); + if (MusicDev) { + MusicDev->reset(MusicDev); + } + SDL_UnlockMutex(MyMutex); +} + +int IsPlaying(int i) { return SDL_AtomicGet(&ThreadPlaying[i]); } + +void InitReadXMI(void) { + int channel, i; + SDL_Thread *thread; + + InitDecXMI(); + + MyMutex = SDL_CreateMutex(); + + for (channel = 0; channel < 16; channel++) { + ChannelThread[channel] = -1; + SDL_AtomicSet(&DeviceChannelVolume[channel], 0); + } + + for (i = 0; i < NUM_THREADS; i++) { + SDL_AtomicSet(&ThreadPlaying[i], 0); + SDL_AtomicSet(&ThreadCommand[i], THREAD_INIT); + } + + thread = SDL_CreateThread(MyThread, "MyThread", NULL); + SDL_DetachThread(thread); // thread will go away on its own upon completion + + i = 0; + while (SDL_AtomicGet(&ThreadCommand[i]) == THREAD_INIT) + SDL_Delay(1); + + atexit(ShutdownReadXMI); +} + +void InitDecXMI(void) { + SDL_LockMutex(MyMutex); + if (MusicDev) { + WARN("InitDecXMI(): *****WARNING***** Creating new music device, but one already exists!"); + } + + // Start the Midi device + MusicDevice *musicdev = NULL; + int musicrate = 48000; + + switch (gShockPrefs.soMidiBackend) { + case OPT_SEQ_ADLMIDI: // adlmidi + { + INFO("Creating ADLMIDI device"); + musicdev = CreateMusicDevice(Music_AdlMidi); + } break; + case OPT_SEQ_NativeMI: // native midi + { + INFO("Creating native MIDI device"); + musicdev = CreateMusicDevice(Music_Native); + } break; +#ifdef USE_FLUIDSYNTH + case OPT_SEQ_FluidSyn: // fluidsynth + { + INFO("Creating FluidSynth MIDI device"); + musicdev = CreateMusicDevice(Music_FluidSynth); + } break; +#endif + } + + // init chosen music device + INFO("Opening MIDI device using output %d", gShockPrefs.soMidiOutput); + if (musicdev && musicdev->init(musicdev, gShockPrefs.soMidiOutput, musicrate) != 0) { + musicdev->destroy(musicdev); + musicdev = NULL; + } + + // fallback to dummy + if (!musicdev) { + WARN("Using dummy MIDI driver"); + musicdev = CreateMusicDevice(Music_None); + if (musicdev) { + musicdev->init(musicdev, gShockPrefs.soMidiOutput, musicrate); + } + } + + // force prefs to align with music device output + if (musicdev) { + gShockPrefs.soMidiOutput = musicdev->outputIndex; + } + + MusicDev = musicdev; + SDL_UnlockMutex(MyMutex); +} + +void ReloadDecXMI(void) { + int i; + + // determine whether a device type change is being requested, by comparing the + // current device type (if any) with current preferences setting + short deviceTypeMatch = 0; + SDL_LockMutex(MyMutex); + if (MusicDev) { + switch (MusicDev->deviceType) { + case Music_None: + deviceTypeMatch = 0; + break; + case Music_AdlMidi: + deviceTypeMatch = (gShockPrefs.soMidiBackend == 0); + break; + case Music_Native: + deviceTypeMatch = (gShockPrefs.soMidiBackend == 1); + break; +#ifdef USE_FLUIDSYNTH + case Music_FluidSynth: + deviceTypeMatch = (gShockPrefs.soMidiBackend == 2); + break; +#endif + } + } + + // only destroy the device if it exists and any of the following apply: + // - it hasn't been opened yet + // - device type change requested + // - device output change requested + // this is needed to protect against reload spam generated by the UI slider + if (MusicDev && (!MusicDev->isOpen || !deviceTypeMatch || MusicDev->outputIndex != gShockPrefs.soMidiOutput)) { + SDL_UnlockMutex(MyMutex); + INFO("Closing MIDI driver due to reload"); + + for (i = 0; i < NUM_THREADS; i++) { + StopTrack(i); + } + + SDL_LockMutex(MyMutex); + MusicDev->destroy(MusicDev); + MusicDev = NULL; + } + + // only init music device if it doesn't still exist + if (!MusicDev) { + SDL_UnlockMutex(MyMutex); + InitDecXMI(); + } else { + SDL_UnlockMutex(MyMutex); + } +} + +void ShutdownReadXMI(void) { + int i; + + for (i = 0; i < NUM_THREADS; i++) { + StopTrack(i); + // don't set THREAD_EXIT yet, as this seems to cause deadlocks + } + SDL_Delay(50); + for (i = 0; i < NUM_THREADS; i++) { + SDL_AtomicSet(&ThreadCommand[i], THREAD_EXIT); + } + SDL_Delay(50); // wait a bit for thread to hopefully exit + + INFO("Closing MIDI driver due to shutdown"); + SDL_LockMutex(MyMutex); + if (MusicDev) { + MusicDev->destroy(MusicDev); + MusicDev = NULL; + } else { + WARN("ShutdownReadXMI(): Shutdown request received, but no music device exists!"); + } + SDL_UnlockMutex(MyMutex); + + FreeXMI(); + + SDL_DestroyMutex(MyMutex); +} + +unsigned int GetOutputCountXMI(void) { + unsigned int outputCount = 0; + + SDL_LockMutex(MyMutex); + if (MusicDev) { + outputCount = MusicDev->getOutputCount(MusicDev); + } + SDL_UnlockMutex(MyMutex); + + return outputCount; +} + +void GetOutputNameXMI(const unsigned int outputIndex, char *buffer, const unsigned int bufferSize) { + SDL_LockMutex(MyMutex); + if (MusicDev && buffer && bufferSize >= 1) { + MusicDev->getOutputName(MusicDev, outputIndex, buffer, bufferSize); + } + SDL_UnlockMutex(MyMutex); +} + +void UpdateVolumeXMI(void) { + // global volume has been changed + extern uchar curr_vol_lev; // 0-100 + INFO("UpdateVolumeXMI(): Global music volume change to %d percent", curr_vol_lev); + + // tell the music driver + SDL_LockMutex(MyMutex); + if (MusicDev && MusicDev->isOpen) { + // send volume change controller (#7) for all channels + for (int i = 0; i <= 15; ++i) { + // skip unused device channels + if (i != 9 && ChannelThread[i] == -1) + continue; + // scale new volume according to global music volume + const int scaledVolume = ((int)SDL_AtomicGet(&DeviceChannelVolume[i]) * (int)curr_vol_lev) / 100; + MusicDev->sendControllerChange(MusicDev, i, 7, scaledVolume); + } + } + SDL_UnlockMutex(MyMutex); +} diff --git a/engine/src/MacSrc/Xmi.h b/engine/src/MacSrc/Xmi.h new file mode 100644 index 0000000..d170c00 --- /dev/null +++ b/engine/src/MacSrc/Xmi.h @@ -0,0 +1,54 @@ +#define NUM_THREADS 8 + +#define THREAD_INIT 0 +#define THREAD_READY 1 +#define THREAD_PLAYTRACK 2 +#define THREAD_STOPTRACK 3 +#define THREAD_EXIT 4 + +extern unsigned int NumTracks; + +//-1: no thread is using this device channel; 0- : thread index that is using this device channel +extern char ChannelThread[16]; // 16 device channels + +extern int NumUsedChannels; // number of in-use device channels + +void FreeXMI(void); +int ReadXMI(const char *filename); +void StartTrack(int i, unsigned int track); +void StopTrack(int i); +void StopTheMusic(void); +int IsPlaying(int i); +void InitReadXMI(void); +void InitDecXMI(void); +void ReloadDecXMI(void); +void ShutdownReadXMI(void); +unsigned int GetOutputCountXMI(void); +void GetOutputNameXMI(const unsigned int outputIndex, char *buffer, const unsigned int bufferSize); +void UpdateVolumeXMI(void); + +struct midi_event_struct { + int time; + unsigned char status; + unsigned char data[2]; + unsigned int len; + unsigned char *buffer; + struct midi_event_struct *next; +}; + +typedef struct midi_event_struct MIDI_EVENT; + +extern MIDI_EVENT **TrackEvents; +extern short *TrackTiming; +extern unsigned short *TrackUsedChannels; + +extern MIDI_EVENT *ThreadEventList[NUM_THREADS]; +extern int ThreadTiming[NUM_THREADS]; +extern char ThreadChannelRemap[16 * NUM_THREADS]; +extern SDL_atomic_t DeviceChannelVolume[16]; // only msb: 0-127 +extern SDL_atomic_t ThreadPlaying[NUM_THREADS]; +extern SDL_atomic_t ThreadCommand[NUM_THREADS]; + +struct thread_data { + int i; // thread index +}; diff --git a/engine/src/MusicSrc/MusicDevice.c b/engine/src/MusicSrc/MusicDevice.c new file mode 100644 index 0000000..8519229 --- /dev/null +++ b/engine/src/MusicSrc/MusicDevice.c @@ -0,0 +1,1337 @@ +#include "MusicDevice.h" +#include +#include +#ifdef WIN32 +// General Windows API support +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +// Windows NativeMidi backend support +# include +#else +// Linux NativeMidi backend support +# if defined(USE_ALSA) +# include +# endif +// Linux/Mac FluidMidi SF2 search support +# include +# include +#endif +#ifdef __APPLE__ +#include +#endif + +//------------------------------------------------------------------------------ +// Dummy MIDI player + +static int NullMidiInit(MusicDevice *dev, const unsigned int outputIndex, unsigned samplerate) +{ + if (!dev || dev->isOpen) return 0; + + dev->isOpen = 1; + dev->outputIndex = 0; + + // suppress compiler warnings + (void)outputIndex; + (void)samplerate; + + return 0; +} + +static void NullMidiDestroy(MusicDevice *dev) +{ + if (!dev) return; + + free(dev); +} + +static void NullMidiSetupMode(MusicDevice *dev, MusicMode mode) +{ + // suppress compiler warnings + (void)dev; + (void)mode; +} + +static void NullMidiReset(MusicDevice *dev) +{ + // suppress compiler warnings + (void)dev; +} + +static void NullMidiGenerate(MusicDevice *dev, short *samples, int numframes) +{ + memset(samples, 0, 2 * (unsigned int)numframes * sizeof(short)); + + // suppress compiler warnings + (void)dev; +} + +static void NullMidiSendNoteOff(MusicDevice *dev, int channel, int note, int vel) +{ + // suppress compiler warnings + (void)dev; + (void)channel; + (void)note; + (void)vel; +} + +static void NullMidiSendNoteOn(MusicDevice *dev, int channel, int note, int vel) +{ + // suppress compiler warnings + (void)dev; + (void)channel; + (void)note; + (void)vel; +} + +static void NullMidiSendNoteAfterTouch(MusicDevice *dev, int channel, int note, int touch) +{ + // suppress compiler warnings + (void)dev; + (void)channel; + (void)note; + (void)touch; +} + +static void NullMidiSendControllerChange(MusicDevice *dev, int channel, int ctl, int val) +{ + // suppress compiler warnings + (void)dev; + (void)channel; + (void)ctl; + (void)val; +} + +static void NullMidiSendProgramChange(MusicDevice *dev, int channel, int pgm) +{ + // suppress compiler warnings + (void)dev; + (void)channel; + (void)pgm; +} + +static void NullMidiSendChannelAfterTouch(MusicDevice *dev, int channel, int touch) +{ + // suppress compiler warnings + (void)dev; + (void)channel; + (void)touch; +} + +static void NullMidiSendPitchBendML(MusicDevice *dev, int channel, int msb, int lsb) +{ + // suppress compiler warnings + (void)dev; + (void)channel; + (void)msb; + (void)lsb; +} + +static unsigned int NullMidiGetOutputCount(MusicDevice *dev) +{ + // suppress compiler warnings + (void)dev; + + return 1; +} + +static void NullMidiGetOutputName(MusicDevice *dev, const unsigned int outputIndex, char *buffer, const unsigned int bufferSize) +{ + if (!buffer || bufferSize < 1) return; + // save last position for NULL character + strncpy(buffer, "NullMidi", bufferSize - 1); + // put NULL in last position in case we filled up everything else + *(buffer + bufferSize - 1) = '\0'; + + // suppress compiler warnings + (void)dev; + (void)outputIndex; +} + +static MusicDevice *createNullMidiDevice() +{ + MusicDevice *dev = malloc(sizeof(MusicDevice)); + dev->init = &NullMidiInit; + dev->destroy = &NullMidiDestroy; + dev->setupMode = &NullMidiSetupMode; + dev->reset = &NullMidiReset; + dev->generate = &NullMidiGenerate; + dev->sendNoteOff = &NullMidiSendNoteOff; + dev->sendNoteOn = &NullMidiSendNoteOn; + dev->sendNoteAfterTouch = &NullMidiSendNoteAfterTouch; + dev->sendControllerChange = &NullMidiSendControllerChange; + dev->sendProgramChange = &NullMidiSendProgramChange; + dev->sendChannelAfterTouch = &NullMidiSendChannelAfterTouch; + dev->sendPitchBendML = &NullMidiSendPitchBendML; + dev->getOutputCount = &NullMidiGetOutputCount; + dev->getOutputName = &NullMidiGetOutputName; + dev->isOpen = 0; + dev->outputIndex = 0; + dev->deviceType = Music_None; + dev->musicType = MUSICTYPE_SBLASTER; + return dev; +} + +//------------------------------------------------------------------------------ +// ADLMIDI player for OPL3 + +#include "adlmidi.h" + +typedef struct AdlMidiDevice +{ + MusicDevice dev; + struct ADL_MIDIPlayer *adl; +} AdlMidiDevice; + +static int AdlMidiInit(MusicDevice *dev, const unsigned int outputIndex, unsigned samplerate) +{ + AdlMidiDevice *adev = (AdlMidiDevice *)dev; + if (!adev || adev->dev.isOpen) return 0; + struct ADL_MIDIPlayer *adl = adl_init(samplerate); + + adl_switchEmulator(adl, ADLMIDI_EMU_NUKED_174); + adl_setNumChips(adl, 1); + adl_setVolumeRangeModel(adl, ADLMIDI_VolumeModel_AUTO); + adl_setRunAtPcmRate(adl, 1); + + adev->adl = adl; + + adev->dev.isOpen = 1; + adev->dev.outputIndex = outputIndex; + + return 0; +} + +static void AdlMidiDestroy(MusicDevice *dev) +{ + AdlMidiDevice *adev = (AdlMidiDevice *)dev; + if (!adev) return; + if (adev->dev.isOpen) + { + adl_close(adev->adl); + } + free(adev); +} + +static void AdlMidiSetupMode(MusicDevice *dev, MusicMode mode) +{ + AdlMidiDevice *adev = (AdlMidiDevice *)dev; + if (!adev || !adev->dev.isOpen) return; + + //Use sound bank 45 for res/sound/sblaster, 0 for res/sound/genmidi + adl_setBank(adev->adl, (mode == Music_SoundBlaster) ? 45 : 0); +} + +static void AdlMidiReset(MusicDevice *dev) +{ + AdlMidiDevice *adev = (AdlMidiDevice *)dev; + if (!adev || !adev->dev.isOpen) return; + + adl_reset(adev->adl); +} + +static void AdlMidiGenerate(MusicDevice *dev, short *samples, int numframes) +{ + AdlMidiDevice *adev = (AdlMidiDevice *)dev; + if (!adev || !adev->dev.isOpen) return; + + const int numSamples = numframes * 2; + adl_generate(adev->adl, numSamples, samples); + // ugly hack: libadlmidi has quiet output, so double all values + short *sample = samples; + for (int i = 0; i < numSamples; ++i, ++sample) + { + *sample *= 2; + } +} + +static void AdlMidiSendNoteOff(MusicDevice *dev, int channel, int note, int vel) +{ + AdlMidiDevice *adev = (AdlMidiDevice *)dev; + if (!adev || !adev->dev.isOpen) return; + + adl_rt_noteOff(adev->adl, channel, note); + (void)vel; +} + +static void AdlMidiSendNoteOn(MusicDevice *dev, int channel, int note, int vel) +{ + AdlMidiDevice *adev = (AdlMidiDevice *)dev; + if (!adev || !adev->dev.isOpen) return; + + adl_rt_noteOn(adev->adl, channel, note, vel); +} + +static void AdlMidiSendNoteAfterTouch(MusicDevice *dev, int channel, int note, int touch) +{ + AdlMidiDevice *adev = (AdlMidiDevice *)dev; + if (!adev || !adev->dev.isOpen) return; + + adl_rt_noteAfterTouch(adev->adl, channel, note, touch); +} + +static void AdlMidiSendControllerChange(MusicDevice *dev, int channel, int ctl, int val) +{ + AdlMidiDevice *adev = (AdlMidiDevice *)dev; + if (!adev || !adev->dev.isOpen) return; + + adl_rt_controllerChange(adev->adl, channel, ctl, val); +} + +static void AdlMidiSendProgramChange(MusicDevice *dev, int channel, int pgm) +{ + AdlMidiDevice *adev = (AdlMidiDevice *)dev; + if (!adev || !adev->dev.isOpen) return; + + adl_rt_patchChange(adev->adl, channel, pgm); +} + +static void AdlMidiSendChannelAfterTouch(MusicDevice *dev, int channel, int touch) +{ + AdlMidiDevice *adev = (AdlMidiDevice *)dev; + if (!adev || !adev->dev.isOpen) return; + + adl_rt_channelAfterTouch(adev->adl, channel, touch); +} + +static void AdlMidiSendPitchBendML(MusicDevice *dev, int channel, int msb, int lsb) +{ + AdlMidiDevice *adev = (AdlMidiDevice *)dev; + if (!adev || !adev->dev.isOpen) return; + + adl_rt_pitchBendML(adev->adl, channel, msb, lsb); +} + +static unsigned int AdlMidiGetOutputCount(MusicDevice *dev) +{ + // suppress compiler warnings + (void)dev; + + // TODO: add support for SB and GM modes? + return 1; +} + +static void AdlMidiGetOutputName(MusicDevice *dev, const unsigned int outputIndex, char *buffer, const unsigned int bufferSize) +{ + if (!buffer || bufferSize < 1) return; + // save last position for NULL character + strncpy(buffer, "AdlMidi", bufferSize - 1); + // put NULL in last position in case we filled up everything else + *(buffer + bufferSize - 1) = '\0'; + + // suppress compiler warnings + (void)dev; + (void)outputIndex; +} + +static MusicDevice *createAdlMidiDevice() +{ + AdlMidiDevice *adev = malloc(sizeof(AdlMidiDevice)); + adev->dev.init = &AdlMidiInit; + adev->dev.destroy = &AdlMidiDestroy; + adev->dev.setupMode = &AdlMidiSetupMode; + adev->dev.reset = &AdlMidiReset; + adev->dev.generate = &AdlMidiGenerate; + adev->dev.sendNoteOff = &AdlMidiSendNoteOff; + adev->dev.sendNoteOn = &AdlMidiSendNoteOn; + adev->dev.sendNoteAfterTouch = &AdlMidiSendNoteAfterTouch; + adev->dev.sendControllerChange = &AdlMidiSendControllerChange; + adev->dev.sendProgramChange = &AdlMidiSendProgramChange; + adev->dev.sendChannelAfterTouch = &AdlMidiSendChannelAfterTouch; + adev->dev.sendPitchBendML = &AdlMidiSendPitchBendML; + adev->dev.getOutputCount = &AdlMidiGetOutputCount; + adev->dev.getOutputName = &AdlMidiGetOutputName; + adev->dev.isOpen = 0; + adev->dev.outputIndex = 0; + adev->dev.deviceType = Music_AdlMidi; + adev->dev.musicType = MUSICTYPE_SBLASTER; + return &adev->dev; +} + +//------------------------------------------------------------------------------ +// Native OS MIDI +// +// Currently only supports Windows MCI MIDI +// could support coremidi on OSX in the future? +// this devolves into another null driver on unsupported configurations + +typedef struct +{ + MusicDevice dev; +#ifdef WIN32 + HMIDIOUT outHandle; +#elif defined(USE_ALSA) + snd_seq_t *outHandle; + int alsaMyId; + int alsaMyPort; + int alsaOutputId; + int alsaOutputPort; +#endif +} NativeMidiDevice; + +// all standard MIDI message types +// these go in the high nibble of status byte +// low nibble is used for channel # +// source: http://midi.teragonaudio.com/tech/midispec.htm +typedef enum +{ + MME_NOTE_OFF = 0x8, + MME_NOTE_ON = 0x9, + MME_AFTERTOUCH = 0xA, + MME_CONTROL_CHANGE = 0xB, + MME_PROGRAM_CHANGE = 0xC, + MME_CHANNEL_PRESSURE = 0xD, + MME_PITCH_WHEEL = 0xE +} MidiMessageEnum; + +// data1 byte for MSE_CONTROL_CHANGE message +// this is a relevant subset, because there are dozens +// source: http://midi.teragonaudio.com/tech/midispec.htm +typedef enum +{ + MCE_ALL_SOUND_OFF = 120, + MCE_ALL_CONTROLLERS_OFF = 121 +} MidiControllerEnum; + +// define backend-API-specific helper macros here +#define NM_CLAMP15(x) ((unsigned char)((unsigned char)(x) & 0x0F)) +#define NM_CLAMP127(x) ((unsigned char)((unsigned char)(x) & 0x7F)) +#define NM_CLAMP255(x) ((unsigned char)((unsigned char)(x) & 0xFF)) + +// define backend-API-specific helper functions here +#ifdef WIN32 +inline static void NativeMidiSendMessage( + HMIDIOUT outHandle, + const MidiMessageEnum message, + const UCHAR channel, + const UCHAR data1, + const UCHAR data2) +{ + union { + DWORD dwData; + UCHAR bData[4]; + } u; + u.bData[0] = (UCHAR)(NM_CLAMP15(message) << 4 | NM_CLAMP15(channel)); + u.bData[1] = NM_CLAMP127(data1); + u.bData[2] = NM_CLAMP127(data2); + u.bData[3] = 0; + +// INFO("NativeMidiSendMessage(): Sending MIDI data: 0x%08X", u.dwData); + const unsigned long err = midiOutShortMsg(outHandle, u.dwData); + if (err) + { + static char buffer[1024]; + midiOutGetErrorText(err, &buffer[0], 1024); + WARN("NativeMidiSendMessage(): midiOutShortMsg() error: %s", &buffer[0]); + } +} +#elif defined(USE_ALSA) +inline static void NativeMidiAlsaInitEvent(NativeMidiDevice *ndev, snd_seq_event_t* ev) +{ + if (!ndev || !ndev->dev.isOpen || !ev) return; + snd_seq_ev_clear(ev); + snd_seq_ev_set_direct(ev); // do it now + snd_seq_ev_set_source(ev, ndev->alsaMyPort); + snd_seq_ev_set_dest(ev, ndev->alsaOutputId, ndev->alsaOutputPort); +} + +inline static void NativeMidiAlsaSendEvent(NativeMidiDevice *ndev, snd_seq_event_t* ev) +{ + if (!ndev || !ndev->dev.isOpen || !ev) return; + snd_seq_event_output(ndev->outHandle, ev); // send to queue + snd_seq_drain_output(ndev->outHandle); // process queue +} +#endif + +// forward declares +static void NativeMidiSendControllerChange(MusicDevice *dev, int channel, int ctl, int val); +static void NativeMidiReset(MusicDevice *dev); + +static int NativeMidiInit(MusicDevice *dev, const unsigned int outputIndex, unsigned samplerate) +{ +// INFO("Native MIDI device open request for outputIndex=%d", outputIndex); + NativeMidiDevice *ndev = (NativeMidiDevice *)dev; + if (!ndev || ndev->dev.isOpen) return 0; +#ifdef WIN32 + // if outputIndex is 0, use MIDI_MAPPER + // else subract 1 to get the real output number + const UINT realOutput = ( + outputIndex == 0 ? MIDI_MAPPER : outputIndex - 1); + MMRESULT res = midiOutOpen(&(ndev->outHandle), realOutput, 0, 0, CALLBACK_NULL); + if (res != MMSYSERR_NOERROR) + { + static char buffer[1024]; + midiOutGetErrorText(res, &buffer[0], 1024); + WARN("NativeMidiInit(): native midi open failed with error: %s", &buffer[0]); + return -1; + } +// INFO("NativeMidiInit(): native midi open succeeded"); + ndev->dev.isOpen = 1; + ndev->dev.outputIndex = outputIndex; + // send MIDI reset in case it was in a dirty state when we opened it + NativeMidiReset(dev); +#elif defined(USE_ALSA) + unsigned short foundOutput = 0; + unsigned int outputCount = 0; // subtract 1 to get index + int alsaError = 0; + snd_seq_client_info_t *cinfo = 0; + snd_seq_port_info_t *pinfo = 0; + int client = 0; + + // open the sequencer interface + if ((alsaError = snd_seq_open(&(ndev->outHandle), "default", SND_SEQ_OPEN_OUTPUT, 0)) < 0) + { + WARN("Error opening ALSA sequencer: %s", snd_strerror(alsaError)); + if (ndev->outHandle) + { + snd_seq_close(ndev->outHandle); + ndev->outHandle = 0; + } + return -1; + } + + // count ports that support MIDI write until we reach the requested index, + // which is probably the one we want + snd_seq_client_info_alloca(&cinfo); + snd_seq_port_info_alloca(&pinfo); + snd_seq_client_info_set_client(cinfo, -1); + while (outputCount <= outputIndex && + snd_seq_query_next_client(ndev->outHandle, cinfo) >= 0) + { + client = snd_seq_client_info_get_client(cinfo); + snd_seq_port_info_set_client(pinfo, client); + snd_seq_port_info_set_port(pinfo, -1); + while (outputCount <= outputIndex && + snd_seq_query_next_port(ndev->outHandle, pinfo) >= 0) + { + /* port must understand MIDI messages */ + if (!(snd_seq_port_info_get_type(pinfo) & SND_SEQ_PORT_TYPE_MIDI_GENERIC)) + continue; + /* we need both WRITE and SUBS_WRITE */ + if ((snd_seq_port_info_get_capability(pinfo) & (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)) != + (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)) + continue; + + ++outputCount; + if (outputCount - 1 == outputIndex) + { + // found it + foundOutput = 1; + } + } + } + + if (!foundOutput) + { + WARN("Failed to locate ALSA MIDI output at outputIndex=%d", outputIndex); + // close the sequencer interface + snd_seq_close(ndev->outHandle); + ndev->outHandle = 0; + return -1; + } + + // get client ID + ndev->alsaMyId = snd_seq_client_id(ndev->outHandle); + if ((alsaError = snd_seq_set_client_name(ndev->outHandle, "Shockolate")) < 0) + { + WARN("Error setting ALSA sequencer client name: %s", snd_strerror(alsaError)); + } + // create client port + ndev->alsaMyPort = snd_seq_create_simple_port( + ndev->outHandle, + "Shockolate", + 0, + SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION + ); + if (ndev->alsaMyPort < 0) + { + WARN("Error creating ALSA sequencer client port: %s", snd_strerror(ndev->alsaMyPort)); + snd_seq_close(ndev->outHandle); + ndev->outHandle = 0; + return -1; + } + + // connect our client to the output + ndev->alsaOutputId = snd_seq_port_info_get_client(pinfo); + ndev->alsaOutputPort = snd_seq_port_info_get_port(pinfo); + if ((alsaError = snd_seq_connect_to( + ndev->outHandle, ndev->alsaMyPort, + ndev->alsaOutputId, ndev->alsaOutputPort + )) < 0) + { + WARN("Failed to connect ALSA MIDI device: %s", snd_strerror(alsaError)); + snd_seq_close(ndev->outHandle); + ndev->outHandle = 0; + return -1; + } + + // connected + ndev->dev.isOpen = 1; + ndev->dev.outputIndex = outputIndex; +#endif + // suppress compiler warnings + (void)outputIndex; + (void)samplerate; + + return 0; +} + +static void NativeMidiDestroy(MusicDevice *dev) +{ + NativeMidiDevice *ndev = (NativeMidiDevice *)dev; + if (!ndev) return; +#ifdef WIN32 + if (ndev->dev.isOpen) + { +// INFO("NativeMidiDestroy(): closing native midi"); + // reset before close, so that notes aren't left hanging + NativeMidiReset(dev); + midiOutClose(ndev->outHandle); + ndev->outHandle = 0; + ndev->dev.isOpen = 0; + } +#elif defined(USE_ALSA) + if (ndev->dev.isOpen) + { + if (ndev->outHandle) + { + NativeMidiReset(dev); + snd_seq_close(ndev->outHandle); + ndev->outHandle = 0; + ndev->alsaMyId = 0; + ndev->alsaMyPort = 0; + ndev->alsaOutputId = 0; + ndev->alsaOutputPort = 0; + } + ndev->dev.isOpen = 0; + } +#endif + free(ndev); +} + +static void NativeMidiSetupMode(MusicDevice *dev, MusicMode mode) +{ + // nothing to do + + // suppress compiler warnings + (void)dev; + (void)mode; +} + +static void NativeMidiReset(MusicDevice *dev) +{ + NativeMidiDevice *ndev = (NativeMidiDevice *)dev; + if (!ndev || !ndev->dev.isOpen) return; +#if defined(WIN32) || defined(USE_ALSA) + // send All Sound Off for all channels + for (unsigned char chan = 0; chan <= 15; ++chan) + { + NativeMidiSendControllerChange(dev, chan, MCE_ALL_SOUND_OFF, 0); + } + // send All Controllers Off for all channels + // this is done in a separate loop to give the previous one a chance to + // settle out + for (unsigned char chan = 0; chan <= 15; ++chan) + { + NativeMidiSendControllerChange(dev, chan, MCE_ALL_CONTROLLERS_OFF, 0); + } +#endif +} + +static void NativeMidiGenerate(MusicDevice *dev, short *samples, int numframes) +{ + // native MIDI outputs at the OS level, to an external driver or real synth + // generate an empty sample since we have nothing to mix at the game level + memset(samples, 0, 2 * (unsigned int)numframes * sizeof(short)); + + // suppress compiler warnings + (void)dev; +} + +static void NativeMidiSendNoteOff(MusicDevice *dev, int channel, int note, int vel) +{ + NativeMidiDevice *ndev = (NativeMidiDevice *)dev; + if (!ndev || !ndev->dev.isOpen) return; +#ifdef WIN32 + // send note off + // yes, velocity is potentially relevant + NativeMidiSendMessage(ndev->outHandle, + MME_NOTE_OFF, + NM_CLAMP15(channel), + NM_CLAMP127(note), + NM_CLAMP127(vel)); +#elif defined(USE_ALSA) + // send note off + snd_seq_event_t ev; + NativeMidiAlsaInitEvent(ndev, &ev); + snd_seq_ev_set_noteoff(&ev, channel, note, vel); + NativeMidiAlsaSendEvent(ndev, &ev); +#else + // suppress compiler warnings + (void)channel; + (void)note; + (void)vel; +#endif +} + +static void NativeMidiSendNoteOn(MusicDevice *dev, int channel, int note, int vel) +{ + NativeMidiDevice *ndev = (NativeMidiDevice *)dev; + if (!ndev || !ndev->dev.isOpen) return; +#ifdef WIN32 + // send note on + NativeMidiSendMessage(ndev->outHandle, + MME_NOTE_ON, + NM_CLAMP15(channel), + NM_CLAMP127(note), + NM_CLAMP127(vel)); +#elif defined(USE_ALSA) + // send note on + snd_seq_event_t ev; + NativeMidiAlsaInitEvent(ndev, &ev); + snd_seq_ev_set_noteon(&ev, channel, note, vel); + NativeMidiAlsaSendEvent(ndev, &ev); +#else + // suppress compiler warnings + (void)channel; + (void)note; + (void)vel; +#endif +} + +static void NativeMidiSendNoteAfterTouch(MusicDevice *dev, int channel, int note, int touch) +{ + NativeMidiDevice *ndev = (NativeMidiDevice *)dev; + if (!ndev || !ndev->dev.isOpen) return; +#ifdef WIN32 + // send note aftertouch (pressure) + NativeMidiSendMessage(ndev->outHandle, + MME_AFTERTOUCH, + NM_CLAMP15(channel), + NM_CLAMP127(note), + NM_CLAMP127(touch)); +#elif defined(USE_ALSA) + // send note aftertouch (pressure) + snd_seq_event_t ev; + NativeMidiAlsaInitEvent(ndev, &ev); + snd_seq_ev_set_keypress(&ev, channel, note, touch); + NativeMidiAlsaSendEvent(ndev, &ev); +#else + // suppress compiler warnings + (void)channel; + (void)note; + (void)touch; +#endif +} + +static void NativeMidiSendControllerChange(MusicDevice *dev, int channel, int ctl, int val) +{ + NativeMidiDevice *ndev = (NativeMidiDevice *)dev; + if (!ndev || !ndev->dev.isOpen) return; +#ifdef WIN32 + // send controller change + NativeMidiSendMessage(ndev->outHandle, + MME_CONTROL_CHANGE, + NM_CLAMP15(channel), + NM_CLAMP127(ctl), + NM_CLAMP127(val)); +#elif defined(USE_ALSA) + // send controller change + snd_seq_event_t ev; + NativeMidiAlsaInitEvent(ndev, &ev); + snd_seq_ev_set_controller(&ev, channel, ctl, val); + NativeMidiAlsaSendEvent(ndev, &ev); +#else + // suppress compiler warnings + (void)channel; + (void)ctl; + (void)val; +#endif +} + +static void NativeMidiSendProgramChange(MusicDevice *dev, int channel, int pgm) +{ + NativeMidiDevice *ndev = (NativeMidiDevice *)dev; + if (!ndev || !ndev->dev.isOpen) return; +#ifdef WIN32 + // send program change + // only one data byte is used + NativeMidiSendMessage(ndev->outHandle, + MME_PROGRAM_CHANGE, + NM_CLAMP15(channel), + NM_CLAMP127(pgm), + 0); +#elif defined(USE_ALSA) + // send program change + snd_seq_event_t ev; + NativeMidiAlsaInitEvent(ndev, &ev); + snd_seq_ev_set_pgmchange(&ev, channel, pgm); + NativeMidiAlsaSendEvent(ndev, &ev); +#else + // suppress compiler warnings + (void)channel; + (void)pgm; +#endif +} + +static void NativeMidiSendChannelAfterTouch(MusicDevice *dev, int channel, int touch) +{ + NativeMidiDevice *ndev = (NativeMidiDevice *)dev; + if (!ndev || !ndev->dev.isOpen) return; +#ifdef WIN32 + // send channel aftertouch (pressure) + // only one data byte is used + NativeMidiSendMessage(ndev->outHandle, + MME_CHANNEL_PRESSURE, + NM_CLAMP15(channel), + NM_CLAMP127(touch), + 0); +#elif defined(USE_ALSA) + // send channel aftertouch (pressure) + snd_seq_event_t ev; + NativeMidiAlsaInitEvent(ndev, &ev); + snd_seq_ev_set_chanpress(&ev, channel, touch); + NativeMidiAlsaSendEvent(ndev, &ev); +#else + // suppress compiler warnings + (void)channel; + (void)touch; +#endif +} + +static void NativeMidiSendPitchBendML(MusicDevice *dev, int channel, int msb, int lsb) +{ + NativeMidiDevice *ndev = (NativeMidiDevice *)dev; + if (!ndev || !ndev->dev.isOpen) return; +#ifdef WIN32 + // send pitch bend + NativeMidiSendMessage(ndev->outHandle, + MME_PITCH_WHEEL, + NM_CLAMP15(channel), + NM_CLAMP127(lsb), + NM_CLAMP127(msb)); +#elif defined(USE_ALSA) + // send pitch bend + snd_seq_event_t ev; + NativeMidiAlsaInitEvent(ndev, &ev); + // from ScummVM - seems to sound correct + const long theBend = ((long)lsb + (long)(msb << 7)) - 0x2000; + snd_seq_ev_set_pitchbend(&ev, channel, theBend); + NativeMidiAlsaSendEvent(ndev, &ev); +#else + // suppress compiler warnings + (void)channel; + (void)msb; + (void)lsb; +#endif +} + +static unsigned int NativeMidiGetOutputCount(MusicDevice *dev) +{ +// INFO("Native MIDI output count request"); + // suppress compiler warnings + (void)dev; +#ifdef WIN32 + // add one for MIDI_MAPPER + return midiOutGetNumDevs() + 1; +#elif defined(USE_ALSA) + unsigned int outputCount = 0; + int alsaError = 0; + snd_seq_t *seqHandle = 0; + snd_seq_client_info_t *cinfo = 0; + snd_seq_port_info_t *pinfo = 0; + int client = 0; + + // open the sequencer interface + if ((alsaError = snd_seq_open(&seqHandle, "default", SND_SEQ_OPEN_OUTPUT, 0)) < 0) + { + WARN("Error opening ALSA sequencer: %s", snd_strerror(alsaError)); + return 0; + } + + // count all ports that support MIDI write + snd_seq_client_info_alloca(&cinfo); + snd_seq_port_info_alloca(&pinfo); + snd_seq_client_info_set_client(cinfo, -1); + while (snd_seq_query_next_client(seqHandle, cinfo) >= 0) + { + client = snd_seq_client_info_get_client(cinfo); + snd_seq_port_info_set_client(pinfo, client); + snd_seq_port_info_set_port(pinfo, -1); + while (snd_seq_query_next_port(seqHandle, pinfo) >= 0) + { + /* port must understand MIDI messages */ + if (!(snd_seq_port_info_get_type(pinfo) & SND_SEQ_PORT_TYPE_MIDI_GENERIC)) + continue; + /* we need both WRITE and SUBS_WRITE */ + if ((snd_seq_port_info_get_capability(pinfo) & (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)) != + (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)) + continue; + + ++outputCount; + } + } + + // close the sequencer interface + snd_seq_close(seqHandle); + + return outputCount; +#else + // "NULL "Unsupported" output + return 1; +#endif +} + +static void NativeMidiGetOutputName(MusicDevice *dev, const unsigned int outputIndex, char *buffer, const unsigned int bufferSize) +{ + if (!buffer || bufferSize < 1) return; +// INFO("Native MIDI output name request for outputIndex=%d", outputIndex); +#ifdef WIN32 + if (outputIndex == 0) + { + // the output #0 we advertise is MIDI_MAPPER + strncpy(buffer, "Windows MIDI mapper", bufferSize - 1); + } + else + { + // subtract one to get the real device number + MIDIOUTCAPS moc; + midiOutGetDevCaps(outputIndex - 1, &moc, sizeof(MIDIOUTCAPS)); + strncpy(buffer, moc.szPname, bufferSize - 1); + } +#elif defined(USE_ALSA) + unsigned int outputCount = 0; // subtract 1 to get index + int alsaError = 0; + snd_seq_t *seqHandle = 0; + snd_seq_client_info_t *cinfo = 0; + snd_seq_port_info_t *pinfo = 0; + int client = 0; + + // default to nothing + strncpy(buffer, "Device not found", bufferSize - 1); + + // open the sequencer interface + if ((alsaError = snd_seq_open(&seqHandle, "default", SND_SEQ_OPEN_OUTPUT, 0)) < 0) + { + WARN("Error opening ALSA sequencer: %s", snd_strerror(alsaError)); + return; + } + + // count ports that support MIDI write until we reach the requested index, + // which is probably the one we want + snd_seq_client_info_alloca(&cinfo); + snd_seq_port_info_alloca(&pinfo); + snd_seq_client_info_set_client(cinfo, -1); + while (outputCount <= outputIndex && + snd_seq_query_next_client(seqHandle, cinfo) >= 0) + { + client = snd_seq_client_info_get_client(cinfo); + snd_seq_port_info_set_client(pinfo, client); + snd_seq_port_info_set_port(pinfo, -1); + while (outputCount <= outputIndex && + snd_seq_query_next_port(seqHandle, pinfo) >= 0) + { + /* port must understand MIDI messages */ + if (!(snd_seq_port_info_get_type(pinfo) & SND_SEQ_PORT_TYPE_MIDI_GENERIC)) + continue; + /* we need both WRITE and SUBS_WRITE */ + if ((snd_seq_port_info_get_capability(pinfo) & (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)) != + (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)) + continue; + + ++outputCount; + if (outputCount - 1 == outputIndex) + { + // found the one we're looking for + snprintf(buffer, bufferSize, "%d:%d %s", + snd_seq_port_info_get_client(pinfo), + snd_seq_port_info_get_port(pinfo), + snd_seq_client_info_get_name(cinfo)); + // don't include port name, because we need to keep it brief + // snd_seq_port_info_get_name(pinfo)); +// INFO("MIDI outputIndex %d resolved to ALSA sequencer output %s", outputIndex, buffer); + } + } + } + + // close the sequencer interface + snd_seq_close(seqHandle); +#else + strncpy(buffer, "Unsupported", bufferSize - 1); + // suppress compiler warnings + (void)outputIndex; +#endif + // put NULL in last position in case we filled up everything else + *(buffer + bufferSize - 1) = '\0'; + + // suppress compiler warnings + (void)dev; +} + +static MusicDevice *createNativeMidiDevice() +{ + NativeMidiDevice *ndev = malloc(sizeof(NativeMidiDevice)); + ndev->dev.init = &NativeMidiInit; + ndev->dev.destroy = &NativeMidiDestroy; + ndev->dev.setupMode = &NativeMidiSetupMode; + ndev->dev.reset = &NativeMidiReset; + ndev->dev.generate = &NativeMidiGenerate; + ndev->dev.sendNoteOff = &NativeMidiSendNoteOff; + ndev->dev.sendNoteOn = &NativeMidiSendNoteOn; + ndev->dev.sendNoteAfterTouch = &NativeMidiSendNoteAfterTouch; + ndev->dev.sendControllerChange = &NativeMidiSendControllerChange; + ndev->dev.sendProgramChange = &NativeMidiSendProgramChange; + ndev->dev.sendChannelAfterTouch = &NativeMidiSendChannelAfterTouch; + ndev->dev.sendPitchBendML = &NativeMidiSendPitchBendML; + ndev->dev.getOutputCount = &NativeMidiGetOutputCount; + ndev->dev.getOutputName = &NativeMidiGetOutputName; + ndev->dev.isOpen = 0; + ndev->dev.outputIndex = 0; + ndev->dev.deviceType = Music_Native; +#ifdef WIN32 + ndev->outHandle = 0; +#elif defined(USE_ALSA) + ndev->outHandle = 0; + ndev->alsaMyId = 0; + ndev->alsaMyPort = 0; + ndev->alsaOutputId = 0; + ndev->alsaOutputPort = 0; +#endif + ndev->dev.musicType = MUSICTYPE_GENMIDI; + return &(ndev->dev); +} + +//------------------------------------------------------------------------------ +// FluidSynth soundfont synthesizer + +#ifdef USE_FLUIDSYNTH +#include + +typedef struct FluidMidiDevice +{ + MusicDevice dev; + fluid_synth_t *synth; + fluid_settings_t *settings; +} FluidMidiDevice; + +// forward declaration +static void FluidMidiGetOutputName(MusicDevice *dev, const unsigned int outputIndex, char *buffer, const unsigned int bufferSize); + +static int FluidMidiInit(MusicDevice *dev, const unsigned int outputIndex, unsigned samplerate) +{ + FluidMidiDevice *fdev = (FluidMidiDevice *)dev; + if (!fdev || fdev->dev.isOpen) return 0; + + fluid_settings_t *settings; + fluid_synth_t *synth; + int sfid; +#ifdef __APPLE__ + char fileName[1024] = ""; + sprintf(fileName, "%sres/", SDL_GetBasePath()); + + FluidMidiGetOutputName(dev, outputIndex, &fileName[strlen(fileName)], 1024 - strlen(fileName)); +#else + char fileName[1024] = "res/"; + FluidMidiGetOutputName(dev, outputIndex, &fileName[4], 1020); +#endif + if (strlen(fileName) == 4) + { + WARN("Failed to locate SoundFont for outputIndex=%d", outputIndex); + return -1; + } + + settings = new_fluid_settings(); + fluid_settings_setnum(settings, "synth.sample-rate", samplerate); + // default gain is 0.2, which is too conservative and ends up being quiet + fluid_settings_setnum(settings, "synth.gain", 0.5); + + synth = new_fluid_synth(settings); + sfid = fluid_synth_sfload(synth, fileName, 1); + + if (sfid == FLUID_FAILED) + { + WARN("cannot load %s for FluidSynth", fileName); + delete_fluid_synth(synth); + delete_fluid_settings(settings); + fdev->synth = NULL; + fdev->settings = NULL; + return -1; + } + + fluid_synth_sfont_select(synth, 0, sfid); + + fdev->synth = synth; + fdev->settings = settings; + + fdev->dev.isOpen = 1; + fdev->dev.outputIndex = outputIndex; + + return 0; +} + +static void FluidMidiDestroy(MusicDevice *dev) +{ + FluidMidiDevice *fdev = (FluidMidiDevice *)dev; + if (!fdev) return; + + delete_fluid_synth(fdev->synth); + delete_fluid_settings(fdev->settings); + free(fdev); +} + +static void FluidMidiSetupMode(MusicDevice *dev, MusicMode mode) +{ + (void)dev; + (void)mode; +} + +static void FluidMidiReset(MusicDevice *dev) +{ + FluidMidiDevice *fdev = (FluidMidiDevice *)dev; + if (!fdev || !fdev->dev.isOpen) return; + + fluid_synth_t *synth = fdev->synth; + fluid_synth_system_reset(synth); +} + +static void FluidMidiGenerate(MusicDevice *dev, short *samples, int numframes) +{ + FluidMidiDevice *fdev = (FluidMidiDevice *)dev; + if (!fdev || !fdev->dev.isOpen) return; + + fluid_synth_t *synth = fdev->synth; + fluid_synth_write_s16(synth, numframes, + samples, 0, 2, /* left channel*/ + samples, 1, 2 /* right channel*/); +} + +static void FluidMidiSendNoteOff(MusicDevice *dev, int channel, int note, int vel) +{ + FluidMidiDevice *fdev = (FluidMidiDevice *)dev; + if (!fdev || !fdev->dev.isOpen) return; + + fluid_synth_t *synth = fdev->synth; + fluid_synth_noteoff(synth, channel, note); + (void)vel; +} + +static void FluidMidiSendNoteOn(MusicDevice *dev, int channel, int note, int vel) +{ + FluidMidiDevice *fdev = (FluidMidiDevice *)dev; + if (!fdev || !fdev->dev.isOpen) return; + + fluid_synth_t *synth = fdev->synth; + fluid_synth_noteon(synth, channel, note, vel); +} + +static void FluidMidiSendNoteAfterTouch(MusicDevice *dev, int channel, int note, int touch) +{ +#if FLUIDSYNTH_VERSION_MAJOR >= 2 + FluidMidiDevice *fdev = (FluidMidiDevice *)dev; + if (!fdev || !fdev->dev.isOpen) return; + + fluid_synth_t *synth = fdev->synth; + fluid_synth_key_pressure(synth, channel, note, touch); +#else + // suppress compiler warnings + (void)dev; + (void)channel; + (void)note; + (void)touch; +#endif +} + +static void FluidMidiSendControllerChange(MusicDevice *dev, int channel, int ctl, int val) +{ + FluidMidiDevice *fdev = (FluidMidiDevice *)dev; + if (!fdev || !fdev->dev.isOpen) return; + + fluid_synth_t *synth = fdev->synth; + fluid_synth_cc(synth, channel, ctl, val); +} + +static void FluidMidiSendProgramChange(MusicDevice *dev, int channel, int pgm) +{ + FluidMidiDevice *fdev = (FluidMidiDevice *)dev; + if (!fdev || !fdev->dev.isOpen) return; + + fluid_synth_t *synth = fdev->synth; + fluid_synth_program_change(synth, channel, pgm); +} + +static void FluidMidiSendChannelAfterTouch(MusicDevice *dev, int channel, int touch) +{ + FluidMidiDevice *fdev = (FluidMidiDevice *)dev; + if (!fdev || !fdev->dev.isOpen) return; + + fluid_synth_t *synth = fdev->synth; + fluid_synth_channel_pressure(synth, channel, touch); +} + +static void FluidMidiSendPitchBendML(MusicDevice *dev, int channel, int msb, int lsb) +{ + FluidMidiDevice *fdev = (FluidMidiDevice *)dev; + if (!fdev || !fdev->dev.isOpen) return; + + fluid_synth_t *synth = fdev->synth; + fluid_synth_pitch_bend(synth, channel, msb * 128 + lsb); +} + +static unsigned int FluidMidiGetOutputCount(MusicDevice *dev) +{ + unsigned int outputCount = 0; +#ifdef WIN32 + // count number of .sf2 files in res/ subdirectory + char const * const pattern = "res\\*.sf2"; + WIN32_FIND_DATA data; + HANDLE hFind; + if ((hFind = FindFirstFile(pattern, &data)) != INVALID_HANDLE_VALUE) + { + // INFO("Counting SoundFont file: %s", data.cFileName); + do { ++outputCount; } while (FindNextFile(hFind, &data)); + FindClose(hFind); + } +#else +#ifdef __APPLE__ + char *respath[1024]; + sprintf(respath, "%sres/", SDL_GetBasePath()); + DIR *dirp = opendir(respath); +#else + DIR *dirp = opendir("res"); +#endif + struct dirent *dp = 0; + while ((dp = readdir(dirp))) + { + char *filename = dp->d_name; + char namelen = strlen(filename); + if (namelen < 4) continue; // ".sf2" + if (strcasecmp(".sf2", (char*)(filename + (namelen - 4)))) continue; + // found one + // INFO("Counting SoundFont file: %s", filename); + ++outputCount; + } + closedir(dirp); +#endif + + // suppress compiler warnings + (void)dev; + + return outputCount; +} + +static void FluidMidiGetOutputName(MusicDevice *dev, const unsigned int outputIndex, char *buffer, const unsigned int bufferSize) +{ + if (!buffer || bufferSize < 1) return; + // default to nothing + // save last position for NULL character + strncpy(buffer, "No SoundFonts found", bufferSize - 1); +#if WIN32 + unsigned int outputCount = 0; // subtract 1 to get index + // count .sf2 files in res/ subdirectory until we find the one that the user + // probably wants + char const * const pattern = "res\\*.sf2"; + WIN32_FIND_DATA data; + HANDLE hFind; + if ((hFind = FindFirstFile(pattern, &data)) != INVALID_HANDLE_VALUE) + { + do + { + ++outputCount; + if (outputCount - 1 == outputIndex) + { + // found it + strncpy(buffer, data.cFileName, bufferSize - 1); + // INFO("Found SoundFont file for outputIndex=%d: %s", outputIndex, data.cFileName); + break; + } + } while (FindNextFile(hFind, &data)); + FindClose(hFind); + } +#else + unsigned int outputCount = 0; // subtract 1 to get index + // count .sf2 files in res/ subdirectory until we find the one that the user + // probably wants +#ifdef __APPLE__ + char *respath[1024]; + sprintf(respath, "%sres/", SDL_GetBasePath()); + DIR *dirp = opendir(respath); +#else + DIR *dirp = opendir("res"); +#endif + struct dirent *dp = 0; + while ((outputCount <= outputIndex) && + (dp = readdir(dirp))) + { + char *filename = dp->d_name; + char namelen = strlen(filename); + if (namelen < 4) continue; // ".sf2" + if (strcasecmp(".sf2", (char*)(filename + (namelen - 4)))) continue; + // found one + // INFO("Counting SoundFont file: %s", filename); + ++outputCount; + if (outputCount - 1 != outputIndex) continue; + // found it + strncpy(buffer, filename, bufferSize - 1); + // INFO("Found SoundFont file for outputIndex=%d: %s", outputIndex, filename); + } + closedir(dirp); +#endif + // put NULL in last position in case we filled up everything else + *(buffer + bufferSize - 1) = '\0'; + + // suppress compiler warnings + (void)dev; + (void)outputIndex; +} + +static MusicDevice *createFluidSynthDevice() +{ + FluidMidiDevice *fdev = malloc(sizeof(FluidMidiDevice)); + fdev->dev.init = &FluidMidiInit; + fdev->dev.destroy = &FluidMidiDestroy; + fdev->dev.setupMode = &FluidMidiSetupMode; + fdev->dev.reset = &FluidMidiReset; + fdev->dev.generate = &FluidMidiGenerate; + fdev->dev.sendNoteOff = &FluidMidiSendNoteOff; + fdev->dev.sendNoteOn = &FluidMidiSendNoteOn; + fdev->dev.sendNoteAfterTouch = &FluidMidiSendNoteAfterTouch; + fdev->dev.sendControllerChange = &FluidMidiSendControllerChange; + fdev->dev.sendProgramChange = &FluidMidiSendProgramChange; + fdev->dev.sendChannelAfterTouch = &FluidMidiSendChannelAfterTouch; + fdev->dev.sendPitchBendML = &FluidMidiSendPitchBendML; + fdev->dev.getOutputCount = &FluidMidiGetOutputCount; + fdev->dev.getOutputName = &FluidMidiGetOutputName; + fdev->dev.isOpen = 0; + fdev->dev.outputIndex = 0; + fdev->dev.deviceType = Music_FluidSynth; + fdev->dev.musicType = MUSICTYPE_GENMIDI; + return &(fdev->dev); +} +#endif // USE_FLUIDSYNTH + +//------------------------------------------------------------------------------ +MusicDevice *CreateMusicDevice(MusicType type) +{ + MusicDevice *dev = 0; + + switch (type) + { + case Music_None: + dev = createNullMidiDevice(); + break; + case Music_AdlMidi: + dev = createAdlMidiDevice(); + break; + case Music_Native: + dev = createNativeMidiDevice(); + break; +#ifdef USE_FLUIDSYNTH + case Music_FluidSynth: + dev = createFluidSynthDevice(); + break; +#endif + } + + return dev; +} diff --git a/engine/src/MusicSrc/MusicDevice.h b/engine/src/MusicSrc/MusicDevice.h new file mode 100644 index 0000000..dc80550 --- /dev/null +++ b/engine/src/MusicSrc/MusicDevice.h @@ -0,0 +1,46 @@ +#pragma once + +typedef struct MusicDevice MusicDevice; + +typedef enum MusicType +{ + Music_None + ,Music_AdlMidi + ,Music_Native +#ifdef USE_FLUIDSYNTH + ,Music_FluidSynth +#endif +} MusicType; + +typedef enum MusicMode +{ + Music_GeneralMidi, + Music_SoundBlaster, +} MusicMode; + +struct MusicDevice +{ + int (*init)(MusicDevice *dev, const unsigned int outputIndex, unsigned samplerate); + void (*destroy)(MusicDevice *dev); + void (*setupMode)(MusicDevice *dev, MusicMode mode); + void (*reset)(MusicDevice *dev); + void (*generate)(MusicDevice *dev, short *samples, int numframes); + void (*sendNoteOff)(MusicDevice *dev, int channel, int note, int vel); + void (*sendNoteOn)(MusicDevice *dev, int channel, int note, int vel); + void (*sendNoteAfterTouch)(MusicDevice *dev, int channel, int note, int touch); + void (*sendControllerChange)(MusicDevice *dev, int channel, int ctl, int val); + void (*sendProgramChange)(MusicDevice *dev, int channel, int pgm); + void (*sendChannelAfterTouch)(MusicDevice *dev, int channel, int touch); + void (*sendPitchBendML)(MusicDevice *dev, int channel, int msb, int lsb); + unsigned int (*getOutputCount)(MusicDevice *dev); + void (*getOutputName)(MusicDevice *dev, const unsigned int outputIndex, char *buffer, const unsigned int bufferSize); + unsigned short isOpen; // 1 if device open, 0 if closed + unsigned int outputIndex; // index of currently opened output + MusicType deviceType; // type of device + char *musicType; // "sblaster" or "genmidi" +}; + +#define MUSICTYPE_SBLASTER "sblaster" +#define MUSICTYPE_GENMIDI "genmidi" + +MusicDevice *CreateMusicDevice(MusicType type); diff --git a/engine/src/Shock Build Notes b/engine/src/Shock Build Notes new file mode 100644 index 0000000..537abc2 --- /dev/null +++ b/engine/src/Shock Build Notes @@ -0,0 +1,9 @@ +Shock Build Notes +---------------- + +First build the libraries- go into the Libraries folder, and in each sub folder open the project in Metrowerks. Build the library, then copy the library file you just made into the LIB folder inside the Shock Folder. + +Second, build the game- open the Shock project and build it. + +You may also have to recompile the precompiled headers in the LIB folder. + diff --git a/registry.env.example b/registry.env.example new file mode 100644 index 0000000..1610b64 --- /dev/null +++ b/registry.env.example @@ -0,0 +1,9 @@ +# Copy to registry.env (gitignored, never commit the real values) and fill +# in your registry credentials. Used by build-image.sh, run-image.sh, and +# upload-image.sh. Only REGISTRY_IMAGE is needed for local builds/runs; +# REGISTRY/REGISTRY_USER/REGISTRY_PASSWORD are only needed to push with +# upload-image.sh. +REGISTRY=cr.ladkau.de +REGISTRY_IMAGE=cr.ladkau.de/questshock/builder +REGISTRY_USER= +REGISTRY_PASSWORD= diff --git a/res/assets/GET_ASSETS.txt b/res/assets/GET_ASSETS.txt new file mode 100644 index 0000000..1b9e162 --- /dev/null +++ b/res/assets/GET_ASSETS.txt @@ -0,0 +1,16 @@ +This package does not include System Shock's game data - it's +copyrighted, proprietary content that can't be redistributed. To play, +you need a copy of System Shock: Enhanced Edition (e.g. from gog.com). + +From your Enhanced Edition install, you need its classic-game data and +sound files - the res/pc/hd/data and res/pc/cdrom/data trees merged +together (res/pc/hd's copies win the couple of filenames present in +both: intro.res, objprop.dat), and the res/pc/hd/sound tree, packed +inside the install's sshock.kpf (a zip file). + +Copy that merged data into place, alongside this file: + res/data/ <- res/pc/hd/data + res/pc/cdrom/data, merged + res/sound/ <- res/pc/hd/sound + +Once res/data/ and res/sound/ exist next to this file, run ./run.sh from +the root of this package to play. diff --git a/res/assets/extract_assets.sh b/res/assets/extract_assets.sh new file mode 100755 index 0000000..92d5cdf --- /dev/null +++ b/res/assets/extract_assets.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env bash +# Extracts the game data Shockolate needs out of a purchased System Shock: +# Enhanced Edition installer from gog.com, into ./ss_ee/{data,sound}. +# +# Usage: +# 1. Buy System Shock: Enhanced Edition on gog.com and download the +# offline installer (a Windows .exe, e.g. +# setup_system_shock_enhanced_edition_1.2.16_(64bit)_(44378).exe). +# 2. Drop it in this directory (res/assets/). +# 3. Run this script: ./extract_assets.sh +# +# The installer is an Inno Setup package; the actual game data lives +# inside it in sshock.kpf, which is just a zip file. Its res/pc/hd/ and +# res/pc/cdrom/ trees together hold every classic-game data/sound file +# Shockolate's res/ folder expects (res/pc/hd wins the couple of +# filenames present in both: intro.res, objprop.dat). Needs innoextract +# and unzip; if they're not installed locally, this script does the +# extraction inside a throwaway Ubuntu container instead (needs docker in +# that case, nothing else). +set -euo pipefail + +cd "$(dirname "${BASH_SOURCE[0]}")" +ASSETS_DIR="$(pwd)" +OUT_DIR="$ASSETS_DIR/ss_ee" + +fail() { echo "PREFLIGHT FAIL: $*" >&2; exit 1; } + +shopt -s nullglob +installers=(setup_system_shock_enhanced_edition*.exe) +shopt -u nullglob +case "${#installers[@]}" in + 0) fail "no setup_system_shock_enhanced_edition*.exe found in $ASSETS_DIR - buy System Shock: Enhanced Edition on gog.com, download the offline installer, and drop it here" ;; + 1) ;; + *) fail "multiple installer .exe files found in $ASSETS_DIR - keep only one: ${installers[*]}" ;; +esac +INSTALLER="${installers[0]}" + +# Extracts sshock.kpf from $1 (an installer .exe) and assembles $2 (an +# ss_ee output directory) from its res/pc/{hd,cdrom} trees. Defined as a +# function so the exact same logic can run either natively or, via +# `declare -f`, inside a throwaway container. +run_extraction() { + set -euo pipefail + local installer="$1" out_dir="$2" + # Deliberately not `local`: an EXIT trap set here fires when the whole + # shell process exits, which is after this function - and this + # function's own scope - has already returned, so a `local` var would + # already be unset by then (unbound-variable error under `set -u`). + work="$(mktemp -d)" + trap 'rm -rf "${work:-}"' EXIT + + echo "== Extracting sshock.kpf from installer ==" + innoextract --silent --include sshock.kpf -d "$work/installer" "$installer" + + echo "== Unpacking sshock.kpf (data/sound trees only) ==" + mkdir -p "$work/kpf" + unzip -q "$work/installer/sshock.kpf" \ + 'res/pc/hd/data/*' 'res/pc/hd/sound/*' 'res/pc/cdrom/data/*' \ + -d "$work/kpf" + + echo "== Assembling $out_dir ==" + rm -rf "$out_dir" + mkdir -p "$out_dir/data" "$out_dir/sound" + # cdrom/data first (cutscenes, audio logs, and shared tables only it + # has), then hd/data on top (wins the 2 overlapping filenames: + # intro.res, objprop.dat - the hd install's own copies). + cp -a "$work/kpf/res/pc/cdrom/data/." "$out_dir/data/" + cp -a "$work/kpf/res/pc/hd/data/." "$out_dir/data/" + cp -a "$work/kpf/res/pc/hd/sound/." "$out_dir/sound/" +} + +if command -v innoextract >/dev/null 2>&1 && command -v unzip >/dev/null 2>&1; then + echo "== Using local innoextract/unzip ==" + run_extraction "$ASSETS_DIR/$INSTALLER" "$OUT_DIR" +else + echo "== innoextract/unzip not found locally - using a throwaway docker container ==" + command -v docker >/dev/null 2>&1 \ + || fail "docker not found in PATH (needed since innoextract/unzip aren't installed locally)" + docker run --rm \ + -v "$ASSETS_DIR:/assets" \ + -e HOST_UID="$(id -u)" \ + -e HOST_GID="$(id -g)" \ + ubuntu:22.04 \ + bash -c " + set -euo pipefail + apt-get update -qq >/dev/null + apt-get install -y -qq innoextract unzip >/dev/null + $(declare -f run_extraction) + run_extraction '/assets/$INSTALLER' '/assets/ss_ee' + chown -R \"\$HOST_UID:\$HOST_GID\" /assets/ss_ee + " +fi + +echo "== Done ==" +echo "Assets extracted to $OUT_DIR" +echo "Note: mfdfrn.res/mfdger.res (French/German MFD art) are not present in this" +echo "installer and are only needed for those localizations - not for English play." diff --git a/res/run.sh b/res/run.sh new file mode 100755 index 0000000..c6b2359 --- /dev/null +++ b/res/run.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# Launches System Shock. Copied next to the systemshock binary in dist/ +# by the root Makefile. +set -euo pipefail +cd "$(dirname "${BASH_SOURCE[0]}")" +export LD_LIBRARY_PATH="$(pwd)/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" +exec ./systemshock "$@" diff --git a/run-image.sh b/run-image.sh new file mode 100755 index 0000000..dbc7e19 --- /dev/null +++ b/run-image.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +# Runs the build-image against the repo to compile engine/ (the vendored +# Shockolate snapshot), entirely offline - every dependency it needs +# (SDL2, SDL2_mixer, fluidsynth-lite, a MIDI soundfont) was already built +# into the image by ./build-image.sh. +# +# Output lands in engine/.build-output/ - run `make dist` afterwards to +# assemble it (together with the game assets from res/assets/ss_ee/) into +# dist/. +# +# Usage: ./run-image.sh [command...] +# With no arguments, runs the engine build. Pass a command (e.g. `bash`) +# to get a shell in the build environment instead, useful for debugging +# a failed build. +set -euo pipefail + +cd "$(dirname "${BASH_SOURCE[0]}")" +ROOT="$(pwd)" + +fail() { echo "PREFLIGHT FAIL: $*" >&2; exit 1; } + +command -v docker >/dev/null 2>&1 \ + || fail "docker not found in PATH" + +IMAGE="${REGISTRY_IMAGE:-questshock/builder}" +if [ -f "$ROOT/registry.env" ]; then + # shellcheck disable=SC1091 + source "$ROOT/registry.env" + IMAGE="${REGISTRY_IMAGE:-$IMAGE}" +fi + +IMAGE_TAG="$(<"$ROOT/build-image/VERSION")" +[ -n "$IMAGE_TAG" ] || fail "build-image/VERSION is empty" +IMAGE="$IMAGE:$IMAGE_TAG" + +docker image inspect "$IMAGE" >/dev/null 2>&1 \ + || fail "$IMAGE not found locally - run ./build-image.sh first" + +TTY_FLAGS="-i" +[ -t 1 ] && TTY_FLAGS="-it" + +# shellcheck disable=SC2086 +docker run --rm $TTY_FLAGS \ + -v "$ROOT:/workspace" \ + -e HOST_UID="$(id -u)" \ + -e HOST_GID="$(id -g)" \ + "$IMAGE" \ + "$@" + +if [ "$#" -eq 0 ]; then + echo + echo "This only compiled the engine (engine/.build-output/) - it does not" + echo "create dist/ by itself. Run 'make dist' next to assemble a runnable" + echo "copy (or 'make package' for a redistributable tarball)." +fi diff --git a/upload-image.sh b/upload-image.sh new file mode 100755 index 0000000..84a93d7 --- /dev/null +++ b/upload-image.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# Pushes the build-image - already built locally with ./build-image.sh, +# and ideally verified with ./run-image.sh - to a container registry, so +# CI or other machines can reuse it without rebuilding the whole toolchain. +set -euo pipefail + +cd "$(dirname "${BASH_SOURCE[0]}")" +ROOT="$(pwd)" + +fail() { echo "PREFLIGHT FAIL: $*" >&2; exit 1; } + +command -v docker >/dev/null 2>&1 \ + || fail "docker not found in PATH" + +[ -f "$ROOT/registry.env" ] \ + || fail "registry.env not found - copy registry.env.example to registry.env and fill in your registry credentials" + +# shellcheck disable=SC1091 +source "$ROOT/registry.env" + +: "${REGISTRY:?registry.env must set REGISTRY}" +: "${REGISTRY_IMAGE:?registry.env must set REGISTRY_IMAGE}" +: "${REGISTRY_USER:?registry.env must set REGISTRY_USER}" +: "${REGISTRY_PASSWORD:?registry.env must set REGISTRY_PASSWORD}" + +VERSION="$(<"$ROOT/build-image/VERSION")" +[ -n "$VERSION" ] || fail "build-image/VERSION is empty" + +docker image inspect "$REGISTRY_IMAGE:$VERSION" >/dev/null 2>&1 \ + || fail "$REGISTRY_IMAGE:$VERSION not found locally - run ./build-image.sh first" + +echo "== Logging in to $REGISTRY ==" +echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY" -u "$REGISTRY_USER" --password-stdin + +echo "== Pushing $REGISTRY_IMAGE:$VERSION and :latest ==" +docker push "$REGISTRY_IMAGE:$VERSION" +docker push "$REGISTRY_IMAGE:latest" + +echo "== Done ==" +echo "Image: $REGISTRY_IMAGE:$VERSION"